/*
    This file is part of the FElt finite element analysis package.
    Copyright (C) 1993-2000 Jason I. Gobat and Darren C. Atkinson

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

/************************************************************************
 * File:	filedialog.c						*
 *									*
 * Description: This file contains the private and public function and	*
 *		type definitions for the file file dialog box.		*
 ************************************************************************/

# include <stdio.h>
# include <X11/Xos.h>
# include <sys/stat.h>
# include <pwd.h>
# include <dirent.h>
# include <X11/Intrinsic.h>
# include <X11/StringDefs.h>
# include <X11/Shell.h>
# include <X11/Xaw/AsciiText.h>
# include <X11/Xaw/Command.h>
# include <X11/Xaw/Label.h>
# include <X11/Xaw/List.h>
# include <X11/Xaw/Viewport.h>
# include <X11/Xaw/Toggle.h>
# include "Layout.h"
# include "FileDialog.h"
# include "TabGroup.h"
# include "scroll.h"
# include "util.h"

# ifndef X_NOT_STDC_ENV
# include <stdlib.h>
# else
extern char *getenv ( );
# endif

# define MaxEntries 2048
# define MaxPathLen 2048

# define Waiting   0
# define Canceled -1
# define Okayed    1

struct file_dialog {
    Widget	   shell;		/* transientShell  <specified>	  */
    Widget	   layout;		/*	Layout  layout		  */
    Widget	   label;		/*	     Label  label	  */
    Widget	   directory;		/*	     Label  directory	  */
    Widget	   entry;		/*	     AsciiText  entry	  */
    Widget	   viewport;		/*	     Viewport  viewport	  */
    Widget	   list;		/*		  List  list	  */
    Widget         toggle1;		/*	     Toggle  toggle1      */
    Widget         toggle2;		/*	     Toggle  toggle2      */
    Widget         toggle1_name;	/*	     Toggle  toggle1_name */
    Widget         toggle2_name;	/*	     Toggle  toggle2_name */
    Widget	   okay;		/*	     Command  okay	  */
    Widget	   cancel;		/*	     Command  cancel	  */
    String	   path;
    String	   displayed;
    XtCallbackProc callback;
    XtPointer	   client_data;
    int		   status;
};


/* Resources */

static Pixel highlight;

static String dummy_list [ ] = {
    "                              ", "", "", "", "", "", "", "", "", "", NULL
};

static char layout_string [ ] =
"vertical { 4 \
 horizontal { 4 label <+inf -100% *> 4 } 4 \
 horizontal { 4 directory <+inf -100% *> 4 } 4 \
 horizontal { 4 entry <+inf -100% *> 4 } 4 \
 horizontal { 4 viewport <+inf -100% * +inf -100%> 4 } 4 \
 horizontal { 4 toggle1 2 toggle1_name 4 <+inf -100%> \
                toggle2 2 toggle2_name 4 } 4 \
 horizontal { 4 okay 4 <+inf -100%> cancel 4 } 4 }";

static Arg color_args [ ] = {
    {XtNborderColor, (XtArgVal) &highlight},
};

static Arg layout_args [ ] = {
    {XtNlayout, (XtArgVal) NULL},
};

static Arg label_args [ ] = {
    {XtNresize,      (XtArgVal) True},
    {XtNjustify,     (XtArgVal) XtJustifyLeft},
    {XtNborderWidth, (XtArgVal) 0},
};

static Arg directory_args [ ] = {
    {XtNresize,      (XtArgVal) True},
    {XtNjustify,     (XtArgVal) XtJustifyLeft},
    {XtNborderWidth, (XtArgVal) 0},
};

static Arg entry_args [ ] = {
    {XtNresize,      (XtArgVal) XawtextResizeWidth},
    {XtNeditType,    (XtArgVal) XawtextEdit},
    {XtNborderWidth, (XtArgVal) 0},
};

static Arg viewport_args [ ] = {
    {XtNallowVert,   (XtArgVal) True},
    {XtNuseRight,    (XtArgVal) True},
    {XtNforceBars,   (XtArgVal) True},
    {XtNborderWidth, (XtArgVal) 0},
};

