/*********************************************************************/
/*  bibView: Administration of BibTeX-Databases                      */
/*           (Verwaltung von BibTeX-Literaturdatenbanken)            */
/*                                                                   */
/*  Module:  FileNom.c                                               */
/*                                                                   */
/*             - File Selection Box                                  */
/*               Adapted and modified Widget from aXe editor         */
/*                                                                   */
/*  Author:  Holger Martin,  martinh@informatik.tu-muenchen.de       */
/*           Peter M. Urban, urban@informatik.tu-muenchen.de         */
/*                                                                   */
/*  History:                                                         */
/*    11.22.91  HM   created                                         */
/*    05.26.92       Version 1.0 released                            */
/*                                                                   */
/*  Copyright 1992 TU MUENCHEN	                                     */
/*    See ./Copyright for complete rights and liability information. */
/*                                                                   */
/*********************************************************************/

/*
 * Copyright 1991 The University of Newcastle upon Tyne
 * 
 * Permission to use, copy, modify and distribute this software and its
 * documentation for any purpose other than its commercial exploitation
 * is hereby granted without fee, provided that the above copyright
 * notice appear in all copies and that both that copyright notice and
 * this permission notice appear in supporting documentation, and that
 * the name of The University of Newcastle upon Tyne not be used in
 * advertising or publicity pertaining to distribution of the software
 * without specific, written prior permission. The University of
 * Newcastle upon Tyne makes no representations about the suitability of
 * this software for any purpose. It is provided "as is" without express
 * or implied warranty.
 * 
 * THE UNIVERSITY OF NEWCASTLE UPON TYNE DISCLAIMS ALL WARRANTIES WITH
 * REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL THE UNIVERSITY OF
 * NEWCASTLE UPON TYNE BE LIABLE FOR ANY SPECIAL, INDIRECT OR
 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
 * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
 * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
 * 
 * Author:  Jim Wight (j.k.wight@newcastle.ac.uk)
 *          Computing Laboratory, University of Newcastle upon Tyne, UK
 */

#include <X11/IntrinsicP.h>	
#include <X11/StringDefs.h>	
#include <X11/Xaw/MenuButton.h>
#include <X11/Xaw/SimpleMenu.h>
#include <X11/Xaw/SmeBSB.h>
#include <X11/Xaw/Viewport.h>
#include <X11/Xaw/List.h>
#include <X11/Xaw/Scrollbar.h>
#include <X11/Xaw/AsciiText.h>
#include <X11/Xaw/Command.h>
#include <X11/Xos.h>

#include <sys/param.h>
#include <sys/stat.h>
#include <errno.h>
#ifndef HAVE_DIR
#include <dirent.h>
#else
#include <sys/dir.h>
#define dirent direct
#endif
#include <stdio.h>

extern char *getenv();

extern char *actual_path;


#include "FileNomP.h"

#define Offset(field) XtOffsetOf(FileNominatorRec, fileNominator.field)

static int first_in = 1; 

static XtResource resources[] = {
    {XtNselectCallback, XtCCallback, XtRCallback, sizeof(XtCallbackList),
	 Offset(select_callback), XtRCallback, (XtPointer) NULL},
    {XtNselectMenu, XtCSelectMenu, XtRString, sizeof(String),
	 Offset(select_menu), XtRString, (XtPointer) NULL},
    {XtNcancelCallback, XtCCallback, XtRCallback, sizeof(XtCallbackList),
	 Offset(cancel_callback), XtRCallback, (XtPointer) NULL},
    {XtNmargin, XtCMargin, XtRDimension, sizeof(Dimension),
	 Offset(margin), XtRImmediate, (XtPointer) 10},
    {XtNnumberRows, XtCNumberStrings, XtRInt, sizeof(int),
	 Offset(number_rows), XtRImmediate, (XtPointer) 15},
    {XtNshowDotFiles, XtCShowDotFiles, XtRBoolean, sizeof(Boolean),
	 Offset(show_dot_files), XtRImmediate, (XtPointer) True},
    {XtNbellLevel, XtCBellLevel, XtRInt, sizeof(int),
	 Offset(bell_level), XtRImmediate, (XtPointer) 100},
};

#undef Offset

