/*****************************************************************************

	FileSelector.c

	This file contains the C code for the FileSelector widget, which
	is intended to be used as a hierarchical Unix file selector box.

******************************************************************************/

/*
 * Author:
 * 	Brian Totty
 * 	Department of Computer Science
 * 	University Of Illinois at Urbana-Champaign
 *	1304 West Springfield Avenue
 * 	Urbana, IL 61801
 * 
 * 	totty@cs.uiuc.edu
 * 	
 */ 

#if defined(AIX) && !defined(__GNUC__) && !defined(_ALL_SOURCE)
# define _ALL_SOURCE
#endif

#include <xite/includes.h>
#include XITE_STDIO_H
#include XITE_MALLOC_H
#include XITE_UNISTD_H
#include <ctype.h>

#include <X11/Xos.h>
#include <X11/Xlib.h>
#include <X11/StringDefs.h>
#include <X11/IntrinsicP.h>
#include <X11/Composite.h>
#include <X11/cursorfont.h>
#include <X11/Xaw/Simple.h>
#include <X11/Xaw/Label.h>
#include <X11/Xaw/Command.h>
#include <Xfwf/Button.h>
#include <X11/Xaw/AsciiText.h>
#include <X11/Xaw/Form.h>

#include <Xfwf/ScrList.h>
#include <Xfwf/FileSelP.h>

#include <xite/utils.h>

#ifdef X11R3
#define asciiTextWidgetClass	asciiStringWidgetClass
#define	XawtextEdit		XttextEdit
#define	XtNtype			"type"
#define	XawAsciiString		NULL
#define	XtNautoFill		"autoFill"
typedef	char *			XtPointer;
#endif

#define	NO_BUSY_GRAB

/*---------------------------------------------------------------------------*

                       D E C L A R A T I O N S

 *---------------------------------------------------------------------------*/

#define	SUPERCLASS		(&compositeClassRec)

#define max(a,b)		((a) > (b) ? (a) : (b))
#define min(a,b)		((a) < (b) ? (a) : (b))
#define XtStrlen(s)		((s) ? strlen(s) : 0)

#define	StrCopy(s)		((char*)strcpy((char *)XtMalloc((strlen(s)+1)*\
					sizeof(char)),((char *)(s))))

#define FSFieldOffset(field)	XtOffset(XfwfFileSelectorWidget,	\
					 fileSelector.field)
#define CoreFieldOffset(field)	XtOffset(Widget,core.field)

/* Remove translations which control multi-line and search
 * behaviour.
 */
static char *text_box_translations =
  "#override\n\
  <Key>Return: no-op()\n\
  Ctrl<Key>J: no-op()\n\
  Ctrl<Key>M: no-op()\n\
  Ctrl<Key>N: no-op()\n\
  Ctrl<Key>O: no-op()\n\
  Ctrl<Key>P: no-op()\n\
  Ctrl<Key>R: no-op()\n\
  Ctrl<Key>S: no-op()\n\
  Ctrl<Key>V: no-op()\n\
  Ctrl<Key>Z: no-op()\n\
  Meta<Key>I: no-op()\n\
  Meta<Key>Q: no-op()\n\
  Meta<Key>V: no-op()\n\
  Meta<Key>Z: no-op()\n\
  <Key>Down: no-op()\n\
  <Key>Up: no-op()\n\
  <Key>Linefeed: no-op()";

/*---------------------------------------------------------------------------*

                   I N T E R N A L    R O U T I N E S

 *---------------------------------------------------------------------------*/

#if (!NeedFunctionPrototypes)

static void		Initialize();
static void		Realize();
static void		Destroy();
static void		Resize();
static Boolean		SetValues();
static XtGeometryResult	GeometryManager();
static void		ChildrenCreate();
static void		ChildrenRealize();
static void		ChildrenRecalculate();
static void		ChildrenUpdate();
static void		ButtonUp();
static void		ButtonOk();
static void		ButtonCancel();
static void		ButtonGoto();
static void		ButtonSelect();
static char *		GetFileBoxText();
static void		ClickOnPathList();
static void		ClickOnFileList();
static void		SelectFileByIndex();
static Boolean		SelectFileByName();
static void		UnselectAll();
static void		NotifySelectionChange();
static void		ParentizeDirectory();
static void		GotoDeepestLegalDirectory();
static void		UpdateLists();
static void		UpdateTextLines();
static void		Chdir();
static void		DoBusyCursor();
static void		UndoBusyCursor();
static void		TextWidgetSetText();

#else

static void		Initialize(Widget request, Widget new);
static void		Realize(Widget gw, XtValueMask *valueMask,
				XSetWindowAttributes *attrs);
static void		Destroy(XfwfFileSelectorWidget fsw);
static void		Resize(Widget gw);
static Boolean		SetValues(Widget gcurrent, Widget grequest,
				Widget gnew);
static XtGeometryResult	GeometryManager(Widget w,
				XtWidgetGeometry *request,
				XtWidgetGeometry *reply);
static void		ChildrenCreate(XfwfFileSelectorWidget fsw);
static void		ChildrenRealize(XfwfFileSelectorWidget fsw);
static void		ChildrenRecalculate(XfwfFileSelectorWidget fsw);
static void		ChildrenUpdate(XfwfFileSelectorWidget fsw);
static void		ButtonUp(Widget w, XfwfFileSelectorWidget fsw,
				XtPointer call_data);
static void		ButtonOk(Widget w, XfwfFileSelectorWidget fsw,
				XtPointer call_data);
static void		ButtonCancel(Widget w, XfwfFileSelectorWidget fsw,
				XtPointer call_data);
static void		ButtonGoto(Widget w, XfwfFileSelectorWidget fsw,
				XtPointer call_data);
static void		ButtonSelect(Widget w, XfwfFileSelectorWidget fsw,
				XtPointer call_data);
static char *		GetFileBoxText(XfwfFileSelectorWidget fsw);
static void		ClickOnPathList(Widget w, XfwfFileSelectorWidget fsw,
				XtPointer call_data);
static void		ClickOnFileList(Widget w, XfwfFileSelectorWidget fsw,
				XtPointer call_data);
static void		SelectFileByIndex(XfwfFileSelectorWidget fsw,
				int index);
static Boolean		SelectFileByName(XfwfFileSelectorWidget fsw,
				char *name);
static void		UnselectAll(XfwfFileSelectorWidget fsw);
static void		NotifySelectionChange(XfwfFileSelectorWidget fsw);
static void		ParentizeDirectory(char *dir);
static void		GotoDeepestLegalDirectory(XfwfFileSelectorWidget fsw);
static void		UpdateLists(XfwfFileSelectorWidget fsw);
static void		UpdateTextLines(XfwfFileSelectorWidget fsw);
static void		Chdir(XfwfFileSelectorWidget fsw);
static void		DoBusyCursor(Widget w);
static void		UndoBusyCursor(Widget w);
static void		TextWidgetSetText(Widget tw, char *text);

#endif

/*---------------------------------------------------------------------------*

              R E S O U R C E    I N I T I A L I Z A T I O N

 *---------------------------------------------------------------------------*/