static Arg list_args [ ] = {
    {XtNdefaultColumns, (XtArgVal) 1},
    {XtNforceColumns,   (XtArgVal) 1},
    {XtNresize,         (XtArgVal) True},
    {XtNlist,           (XtArgVal) dummy_list},
};

static Arg name_args [ ] = {
    {XtNlabel,       (XtArgVal) ""},
    {XtNborderWidth, (XtArgVal) 0},
};

static Arg toggle_args [ ] = {
    {XtNlabel,	     (XtArgVal) " "},
};

/* Translation tables */

static String entry_table =
"<Key>Return:   FileDialogOkay()\n\
 <Key>Escape:   FileDialogCancel()\n\
 <Btn1Down>:    SetFocus() select-start()";

static XtTranslations entry_translations;

static String viewport_table =
"<Key>Return:   FileDialogOkay()\n\
 <Key>Escape:   FileDialogCancel()\n\
 <Btn1Down>:    SetFocus()";

static XtTranslations viewport_translations;

static String list_table =
"<Btn1Down>(2): Set() Notify() FileDialogOkay()\n\
 <Btn1Down>:    Set() Notify()\n\
 <Btn2Down>:    Set() Notify() FileDialogOkay()";

static XtTranslations list_translations;

static String okay_table =
"<Key>Return:   AutoRepeat(off) set()\n\
 <KeyUp>Return: AutoRepeat(saved) notify() unset()\n\
 <Key>space:    AutoRepeat(off) set()\n\
 <KeyUp>space:  AutoRepeat(saved) notify() unset()\n\
 <Key>Escape:   FileDialogCancel()";

static XtTranslations okay_translations;

static String cancel_table =
"<Key>Return:   AutoRepeat(off) set()\n\
 <KeyUp>Return: AutoRepeat(saved) notify() unset()\n\
 <Key>space:    AutoRepeat(off) set()\n\
 <KeyUp>space:  AutoRepeat(saved) notify() unset()\n\
 <Key>Escape:   FileDialogCancel()";

static XtTranslations cancel_translations;

static String toggle_table = 
"<Key>Return: FileDialogOkay()\n\
 <Key>Escape: FileDialogCancel()\n\
 <Key>space:  toggle() notify()";

static XtTranslations toggle_translations;


/************************************************************************
 * Function:	DisplayPath						*
 *									*
 * Description:	Displays the path of the current directory by possibly	*
 *		shortening the directory at the front so that it fits.	*
 ************************************************************************/

static void DisplayPath (filed, width)
    FileDialog filed;
    Dimension  width;
{
    Arg		 args [1];
    int		 length;
    String	 ptr;
    XFontStruct	*font;


    /* Retrieve the width if necessary. */

    if (width == 0) {
	XtSetArg (args [0], XtNwidth, &width);
	XtGetValues (filed -> directory, args, 1);
    }


    /* Retrieve the font. */

    XtSetArg (args [0], XtNfont, &font);
    XtGetValues (filed -> directory, args, 1);


    /* Prepare to shorten string so that it will fit. */

    length = strlen (filed -> path);
    ptr = filed -> path;

    width -= GetTextWidth (font, "...", 3) + 4;


    /* Keep shortening the string until it fits. */

    while (GetTextWidth (font, ptr, length) > width && length > 1) {
	ptr ++;
	length --;
    }


    /* Display the possibly shortened string. */

    if (ptr != filed -> path)
	sprintf (filed -> displayed, "...%s", ptr);
    else
	strcpy (filed -> displayed, filed -> path);

    XtSetArg (args [0], XtNlabel, filed -> displayed);
    XtSetValues (filed -> directory, args, 1);
}


/************************************************************************
 * Function:	ResizeHandler						*
 *									*
 * Description:	Event handler called upon StructureNotify events.  If	*
 *		the event is specifically a ConfigureNotify event then	*
 *		the path to the current directory is redisplayed within	*
 *		the new window size.  (We subtract 8 to account for the	*
 *		4 pixel space between the label width and the edge of	*
 *		the shell window; we use an event handler rather than a	*
 *		translation so that we can pass the file dialog as	*
 *		client data.						*
 ************************************************************************/

