~maddevelopers/mg5amcnlo/2.9.4

« back to all changes in this revision

Viewing changes to vendor/StdHEP/mcfio/src/getfiles.c

pass to v2.0.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/******************************************************************************
 
2
*                                                                             *
 
3
* Getfiles.c -- File Interface Routines                                       *
 
4
*                                                                             *
 
5
* Copyright (c) 1993 Universities Research Association, Inc.                  *
 
6
* All Rights Reserved.                                                        *
 
7
*                                                                             *
 
8
* This material resulted from work developed under a Government Contract and  *
 
9
* is subject to the following license:  The Government retains a paid-up,     *
 
10
* nonexclusive, irrevocable worldwide license to reproduce, prepare derivative*
 
11
* works, perform publicly and display publicly by or for the Government,      *
 
12
* including the right to distribute to other Government contractors.  Neither *
 
13
* the United States nor the United States Department of Energy, nor any of    *
 
14
* their employees, makes any warranty, express or implied, or assumes any     *
 
15
* legal liability or responsibility for the accuracy, completeness, or        *
 
16
* usefulness of any information, apparatus, product, or process disclosed, or *
 
17
* represents that its use would not infringe privately owned rights.          *
 
18
*                                                                             *
 
19
* Fermilab Nirvana GUI Library                                                *
 
20
* May 23, 1991                                                                *
 
21
*                                                                             *
 
22
* Written by Donna Reid                                                       *
 
23
*                                                                             *
 
24
* modified 11/5/91 by JMK: integrated changes made by M. Edel; updated for    *
 
25
*                          destroy widget problem (took out ManageModalDialog *
 
26
*                          call; added comments.                              *
 
27
*          10/1/92 by MWE: Added help dialog and fixed a few bugs             *
 
28
*           4/7/93 by DR:  Port to VMS                                        *
 
29
*           6/1/93 by JMK: Integrate Port and changes by MWE to make          *
 
30
*                          directories "sticky" and a fix to prevent opening  *
 
31
*                          a directory when no filename was specified         *
 
32
*          6/24/92 by MWE: Made filename list and directory list typeable,    *
 
33
*                          set initial focus to filename list                 *
 
34
*          6/25/93 by JMK: Fix memory leaks found by Purify.                  *
 
35
*                                                                             *
 
36
* Included are two routines written using Motif for accessing files:          *
 
37
*                                                                             *
 
38
* GetExistingFilename  presents a FileSelectionBox dialog where users can     *
 
39
*                      choose an existing file to open.                       *
 
40
*                                                                             *
 
41
* GetNewFilename       presents a FileSelectionBox dialog to help the user    *
 
42
*                      find a place for a new file.                           *
 
43
*                                                                             *
 
44
******************************************************************************/
 
45
static char SCCSID[] = "@(#)getfiles.c  1.33    2/5/96";
 
46
#include <stdio.h>
 
47
#include <stdlib.h>
 
48
#include <string.h>
 
49
#include <ctype.h>
 
50
#ifdef VMS
 
51
#include <unixio.h>
 
52
#include <file.h>
 
53
#include "VMSparam.h"
 
54
#else
 
55
#include <unistd.h>
 
56
#include <fcntl.h>
 
57
#include <dirent.h>
 
58
#include <sys/param.h>
 
59
#endif /*VMS*/
 
60
#include <sys/types.h>
 
61
#include <sys/stat.h>
 
62
#include <sys/errno.h>
 
63
#include <X11/keysym.h>
 
64
#include <Xm/Xm.h>
 
65
#include <Xm/PushBG.h>
 
66
#include <Xm/FileSB.h>
 
67
#include <Xm/Form.h>
 
68
#include <Xm/Text.h>
 
69
#include <Xm/MessageB.h>
 
70
#include <Xm/List.h>
 
71
#include "fileUtils.h"
 
72
#include "misc.h"
 
73
#include "getfiles.h"
 
74
 
 
75
#define MAX_ARGS 20                     /* Maximum number of X arguments */
 
76
#define PERMS 0666                      /* UNIX file permission, RW for owner,
 
77
                                           group, world */
 
78
#define MAX_LIST_KEYSTROKES 100         /* Max # of keys user can type to 
 
79
                                           a file list */
 
80
#define MAX_LIST_KESTROKE_WAIT 2000     /* Allowable delay in milliseconds
 
81
                                           between characters typed to a list
 