static XtResource resources[] =
{
	{XtNwidth, XtCWidth, XtRDimension, sizeof(Dimension),
		CoreFieldOffset(width), XtRString, "500"},
	{XtNheight, XtCHeight, XtRDimension, sizeof(Dimension),
		CoreFieldOffset(height), XtRString, "250"},
	{XtNbackground, XtCBackground, XtRPixel, sizeof(Pixel),
		CoreFieldOffset(background_pixel), XtRString, "white"},

	{XtNokButtonCallback, XtCCallback, XtRCallback,
		sizeof(XtCallbackList), FSFieldOffset(ok_button_callbacks),
		XtRCallback, NULL},
	{XtNcancelButtonCallback, XtCCallback, XtRCallback,
		sizeof(XtCallbackList), FSFieldOffset(cancel_button_callbacks),
		XtRCallback, NULL},
	{XtNselectionChangeCallback, XtCCallback, XtRCallback,
		sizeof(XtCallbackList), FSFieldOffset(sel_change_callbacks),
		XtRCallback, NULL},

	{XtNshowOkButton, XtCBoolean, XtRBoolean, sizeof(Boolean),
		FSFieldOffset(show_ok_button), XtRString, "True"},
	{XtNshowCancelButton, XtCBoolean, XtRBoolean, sizeof(Boolean),
		FSFieldOffset(show_cancel_button), XtRString, "True"},
	{XtNflagLinks, XtCBoolean, XtRBoolean, sizeof(Boolean),
		FSFieldOffset(flag_links), XtRString, "False"},
	{XtNcheckExistence, XtCBoolean, XtRBoolean, sizeof(Boolean),
		FSFieldOffset(check_existence), XtRString, "True"},

	{XtNfileSelected, XtCBoolean, XtRBoolean, sizeof(Boolean),
		FSFieldOffset(file_selected), XtRString, "False"},

	{XtNcurrentDirectory, XtCPathname, XtRString, sizeof(String),
		FSFieldOffset(current_directory), XtRString, NULL},
	{XtNcurrentFile, XtCFilename, XtRString, sizeof(String),
		FSFieldOffset(current_file), XtRString, NULL},
	{XtNtitle, XtCLabel, XtRString, sizeof(String),
		FSFieldOffset(title), XtRString, "File Selector"},
	{XtNsortMode, XtCValue, XtRInt, sizeof(int),
		FSFieldOffset(sort_mode), XtRString, "2"},
	{XtNpattern, XtCFile, XtRString, sizeof(String),
		FSFieldOffset(pattern), XtRString, NULL},
};

#undef FSFieldOffset
#undef CoreFieldOffset

/*---------------------------------------------------------------------------*

                    C L A S S    A L L O C A T I O N

 *---------------------------------------------------------------------------*/

XfwfFileSelectorClassRec xfwfFileSelectorClassRec =
{
	{
		/* superclass		*/	(WidgetClass)SUPERCLASS,
		/* class_name		*/	"XfwfFileSelector",
		/* widget_size		*/	sizeof(XfwfFileSelectorRec),
		/* class_initialize	*/	NULL,
		/* class_part_initialize*/	NULL,
		/* class_inited		*/	FALSE,
		/* initialize		*/	(XtInitProc)Initialize,
		/* initialize_hook	*/	NULL,
		/* realize		*/	(XtRealizeProc)Realize,
		/* actions		*/	NULL,
		/* num_actions		*/	0,
		/* resources		*/	resources,
		/* resource_count	*/	XtNumber(resources),
		/* xrm_class		*/	NULLQUARK,
		/* compress_motion	*/	TRUE,
		/* compress_exposure	*/	TRUE,
		/* compress_enterleave	*/	TRUE,
		/* visible_interest	*/	FALSE,
		/* destroy		*/	(XtWidgetProc)Destroy,
		/* resize		*/	(XtWidgetProc)Resize,
		/* expose		*/	XtInheritExpose,
		/* set_values		*/	(XtSetValuesFunc)SetValues,
		/* set_values_hook	*/	NULL,
		/* set_values_almost	*/	XtInheritSetValuesAlmost,
		/* get_values_hook	*/	NULL,
		/* accept_focus		*/	NULL,
		/* version		*/	XtVersion,
		/* callback_private	*/	NULL,
		/* tm_table		*/	NULL,
		/* query_geometry       */	XtInheritQueryGeometry,
		/* display_accelerator  */	XtInheritDisplayAccelerator,
		/* extension            */	NULL
	}, /* Core Part */
	{
		/* geometry_manager     */	GeometryManager,
		/* change_managed       */	XtInheritChangeManaged,
		/* insert_child         */	XtInheritInsertChild,
		/* delete_child         */	XtInheritDeleteChild,
		/* extension            */	NULL
	}, /* Composite Part */		
	{
		/* no extra class data  */	0
	}  /* FileSelector Part */
};

WidgetClass xfwfFileSelectorWidgetClass =
	(WidgetClass)&xfwfFileSelectorClassRec;

/*---------------------------------------------------------------------------*

                    E X P O R T E D    M E T H O D S

 *---------------------------------------------------------------------------*/


/*---------------------------------------------------------------------------*

	Initialize()

	This procedure is called by the X toolkit to initialize
	the widget instance.  The hook to this routine is in the
	initialize part of the core part of the class.

 *---------------------------------------------------------------------------*/

/* ARGSUSED */
#ifndef FUNCPROTO
static void Initialize(request,new)
Widget request,new;
#else /* FUNCPROTO */
static void Initialize(Widget request, Widget new)
#endif /* FUNCPROTO */
{
	int i;
	XfwfFileSelectorWidget fsw;
	char *str,*initial_file,path[MAXPATHLEN + 2], *dir = NULL;
	static char *star = "*";

	fsw = (XfwfFileSelectorWidget)new;
	FSHandCursor(fsw) = XCreateFontCursor(XtDisplay(fsw),XC_hand1);
	FSBusyCursor(fsw) = XCreateFontCursor(XtDisplay(fsw),XC_watch);

	for (i = 0; i < FS_NUM_CHILDREN; i++) FSNthWidget(fsw,i) = NULL;

	FSDirMgr(fsw) = NULL;
	FSPathList(fsw) = NULL;
	FSPathListCount(fsw) = 0;
	FSFileList(fsw) = NULL;
	FSFileListCount(fsw) = 0;

	str = (char *)XtCalloc((MAXPATHLEN + 2),sizeof(char));
	if (FSCurrentDirectory(fsw) != NULL)	/* User Specified Path */
	{
	        dir = tilde_expand(FSCurrentDirectory(fsw));
		if (dir) strcpy(str, dir);
	}
	    else
	{
		getwd(path);
		if (path) strcpy(str,path);
	}
	FSCurrentDirectory(fsw) = str;

	str = (char *)XtCalloc((MAXPATHLEN + 2),sizeof(char));
	initial_file = FSCurrentFile(fsw);
	FSCurrentFile(fsw) = str;

	if (FSPattern(fsw) == NULL) FSPattern(fsw) = star;
	FSPattern(fsw) = StrCopy(FSPattern(fsw));

	if (FSCorePart(request)->width <= 0) FSCorePart(new)->width = 500;
	if (FSCorePart(request)->height <= 0) FSCorePart(new)->height = 200;

	ChildrenCreate(fsw);
	GotoDeepestLegalDirectory(fsw);
	if (initial_file) SelectFileByName(fsw,initial_file);
} /* End Initialize */


/*---------------------------------------------------------------------------*

	Realize()

	This function is called to realize a FileSelector widget.

 *---------------------------------------------------------------------------*/

#ifndef FUNCPROTO
static void Realize(gw,valueMask,attrs)
Widget gw;
XtValueMask *valueMask;
XSetWindowAttributes *attrs;
#else /* FUNCPROTO */
static void Realize(Widget gw, XtValueMask *valueMask, XSetWindowAttributes *attrs)
#endif /* FUNCPROTO */
{
	XfwfFileSelectorWidget fsw;

	fsw = (XfwfFileSelectorWidget)gw;
	XtCreateWindow(gw,InputOutput,(Visual *)CopyFromParent,
		*valueMask,attrs);

	ChildrenRealize(fsw);
	ChildrenUpdate(fsw);

	Resize(gw);
	NotifySelectionChange(fsw);
} /* End Realize */



/*---------------------------------------------------------------------------*

	Destroy()

	This function is called to destroy a fileSelector widget.

 *---------------------------------------------------------------------------*/

#ifndef FUNCPROTO
static void Destroy(fsw)
XfwfFileSelectorWidget fsw;
#else /* FUNCPROTO */
static void Destroy(XfwfFileSelectorWidget fsw)
#endif /* FUNCPROTO */
{
	int i;

	XtFree(FSCurrentDirectory(fsw));
	XtFree(FSCurrentFile(fsw));
	XtFree(FSPattern(fsw));

	if (FSPathList(fsw) != NULL)
	{
		for (i = 0; i < FSPathListCount(fsw); i++)
			free(FSPathList(fsw)[i]);
		free(FSPathList(fsw));
	}
	if (FSFileList(fsw) != NULL)
	{
		for (i = 0; i < FSFileListCount(fsw); i++)
			free(FSFileList(fsw)[i]);
		free(FSFileList(fsw));
	}
} /* End Destroy */



/*---------------------------------------------------------------------------*

	Resize()

	This function is called to resize a fileSelector widget.

 *---------------------------------------------------------------------------*/