static void ResizeHandler (w, client_data, event, cont)
    Widget    w;
    XtPointer client_data;
    XEvent   *event;
    Boolean  *cont;
{
    if (event -> type == ConfigureNotify)
	DisplayPath ((FileDialog) client_data, event -> xconfigure.width - 8);
}


/************************************************************************
 * Function:	SortEntries						*
 *									*
 * Description:	Used by qsort() to compare two strings; directories	*
 *		(which end with a /) are always placed before files	*
 ************************************************************************/

static int SortEntries (s1, s2)
    String *s1;
    String *s2;
{
    String dir1;
    String dir2;


    dir1 = strchr (*s1, '/');
    dir2 = strchr (*s2, '/');

    if (dir1 != NULL && dir2 == NULL)
	return -1;

    if (dir1 == NULL && dir2 != NULL)
	return 1;

    return strcmp (*s1, *s2);
}


/************************************************************************
 * Function:	ReadDirectory						*
 *									*
 * Description:	Reads the entries of the specified directory, sorts the	*
 *		entries, and updates the associated list widget with	*
 *		the entries.  Directories are placed before files in	*
 *		sorted order and directory names end with a /.  If the	*
 *		directory cannot be opened, False is returned.		*
 *		Otherwise, True is returned.				*
 ************************************************************************/

static Boolean ReadDirectory (filed, name)
    FileDialog filed;
    String     name;
{
    int		   count;
    DIR		  *dirp;
    struct stat	   buf;
    struct dirent *entry;
    static String  list [MaxEntries] = {NULL};
    char	   buffer [MaxPathLen];


    if ((dirp = opendir (name)) == NULL)
	return False;

    count = 0;
    while ((entry = readdir (dirp)) != NULL && count < MaxEntries) {
	if (!strcmp (entry -> d_name, "."))
	    continue;

	XtFree (list [count]);
	sprintf (buffer, "%s/%s", name, entry -> d_name);
	if (!stat (buffer, &buf) && S_ISDIR (buf.st_mode)) {
	    sprintf (buffer, "%s/", entry -> d_name);
	    list [count ++] = XtNewString (buffer);
	} else
	    list [count ++] = XtNewString (entry -> d_name);
    }

    closedir (dirp);
    qsort (list, count, sizeof (String), SortEntries);

    XawListChange (filed -> list, list, count, 0, True);
    XawViewportSetCoordinates (filed -> viewport, 0, 0);
    return True;
}


/************************************************************************
 * Function:	CopySelected						*
 *									*
 * Description:	A callback for the List widget which copies the current	*
 *		entry to the entry widget.				*
 ************************************************************************/

static void CopySelected (w, client_data, call_data)
    Widget    w;
    XtPointer client_data;
    XtPointer call_data;
{
    FileDialog		 filed;
    XawListReturnStruct *info;


    filed = (FileDialog) client_data;
    info = (XawListReturnStruct *) call_data;
    SetTextString (filed -> entry, info -> string);
}


/************************************************************************
 * Function:	Okay							*
 *									*
 * Description:	A callback for the okay button which forms a path name	*
 *		from the directory and current entry.  If the entry	*
 *		is an absolute path (begins with / or ~) then the	*
 *		current directory is ignored.  The path name is then	*
 *		normalized (removing .., //, and ~).  If the path names	*
 *		a file then that file is selected.  Otherwise, the path	*
 *		is used as a directory which is then read.		*
 ************************************************************************/

