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