#ifndef FUNCPROTO
static void Resize(gw) 
Widget gw;
#else /* FUNCPROTO */
static void Resize(Widget gw)
#endif /* FUNCPROTO */
{
	XfwfFileSelectorWidget w;

	w = (XfwfFileSelectorWidget)gw;
	ChildrenUpdate(w);
} /* End Resize */


/*---------------------------------------------------------------------------*

	SetValues(gcurrent,grequest,gnew)

	This function is the external interface for setting resources.

 *---------------------------------------------------------------------------*/

/* ARGSUSED */
#ifndef FUNCPROTO
static Boolean SetValues(gcurrent,grequest,gnew)
Widget gcurrent,grequest,gnew;
#else /* FUNCPROTO */
static Boolean SetValues(Widget gcurrent, Widget grequest, Widget gnew)
#endif /* FUNCPROTO */
{
	XfwfFileSelectorWidget current,new;

	current = (XfwfFileSelectorWidget)gcurrent;
	new = (XfwfFileSelectorWidget)gnew;

	if (FSPattern(current) != FSPattern(new))
	{
		XtFree(FSPattern(current));
		FSPattern(new) = StrCopy(FSPattern(current));
		Chdir(new);
	}
	if (FSSortMode(current) != FSSortMode(new))
	{
		FSSortMode(new) = FSSortMode(current);
	}
	if (FSTitle(current) != FSTitle(new))
	{
		XtWarning("Title Change Not Supported");
		FSTitle(new) = FSTitle(current);
	}
	if (FSFileSelected(current) != FSFileSelected(new))
	{
		XtWarning("Can't Change XtNfileSelected Resource");
		FSFileSelected(new) = FSFileSelected(current);
	}
	if (FSCheckExistence(current) != FSCheckExistence(new))
	{
		if (!FSCheckExistence(new))
		{
			XtSetSensitive(FSNthWidget(new,FS_I_OK_BUTTON),True);
		}
		    else
		{
			Chdir(new);
		}			    
	}
	if (FSShowOkButton(current) != FSShowOkButton(new))
	{
		if (FSShowOkButton(new) == True)
			XtManageChild(FSNthWidget(new,FS_I_OK_BUTTON));
		    else
			XtUnmanageChild(FSNthWidget(new,FS_I_OK_BUTTON));
	}
	if (FSShowCancelButton(current) != FSShowCancelButton(new))
	{
		if (FSShowCancelButton(new) == True)
			XtManageChild(FSNthWidget(new,FS_I_CANCEL_BUTTON));
		    else
			XtUnmanageChild(FSNthWidget(new,FS_I_CANCEL_BUTTON));
	}
	if (FSCurrentDirectory(current) != FSCurrentDirectory(new))
	{
		strcpy(FSCurrentDirectory(current),FSCurrentDirectory(new));
		FSCurrentDirectory(new) = FSCurrentDirectory(current);
		Chdir(new);
	}
	if (FSCurrentFile(current) != FSCurrentFile(new))
	{
		char *new_name;

		new_name = FSCurrentFile(new);
		FSCurrentFile(new) = FSCurrentFile(current);
		SelectFileByName(new,new_name);
	}
	return(False);
} /* End SetValues */



/*---------------------------------------------------------------------------*

	GeometryManager(w,request,reply)

	This routine acts as the geometry_manager method for the
	FileSelector widget.  It is called when a child wants to
	resize/reposition itself.

	Currently, we allow all requests.

 *---------------------------------------------------------------------------*/

/* ARGSUSED */
#ifndef FUNCPROTO
static XtGeometryResult GeometryManager(w,request,reply)
Widget w;
XtWidgetGeometry *request;
XtWidgetGeometry *reply;
#else /* FUNCPROTO */
static XtGeometryResult GeometryManager(Widget w, XtWidgetGeometry *request, XtWidgetGeometry *reply)
#endif /* FUNCPROTO */
{
	return(XtGeometryYes);
} /* End GeometryManager */

/*---------------------------------------------------------------------------*

                     L O C A L    R O U T I N E S

 *---------------------------------------------------------------------------*/

/*---------------------------------------------------------------------------*

	ChildrenCreate(fsw)

	This routine creates the initial child widgets for the
	file selector widget and places them in the widget fsw.
	No placement or resizing is done.  That is done by
	ChildrenUpdate().


 *---------------------------------------------------------------------------*/

#ifndef FUNCPROTO
static void ChildrenCreate(fsw)
XfwfFileSelectorWidget fsw;
#else /* FUNCPROTO */
static void ChildrenCreate(XfwfFileSelectorWidget fsw)
#endif /* FUNCPROTO */
{
	Arg args[10];
	static char *dummy_string_list[] = { NULL };

		/* Title */

	XtSetArg(args[0],XtNlabel,FSTitle(fsw));
	XtSetArg(args[1],XtNborderWidth,0);
	XtSetArg(args[2],XtNresizable,True);
	FSNthWidget(fsw,FS_I_TITLE) = XtCreateManagedWidget("title",
		labelWidgetClass,(Widget)fsw,args,3);

		/* Current Directory Line */

	XtSetArg(args[0],XtNtype,XawAsciiString);
	XtSetArg(args[1],XtNeditType,XawtextEdit);
	XtSetArg(args[2],XtNstring,"");
	XtSetArg(args[3],XtNautoFill,True);
	XtSetArg(args[4],XtNtranslations,
		 XtParseTranslationTable(text_box_translations));
	FSNthWidget(fsw,FS_I_CUR_DIR_TEXT) =
	  XtCreateManagedWidget("cur_dir_text",
				asciiTextWidgetClass,(Widget)fsw,args,5);

		/* Current File Line */

	XtSetArg(args[0],XtNtype,XawAsciiString);
	XtSetArg(args[1],XtNeditType,XawtextEdit);
	XtSetArg(args[2],XtNstring,"");
	XtSetArg(args[3],XtNautoFill,True);
	XtSetArg(args[4],XtNtranslations,
		 XtParseTranslationTable(text_box_translations));
	FSNthWidget(fsw,FS_I_CUR_FILE_TEXT) =
		XtCreateManagedWidget("cur_file_text",
				      asciiTextWidgetClass,(Widget)fsw,args,5);

		/* Path From Root Title */

	XtSetArg(args[0],XtNlabel,"Path From Root");
	XtSetArg(args[1],XtNborderWidth,0);
	FSNthWidget(fsw,FS_I_PATH_LIST_TITLE) =
		XtCreateManagedWidget("path_list_title",
				      labelWidgetClass,(Widget)fsw,args,2);

		/* Directory List */

	XtSetArg(args[0],XtNlist,dummy_string_list);
	XtSetArg(args[1],XtNnumberStrings,0);
	FSNthWidget(fsw,FS_I_PATH_LIST) =
		XtCreateManagedWidget("path_list",
			xfwfScrolledListWidgetClass,(Widget)fsw,args,2);
	XtAddCallback(FSNthWidget(fsw,FS_I_PATH_LIST),
		      XtNcallback,(XtCallbackProc)ClickOnPathList,
		      (XtPointer)fsw);

		/* File List Title */

	XtSetArg(args[0],XtNlabel,"Directory Contents");
	XtSetArg(args[1],XtNborderWidth,0);
	FSNthWidget(fsw,FS_I_FILE_LIST_TITLE) =
		XtCreateManagedWidget("file_list_title",
				      labelWidgetClass,(Widget)fsw,args,2);

		/* File List */

	XtSetArg(args[0],XtNlist,dummy_string_list);
	XtSetArg(args[1],XtNnumberStrings,0);
	FSNthWidget(fsw,FS_I_FILE_LIST) =
		XtCreateManagedWidget("file_list",
			xfwfScrolledListWidgetClass,(Widget)fsw,args,2);
	XtAddCallback(FSNthWidget(fsw,FS_I_FILE_LIST),
		      XtNcallback,(XtCallbackProc)ClickOnFileList,
		      (XtPointer)fsw);

		/* Goto Button */

	XtSetArg(args[0],XtNlabel,"Goto");
/*	XtSetArg(args[1],XtNborderWidth,2); */
	FSNthWidget(fsw,FS_I_GOTO_BUTTON) =
	  XtCreateManagedWidget("goto_button",
				xfwfButtonWidgetClass,(Widget)fsw,args,1);
	XtAddCallback(FSNthWidget(fsw,FS_I_GOTO_BUTTON),
		      XtNactivate,(XtCallbackProc)ButtonGoto,
		      (XtPointer)fsw);

		/* Select Button */

	XtSetArg(args[0],XtNlabel,"Select");
/*	XtSetArg(args[1],XtNborderWidth,2); */
	FSNthWidget(fsw,FS_I_SELECT_BUTTON) =
	  XtCreateManagedWidget("select_button",
				xfwfButtonWidgetClass,(Widget)fsw,args,1);
	XtAddCallback(FSNthWidget(fsw,FS_I_SELECT_BUTTON),
		      XtNactivate,(XtCallbackProc)ButtonSelect,
		      (XtPointer)fsw);
	XtAddCallback(FSNthWidget(fsw,FS_I_SELECT_BUTTON),
		      XtNactivate,(XtCallbackProc)ButtonOk,
		      (XtPointer)fsw);

		/* Up Button */

	XtSetArg(args[0],XtNlabel,"Up");
/*	XtSetArg(args[1],XtNborderWidth,2); */
	FSNthWidget(fsw,FS_I_UP_BUTTON) =
	  XtCreateManagedWidget("up_button",
				xfwfButtonWidgetClass,(Widget)fsw,args,1);
	XtAddCallback(FSNthWidget(fsw,FS_I_UP_BUTTON),
		      XtNactivate,(XtCallbackProc)ButtonUp,
		      (XtPointer)fsw);

		/* OK Button */

	XtSetArg(args[0],XtNlabel,"OK");
/*	XtSetArg(args[1],XtNborderWidth,2); */
	FSNthWidget(fsw,FS_I_OK_BUTTON) =
	  XtCreateManagedWidget("ok_button",
				xfwfButtonWidgetClass,(Widget)fsw,args,1);
	XtAddCallback(FSNthWidget(fsw,FS_I_OK_BUTTON),
		      XtNactivate,(XtCallbackProc)ButtonOk,
		      (XtPointer)fsw);

		/* Cancel Button */

	XtSetArg(args[0],XtNlabel,"Cancel");
/*	XtSetArg(args[1],XtNborderWidth,2); */
	FSNthWidget(fsw,FS_I_CANCEL_BUTTON) =
	  XtCreateManagedWidget("cancel_button",
				xfwfButtonWidgetClass,(Widget)fsw,args,1);
	XtAddCallback(FSNthWidget(fsw,FS_I_CANCEL_BUTTON),
		      XtNactivate,(XtCallbackProc)ButtonCancel,
		      (XtPointer)fsw);

	XtInstallAccelerators(FSNthWidget(fsw,FS_I_CUR_DIR_TEXT),
			      FSNthWidget(fsw,FS_I_GOTO_BUTTON));
	XtInstallAccelerators(FSNthWidget(fsw,FS_I_CUR_FILE_TEXT),
			      FSNthWidget(fsw,FS_I_SELECT_BUTTON));
	XtInstallAccelerators(FSNthWidget(fsw,FS_I_FILE_LIST),
			      FSNthWidget(fsw,FS_I_SELECT_BUTTON));


} /* End ChildrenCreate */	


