Fix some resource leaks
[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 static char *oneLineTextEditTranslations = "\
143         <Key>Return:    redraw-display()\n\
144         Ctrl<Key>M:     redraw-display()\n\
145 ";
146
147 /* ARGSUSED */
148 static void
149 SFexposeList(w, n, event, cont)
150         Widget          w;
151         XtPointer       n;
152         XEvent          *event;
153         Boolean         *cont;
154 {
155         if ((event->type == NoExpose) || event->xexpose.count) {
156                 return;
157         }
158
159         SFdrawList((int)n, SF_DO_NOT_SCROLL);
160 }
161
162 /* ARGSUSED */
163 static void
164 SFmodVerifyCallback(w, client_data, event, cont)
165         Widget                  w;
166         XtPointer               client_data;
167         XEvent                  *event;
168         Boolean                 *cont;
169 {
170         char    buf[2];
171
172         if (
173                 (XLookupString(&(event->xkey), buf, 2, NULL, NULL) == 1) &&
174                 ((*buf) == '\r')
175         ) {
176                 SFstatus = SEL_FILE_OK;
177         } else {
178                 SFstatus = SEL_FILE_TEXT;
179         }
180 }
181
182 /* ARGSUSED */
183 static void
184 SFokCallback(w, cl, cd)
185         Widget  w;
186         XtPointer cl, cd;
187 {
188         SFstatus = SEL_FILE_OK;
189 }
190
191 static XtCallbackRec SFokSelect[] = {
192         { SFokCallback, (XtPointer) NULL },
193         { NULL, (XtPointer) NULL },
194 };
195
196 /* ARGSUSED */
197 static void
198 SFcancelCallback(w, cl, cd)
199         Widget  w;
200         XtPointer cl, cd;
201 {
202         SFstatus = SEL_FILE_CANCEL;
203 }
204
205 static XtCallbackRec SFcancelSelect[] = {
206         { SFcancelCallback, (XtPointer) NULL },
207         { NULL, (XtPointer) NULL },
208 };
209
210 /* ARGSUSED */
211 static void
212 SFdismissAction(w, event, params, num_params)
213         Widget  w;
214         XEvent *event;
215         String *params;
216         Cardinal *num_params;
217 {
218         if (event->type == ClientMessage &&
219             event->xclient.data.l[0] != SFwmDeleteWindow) return;
220
221         SFstatus = SEL_FILE_CANCEL;
222 }
223
224 static char *wmDeleteWindowTranslation = "\
225         <Message>WM_PROTOCOLS:  SelFileDismiss()\n\
226 ";
227
228 static XtActionsRec actions[] = {
229         {"SelFileDismiss",      SFdismissAction},
230 };
231
232 static void
233 SFcreateWidgets(toplevel, prompt, ok, cancel)
234         Widget  toplevel;
235         char    *prompt;
236         char    *ok;
237         char    *cancel;
238 {
239         Cardinal        i, n;
240         int             listWidth, listHeight;
241         int             listSpacing = 10;
242         int             scrollThickness = 15;
243         int             hScrollX, hScrollY;
244         int             vScrollX, vScrollY;
245         Cursor
246                         xtermCursor,
247                         sbRightArrowCursor,
248                         dotCursor;
249         Arg             arglist[20];
250
251         i = 0;
252         XtSetArg(arglist[i], XtNtransientFor, toplevel);                i++;
253
254         selFile = XtAppCreateShell("Browse", "SelFile",
255                 transientShellWidgetClass, SFdisplay, arglist, i);
256
257         /* Add WM_DELETE_WINDOW protocol */
258         XtAppAddActions(XtWidgetToApplicationContext(selFile),
259                 actions, XtNumber(actions));
260         XtOverrideTranslations(selFile,
261                 XtParseTranslationTable(wmDeleteWindowTranslation));
262
263         i = 0;
264         XtSetArg(arglist[i], XtNdefaultDistance, 30);                   i++;
265         selFileForm = XtCreateManagedWidget("selFileForm",
266                 formWidgetClass, selFile, arglist, i);
267
268         i = 0;
269         XtSetArg(arglist[i], XtNlabel, prompt);                         i++;
270         XtSetArg(arglist[i], XtNresizable, True);                       i++;
271         XtSetArg(arglist[i], XtNtop, XtChainTop);                       i++;
272         XtSetArg(arglist[i], XtNbottom, XtChainTop);                    i++;
273         XtSetArg(arglist[i], XtNleft, XtChainLeft);                     i++;
274         XtSetArg(arglist[i], XtNright, XtChainLeft);                    i++;
275         XtSetArg(arglist[i], XtNborderWidth, 0);                        i++;
276         selFilePrompt = XtCreateManagedWidget("selFilePrompt",
277                 labelWidgetClass, selFileForm, arglist, i);
278
279         i = 0;
280         XtSetArg(arglist[i], XtNforeground, &SFfore);                   i++;
281         XtSetArg(arglist[i], XtNbackground, &SFback);                   i++;
282         XtGetValues(selFilePrompt, arglist, i);
283
284         SFinitFont();
285
286         SFentryWidth = SFbesideText + SFcharsPerEntry * SFcharWidth +
287                         SFbesideText;
288         SFentryHeight = SFaboveAndBelowText + SFcharHeight +
289                         SFaboveAndBelowText;
290
291         listWidth = SFlineToTextH + SFentryWidth + SFlineToTextH + 1 +
292                         scrollThickness;
293         listHeight = SFlineToTextV + SFentryHeight + SFlineToTextV + 1 +
294                         SFlineToTextV + SFlistSize * SFentryHeight +
295                         SFlineToTextV + 1 + scrollThickness;
296
297         SFpathScrollWidth = NR * listWidth + (NR-1) * listSpacing + 4;
298
299         hScrollX = -1;
300         hScrollY = SFlineToTextV + SFentryHeight + SFlineToTextV + 1 +
301                         SFlineToTextV + SFlistSize * SFentryHeight +
302                         SFlineToTextV;
303         SFhScrollWidth = SFlineToTextH + SFentryWidth + SFlineToTextH;
304
305         vScrollX = SFlineToTextH + SFentryWidth + SFlineToTextH;
306         vScrollY = SFlineToTextV + SFentryHeight + SFlineToTextV;
307         SFvScrollHeight = SFlineToTextV + SFlistSize * SFentryHeight +
308                         SFlineToTextV;
309
310         SFupperX = SFlineToTextH + SFentryWidth + SFlineToTextH - 1;
311         SFlowerY = SFlineToTextV + SFentryHeight + SFlineToTextV + 1 +
312                         SFlineToTextV;
313         SFupperY = SFlineToTextV + SFentryHeight + SFlineToTextV + 1 +
314                         SFlineToTextV + SFlistSize * SFentryHeight - 1;
315
316         SFtextX = SFlineToTextH + SFbesideText;
317         SFtextYoffset = SFlowerY + SFaboveAndBelowText + SFcharAscent;
318
319         SFsegs[0].x1 = 0;
320         SFsegs[0].y1 = vScrollY;
321         SFsegs[0].x2 = vScrollX - 1;
322         SFsegs[0].y2 = vScrollY;
323         SFsegs[1].x1 = vScrollX;
324         SFsegs[1].y1 = 0;
325         SFsegs[1].x2 = vScrollX;
326         SFsegs[1].y2 = vScrollY - 1;
327
328         SFcompletionSegs[0].x1 = SFcompletionSegs[0].x2 = SFlineToTextH;
329         SFcompletionSegs[1].x1 = SFcompletionSegs[1].x2 =
330                 SFlineToTextH + SFentryWidth - 1;
331
332         i = 0;
333         XtSetArg(arglist[i], XtNwidth, NR * listWidth + (NR - 1) * listSpacing + 4);
334                                                                         i++;
335         XtSetArg(arglist[i], XtNborderColor, SFfore);                   i++;
336
337         XtSetArg(arglist[i], XtNfromVert, selFilePrompt);               i++;
338         XtSetArg(arglist[i], XtNvertDistance, 10);                      i++;
339         XtSetArg(arglist[i], XtNresizable, True);                       i++;
340         XtSetArg(arglist[i], XtNtop, XtChainTop);                       i++;
341         XtSetArg(arglist[i], XtNbottom, XtChainTop);                    i++;
342         XtSetArg(arglist[i], XtNleft, XtChainLeft);                     i++;
343         XtSetArg(arglist[i], XtNright, XtChainLeft);                    i++;
344         XtSetArg(arglist[i], XtNstring, SFtextBuffer);                  i++;
345         XtSetArg(arglist[i], XtNlength, MAXPATHLEN);                    i++;
346         XtSetArg(arglist[i], XtNeditType, XawtextEdit);                 i++;
347         XtSetArg(arglist[i], XtNwrap, XawtextWrapWord);                 i++;
348         XtSetArg(arglist[i], XtNresize, XawtextResizeHeight);           i++;
349         XtSetArg(arglist[i], XtNuseStringInPlace, True);                i++;
350         selFileField = XtCreateManagedWidget("selFileField",
351                 asciiTextWidgetClass, selFileForm, arglist, i);
352
353         XtOverrideTranslations(selFileField,
354                 XtParseTranslationTable(oneLineTextEditTranslations));
355         XtSetKeyboardFocus(selFileForm, selFileField);
356
357         i = 0;
358         XtSetArg(arglist[i], XtNorientation, XtorientHorizontal);       i++;
359         XtSetArg(arglist[i], XtNwidth, SFpathScrollWidth);              i++;
360         XtSetArg(arglist[i], XtNheight, scrollThickness);               i++;
361         XtSetArg(arglist[i], XtNborderColor, SFfore);                   i++;
362         XtSetArg(arglist[i], XtNfromVert, selFileField);                i++;
363         XtSetArg(arglist[i], XtNvertDistance, 30);                      i++;
364         XtSetArg(arglist[i], XtNtop, XtChainTop);                       i++;
365         XtSetArg(arglist[i], XtNbottom, XtChainTop);                    i++;
366         XtSetArg(arglist[i], XtNleft, XtChainLeft);                     i++;
367         XtSetArg(arglist[i], XtNright, XtChainLeft);                    i++;
368         selFileHScroll = XtCreateManagedWidget("selFileHScroll",
369                 scrollbarWidgetClass, selFileForm, arglist, i);
370
371         XtAddCallback(selFileHScroll, XtNjumpProc,
372                 SFpathSliderMovedCallback, (XtPointer) NULL);
373         XtAddCallback(selFileHScroll, XtNscrollProc,
374                 SFpathAreaSelectedCallback, (XtPointer) NULL);
375
376         i = 0;
377         XtSetArg(arglist[i], XtNwidth, listWidth);                      i++;
378         XtSetArg(arglist[i], XtNheight, listHeight);                    i++;
379         XtSetArg(arglist[i], XtNborderColor, SFfore);                   i++;
380         XtSetArg(arglist[i], XtNfromVert, selFileHScroll);              i++;
381         XtSetArg(arglist[i], XtNvertDistance, 10);                      i++;
382         XtSetArg(arglist[i], XtNtop, XtChainTop);                       i++;
383         XtSetArg(arglist[i], XtNbottom, XtChainTop);                    i++;
384         XtSetArg(arglist[i], XtNleft, XtChainLeft);                     i++;
385         XtSetArg(arglist[i], XtNright, XtChainLeft);                    i++;
386         selFileLists[0] = XtCreateManagedWidget("selFileList1",
387                 compositeWidgetClass, selFileForm, arglist, i);
388 #if (NR == 3)
389         i = 0;
390         XtSetArg(arglist[i], XtNwidth, listWidth);                      i++;
391         XtSetArg(arglist[i], XtNheight, listHeight);                    i++;
392         XtSetArg(arglist[i], XtNborderColor, SFfore);                   i++;
393         XtSetArg(arglist[i], XtNfromHoriz, selFileLists[0]);            i++;
394         XtSetArg(arglist[i], XtNfromVert, selFileHScroll);              i++;
395         XtSetArg(arglist[i], XtNhorizDistance, listSpacing);            i++;
396         XtSetArg(arglist[i], XtNvertDistance, 10);                      i++;
397         XtSetArg(arglist[i], XtNtop, XtChainTop);                       i++;
398         XtSetArg(arglist[i], XtNbottom, XtChainTop);                    i++;
399         XtSetArg(arglist[i], XtNleft, XtChainLeft);                     i++;
400         XtSetArg(arglist[i], XtNright, XtChainLeft);                    i++;
401         selFileLists[1] = XtCreateManagedWidget("selFileList2",
402                 compositeWidgetClass, selFileForm, arglist, i);
403
404         i = 0;
405         XtSetArg(arglist[i], XtNwidth, listWidth);                      i++;
406         XtSetArg(arglist[i], XtNheight, listHeight);                    i++;
407         XtSetArg(arglist[i], XtNborderColor, SFfore);                   i++;
408         XtSetArg(arglist[i], XtNfromHoriz, selFileLists[1]);            i++;
409         XtSetArg(arglist[i], XtNfromVert, selFileHScroll);              i++;
410         XtSetArg(arglist[i], XtNhorizDistance, listSpacing);            i++;
411         XtSetArg(arglist[i], XtNvertDistance, 10);                      i++;
412         XtSetArg(arglist[i], XtNtop, XtChainTop);                       i++;
413         XtSetArg(arglist[i], XtNbottom, XtChainTop);                    i++;
414         XtSetArg(arglist[i], XtNleft, XtChainLeft);                     i++;
415         XtSetArg(arglist[i], XtNright, XtChainLeft);                    i++;
416         selFileLists[2] = XtCreateManagedWidget("selFileList3",
417                 compositeWidgetClass, selFileForm, arglist, i);
418 #endif
419         for (n = 0; n < NR; n++) {
420
421                 i = 0;
422                 XtSetArg(arglist[i], XtNx, vScrollX);                   i++;
423                 XtSetArg(arglist[i], XtNy, vScrollY);                   i++;
424                 XtSetArg(arglist[i], XtNwidth, scrollThickness);        i++;
425                 XtSetArg(arglist[i], XtNheight, SFvScrollHeight);       i++;
426                 XtSetArg(arglist[i], XtNborderColor, SFfore);           i++;
427                 selFileVScrolls[n] = XtCreateManagedWidget("selFileVScroll",
428                         scrollbarWidgetClass, selFileLists[n], arglist, i);
429
430                 XtAddCallback(selFileVScrolls[n], XtNjumpProc,
431                         SFvFloatSliderMovedCallback, (XtPointer) n);
432                 XtAddCallback(selFileVScrolls[n], XtNscrollProc,
433                         SFvAreaSelectedCallback, (XtPointer) n);
434
435                 i = 0;
436
437                 XtSetArg(arglist[i], XtNorientation, XtorientHorizontal);
438                                                                         i++;
439                 XtSetArg(arglist[i], XtNx, hScrollX);                   i++;
440                 XtSetArg(arglist[i], XtNy, hScrollY);                   i++;
441                 XtSetArg(arglist[i], XtNwidth, SFhScrollWidth);         i++;
442                 XtSetArg(arglist[i], XtNheight, scrollThickness);       i++;
443                 XtSetArg(arglist[i], XtNborderColor, SFfore);           i++;
444                 selFileHScrolls[n] = XtCreateManagedWidget("selFileHScroll",
445                         scrollbarWidgetClass, selFileLists[n], arglist, i);
446
447                 XtAddCallback(selFileHScrolls[n], XtNjumpProc,
448                         SFhSliderMovedCallback, (XtPointer) n);
449                 XtAddCallback(selFileHScrolls[n], XtNscrollProc,
450                         SFhAreaSelectedCallback, (XtPointer) n);
451         }
452
453         i = 0;
454         XtSetArg(arglist[i], XtNlabel, ok);                             i++;
455         XtSetArg(arglist[i], XtNresizable, True);                       i++;
456         XtSetArg(arglist[i], XtNcallback, SFokSelect);                  i++;
457         XtSetArg(arglist[i], XtNborderColor, SFfore);                   i++;
458         XtSetArg(arglist[i], XtNfromVert, selFileLists[0]);             i++;
459         XtSetArg(arglist[i], XtNvertDistance, 30);                      i++;
460         XtSetArg(arglist[i], XtNtop, XtChainTop);                       i++;
461         XtSetArg(arglist[i], XtNbottom, XtChainTop);                    i++;
462         XtSetArg(arglist[i], XtNleft, XtChainLeft);                     i++;
463         XtSetArg(arglist[i], XtNright, XtChainLeft);                    i++;
464         selFileOK = XtCreateManagedWidget("selFileOK", commandWidgetClass,
465                 selFileForm, arglist, i);
466
467         i = 0;
468         XtSetArg(arglist[i], XtNlabel, cancel);                         i++;
469         XtSetArg(arglist[i], XtNresizable, True);                       i++;
470         XtSetArg(arglist[i], XtNcallback, SFcancelSelect);              i++;
471         XtSetArg(arglist[i], XtNborderColor, SFfore);                   i++;
472         XtSetArg(arglist[i], XtNfromHoriz, selFileOK);                  i++;
473         XtSetArg(arglist[i], XtNfromVert, selFileLists[0]);             i++;
474         XtSetArg(arglist[i], XtNhorizDistance, 30);                     i++;
475         XtSetArg(arglist[i], XtNvertDistance, 30);                      i++;
476         XtSetArg(arglist[i], XtNtop, XtChainTop);                       i++;
477         XtSetArg(arglist[i], XtNbottom, XtChainTop);                    i++;
478         XtSetArg(arglist[i], XtNleft, XtChainLeft);                     i++;
479         XtSetArg(arglist[i], XtNright, XtChainLeft);                    i++;
480         selFileCancel = XtCreateManagedWidget("selFileCancel",
481                 commandWidgetClass, selFileForm, arglist, i);
482
483         XtSetMappedWhenManaged(selFile, False);
484         XtRealizeWidget(selFile);
485
486         /* Add WM_DELETE_WINDOW protocol */
487         SFwmDeleteWindow = XInternAtom(SFdisplay, "WM_DELETE_WINDOW", False);
488         XSetWMProtocols(SFdisplay, XtWindow(selFile), &SFwmDeleteWindow, 1);
489
490         SFcreateGC();
491
492         xtermCursor = XCreateFontCursor(SFdisplay, XC_xterm);
493
494         sbRightArrowCursor = XCreateFontCursor(SFdisplay, XC_sb_right_arrow);
495         dotCursor = XCreateFontCursor(SFdisplay, XC_dot);
496
497         XDefineCursor(SFdisplay, XtWindow(selFileForm), xtermCursor);
498         XDefineCursor(SFdisplay, XtWindow(selFileField), xtermCursor);
499
500         for (n = 0; n < NR; n++) {
501                 XDefineCursor(SFdisplay, XtWindow(selFileLists[n]),
502                         sbRightArrowCursor);
503         }
504         XDefineCursor(SFdisplay, XtWindow(selFileOK), dotCursor);
505         XDefineCursor(SFdisplay, XtWindow(selFileCancel), dotCursor);
506
507         for (n = 0; n < NR; n++) {
508                 XtAddEventHandler(selFileLists[n], ExposureMask, True,
509                         SFexposeList, (XtPointer) n);
510                 XtAddEventHandler(selFileLists[n], EnterWindowMask, False,
511                         SFenterList, (XtPointer) n);
512                 XtAddEventHandler(selFileLists[n], LeaveWindowMask, False,
513                         SFleaveList, (XtPointer) n);
514                 XtAddEventHandler(selFileLists[n], PointerMotionMask, False,
515                         SFmotionList, (XtPointer) n);
516                 XtAddEventHandler(selFileLists[n], ButtonPressMask, False,
517                         SFbuttonPressList, (XtPointer) n);
518                 XtAddEventHandler(selFileLists[n], ButtonReleaseMask, False,
519                         SFbuttonReleaseList, (XtPointer) n);
520         }
521
522         XtAddEventHandler(selFileField, KeyPressMask, False,
523                 SFmodVerifyCallback, (XtPointer) NULL);
524
525         SFapp = XtWidgetToApplicationContext(selFile);
526
527 }
528
529 /* position widget under the cursor */
530 void
531 SFpositionWidget(w)
532     Widget w;
533 {
534     Arg args[3];
535     Cardinal num_args;
536     Dimension width, height, b_width;
537     int x, y, max_x, max_y;
538     Window root, child;
539     int dummyx, dummyy;
540     unsigned int dummymask;
541     
542     XQueryPointer(XtDisplay(w), XtWindow(w), &root, &child, &x, &y,
543                   &dummyx, &dummyy, &dummymask);
544     num_args = 0;
545     XtSetArg(args[num_args], XtNwidth, &width); num_args++;
546     XtSetArg(args[num_args], XtNheight, &height); num_args++;
547     XtSetArg(args[num_args], XtNborderWidth, &b_width); num_args++;
548     XtGetValues(w, args, num_args);
549
550     width += 2 * b_width;
551     height += 2 * b_width;
552
553     x -= ( (Position) width/2 );
554     if (x < 0) x = 0;
555     if ( x > (max_x = (Position) (XtScreen(w)->width - width)) ) x = max_x;
556
557     y -= ( (Position) height/2 );
558     if (y < 0) y = 0;
559     if ( y > (max_y = (Position) (XtScreen(w)->height - height)) ) y = max_y;
560     
561     num_args = 0;
562     XtSetArg(args[num_args], XtNx, x); num_args++;
563     XtSetArg(args[num_args], XtNy, y); num_args++;
564     XtSetValues(w, args, num_args);
565 }
566
567 FILE *
568 SFopenFile(name, mode, prompt, failed)
569     char *name;
570     char *mode;
571     char *prompt;
572     char *failed;
573 {
574     Arg args[1];
575     FILE *fp;
576
577     SFchdir(SFstartDir);
578     if ((fp = fopen(name, mode)) == NULL) {
579         char *buf;
580         if (1) { // [HGM] always use strerror
581             buf = XtMalloc(strlen(failed) + strlen(strerror(errno)) + 
582                            strlen(prompt) + 2);
583             strcpy(buf, failed);
584             strcat(buf, strerror(errno));
585             strcat(buf, "\n");
586             strcat(buf, prompt);
587         } else {
588             buf = XtMalloc(strlen(failed) + strlen(prompt) + 2);
589             strcpy(buf, failed);
590             strcat(buf, "\n");
591             strcat(buf, prompt);
592         }
593         XtSetArg(args[0], XtNlabel, buf);
594         XtSetValues(selFilePrompt, args, ONE);
595         XtFree(buf);
596         return NULL;
597     }
598     return fp;
599 }
600
601 void
602 SFtextChanged()
603 {
604
605         if ((SFtextBuffer[0] == '/') || (SFtextBuffer[0] == '~')) {
606           (void) strncpy(SFcurrentPath, SFtextBuffer, MAXPATHLEN);
607
608                 SFtextPos = XawTextGetInsertionPoint(selFileField);
609         } else {
610           (void) strcat(strncpy(SFcurrentPath, SFstartDir, MAXPATHLEN), SFtextBuffer);
611
612                 SFtextPos = XawTextGetInsertionPoint(selFileField) +
613                         strlen(SFstartDir);
614         }
615
616         if (!SFworkProcAdded) {
617                 (void) XtAppAddWorkProc(SFapp, SFworkProc, NULL);
618                 SFworkProcAdded = 1;
619         }
620
621         SFupdatePath();
622         return;
623 }
624
625 static char *
626 SFgetText()
627 {
628         return strcpy(XtMalloc((unsigned) (strlen(SFtextBuffer) + 1)),
629                 SFtextBuffer);
630 }
631
632 static void
633 SFprepareToReturn()
634 {
635         SFstatus = SEL_FILE_NULL;
636         XtRemoveGrab(selFile);
637         XtUnmapWidget(selFile);
638         XtRemoveTimeOut(SFdirModTimerId);
639         if (SFchdir(SFstartDir)) {
640                 XtAppError(
641                         SFapp,
642                         "XsraSelFile: can't return to current directory"
643                 );
644         }
645         return;
646 }
647
648 FILE *
649 XsraSelFile(toplevel, prompt, ok, cancel, failed,
650             init_path, mode, show_entry, name_return)
651         Widget          toplevel;
652         char            *prompt;
653         char            *ok;
654         char            *cancel;
655         char            *failed;
656         char            *init_path;
657         char            *mode;
658         int             (*show_entry)();
659         char            **name_return;
660 {
661         static int      firstTime = 1;
662         Cardinal        i;
663         Arg             arglist[20];
664         XEvent          event;
665         FILE            *fp;
666
667         if (!prompt) {
668                 prompt = "Pathname:";
669         }
670
671         if (!ok) {
672                 ok = "OK";
673         }
674
675         if (!cancel) {
676                 cancel = "Cancel";
677         }
678
679         if (firstTime) {
680                 firstTime = 0;
681                 SFdisplay = XtDisplay(toplevel);
682                 SFcreateWidgets(toplevel, prompt, ok, cancel);
683         } else {
684                 i = 0;
685
686                 XtSetArg(arglist[i], XtNlabel, prompt);                 i++;
687                 XtSetValues(selFilePrompt, arglist, i);
688
689                 i = 0;
690                 XtSetArg(arglist[i], XtNlabel, ok);                     i++;
691                 XtSetValues(selFileOK, arglist, i);
692
693                 i = 0;
694                 XtSetArg(arglist[i], XtNlabel, cancel);                 i++;
695                 XtSetValues(selFileCancel, arglist, i);
696         }
697
698         SFpositionWidget(selFile);
699         XtMapWidget(selFile);
700
701 #if defined(SVR4) || defined(SYSV) || defined(USG) || 1
702         if (!getcwd(SFstartDir, MAXPATHLEN)) { // [HGM] always do this, as I do not know when exactly to do it
703 #else /* defined(SVR4) || defined(SYSV) || defined(USG) */
704         if (!getwd(SFstartDir)) {
705 #endif /* defined(SVR4) || defined(SYSV) || defined(USG) */
706
707                 XtAppError(SFapp, "XsraSelFile: can't get current directory");
708         }
709         (void) strcat(SFstartDir, "/");
710         (void) strncpy(SFcurrentDir, SFstartDir, MAXPATHLEN);
711
712         if (init_path) {
713                 if (init_path[0] == '/') {
714                   (void) strncpy(SFcurrentPath, init_path, MAXPATHLEN);
715                         if (strncmp(
716                                 SFcurrentPath,
717                                 SFstartDir,
718                                 strlen(SFstartDir)
719                         )) {
720                                 SFsetText(SFcurrentPath);
721                         } else {
722                                 SFsetText(&(SFcurrentPath[strlen(SFstartDir)]));
723                         }
724                 } else {
725                   (void) strcat(strncpy(SFcurrentPath, SFstartDir, MAXPATHLEN),
726                                 init_path);
727                         SFsetText(&(SFcurrentPath[strlen(SFstartDir)]));
728                 }
729         } else {
730           (void) strncpy(SFcurrentPath, SFstartDir, MAXPATHLEN);
731         }
732
733         SFfunc = show_entry;
734
735         SFtextChanged();
736
737         XtAddGrab(selFile, True, True);
738
739         SFdirModTimerId = XtAppAddTimeOut(SFapp, (unsigned long) 1000,
740                 SFdirModTimer, (XtPointer) NULL);
741
742         while (1) {
743                 XtAppNextEvent(SFapp, &event);
744                 XtDispatchEvent(&event);
745                 switch (SFstatus) {
746                 case SEL_FILE_TEXT:
747                         SFstatus = SEL_FILE_NULL;
748                         SFtextChanged();
749                         break;
750                 case SEL_FILE_OK:
751                         *name_return = SFgetText();
752                         if (fp = SFopenFile(*name_return, mode,
753                                             prompt, failed)) {
754                                 SFprepareToReturn();
755                                 return fp;
756                         }
757                         SFstatus = SEL_FILE_NULL;
758                         break;
759                 case SEL_FILE_CANCEL:
760                         SFprepareToReturn();
761                         return NULL;
762                 case SEL_FILE_NULL:
763                         break;
764                 }
765         }
766 }