static void Okay (w, client_data, call_data)
    Widget    w;
    XtPointer client_data;
    XtPointer call_data;
{
    char	   old;
    char	   buffer [MaxPathLen];
    Arg		   args   [1];
    String	   dest;
    String	   string;
    String	   ptr;
    FileDialog	   filed;
    struct passwd *entry;


    filed = (FileDialog) client_data;

    XtSetArg (args [0], XtNstring, &string);
    XtGetValues (filed -> entry, args, 1);


    /* If the entry is absolute ignore the current directory.  Otherwise,
       concatenate the entry and directory to form the new path. */

    if (string [0] == '/' || string [0] == '~')
	strcpy (buffer, string);
    else {
	strcpy (buffer, filed -> path);
	strcat (buffer, string);
    }


    /* Expand the tilde. */

    ptr = buffer;
    dest = filed -> path;

    if (!strncmp (buffer, "~/", 2) || !strcmp (buffer, "~")) {
	strcpy (dest, getenv ("HOME"));
	dest += strlen (dest);
	ptr ++;

    } else if (buffer [0] == '~') {
	while (*ptr && *ptr != '/')
	    ptr ++;

	old = *ptr;
	*ptr = 0;

	if ((entry = getpwnam (buffer + 1)) != NULL) {
	    strcpy (dest, entry -> pw_dir);
	    dest += strlen (dest);
	    *ptr = old;
	} else
	    ptr = buffer;
    }


    /* Finish normalizing the directory entry removing any .. or //. */

    while (*ptr) {
	if (!strncmp (ptr, "//", 2) || !strcmp (ptr, "/"))
	    ptr ++;

	else if (!strncmp (ptr, "/../", 4) || !strcmp (ptr, "/..")) {
	    while (dest != filed -> path && *dest != '/')
		dest --;
	    ptr +=3;

	} else
	    *dest ++ = *ptr ++;

	*dest = 0;
    }

    if (filed -> path [0] == 0)
	strcpy (filed -> path, "/");


    /* Try to read the normalized path as a directory. */

    if (ReadDirectory (filed, filed -> path) == True) {


	/* Indicate the new directory and an empty entry. */

	if (strcmp (filed -> path, "/"))
	    strcat (filed -> path, "/");

	DisplayPath (filed, 0);
        SetTextString (filed -> entry, "");


    /* A file was selected.  If this is the initial suggestion then decompose
       the suggestion into a directory and file name and use them. */

    } else if (call_data != NULL) {
	ptr = strrchr (filed -> path, '/');

        SetTextString (filed -> entry, ptr + 1);

	ptr [1] = 0;
	DisplayPath (filed, 0);
	ReadDirectory (filed, filed -> path);


    /* A file was selected.  Call the callback or set the
       selected flag, whichever is appropriate. */

    } else if (filed -> callback != NULL)
	filed -> callback (filed -> entry, filed -> client_data, filed -> path);

    else
	filed -> status = Okayed;
}

/************************************************************************
 * Function:	CheckToggle						*
 *									*
 * Description:	                     					*
 ************************************************************************/

static void CheckToggle (w, client_data, call_data)
    Widget	w;
    XtPointer   client_data;
    XtPointer   call_data;
{
    FileDialog		filed;
    Boolean		state;
    Arg			args [1];

    filed = (FileDialog) client_data;

    XtSetArg (args [0], XtNstate, &state);
    XtGetValues (w, args, 1);

    if (state) {
       XtSetArg (args [0], XtNstate, False);
       if (w == filed -> toggle1)
          XtSetValues (filed -> toggle2, args, 1);
       else
          XtSetValues (filed -> toggle1, args, 1);
    }
    else {
       XtSetArg (args [0], XtNstate, True);
       XtSetValues (w, args, 1);
    }
}

/************************************************************************
 * Function:	Cancel							*
 *									*
 * Description:	A callback for the cancel button which indicates that	*
 *		the file dialog has been canceled by either calling the	*
 *		registered callback with a NULL string or by setting	*
 *		the canceled flag.					*
 ************************************************************************/

static void Cancel (w, client_data, call_data)
    Widget    w;
    XtPointer client_data;
    XtPointer call_data;
{
    FileDialog filed;


    filed = (FileDialog) client_data;

    if (filed -> callback != NULL)
	filed -> callback (filed -> entry, filed -> client_data, NULL);
    else
	filed -> status = Canceled;
}