/*---------------------------------------------------------------------------*

	ChildrenRealize(fsw)

	This routine realizes the child widgets.  The widgets must
	already have been created and initialized.  Their coordinates
	should already have been set.

 *---------------------------------------------------------------------------*/

#ifndef FUNCPROTO
static void ChildrenRealize(fsw)
XfwfFileSelectorWidget fsw;
#else /* FUNCPROTO */
static void ChildrenRealize(XfwfFileSelectorWidget fsw)
#endif /* FUNCPROTO */
{
	int i;
	Widget widget;

	for (i = 0; i < FS_NUM_CHILDREN; i++)
	{
		if (FSNthWidget(fsw,i) != NULL)
		{
			widget = FSNthWidget(fsw,i);
			XtRealizeWidget(widget);
			
			if ((i == FS_I_OK_BUTTON &&
			     !FSShowOkButton(fsw)) ||
			    (i == FS_I_CANCEL_BUTTON &&
			     !FSShowCancelButton(fsw)))
			{
				XtUnmanageChild(widget);
			}
		}
	}
} /* End ChildrenRealize */


/*---------------------------------------------------------------------------*

        ChildrenRecalculate(fsw)

	This routine takes a file selector widget and recalculates
	the coordinates and sizes of the constituent components
	based on the current size of the file selector.

	This function does not actually change the child widgets, it
	just calculates the coordinates and caches the coordinates in
	the FileSelector widget.

 *---------------------------------------------------------------------------*/