#define Child(w,child) (((FileNominatorWidget) w)->fileNominator.child)
#define List(w) (((FileNominatorWidget) w)->fileNominator.listList)
#define Rows(w) (((FileNominatorWidget) w)->fileNominator.number_rows)
#define CurrentDir(w) (((FileNominatorWidget) w)->fileNominator.currentDir)
#define WatchingChanges(w) (((FileNominatorWidget) w)->fileNominator.watchingChanges)
#define Nomination(w) (((FileNominatorWidget) w)->fileNominator.nomination)
#define ShowDotFiles(w) (((FileNominatorWidget) w)->fileNominator.show_dot_files)
#define BellLevel(w) (((FileNominatorWidget) w)->fileNominator.bell_level)


static char pathTranslations[] =
    "<BtnDown>:reset() MakeMenu() XawPositionSimpleMenu(menu) MenuPopup(menu)";

static char listTranslations[] =
    "<Btn1Up>(2):         Set() Nominate() Unset()\n\
     <Btn1Down>,<Btn1Up>: Set() Notify() \n\
     <Btn2Up>:            Set() Notify() Nominate() Unset()";

static char filenameTranslations[] =
     "<Key>Return: Nominate() \n\
      <Key>Escape: ToggleDotFiles()";

static char selectTranslations[] =
    "<Btn1Up>: Nominate() unset()";

static char selectMenuTranslations[] =
    "<Btn3Down>: XawPositionSimpleMenu(selectMenu) MenuPopup(selectMenu)";

static void MakeMenu(), Nominate(), ToggleDotFiles();

static XtActionsRec pathActions[] = {
    "MakeMenu", MakeMenu,
    "Nominate", Nominate,
    "ToggleDotFiles", ToggleDotFiles,
};

static void                     CancelCallback();
static void                     ChangeDir();
static void                     SelectDir();
static void                     FillWindow();
static void                     ReplaceFilename();
static void                     AsciiSourceChanged();
static void                     WatchForChanges();
static void                     DontWatchForChanges();
static void                     CollapsePath();

static void Initialize(), PositionChildren(), Realize(), Destroy();

FileNominatorClassRec fileNominatorClassRec = {
    /* Core class part */
  {
    /* superclass	     */	(WidgetClass) &widgetClassRec,
    /* class_name	     */ "FileNominator",
    /* widget_size	     */ sizeof(FileNominatorRec),
    /* class_initialize      */ NULL,
    /* class_part_initialize */ NULL,
    /* class_inited          */	FALSE,
    /* initialize	     */	Initialize,
    /* initialize_hook       */	NULL,
    /* realize		     */	Realize,
    /* actions		     */	NULL,
    /* num_actions	     */	0,
    /* resources	     */	resources,
    /* num_resources	     */	XtNumber(resources),
    /* xrm_class	     */	NULLQUARK,
    /* compress_motion	     */	TRUE,
    /* compress_exposure     */	XtExposeCompressMultiple,
    /* compress_enterleave   */	TRUE,
    /* visible_interest	     */	FALSE,
    /* destroy		     */	Destroy,
    /* resize		     */	PositionChildren,
    /* expose		     */	NULL,
    /* set_values	     */	NULL,
    /* set_values_hook       */	NULL,			
    /* set_values_almost     */	XtInheritSetValuesAlmost,  
    /* get_values_hook       */	NULL,
    /* accept_focus	     */	NULL,
    /* version		     */	XtVersion,
    /* callback offsets      */	NULL,
    /* tm_table              */	NULL,
    /* query_geometry	     */	XtInheritQueryGeometry,
    /* display_accelerator   */	NULL,
    /* extension	     */	NULL,
  },
   /* FileNominator class part */
  {
    /* extension	     */	NULL,
  }
};

WidgetClass fileNominatorWidgetClass = (WidgetClass) &fileNominatorClassRec;