/************************************************************************
 * Function:	FileDialogOkay						*
 *									*
 * Description:	An action procedure which emulates pressing of the okay	*
 *		button.							* 
 ************************************************************************/

static void FileDialogOkay (w, event, params, num_params)
    Widget   w;
    XEvent  *event;
    String  *params;
    Cardinal num_params;
{
    if (XtClass (w) == listWidgetClass)
	w = XtParent (w);

    w = XtNameToWidget (XtParent (w), "okay");
    XtCallCallbacks (w, XtNcallback, NULL);
}


/************************************************************************
 * Function:	FileDialogCancel					*
 *									*
 * Description:	An action procedure which emulates pressing of the	*
 *		cancel button.						*
 ************************************************************************/

static void FileDialogCancel (w, event, params, num_params)
    Widget   w;
    XEvent  *event;
    String  *params;
    Cardinal num_params;
{
    if (event -> type == ClientMessage)
	w = XtNameToWidget (w, "layout.cancel");
    else
	w = XtNameToWidget (XtParent (w), "cancel");

    XtCallCallbacks (w, XtNcallback, NULL);
}


/************************************************************************
 * Function:	FileDialogCreate					*
 *									*
 * Description:	Creates and returns a new file dialog box.  The dialog	*
 *		can be popped up using FileDialogSelect() which forces	*
 *		the user to make a selection, or by using FileDialog-	*
 *		Popup() which will call a callback when a file is	*
 *		selected or the dialog is canceled.  The dialog can	*
 *		then be popped down using FileDialogPopdown().		*
 ************************************************************************/