#ifndef FUNCPROTO
static void ChildrenRecalculate(fsw)
XfwfFileSelectorWidget fsw;
#else /* FUNCPROTO */
static void ChildrenRecalculate(XfwfFileSelectorWidget fsw)
#endif /* FUNCPROTO */
{
	BOX *coords;
	Widget widget;
	int i,w,h,empty_space,gap,orig_path_list_h,orig_file_list_h,top;
	XtWidgetGeometry parent_idea,child_idea;

	w = FSCorePart(fsw)->width;
	h = FSCorePart(fsw)->height;

	/* Get The Child Widgets Current Widths And Heights */

	for (i = 0; i < FS_NUM_CHILDREN; i++)
	{
		if (FSNthWidget(fsw,i) != NULL)
		{
			widget = FSNthWidget(fsw,i);
			coords = FSNthCoords(fsw,i);
			BoxW(coords) = CoreWidth(widget);
			BoxH(coords) = CoreHeight(widget);
		}
	}

		/* Adjust Widths */

	BoxW(FSNthCoords(fsw,FS_I_CUR_DIR_TEXT)) = .7 * w;
	BoxW(FSNthCoords(fsw,FS_I_CUR_FILE_TEXT)) = .7 * w;
	BoxW(FSNthCoords(fsw,FS_I_GOTO_BUTTON)) = .15 * w;
	BoxW(FSNthCoords(fsw,FS_I_SELECT_BUTTON)) = .15 * w;

	BoxW(FSNthCoords(fsw,FS_I_PATH_LIST)) = .45 * w;
	BoxW(FSNthCoords(fsw,FS_I_FILE_LIST)) = .45 * w;
	BoxW(FSNthCoords(fsw,FS_I_UP_BUTTON)) =
		BoxW(FSNthCoords(fsw,FS_I_PATH_LIST)) * .35;
	BoxW(FSNthCoords(fsw,FS_I_OK_BUTTON)) =
		BoxW(FSNthCoords(fsw,FS_I_FILE_LIST)) * .35;
	BoxW(FSNthCoords(fsw,FS_I_CANCEL_BUTTON)) =
		BoxW(FSNthCoords(fsw,FS_I_FILE_LIST)) * .35;

		/* Adjust Heights */

	BoxH(FSNthCoords(fsw,FS_I_CUR_DIR_TEXT)) =
		max(BoxH(FSNthCoords(fsw,FS_I_CUR_DIR_TEXT)),
		    BoxH(FSNthCoords(fsw,FS_I_GOTO_BUTTON)));
	BoxH(FSNthCoords(fsw,FS_I_GOTO_BUTTON)) =
		BoxH(FSNthCoords(fsw,FS_I_CUR_DIR_TEXT));

	BoxH(FSNthCoords(fsw,FS_I_CUR_FILE_TEXT)) =
		max(BoxH(FSNthCoords(fsw,FS_I_CUR_FILE_TEXT)),
		    BoxH(FSNthCoords(fsw,FS_I_SELECT_BUTTON)));
	BoxH(FSNthCoords(fsw,FS_I_SELECT_BUTTON)) =
		BoxH(FSNthCoords(fsw,FS_I_CUR_FILE_TEXT));

	empty_space = h - (BoxH(FSNthCoords(fsw,FS_I_TITLE)) +
			   BoxH(FSNthCoords(fsw,FS_I_CUR_DIR_TEXT)) +
			   BoxH(FSNthCoords(fsw,FS_I_CUR_FILE_TEXT)) +
			   BoxH(FSNthCoords(fsw,FS_I_PATH_LIST_TITLE)) +
			   BoxH(FSNthCoords(fsw,FS_I_OK_BUTTON)));
	gap = .025 * h;
	BoxH(FSNthCoords(fsw,FS_I_PATH_LIST)) = empty_space - 8 * gap;
	BoxH(FSNthCoords(fsw,FS_I_FILE_LIST)) = empty_space - 8 * gap;
	orig_path_list_h = BoxH(FSNthCoords(fsw,FS_I_PATH_LIST));
	orig_file_list_h = BoxH(FSNthCoords(fsw,FS_I_FILE_LIST));

		/* Listen To Child Height Requests For Lists */

	parent_idea.request_mode = CWWidth | CWHeight;
	parent_idea.width = BoxW(FSNthCoords(fsw,FS_I_PATH_LIST));
	parent_idea.height = BoxH(FSNthCoords(fsw,FS_I_PATH_LIST));
	XtQueryGeometry(FSNthWidget(fsw,FS_I_PATH_LIST),
			&parent_idea,&child_idea);
	if ((child_idea.request_mode & CWHeight) &&
	    (child_idea.height < parent_idea.height))
	{
		BoxH(FSNthCoords(fsw,FS_I_PATH_LIST)) = child_idea.height;
	}

	parent_idea.request_mode = CWWidth | CWHeight;
	parent_idea.width = BoxW(FSNthCoords(fsw,FS_I_FILE_LIST));
	parent_idea.height = BoxH(FSNthCoords(fsw,FS_I_FILE_LIST));
	XtQueryGeometry(FSNthWidget(fsw,FS_I_FILE_LIST),
			&parent_idea,&child_idea);
	if ((child_idea.request_mode & CWHeight) &&
	    (child_idea.height < parent_idea.height))
	{
		BoxH(FSNthCoords(fsw,FS_I_FILE_LIST)) = child_idea.height;
	}

		/* Make Sure Both Lists Have Same Minimum Height */

	BoxH(FSNthCoords(fsw,FS_I_PATH_LIST)) =
		min(BoxH(FSNthCoords(fsw,FS_I_PATH_LIST)),
		    BoxH(FSNthCoords(fsw,FS_I_FILE_LIST)));
	BoxH(FSNthCoords(fsw,FS_I_FILE_LIST)) =
		min(BoxH(FSNthCoords(fsw,FS_I_PATH_LIST)),
		    BoxH(FSNthCoords(fsw,FS_I_FILE_LIST)));

		/* Vertical Positions */

	BoxY(FSNthCoords(fsw,FS_I_TITLE)) = gap;

	BoxY(FSNthCoords(fsw,FS_I_CUR_DIR_TEXT)) =
		(BoxY(FSNthCoords(fsw,FS_I_TITLE)) +
		 BoxH(FSNthCoords(fsw,FS_I_TITLE))) + gap;
	BoxY(FSNthCoords(fsw,FS_I_GOTO_BUTTON)) =
		BoxY(FSNthCoords(fsw,FS_I_CUR_DIR_TEXT));

	BoxY(FSNthCoords(fsw,FS_I_CUR_FILE_TEXT)) =
		(BoxY(FSNthCoords(fsw,FS_I_CUR_DIR_TEXT)) +
		 BoxH(FSNthCoords(fsw,FS_I_CUR_DIR_TEXT))) + gap;
	BoxY(FSNthCoords(fsw,FS_I_SELECT_BUTTON)) =
		BoxY(FSNthCoords(fsw,FS_I_CUR_FILE_TEXT));

	BoxY(FSNthCoords(fsw,FS_I_PATH_LIST_TITLE)) =
		(BoxY(FSNthCoords(fsw,FS_I_CUR_FILE_TEXT)) +
		 BoxH(FSNthCoords(fsw,FS_I_CUR_FILE_TEXT))) + gap;
	BoxY(FSNthCoords(fsw,FS_I_FILE_LIST_TITLE)) =
		BoxY(FSNthCoords(fsw,FS_I_PATH_LIST_TITLE));

	BoxY(FSNthCoords(fsw,FS_I_PATH_LIST)) =
		(BoxY(FSNthCoords(fsw,FS_I_PATH_LIST_TITLE)) +
		 BoxH(FSNthCoords(fsw,FS_I_PATH_LIST_TITLE))) + gap +
		(orig_path_list_h - BoxH(FSNthCoords(fsw,FS_I_PATH_LIST))) / 2;
	BoxY(FSNthCoords(fsw,FS_I_FILE_LIST)) =
		BoxY(FSNthCoords(fsw,FS_I_PATH_LIST));

	top = BoxY(FSNthCoords(fsw,FS_I_PATH_LIST)) +
		BoxH(FSNthCoords(fsw,FS_I_PATH_LIST));
	empty_space = h - top;
	BoxY(FSNthCoords(fsw,FS_I_UP_BUTTON)) = top +
		(h - top - BoxH(FSNthCoords(fsw,FS_I_UP_BUTTON))) / 2;
	BoxY(FSNthCoords(fsw,FS_I_OK_BUTTON)) = top +
		(h - top - BoxH(FSNthCoords(fsw,FS_I_OK_BUTTON))) / 2;
	BoxY(FSNthCoords(fsw,FS_I_CANCEL_BUTTON)) = top +
		(h - top - BoxH(FSNthCoords(fsw,FS_I_CANCEL_BUTTON))) / 2;

		/* Horizontal Positions */

	BoxX(FSNthCoords(fsw,FS_I_TITLE)) =
		(w - BoxW(FSNthCoords(fsw,FS_I_TITLE))) / 2;

	empty_space = w - (BoxW(FSNthCoords(fsw,FS_I_CUR_DIR_TEXT)) +
			   BoxW(FSNthCoords(fsw,FS_I_GOTO_BUTTON)));
	gap = empty_space / 3;
	BoxX(FSNthCoords(fsw,FS_I_CUR_DIR_TEXT)) = gap;
	BoxX(FSNthCoords(fsw,FS_I_GOTO_BUTTON)) =
		(BoxX(FSNthCoords(fsw,FS_I_CUR_DIR_TEXT)) +
		 BoxW(FSNthCoords(fsw,FS_I_CUR_DIR_TEXT))) + gap;

	empty_space = w - (BoxW(FSNthCoords(fsw,FS_I_CUR_FILE_TEXT)) +
			   BoxW(FSNthCoords(fsw,FS_I_SELECT_BUTTON)));
	gap = empty_space / 3;
	BoxX(FSNthCoords(fsw,FS_I_CUR_FILE_TEXT)) = gap;
	BoxX(FSNthCoords(fsw,FS_I_SELECT_BUTTON)) =
		(BoxX(FSNthCoords(fsw,FS_I_CUR_FILE_TEXT)) +
		 BoxW(FSNthCoords(fsw,FS_I_CUR_FILE_TEXT))) + gap;

	empty_space = w - (BoxW(FSNthCoords(fsw,FS_I_PATH_LIST)) +
			   BoxW(FSNthCoords(fsw,FS_I_FILE_LIST)));
	gap = empty_space / 3;
	BoxX(FSNthCoords(fsw,FS_I_PATH_LIST)) = gap;
	BoxX(FSNthCoords(fsw,FS_I_FILE_LIST)) =
		(BoxX(FSNthCoords(fsw,FS_I_PATH_LIST)) +
		 BoxW(FSNthCoords(fsw,FS_I_PATH_LIST))) + gap;

	BoxX(FSNthCoords(fsw,FS_I_PATH_LIST_TITLE)) =
		(BoxX(FSNthCoords(fsw,FS_I_PATH_LIST)) +
		 (BoxW(FSNthCoords(fsw,FS_I_PATH_LIST)) -
		  BoxW(FSNthCoords(fsw,FS_I_PATH_LIST_TITLE))) / 2);

	BoxX(FSNthCoords(fsw,FS_I_FILE_LIST_TITLE)) =
		(BoxX(FSNthCoords(fsw,FS_I_FILE_LIST)) +
		 (BoxW(FSNthCoords(fsw,FS_I_FILE_LIST)) -
		  BoxW(FSNthCoords(fsw,FS_I_FILE_LIST_TITLE))) / 2);

	empty_space = BoxW(FSNthCoords(fsw,FS_I_PATH_LIST)) -
		BoxW(FSNthCoords(fsw,FS_I_UP_BUTTON));
	gap = empty_space / 2;
	BoxX(FSNthCoords(fsw,FS_I_UP_BUTTON)) =
		BoxX(FSNthCoords(fsw,FS_I_PATH_LIST)) + gap;

	empty_space = BoxW(FSNthCoords(fsw,FS_I_FILE_LIST)) -
		(BoxW(FSNthCoords(fsw,FS_I_OK_BUTTON)) +
		 BoxW(FSNthCoords(fsw,FS_I_CANCEL_BUTTON)));
	gap = empty_space / 3;
	BoxX(FSNthCoords(fsw,FS_I_OK_BUTTON)) =
		BoxX(FSNthCoords(fsw,FS_I_FILE_LIST)) + gap;
	BoxX(FSNthCoords(fsw,FS_I_CANCEL_BUTTON)) =
		(BoxX(FSNthCoords(fsw,FS_I_OK_BUTTON)) +
		 BoxW(FSNthCoords(fsw,FS_I_OK_BUTTON))) + gap;
} /* End ChildrenRecalculate */