static void
CalculateSize(fnw, width, height)
    FileNominatorWidget fnw;
    Dimension *width, *height;
{
    int file_width = 2 * Child(fnw,filename_widget)->core.width +
	2 * Child(fnw,filename_widget)->core.border_width;
    int select_width = 2 * Child(fnw,select_widget)->core.width + 
	2 * Child(fnw,select_widget)->core.border_width + fnw->fileNominator.margin +
	    Child(fnw,cancel_widget)->core.width +
		2 * Child(fnw,cancel_widget)->core.border_width +
		    Child(fnw,path_widget)->core.width +
	2 * Child(fnw,path_widget)->core.border_width + fnw->fileNominator.margin;

    int max;

    if (fnw->fileNominator.margin == 0)
    {
	max = select_width - 4 * (Child(fnw,select_widget)->core.border_width) >
	    file_width - 2 * Child(fnw,filename_widget)->core.border_width ?
		select_width - 4 * (Child(fnw,select_widget)->core.border_width) :
		    file_width - 2 * Child(fnw,filename_widget)->core.border_width;
    }
    else
    {
	max = select_width > file_width ? select_width : file_width;
    }
    *width = max + 2 * fnw->fileNominator.margin; 
    *height = Child(fnw,viewport_widget)->core.height +
	    Child(fnw,filename_widget)->core.height +
		Child(fnw,select_widget)->core.height +
		    4 * fnw->fileNominator.margin;

    if (fnw->fileNominator.margin == 0)
    {
	*height += 2 * (Child(fnw,filename_widget)->core.border_width);
    }
    else
    {
	*height += 2 * (Child(fnw,filename_widget)->core.border_width +
		       Child(fnw,viewport_widget)->core.border_width +
		       Child(fnw,select_widget)->core.border_width);
    }
}

static void
PositionChildren(fnw)
    FileNominatorWidget fnw;
{
    if (fnw->fileNominator.margin == 0)
    {
	XtConfigureWidget(Child(fnw,filename_widget),
			  0, 0,
			  fnw->core.width,
			  fnw->core.height -
			  Child(fnw,viewport_widget)->core.height -
			  2 * Child(fnw,viewport_widget)->core.border_width -
			  Child(fnw,select_widget)->core.height,
			  0);
    }
    else
    {
          XtConfigureWidget(Child(fnw,filename_widget),
		      fnw->fileNominator.margin,
		      fnw->fileNominator.margin,
	              fnw->core.width -
                      2 * fnw->fileNominator.margin -
                      2 * Child(fnw,filename_widget)->core.border_width,
		      fnw->core.height -
		      2 * Child(fnw,filename_widget)->core.border_width -
		      Child(fnw,viewport_widget)->core.height -
		      2 * Child(fnw,viewport_widget)->core.border_width -
		      Child(fnw,select_widget)->core.height -
		      2 * Child(fnw,select_widget)->core.border_width -
		      4 * fnw->fileNominator.margin,
		      Child(fnw,filename_widget)->core.border_width);
      }

    if (fnw->fileNominator.margin == 0)
    {
	XtConfigureWidget(Child(fnw,viewport_widget),
			  -(Child(fnw,viewport_widget)->core.border_width),
			  Child(fnw,filename_widget)->core.height,
			  fnw->core.width,
			  Child(fnw,viewport_widget)->core.height,
			  Child(fnw,viewport_widget)->core.border_width);
    }
    else
    {
	XtConfigureWidget(Child(fnw,viewport_widget),
                          fnw->fileNominator.margin,
			  fnw->core.height -
			  Child(fnw,select_widget)->core.height -
			  2 * Child(fnw,select_widget)->core.border_width -
			  Child(fnw,viewport_widget)->core.height -
			  2 * Child(fnw,viewport_widget)->core.border_width -
			  2 * fnw->fileNominator.margin,
			  fnw->core.width -
			  2 * fnw->fileNominator.margin -
			  2 * Child(fnw,viewport_widget)->core.border_width,
			  Child(fnw,viewport_widget)->core.height,
			  Child(fnw,viewport_widget)->core.border_width);
    }

    if (fnw->fileNominator.margin == 0)
    {
	XtMoveWidget(Child(fnw,select_widget), -1,
		     fnw->core.height -
		     Child(fnw,select_widget)->core.height - 1);
    }
    else
    {
	XtMoveWidget(Child(fnw,select_widget), fnw->fileNominator.margin,
		     fnw->core.height -
		     fnw->fileNominator.margin -
		     2 * Child(fnw,select_widget)->core.border_width -
		     Child(fnw,select_widget)->core.height);
    }
	

    if (fnw->fileNominator.margin == 0)
    {
	XtMoveWidget(Child(fnw,path_widget),
		     Child(fnw,select_widget)->core.width,
		     fnw->core.height -
		     Child(fnw,select_widget)->core.height - 1);
    }
    else
    {
	XtMoveWidget(Child(fnw,path_widget),
		     Child(fnw,select_widget)->core.width +
		     2 * Child(fnw,select_widget)->core.border_width +
		     2 * fnw->fileNominator.margin,
		     fnw->core.height -
		     fnw->fileNominator.margin -
		     2 * Child(fnw,select_widget)->core.border_width -
		     Child(fnw,select_widget)->core.height);
    }
    
    if (fnw->fileNominator.margin == 0)
    {
	XtMoveWidget(Child(fnw,cancel_widget), 
		     Child(fnw,select_widget)->core.width +
		     Child(fnw,path_widget)->core.width +
		     Child(fnw,select_widget)->core.border_width,
		     fnw->core.height -
		     Child(fnw,select_widget)->core.height - 1);
    }
    else
    {
	XtMoveWidget(Child(fnw,cancel_widget), 
		     Child(fnw,path_widget)->core.width +
		     2 * Child(fnw,path_widget)->core.border_width +
		     Child(fnw,select_widget)->core.width +
		     2 * Child(fnw,select_widget)->core.border_width +
		     3 * fnw->fileNominator.margin,
		     fnw->core.height -
		     fnw->fileNominator.margin -
		     2 * Child(fnw,select_widget)->core.border_width -
		     Child(fnw,select_widget)->core.height);
    }
}