FileDialog FileDialogCreate (parent, name, toggle_labels)
    Widget parent;
    String name;
    char   **toggle_labels;
{
    Arg			args [1];
    Widget		group [6];
    unsigned		group_count;
    unsigned		mask;
    FileDialog		filed;
    static XtAppContext	app_context = NULL;
    static XtActionsRec actions [ ] =
			{{"FileDialogOkay",   FileDialogOkay},
			 {"FileDialogCancel", FileDialogCancel}};


    /* Perform one time initialization. */

    if (app_context == NULL) {
	app_context = XtWidgetToApplicationContext (parent);
	XtAppAddActions (app_context, actions, XtNumber (actions));
	AddAutoRepeatAction (app_context);

	layout_args [0].value = StringToLayout (parent, layout_string);

	entry_translations    = XtParseTranslationTable (entry_table);
	viewport_translations = XtParseTranslationTable (viewport_table);
	list_translations     = XtParseTranslationTable (list_table);
	okay_translations     = XtParseTranslationTable (okay_table);
	cancel_translations   = XtParseTranslationTable (cancel_table);
        toggle_translations   = XtParseTranslationTable (toggle_table);
    }


    /* Create the file dialog and its widgets. */

    filed = XtNew (struct file_dialog);

    filed -> path      = (String) XtMalloc (MaxPathLen);

    filed -> displayed = (String) XtMalloc (MaxPathLen);

    filed -> shell     = XtCreatePopupShell (name,
			 transientShellWidgetClass, parent,
			 NULL, 0);

    filed -> layout    = XtCreateManagedWidget ("layout",
			 layoutWidgetClass, filed -> shell,
			 layout_args, XtNumber (layout_args));

    filed -> label     = XtCreateManagedWidget ("label",
			 labelWidgetClass, filed -> layout,
			 label_args, XtNumber (label_args));

    filed -> directory = XtCreateManagedWidget ("directory",
			 labelWidgetClass, filed -> layout,
			 directory_args, XtNumber (directory_args));

    filed -> entry     = XtCreateManagedWidget ("entry",
			 asciiTextWidgetClass, filed -> layout,
			 entry_args, XtNumber (entry_args));

    filed -> viewport  = XtCreateManagedWidget ("viewport",
			 viewportWidgetClass, filed -> layout, 
			 viewport_args, XtNumber (viewport_args));

    filed -> list      = XtCreateManagedWidget ("list",
			 listWidgetClass, filed -> viewport,
			 list_args, XtNumber (list_args));
 
    filed -> okay      = XtCreateManagedWidget ("okay",
			 commandWidgetClass, filed -> layout,
			 NULL, 0);

    filed -> cancel    = XtCreateManagedWidget ("cancel",
			 commandWidgetClass, filed -> layout,
			 NULL, 0);

    if (toggle_labels != NULL) {
       filed -> toggle1   = XtCreateManagedWidget ("toggle1",
                            toggleWidgetClass, filed -> layout,
                            toggle_args, XtNumber (toggle_args));

       name_args [0].value = (XtArgVal) toggle_labels [0];
       filed -> toggle1_name = XtCreateManagedWidget ("toggle1_name", 
                               labelWidgetClass, filed -> layout, name_args, 
                               XtNumber (name_args));

       filed -> toggle2   = XtCreateManagedWidget ("toggle2",
                            toggleWidgetClass, filed -> layout,
                            toggle_args, XtNumber (toggle_args));

       name_args [0].value = (XtArgVal) toggle_labels [1];
       filed -> toggle2_name = XtCreateManagedWidget ("toggle2_name", 
                               labelWidgetClass, filed -> layout, name_args, 
                               XtNumber (name_args));
    }
    else {
       filed -> toggle1 = NULL;
       filed -> toggle2 = NULL;
    } 


    /* Create a tab group for the file dialog. */

    group_count = 0;

    group [group_count++] = filed -> entry;
    group [group_count++] = filed -> viewport;
    if (toggle_labels != NULL) {
       group [group_count++] = filed -> toggle1;
       group [group_count++] = filed -> toggle2;
    }
    group [group_count++] = filed -> okay;
    group [group_count++] = filed -> cancel;

    XtGetValues (filed -> layout, color_args, XtNumber (color_args));
    CreateTabGroup (filed -> shell, group, group_count, highlight, True);
    XtRealizeWidget (filed -> shell);


    /* Add the translations to each widget. */

    AddDeleteWindowProtocol   (filed -> shell, "FileDialogCancel()");
    ListAddCursorTranslations (filed -> viewport);
    ListAddCursorAccelerators (filed -> viewport, filed -> entry);
    AddScrollableTextTranslations (filed -> entry);

    XtOverrideTranslations (filed -> entry,    entry_translations);
    XtOverrideTranslations (filed -> viewport, viewport_translations);
    XtOverrideTranslations (filed -> okay,     okay_translations);
    XtOverrideTranslations (filed -> cancel,   cancel_translations);

    XtSetArg (args [0], XtNtranslations, list_translations);
    XtSetValues (filed -> list, args, 1);

    mask = StructureNotifyMask;
    XtAddEventHandler (filed -> directory, mask, False, ResizeHandler, filed);



    /* Add the necessary callbacks. */

    XtAddCallback (filed -> list, XtNcallback, CopySelected, (XtPointer) filed);
    XtAddCallback (filed -> cancel, XtNcallback, Cancel, (XtPointer) filed);
    XtAddCallback (filed -> okay, XtNcallback, Okay, (XtPointer) filed);
  
    if (toggle_labels != NULL) {
       XtAddCallback (filed -> toggle1, XtNcallback, 
                      CheckToggle, (XtPointer) filed);
       XtAddCallback (filed -> toggle2, XtNcallback, 
                      CheckToggle, (XtPointer) filed);

       XtOverrideTranslations (filed -> toggle1,  toggle_translations);
       XtOverrideTranslations (filed -> toggle2,  toggle_translations);

       XtSetArg (args [0], XtNstate, True);
       XtSetValues (filed -> toggle1, args, 1);
       XtSetArg (args [0], XtNstate, False);
       XtSetValues (filed -> toggle2, args, 1);
    }

    return filed;
}


/************************************************************************
 * Function:	FileDialogSelect					*
 *									*
 * Description:	Pops up the file dialog with the specified shell title,	*
 *		label, and suggestion and waits until a selection is	*
 *		made or the dialog is canceled; answer will point to	*
 *		the selection or NULL if the dialog was canceled	*
 ************************************************************************/

