1
/******************************************************************************
3
* Getfiles.c -- File Interface Routines *
5
* Copyright (c) 1993 Universities Research Association, Inc. *
6
* All Rights Reserved. *
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. *
19
* Fermilab Nirvana GUI Library *
22
* Written by Donna Reid *
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. *
36
* Included are two routines written using Motif for accessing files: *
38
* GetExistingFilename presents a FileSelectionBox dialog where users can *
39
* choose an existing file to open. *
41
* GetNewFilename presents a FileSelectionBox dialog to help the user *
42
* find a place for a new file. *
44
******************************************************************************/
45
static char SCCSID[] = "@(#)getfiles.c 1.33 2/5/96";
58
#include <sys/param.h>
60
#include <sys/types.h>
62
#include <sys/errno.h>
63
#include <X11/keysym.h>
65
#include <Xm/PushBG.h>
66
#include <Xm/FileSB.h>
69
#include <Xm/MessageB.h>
71
#include "fileUtils.h"
75
#define MAX_ARGS 20 /* Maximum number of X arguments */
76
#define PERMS 0666 /* UNIX file permission, RW for owner,
78
#define MAX_LIST_KEYSTROKES 100 /* Max # of keys user can type to
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 */
85
#define SET_ONE_RSRC(widget, name, newValue) \
87
static Arg args[1] = {{name, (XtArgVal)0}}; \
88
args[0].value = (XtArgVal)newValue; \
89
XtSetValues(widget, args, 1); \
92
enum yesNoValues {ynNone, ynYes, ynNo};
94
/* Saved default directory and pattern from last successful call */
95
static XmString DefaultDirectory = NULL;
96
static XmString DefaultPattern = NULL;
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;
103
/* Text for help button help display */
104
/* ... needs variant for VMS */
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\
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\
132
To replace an existing file, select it from the Files list \
133
and press \"OK\", or simply double click on the name.\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.";
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\
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\
162
To replace an existing file, select it from the Files list \
163
and press \"OK\", or simply double click on the name.\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.";
171
/* Local Callback Routines and variables */
173
static void newFileOKCB(Widget w, Boolean *client_data,
174
XmFileSelectionBoxCallbackStruct *call_data);
175
static void newFileCancelCB(Widget w, Boolean *client_data, caddr_t
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);
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 */
204
static void (*OrigDirSearchProc)();
205
static void (*OrigFileSearchProc)();
207
static XmSearchProc OrigDirSearchProc; /* Built in Motif directory search */
208
static XmSearchProc OrigFileSearchProc; /* Built in Motif file search proc */
211
/* GetExistingFilename */
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. */
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) */
230
/* Returns: GFN_OK - file was selected and OK button pressed */
231
/* GFN_CANCEL - Cancel button pressed and no returned file */
233
int GetExistingFilename (Widget parent, char *promptString, char *filename)
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 */
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,
269
return HandleCustomExistFileSB(existFileSB, filename);
273
** HandleCustomExistFileSB
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.
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)
288
** Returns: GFN_OK - file was selected and OK button pressed
289
** GFN_CANCEL - Cancel button pressed and no returned file
293
int HandleCustomExistFileSB(Widget existFileSB, char *filename)
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 */
303
XtAddCallback(existFileSB, XmNokCallback, (XtCallbackProc)existOkCB,
305
XtAddCallback(existFileSB, XmNcancelCallback, (XtCallbackProc)existCancelCB,
307
help = createPanelHelp(existFileSB, HelpExist, "Selecting Files to Open");
308
createErrorDialog(existFileSB);
309
XtAddCallback(existFileSB, XmNhelpCallback, (XtCallbackProc)existHelpCB,
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);
320
ManageDialogCenteredOnPointer(existFileSB);
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));
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);
343
XmProcessTraversal(XmFileSelectionBoxGetChild(existFileSB,
344
XmDIALOG_LIST), XmTRAVERSE_CURRENT);
347
while (!done_with_dialog)
348
XtAppProcessEvent(XtWidgetToApplicationContext(existFileSB), XtIMAll);
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);
360
XtDestroyWidget(existFileSB);
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. */
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) */
387
/* Returns: GFN_OK - file was selected and OK button pressed */
388
/* GFN_CANCEL - Cancel button pressed and no returned file */
390
int GetNewFilename (Widget parent, char *promptString, char *filename)
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 */
407
labelString = XmStringCreateLtoR (promptString,
408
XmSTRING_DEFAULT_CHARSET);
409
titleString = XmStringCreateLtoR (" ", XmSTRING_DEFAULT_CHARSET);
410
XtSetArg(args[n], XmNselectionLabelString, labelString); n++;
412
XtSetArg(args[n], XmNdialogStyle, XmDIALOG_APPLICATION_MODAL); n++;
414
XtSetArg(args[n], XmNdialogStyle, XmDIALOG_FULL_APPLICATION_MODAL); n++;
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,
421
XtAddCallback(newFileSB, XmNcancelCallback, (XtCallbackProc)newFileCancelCB,
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,
449
XmStringFree(labelString);
450
XmStringFree(titleString);
451
#if XmVersion >= 1002
452
XtVaSetValues(newFileSB, XmNinitialFocus,
453
XmFileSelectionBoxGetChild(newFileSB, XmDIALOG_TEXT), 0);
455
ManageDialogCenteredOnPointer(newFileSB);
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);
465
XmProcessTraversal(XmFileSelectionBoxGetChild(newFileSB, XmDIALOG_TEXT),
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));
482
while (!done_with_dialog)
483
XtAppProcessEvent (XtWidgetToApplicationContext(newFileSB), XtIMAll);
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);
495
XtDestroyWidget(newFileSB);
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.
505
char *GetFileDialogDefaultDirectory(void)
509
if (DefaultDirectory == NULL)
511
XmStringGetLtoR(DefaultDirectory, XmSTRING_DEFAULT_CHARSET, &string);
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.
521
char *GetFileDialogDefaultPattern(void)
525
if (DefaultPattern == NULL)
527
XmStringGetLtoR(DefaultPattern, XmSTRING_DEFAULT_CHARSET, &string);
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.
536
void SetFileDialogDefaultDirectory(char *dir)
538
if (DefaultDirectory != NULL)
539
XmStringFree(DefaultDirectory);
540
DefaultDirectory = dir==NULL ? NULL : XmStringCreateSimple(dir);
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.
548
void SetFileDialogDefaultPattern(char *pattern)
550
if (DefaultPattern != NULL)
551
XmStringFree(DefaultPattern);
552
DefaultPattern = pattern==NULL ? NULL : XmStringCreateSimple(pattern);
555
void SetGetExistingFilenameTextFieldRemoval(int state)
557
RemoveRedundantTextField = state;
561
** createYesNoDialog, createErrorDialog, doYesNoDialog, doErrorDialog
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
571
static void createYesNoDialog(Widget parent)
573
XmString buttonString; /* compound string for dialog buttons */
574
int n; /* number of arguments */
575
Arg args[MAX_ARGS]; /* arg list */
579
XtSetArg(args[n], XmNdialogStyle, XmDIALOG_APPLICATION_MODAL); n++;
581
XtSetArg(args[n], XmNdialogStyle, XmDIALOG_FULL_APPLICATION_MODAL); n++;
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);
597
static void createErrorDialog(Widget parent)
599
XmString buttonString; /* compound string for dialog button */
600
int n; /* number of arguments */
601
Arg args[MAX_ARGS]; /* arg list */
605
XtSetArg(args[n], XmNdialogStyle, XmDIALOG_APPLICATION_MODAL); n++;
607
XtSetArg(args[n], XmNdialogStyle, XmDIALOG_FULL_APPLICATION_MODAL); n++;
609
XtSetArg(args[n], XmNtitle, " "); n++;
610
ErrorDialog = XmCreateErrorDialog(parent, "error", args, n);
611
XtAddCallback(ErrorDialog, XmNcancelCallback, (XtCallbackProc)errorOKCB,
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);
620
static int doYesNoDialog(char *filename)
625
YesNoResult = ynNone;
627
sprintf(string, "File %s already exists,\nOk to overwrite?", filename);
628
mString = XmStringCreateLtoR(string, XmSTRING_DEFAULT_CHARSET);
630
SET_ONE_RSRC(YesNoDialog, XmNmessageString, mString);
631
XmStringFree(mString);
632
ManageDialogCenteredOnPointer(YesNoDialog);
634
while (YesNoResult == ynNone)
635
XtAppProcessEvent(XtWidgetToApplicationContext(YesNoDialog), XtIMAll);
637
XtUnmanageChild(YesNoDialog);
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);
648
return YesNoResult == ynYes;
651
static void doErrorDialog(char *errorString, char *filename)
658
sprintf(string, errorString, filename);
659
mString = XmStringCreateLtoR(string, XmSTRING_DEFAULT_CHARSET);
661
SET_ONE_RSRC(ErrorDialog, XmNmessageString, mString);
662
XmStringFree(mString);
663
ManageDialogCenteredOnPointer(ErrorDialog);
666
XtAppProcessEvent (XtWidgetToApplicationContext(ErrorDialog), XtIMAll);
668
XtUnmanageChild(ErrorDialog);
671
static void newFileOKCB(Widget w, Boolean *client_data,
672
XmFileSelectionBoxCallbackStruct *call_data)
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 */
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);
690
if (strchr(filename,';') && (fd = open(filename, O_RDONLY, 0)) != -1) {
692
if ((fd = open(filename, O_RDONLY, 0)) != -1) { /* exists */
696
if (buf.st_mode & S_IFDIR) {
697
doErrorDialog("Error: %s is a directory", filename);
700
response = doYesNoDialog(filename);
703
if (access(filename, 2) != 0) { /* have write/delete access? */
704
doErrorDialog("Error: can't overwrite %s ", filename);
715
if ((fd = creat(filename, PERMS)) == -1) {
716
doErrorDialog("Error: can't create %s ", filename);
723
*client_data = True; /* done with dialog */
727
static void newFileCancelCB(Widget w, Boolean *client_data, caddr_t call_data)
729
SelectResult = GFN_CANCEL;
733
static void newHelpCB(Widget w, Widget helpPanel, caddr_t call_data)
735
ManageDialogCenteredOnPointer(helpPanel);
738
static void existOkCB(Widget w, Boolean * client_data,
739
XmFileSelectionBoxCallbackStruct *call_data)
741
char *filename; /* name of chosen file */
742
int fd; /* file descriptor */
743
int length; /* length of file name */
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);
752
} else if ((fd = open(filename, O_RDONLY,0)) == -1) {
753
doErrorDialog("Error: can't open %s ", filename);
760
*client_data = True; /* done with dialog */
764
static void existCancelCB(Widget w, Boolean * client_data, caddr_t call_data)
766
SelectResult = GFN_CANCEL;
767
*client_data = True; /* done with dialog */
770
static void yesNoOKCB(Widget w, caddr_t client_data, caddr_t call_data)
775
static void existHelpCB(Widget w, Widget helpPanel, caddr_t call_data)
777
ManageDialogCenteredOnPointer(helpPanel);
780
static void errorOKCB(Widget w, caddr_t client_data, caddr_t call_data)
785
static void yesNoCancelCB(Widget w, caddr_t client_data, caddr_t call_data)
790
static Widget createPanelHelp(Widget parent, char *helpText, char *title)
794
Widget form, text, button;
798
form = XmCreateFormDialog(parent, "helpForm", al, ac);
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,
810
XtManageChild(button);
811
SET_ONE_RSRC(form, XmNdefaultButton, button);
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++;
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++;
831
text = XmCreateScrolledText(form, "helpText", al, ac);
834
SET_ONE_RSRC(XtParent(form), XmNtitle, title);
839
static void helpDismissCB(Widget w, Widget helpPanel, caddr_t call_data)
841
XtUnmanageChild(helpPanel);
845
** Add ability for user to type filenames to a list widget
847
static void makeListTypeable(Widget listW)
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";
854
/* The first time through, compile translations and register actions */
855
if (translationTable == NULL) {
856
XtAppAddActions(XtWidgetToApplicationContext(listW), actionTable, 1);
857
translationTable = XtParseTranslationTable(translations);
860
/* Add new translations to widget, leaving existing translations intact */
861
XtAugmentTranslations(listW, translationTable);
865
** Action procedure for processing characters typed in a list, finds the
866
** first item matching the characters typed so far.
868
static void listCharCB(Widget w, XEvent *event, String *params, Cardinal n)
870
char charString[5], c, *itemString;
871
int nChars, nItems, i, cmp, selectPos, topPos, nVisible;
874
char name[MAXPATHLEN], path[MAXPATHLEN];
875
static char keystrokes[MAX_LIST_KEYSTROKES];
876
static int nKeystrokes = 0;
877
static Time lastKeyTime = 0;
879
/* Get the ascii character code represented by the event */
880
nChars = XLookupString((XKeyEvent *)event, charString, sizeof(charString),
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;
889
} else if (kSym==XK_Clear || kSym==XK_Escape ||
890
kSym==XK_Cancel || kSym==XK_Break) {
893
} else if (nChars!=1 || c<0x021 || c>0x07e)
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)
900
lastKeyTime = ((XKeyEvent *)event)->time;
902
/* Accumulate the current keystroke, just beep if there are too many */
903
if (nKeystrokes >= MAX_LIST_KEYSTROKES)
904
XBell(XtDisplay(w), 100);
907
keystrokes[nKeystrokes++] = toupper(c);
909
keystrokes[nKeystrokes++] = c;
912
/* Get the items (filenames) in the list widget */
913
XtVaGetValues(w, XmNitems, &items, XmNitemCount, &nItems, 0);
915
/* compare them with the accumulated user keystrokes & decide the
916
appropriate line in the list widget to select */
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);
925
} else if (cmp > 0) {
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);
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.
958
static void replacementDirSearchProc(Widget w, XtPointer searchData)
962
/* Call the original search procedure to do the actual search */
963
(*OrigDirSearchProc)(w, searchData);
964
XtVaGetValues(w, XmNlistUpdated, &updated, 0);
968
/* Sort the items in the list */
969
sortWidgetList(XmFileSelectionBoxGetChild(w, XmDIALOG_DIR_LIST));
971
static void replacementFileSearchProc(Widget w, XtPointer searchData)
975
/* Call the original search procedure to do the actual search */
976
(*OrigFileSearchProc)(w, searchData);
977
XtVaGetValues(w, XmNlistUpdated, &updated, 0);
981
/* Sort the items in the list */
982
sortWidgetList(XmFileSelectionBoxGetChild(w, XmDIALOG_LIST));
986
** Sort the items in a list widget "listWidget"
988
static void sortWidgetList(Widget listWidget)
990
XmString *items, *sortedItems;
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);
1002
** Compare procedure for qsort for sorting a list of XmStrings
1004
static int compareXmStrings(const void *string1, const void *string2)
1009
XmStringGetLtoR(*(XmString *)string1, XmSTRING_DEFAULT_CHARSET, &s1);
1010
XmStringGetLtoR(*(XmString *)string2, XmSTRING_DEFAULT_CHARSET, &s2);
1011
result = strcmp(s1, s2);