/* ARGSUSED */
static void
Initialize(req, new, args, num_args)
    Widget req, new;
    ArgList args;
    Cardinal *num_args;
{
    FileNominatorWidget fnw = (FileNominatorWidget) new;
    Widget sMenu, menuEntry;
    String menuList, p, q;

    List(new) = NULL;
    Nomination(new).directoryPart = NULL;
    Nomination(new).filenamePart = NULL;
#ifdef SYSV
    (void) getcwd(CurrentDir(new), MAXPATHLEN);
#else    
    (void) getwd(CurrentDir(new));
#endif

    if (actual_path==NULL){
       actual_path = (char *) XtMalloc(MAXPATHLEN+1);
       strcpy(actual_path, CurrentDir(new));
       }
    else{
       if (CurrentDir(new)[strlen(CurrentDir(new)) - 1] != '/')
          {
  	   strcat(CurrentDir(new), "/");
          }
       if (*actual_path != '/'){
          strcat(CurrentDir(new), actual_path);
          strcpy(actual_path, CurrentDir(new));
	  CollapsePath(actual_path, CurrentDir(new));
          }
       else
	  CollapsePath(actual_path, CurrentDir(new));
       }
    if (CurrentDir(new)[strlen(CurrentDir(new)) - 1] != '/')
    {
	strcat(CurrentDir(new), "/");
    }
    WatchingChanges(new) = False;

    Child(fnw,viewport_widget)
	= XtVaCreateWidget("viewport", viewportWidgetClass, new,
			   XtNallowVert, True,
			   XtNallowHoriz, True,
			   NULL);

    Child(fnw,list_widget) =
	XtVaCreateManagedWidget("list", listWidgetClass, Child(fnw,viewport_widget),
				XtNdefaultColumns, 1,
				XtNforceColumns, True,
				NULL);
    
    XtOverrideTranslations(Child(fnw,list_widget),
			   XtParseTranslationTable(listTranslations));
    XtAddCallback(Child(fnw,list_widget), XtNcallback, ReplaceFilename, NULL); 
    FillWindow(fnw);

    Child(fnw,filename_widget)
	= XtVaCreateWidget("filename", asciiTextWidgetClass, new,
			   XtNeditType, XawtextEdit,
			   NULL);
    XtOverrideTranslations(Child(fnw,filename_widget),
			   XtParseTranslationTable(filenameTranslations));
    
    XtSetKeyboardFocus(new, Child(fnw,filename_widget));
    WatchForChanges(fnw);

    Child(fnw,select_widget)
	= XtVaCreateWidget("select", commandWidgetClass, new,
                           XtNlabel,  "OK",
			   NULL);
    XtOverrideTranslations(Child(fnw,select_widget),
			   XtParseTranslationTable(selectTranslations));
    if (fnw->fileNominator.select_menu)
    {
	sMenu = XtVaCreatePopupShell("selectMenu",
				     simpleMenuWidgetClass, Child(fnw,select_widget),
				     NULL);
	menuList = XtNewString(fnw->fileNominator.select_menu);
	p = menuList;
	for (p = menuList;  (q = (char *)index(p, ':'));  p = q + 1)
	{
	    *q = '\0';
	    menuEntry = XtVaCreateManagedWidget(p,
						smeBSBObjectClass, sMenu,
						NULL);
	    XtAddCallback(menuEntry, XtNcallback, SelectDir, NULL);
	}
	menuEntry = XtVaCreateManagedWidget(p,
					    smeBSBObjectClass, sMenu,
					    NULL);
	XtAddCallback(menuEntry, XtNcallback, SelectDir, NULL);
	XtVaSetValues(sMenu,
		      XtNpopupOnEntry, menuEntry,
		      NULL);
    
	XtOverrideTranslations(Child(fnw,select_widget),
			      XtParseTranslationTable(selectMenuTranslations));
    
	XtFree(menuList);
    }

#ifdef GERMAN
    Child(fnw,path_widget)
	= XtVaCreateWidget("path", menuButtonWidgetClass, new,
                           XtNlabel,  "Pfad",
			   NULL);
#else    
    Child(fnw,path_widget)
	= XtVaCreateWidget("path", menuButtonWidgetClass, new,
                           XtNlabel,  "Path",
			   NULL);
#endif    
    XtOverrideTranslations(Child(fnw,path_widget),
			   XtParseTranslationTable(pathTranslations));
    XtAppAddActions(XtWidgetToApplicationContext(new),
		    pathActions, XtNumber(pathActions));
if (first_in == 1){ 
    XawSimpleMenuAddGlobalActions(XtWidgetToApplicationContext(new));
    first_in = 0;  
    }  

#ifdef GERMAN
    Child(fnw,cancel_widget)
	= XtVaCreateWidget("cancel", commandWidgetClass, new,
                             XtNlabel,  "Abbruch",
                             NULL);
#else    
    Child(fnw,cancel_widget)
	= XtVaCreateWidget("cancel", commandWidgetClass, new,
                             XtNlabel,  "Cancel",
                             NULL);
#endif    
    XtAddCallback(Child(fnw,cancel_widget), XtNcallback, CancelCallback, NULL); 
    
    CalculateSize(fnw, &fnw->core.width, &fnw->core.height);
    PositionChildren(fnw);
}