void FileDialogSelect (filed, title, label, suggestion, answer, toggle)
    FileDialog filed;
    String     title;
    String     label;
    String     suggestion;
    String    *answer;
    String    *toggle;
{
    XEvent       event;
    XtAppContext app_context;


    FileDialogPopup (filed, title, label, suggestion, NULL, NULL);

    filed -> status = Waiting;
    app_context = XtWidgetToApplicationContext (filed -> shell);

    while (filed -> status == Waiting) {
	XtAppNextEvent (app_context, &event);
	XtDispatchEvent (&event);
    }

    FileDialogPopdown (filed);

    if (answer != NULL)
	*answer = filed -> status == Canceled ? NULL : filed -> path;

    if (toggle != NULL && filed -> toggle1 != NULL)
       *toggle = FileDialogToggle (filed);
}

/************************************************************************
 * Function:	FileDialogToggle					*
 *									*
 * Description:	Returns the name of the currently active toggle button.	*
 ************************************************************************/

String FileDialogToggle (filed)
    FileDialog filed;
{
    Arg     args [1];
    Boolean state;
    String  label;


    XtSetArg (args [0], XtNstate, &state); 
    XtGetValues (filed -> toggle1, args, 1);

    XtSetArg (args [0], XtNlabel, &label);

    if (state) 
       XtGetValues (filed -> toggle1_name, args, 1);
    else 
       XtGetValues (filed -> toggle2_name, args, 1);

    return label;
}

/************************************************************************
 * Function:	FileDialogSetToggles					*
 *									*
 * Description:	Sets the labels for the toggle buttons of the specified	*
 *		file dialog.						*
 ************************************************************************/

void FileDialogSetToggles (filed, label1, label2)
    FileDialog filed;
    String     label1;
    String     label2;
{
    Arg args [1];


    if (filed -> toggle1 != NULL && filed -> toggle2 != NULL) {
	XtSetArg (args [0], XtNlabel, label1);
	XtSetValues (filed -> toggle1_name, args, 1);

	XtSetArg (args [0], XtNlabel, label2);
	XtSetValues (filed -> toggle2_name, args, 1);
    }
}

/************************************************************************
 * Function:	FileDialogPopup						*
 *									*
 * Description: Pops up the file dialog with the specified shell title,	*
 *		label, and suggestion.  If the suggestion is NULL then	*
 *		the current working directory is used.  If the callback	*
 *		is not NULL then it will be called upon a selection or	*
 *		cancelation with the specified client data.		*
 ************************************************************************/

void FileDialogPopup (filed, title, label, suggestion, callback, client_data)
    FileDialog	   filed;
    String	   title;
    String	   label;
    String	   suggestion;
    XtCallbackProc callback;
    XtPointer	   client_data;
{
    Arg args [1];


    XtSetArg (args [0], XtNtitle, title);
    XtSetValues (filed -> shell, args, 1);

    XtSetArg (args [0], XtNlabel, label);
    XtSetValues (filed -> label, args, 1);

    if (suggestion == NULL)
	getcwd (filed -> path, MaxPathLen);
    else if (suggestion [0] == '/' || suggestion [0] == '~')
	strcpy (filed -> path, suggestion);
    else {
	getcwd (filed -> path, MaxPathLen);
	strcat (filed -> path, "/");
	strcat (filed -> path, suggestion);
    }

    SetTextString (filed -> entry, filed -> path);

    filed -> callback = callback;
    filed -> client_data = client_data;

    SetFocus (filed -> entry);
    XtCallCallbacks (filed -> okay, XtNcallback, (XtPointer) filed);
    XtPopup (filed -> shell, callback == NULL ? XtGrabExclusive : XtGrabNone);
}


/************************************************************************
 * Function:	FileDialogPopdown					*
 *									*
 * Descripion:	Pops down the specified file dialog			*
 ************************************************************************/

void FileDialogPopdown (filed)
    FileDialog filed;
{
    XtPopdown (filed -> shell);
}


syntax highlighted by Code2HTML, v. 0.9.1