82
                                           before starting over (throwing
 
83
                                           out the accumulated characters */
 
84
 
 
85
#define SET_ONE_RSRC(widget, name, newValue) \
 
86
{ \
 
87
    static Arg args[1] = {{name, (XtArgVal)0}}; \
 
88
    args[0].value = (XtArgVal)newValue; \
 
89
    XtSetValues(widget, args, 1); \
 
90
}       
 
91
 
 
92
enum yesNoValues {ynNone, ynYes, ynNo};
 
93
 
 
94
/* Saved default directory and pattern from last successful call */
 
95
static XmString DefaultDirectory = NULL;
 
96
static XmString DefaultPattern = NULL;
 
97
 
 
98
/* User settable option for leaving the file name text field in
 
99
   GetExistingFilename dialogs.  Off by default so new users will get
 
100
   used to typing in the list rather than in the text field */
 
101
static int RemoveRedundantTextField = True;
 
102
 
 
103
/* Text for help button help display */
 
104
/* ... needs variant for VMS */
 
105
#ifndef MOTIF10
 
106
static char *HelpExist =
 
107
"The file open dialog shows a list of directories on the left, and a list \
 
108
of files on the right.  Double clicking on a file name in the list on the \
 
109
right, or selecting it and pressing the OK button, will open that file.  \
 
110
Double clicking on a directory name, or selecting \
 
111
it and pressing \"Filter\", will move into that directory.  To move upwards in \
 
112
the directory tree, double click on the directory entry ending in \"..\".  \
 
113
You can also begin typing a file name to select from the file list, or \
 
114
directly type in directory and file specifications in the \
 
115
field labeled \"Filter\".\n\
 
116
\n\
 
117
If you use the filter field, remember to include \
 
118
either a file name, \"*\" is acceptable, or a trailing \"/\".  If \
 
119
you don't, the name after the last \"/\" is interpreted as the file name to \
 
120
match.  When you leave off the file name or trailing \"/\", you won't see \
 
121
any files to open in the list \
 
122
because the filter specification matched the directory file itself, rather \
 
123
than the files in the directory.";
 
124
static char *HelpNew = 
 
125
"This dialog allows you to create a new file, or to save the current file \
 
126
under a new name.  To specify a file \
 
127
name in the current directory, complete the name displayed in the \"Save File \
 
128
As:\" field near the bottom of the dialog.  If you delete or change \
 
129
the path shown in the field, the file will be saved using whatever path \
 
130
you type, provided that it is a valid Unix file specification.\n\
 
131
\n\
 
132
To replace an existing file, select it from the Files list \
 
133
and press \"OK\", or simply double click on the name.\n\
 
134
\n\
 
135
To save a file in another directory, use the Directories list \
 
136
to move around in the file system hierarchy.  Double clicking on \
 
137
directory names in the list, or selecting them and pressing the \
 
138
\"Filter\" button will select that directory.  To move upwards \
 
139
in the directory tree, double \
 
140
click on the directory entry ending in \"..\".  You can also move directly \
 
141
to a directory by typing the file specification of the path in the \"Filter\" \
 
142
field and pressing the \"Filter\" button.";
 
143
 
 
144
#else /* MOTIF 1.0 file dialogs are different */
 
145
static char *HelpExist =
 
146
"The file open dialog initially shows a list of files in the current \
 
147
directory.  Double clicking on a file name in the list, or selecting it \
 
148
and pressing the OK button, will open that file.  \n\
 
149
\n\
 
150
To open a file outside of the current directory, click on the field labeled \
 
151
\"Filter\", type in a directory specification, and press return, or \
 
152
press the Filter button in the dialog.  The files in that directory will \
 
153
then show up in the file list and you can select one as above.";
 
154
static char *HelpNew = 
 
155
"The Save As... dialog allows you to save the file you are editing under a \
 
156
new name, or to specify the name for an Untitled file.  To specify a file \
 
157
name in the current directory, complete the name displayed in the \"Save File \
 
158
As:\" field near the bottom of the dialog.  If you delete or change \
 
159
the path shown in the field, the file will be saved using whatever path \
 
160
you type provided that it is a valid Unix file specification.\n\
 
161
\n\
 
162
To replace an existing file, select it from the Files list \
 
163
and press \"OK\", or simply double click on the name.\n\
 
164
\n\
 
165
If you would like to save a file in another directory, either type the \
 
166
full name in the \"Save File As:\" field, or type the directory name \
 
167
in the \"File Filter\" field and press the \
 
168
\"Filter\" button to see that directory in the file list.";
 
169
#endif
 
170
 
 
171
/*                    Local Callback Routines and variables                */
 
172
 
 
173
static void newFileOKCB(Widget w, Boolean *client_data,
 
174
               XmFileSelectionBoxCallbackStruct *call_data);
 
175
static void newFileCancelCB(Widget w, Boolean *client_data, caddr_t 
 
176
               call_data);
 
177
static void newHelpCB(Widget w, Widget helpPanel, caddr_t call_data);
 
178
static void createYesNoDialog(Widget parent);
 
179
static void createErrorDialog(Widget parent);
 
180
static int doYesNoDialog(char *msg);
 
181
static void doErrorDialog(char *errorString, char *filename);
 
182
static void existOkCB(Widget w, Boolean * client_data,
 
183
               XmFileSelectionBoxCallbackStruct *call_data);
 
184
static void existCancelCB(Widget w, Boolean * client_data, caddr_t call_data);
 
185
static void existHelpCB(Widget w, Widget helpPanel, caddr_t call_data);
 
186
static void errorOKCB(Widget w, caddr_t client_data, caddr_t call_data);
 
187
static void yesNoOKCB(Widget w, caddr_t client_data, caddr_t call_data);
 
188
static void yesNoCancelCB(Widget w, caddr_t client_data, caddr_t call_data);
 
189
static Widget createPanelHelp(Widget parent, char *text, char *title);
 
190
static void helpDismissCB(Widget w, Widget helpPanel, caddr_t call_data);
 
191
static void makeListTypeable(Widget listW);
 
192
static void listCharCB(Widget w, XEvent *event, String *params, Cardinal n);
 
193
static void replacementDirSearchProc(Widget w, XtPointer searchData);
 
194
static void replacementFileSearchProc(Widget w, XtPointer searchData);
 
195
static void sortWidgetList(Widget listWidget);
 
196
static int compareXmStrings(const void *string1, const void *string2);
 
197
 
 
198
static int  SelectResult = GFN_CANCEL;  /*  Initialize results as cancel   */
 
199
static Widget YesNoDialog;              /* "Overwrite?" dialog widget      */
 
200
static int YesNoResult;                 /* Result of overwrite dialog      */
 
201
static Widget ErrorDialog;              /* Dialog widget for error msgs    */
 
202
static int ErrorDone;                   /* Flag to mark dialog completed   */
 
203
#ifdef AIX
 
204
static void (*OrigDirSearchProc)();
 
205
static void (*OrigFileSearchProc)();
 
206
#else
 
207
static XmSearchProc OrigDirSearchProc;  /* Built in Motif directory search */
 
208
static XmSearchProc OrigFileSearchProc; /* Built in Motif file search proc */
 
209
#endif          
 
210
 
 
211
/*  GetExistingFilename                                                    */
 
212
/*                                                                         */
 
213
/*  This routine will popup a file selection box so that the user can      */
 
214
/*  select an existing file from the scrollable list.  The user is         */
 
215
/*  prevented from entering a new filename because the edittable text      */
 
216
/*  area of the file selection box widget is unmanaged.  After the user    */
 
217
/*  selects a file, GetExistingFilename returns the selected filename and  */
 
218
/*  GFN_OK, indicating that the OK button was pressed.  If the user        */
 
219
/*  pressed the cancel button, the return value is GFN_CANCEL, and the     */
 
220
/*  filename character string supplied in the call is not altered.         */
 
221
/*                                                                         */
 
222
/*  Arguments:                                                             */
 
223
/*                                                                         */
 
224
/*      Widget  parent        - parent widget id                           */
 
225
/*      char *  promptString  - prompt string                              */
 
226
/*      char *  filename      - a string to receive the selected filename  */
 
227
/*                              (this string will not be altered if the    */
 
228
/*                              user pressed the cancel button)            */
 
229
/*                                                                         */
 
230
/*  Returns:    GFN_OK        - file was selected and OK button pressed    */
 
231
/*              GFN_CANCEL    - Cancel button pressed and no returned file */
 
232
/*                                                                         */
 
233
int GetExistingFilename (Widget parent, char *promptString, char *filename) 
 
234
{
 
235
    int       n;                      /* number of arguments               */
 
236
    Arg       args[MAX_ARGS];         /* arg list                          */
 
237
    Widget    existFileSB;            /* widget file select box            */
 
238
    XmString  labelString;            /* compound string for prompt label  */
 
239
    XmString  titleString;            /* compound string for dialog title  */
 
240
 
 
241
    n = 0;
 
242
    labelString = XmStringCreateSimple(promptString);
 
243
    titleString = XmStringCreateSimple(" ");
 
244
    XtSetArg(args[n], XmNlistLabelString, labelString); n++;
 
245
    XtSetArg(args[n], XmNdialogStyle, XmDIALOG_FULL_APPLICATION_MODAL); n++;
 
246
    XtSetArg(args[n], XmNdialogTitle, titleString); n++;
 
247
    XtSetArg(args[n], XmNresizePolicy, XmRESIZE_GROW); n++;
 
248
    existFileSB = XmCreateFileSelectionDialog(parent,"FileSelect",args,n);
 
249
    XmStringFree(labelString);
 
250
    XmStringFree(titleString);
 
251
    if (RemoveRedundantTextField)
 
252
        XtUnmanageChild(XmFileSelectionBoxGetChild(existFileSB, XmDIALOG_TEXT)); 
 
253
    XtUnmanageChild(XmFileSelectionBoxGetChild(existFileSB,
 
254
            XmDIALOG_SELECTION_LABEL));
 
255
    XtVaSetValues(XmFileSelectionBoxGetChild(existFileSB,
 
256
            XmDIALOG_FILTER_LABEL), XmNmnemonic, 'l', XmNuserData,
 
257
            XmFileSelectionBoxGetChild(existFileSB, XmDIALOG_FILTER_TEXT), 0);
 
258
    XtVaSetValues(XmFileSelectionBoxGetChild(existFileSB,
 
259
            XmDIALOG_DIR_LIST_LABEL), XmNmnemonic, 'D', XmNuserData,
 
260
            XmFileSelectionBoxGetChild(existFileSB, XmDIALOG_DIR_LIST), 0);
 
261
    XtVaSetValues(XmFileSelectionBoxGetChild(existFileSB, XmDIALOG_LIST_LABEL),
 
262
            XmNmnemonic, promptString[strspn(promptString, "lD")], XmNuserData,
 
263
            XmFileSelectionBoxGetChild(existFileSB, XmDIALOG_LIST), 0);
 
264
    AddDialogMnemonicHandler(existFileSB);
 
265
    RemapDeleteKey(XmFileSelectionBoxGetChild(existFileSB,
 
266
                    XmDIALOG_FILTER_TEXT));
 
267
    RemapDeleteKey(XmFileSelectionBoxGetChild(existFileSB,
 
268
                    XmDIALOG_TEXT));
 
269
    return HandleCustomExistFileSB(existFileSB, filename);
 
270
}
 
271
 
 
272
/*
 
273
** HandleCustomExistFileSB
 
274
**
 
275
** Manage a customized file selection box for opening existing files.
 
276
** Use this if you want to change the standard file selection dialog
 
277
** from the defaults provided in GetExistingFilename, but still
 
278
** want take advantage of the button processing, help messages, and
 
279
** file checking of GetExistingFilename.
 
280
**
 
281
**  Arguments:
 
282
**
 
283
**      Widget  existFileSB   - your custom file selection box widget id
 
284
**      char *  filename      - a string to receive the selected filename
 
285
**                              (this string will not be altered if the
 
286
**                              user pressed the cancel button) 
 
287
**
 
288
**  Returns:    GFN_OK        - file was selected and OK button pressed 
 
289
**              GFN_CANCEL    - Cancel button pressed and no returned file
 
290
**
 
291
*/
 
292
 
 
293
int HandleCustomExistFileSB(Widget existFileSB, char *filename)
 
294
{
 
295
    Boolean   done_with_dialog=False; /* ok to destroy dialog flag         */
 
296
    char      *fileString;            /* C string for file selected        */
 
297
    XmString  cFileString;            /* compound string for file selected */
 
298
    XmString  cDir;                   /* compound directory selected       */
 
299
    XmString  cPattern;               /* compound filter pattern           */
 
300
    Widget    help;                   /* help window form dialog           */
 
301
    int i;
 
302
 
 
303
    XtAddCallback(existFileSB, XmNokCallback, (XtCallbackProc)existOkCB,
 
304
            &done_with_dialog);
 
305
    XtAddCallback(existFileSB, XmNcancelCallback, (XtCallbackProc)existCancelCB,
 
306
            &done_with_dialog);
 
307
    help = createPanelHelp(existFileSB, HelpExist, "Selecting Files to Open");
 
308
    createErrorDialog(existFileSB);
 
309
    XtAddCallback(existFileSB, XmNhelpCallback, (XtCallbackProc)existHelpCB,
 
310
            (char *)help);
 
311
    makeListTypeable(XmFileSelectionBoxGetChild(existFileSB,XmDIALOG_LIST));
 
312
    makeListTypeable(XmFileSelectionBoxGetChild(existFileSB,XmDIALOG_DIR_LIST));
 
313
    if (DefaultDirectory != NULL || DefaultPattern != NULL)
 
314
        XtVaSetValues(existFileSB, XmNdirectory, DefaultDirectory,
 
315
                XmNpattern, DefaultPattern, 0);
 
316
#if XmVersion >= 1002
 
317
    XtVaSetValues(existFileSB, XmNinitialFocus, XtParent(
 
318
            XmFileSelectionBoxGetChild(existFileSB, XmDIALOG_LIST)), 0);
 
319
#endif
 
320
    ManageDialogCenteredOnPointer(existFileSB);
 
321
    
 
322
    /* Typing in the directory list is dependent on the list being in the
 
323
       same form of alphabetical order expected by the character processing
 
324
       routines.  As of about 1.2.3, some Motif libraries seem to have a
 
325
       different idea of ordering than is usual for Unix directories.
 
326
       To sort them properly, we have to patch the directory and file
 
327
       searching routines to re-sort the lists when they change */
 
328
    XtVaGetValues(existFileSB, XmNdirSearchProc, &OrigDirSearchProc,
 
329
            XmNfileSearchProc, &OrigFileSearchProc, 0);
 
330
    XtVaSetValues(existFileSB, XmNdirSearchProc, replacementDirSearchProc,
 
331
            XmNfileSearchProc, replacementFileSearchProc, 0);
 
332
    sortWidgetList(XmFileSelectionBoxGetChild(existFileSB, XmDIALOG_DIR_LIST));
 
333
    sortWidgetList(XmFileSelectionBoxGetChild(existFileSB, XmDIALOG_LIST));
 
334
 
 
335
#if XmVersion < 1002
 
336
    /* To give file list initial focus, revoke default button status for
 
337
       the "OK" button.  Dynamic defaulting will restore it as the default
 
338
       button after the keyboard focus is established.  Note the voodoo
 
339
       below: calling XmProcess traversal extra times (a recommendation from
 
340
       OSF technical support) somehow succeedes in giving the file list focus */
 
341
    XtVaSetValues(existFileSB, XmNdefaultButton, NULL, 0);
 
342
    for (i=1; i<30; i++)
 
343
        XmProcessTraversal(XmFileSelectionBoxGetChild(existFileSB,
 
344
                XmDIALOG_LIST), XmTRAVERSE_CURRENT);
 
345
#endif
 
346
 
 
347
    while (!done_with_dialog)
 
348
        XtAppProcessEvent(XtWidgetToApplicationContext(existFileSB), XtIMAll);
 
349
    
 
350
    if (SelectResult == GFN_OK) {
 
351
        XtVaGetValues(existFileSB, XmNdirSpec, &cFileString, XmNdirectory,
 
352
                &cDir, XmNpattern, &cPattern, 0);
 
353
        if (DefaultDirectory != NULL) XmStringFree(DefaultDirectory);
 
354
        if (DefaultPattern != NULL) XmStringFree(DefaultPattern);
 
355
        DefaultDirectory = XmStringCopy(cDir);
 
356
        DefaultPattern = XmStringCopy(cPattern);
 
357
        XmStringGetLtoR(cFileString, XmSTRING_DEFAULT_CHARSET, &fileString);
 
358
        strcpy(filename, fileString);
 
359
    }
 
360
    XtDestroyWidget(existFileSB);
 
361
    return SelectResult;
 
362
}
 
363
 
 
364
 
 
365
/*      GetNewFilename                                                     */
 
366
/*                                                                         */
 
367
/*  This routine will popup a file selection box so that the user can      */
 
368
/*  select a file that will be, at a later time in the application,        */
 
369
/*  created and written to.  After the user selects a file, GetNewFilename */
 
370
/*  checks whether the file already exists, and if it does, asks the user  */
 
371
/*  if he/she wants to overwrite the file.  Answering no, allows the user  */
 
372
/*  to select a new filename.  GetNewFilename also checks that the file    */
 
373
/*  name specified by the user can be created, and allows re-entry if not. */
 
374
/*  When the user presses the OK button to a filename satisying the above  */
 
375
/*  criteria, GetNewFilename returns the selected filename and GFN_OK.     */
 
376
/*  If the user presses the cancel button, the return value is GFN_CANCEL, */
 
377
/*  and the filename character string supplied in the call is not altered. */
 
378
/*                                                                         */
 
379
/*  Arguments:                                                             */
 
380
/*                                                                         */
 
381
/*      Widget  parent        - parent widget id                           */
 
382
/*      char *  promptString  - prompt string                              */
 
383
/*      char *  filename      - a string to receive the selected filename  */
 
384
/*                              (this string will not be altered if the    */
 
385
/*                              user pressed the cancel button)            */
 
386
/*                                                                         */
 
387
/*  Returns:    GFN_OK        - file was selected and OK button pressed    */
 
388
/*              GFN_CANCEL    - Cancel button pressed and no returned file */
 
389
 
 
390
int GetNewFilename (Widget parent, char *promptString, char *filename)
 
391
{
 
392
    int       n;                      /* number of arguments               */ 
 
393
    Boolean   done_with_dialog=False; /* ok to destroy dialog flag         */
 
394
    Arg       args[MAX_ARGS];         /* arg list                          */
 
395
    Widget    newFileSB = NULL;       /* widget file select box for        */
 
396
    Widget    help;                   /* help window form dialog           */
 
397
    XmString  labelString;            /* compound string for prompt label  */
 
398
    XmString  cFileString;            /* compound string for file selected */
 
399
    XmString  cDir;                   /* compound directory selected       */
 
400
    XmString  cPattern;               /* compound filter pattern           */
 
401
    XmString  titleString;            /* compound string for dialog title  */
 
402
    char      *fileString;            /* C string for file selected        */
 
403
    int i;
 
404
 
 
405
    
 
406
    n = 0;
 
407
    labelString = XmStringCreateLtoR (promptString, 
 
408
                  XmSTRING_DEFAULT_CHARSET);
 
409
    titleString = XmStringCreateLtoR (" ", XmSTRING_DEFAULT_CHARSET);
 
410
    XtSetArg(args[n], XmNselectionLabelString, labelString); n++;     
 
411
#ifdef MOTIF10
 
412
    XtSetArg(args[n], XmNdialogStyle, XmDIALOG_APPLICATION_MODAL); n++;
 
413
#else
 
414
    XtSetArg(args[n], XmNdialogStyle, XmDIALOG_FULL_APPLICATION_MODAL); n++;
 
415
#endif
 
416
    XtSetArg(args[n], XmNdialogTitle, titleString); n++;
 
417
    XtSetArg(args[n], XmNresizePolicy, XmRESIZE_GROW); n++;
 
418
    newFileSB=XmCreateFileSelectionDialog(parent,"FileSelect",args,n);
 
419
    XtAddCallback(newFileSB, XmNokCallback, (XtCallbackProc)newFileOKCB,
 
420
            &done_with_dialog);
 
421
    XtAddCallback(newFileSB, XmNcancelCallback, (XtCallbackProc)newFileCancelCB,
 
422
            &done_with_dialog);
 
423
    XtVaSetValues(XmFileSelectionBoxGetChild(newFileSB,
 
424
            XmDIALOG_FILTER_LABEL), XmNmnemonic, 'l', XmNuserData,
 
425
            XmFileSelectionBoxGetChild(newFileSB, XmDIALOG_FILTER_TEXT), 0);
 
426
    XtVaSetValues(XmFileSelectionBoxGetChild(newFileSB,
 
427
            XmDIALOG_DIR_LIST_LABEL), XmNmnemonic, 'D', XmNuserData,
 
428
            XmFileSelectionBoxGetChild(newFileSB, XmDIALOG_DIR_LIST), 0);
 
429
    XtVaSetValues(XmFileSelectionBoxGetChild(newFileSB,
 
430
            XmDIALOG_LIST_LABEL), XmNmnemonic, 'F', XmNuserData,
 
431
            XmFileSelectionBoxGetChild(newFileSB, XmDIALOG_LIST), 0);
 
432
    XtVaSetValues(XmFileSelectionBoxGetChild(newFileSB,
 
433
            XmDIALOG_SELECTION_LABEL), XmNmnemonic,
 
434
            promptString[strspn(promptString, "lFD")], XmNuserData,
 
435
            XmFileSelectionBoxGetChild(newFileSB, XmDIALOG_TEXT), 0);
 
436
    AddDialogMnemonicHandler(newFileSB);
 
437
    RemapDeleteKey(XmFileSelectionBoxGetChild(newFileSB, XmDIALOG_FILTER_TEXT));
 
438
    RemapDeleteKey(XmFileSelectionBoxGetChild(newFileSB, XmDIALOG_TEXT));
 
439
    makeListTypeable(XmFileSelectionBoxGetChild(newFileSB,XmDIALOG_LIST));
 
440
    makeListTypeable(XmFileSelectionBoxGetChild(newFileSB,XmDIALOG_DIR_LIST));
 
441
    if (DefaultDirectory != NULL || DefaultPattern != NULL)
 
442
        XtVaSetValues(newFileSB, XmNdirectory, DefaultDirectory,
 
443
                XmNpattern, DefaultPattern, 0);
 
444
    help = createPanelHelp(newFileSB, HelpNew, "Saving a File");
 
445
    createYesNoDialog(newFileSB);
 
446
    createErrorDialog(newFileSB);
 
447
    XtAddCallback(newFileSB, XmNhelpCallback, (XtCallbackProc)newHelpCB, 
 
448
            (char *)help);
 
449
    XmStringFree(labelString);
 
450
    XmStringFree(titleString);
 
451
#if XmVersion >= 1002
 
452
    XtVaSetValues(newFileSB, XmNinitialFocus, 
 
453
            XmFileSelectionBoxGetChild(newFileSB, XmDIALOG_TEXT), 0);
 
454
#endif
 
455
    ManageDialogCenteredOnPointer(newFileSB);
 
456
 
 
457
#if XmVersion < 1002
 
458
    /* To give filename text initial focus, revoke default button status for
 
459
       the "OK" button.  Dynamic defaulting will restore it as the default
 
460
       button after the keyboard focus is established.  Note the voodoo
 
461
       below: calling XmProcess traversal FOUR times (a recommendation from
 
462
       OSF technical support) somehow succeedes in changing the focus */
 
463
    XtVaSetValues(newFileSB, XmNdefaultButton, NULL, 0);
 
464
    for (i=1; i<30; i++)
 
465
        XmProcessTraversal(XmFileSelectionBoxGetChild(newFileSB, XmDIALOG_TEXT),
 
466
            XmTRAVERSE_CURRENT);
 
467
#endif
 
468
    
 
469
    /* Typing in the directory list is dependent on the list being in the
 
470
       same form of alphabetical order expected by the character processing
 
471
       routines.  As of about 1.2.3, some Motif libraries seem to have a
 
472
       different idea of ordering than is usual for Unix directories.
 
473
       To sort them properly, we have to patch the directory and file
 
474
       searching routines to re-sort the lists when they change */
 
475
    XtVaGetValues(newFileSB, XmNdirSearchProc, &OrigDirSearchProc,
 
476
            XmNfileSearchProc, &OrigFileSearchProc, 0);
 
477
    XtVaSetValues(newFileSB, XmNdirSearchProc, replacementDirSearchProc,
 
478
            XmNfileSearchProc, replacementFileSearchProc, 0);
 
479
    sortWidgetList(XmFileSelectionBoxGetChild(newFileSB, XmDIALOG_DIR_LIST));
 
480
    sortWidgetList(XmFileSelectionBoxGetChild(newFileSB, XmDIALOG_LIST));
 
481
 
 
482
    while (!done_with_dialog)
 
483
        XtAppProcessEvent (XtWidgetToApplicationContext(newFileSB), XtIMAll);
 
484
 
 
485
    if (SelectResult == GFN_OK) {
 
486
        XtVaGetValues(newFileSB, XmNdirSpec, &cFileString, XmNdirectory,
 
487
                &cDir, XmNpattern, &cPattern, 0);
 
488
        if (DefaultDirectory != NULL) XmStringFree(DefaultDirectory);
 
489
        if (DefaultPattern != NULL) XmStringFree(DefaultPattern);
 
490
        DefaultDirectory = XmStringCopy(cDir);
 
491
        DefaultPattern = XmStringCopy(cPattern);
 
492
        XmStringGetLtoR(cFileString, XmSTRING_DEFAULT_CHARSET, &fileString);
 
493
        strcpy(filename, fileString);
 
494
    }
 
495
    XtDestroyWidget(newFileSB);
 
496
    return SelectResult;
 
497
}
 
498
 
 
499
/*
 
500
** Return current default directory used by GetExistingFilename and
 
501
** GetNewFilename.  Can return NULL if no default directory has been set
 
502
** (meaning use the application's current working directory) String must
 
503
** be freed by the caller using XtFree.
 
504
*/
 
505
char *GetFileDialogDefaultDirectory(void)
 
506
{
 
507
    char *string;
 
508
    
 
509
    if (DefaultDirectory == NULL)
 
510
        return NULL;
 
511
    XmStringGetLtoR(DefaultDirectory, XmSTRING_DEFAULT_CHARSET, &string);
 
512
    return string;
 
513
}
 
514
 
 
515
/*
 
516
** Return current default match pattern used by GetExistingFilename and
 
517
** GetNewFilename.  Can return NULL if no default pattern has been set
 
518
** (meaning use a pattern matching all files in the directory) String must
 
519
** be freed by the caller using XtFree.
 
520
*/
 
521
char *GetFileDialogDefaultPattern(void)
 
522
{
 
523
    char *string;
 
524
    
 
525
    if (DefaultPattern == NULL)
 
526
        return NULL;
 
527
    XmStringGetLtoR(DefaultPattern, XmSTRING_DEFAULT_CHARSET, &string);
 
528
    return string;
 
529
}
 
530
 
 
531
/*
 
532
** Set the current default directory to be used by GetExistingFilename and
 
533
** GetNewFilename.  "dir" can be passed as NULL to clear the current default
 
534
** directory and use the application's working directory instead.
 
535
*/
 
536
void SetFileDialogDefaultDirectory(char *dir)
 
537
{
 
538
    if (DefaultDirectory != NULL)
 
539
        XmStringFree(DefaultDirectory);
 
540
    DefaultDirectory = dir==NULL ? NULL : XmStringCreateSimple(dir);
 
541
}
 
542
 
 
543
/*
 
544
** Set the current default match pattern to be used by GetExistingFilename and
 
545
** GetNewFilename.  "pattern" can be passed as NULL as the equivalent a pattern
 
546
** matching all files in the directory.
 
547
*/
 
548
void SetFileDialogDefaultPattern(char *pattern)
 
549
{
 
550
    if (DefaultPattern != NULL)
 
551
        XmStringFree(DefaultPattern);
 
552
    DefaultPattern = pattern==NULL ? NULL : XmStringCreateSimple(pattern);
 
553
}
 
554
 
 
555
void SetGetExistingFilenameTextFieldRemoval(int state)
 
556
{
 
557
    RemoveRedundantTextField = state;
 
558
}
 
559
 
 
560
/*
 
561
** createYesNoDialog, createErrorDialog, doYesNoDialog, doErrorDialog
 
562
**
 
563
** Error Messages and question dialogs to be used with the file selection
 
564
** box.  Due to a crash bug in Motif 1.1.1 thru (at least) 1.1.5
 
565
** getfiles can not use DialogF.  According to OSF, there is an error
 
566
** in the creation of pushButtonGadgets involving the creation and
 
567
** destruction of some sort of temporary object.  These routines create
 
568
** the dialogs along with the file selection dialog and manage them
 
569
** to display messages.  This somehow avoids the problem
 
570
*/
 
571
static void createYesNoDialog(Widget parent)
 
572
{
 
573
    XmString  buttonString;           /* compound string for dialog buttons */
 
574
    int       n;                      /* number of arguments               */ 
 
575
    Arg       args[MAX_ARGS];         /* arg list                          */
 
576
 
 
577
    n = 0;
 
578
#ifdef MOTIF10
 
579
    XtSetArg(args[n], XmNdialogStyle, XmDIALOG_APPLICATION_MODAL); n++;
 
580
#else
 
581
    XtSetArg(args[n], XmNdialogStyle, XmDIALOG_FULL_APPLICATION_MODAL); n++;
 
582
#endif
 
583
    XtSetArg(args[n], XmNtitle, " "); n++;
 
584
    YesNoDialog = XmCreateQuestionDialog(parent, "yesNo", args, n);
 
585
    XtAddCallback (YesNoDialog, XmNokCallback, (XtCallbackProc)yesNoOKCB, NULL);
 
586
    XtAddCallback (YesNoDialog, XmNcancelCallback,
 
587
            (XtCallbackProc)yesNoCancelCB, NULL);
 
588
    XtUnmanageChild(XmMessageBoxGetChild (YesNoDialog, XmDIALOG_HELP_BUTTON));
 
589
    buttonString = XmStringCreateSimple("Yes");
 
590
    SET_ONE_RSRC(YesNoDialog, XmNokLabelString, buttonString);
 
591
    XmStringFree(buttonString);
 
592
    buttonString = XmStringCreateSimple("No");
 
593
    SET_ONE_RSRC(YesNoDialog, XmNcancelLabelString, buttonString);
 
594
    XmStringFree(buttonString);
 
595
}
 
596
 
 
597
static void createErrorDialog(Widget parent)
 
598
{
 
599
    XmString  buttonString;           /* compound string for dialog button */
 
600
    int       n;                      /* number of arguments               */ 
 
601
    Arg       args[MAX_ARGS];         /* arg list                          */
 
602
 
 
603
    n = 0;
 
604
#ifdef MOTIF10
 
605
    XtSetArg(args[n], XmNdialogStyle, XmDIALOG_APPLICATION_MODAL); n++;
 
606
#else
 
607
    XtSetArg(args[n], XmNdialogStyle, XmDIALOG_FULL_APPLICATION_MODAL); n++;
 
608
#endif
 
609
    XtSetArg(args[n], XmNtitle, " "); n++;
 
610
    ErrorDialog = XmCreateErrorDialog(parent, "error", args, n);
 
611
    XtAddCallback(ErrorDialog, XmNcancelCallback, (XtCallbackProc)errorOKCB,
 
612
            NULL);
 
613
    XtUnmanageChild(XmMessageBoxGetChild(ErrorDialog, XmDIALOG_OK_BUTTON));
 
614
    XtUnmanageChild(XmMessageBoxGetChild(ErrorDialog, XmDIALOG_HELP_BUTTON));
 
615
    buttonString = XmStringCreateLtoR ("Dismiss", XmSTRING_DEFAULT_CHARSET);
 
616
    XtVaSetValues(ErrorDialog, XmNcancelLabelString, buttonString, 0);
 
617
    XmStringFree(buttonString);
 
618
}
 
619
 
 
620
static int doYesNoDialog(char *filename)
 
621
{
 
622
    char string[255];
 
623
    XmString mString;
 
624
 
 
625
    YesNoResult = ynNone;
 
626
 
 
627
    sprintf(string, "File %s already exists,\nOk to overwrite?", filename);
 
628
    mString = XmStringCreateLtoR(string, XmSTRING_DEFAULT_CHARSET);
 
629
    
 
630
    SET_ONE_RSRC(YesNoDialog, XmNmessageString, mString);
 
631
    XmStringFree(mString);
 
632
    ManageDialogCenteredOnPointer(YesNoDialog);
 
633
 
 
634
    while (YesNoResult == ynNone)
 
635
        XtAppProcessEvent(XtWidgetToApplicationContext(YesNoDialog), XtIMAll);
 
636
    
 
637
    XtUnmanageChild(YesNoDialog);
 
638
 
 
639
    /* Nasty motif bug here, patched around by waiting for a ReparentNotify
 
640
       event (with timeout) before allowing file selection dialog to pop
 
641
       down.  If this routine returns too quickly, and the file selection
 
642
       dialog (and thereby, this dialog as well) are destroyed while X
 
643
       is still sorting through the events generated by the pop-down,
 
644
       something bad happens and we get a crash */
 
645
    if (YesNoResult == ynYes)
 
646
        PopDownBugPatch(YesNoDialog);
 
647
 
 
648
    return YesNoResult == ynYes;
 
649
}
 
650
 
 
651
static void doErrorDialog(char *errorString, char *filename)
 
652
{
 
653
    char string[255];
 
654
    XmString mString;
 
655
 
 
656
    ErrorDone = False;
 
657
 
 
658
    sprintf(string, errorString, filename);
 
659
    mString = XmStringCreateLtoR(string, XmSTRING_DEFAULT_CHARSET);
 
660
    
 
661
    SET_ONE_RSRC(ErrorDialog, XmNmessageString, mString);
 
662
    XmStringFree(mString);
 
663
    ManageDialogCenteredOnPointer(ErrorDialog);
 
664
 
 
665
    while (!ErrorDone)
 
666
        XtAppProcessEvent (XtWidgetToApplicationContext(ErrorDialog), XtIMAll);
 
667
    
 
668
    XtUnmanageChild(ErrorDialog);
 
669
}
 
670
 
 
671
static void newFileOKCB(Widget  w, Boolean *client_data, 
 
672
                 XmFileSelectionBoxCallbackStruct *call_data)
 
673
 
 
674
{
 
675
    char *filename;                   /* name of chosen file             */
 
676
    int  fd;                          /* file descriptor                 */
 
677
    int  length;                      /* length of file name             */
 
678
    int  response;                    /* response to dialog              */
 
679
    struct stat buf;                  /* status from fstat               */
 
680
    
 
681
    XmStringGetLtoR(call_data->value, XmSTRING_DEFAULT_CHARSET, &filename);
 
682
    SelectResult = GFN_OK;
 
683
    length = strlen(filename);
 
684
    if (length == 0 || filename[length-1] == '/') {
 
685
        doErrorDialog("Please supply a name for the file", NULL);
 
686
        return;
 
687
    }
 
688
 
 
689
#ifdef VMS
 
690
    if (strchr(filename,';') && (fd = open(filename, O_RDONLY, 0)) != -1) {
 
691
#else  /* not VMS*/
 
692
    if ((fd = open(filename, O_RDONLY, 0)) != -1) {     /* exists */
 
693
#endif /*VMS*/
 
694
        fstat(fd, &buf);
 
695
        close(fd);
 
696
        if (buf.st_mode & S_IFDIR) {
 
697
            doErrorDialog("Error: %s is a directory", filename);
 
698
            return;
 
699
        }
 
700
        response = doYesNoDialog(filename);
 
701
#ifdef VMS
 
702
        if (response) {
 
703
            if (access(filename, 2) != 0) { /* have write/delete access? */
 
704
                doErrorDialog("Error: can't overwrite %s ", filename);
 
705
                XtFree(filename);
 
706
                return;
 
707
            }
 
708
        } else {
 
709
#else
 
710
        if (!response) {
 
711
#endif /*VMS*/
 
712
             return;
 
713
        }
 
714
    } else {
 
715
        if ((fd = creat(filename, PERMS)) == -1) {
 
716
            doErrorDialog("Error: can't create %s ", filename);
 
717
            return;
 
718
        } else {
 
719
            close(fd);
 
720
            remove(filename);
 
721
        }
 
722
    }
 
723
    *client_data = True;                            /* done with dialog */
 
724
}
 
725
 
 
726
 
 
727
static void newFileCancelCB(Widget w, Boolean *client_data, caddr_t call_data)
 
728
{
 
729
    SelectResult = GFN_CANCEL;
 
730
    *client_data = True;
 
731
}
 
732
 
 
733
static void newHelpCB(Widget w, Widget helpPanel, caddr_t call_data)
 
734
{
 
735
    ManageDialogCenteredOnPointer(helpPanel);
 
736
}
 
737
 
 
738
static void existOkCB(Widget w, Boolean * client_data,
 
739
               XmFileSelectionBoxCallbackStruct *call_data)
 
740
{
 
741
    char *filename;                   /* name of chosen file             */
 
742
    int  fd;                          /* file descriptor                 */
 
743
    int  length;                      /* length of file name             */
 
744
    
 
745
    XmStringGetLtoR(call_data->value, XmSTRING_DEFAULT_CHARSET, &filename);
 
746
    SelectResult = GFN_OK;
 
747
    length = strlen(filename);
 
748
    if (length == 0 || filename[length-1] == '/') {
 
749
        doErrorDialog("Please select a file to open", NULL);
 
750
        XtFree(filename);
 
751
        return;
 
752
    } else    if ((fd = open(filename, O_RDONLY,0))  == -1) {
 
753
        doErrorDialog("Error: can't open %s ", filename);
 
754
        XtFree(filename);
 
755
        return;
 
756
    } else
 
757
        close(fd);
 
758
    XtFree(filename);
 
759
        
 
760
    *client_data = True;                /* done with dialog             */
 
761
}
 
762
 
 
763
 
 
764
static void existCancelCB(Widget w, Boolean * client_data, caddr_t call_data)
 
765
{
 
766
    SelectResult = GFN_CANCEL;
 
767
    *client_data = True;                /* done with dialog             */
 
768
}
 
769
 
 
770
static void yesNoOKCB(Widget w, caddr_t client_data, caddr_t call_data)
 
771
{
 
772
    YesNoResult = ynYes;
 
773
}
 
774
 
 
775
static void existHelpCB(Widget w, Widget helpPanel, caddr_t call_data)
 
776
{
 
777
    ManageDialogCenteredOnPointer(helpPanel);
 
778
}
 
779
 
 
780
static void errorOKCB(Widget w, caddr_t client_data, caddr_t call_data)
 
781
{
 
782
    ErrorDone = True;
 
783
}
 
784
 
 
785
static void yesNoCancelCB(Widget w, caddr_t client_data, caddr_t call_data)
 
786
{
 
787
    YesNoResult = ynNo;
 
788
}
 
789
 
 
790
static Widget createPanelHelp(Widget parent, char *helpText, char *title)
 
791
{
 
792
    Arg al[50];
 
793
    int ac;
 
794
    Widget form, text, button;
 
795
    XmString st1;
 
796
    
 
797
    ac = 0;
 
798
    form = XmCreateFormDialog(parent, "helpForm", al, ac);
 
799
 
 
800
#ifndef MOTIF10
 
801
    ac = 0;
 
802
    XtSetArg (al[ac], XmNbottomAttachment, XmATTACH_FORM);  ac++;
 
803
    XtSetArg (al[ac], XmNtopAttachment, XmATTACH_NONE);  ac++;
 
804
    XtSetArg(al[ac], XmNlabelString, st1=XmStringCreateLtoR ("Dismiss", 
 
805
                      XmSTRING_DEFAULT_CHARSET)); ac++;
 
806
    button = XmCreatePushButtonGadget(form, "dismiss", al, ac);
 
807
    XtAddCallback(button, XmNactivateCallback, (XtCallbackProc)helpDismissCB,
 
808
            (char *)form);
 
809
    XmStringFree(st1);
 
810
    XtManageChild(button);
 
811
    SET_ONE_RSRC(form, XmNdefaultButton, button);
 
812
#endif
 
813
    
 
814
    ac = 0;
 
815
    XtSetArg(al[ac], XmNrows, 15);  ac++;
 
816
    XtSetArg(al[ac], XmNcolumns, 60);  ac++;
 
817
    XtSetArg(al[ac], XmNresizeHeight, False);  ac++;
 
818
    XtSetArg(al[ac], XmNtraversalOn, False); ac++;
 
819
    XtSetArg(al[ac], XmNwordWrap, True);  ac++;
 
820
    XtSetArg(al[ac], XmNscrollHorizontal, False);  ac++;
 
821
    XtSetArg(al[ac], XmNeditMode, XmMULTI_LINE_EDIT);  ac++;
 
822
    XtSetArg(al[ac], XmNeditable, False);  ac++;
 
823
    XtSetArg(al[ac], XmNvalue, helpText);  ac++;
 
824
#ifndef MOTIF10
 
825
    XtSetArg(al[ac], XmNtopAttachment, XmATTACH_FORM);  ac++;
 
826
    XtSetArg(al[ac], XmNleftAttachment, XmATTACH_FORM);  ac++;
 
827
    XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET);  ac++;
 
828
    XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM);  ac++;
 
829
    XtSetArg(al[ac], XmNbottomWidget, button);  ac++;
 
830
#endif
 
831
    text = XmCreateScrolledText(form, "helpText", al, ac);
 
832
    XtManageChild(text);
 
833
    
 
834
    SET_ONE_RSRC(XtParent(form), XmNtitle, title);
 
835
    
 
836
    return form;
 
837
}
 
838
 
 
839
static void helpDismissCB(Widget w, Widget helpPanel, caddr_t call_data)
 
840
{
 
841
    XtUnmanageChild(helpPanel);
 
842
}
 
843
 
 
844
/*
 
845
** Add ability for user to type filenames to a list widget
 
846
*/
 
847
static void makeListTypeable(Widget listW)
 
848
{
 
849
    static XtActionsRec actionTable[] = {"file-list-typein",
 
850
            (XtActionProc)listCharCB};
 
851
    static XtTranslations translationTable = NULL;
 
852
    static char *translations = " ~Ctrl ~Meta ~Alt <Key>: file-list-typein()\n";
 
853
 
 
854
    /* The first time through, compile translations and register actions */
 
855
    if (translationTable == NULL) {
 
856
        XtAppAddActions(XtWidgetToApplicationContext(listW), actionTable, 1);
 
857
        translationTable = XtParseTranslationTable(translations);
 
858
    }
 
859
    
 
860
    /* Add new translations to widget, leaving existing translations intact */
 
861
    XtAugmentTranslations(listW, translationTable);
 
862
}
 
863
 
 
864
/*
 
865
** Action procedure for processing characters typed in a list, finds the
 
866
** first item matching the characters typed so far.
 
867
*/
 
868
static void listCharCB(Widget w, XEvent *event, String *params, Cardinal n)
 
869
{
 
870
    char charString[5], c, *itemString;
 
871
    int nChars, nItems, i, cmp, selectPos, topPos, nVisible;
 
872
    XmString *items;
 
873
    KeySym kSym;
 
874
    char name[MAXPATHLEN], path[MAXPATHLEN];
 
875
    static char keystrokes[MAX_LIST_KEYSTROKES];
 
876
    static int nKeystrokes = 0;
 
877
    static Time lastKeyTime = 0;
 
878
    
 
879
    /* Get the ascii character code represented by the event */
 
880
    nChars = XLookupString((XKeyEvent *)event, charString, sizeof(charString),
 
881
            &kSym, NULL);
 
882
    c = charString[0];
 
883
    
 
884
    /* Process selected control keys, but otherwise ignore the keystroke
 
885
       if it isn't a single printable ascii character */
 
886
    if (kSym==XK_BackSpace || kSym==XK_Delete) {
 
887
        nKeystrokes = nKeystrokes > 0 ? nKeystrokes-1 : 0;
 
888
        return;
 
889
    } else if (kSym==XK_Clear || kSym==XK_Escape ||
 
890
            kSym==XK_Cancel || kSym==XK_Break) {
 
891
        nKeystrokes = 0;
 
892
        return;
 
893
    } else if (nChars!=1 || c<0x021 || c>0x07e)
 
894
        return;
 
895
   
 
896
    /* Throw out keystrokes and start keystroke accumulation over from 
 
897
       scratch if user waits more than MAX_LIST_KESTROKE_WAIT milliseconds */
 
898
    if (((XKeyEvent *)event)->time - lastKeyTime > MAX_LIST_KESTROKE_WAIT)
 
899
        nKeystrokes = 0;
 
900
    lastKeyTime = ((XKeyEvent *)event)->time;
 
901
        
 
902
    /* Accumulate the current keystroke, just beep if there are too many */
 
903
    if (nKeystrokes >= MAX_LIST_KEYSTROKES)
 
904
        XBell(XtDisplay(w), 100);
 
905
    else
 
906
#ifdef VMS
 
907
        keystrokes[nKeystrokes++] = toupper(c);
 
908
#else
 
909
        keystrokes[nKeystrokes++] = c;
 
910
#endif
 
911
    
 
912
    /* Get the items (filenames) in the list widget */
 
913
    XtVaGetValues(w, XmNitems, &items, XmNitemCount, &nItems, 0);
 
914
    
 
915
    /* compare them with the accumulated user keystrokes & decide the
 
916
       appropriate line in the list widget to select */
 
917
    selectPos = 0;
 
918
    for (i=0; i<nItems; i++) {
 
919
        XmStringGetLtoR(items[i], XmSTRING_DEFAULT_CHARSET, &itemString);
 
920
        ParseFilename(itemString, name, path);
 
921
        cmp = strncmp(name, keystrokes, nKeystrokes);
 
922
        if (cmp == 0) {
 
923
            selectPos = i+1;
 
924
            break;
 
925
        } else if (cmp > 0) {
 
926
            selectPos = i;
 
927
            break;
 
928
        }
 
929
    }
 
930
 
 
931
    /* Make the selection, and make sure it will be visible */
 
932
    XmListSelectPos(w, selectPos, True);
 
933
    if (selectPos == 0) /* XmListSelectPos curiously returns 0 for last item */
 
934
        selectPos = nItems + 1;
 
935
    XtVaGetValues(w, XmNtopItemPosition, &topPos,
 
936
            XmNvisibleItemCount, &nVisible, 0);
 
937
    if (selectPos < topPos)
 
938
        XmListSetPos(w, selectPos-2 > 1 ? selectPos-2 : 1);
 
939
    else if (selectPos > topPos+nVisible-1)
 
940
        XmListSetBottomPos(w, selectPos+2 <= nItems ? selectPos+2 : 0);
 
941
}
 
942
 
 
943
/*
 
944
** Replacement directory and file search procedures for the file selection
 
945
** box to re-sort the items in a standard order.  This is a patch, and not
 
946
** a very good one, for the problem that in some Motif versions, the directory
 
947
** list is sorted differently, such that typing of filenames fails because
 
948
** it expects strcmp alphabetical order, as opposed to strcasecmp.  Most
 
949
** users prefer the old ordering, which is what this enforces, but if
 
950
** ifdefs can be found that will correctly predict the ordering and adjust
 
951
** listCharCB above, instead of resorting to re-sorting, it should be done.
 
952
** This obviously wastes valuable time as the selection box is popping up
 
953
** and should be removed.  These routines also leak memory like a seive,
 
954
** because Motif's inconsistent treatment of memory in list widgets does
 
955
** not allow us to free lists that we pass in, and most Motif versions
 
956
** don't clean it up properly.
 
957
*/
 
958
static void replacementDirSearchProc(Widget w, XtPointer searchData)
 
959
{
 
960
    Boolean updated;
 
961
    
 
962
    /* Call the original search procedure to do the actual search */
 
963
    (*OrigDirSearchProc)(w, searchData);
 
964
    XtVaGetValues(w, XmNlistUpdated, &updated, 0);
 
965
    if (!updated)
 
966
        return;
 
967
        
 
968
    /* Sort the items in the list */
 
969
    sortWidgetList(XmFileSelectionBoxGetChild(w, XmDIALOG_DIR_LIST));
 
970
}
 
971
static void replacementFileSearchProc(Widget w, XtPointer searchData)
 
972
{
 
973
    Boolean updated;
 
974
    
 
975
    /* Call the original search procedure to do the actual search */
 
976
    (*OrigFileSearchProc)(w, searchData);
 
977
    XtVaGetValues(w, XmNlistUpdated, &updated, 0);
 
978
    if (!updated)
 
979
        return;
 
980
        
 
981
    /* Sort the items in the list */
 
982
    sortWidgetList(XmFileSelectionBoxGetChild(w, XmDIALOG_LIST));
 
983
}
 
984
 
 
985
/*
 
986
** Sort the items in a list widget "listWidget"
 
987
*/
 
988
static void sortWidgetList(Widget listWidget)
 
989
{
 
990
    XmString *items, *sortedItems;
 
991
    int nItems, i;
 
992
    
 
993
    XtVaGetValues(listWidget, XmNitems, &items, XmNitemCount, &nItems, 0);
 
994
    sortedItems = (XmString *)XtMalloc(sizeof(XmString) * nItems);
 
995
    for (i=0; i<nItems; i++)
 
996
        sortedItems[i] = XmStringCopy(items[i]);
 
997
    qsort(sortedItems, nItems, sizeof(XmString), compareXmStrings);
 
998
    XmListReplaceItemsPos(listWidget, sortedItems, nItems, 1);
 
999
}
 
1000
 
 
1001
/*
 
1002
** Compare procedure for qsort for sorting a list of XmStrings
 
1003
*/
 
1004
static int compareXmStrings(const void *string1, const void *string2)
 
1005
{
 
1006
    char *s1, *s2;
 
1007
    int result;
 
1008
    
 
1009
    XmStringGetLtoR(*(XmString *)string1, XmSTRING_DEFAULT_CHARSET, &s1);
 
1010
    XmStringGetLtoR(*(XmString *)string2, XmSTRING_DEFAULT_CHARSET, &s2);
 
1011
    result = strcmp(s1, s2);
 
1012
    return result;
 
1013
}