/*---------------------------------------------------------------------------*

	ChildrenUpdate(fsw)

	This routine takes a File Selector widget <fsw> and updates
	the child widgets by recalculating their coordinates and
	setting the appropriate resources.   

 *---------------------------------------------------------------------------*/

#ifndef FUNCPROTO
static void ChildrenUpdate(fsw)
XfwfFileSelectorWidget fsw;
#else /* FUNCPROTO */
static void ChildrenUpdate(XfwfFileSelectorWidget fsw)
#endif /* FUNCPROTO */
{
	int i;
	Widget widget;
	BOX *coords;
	Boolean not_at_root;
	XfwfScrolledListWidget file_list;

	ChildrenRecalculate(fsw);

	for (i = 0; i < FS_NUM_CHILDREN; i++)
	{
		if (FSNthWidget(fsw,i) != NULL)
		{
			widget = FSNthWidget(fsw,i);
			coords = FSNthCoords(fsw,i);
			XtMoveWidget(widget,BoxX(coords),BoxY(coords));
			XtResizeWidget(widget,BoxW(coords),BoxH(coords),
				       CoreBorderWidth(widget));
		}
	}

	file_list = (XfwfScrolledListWidget)
		FSNthWidget(fsw,FS_I_FILE_LIST);

	if (FSPathListCount(fsw) != 1)
		not_at_root = True;
	    else
		not_at_root = False;

/*	XtSetSensitive(FSNthWidget(fsw,FS_I_UP_BUTTON),not_at_root); */
	XtSetSensitive(FSNthWidget(fsw,FS_I_UP_BUTTON), True);
} /* End ChildrenUpdate */


/*---------------------------------------------------------------------------*

                 I N T E R N A L    C A L L B A C K S

 *---------------------------------------------------------------------------*/

#ifndef FUNCPROTO
static void ButtonUp(w,fsw,call_data)
Widget w;
XfwfFileSelectorWidget fsw;
XtPointer call_data;
#else /* FUNCPROTO */
static void ButtonUp(Widget w, XfwfFileSelectorWidget fsw, XtPointer call_data)
#endif /* FUNCPROTO */
{
	strcat(FSCurrentDirectory(fsw),"..");
	Chdir(fsw);
} /* End ButtonUp */


#ifndef FUNCPROTO
static void ButtonOk(w,fsw,call_data)
Widget w;
XfwfFileSelectorWidget fsw;
XtPointer call_data;
#else /* FUNCPROTO */
static void ButtonOk(Widget w, XfwfFileSelectorWidget fsw, XtPointer call_data)
#endif /* FUNCPROTO */
{
	XfwfFileSelectorOkButtonReturnStruct *ret;

	if (FSCheckExistence(fsw) && !FSFileSelected(fsw)) return;

	ret = XtNew(XfwfFileSelectorOkButtonReturnStruct);
	
	ret->path = FSCurrentDirectory(fsw);
	ret->file = FSCurrentFile(fsw);
	ret->file_box_text = GetFileBoxText(fsw);
	XtCallCallbacks((Widget)fsw,XtNokButtonCallback, (XtPointer) ret);
	
} /* End ButtonOk */


#ifndef FUNCPROTO
static void ButtonCancel(w,fsw,call_data)
Widget w;
XfwfFileSelectorWidget fsw;
XtPointer call_data;
#else /* FUNCPROTO */
static void ButtonCancel(Widget w, XfwfFileSelectorWidget fsw, XtPointer call_data)
#endif /* FUNCPROTO */
{
	XtCallCallbacks((Widget)fsw,XtNcancelButtonCallback,NULL);
} /* End ButtonCancel */


#ifndef FUNCPROTO
static void ButtonGoto(w,fsw,call_data)
Widget w;
XfwfFileSelectorWidget fsw;
XtPointer call_data;
#else /* FUNCPROTO */
static void ButtonGoto(Widget w, XfwfFileSelectorWidget fsw, XtPointer call_data)
#endif /* FUNCPROTO */
{
	Widget cur_dir_text;
	Arg args[10];
	String path;
	char *dir = NULL;

	XtSetArg(args[0],XtNstring,(XtArgVal)(&path));
	cur_dir_text = FSNthWidget(fsw,FS_I_CUR_DIR_TEXT);
	XtGetValues(cur_dir_text,args,1);
	if (path) dir = tilde_expand(path);
	if (dir) strcpy(FSCurrentDirectory(fsw), dir);
	Chdir(fsw);
} /* End ButtonGoto */


#ifndef FUNCPROTO
static void ButtonSelect(w,fsw,call_data)
Widget w;
XfwfFileSelectorWidget fsw;
XtPointer call_data;
#else /* FUNCPROTO */
static void ButtonSelect(Widget w, XfwfFileSelectorWidget fsw, XtPointer call_data)
#endif /* FUNCPROTO */
{
	String file;

	if (FSCheckExistence(fsw)) {
		file = GetFileBoxText(fsw);
		if (SelectFileByName(fsw,file) == False)
		  XBell(XtDisplay(fsw),0);
		UpdateTextLines(fsw);
	}

} /* End ButtonSelect */


#ifndef FUNCPROTO
static char *GetFileBoxText(fsw)
XfwfFileSelectorWidget fsw;
#else /* FUNCPROTO */
static char *GetFileBoxText(XfwfFileSelectorWidget fsw)
#endif /* FUNCPROTO */
{
	char *text;
	Arg args[1];
	Widget file_box_widget;

	XtSetArg(args[0],XtNstring,(XtArgVal)(&text));
	file_box_widget = FSNthWidget(fsw,FS_I_CUR_FILE_TEXT);
	XtGetValues(file_box_widget,args,1);
	return(text);
} /* End GetFileBoxText */


#ifndef FUNCPROTO
static void ClickOnPathList(w,fsw,call_data)
Widget w;
XfwfFileSelectorWidget fsw;
XtPointer call_data;
#else /* FUNCPROTO */
static void ClickOnPathList(Widget w, XfwfFileSelectorWidget fsw, XtPointer call_data)
#endif /* FUNCPROTO */
{
	int i;
	XfwfScrolledListReturnStruct *ret;
	
	ret = (XfwfScrolledListReturnStruct *)call_data;
	if (ret->index == -1)
	{
		UnselectAll(fsw);		/* Click On Blank Space */
		return;
	}
	strcpy(FSCurrentDirectory(fsw),"/");
	for (i = 1; i <= ret->index; i++)
	{
		strcat(FSCurrentDirectory(fsw),FSPathList(fsw)[i]);
		strcat(FSCurrentDirectory(fsw),"/");
	}
	Chdir(fsw);
} /* End ClickOnPathList */


#ifndef FUNCPROTO
static void ClickOnFileList(w,fsw,call_data)
Widget w;
XfwfFileSelectorWidget fsw;
XtPointer call_data;
#else /* FUNCPROTO */
static void ClickOnFileList(Widget w, XfwfFileSelectorWidget fsw, XtPointer call_data)
#endif /* FUNCPROTO */
{
	XfwfScrolledListReturnStruct *ret;

	ret = (XfwfScrolledListReturnStruct *)call_data;
	if (ret->index == -1)
		UnselectAll(fsw);		/* Click On Blank Space */
	else {
	        SelectFileByIndex(fsw,ret->index);
		if (ret->multiple != 0)
		  ButtonOk(w, fsw, call_data);
	      }
} /* End ClickOnFileList */


/*---------------------------------------------------------------------------*

             I N T E R N A L    S U P P O R T    R O U T I N E S

 *---------------------------------------------------------------------------*/