static void 
Realize(w, valueMask, attributes)
    Widget w;
    XtValueMask *valueMask;
    XSetWindowAttributes *attributes;
{
    (*fileNominatorWidgetClass->core_class.superclass->core_class.realize)
	(w, valueMask, attributes);

    XtRealizeWidget(Child(w,viewport_widget));
    XtRealizeWidget(Child(w,list_widget));
    XtRealizeWidget(Child(w,filename_widget));
    XtRealizeWidget(Child(w,select_widget));
    XtRealizeWidget(Child(w,path_widget));
    XtRealizeWidget(Child(w,cancel_widget));

    XMapSubwindows(XtDisplay(w), XtWindow(w));
}

static void Destroy(w)
    Widget w;
{
    int idx;
    FileNominatorWidget fnw = (FileNominatorWidget) w;
    XtDestroyWidget(Child(fnw,list_widget));
    XtDestroyWidget(Child(fnw,viewport_widget));
    XtDestroyWidget(Child(fnw,filename_widget));
    XtDestroyWidget(Child(fnw,select_widget));
    XtDestroyWidget(Child(fnw,path_widget));
    XtDestroyWidget(Child(fnw,cancel_widget));

    idx = 0;
    while (List(fnw)[idx])
    {
	XtFree(List(fnw)[idx++]);
    }
    XtFree(List(fnw)[idx]);
    XtFree((char *)List(fnw));

    strcpy(actual_path, Nomination(fnw).directoryPart);

    XtFree(Nomination(fnw).directoryPart);
    XtFree(Nomination(fnw).filenamePart);
}

/* ARGSUSED */
static void 
CancelCallback(w, client_data, call_data)
     Widget w;
     XtPointer client_data, call_data;
{
    XtCallCallbacks(XtParent(w), XtNcancelCallback, NULL);
}

/* ARGSUSED */
static void
DestroyMenu(w, client_data, call_data)
     Widget w;
     XtPointer client_data, call_data;
{
    XtDestroyWidget(w);
}

