2e3495589291cf45700edfcd2816971b1d3453b0
[xboard.git] / filebrowser / selfile.c
1 /*
2  * Copyright 1989 Software Research Associates, Inc., Tokyo, Japan
3  *
4  * Permission to use, copy, modify, and distribute this software and its
5  * documentation for any purpose and without fee is hereby granted, provided
6  * that the above copyright notice appear in all copies and that both that
7  * copyright notice and this permission notice appear in supporting
8  * documentation, and that the name of Software Research Associates not be used
9  * in advertising or publicity pertaining to distribution of the software
10  * without specific, written prior permission.  Software Research Associates
11  * makes no representations about the suitability of this software for any
12  * purpose.  It is provided "as is" without express or implied warranty.
13  *
14  * SOFTWARE RESEARCH ASSOCIATES DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
15  * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
16  * IN NO EVENT SHALL SOFTWARE RESEARCH ASSOCIATES BE LIABLE FOR ANY SPECIAL,
17  * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
18  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
19  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
20  * PERFORMANCE OF THIS SOFTWARE.
21  *
22  * Author: Erik M. van der Poel
23  *         Software Research Associates, Inc., Tokyo, Japan
24  *         erik@sra.co.jp
25  */
26
27 /*
28  * Author's address:
29  *
30  *     erik@sra.co.jp
31  *                                            OR
32  *     erik%sra.co.jp@uunet.uu.net
33  *                                            OR
34  *     erik%sra.co.jp@mcvax.uucp
35  *                                            OR
36  *     try junet instead of co.jp
37  *                                            OR
38  *     Erik M. van der Poel
39  *     Software Research Associates, Inc.
40  *     1-1-1 Hirakawa-cho, Chiyoda-ku
41  *     Tokyo 102 Japan. TEL +81-3-234-2692
42  */
43
44 #include <stdio.h>
45 #include <errno.h>
46 /* BSD 4.3 errno.h does not declare errno */
47 extern int errno;
48 //extern int sys_nerr;
49 //extern char *sys_errlist[]; // [HGM] this produced a compile error in Ubuntu 8.04
50
51 #include <sys/param.h>
52 #include <X11/cursorfont.h>
53 #include <X11/Intrinsic.h>
54 #include <X11/StringDefs.h>
55 #include <X11/Composite.h>
56 #include <X11/Shell.h>
57 #include <X11/Xaw/Form.h>
58 #include <X11/Xaw/Command.h>
59 #include <X11/Xaw/Scrollbar.h>
60 #include <X11/Xaw/Label.h>
61 #include <X11/Xaw/Cardinals.h>
62
63 #include "selfile.h"
64 #include "xstat.h"
65
66 /* added missing prototypes */
67 extern void SFdrawList(int,int);
68 extern void SFinitFont();
69 extern void SFcreateGC();
70 extern int SFchdir(char *);
71 extern void SFupdatePath();
72 extern void SFsetText(char *);
73 extern char SFstatChar(struct stat*);
74
75 #ifndef MAXPATHLEN
76 #define MAXPATHLEN 1024
77 #endif /* ndef MAXPATHLEN */
78
79 #if !defined(SVR4) && !defined(SYSV) && !defined(USG)
80 extern char *getwd();
81 #endif /* !defined(SVR4) && !defined(SYSV) && !defined(USG) */
82
83 int SFstatus = SEL_FILE_NULL;
84
85 char
86         SFstartDir[MAXPATHLEN],
87         SFcurrentPath[MAXPATHLEN],
88         SFcurrentDir[MAXPATHLEN];
89
90 Widget
91         selFile,
92         selFileCancel,
93         selFileField,
94         selFileForm,
95         selFileHScroll,
96         selFileHScrolls[3],
97         selFileLists[3],
98         selFileOK,
99         selFilePrompt,
100         selFileVScrolls[3];
101
102 Display *SFdisplay;
103
104 Pixel SFfore, SFback;
105
106 Atom    SFwmDeleteWindow;
107
108 XSegment SFsegs[2], SFcompletionSegs[2];
109
110 XawTextPosition SFtextPos;
111
112 int SFupperX, SFlowerY, SFupperY;
113
114 int SFtextX, SFtextYoffset;
115
116 int SFentryWidth, SFentryHeight;
117
118 int SFlineToTextH = 3;
119
120 int SFlineToTextV = 3;
121
122 int SFbesideText = 3;
123
124 int SFaboveAndBelowText = 2;
125
126 int SFcharsPerEntry = 15;
127
128 int SFlistSize = 10;
129
130 int SFworkProcAdded = 0;
131
132 XtAppContext SFapp;
133
134 int SFpathScrollWidth, SFvScrollHeight, SFhScrollWidth;
135
136 char SFtextBuffer[MAXPATHLEN];
137
138 XtIntervalId SFdirModTimerId;
139
140 int (*SFfunc)();
141
142 Boolean SFpathFlag; // [HGM]
143
144 static char *oneLineTextEditTranslations = "\
145         <Key>Return:    redraw-display()\n\
146         Ctrl<Key>M:     redraw-display()\n\
147 ";
148
149 /* ARGSUSED */
150 static void
151 SFexposeList(w, n, event, cont)
152         Widget          w;
153         XtPointer       n;
154         XEvent          *event;
155         Boolean         *cont;
156 {
157         if ((event->type == NoExpose) || event->xexpose.count) {
158                 return;
159         }
160
161         SFdrawList((int)(intptr_t)n, SF_DO_NOT_SCROLL);
162 }
163
164 /* ARGSUSED */
165 static void
166 SFmodVerifyCallback(w, client_data, event, cont)
167         Widget                  w;
168         XtPointer               client_data;
169         XEvent                  *event;
170         Boolean                 *cont;
171 {
172         char    buf[2];
173
174         if (
175                 (XLookupString(&(event->xkey), buf, 2, NULL, NULL) == 1) &&
176                 ((*buf) == '\r')
177         ) {
178                 SFstatus = SEL_FILE_OK;
179         } else {
180                 SFstatus = SEL_FILE_TEXT;
181         }
182 }
183
184 /* ARGSUSED */
185 static void
186 SFokCallback(w, cl, cd)
187         Widget  w;
188         XtPointer cl, cd;
189 {
190         SFstatus = SEL_FILE_OK;
191 }
192
193 static XtCallbackRec SFokSelect[] = {
194         { SFokCallback, (XtPointer) NULL },
195         { NULL, (XtPointer) NULL },
196 };
197
198 /* ARGSUSED */
199 static void
200 SFcancelCallback(w, cl, cd)
201         Widget  w;
202         XtPointer cl, cd;
203 {
204         SFstatus = SEL_FILE_CANCEL;
205 }
206
207 static XtCallbackRec SFcancelSelect[] = {
208         { SFcancelCallback, (XtPointer) NULL },
209         { NULL, (XtPointer) NULL },
210 };
211
212 /* ARGSUSED */
213 static void
214 SFdismissAction(w, event, params, num_params)
215         Widget  w;
216         XEvent *event;
217         String *params;
218         Cardinal *num_params;
219 {
220         if (event->type == ClientMessage &&
221             event->xclient.data.l[0] != SFwmDeleteWindow) return;
222
223         SFstatus = SEL_FILE_CANCEL;
224 }
225
226 static char *wmDeleteWindowTranslation = "\
227         <Message>WM_PROTOCOLS:  SelFileDismiss()\n\
228 ";
229
230 static XtActionsRec actions[] = {
231         {"SelFileDismiss",      SFdismissAction},
232 };
233
234 static void
235 SFcreateWidgets(toplevel, prompt, ok, cancel)
236         Widget  toplevel;
237         char    *prompt;
238         char    *ok;
239         char    *cancel;
240 {
241         Cardinal        i, n;
242         int             listWidth, listHeight;
243         int             listSpacing = 10;
244         int             scrollThickness = 15;
245         int             hScrollX, hScrollY;
246         int             vScrollX, vScrollY;
247         Cursor
248                         xtermCursor,
249                         sbRightArrowCursor,
250                         dotCursor;
251         Arg             arglist[20];
252
253         i = 0;
254         XtSetArg(arglist[i], XtNtransientFor, toplevel);                i++;
255
256         selFile = XtAppCreateShell("Browse", "SelFile",
257                 transientShellWidgetClass, SFdisplay, arglist, i);
258
259         /* Add WM_DELETE_WINDOW protocol */
260         XtAppAddActions(XtWidgetToApplicationContext(selFile),
261                 actions, XtNumber(actions));
262         XtOverrideTranslations(selFile,
263                 XtParseTranslationTable(wmDeleteWindowTranslation));
264
265         i = 0;
266         XtSetArg(arglist[i], XtNdefaultDistance, 30);                   i++;
267         selFileForm = XtCreateManagedWidget("selFileForm",
268                 formWidgetClass, selFile, arglist, i);
269
270         i = 0;
271         XtSetArg(arglist[i], XtNlabel, prompt);                         i++;
272         XtSetArg(arglist[i], XtNresizable, True);                       i++;
273         XtSetArg(arglist[i], XtNtop, XtChainTop);                       i++;
274         XtSetArg(arglist[i], XtNbottom, XtChainTop);                    i++;
275         XtSetArg(arglist[i], XtNleft, XtChainLeft);                     i++;
276         XtSetArg(arglist[i], XtNright, XtChainLeft);                    i++;
277         XtSetArg(arglist[i], XtNborderWidth, 0);                        i++;
278         selFilePrompt = XtCreateManagedWidget("selFilePrompt",
279                 labelWidgetClass, selFileForm, arglist, i);
280
281         i = 0;
282         XtSetArg(arglist[i], XtNforeground, &SFfore);                   i++;
283         XtSetArg(arglist[i], XtNbackground, &SFback);                   i++;
284         XtGetValues(selFilePrompt, arglist, i);
285
286         SFinitFont();
287
288         SFentryWidth = SFbesideText + SFcharsPerEntry * SFcharWidth +
289                         SFbesideText;
290         SFentryHeight = SFaboveAndBelowText + SFcharHeight +
291                         SFaboveAndBelowText;
292
293         listWidth = SFlineToTextH + SFentryWidth + SFlineToTextH + 1 +
294                         scrollThickness;
295         listHeight = SFlineToTextV + SFentryHeight + SFlineToTextV + 1 +
296                         SFlineToTextV + SFlistSize * SFentryHeight +
297                         SFlineToTextV + 1 + scrollThickness;
298
299         SFpathScrollWidth = NR * listWidth + (NR-1) * listSpacing + 4;
300
301         hScrollX = -1;
302         hScrollY = SFlineToTextV + SFentryHeight + SFlineToTextV + 1 +
303                         SFlineToTextV + SFlistSize * SFentryHeight +
304                         SFlineToTextV;
305         SFhScrollWidth = SFlineToTextH + SFentryWidth + SFlineToTextH;
306
307         vScrollX = SFlineToTextH + SFentryWidth + SFlineToTextH;
308         vScrollY = SFlineToTextV + SFentryHeight + SFlineToTextV;
309         SFvScrollHeight = SFlineToTextV + SFlistSize * SFentryHeight +
310                         SFlineToTextV;
311
312         SFupperX = SFlineToTextH + SFentryWidth + SFlineToTextH - 1;
313         SFlowerY = SFlineToTextV + SFentryHeight + SFlineToTextV + 1 +
314                         SFlineToTextV;
315         SFupperY = SFlineToTextV + SFentryHeight + SFlineToTextV + 1 +
316                         SFlineToTextV + SFlistSize * SFentryHeight - 1;
317
318         SFtextX = SFlineToTextH + SFbesideText;
319         SFtextYoffset = SFlowerY + SFaboveAndBelowText + SFcharAscent;
320
321         SFsegs[0].x1 = 0;
322         SFsegs[0].y1 = vScrollY;
323         SFsegs[0].x2 = vScrollX - 1;
324         SFsegs[0].y2 = vScrollY;
325         SFsegs[1].x1 = vScrollX;
326         SFsegs[1].y1 = 0;
327         SFsegs[1].x2 = vScrollX;
328         SFsegs[1].y2 = vScrollY - 1;
329
330         SFcompletionSegs[0].x1 = SFcompletionSegs[0].x2 = SFlineToTextH;
331         SFcompletionSegs[1].x1 = SFcompletionSegs[1].x2 =
332                 SFlineToTextH + SFentryWidth - 1;
333
334         i = 0;
335         XtSetArg(arglist[i], XtNwidth, NR * listWidth + (NR - 1) * listSpacing + 4);
336                                                                         i++;
337         XtSetArg(arglist[i], XtNborderColor, SFfore);                   i++;
338
339         XtSetArg(arglist[i], XtNfromVert, selFilePrompt);               i++;
340         XtSetArg(arglist[i], XtNvertDistance, 10);                      i++;
341         XtSetArg(arglist[i], XtNresizable, True);                       i++;
342         XtSetArg(arglist[i], XtNtop, XtChainTop);                       i++;
343         XtSetArg(arglist[i], XtNbottom, XtChainTop);                    i++;
344         XtSetArg(arglist[i], XtNleft, XtChainLeft);                     i++;
345         XtSetArg(arglist[i], XtNright, XtChainLeft);                    i++;
346         XtSetArg(arglist[i], XtNstring, SFtextBuffer);                  i++;
347         XtSetArg(arglist[i], XtNlength, MAXPATHLEN);                    i++;
348         XtSetArg(arglist[i], XtNeditType, XawtextEdit);                 i++;
349         XtSetArg(arglist[i], XtNwrap, XawtextWrapWord);                 i++;
350         XtSetArg(arglist[i], XtNresize, XawtextResizeHeight);           i++;
351         XtSetArg(arglist[i], XtNuseStringInPlace, True);                i++;
352         selFileField = XtCreateManagedWidget("selFileField",
353                 asciiTextWidgetClass, selFileForm, arglist, i);
354
355         XtOverrideTranslations(selFileField,
356                 XtParseTranslationTable(oneLineTextEditTranslations));
357         XtSetKeyboardFocus(selFileForm, selFileField);
358
359         i = 0;
360         XtSetArg(arglist[i], XtNorientation, XtorientHorizontal);       i++;
361         XtSetArg(arglist[i], XtNwidth, SFpathScrollWidth);              i++;
362         XtSetArg(arglist[i], XtNheight, scrollThickness);               i++;
363         XtSetArg(arglist[i], XtNborderColor, SFfore);                   i++;
364         XtSetArg(arglist[i], XtNfromVert, selFileField);                i++;
365         XtSetArg(arglist[i], XtNvertDistance, 30);                      i++;
366         XtSetArg(arglist[i], XtNtop, XtChainTop);                       i++;
367         XtSetArg(arglist[i], XtNbottom, XtChainTop);                    i++;
368         XtSetArg(arglist[i], XtNleft, XtChainLeft);                     i++;
369         XtSetArg(arglist[i], XtNright, XtChainLeft);                    i++;
370         selFileHScroll = XtCreateManagedWidget("selFileHScroll",
371                 scrollbarWidgetClass, selFileForm, arglist, i);
372
373         XtAddCallback(selFileHScroll, XtNjumpProc,
374                 SFpathSliderMovedCallback, (XtPointer) NULL);
375         XtAddCallback(selFileHScroll, XtNscrollProc,
376                 SFpathAreaSelectedCallback, (XtPointer) NULL);
377
378         i = 0;
379         XtSetArg(arglist[i], XtNwidth, listWidth);                      i++;
380         XtSetArg(arglist[i], XtNheight, listHeight);                    i++;
381         XtSetArg(arglist[i], XtNborderColor, SFfore);                   i++;
382         XtSetArg(arglist[i], XtNfromVert, selFileHScroll);              i++;
383         XtSetArg(arglist[i], XtNvertDistance, 10);                      i++;
384         XtSetArg(arglist[i], XtNtop, XtChainTop);                       i++;
385         XtSetArg(arglist[i], XtNbottom, XtChainTop);                    i++;
386         XtSetArg(arglist[i], XtNleft, XtChainLeft);                     i++;
387         XtSetArg(arglist[i], XtNright, XtChainLeft);                    i++;
388         selFileLists[0] = XtCreateManagedWidget("selFileList1",
389                 compositeWidgetClass, selFileForm, arglist, i);
390 #if (NR == 3)
391         i = 0;
392         XtSetArg(arglist[i], XtNwidth, listWidth);                      i++;
393         XtSetArg(arglist[i], XtNheight, listHeight);                    i++;
394         XtSetArg(arglist[i], XtNborderColor, SFfore);                   i++;
395         XtSetArg(arglist[i], XtNfromHoriz, selFileLists[0]);            i++;
396         XtSetArg(arglist[i], XtNfromVert, selFileHScroll);              i++;
397         XtSetArg(arglist[i], XtNhorizDistance, listSpacing);            i++;
398         XtSetArg(arglist[i], XtNvertDistance, 10);                      i++;
399         XtSetArg(arglist[i], XtNtop, XtChainTop);                       i++;
400         XtSetArg(arglist[i], XtNbottom, XtChainTop);                    i++;
401         XtSetArg(arglist[i], XtNleft, XtChainLeft);                     i++;
402         XtSetArg(arglist[i], XtNright, XtChainLeft);                    i++;
403         selFileLists[1] = XtCreateManagedWidget("selFileList2",
404                 compositeWidgetClass, selFileForm, arglist, i);
405
406         i = 0;
407         XtSetArg(arglist[i], XtNwidth, listWidth);                      i++;
408         XtSetArg(arglist[i], XtNheight, listHeight);                    i++;
409         XtSetArg(arglist[i], XtNborderColor, SFfore);                   i++;
410         XtSetArg(arglist[i], XtNfromHoriz, selFileLists[1]);            i++;
411         XtSetArg(arglist[i], XtNfromVert, selFileHScroll);              i++;
412         XtSetArg(arglist[i], XtNhorizDistance, listSpacing);            i++;
413         XtSetArg(arglist[i], XtNvertDistance, 10);                      i++;
414         XtSetArg(arglist[i], XtNtop, XtChainTop);                       i++;
415         XtSetArg(arglist[i], XtNbottom, XtChainTop);                    i++;
416         XtSetArg(arglist[i], XtNleft, XtChainLeft);                     i++;
417         XtSetArg(arglist[i], XtNright, XtChainLeft);                    i++;
418         selFileLists[2] = XtCreateManagedWidget("selFileList3",
419                 compositeWidgetClass, selFileForm, arglist, i);
420 #endif
421         for (n = 0; n < NR; n++) {
422
423                 i = 0;
424                 XtSetArg(arglist[i], XtNx, vScrollX);                   i++;
425                 XtSetArg(arglist[i], XtNy, vScrollY);                   i++;
426                 XtSetArg(arglist[i], XtNwidth, scrollThickness);        i++;
427                 XtSetArg(arglist[i], XtNheight, SFvScrollHeight);       i++;
428                 XtSetArg(arglist[i], XtNborderColor, SFfore);           i++;
429                 selFileVScrolls[n] = XtCreateManagedWidget("selFileVScroll",
430                         scrollbarWidgetClass, selFileLists[n], arglist, i);
431
432                 XtAddCallback(selFileVScrolls[n], XtNjumpProc,
433                         SFvFloatSliderMovedCallback, (XtPointer)(intptr_t) n);
434                 XtAddCallback(selFileVScrolls[n], XtNscrollProc,
435                         SFvAreaSelectedCallback, (XtPointer)(intptr_t) n);
436
437                 i = 0;
438
439                 XtSetArg(arglist[i], XtNorientation, XtorientHorizontal);
440                                                                         i++;
441                 XtSetArg(arglist[i], XtNx, hScrollX);                   i++;
442                 XtSetArg(arglist[i], XtNy, hScrollY);                   i++;
443                 XtSetArg(arglist[i], XtNwidth, SFhScrollWidth);         i++;
444                 XtSetArg(arglist[i], XtNheight, scrollThickness);       i++;
445                 XtSetArg(arglist[i], XtNborderColor, SFfore);           i++;
446                 selFileHScrolls[n] = XtCreateManagedWidget("selFileHScroll",
447                         scrollbarWidgetClass, selFileLists[n], arglist, i);
448
449                 XtAddCallback(selFileHScrolls[n], XtNjumpProc,
450                         SFhSliderMovedCallback, (XtPointer)(intptr_t) n);
451                 XtAddCallback(selFileHScrolls[n], XtNscrollProc,
452                         SFhAreaSelectedCallback, (XtPointer)(intptr_t) n);
453         }
454
455         i = 0;
456         XtSetArg(arglist[i], XtNlabel, ok);                             i++;
457         XtSetArg(arglist[i], XtNresizable, True);                       i++;
458         XtSetArg(arglist[i], XtNcallback, SFokSelect);                  i++;
459         XtSetArg(arglist[i], XtNborderColor, SFfore);                   i++;
460         XtSetArg(arglist[i], XtNfromVert, selFileLists[0]);             i++;
461         XtSetArg(arglist[i], XtNvertDistance, 30);                      i++;
462         XtSetArg(arglist[i], XtNtop, XtChainTop);                       i++;
463         XtSetArg(arglist[i], XtNbottom, XtChainTop);                    i++;
464         XtSetArg(arglist[i], XtNleft, XtChainLeft);                     i++;
465         XtSetArg(arglist[i], XtNright, XtChainLeft);                    i++;
466         selFileOK = XtCreateManagedWidget("selFileOK", commandWidgetClass,
467                 selFileForm, arglist, i);
468
469         i = 0;
470         XtSetArg(arglist[i], XtNlabel, cancel);                         i++;
471         XtSetArg(arglist[i], XtNresizable, True);                       i++;
472         XtSetArg(arglist[i], XtNcallback, SFcancelSelect);              i++;
473         XtSetArg(arglist[i], XtNborderColor, SFfore);                   i++;
474         XtSetArg(arglist[i], XtNfromHoriz, selFileOK);                  i++;
475         XtSetArg(arglist[i], XtNfromVert, selFileLists[0]);             i++;
476         XtSetArg(arglist[i], XtNhorizDistance, 30);                     i++;
477         XtSetArg(arglist[i], XtNvertDistance, 30);                      i++;
478         XtSetArg(arglist[i], XtNtop, XtChainTop);                       i++;
479         XtSetArg(arglist[i], XtNbottom, XtChainTop);                    i++;
480         XtSetArg(arglist[i], XtNleft, XtChainLeft);                     i++;
481         XtSetArg(arglist[i], XtNright, XtChainLeft);                    i++;
482         selFileCancel = XtCreateManagedWidget("selFileCancel",
483                 commandWidgetClass, selFileForm, arglist, i);
484
485         XtSetMappedWhenManaged(selFile, False);
486         XtRealizeWidget(selFile);
487
488         /* Add WM_DELETE_WINDOW protocol */
489         SFwmDeleteWindow = XInternAtom(SFdisplay, "WM_DELETE_WINDOW", False);
490         XSetWMProtocols(SFdisplay, XtWindow(selFile), &SFwmDeleteWindow, 1);
491
492         SFcreateGC();
493
494         xtermCursor = XCreateFontCursor(SFdisplay, XC_xterm);
495
496         sbRightArrowCursor = XCreateFontCursor(SFdisplay, XC_sb_right_arrow);
497         dotCursor = XCreateFontCursor(SFdisplay, XC_dot);
498
499         XDefineCursor(SFdisplay, XtWindow(selFileForm), xtermCursor);
500         XDefineCursor(SFdisplay, XtWindow(selFileField), xtermCursor);
501
502         for (n = 0; n < NR; n++) {
503                 XDefineCursor(SFdisplay, XtWindow(selFileLists[n]),
504                         sbRightArrowCursor);
505         }
506         XDefineCursor(SFdisplay, XtWindow(selFileOK), dotCursor);
507         XDefineCursor(SFdisplay, XtWindow(selFileCancel), dotCursor);
508
509         for (n = 0; n < NR; n++) {
510                 XtAddEventHandler(selFileLists[n], ExposureMask, True,
511                         SFexposeList, (XtPointer)(intptr_t) n);
512                 XtAddEventHandler(selFileLists[n], EnterWindowMask, False,
513                         SFenterList, (XtPointer)(intptr_t) n);
514                 XtAddEventHandler(selFileLists[n], LeaveWindowMask, False,
515                         SFleaveList, (XtPointer)(intptr_t) n);
516                 XtAddEventHandler(selFileLists[n], PointerMotionMask, False,
517                         SFmotionList, (XtPointer)(intptr_t) n);
518                 XtAddEventHandler(selFileLists[n], ButtonPressMask, False,
519                         SFbuttonPressList, (XtPointer)(intptr_t) n);
520                 XtAddEventHandler(selFileLists[n], ButtonReleaseMask, False,
521                         SFbuttonReleaseList, (XtPointer)(intptr_t) n);
522         }
523
524         XtAddEventHandler(selFileField, KeyPressMask, False,
525                 SFmodVerifyCallback, (XtPointer) NULL);
526
527         SFapp = XtWidgetToApplicationContext(selFile);
528
529 }
530
531 /* position widget under the cursor */
532 void
533 SFpositionWidget(w)
534     Widget w;
535 {
536     Arg args[3];
537     Cardinal num_args;
538     Dimension width, height, b_width;
539     int x, y, max_x, max_y;
540     Window root, child;
541     int dummyx, dummyy;
542     unsigned int dummymask;
543     
544     XQueryPointer(XtDisplay(w), XtWindow(w), &root, &child, &x, &y,
545                   &dummyx, &dummyy, &dummymask);
546     num_args = 0;
547     XtSetArg(args[num_args], XtNwidth, &width); num_args++;
548     XtSetArg(args[num_args], XtNheight, &height); num_args++;
549     XtSetArg(args[num_args], XtNborderWidth, &b_width); num_args++;
550     XtGetValues(w, args, num_args);
551
552     width += 2 * b_width;
553     height += 2 * b_width;
554
555     x -= ( (Position) width/2 );
556     if (x < 0) x = 0;
557     if ( x > (max_x = (Position) (XtScreen(w)->width - width)) ) x = max_x;
558
559     y -= ( (Position) height/2 );
560     if (y < 0) y = 0;
561     if ( y > (max_y = (Position) (XtScreen(w)->height - height)) ) y = max_y;
562     
563     num_args = 0;
564     XtSetArg(args[num_args], XtNx, x); num_args++;
565     XtSetArg(args[num_args], XtNy, y); num_args++;
566     XtSetValues(w, args, num_args);
567 }
568
569 FILE *
570 SFopenFile(name, mode, prompt, failed)
571     char *name;
572     char *mode;
573     char *prompt;
574     char *failed;
575 {
576     Arg args[1];
577     FILE *fp;
578
579     SFchdir(SFstartDir);
580     if ((fp = fopen(name, mode)) == NULL) {
581         char *buf;
582         if (1) { // [HGM] always use strerror
583             buf = XtMalloc(strlen(failed) + strlen(strerror(errno)) + 
584                            strlen(prompt) + 2);
585             strcpy(buf, failed);
586             strcat(buf, strerror(errno));
587             strcat(buf, "\n");
588             strcat(buf, prompt);
589         } else {
590             buf = XtMalloc(strlen(failed) + strlen(prompt) + 2);
591             strcpy(buf, failed);
592             strcat(buf, "\n");
593             strcat(buf, prompt);
594         }
595         XtSetArg(args[0], XtNlabel, buf);
596         XtSetValues(selFilePrompt, args, ONE);
597         XtFree(buf);
598         return NULL;
599     }
600     return fp;
601 }
602
603 void
604 SFtextChanged()
605 {
606
607         if ((SFtextBuffer[0] == '/') || (SFtextBuffer[0] == '~')) {
608           (void) strncpy(SFcurrentPath, SFtextBuffer, MAXPATHLEN);
609
610                 SFtextPos = XawTextGetInsertionPoint(selFileField);
611         } else {
612           (void) strcat(strncpy(SFcurrentPath, SFstartDir, MAXPATHLEN), SFtextBuffer);
613
614                 SFtextPos = XawTextGetInsertionPoint(selFileField) +
615                         strlen(SFstartDir);
616         }
617
618         if (!SFworkProcAdded) {
619                 (void) XtAppAddWorkProc(SFapp, SFworkProc, NULL);
620                 SFworkProcAdded = 1;
621         }
622
623         SFupdatePath();
624         return;
625 }
626
627 static char *
628 SFgetText()
629 {
630         return strcpy(XtMalloc((unsigned) (strlen(SFtextBuffer) + 1)),
631                 SFtextBuffer);
632 }
633
634 static void
635 SFprepareToReturn()
636 {
637         SFstatus = SEL_FILE_NULL;
638         XtRemoveGrab(selFile);
639         XtUnmapWidget(selFile);
640         XtRemoveTimeOut(SFdirModTimerId);
641         if (SFchdir(SFstartDir)) {
642                 XtAppError(
643                         SFapp,
644                         "XsraSelFile: can't return to current directory"
645                 );
646         }
647         return;
648 }
649
650 FILE *
651 XsraSelFile(toplevel, prompt, ok, cancel, failed,
652             init_path, mode, show_entry, name_return)
653         Widget          toplevel;
654         char            *prompt;
655         char            *ok;
656         char            *cancel;
657         char            *failed;
658         char            *init_path;
659         char            *mode;
660         int             (*show_entry)();
661         char            **name_return;
662 {
663         static int      firstTime = 1;
664         Cardinal        i;
665         Arg             arglist[20];
666         XEvent          event;
667         FILE            *fp;
668
669         if (!prompt) {
670                 prompt = "Pathname:";
671         }
672
673         if (!ok) {
674                 ok = "OK";
675         }
676
677         if (!cancel) {
678                 cancel = "Cancel";
679         }
680
681         if(SFpathFlag != (mode && mode[0] == 'p')) { // [HGM] ignore everything that is not a directory
682                 if(SFdirs) XtFree(SFdirs);
683                 SFdirs = NULL; // kludge to throw away all cached info
684                 SFpathFlag = !SFpathFlag;
685         }
686
687         if (firstTime) {
688                 firstTime = 0;
689                 SFdisplay = XtDisplay(toplevel);
690                 SFcreateWidgets(toplevel, prompt, ok, cancel);
691         } else {
692                 i = 0;
693
694                 XtSetArg(arglist[i], XtNlabel, prompt);                 i++;
695                 XtSetValues(selFilePrompt, arglist, i);
696
697                 i = 0;
698                 XtSetArg(arglist[i], XtNlabel, ok);                     i++;
699                 XtSetValues(selFileOK, arglist, i);
700
701                 i = 0;
702                 XtSetArg(arglist[i], XtNlabel, cancel);                 i++;
703                 XtSetValues(selFileCancel, arglist, i);
704         }
705
706         SFpositionWidget(selFile);
707         XtMapWidget(selFile);
708
709 #if defined(SVR4) || defined(SYSV) || defined(USG) || 1
710         if (!getcwd(SFstartDir, MAXPATHLEN)) { // [HGM] always do this, as I do not know when exactly to do it
711 #else /* defined(SVR4) || defined(SYSV) || defined(USG) */
712         if (!getwd(SFstartDir)) {
713 #endif /* defined(SVR4) || defined(SYSV) || defined(USG) */
714
715                 XtAppError(SFapp, "XsraSelFile: can't get current directory");
716         }
717         (void) strcat(SFstartDir, "/");
718         (void) strncpy(SFcurrentDir, SFstartDir, MAXPATHLEN);
719
720         if (init_path) {
721                 if (init_path[0] == '/') {
722                   (void) strncpy(SFcurrentPath, init_path, MAXPATHLEN);
723                         if (strncmp(
724                                 SFcurrentPath,
725                                 SFstartDir,
726                                 strlen(SFstartDir)
727                         )) {
728                                 SFsetText(SFcurrentPath);
729                         } else {
730                                 SFsetText(&(SFcurrentPath[strlen(SFstartDir)]));
731                         }
732                 } else {
733                   (void) strcat(strncpy(SFcurrentPath, SFstartDir, MAXPATHLEN),
734                                 init_path);
735                         SFsetText(&(SFcurrentPath[strlen(SFstartDir)]));
736                 }
737         } else {
738           (void) strncpy(SFcurrentPath, SFstartDir, MAXPATHLEN);
739         }
740
741         SFfunc = show_entry;
742
743         SFtextChanged();
744
745         XtAddGrab(selFile, True, True);
746
747         SFdirModTimerId = XtAppAddTimeOut(SFapp, (unsigned long) 1000,
748                 SFdirModTimer, (XtPointer) NULL);
749
750         while (1) {
751                 XtAppNextEvent(SFapp, &event);
752                 XtDispatchEvent(&event);
753                 switch (SFstatus) {
754                 case SEL_FILE_TEXT:
755                         SFstatus = SEL_FILE_NULL;
756                         SFtextChanged();
757                         break;
758                 case SEL_FILE_OK:
759                         *name_return = SFgetText();
760                         if(mode && (mode[0] == 'p' || mode[0] == 'f')) { // [HGM] for use in file-option browse button
761                                 SFprepareToReturn();
762                                 return stderr;
763                         }
764                         if (fp = SFopenFile(*name_return, mode,
765                                             prompt, failed)) {
766                                 SFprepareToReturn();
767                                 return fp;
768                         }
769                         SFstatus = SEL_FILE_NULL;
770                         break;
771                 case SEL_FILE_CANCEL:
772                         SFprepareToReturn();
773                         return NULL;
774                 case SEL_FILE_NULL:
775                         break;
776                 }
777         }
778 }