#ifndef FUNCPROTO
static void SelectFileByIndex(fsw,index)
XfwfFileSelectorWidget fsw;
int index;
#else /* FUNCPROTO */
static void SelectFileByIndex(XfwfFileSelectorWidget fsw, int index)
#endif /* FUNCPROTO */
{
	DirEntry *dir_entry;

	DirectoryMgrGotoItem(FSDirMgr(fsw),index);
	dir_entry = DirectoryMgrCurrentEntry(FSDirMgr(fsw));
	if (dir_entry == NULL)
	{
		fprintf(stderr,"SelectFileByIndex: Entry %d invalid\n",index);
		exit(-1);
	}
	if (DirEntryIsDir(dir_entry) || DirEntryIsDirectoryLink(dir_entry))
	{
		strcat(FSCurrentDirectory(fsw),DirEntryFileName(dir_entry));
		Chdir(fsw);
	}
	    else if (!DirEntryIsBrokenLink(dir_entry))	/* File */
	{
		strcpy(FSCurrentFile(fsw),DirEntryFileName(dir_entry));
		if (FSCheckExistence(fsw))
		{
			XtSetSensitive(FSNthWidget(fsw,FS_I_OK_BUTTON),True);
		}
		FSFileSelected(fsw) = True;
		TextWidgetSetText(FSNthWidget(fsw,FS_I_CUR_FILE_TEXT),
				  FSCurrentFile(fsw));
		XfwfScrolledListHighlightItem(FSNthWidget(fsw,FS_I_FILE_LIST),
					  index);
		NotifySelectionChange(fsw);
	}
	    else					/* Broken Link */
	{
		XBell(XtDisplay(fsw),0);
		UnselectAll(fsw);
	}
} /* End SelectFileByIndex */


#ifndef FUNCPROTO
static Boolean SelectFileByName(fsw,name)
XfwfFileSelectorWidget fsw;
char *name;
#else /* FUNCPROTO */
static Boolean SelectFileByName(XfwfFileSelectorWidget fsw, char *name)
#endif /* FUNCPROTO */
{
	if (DirectoryMgrGotoNamedItem(FSDirMgr(fsw),name) == FALSE)
	{
		return(False);
	}
	SelectFileByIndex(fsw,DirectoryMgrCurrentIndex(FSDirMgr(fsw)));
	return(True);
} /* End SelectFileByName */


#ifndef FUNCPROTO
static void UnselectAll(fsw)
XfwfFileSelectorWidget fsw;
#else /* FUNCPROTO */
static void UnselectAll(XfwfFileSelectorWidget fsw)
#endif /* FUNCPROTO */
{
	Boolean old_file_selected_flag;

	old_file_selected_flag = FSFileSelected(fsw);
	if (FSCheckExistence(fsw))
	{
/*		XtSetSensitive(FSNthWidget(fsw,FS_I_OK_BUTTON),False); */
		XtSetSensitive(FSNthWidget(fsw,FS_I_OK_BUTTON),True);
	}
	FSCurrentFile(fsw)[0] = '\0';
	FSFileSelected(fsw) = False;
	TextWidgetSetText(FSNthWidget(fsw,FS_I_CUR_FILE_TEXT),
			  FSCurrentFile(fsw));
	XfwfScrolledListUnhighlightAll(FSNthWidget(fsw,FS_I_FILE_LIST));
	if (old_file_selected_flag) NotifySelectionChange(fsw);
} /* End UnselectAll */


#ifndef FUNCPROTO
static void NotifySelectionChange(fsw)
XfwfFileSelectorWidget fsw;
#else /* FUNCPROTO */
static void NotifySelectionChange(XfwfFileSelectorWidget fsw)
#endif /* FUNCPROTO */
{
	XfwfFileSelectorSelectionChangeReturnStruct ret;

	if (FSFileSelected(fsw) == True)
	{
		ret.file_selected = True;
		ret.path = FSCurrentDirectory(fsw);
		ret.file = FSCurrentFile(fsw);
	}
	    else
	{
		ret.file_selected = False;
		ret.path = NULL;
		ret.file = NULL;
	}
	XtCallCallbacks((Widget)fsw,XtNselectionChangeCallback,
			(XtPointer)&ret);
} /* End NotifySelectionChange */


/*---------------------------------------------------------------------------*

	ParentizeDirectory(dir)

	This routine takes a directory pathname <dir> and modifies the
	pathname to strip off the deepest directory, leaving just the parent
	path, and the trailing slash.  The parent directory of the root
	directory (a single slash) is the root directory.

 *---------------------------------------------------------------------------*/

#ifndef FUNCPROTO
static char *strip_blanks(t)
char *t;
#else /* FUNCPROTO */
static char *strip_blanks(char *t)
#endif /* FUNCPROTO */
{
  int len;
  char *t2, *t3;

  if (!t) return(NULL);
  if ((len = strlen(t)) == 0) return(NULL);

  t2 = t;
  while (*t2 != '\0' && isspace(*t2++)) ;
  if (*t2 == '\0') return(NULL);

  t3 = t + strlen(t) - 1;

  while (t3 >= t && isspace(*t3)) *t3-- = '\0';

  if (*t == '\0') return(NULL);

  return(t);

} /* strip_blanks() */

#ifndef FUNCPROTO
static void ParentizeDirectory(dir)
char *dir;
#else /* FUNCPROTO */
static void ParentizeDirectory(char *dir)
#endif /* FUNCPROTO */
{
	char *end_of_dir;

	if (dir == NULL) return;

	end_of_dir = &dir[strlen(dir)-1];

	/* end_of_dir now refers to the last non-NULL character. */

	if ((*end_of_dir == '/') && (end_of_dir != dir))
	        -- end_of_dir;

	while ((*end_of_dir != '/') && (end_of_dir != dir))
	{
		-- end_of_dir;
	}
	++ end_of_dir;
	*end_of_dir = '\0';
} /* End ParentizeDirectory */


/*---------------------------------------------------------------------------*

	GotoDeepestLegalDirectory(fsw)

	This function takes a FileSelector widget <fsw> and modifies the
	directory string in FSCurrentDirectory(fsw) to be the deepest
	legal directory above the string.  Partial or incorrect directory
	names are stripped starting at the end.

	This routine takes a File Selector widget <fsw> and updates
	the child widgets by recalculating their coordinates and
	setting the appropriate resources.  The old directory manager
	is closed and a new one is opened for this directory.  The
	file and path lists are deallocated, reallocated, and loaded
	with the path and file data lists.  The lists are then reset
	in the scrolled list widgets.

	This routine requires that all the child widgets have already
	been created.

 *---------------------------------------------------------------------------*/

#ifndef FUNCPROTO
static void GotoDeepestLegalDirectory(fsw)
XfwfFileSelectorWidget fsw;
#else /* FUNCPROTO */
static void GotoDeepestLegalDirectory(XfwfFileSelectorWidget fsw)
#endif /* FUNCPROTO */
{
	char dir[MAXPATHLEN + 2], *t;

	strcpy(dir,FSCurrentDirectory(fsw));
	t = strip_blanks(dir);
	if (!t) (void) strcpy(dir, ".");
	
	while (1)
	{
		if (!DirectoryMgrCanOpen(dir))
		{
			XBell(XtDisplay(fsw),0);
			ParentizeDirectory(dir);
			if (strcmp(dir,"/") == 0)
			{
				fprintf(stderr,"%s: Can't open root\n",
					"GotoDeepestLegalDirectory");
				exit(1);
			}
		}
		    else
		{
			DirectoryPathExpand(dir,FSCurrentDirectory(fsw));
			if (!DirectoryMgrCanOpen(dir))
				strcpy(FSCurrentDirectory(fsw),dir);
			break;
		}
	}
	UnselectAll(fsw);
	UpdateLists(fsw);
} /* End GotoDeepestLegalDirectory */


/*---------------------------------------------------------------------------*

	UpdateLists(fsw)

	This routine takes a FileSelector widget and builds new path
	and file lists from the current directory, sort mode, and pattern
	strings.  The lists and text items are then changed.

 *---------------------------------------------------------------------------*/