/* ARGSUSED */
static void
MakeMenu(w, event, params, num_params)
     Widget w;
     XEvent *event;
     String *params;
     Cardinal *num_params;
{
    FileNominatorWidget fnw = (FileNominatorWidget) XtParent(w);
    String menuName;
    Widget menu, menuEntry;
    char *where, *p1, *p2;
    int menuItem = 1, len;

    XtVaGetValues(w,
		  XtNmenuName, &menuName,
		  NULL);
    
    menu = XtVaCreatePopupShell(menuName,
				simpleMenuWidgetClass, w,
				NULL);
    XtAddCallback(menu, XtNpopdownCallback, DestroyMenu, NULL);

    where = XtNewString(CurrentDir(fnw));
    menuEntry = XtVaCreateManagedWidget("/",
					smeBSBObjectClass, menu, NULL);
    XtAddCallback(menuEntry, XtNcallback, ChangeDir, (XtPointer) menuItem++);
    p1 = where + 1;
    len = strlen(where);
    while (p1 < &where[len])
    {
	if (!(p2 = (char *)index(p1, '/')))
	{
	    p2 = &where[len];
	}
	*p2 = '\0';
	menuEntry = XtVaCreateManagedWidget(p1,
					    smeBSBObjectClass, menu, NULL);
	XtAddCallback(menuEntry, XtNcallback, ChangeDir,
		      (XtPointer) menuItem++);
	p1 = p2 + 1;
    }
    
    XtVaSetValues(menu,
		  XtNpopupOnEntry, menuEntry,
		  NULL);
    
    XtFree(where);
}

static void
ChangeDirectory(fnw, position)
     FileNominatorWidget fnw;
     int position;
{
    String p;
    int m;

    if (position > 0)
    {
	p = CurrentDir(fnw);
	for (m = 0;  m < position;  ++m)
	{
	    while(*p++ != '/')
		;
	}
	*p = '\0';
    }

    XtVaSetValues(Child(fnw, filename_widget),
		  XtNstring, "",
		  NULL);

    FillWindow(fnw);

    PositionChildren(fnw);
}
	 
/* ARGSUSED */
static void
ChangeDir(w, client_data, call_data)
     Widget w;
     XtPointer client_data, call_data;
{
    FileNominatorWidget fnw
        = (FileNominatorWidget) XtParent(XtParent(XtParent(w)));

    ChangeDirectory(fnw, (int) client_data);
}

/* ARGSUSED */
static void
SelectDir(w, client_data, call_data)
     Widget w;
     XtPointer client_data, call_data;
{
    FileNominatorWidget fnw
        = (FileNominatorWidget) XtParent(XtParent(XtParent(w)));
    String label;

    XtVaGetValues(w,
		  XtNlabel, &label,
		  NULL);

    XtVaSetValues(Child(fnw,filename_widget),
		  XtNstring, label,
		  NULL);

    Nominate(Child(fnw,select_widget), NULL, NULL, NULL);
}

/* ARGSUSED */
static void
Nominate(w, event, params, num_params)
     Widget w;
     XEvent *event;
     String *params;
     Cardinal *num_params;
{
    FileNominatorWidget fnw;
    char *nomination, *home, selection[MAXPATHLEN], *newPath, *r;
    struct stat fstats;
    int status, len;

    if (XtIsSubclass(w, listWidgetClass))
    {
	fnw = (FileNominatorWidget) XtParent(XtParent(w));
    }
    else
    {
	fnw = (FileNominatorWidget) XtParent(w);
    }
    
    XtVaGetValues(Child(fnw,filename_widget),
		  XtNstring, &nomination,
		  NULL);

    selection[0] = '\0';
    if (*nomination == '/')
    {
	strcpy(selection, nomination);
    }
    else if (*nomination == '~' && (home = getenv("HOME")))
    {
	strcpy(selection, home);
	strcat(selection, &nomination[1]);
    }
    else
    {
	if (strlen(CurrentDir(fnw)) > 1)
	{
	    strcpy(selection, CurrentDir(fnw));
	}
	strcat(selection, "/");
	strcat(selection, nomination);
    }

    len = strlen(selection);
    if (len != 0)
    {
	newPath = (char *) XtMalloc(len + 2);
	CollapsePath(selection, newPath);
	status = stat(newPath, &fstats);
	if (status != -1 && fstats.st_mode & S_IFDIR)
	{
	    if (access(newPath, R_OK) == 0)
	    {
		if (newPath[strlen(newPath) - 1] != '/')
		{
		    strcat(newPath, "/");
		}
		strcpy(CurrentDir(fnw), newPath);
		ChangeDirectory(fnw, 0);
	    }
	    else
	    {
		XBell(XtDisplay(fnw), BellLevel(fnw));
	    }
	}
	else if (status == 0 || (status == -1 && errno == ENOENT))
	{
	    status = access(newPath, R_OK | W_OK);
	    r = (char *)rindex(newPath, '/');
	    XtFree(Nomination(fnw).filenamePart);
	    Nomination(fnw).filenamePart = XtNewString(r + 1);
	    Nomination(fnw).filenameStatus = (status == 0) ? status : errno;
	    *(r + 1) = '\0';
	    XtFree(Nomination(fnw).directoryPart);
	    status = access(newPath, R_OK);
	    if (strcmp(newPath, CurrentDir(fnw)) != 0 && status == 0)
	    {
		strcpy(CurrentDir(fnw), newPath);
		ChangeDirectory(fnw, 0);
		Nomination(fnw).directoryPart = XtNewString(CurrentDir(fnw));
	    }
	    else
	    {
		Nomination(fnw).directoryPart = XtNewString(newPath);
	    }
	    Nomination(fnw).directoryStatus = (status == 0) ? status : errno;
	    XtCallCallbacks((Widget) fnw, XtNselectCallback,
			                        (XtPointer) &Nomination(fnw));
	}
	else
	{
            XBell(XtDisplay(fnw), BellLevel(fnw));
	}
	XtFree(newPath);
    }
}

static void
FillWindow(fnw)
     Widget fnw;
{
    XFontStruct *font;
    Dimension height, internalHeight, rowSpacing;
    int num, newNum, idx;
    struct dirent **namelist;
    extern int alphasort();
    char buf[MAXPATHLEN], *bp;
    String name;
    struct stat fstats;
    
    num = scandir(CurrentDir(fnw), &namelist, (int(*)())0, alphasort);
    if (num <= 0)
    {
	return;
    }

    if (List(fnw))
    {
        idx = 0;
        while (List(fnw)[idx])
        {
            XtFree(List(fnw)[idx++]);
        }
	XtFree(List(fnw)[idx]);
	XtFree((char *)List(fnw));
    }
    List(fnw) = (String *) XtMalloc((num + 1) * sizeof(String));

    strcpy(buf, CurrentDir(fnw));
    strcat(buf, "/");
    bp = buf + strlen(buf);
    for(idx = 0, newNum = 0; idx < num;  idx++)
    {
	name = namelist[idx]->d_name;
	if (ShowDotFiles(fnw) || (!ShowDotFiles(fnw) &&
				  (*name != '.' 
				  || ((strcmp(name, ".") == 0) ||
				      (strcmp(name, "..") == 0)))))
	{
	    List(fnw)[newNum] = XtMalloc(strlen(name) + 2);
	    strcpy(List(fnw)[newNum], name);
	    strcpy(bp, name);
	    (void) stat(buf, &fstats);
	    if (fstats.st_mode & S_IFDIR)
	    {
		strcat(List(fnw)[newNum], "/");
	    }
	    ++newNum;
	}
    }

    for(idx = 0; idx < num;  idx++)
    {
	XtFree((char *)namelist[idx]);
    }
    XtFree((char *)namelist);

    List(fnw)[newNum] = NULL;

    XtVaGetValues(Child(fnw,list_widget),
                  XtNfont, &font,
                  XtNinternalHeight, &internalHeight,
		  XtNrowSpacing, &rowSpacing,
                  NULL);

    height = Rows(fnw) * (font->max_bounds.ascent +
		     font->max_bounds.descent + rowSpacing) -
			 rowSpacing + 2 * internalHeight; 
    
    XtVaSetValues(Child(fnw,viewport_widget),
                  XtNheight, height,
                  NULL);

    XawListChange(Child(fnw,list_widget), List(fnw), newNum, -1, True);
}

/* ARGSUSED */
static void
ToggleDotFiles(w, event, params, num_params)
     Widget w;
     XEvent *event;
     String *params;
     Cardinal *num_params;
{
    Widget fnw = XtParent(w);
    Boolean showDotFiles;

    XtVaGetValues(fnw,
		  XtNshowDotFiles, &showDotFiles,
		  NULL);

    XtVaSetValues(fnw,
		  XtNshowDotFiles, !showDotFiles,
		  NULL);

    XtVaSetValues(w,
		  XtNstring, "./",
		  NULL);

    Nominate(Child(fnw,select_widget), NULL, NULL, NULL);
}