#ifndef FUNCPROTO
static void UpdateLists(fsw)
XfwfFileSelectorWidget fsw;
#else /* FUNCPROTO */
static void UpdateLists(XfwfFileSelectorWidget fsw)
#endif /* FUNCPROTO */
{
	int i,count;
	char *dir,*start;
	DirEntry *dir_entry;
	DirectoryMgr *dir_mgr;
	char temp[MAXPATHLEN + 2];

	if (FSDirMgr(fsw)) DirectoryMgrClose(FSDirMgr(fsw));
	dir_mgr = DirectoryMgrSimpleOpen(FSCurrentDirectory(fsw),
					 FSSortMode(fsw),
					 FSPattern(fsw));
	if (dir_mgr == NULL)
	{
		fprintf(stderr,"UpdateLists: Can't read directory '%s'\n",
			FSCurrentDirectory(fsw));
		exit(1);
	}
		
	FSDirMgr(fsw) = dir_mgr;
	if (FSPathList(fsw) != NULL)
	{
		for (i = 0; i < FSPathListCount(fsw); i++)
			free(FSPathList(fsw)[i]);
		free(FSPathList(fsw));
	}
	if (FSFileList(fsw) != NULL)
	{
		for (i = 0; i < FSFileListCount(fsw); i++)
			free(FSFileList(fsw)[i]);
		free(FSFileList(fsw));
	}

	FSFileListCount(fsw) = DirectoryMgrFilteredCount(FSDirMgr(fsw));
	FSPathListCount(fsw) = 1;
	for (dir = FSCurrentDirectory(fsw) + 1; *dir != '\0'; dir++)
	{
		if (*dir == '/') ++ FSPathListCount(fsw);
	}

	FSFileList(fsw) = (char **)malloc(sizeof(char *) *
					  (FSFileListCount(fsw) + 1));
	if (FSFileList(fsw) == NULL)
	{
		fprintf(stderr,"UpdateLists: Out of memory\n");
		exit(-1);
	}

	for (i = 0; i < FSFileListCount(fsw); i++)
	{
		dir_entry = DirectoryMgrNextEntry(FSDirMgr(fsw));
		if (dir_entry == NULL) XtError("Inconsistent Directory");
		strcpy(temp,DirEntryFileName(dir_entry));
		if (DirEntryIsDir(dir_entry))
			strcat(temp,"/");
		    else if (DirEntryIsBrokenLink(dir_entry))
			strcat(temp," X");
		    else if (DirEntryIsDirectoryLink(dir_entry))
			strcat(temp,"/");
		    else if (DirEntryIsSymLink(dir_entry) && FSFlagLinks(fsw))
			strcat(temp," @");

		FSFileList(fsw)[i] = StrCopy(temp);
	}
	FSFileList(fsw)[i] = NULL;

	FSPathList(fsw) = (char **)malloc(sizeof(char *) *
					  (FSPathListCount(fsw) + 1));
	start = FSCurrentDirectory(fsw);
	FSPathList(fsw)[0] = StrCopy("/");
	for (i = 1; i < FSPathListCount(fsw); i++)
	{
		while (*start != '\0' && *start == '/') ++start;
		count = 0;
		while (*start != '\0' && *start != '/')
			temp[count++] = *start++;
		temp[count++] = '\0';
		FSPathList(fsw)[i] = StrCopy(temp);
	}
	FSPathList(fsw)[i] = NULL;

	XfwfScrolledListSetList(FSNthWidget(fsw,FS_I_PATH_LIST),
			    FSPathList(fsw),FSPathListCount(fsw),True,NULL);
	XfwfScrolledListSetList(FSNthWidget(fsw,FS_I_FILE_LIST),
			    FSFileList(fsw),FSFileListCount(fsw),True,NULL);
	UpdateTextLines(fsw);
} /* End UpdateLists */


#ifndef FUNCPROTO
static void UpdateTextLines(fsw)
XfwfFileSelectorWidget fsw;
#else /* FUNCPROTO */
static void UpdateTextLines(XfwfFileSelectorWidget fsw)
#endif /* FUNCPROTO */
{
	TextWidgetSetText(FSNthWidget(fsw,FS_I_CUR_DIR_TEXT),
			  FSCurrentDirectory(fsw));
	TextWidgetSetText(FSNthWidget(fsw,FS_I_CUR_FILE_TEXT),
			  FSCurrentFile(fsw));
} /* End UpdateTextLines */


#ifndef FUNCPROTO
static void Chdir(fsw)
XfwfFileSelectorWidget fsw;
#else /* FUNCPROTO */
static void Chdir(XfwfFileSelectorWidget fsw)
#endif /* FUNCPROTO */
{
	DoBusyCursor((Widget)fsw);
	GotoDeepestLegalDirectory(fsw);
	ChildrenUpdate(fsw);
	UndoBusyCursor((Widget)fsw);
} /* End Chdir */


#ifndef FUNCPROTO
static void DoBusyCursor(w)
Widget w;
#else /* FUNCPROTO */
static void DoBusyCursor(Widget w)
#endif /* FUNCPROTO */
{
#ifndef NO_BUSY_GRAB
	if (XtIsRealized(w))
	{
		XGrabPointer(XtDisplay(w),XtWindow(w),True,None,
			     GrabModeSync,GrabModeSync,None,FSBusyCursor(w),
			     CurrentTime);
	}
#endif
} /* End DoBusyCursor */


#ifndef FUNCPROTO
static void UndoBusyCursor(w)
Widget w;
#else /* FUNCPROTO */
static void UndoBusyCursor(Widget w)
#endif /* FUNCPROTO */
{
#ifndef NO_BUSY_GRAB
	if (XtIsRealized(w))
	{
		XUngrabPointer(XtDisplay(w),CurrentTime);
	}
#endif
} /* End UndoBusyCursor */


#ifndef FUNCPROTO
static void TextWidgetSetText(tw,text)
Widget tw;
char *text;
#else /* FUNCPROTO */
static void TextWidgetSetText(Widget tw, char *text)
#endif /* FUNCPROTO */
{
	Arg args[3];
	int length,insert_position;
#ifdef X11R3
	static char text_widget_storage[MAXPATHLEN + 2];
#endif

	length = strlen(text);
	insert_position = max(length,0);
#ifdef X11R3
	/*
	XtTextSetInsertionPoint(tw,insert_position);
	XtSetArg(args[0],XtNstring,text);
	XtSetValues(tw,args,1);
	{
		XtTextBlock block;

		block.firstPos = 0;
		block.length = length + 1;
		block.ptr = text;
		block.format = FMT8BIT;
		XtTextReplace(fsw,0,block.length - 1,&block);
	}
	*/
#else
	XtSetArg(args[0],XtNstring,text);
	XtSetValues(tw,args,1);
	XawTextSetInsertionPoint(tw,insert_position);
#endif
} /* End TextWidgetSetText */

/*---------------------------------------------------------------------------*

                    E X T E R N A L    R O U T I N E S

 *---------------------------------------------------------------------------*/

#ifndef FUNCPROTO
void XfwfFileSelectorChangeDirectory(fsw,dir)
XfwfFileSelectorWidget fsw;
char *dir;
#else /* FUNCPROTO */
void XfwfFileSelectorChangeDirectory(XfwfFileSelectorWidget fsw, char *dir)
#endif /* FUNCPROTO */
{
	strcpy(FSCurrentDirectory(fsw),dir);
	Chdir(fsw);
} /* End XfwfFileSelectorChangeDirectory */


#ifndef FUNCPROTO
void XfwfFileSelectorRefresh(fsw)
XfwfFileSelectorWidget fsw;
#else /* FUNCPROTO */
void XfwfFileSelectorRefresh(XfwfFileSelectorWidget fsw)
#endif /* FUNCPROTO */
{
	XfwfFileSelectorChangeDirectory(fsw,".");
} /* End XfwfFileSelectorRefresh */


#ifndef FUNCPROTO
void XfwfFileSelectorGetStatus(fsw,ssp)
XfwfFileSelectorWidget fsw;
XfwfFileSelectorStatusStruct *ssp;
#else /* FUNCPROTO */
void XfwfFileSelectorGetStatus(XfwfFileSelectorWidget fsw, XfwfFileSelectorStatusStruct *ssp)
#endif /* FUNCPROTO */
{	
	ssp->file_selected = FSFileSelected(fsw);
	ssp->path = FSCurrentDirectory(fsw);
	ssp->file = FSCurrentFile(fsw);
	ssp->file_box_text = GetFileBoxText(fsw);
} /* End XfwfFileSelectorGetStatus */