/* ARGSUSED */
static void
ReplaceFilename(w, client_data, call_data)
     Widget w;
     XtPointer client_data, call_data;
{
    FileNominatorWidget fnw
	= (FileNominatorWidget) XtParent(XtParent(w));

    XawListReturnStruct *list = XawListShowCurrent(Child(fnw,list_widget));

    XtVaSetValues(Child(fnw,filename_widget),
		  XtNstring, list->string,
		  NULL);

    XawTextSetInsertionPoint(Child(fnw,filename_widget),
			     (XawTextPosition) strlen(list->string));

    WatchForChanges(fnw);
}

/* ARGSUSED */
static void
AsciiSourceChanged(w, client_data, call_data)
Widget w;
XtPointer client_data, call_data;
{
    FileNominatorWidget fnw = (FileNominatorWidget) client_data;

    DontWatchForChanges(fnw);

    XawListUnhighlight(Child(fnw,list_widget));
}

static void
WatchForChanges(fnw)
     Widget fnw;
{
    if (!WatchingChanges(fnw))
    {
	XtAddCallback(XawTextGetSource(Child(fnw,filename_widget)), XtNcallback,
		      AsciiSourceChanged, (XtPointer) fnw);

	WatchingChanges(fnw) = True;
    }
}

static void
DontWatchForChanges(fnw)
     Widget fnw;
{
    XtRemoveCallback(XawTextGetSource(Child(fnw,filename_widget)), XtNcallback,
		     AsciiSourceChanged, (XtPointer) fnw);

    WatchingChanges(fnw) = False;
}

static void 
CollapsePath(in, out)
     char *in, *out;
{
    char *p = in, *q = out, *pend = p + strlen(p);
    
    while (p < pend)
    {
	if (*p != '/')
	{
	    *q++ = *p++;
	}
	else if (p + 1 < pend && *(p + 1) == '/')
	{
	    ++p;
	}
	else if ( (p + 2 == pend && *(p + 1) == '.') || 
		  (p + 2 < pend && *(p + 1) == '.' && *(p + 2) == '/') )
	{
	    p += 2;
	}
	else if ( (p + 3 == pend && *(p + 1) == '.' && *(p + 2) == '.') ||
		 (p + 3 < pend && *(p + 1) == '.'
		                      && *(p + 2) == '.' && *(p + 3) == '/') )
	{
	    while (q > out && *--q != '/')
		;
	    p += 3;
	}
	else
	{
	    *q++ = *p++;
	}
    }
    if (q == out)
    {
	*q++ = '/';
    }

    while (q > out)
    {
	if (*--q != '/')
	    break;
    }
    *++q = '\0';
}

String
FileNominatorGetDirectory(fnw)
     Widget fnw;
{
    if (XtIsSubclass(fnw, fileNominatorWidgetClass))
    {
	return CurrentDir(fnw);
    }
    else
    {
	return NULL;
    }
}

String
FileNominatorGetFileName(fnw)
Widget fnw;
{
    String newName;

    if (XtIsSubclass(fnw, fileNominatorWidgetClass))
    {
    
      FileNominatorStruct data =  Nomination(fnw);

      if (data.directoryStatus == 0)
      {
	newName = XtMalloc(strlen(data.filenamePart) + 1);
	strcpy(newName, data.filenamePart);
	strcpy(actual_path, data.directoryPart);
        return newName;
      }
      else
	return NULL;
    }
    else
    {
	return NULL;
    }
}

String
FileNominatorGetFullFileName(fnw)
     Widget fnw;
{
    String newName;

    if (XtIsSubclass(fnw, fileNominatorWidgetClass))
    {
    
      FileNominatorStruct data =  Nomination(fnw);

      if (data.directoryStatus == 0)
      {
	newName = XtMalloc(strlen(data.directoryPart)
			   + strlen(data.filenamePart) + 1);
	strcpy(newName, data.directoryPart);
	strcat(newName, data.filenamePart);
	strcpy(actual_path, data.directoryPart);
        return newName;
      }
      else
	return NULL;
    }
    else
    {
	return NULL;
    }
}


void
FileNominatorSetDirectory(fnw, dir)
     Widget fnw;
     String dir;
{
    if (!XtIsSubclass(fnw, fileNominatorWidgetClass))
    {
	return;
    }

    /* Should do more checks */
    strcpy(CurrentDir(fnw), dir);
    if (dir[strlen(dir) - 1] != '/')
    {
	strcat(CurrentDir(fnw), "/");
    }

    ChangeDirectory(fnw, 0);
}
