Interface XBoard to Ghostvie file-browser dialog
authorH.G. Muller <h.g.muller@hccnet.nl>
Thu, 7 Jan 2010 15:59:37 +0000 (16:59 +0100)
committerH.G. Muller <h.g.muller@hccnet.nl>
Thu, 7 Jan 2010 15:59:37 +0000 (16:59 +0100)
Makefile.am
dir.c [new file with mode: 0644]
draw.c [new file with mode: 0644]
path.c [new file with mode: 0644]
selfile.c [new file with mode: 0644]
selfile.h [new file with mode: 0644]
xboard.c
xstat.h [new file with mode: 0644]

index 7345205..697bb3a 100644 (file)
@@ -26,6 +26,8 @@ xboard_SOURCES = backend.c backend.h backendz.h \
                 xgamelist.c xgamelist.h\
                 xhistory.c  xhistory.h \
                 xoptions.c \
+                selfile.c selfile.h \
+                draw.c path.c dir.c xstat.h \
                 $(ZPY)
 xboard_LDADD = -lm @XAW_LIBS@ @X_LIBS@ 
 
diff --git a/dir.c b/dir.c
new file mode 100644 (file)
index 0000000..102187e
--- /dev/null
+++ b/dir.c
@@ -0,0 +1,163 @@
+/*
+ * Copyright 1989 Software Research Associates, Inc., Tokyo, Japan
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose and without fee is hereby granted, provided
+ * that the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of Software Research Associates not be used
+ * in advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.  Software Research Associates
+ * makes no representations about the suitability of this software for any
+ * purpose.  It is provided "as is" without express or implied warranty.
+ *
+ * SOFTWARE RESEARCH ASSOCIATES DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
+ * IN NO EVENT SHALL SOFTWARE RESEARCH ASSOCIATES BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Erik M. van der Poel
+ *         Software Research Associates, Inc., Tokyo, Japan
+ *         erik@sra.co.jp
+ */
+
+#include <stdio.h>
+
+#ifdef SEL_FILE_IGNORE_CASE
+#include <ctype.h>
+#endif /* def SEL_FILE_IGNORE_CASE */
+
+#include "selfile.h"
+
+#if defined(SVR4) || defined(SYSV) || defined(USG) || defined(__osf__)
+#include <dirent.h>
+#else /* defined(SVR4) || defined(SYSV) || defined(USG) */
+#include <sys/dir.h>
+#define dirent direct
+#endif /* defined(SVR4) || defined(SYSV) || defined(USG) */
+
+#include <sys/stat.h>
+
+#if defined(SVR4) || defined(SYSV) || defined(USG)
+extern void qsort();
+#endif /* defined(SVR4) || defined(SYSV) || defined(USG) */
+
+#ifdef SEL_FILE_IGNORE_CASE
+int
+SFcompareEntries(p, q)
+       SFEntry *p;
+       SFEntry *q;
+{
+       register char   *r, *s;
+       register char   c1, c2;
+
+       r = p->real;
+       s = q->real;
+
+       c1 = *r++;
+       if (islower(c1)) {
+               c1 = toupper(c1);
+       }
+       c2 = *s++;
+       if (islower(c2)) {
+               c2 = toupper(c2);
+       }
+
+       while (c1 == c2) {
+               if (!c1) {
+                       return strcmp(p->real, q->real);
+               }
+               c1 = *r++;
+               if (islower(c1)) {
+                       c1 = toupper(c1);
+               }
+               c2 = *s++;
+               if (islower(c2)) {
+                       c2 = toupper(c2);
+               }
+       }
+
+       return c1 - c2;
+}
+#else /* def SEL_FILE_IGNORE_CASE */
+int
+SFcompareEntries(p, q)
+       SFEntry *p;
+       SFEntry *q;
+{
+       return strcmp(p->real, q->real);
+}
+#endif /* def SEL_FILE_IGNORE_CASE */
+
+int
+SFgetDir(dir)
+       SFDir   *dir;
+{
+       SFEntry         *result = NULL;
+       int             alloc = 0;
+       int             i;
+       DIR             *dirp;
+       struct dirent   *dp;
+       char            *str;
+       int             len;
+       int             maxChars;
+       struct stat     statBuf;
+
+       maxChars = strlen(dir->dir) - 1;
+
+       dir->entries = NULL;
+       dir->nEntries = 0;
+       dir->nChars = 0;
+
+       result = NULL;
+       i = 0;
+
+       dirp = opendir(".");
+       if (!dirp) {
+               return 1;
+       }
+
+       (void) stat(".", &statBuf);
+       dir->mtime = statBuf.st_mtime;
+
+       (void) readdir(dirp);   /* throw away "." */
+
+#ifndef S_IFLNK
+       (void) readdir(dirp);   /* throw away ".." */
+#endif /* ndef S_IFLNK */
+
+       while (dp = readdir(dirp)) {
+               if (i >= alloc) {
+                       alloc = 2 * (alloc + 1);
+                       result = (SFEntry *) XtRealloc((char *) result,
+                               (unsigned) (alloc * sizeof(SFEntry)));
+               }
+               result[i].statDone = 0;
+               str = dp->d_name;
+               len = strlen(str);
+               result[i].real = XtMalloc((unsigned) (len + 2));
+               (void) strcat(strcpy(result[i].real, str), " ");
+               if (len > maxChars) {
+                       maxChars = len;
+               }
+               result[i].shown = result[i].real;
+               i++;
+       }
+
+#if defined(SVR4) || defined(SYSV) || defined(USG)
+       qsort((char *) result, (unsigned) i, sizeof(SFEntry), SFcompareEntries);
+#else /* defined(SVR4) || defined(SYSV) || defined(USG) */
+       qsort((char *) result, i, sizeof(SFEntry), SFcompareEntries);
+#endif /* defined(SVR4) || defined(SYSV) || defined(USG) */
+
+       dir->entries = result;
+       dir->nEntries = i;
+       dir->nChars = maxChars + 1;
+
+       closedir(dirp);
+
+       return 0;
+}
diff --git a/draw.c b/draw.c
new file mode 100644 (file)
index 0000000..47efa17
--- /dev/null
+++ b/draw.c
@@ -0,0 +1,920 @@
+/*
+ * Copyright 1989 Software Research Associates, Inc., Tokyo, Japan
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose and without fee is hereby granted, provided
+ * that the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of Software Research Associates not be used
+ * in advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.  Software Research Associates
+ * makes no representations about the suitability of this software for any
+ * purpose.  It is provided "as is" without express or implied warranty.
+ *
+ * SOFTWARE RESEARCH ASSOCIATES DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
+ * IN NO EVENT SHALL SOFTWARE RESEARCH ASSOCIATES BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Erik M. van der Poel
+ *         Software Research Associates, Inc., Tokyo, Japan
+ *         erik@sra.co.jp
+ */
+
+#include <stdio.h>
+#include "selfile.h"
+#include "xstat.h"
+#include <X11/StringDefs.h>
+#include <X11/Xaw/Scrollbar.h>
+#include <X11/Xaw/Cardinals.h>
+
+#define SF_DEFAULT_FONT "9x15"
+
+#ifdef ABS
+#undef ABS
+#endif
+#define ABS(x) (((x) < 0) ? (-(x)) : (x))
+
+typedef struct {
+       char *fontname;
+} TextData, *textPtr;
+
+int SFcharWidth, SFcharAscent, SFcharHeight;
+
+int SFcurrentInvert[3] = { -1, -1, -1 };
+
+static GC SFlineGC, SFscrollGC, SFinvertGC, SFtextGC;
+
+static XtResource textResources[] = {
+       {XtNfont, XtCFont, XtRString, sizeof (char *),
+               XtOffset(textPtr, fontname), XtRString, SF_DEFAULT_FONT},
+};
+
+static XFontStruct *SFfont;
+
+static int SFcurrentListY;
+
+static XtIntervalId SFscrollTimerId;
+
+SFinitFont()
+{
+       TextData        *data;
+
+       data = XtNew(TextData);
+
+       XtGetApplicationResources(selFileForm, (XtPointer) data, textResources,
+               XtNumber(textResources), (Arg *) NULL, ZERO);
+
+       SFfont = XLoadQueryFont(SFdisplay, data->fontname);
+       if (!SFfont) {
+               SFfont = XLoadQueryFont(SFdisplay, SF_DEFAULT_FONT);
+               if (!SFfont) {
+                       char    sbuf[256];
+
+                       (void) sprintf(sbuf, "XsraSelFile: can't get font %s",
+                               SF_DEFAULT_FONT);
+
+                       XtAppError(SFapp, sbuf);
+               }
+       }
+
+       SFcharWidth = (SFfont->max_bounds.width + SFfont->min_bounds.width) / 2;
+       SFcharAscent = SFfont->max_bounds.ascent;
+       SFcharHeight = SFcharAscent + SFfont->max_bounds.descent;
+}
+
+SFcreateGC()
+{
+       XGCValues       gcValues;
+       XRectangle      rectangles[1];
+
+       gcValues.foreground = SFfore;
+
+       SFlineGC = XtGetGC(
+               selFileLists[0],
+               (XtGCMask)
+                       GCForeground            |
+                       0,
+               &gcValues
+       );
+
+       SFscrollGC = XtGetGC(
+               selFileLists[0],
+               (XtGCMask)
+                       0,
+               &gcValues
+       );
+
+       gcValues.function = GXinvert;
+       gcValues.plane_mask = (SFfore ^ SFback);
+
+       SFinvertGC = XtGetGC(
+               selFileLists[0],
+               (XtGCMask)
+                       GCFunction              |
+                       GCPlaneMask             |
+                       0,
+               &gcValues
+       );
+
+       gcValues.foreground = SFfore;
+       gcValues.background = SFback;
+       gcValues.font = SFfont->fid;
+
+       SFtextGC = XCreateGC(
+               SFdisplay,
+               XtWindow(selFileLists[0]),
+               (unsigned long)
+                       GCForeground            |
+                       GCBackground            |
+                       GCFont                  |
+                       0,
+               &gcValues
+       );
+
+       rectangles[0].x = SFlineToTextH + SFbesideText;
+       rectangles[0].y = 0;
+       rectangles[0].width = SFcharsPerEntry * SFcharWidth;
+       rectangles[0].height = SFupperY + 1;
+
+       XSetClipRectangles(
+               SFdisplay,
+               SFtextGC,
+               0,
+               0,
+               rectangles,
+               1,
+               Unsorted
+       );
+}
+
+SFclearList(n, doScroll)
+       int     n;
+       int     doScroll;
+{
+       SFDir   *dir;
+
+       SFcurrentInvert[n] = -1;
+
+       XClearWindow(SFdisplay, XtWindow(selFileLists[n]));
+
+       XDrawSegments(SFdisplay, XtWindow(selFileLists[n]), SFlineGC, SFsegs,
+               2);
+
+       if (doScroll) {
+               dir = &(SFdirs[SFdirPtr + n]);
+
+               if ((SFdirPtr + n < SFdirEnd) && dir->nEntries && dir->nChars) {
+                       XawScrollbarSetThumb(
+                               selFileVScrolls[n],
+                               (float) (((double) dir->vOrigin) /
+                                       dir->nEntries),
+                               (float) (((double) ((dir->nEntries < SFlistSize)
+                                       ? dir->nEntries : SFlistSize)) /
+                                       dir->nEntries)
+                       );
+
+                       XawScrollbarSetThumb(
+                               selFileHScrolls[n],
+                               (float) (((double) dir->hOrigin) / dir->nChars),
+                               (float) (((double) ((dir->nChars <
+                                       SFcharsPerEntry) ? dir->nChars :
+                                       SFcharsPerEntry)) / dir->nChars)
+                       );
+               } else {
+                       XawScrollbarSetThumb(selFileVScrolls[n], (float) 0.0,
+                               (float) 1.0);
+                       XawScrollbarSetThumb(selFileHScrolls[n], (float) 0.0,
+                               (float) 1.0);
+               }
+       }
+}
+
+static
+SFdeleteEntry(dir, entry)
+       SFDir   *dir;
+       SFEntry *entry;
+{
+       register SFEntry        *e;
+       register SFEntry        *end;
+       int                     n;
+       int                     idx;
+
+       idx = entry - dir->entries;
+
+       if (idx < dir->beginSelection) {
+               dir->beginSelection--;
+       }
+       if (idx <= dir->endSelection) {
+               dir->endSelection--;
+       }
+       if (dir->beginSelection > dir->endSelection) {
+               dir->beginSelection = dir->endSelection = -1;
+       }
+
+       if (idx < dir->vOrigin) {
+               dir->vOrigin--;
+       }
+
+       XtFree(entry->real);
+
+       end = &(dir->entries[dir->nEntries - 1]);
+
+       for (e = entry; e < end; e++) {
+               *e = *(e + 1);
+       }
+
+       if (!(--dir->nEntries)) {
+               return;
+       }
+
+       n = dir - &(SFdirs[SFdirPtr]);
+       if ((n < 0) || (n > 2)) {
+               return;
+       }
+
+       XawScrollbarSetThumb(
+               selFileVScrolls[n],
+               (float) (((double) dir->vOrigin) / dir->nEntries),
+               (float) (((double) ((dir->nEntries < SFlistSize) ?
+                       dir->nEntries : SFlistSize)) / dir->nEntries)
+       );
+}
+
+static
+SFwriteStatChar(name, last, statBuf)
+       char            *name;
+       int             last;
+       struct stat     *statBuf;
+{
+       name[last] = SFstatChar(statBuf);
+}
+
+static int
+SFstatAndCheck(dir, entry)
+       SFDir   *dir;
+       SFEntry *entry;
+{
+       struct stat     statBuf;
+       char            save;
+       int             last;
+
+       /*
+        * must be restored before returning
+        */
+       save = *(dir->path);
+       *(dir->path) = 0;
+
+       if (!SFchdir(SFcurrentPath)) {
+               last = strlen(entry->real) - 1;
+               entry->real[last] = 0;
+               entry->statDone = 1;
+               if (
+                       (!stat(entry->real, &statBuf))
+
+#ifdef S_IFLNK
+
+                    || (!lstat(entry->real, &statBuf))
+
+#endif /* ndef S_IFLNK */
+
+               ) {
+                       if (SFfunc) {
+                               char *shown;
+
+                               shown = NULL;
+                               if (SFfunc(entry->real, &shown, &statBuf)) {
+                                       if (shown) {
+                                               int len;
+
+                                               len = strlen(shown);
+                                               entry->shown = XtMalloc(
+                                                       (unsigned) (len + 2)
+                                               );
+                                               (void) strcpy(entry->shown,
+                                                       shown);
+                                               SFwriteStatChar(
+                                                       entry->shown,
+                                                       len,
+                                                       &statBuf
+                                               );
+                                               entry->shown[len + 1] = 0;
+                                       }
+                               } else {
+                                       SFdeleteEntry(dir, entry);
+
+                                       *(dir->path) = save;
+                                       return 1;
+                               }
+                       }
+                       SFwriteStatChar(entry->real, last, &statBuf);
+               } else {
+                       entry->real[last] = ' ';
+               }
+       }
+
+       *(dir->path) = save;
+       return 0;
+}
+
+static
+SFdrawStrings(w, dir, from, to)
+       register Window w;
+       register SFDir  *dir;
+       register int    from;
+       register int    to;
+{
+       register int            i;
+       register SFEntry        *entry;
+       int                     x;
+
+       x = SFtextX - dir->hOrigin * SFcharWidth;
+
+       if (dir->vOrigin + to >= dir->nEntries) {
+               to = dir->nEntries - dir->vOrigin - 1;
+       }
+       for (i = from; i <= to; i++) {
+               entry = &(dir->entries[dir->vOrigin + i]);
+               if (!(entry->statDone)) {
+                       if (SFstatAndCheck(dir, entry)) {
+                               if (dir->vOrigin + to >= dir->nEntries) {
+                                       to = dir->nEntries - dir->vOrigin - 1;
+                               }
+                               i--;
+                               continue;
+                       }
+               }
+               XDrawImageString(
+                       SFdisplay,
+                       w,
+                       SFtextGC,
+                       x,
+                       SFtextYoffset + i * SFentryHeight,
+                       entry->shown,
+                       strlen(entry->shown)
+               );
+               if (dir->vOrigin + i == dir->beginSelection) {
+                       XDrawLine(
+                               SFdisplay,
+                               w,
+                               SFlineGC,
+                               SFlineToTextH + 1,
+                               SFlowerY + i * SFentryHeight,
+                               SFlineToTextH + SFentryWidth - 2,
+                               SFlowerY + i * SFentryHeight
+                       );
+               }
+               if (
+                       (dir->vOrigin + i >= dir->beginSelection) &&
+                       (dir->vOrigin + i <= dir->endSelection)
+               ) {
+                       SFcompletionSegs[0].y1 = SFcompletionSegs[1].y1 =
+                               SFlowerY + i * SFentryHeight;
+                       SFcompletionSegs[0].y2 = SFcompletionSegs[1].y2 =
+                               SFlowerY + (i + 1) * SFentryHeight - 1;
+                       XDrawSegments(
+                               SFdisplay,
+                               w,
+                               SFlineGC,
+                               SFcompletionSegs,
+                               2
+                       );
+               }
+               if (dir->vOrigin + i == dir->endSelection) {
+                       XDrawLine(
+                               SFdisplay,
+                               w,
+                               SFlineGC,
+                               SFlineToTextH + 1,
+                               SFlowerY + (i + 1) * SFentryHeight - 1,
+                               SFlineToTextH + SFentryWidth - 2,
+                               SFlowerY + (i + 1) * SFentryHeight - 1
+                       );
+               }
+       }
+}
+
+SFdrawList(n, doScroll)
+       int     n;
+       int     doScroll;
+{
+       SFDir   *dir;
+       Window  w;
+
+       SFclearList(n, doScroll);
+
+       if (SFdirPtr + (3-NR) + n < SFdirEnd) {
+               dir = &(SFdirs[SFdirPtr + n + (3-NR)]);
+               w = XtWindow(selFileLists[n]);
+               XDrawImageString(
+                       SFdisplay,
+                       w,
+                       SFtextGC,
+                       SFtextX - dir->hOrigin * SFcharWidth,
+                       SFlineToTextV + SFaboveAndBelowText + SFcharAscent,
+                       dir->dir,
+                       strlen(dir->dir)
+               );
+               SFdrawStrings(w, dir, 0, SFlistSize - 1);
+       }
+}
+
+SFdrawLists(doScroll)
+       int     doScroll;
+{
+       int     i;
+
+       for (i = 0; i < NR; i++) {
+               SFdrawList(i, doScroll);
+       }
+}
+
+static
+SFinvertEntry(n)
+       register int    n;
+{
+       XFillRectangle(
+               SFdisplay,
+               XtWindow(selFileLists[n]),
+               SFinvertGC,
+               SFlineToTextH,
+               SFcurrentInvert[n] * SFentryHeight + SFlowerY,
+               SFentryWidth,
+               SFentryHeight
+       );
+}
+
+static unsigned long
+SFscrollTimerInterval()
+{
+       static int      maxVal = 200;
+       static int      varyDist = 50;
+       static int      minDist = 50;
+       int             t;
+       int             dist;
+
+       if (SFcurrentListY < SFlowerY) {
+               dist = SFlowerY - SFcurrentListY;
+       } else if (SFcurrentListY > SFupperY) {
+               dist = SFcurrentListY - SFupperY;
+       } else {
+               return (unsigned long) 1;
+       }
+
+       t = maxVal - ((maxVal / varyDist) * (dist - minDist));
+
+       if (t < 1) {
+               t = 1;
+       }
+
+       if (t > maxVal) {
+               t = maxVal;
+       }
+
+       return (unsigned long) t;
+}
+
+static void
+SFscrollTimer(p, id)
+       XtPointer       p;
+        XtIntervalId    *id;
+{
+       SFDir   *dir;
+       int     save;
+       int     n;
+
+        n = (int) p;
+
+       dir = &(SFdirs[SFdirPtr + n]);
+       save = dir->vOrigin;
+
+       if (SFcurrentListY < SFlowerY) {
+               if (dir->vOrigin > 0) {
+                       SFvSliderMovedCallback(selFileVScrolls[n], n,
+                               dir->vOrigin - 1);
+               }
+       } else if (SFcurrentListY > SFupperY) {
+               if (dir->vOrigin < dir->nEntries - SFlistSize) {
+                       SFvSliderMovedCallback(selFileVScrolls[n], n,
+                               dir->vOrigin + 1);
+               }
+       }
+
+       if (dir->vOrigin != save) {
+               if (dir->nEntries) {
+                   XawScrollbarSetThumb(
+                       selFileVScrolls[n],
+                       (float) (((double) dir->vOrigin) / dir->nEntries),
+                       (float) (((double) ((dir->nEntries < SFlistSize) ?
+                               dir->nEntries : SFlistSize)) / dir->nEntries)
+                   );
+               }
+       }
+
+       if (SFbuttonPressed) {
+               SFscrollTimerId = XtAppAddTimeOut(SFapp,
+                       SFscrollTimerInterval(), SFscrollTimer, (XtPointer) n);
+       }
+}
+
+static int
+SFnewInvertEntry(n, event)
+       register int            n;
+       register XMotionEvent   *event;
+{
+       register int    x, y;
+       register int    new;
+       static int      SFscrollTimerAdded = 0;
+
+       x = event->x;
+       y = event->y;
+
+       if (SFdirPtr + n >= SFdirEnd) {
+               return -1;
+       } else if (
+               (x >= 0)        && (x <= SFupperX) &&
+               (y >= SFlowerY) && (y <= SFupperY)
+       ) {
+               register SFDir *dir = &(SFdirs[SFdirPtr + n]);
+
+               if (SFscrollTimerAdded) {
+                       SFscrollTimerAdded = 0;
+                       XtRemoveTimeOut(SFscrollTimerId);
+               }
+
+               new = (y - SFlowerY) / SFentryHeight;
+               if (dir->vOrigin + new >= dir->nEntries) {
+                       return -1;
+               }
+               return new;
+       } else {
+               if (SFbuttonPressed) {
+                       SFcurrentListY = y;
+                       if (!SFscrollTimerAdded) {
+                               SFscrollTimerAdded = 1;
+                               SFscrollTimerId = XtAppAddTimeOut(SFapp,
+                                       SFscrollTimerInterval(), SFscrollTimer,
+                                       (XtPointer) n);
+                       }
+               }
+
+               return -1;
+       }
+}
+
+/* ARGSUSED */
+void
+SFenterList(w, n, event)
+       Widget                          w;
+       register int                    n;
+       register XEnterWindowEvent      *event;
+{
+       register int    new;
+
+       /* sanity */
+       if (SFcurrentInvert[n] != -1) {
+               SFinvertEntry(n);
+               SFcurrentInvert[n] = -1;
+       }
+
+       new = SFnewInvertEntry(n, (XMotionEvent *) event);
+       if (new != -1) {
+               SFcurrentInvert[n] = new;
+               SFinvertEntry(n);
+       }
+}
+
+/* ARGSUSED */
+void
+SFleaveList(w, n, event)
+       Widget          w;
+       register int    n;
+       XEvent          *event;
+{
+       if (SFcurrentInvert[n] != -1) {
+               SFinvertEntry(n);
+               SFcurrentInvert[n] = -1;
+       }
+}
+
+/* ARGSUSED */
+void
+SFmotionList(w, n, event)
+       Widget                  w;
+       register int            n;
+       register XMotionEvent   *event;
+{
+       register int    new;
+
+       new = SFnewInvertEntry(n, event);
+
+       if (new != SFcurrentInvert[n]) {
+               if (SFcurrentInvert[n] != -1) {
+                       SFinvertEntry(n);
+               }
+               SFcurrentInvert[n] = new;
+               if (new != -1) {
+                       SFinvertEntry(n);
+               }
+       }
+}
+
+/* ARGSUSED */
+void
+SFvFloatSliderMovedCallback(w, n, fnew)
+       Widget  w;
+       int     n;
+       float   *fnew;
+{
+       int     new;
+
+       new = (*fnew) * SFdirs[SFdirPtr + n].nEntries;
+
+       SFvSliderMovedCallback(w, n, new);
+}
+
+/* ARGSUSED */
+void
+SFvSliderMovedCallback(w, n, new)
+       Widget  w;
+       int     n;
+       int     new;
+{
+       int             old;
+       register Window win;
+       SFDir           *dir;
+
+       dir = &(SFdirs[SFdirPtr + n]);
+
+       old = dir->vOrigin;
+       dir->vOrigin = new;
+
+       if (old == new) {
+               return;
+       }
+
+       win = XtWindow(selFileLists[n]);
+
+       if (ABS(new - old) < SFlistSize) {
+               if (new > old) {
+                       XCopyArea(
+                               SFdisplay,
+                               win,
+                               win,
+                               SFscrollGC,
+                               SFlineToTextH,
+                               SFlowerY + (new - old) * SFentryHeight,
+                               SFentryWidth + SFlineToTextH,
+                               (SFlistSize - (new - old)) * SFentryHeight,
+                               SFlineToTextH,
+                               SFlowerY
+                       );
+                       XClearArea(
+                               SFdisplay,
+                               win,
+                               SFlineToTextH,
+                               SFlowerY + (SFlistSize - (new - old)) *
+                                       SFentryHeight,
+                               SFentryWidth + SFlineToTextH,
+                               (new - old) * SFentryHeight,
+                               False
+                       );
+                       SFdrawStrings(win, dir, SFlistSize - (new - old),
+                               SFlistSize - 1);
+               } else {
+                       XCopyArea(
+                               SFdisplay,
+                               win,
+                               win,
+                               SFscrollGC,
+                               SFlineToTextH,
+                               SFlowerY,
+                               SFentryWidth + SFlineToTextH,
+                               (SFlistSize - (old - new)) * SFentryHeight,
+                               SFlineToTextH,
+                               SFlowerY + (old - new) * SFentryHeight
+                       );
+                       XClearArea(
+                               SFdisplay,
+                               win,
+                               SFlineToTextH,
+                               SFlowerY,
+                               SFentryWidth + SFlineToTextH,
+                               (old - new) * SFentryHeight,
+                               False
+                       );
+                       SFdrawStrings(win, dir, 0, old - new);
+               }
+       } else {
+               XClearArea(
+                       SFdisplay,
+                       win,
+                       SFlineToTextH,
+                       SFlowerY,
+                       SFentryWidth + SFlineToTextH,
+                       SFlistSize * SFentryHeight,
+                       False
+               );
+               SFdrawStrings(win, dir, 0, SFlistSize - 1);
+       }
+}
+
+/* ARGSUSED */
+void
+SFvAreaSelectedCallback(w, n, pnew)
+       Widget  w;
+       int     n;
+       int     pnew;
+{
+       SFDir   *dir;
+       int     new;
+
+       dir = &(SFdirs[SFdirPtr + n]);
+
+       new = dir->vOrigin +
+               (((double) pnew) / SFvScrollHeight) * dir->nEntries;
+
+       if (new > dir->nEntries - SFlistSize) {
+               new = dir->nEntries - SFlistSize;
+       }
+
+       if (new < 0) {
+               new = 0;
+       }
+
+       if (dir->nEntries) {
+               float   f;
+
+               f = ((double) new) / dir->nEntries;
+
+               XawScrollbarSetThumb(
+                       w,
+                       f,
+                       (float) (((double) ((dir->nEntries < SFlistSize) ?
+                               dir->nEntries : SFlistSize)) / dir->nEntries)
+               );
+       }
+
+       SFvSliderMovedCallback(w, n, new);
+}
+
+/* ARGSUSED */
+void
+SFhSliderMovedCallback(w, n, new)
+       Widget  w;
+       int     n;
+       float   *new;
+{
+       SFDir   *dir;
+       int     save;
+
+       dir = &(SFdirs[SFdirPtr + n]);
+       save = dir->hOrigin;
+       dir->hOrigin = (*new) * dir->nChars;
+       if (dir->hOrigin == save) {
+               return;
+       }
+
+       SFdrawList(n, SF_DO_NOT_SCROLL);
+}
+
+/* ARGSUSED */
+void
+SFhAreaSelectedCallback(w, n, pnew)
+       Widget  w;
+       int     n;
+       int     pnew;
+{
+       SFDir   *dir;
+       int     new;
+
+       dir = &(SFdirs[SFdirPtr + n]);
+
+       new = dir->hOrigin +
+               (((double) pnew) / SFhScrollWidth) * dir->nChars;
+
+       if (new > dir->nChars - SFcharsPerEntry) {
+               new = dir->nChars - SFcharsPerEntry;
+       }
+
+       if (new < 0) {
+               new = 0;
+       }
+
+       if (dir->nChars) {
+               float   f;
+
+               f = ((double) new) / dir->nChars;
+
+               XawScrollbarSetThumb(
+                       w,
+                       f,
+                       (float) (((double) ((dir->nChars < SFcharsPerEntry) ?
+                               dir->nChars : SFcharsPerEntry)) / dir->nChars)
+               );
+
+               SFhSliderMovedCallback(w, n, &f);
+       }
+}
+
+/* ARGSUSED */
+void
+SFpathSliderMovedCallback(w, client_data, new)
+       Widget          w;
+       XtPointer       client_data;
+       float   *new;
+{
+       SFDir           *dir;
+       int             n;
+       XawTextPosition pos;
+       int     SFdirPtrSave;
+
+       SFdirPtrSave = SFdirPtr;
+       SFdirPtr = (*new) * SFdirEnd;
+       if (SFdirPtr == SFdirPtrSave) {
+               return;
+       }
+
+       SFdrawLists(SF_DO_SCROLL);
+
+       n = 2;
+       while (SFdirPtr + n >= SFdirEnd) {
+               n--;
+       }
+
+       dir = &(SFdirs[SFdirPtr + n]);
+
+       pos = dir->path - SFcurrentPath;
+
+       if (!strncmp(SFcurrentPath, SFstartDir, strlen(SFstartDir))) {
+               pos -= strlen(SFstartDir);
+               if (pos < 0) {
+                       pos = 0;
+               }
+       }
+
+       XawTextSetInsertionPoint(selFileField, pos);
+}
+
+/* ARGSUSED */
+
+void
+SFpathAreaSelectedCallback(w, client_data, pnew)
+       Widget          w;
+       XtPointer       client_data;
+       int             pnew;
+{
+       int     new;
+       float   f;
+
+       new = SFdirPtr + (((double) pnew) / SFpathScrollWidth) * SFdirEnd;
+
+       if (new > SFdirEnd - 3) {
+               new = SFdirEnd - 3;
+       }
+
+       if (new < 0) {
+               new = 0;
+       }
+
+       f = ((double) new) / SFdirEnd;
+
+       XawScrollbarSetThumb(
+               w,
+               f,
+               (float) (((double) ((SFdirEnd < 3) ? SFdirEnd : 3)) /
+                       SFdirEnd)
+       );
+
+       SFpathSliderMovedCallback(w, (XtPointer) NULL, &f);
+}
+
+Boolean
+SFworkProc()
+{
+       register SFDir          *dir;
+       register SFEntry        *entry;
+
+       for (dir = &(SFdirs[SFdirEnd - 1]); dir >= SFdirs; dir--) {
+               if (!(dir->nEntries)) {
+                       continue;
+               }
+               for (
+                       entry = &(dir->entries[dir->nEntries - 1]);
+                       entry >= dir->entries;
+                       entry--
+               ) {
+                       if (!(entry->statDone)) {
+                               (void) SFstatAndCheck(dir, entry);
+                               return False;
+                       }
+               }
+       }
+
+       SFworkProcAdded = 0;
+
+       return True;
+}
diff --git a/path.c b/path.c
new file mode 100644 (file)
index 0000000..6379e79
--- /dev/null
+++ b/path.c
@@ -0,0 +1,900 @@
+/*
+ * Copyright 1989 Software Research Associates, Inc., Tokyo, Japan
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose and without fee is hereby granted, provided
+ * that the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of Software Research Associates not be used
+ * in advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.  Software Research Associates
+ * makes no representations about the suitability of this software for any
+ * purpose.  It is provided "as is" without express or implied warranty.
+ *
+ * SOFTWARE RESEARCH ASSOCIATES DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
+ * IN NO EVENT SHALL SOFTWARE RESEARCH ASSOCIATES BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Erik M. van der Poel
+ *         Software Research Associates, Inc., Tokyo, Japan
+ *         erik@sra.co.jp
+ */
+
+#include <stdio.h>
+
+#ifdef SEL_FILE_IGNORE_CASE
+#include <ctype.h>
+#endif /* def SEL_FILE_IGNORE_CASE */
+
+#include <X11/Xos.h>
+#include <pwd.h>
+#include "selfile.h"
+#include "xstat.h"
+#include <X11/Xaw/Scrollbar.h>
+
+#if defined(SVR4) || defined(SYSV) || defined(USG)
+extern uid_t getuid();
+extern void qsort();
+#endif /* defined(SVR4) || defined(SYSV) || defined(USG) */
+
+typedef struct {
+       char    *name;
+       char    *dir;
+} SFLogin;
+
+SFDir *SFdirs = NULL;
+
+int SFdirEnd;
+
+int SFdirPtr;
+
+int SFbuttonPressed = 0;
+
+static int SFdoNotTouchDirPtr = 0;
+
+static int SFdoNotTouchVorigin = 0;
+
+static SFDir SFrootDir, SFhomeDir;
+
+static SFLogin *SFlogins;
+
+static int SFtwiddle = 0;
+
+int
+SFchdir(path)
+       char    *path;
+{
+       int     result;
+
+       result = 0;
+
+       if (strcmp(path, SFcurrentDir)) {
+               result = chdir(path);
+               if (!result) {
+                       (void) strcpy(SFcurrentDir, path);
+               }
+       }
+
+       return result;
+}
+
+static
+SFfree(i)
+       int     i;
+{
+       register SFDir  *dir;
+       register int    j;
+
+       dir = &(SFdirs[i]);
+
+       for (j = dir->nEntries - 1; j >= 0; j--) {
+               if (dir->entries[j].shown != dir->entries[j].real) {
+                       XtFree(dir->entries[j].shown);
+               }
+               XtFree(dir->entries[j].real);
+       }
+
+       XtFree((char *) dir->entries);
+
+       XtFree(dir->dir);
+
+       dir->dir = NULL;
+}
+
+static
+SFstrdup(s1, s2)
+       char    **s1;
+       char    *s2;
+{
+       *s1 = strcpy(XtMalloc((unsigned) (strlen(s2) + 1)), s2);
+}
+
+static
+SFunreadableDir(dir)
+       SFDir   *dir;
+{
+       char    *cannotOpen = "<cannot open> ";
+
+       dir->entries = (SFEntry *) XtMalloc(sizeof(SFEntry));
+       dir->entries[0].statDone = 1;
+       SFstrdup(&dir->entries[0].real, cannotOpen);
+       dir->entries[0].shown = dir->entries[0].real;
+       dir->nEntries = 1;
+       dir->nChars = strlen(cannotOpen);
+}
+
+#ifdef SEL_FILE_IGNORE_CASE
+static
+SFstrncmp(p, q, n)
+       register char   *p, *q;
+       register int    n;
+{
+       register char   c1, c2;
+       char            *psave, *qsave;
+       int             nsave;
+
+       psave = p;
+       qsave = q;
+       nsave = n;
+
+       c1 = *p++;
+       if (islower(c1)) {
+               c1 = toupper(c1);
+       }
+       c2 = *q++;
+       if (islower(c2)) {
+               c2 = toupper(c2);
+       }
+
+       while ((--n >= 0) && (c1 == c2)) {
+               if (!c1) {
+                       return strncmp(psave, qsave, nsave);
+               }
+               c1 = *p++;
+               if (islower(c1)) {
+                       c1 = toupper(c1);
+               }
+               c2 = *q++;
+               if (islower(c2)) {
+                       c2 = toupper(c2);
+               }
+       }
+
+       if (n < 0) {
+               return strncmp(psave, qsave, nsave);
+       }
+
+       return c1 - c2;
+}
+#endif /* def SEL_FILE_IGNORE_CASE */
+
+static
+SFreplaceText(dir, str)
+       SFDir   *dir;
+       char    *str;
+{
+       int     len;
+
+       *(dir->path) = 0;
+       len = strlen(str);
+       if (str[len - 1] == '/') {
+               (void) strcat(SFcurrentPath, str);
+       } else {
+               (void) strncat(SFcurrentPath, str, len - 1);
+       }
+       if (strncmp(SFcurrentPath, SFstartDir, strlen(SFstartDir))) {
+               SFsetText(SFcurrentPath);
+       } else {
+               SFsetText(&(SFcurrentPath[strlen(SFstartDir)]));
+       }
+
+       SFtextChanged();
+}
+
+static void
+SFexpand(str)
+       char    *str;
+{
+       int     len;
+       int     cmp;
+       char    *name, *growing;
+       SFDir   *dir;
+       SFEntry *entry, *max;
+
+       len = strlen(str);
+
+       dir = &(SFdirs[SFdirEnd - 1]);
+
+       if (dir->beginSelection == -1) {
+               SFstrdup(&str, str);
+               SFreplaceText(dir, str);
+               XtFree(str);
+               return;
+       } else if (dir->beginSelection == dir->endSelection) {
+               SFreplaceText(dir, dir->entries[dir->beginSelection].shown);
+               return;
+       }
+
+       max = &(dir->entries[dir->endSelection + 1]);
+
+       name = dir->entries[dir->beginSelection].shown;
+       SFstrdup(&growing, name);
+
+       cmp = 0;
+       while (!cmp) {
+               entry = &(dir->entries[dir->beginSelection]);
+               while (entry < max) {
+                       if (cmp = strncmp(growing, entry->shown, len)) {
+                               break;
+                       }
+                       entry++;
+               }
+               len++;
+       }
+
+       /*
+        * SFreplaceText() expects filename
+        */
+       growing[len - 2] = ' ';
+
+       growing[len - 1] = 0;
+       SFreplaceText(dir, growing);
+       XtFree(growing);
+}
+
+static int
+SFfindFile(dir, str)
+       SFDir           *dir;
+       register char   *str;
+{
+       register int    i, last, max;
+       register char   *name, save;
+       SFEntry         *entries;
+       int             len;
+       int             begin, end;
+       int             result;
+
+       len = strlen(str);
+
+       if (str[len - 1] == ' ') {
+               SFexpand(str);
+               return 1;
+       } else if (str[len - 1] == '/') {
+               len--;
+       }
+
+       max = dir->nEntries;
+
+       entries = dir->entries;
+
+       i = 0;
+       while (i < max) {
+               name = entries[i].shown;
+               last = strlen(name) - 1;
+               save = name[last];
+               name[last] = 0;
+
+#ifdef SEL_FILE_IGNORE_CASE
+               result = SFstrncmp(str, name, len);
+#else /* def SEL_FILE_IGNORE_CASE */
+               result = strncmp(str, name, len);
+#endif /* def SEL_FILE_IGNORE_CASE */
+
+               name[last] = save;
+               if (result <= 0) {
+                       break;
+               }
+               i++;
+       }
+       begin = i;
+       while (i < max) {
+               name = entries[i].shown;
+               last = strlen(name) - 1;
+               save = name[last];
+               name[last] = 0;
+
+#ifdef SEL_FILE_IGNORE_CASE
+               result = SFstrncmp(str, name, len);
+#else /* def SEL_FILE_IGNORE_CASE */
+               result = strncmp(str, name, len);
+#endif /* def SEL_FILE_IGNORE_CASE */
+
+               name[last] = save;
+               if (result) {
+                       break;
+               }
+               i++;
+       }
+       end = i;
+
+       if (begin != end) {
+               if (
+                       (dir->beginSelection != begin) ||
+                       (dir->endSelection != end - 1)
+               ) {
+                       dir->changed = 1;
+                       dir->beginSelection = begin;
+                       if (str[strlen(str) - 1] == '/') {
+                               dir->endSelection = begin;
+                       } else {
+                               dir->endSelection = end - 1;
+                       }
+               }
+       } else {
+               if (dir->beginSelection != -1) {
+                       dir->changed = 1;
+                       dir->beginSelection = -1;
+                       dir->endSelection = -1;
+               }
+       }
+
+       if (
+               SFdoNotTouchVorigin ||
+               ((begin > dir->vOrigin) && (end < dir->vOrigin + SFlistSize))
+       ) {
+               SFdoNotTouchVorigin = 0;
+               return 0;
+       }
+
+       i = begin - 1;
+       if (i > max - SFlistSize) {
+               i = max - SFlistSize;
+       }
+       if (i < 0) {
+               i = 0;
+       }
+
+       if (dir->vOrigin != i) {
+               dir->vOrigin = i;
+               dir->changed = 1;
+       }
+
+       return 0;
+}
+
+static
+SFunselect()
+{
+       SFDir   *dir;
+
+       dir = &(SFdirs[SFdirEnd - 1]);
+       if (dir->beginSelection != -1) {
+               dir->changed = 1;
+       }
+       dir->beginSelection = -1;
+       dir->endSelection = -1;
+}
+
+static int
+SFcompareLogins(p, q)
+       SFLogin *p, *q;
+{
+       return strcmp(p->name, q->name);
+}
+
+static
+SFgetHomeDirs()
+{
+       struct passwd   *pw;
+       int             alloc;
+       int             i;
+       SFEntry         *entries = NULL;
+       int             len;
+       int             maxChars;
+
+       {
+                       alloc = 1;
+                       i = 1;
+                       entries = (SFEntry *) XtMalloc(sizeof(SFEntry));
+                       SFlogins = (SFLogin *) XtMalloc(sizeof(SFLogin));
+                       entries[0].real = XtMalloc(3);
+                       (void) strcpy(entries[0].real, "~");
+                       entries[0].shown = entries[0].real;
+                       entries[0].statDone = 1;
+                       SFlogins[0].name = "";
+                       pw = getpwuid((int) getuid());
+                       SFstrdup(&SFlogins[0].dir, pw ? pw->pw_dir : "/");
+                       maxChars = 0;
+       }
+
+       (void) setpwent();
+
+       while ((pw = getpwent()) && (*(pw->pw_name))) {
+                       if (i >= alloc) {
+                               alloc *= 2;
+                               entries = (SFEntry *) XtRealloc(
+                                       (char *) entries,
+                                       (unsigned) (alloc * sizeof(SFEntry))
+                               );
+                               SFlogins = (SFLogin *) XtRealloc(
+                                       (char *) SFlogins,
+                                       (unsigned) (alloc * sizeof(SFLogin))
+                               );
+                       }
+                       len = strlen(pw->pw_name);
+                       entries[i].real = XtMalloc((unsigned) (len + 3));
+                       (void) strcat(strcpy(entries[i].real, "~"),
+                               pw->pw_name);
+                       entries[i].shown = entries[i].real;
+                       entries[i].statDone = 1;
+                       if (len > maxChars) {
+                               maxChars = len;
+                       }
+                       SFstrdup(&SFlogins[i].name, pw->pw_name);
+                       SFstrdup(&SFlogins[i].dir, pw->pw_dir);
+                       i++;
+       }
+
+       SFhomeDir.dir                   = XtMalloc(1)   ;
+       SFhomeDir.dir[0]                = 0             ;
+       SFhomeDir.path                  = SFcurrentPath ;
+       SFhomeDir.entries               = entries       ;
+       SFhomeDir.nEntries              = i             ;
+       SFhomeDir.vOrigin               = 0             ;       /* :-) */
+       SFhomeDir.nChars                = maxChars + 2  ;
+       SFhomeDir.hOrigin               = 0             ;
+       SFhomeDir.changed               = 1             ;
+       SFhomeDir.beginSelection        = -1            ;
+       SFhomeDir.endSelection          = -1            ;
+
+#if defined(SVR4) || defined(SYSV) || defined(USG)
+       qsort((char *) entries, (unsigned)i, sizeof(SFEntry), SFcompareEntries);
+       qsort((char *) SFlogins, (unsigned)i, sizeof(SFLogin), SFcompareLogins);
+#else /* defined(SVR4) || defined(SYSV) || defined(USG) */
+       qsort((char *) entries, i, sizeof(SFEntry), SFcompareEntries);
+       qsort((char *) SFlogins, i, sizeof(SFLogin), SFcompareLogins);
+#endif /* defined(SVR4) || defined(SYSV) || defined(USG) */
+
+       for (i--; i >= 0; i--) {
+               (void) strcat(entries[i].real, "/");
+       }
+}
+
+static int
+SFfindHomeDir(begin, end)
+       char    *begin, *end;
+{
+       char    save;
+       char    *theRest;
+       int     i;
+
+       save = *end;
+       *end = 0;
+
+       for (i = SFhomeDir.nEntries - 1; i >= 0; i--) {
+               if (!strcmp(SFhomeDir.entries[i].real, begin)) {
+                       *end = save;
+                       SFstrdup(&theRest, end);
+                       (void) strcat(strcat(strcpy(SFcurrentPath,
+                               SFlogins[i].dir), "/"), theRest);
+                       XtFree(theRest);
+                       SFsetText(SFcurrentPath);
+                       SFtextChanged();
+                       return 1;
+               }
+       }
+
+       *end = save;
+
+       return 0;
+}
+
+SFupdatePath()
+{
+       static int      alloc;
+       static int      wasTwiddle = 0;
+       char            *begin, *end;
+       int             i, j;
+       int             prevChange;
+       int             SFdirPtrSave, SFdirEndSave;
+       SFDir           *dir;
+
+       if (!SFdirs) {
+               SFdirs = (SFDir *) XtMalloc((alloc = 10) * sizeof(SFDir));
+               dir = &(SFdirs[0]);
+               SFstrdup(&dir->dir, "/");
+               (void) SFchdir("/");
+               (void) SFgetDir(dir);
+               for (j = 1; j < alloc; j++) {
+                       SFdirs[j].dir = NULL;
+               }
+               dir->path = SFcurrentPath + 1;
+               dir->vOrigin = 0;
+               dir->hOrigin = 0;
+               dir->changed = 1;
+               dir->beginSelection = -1;
+               dir->endSelection = -1;
+               SFhomeDir.dir = NULL;
+       }
+
+       SFdirEndSave = SFdirEnd;
+       SFdirEnd = 1;
+
+       SFdirPtrSave = SFdirPtr;
+       SFdirPtr = 0;
+
+       begin = NULL;
+
+       if (SFcurrentPath[0] == '~') {
+               if (!SFtwiddle) {
+                       SFtwiddle = 1;
+                       dir = &(SFdirs[0]);
+                       SFrootDir = *dir;
+                       if (!SFhomeDir.dir) {
+                               SFgetHomeDirs();
+                       }
+                       *dir = SFhomeDir;
+                       dir->changed = 1;
+               }
+               end = SFcurrentPath;
+               SFdoNotTouchDirPtr = 1;
+               wasTwiddle = 1;
+       } else {
+               if (SFtwiddle) {
+                       SFtwiddle = 0;
+                       dir = &(SFdirs[0]);
+                       *dir = SFrootDir;
+                       dir->changed = 1;
+               }
+               end = SFcurrentPath + 1;
+       }
+
+       i = 0;
+
+       prevChange = 0;
+
+       while (*end) {
+               while (*end++ == '/') {
+                       ;
+               }
+               end--;
+               begin = end;
+               while ((*end) && (*end++ != '/')) {
+                       ;
+               }
+               if ((end - SFcurrentPath <= SFtextPos) && (*(end - 1) == '/')) {
+                       SFdirPtr = i - 1;
+                       if (SFdirPtr < 0) {
+                               SFdirPtr = 0;
+                       }
+               }
+               if (*begin) {
+                       if (*(end - 1) == '/') {
+                               char save = *end;
+
+                               if (SFtwiddle) {
+                                       if (SFfindHomeDir(begin, end)) {
+                                               return;
+                                       }
+                               }
+                               *end = 0;
+                               i++;
+                               SFdirEnd++;
+                               if (i >= alloc) {
+                                       SFdirs = (SFDir *) XtRealloc(
+                                               (char *) SFdirs,
+                                               (unsigned) ((alloc *= 2) *
+                                                       sizeof(SFDir))
+                                       );
+                                       for (j = alloc / 2; j < alloc; j++) {
+                                               SFdirs[j].dir = NULL;
+                                       }
+                               }
+                               dir = &(SFdirs[i]);
+                               if (
+                                       (!(dir->dir)) ||
+                                       prevChange ||
+                                       strcmp(dir->dir, begin)
+                               ) {
+                                       if (dir->dir) {
+                                               SFfree(i);
+                                       }
+                                       prevChange = 1;
+                                       SFstrdup(&dir->dir, begin);
+                                       dir->path = end;
+                                       dir->vOrigin = 0;
+                                       dir->hOrigin = 0;
+                                       dir->changed = 1;
+                                       dir->beginSelection = -1;
+                                       dir->endSelection = -1;
+                                       (void) SFfindFile(dir - 1, begin);
+                                       if (
+                                               SFchdir(SFcurrentPath) ||
+                                               SFgetDir(dir)
+                                       ) {
+                                               SFunreadableDir(dir);
+                                               break;
+                                       }
+                               }
+                               *end = save;
+                               if (!save) {
+                                       SFunselect();
+                               }
+                       } else {
+                               if (SFfindFile(&(SFdirs[SFdirEnd-1]), begin)) {
+                                       return;
+                               }
+                       }
+               } else {
+                       SFunselect();
+               }
+       }
+
+       if ((end == SFcurrentPath + 1) && (!SFtwiddle)) {
+               SFunselect();
+       }
+
+       for (i = SFdirEnd; i < alloc; i++) {
+               if (SFdirs[i].dir) {
+                       SFfree(i);
+               }
+       }
+
+       if (SFdoNotTouchDirPtr) {
+               if (wasTwiddle) {
+                       wasTwiddle = 0;
+                       SFdirPtr = SFdirEnd - 1;
+                       if (SFdirPtr < 0) {
+                               SFdirPtr = 0;
+                       }
+               } else {
+                       SFdirPtr = SFdirPtrSave;
+               }
+               SFdoNotTouchDirPtr = 0;
+       }
+
+       if ((SFdirPtr != SFdirPtrSave) || (SFdirEnd != SFdirEndSave)) {
+               XawScrollbarSetThumb(
+                       selFileHScroll,
+                       (float) (((double) SFdirPtr) / SFdirEnd),
+                       (float) (((double) ((SFdirEnd < NR) ? SFdirEnd : NR)) /
+                               SFdirEnd)
+               );
+       }
+
+       if (SFdirPtr != SFdirPtrSave) {
+               SFdrawLists(SF_DO_SCROLL);
+       } else {
+               for (i = 0; i < NR; i++) {
+                       if (SFdirPtr + i < SFdirEnd) {
+                               if (SFdirs[SFdirPtr + i].changed) {
+                                       SFdirs[SFdirPtr + i].changed = 0;
+                                       SFdrawList(i, SF_DO_SCROLL);
+                               }
+                       } else {
+                               SFclearList(i, SF_DO_SCROLL);
+                       }
+               }
+       }
+}
+
+SFsetText(path)
+       char    *path;
+{
+       XawTextBlock    text;
+
+       text.firstPos = 0;
+       text.length = strlen(path);
+       text.ptr = path;
+       text.format = FMT8BIT;
+
+       XawTextReplace(selFileField, 0, strlen(SFtextBuffer), &text);
+       XawTextSetInsertionPoint(selFileField, strlen(SFtextBuffer));
+}
+
+/* ARGSUSED */
+void
+SFbuttonPressList(w, n, event)
+       Widget                  w;
+       int                     n;
+       XButtonPressedEvent     *event;
+{
+       SFbuttonPressed = 1;
+}
+
+/* ARGSUSED */
+void
+SFbuttonReleaseList(w, n, event)
+       Widget                  w;
+       int                     n;
+       XButtonReleasedEvent    *event;
+{
+       SFDir   *dir;
+
+       SFbuttonPressed = 0;
+
+       if (SFcurrentInvert[n] != -1) {
+               if (n < 2) {
+                       SFdoNotTouchDirPtr = 1;
+               }
+               SFdoNotTouchVorigin = 1;
+               dir = &(SFdirs[SFdirPtr + n]);
+               SFreplaceText(
+                       dir,
+                       dir->entries[dir->vOrigin + SFcurrentInvert[n]].shown
+               );
+               SFmotionList(w, n, event);
+       }
+}
+
+static int
+SFcheckDir(n, dir)
+       int             n;
+       SFDir           *dir;
+{
+       struct stat     statBuf;
+       int             i;
+
+       if (
+               (!stat(".", &statBuf)) &&
+               (statBuf.st_mtime != dir->mtime)
+       ) {
+
+               /*
+                * If the pointer is currently in the window that we are about
+                * to update, we must warp it to prevent the user from
+                * accidentally selecting the wrong file.
+                */
+               if (SFcurrentInvert[n] != -1) {
+                       XWarpPointer(
+                               SFdisplay,
+                               None,
+                               XtWindow(selFileLists[n]),
+                               0,
+                               0,
+                               0,
+                               0,
+                               0,
+                               0
+                       );
+               }
+
+               for (i = dir->nEntries - 1; i >= 0; i--) {
+                       if (dir->entries[i].shown != dir->entries[i].real) {
+                               XtFree(dir->entries[i].shown);
+                       }
+                       XtFree(dir->entries[i].real);
+               }
+               XtFree((char *) dir->entries);
+               if (SFgetDir(dir)) {
+                       SFunreadableDir(dir);
+               }
+               if (dir->vOrigin > dir->nEntries - SFlistSize) {
+                       dir->vOrigin = dir->nEntries - SFlistSize;
+               }
+               if (dir->vOrigin < 0) {
+                       dir->vOrigin = 0;
+               }
+               if (dir->hOrigin > dir->nChars - SFcharsPerEntry) {
+                       dir->hOrigin = dir->nChars - SFcharsPerEntry;
+               }
+               if (dir->hOrigin < 0) {
+                       dir->hOrigin = 0;
+               }
+               dir->beginSelection = -1;
+               dir->endSelection = -1;
+               SFdoNotTouchVorigin = 1;
+               if ((dir + 1)->dir) {
+                       (void) SFfindFile(dir, (dir + 1)->dir);
+               } else {
+                       (void) SFfindFile(dir, dir->path);
+               }
+
+               if (!SFworkProcAdded) {
+                       (void) XtAppAddWorkProc(SFapp, SFworkProc, NULL);
+                       SFworkProcAdded = 1;
+               }
+
+               return 1;
+       }
+
+       return 0;
+}
+
+static int
+SFcheckFiles(dir)
+       SFDir   *dir;
+{
+       int             from, to;
+       int             result;
+       char            old, new;
+       int             i;
+       char            *str;
+       int             last;
+       struct stat     statBuf;
+
+       result = 0;
+
+       from = dir->vOrigin;
+       to = dir->vOrigin + SFlistSize;
+       if (to > dir->nEntries) {
+               to = dir->nEntries;
+       }
+
+       for (i = from; i < to; i++) {
+               str = dir->entries[i].real;
+               last = strlen(str) - 1;
+               old = str[last];
+               str[last] = 0;
+               if (stat(str, &statBuf)) {
+                       new = ' ';
+               } else {
+                       new = SFstatChar(&statBuf);
+               }
+               str[last] = new;
+               if (new != old) {
+                       result = 1;
+               }
+       }
+
+       return result;
+}
+
+void
+SFdirModTimer(cl, id)
+        XtPointer       cl;
+        XtIntervalId    *id;
+{
+       static int      n = -1;
+       static int      f = 0;
+       char            save;
+       SFDir           *dir;
+
+       if ((!SFtwiddle) && (SFdirPtr < SFdirEnd)) {
+               n++;
+               if ((n > NR-1) || (SFdirPtr + n >= SFdirEnd)) {
+                       n = 0;
+                       f++;
+                       if ((f > NR-1) || (SFdirPtr + f >= SFdirEnd)) {
+                               f = 0;
+                       }
+               }
+               dir = &(SFdirs[SFdirPtr + n]);
+               save = *(dir->path);
+               *(dir->path) = 0;
+               if (SFchdir(SFcurrentPath)) {
+                       *(dir->path) = save;
+
+                       /*
+                        * force a re-read
+                        */
+                       *(dir->dir) = 0;
+
+                       SFupdatePath();
+               } else {
+                       *(dir->path) = save;
+                       if (
+                               SFcheckDir(n, dir) ||
+                               ((f == n) && SFcheckFiles(dir))
+                       ) {
+                               SFdrawList(n, SF_DO_SCROLL);
+                       }
+               }
+       }
+
+       SFdirModTimerId = XtAppAddTimeOut(SFapp, (unsigned long) 1000,
+               SFdirModTimer, (XtPointer) NULL);
+}
+
+/* Return a single character describing what kind of file STATBUF is.  */
+
+char
+SFstatChar (statBuf)
+       struct stat *statBuf;
+{
+       if (S_ISDIR (statBuf->st_mode)) {
+               return '/';
+       } else if (S_ISREG (statBuf->st_mode)) {
+         return S_ISXXX (statBuf->st_mode) ? '*' : ' ';
+#ifdef S_ISSOCK
+       } else if (S_ISSOCK (statBuf->st_mode)) {
+               return '=';
+#endif /* S_ISSOCK */
+       } else {
+               return ' ';
+       }
+}
diff --git a/selfile.c b/selfile.c
new file mode 100644 (file)
index 0000000..422cf7b
--- /dev/null
+++ b/selfile.c
@@ -0,0 +1,753 @@
+/*
+ * Copyright 1989 Software Research Associates, Inc., Tokyo, Japan
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose and without fee is hereby granted, provided
+ * that the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of Software Research Associates not be used
+ * in advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.  Software Research Associates
+ * makes no representations about the suitability of this software for any
+ * purpose.  It is provided "as is" without express or implied warranty.
+ *
+ * SOFTWARE RESEARCH ASSOCIATES DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
+ * IN NO EVENT SHALL SOFTWARE RESEARCH ASSOCIATES BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Erik M. van der Poel
+ *         Software Research Associates, Inc., Tokyo, Japan
+ *         erik@sra.co.jp
+ */
+
+/*
+ * Author's address:
+ *
+ *     erik@sra.co.jp
+ *                                            OR
+ *     erik%sra.co.jp@uunet.uu.net
+ *                                            OR
+ *     erik%sra.co.jp@mcvax.uucp
+ *                                            OR
+ *     try junet instead of co.jp
+ *                                            OR
+ *     Erik M. van der Poel
+ *     Software Research Associates, Inc.
+ *     1-1-1 Hirakawa-cho, Chiyoda-ku
+ *     Tokyo 102 Japan. TEL +81-3-234-2692
+ */
+
+#include <stdio.h>
+#include <errno.h>
+/* BSD 4.3 errno.h does not declare errno */
+extern int errno;
+extern int sys_nerr;
+//extern char *sys_errlist[]; // [HGM] this produced a compile error in Ubuntu 8.04
+
+#include <sys/param.h>
+#include <X11/cursorfont.h>
+#include <X11/Intrinsic.h>
+#include <X11/StringDefs.h>
+#include <X11/Composite.h>
+#include <X11/Shell.h>
+#include <X11/Xaw/Form.h>
+#include <X11/Xaw/Command.h>
+#include <X11/Xaw/Scrollbar.h>
+#include <X11/Xaw/Label.h>
+#include <X11/Xaw/Cardinals.h>
+
+#include "selfile.h"
+
+#ifndef MAXPATHLEN
+#define MAXPATHLEN 1024
+#endif /* ndef MAXPATHLEN */
+
+#if !defined(SVR4) && !defined(SYSV) && !defined(USG)
+extern char *getwd();
+#endif /* !defined(SVR4) && !defined(SYSV) && !defined(USG) */
+
+int SFstatus = SEL_FILE_NULL;
+
+char
+       SFstartDir[MAXPATHLEN],
+       SFcurrentPath[MAXPATHLEN],
+       SFcurrentDir[MAXPATHLEN];
+
+Widget
+       selFile,
+       selFileCancel,
+       selFileField,
+       selFileForm,
+       selFileHScroll,
+       selFileHScrolls[3],
+       selFileLists[3],
+       selFileOK,
+       selFilePrompt,
+       selFileVScrolls[3];
+
+Display *SFdisplay;
+
+Pixel SFfore, SFback;
+
+Atom   SFwmDeleteWindow;
+
+XSegment SFsegs[2], SFcompletionSegs[2];
+
+XawTextPosition SFtextPos;
+
+int SFupperX, SFlowerY, SFupperY;
+
+int SFtextX, SFtextYoffset;
+
+int SFentryWidth, SFentryHeight;
+
+int SFlineToTextH = 3;
+
+int SFlineToTextV = 3;
+
+int SFbesideText = 3;
+
+int SFaboveAndBelowText = 2;
+
+int SFcharsPerEntry = 15;
+
+int SFlistSize = 10;
+
+int SFworkProcAdded = 0;
+
+XtAppContext SFapp;
+
+int SFpathScrollWidth, SFvScrollHeight, SFhScrollWidth;
+
+char SFtextBuffer[MAXPATHLEN];
+
+XtIntervalId SFdirModTimerId;
+
+int (*SFfunc)();
+
+static char *oneLineTextEditTranslations = "\
+       <Key>Return:    redraw-display()\n\
+       Ctrl<Key>M:     redraw-display()\n\
+";
+
+/* ARGSUSED */
+static void
+SFexposeList(w, n, event, cont)
+       Widget          w;
+       XtPointer       n;
+        XEvent         *event;
+        Boolean         *cont;
+{
+       if ((event->type == NoExpose) || event->xexpose.count) {
+               return;
+       }
+
+       SFdrawList(n, SF_DO_NOT_SCROLL);
+}
+
+/* ARGSUSED */
+static void
+SFmodVerifyCallback(w, client_data, event, cont)
+       Widget                  w;
+       XtPointer               client_data;
+        XEvent                 *event;
+        Boolean                 *cont;
+{
+       char    buf[2];
+
+       if (
+               (XLookupString(&(event->xkey), buf, 2, NULL, NULL) == 1) &&
+               ((*buf) == '\r')
+       ) {
+               SFstatus = SEL_FILE_OK;
+       } else {
+               SFstatus = SEL_FILE_TEXT;
+       }
+}
+
+/* ARGSUSED */
+static void
+SFokCallback(w, cl, cd)
+       Widget  w;
+        XtPointer cl, cd;
+{
+       SFstatus = SEL_FILE_OK;
+}
+
+static XtCallbackRec SFokSelect[] = {
+       { SFokCallback, (XtPointer) NULL },
+       { NULL, (XtPointer) NULL },
+};
+
+/* ARGSUSED */
+static void
+SFcancelCallback(w, cl, cd)
+       Widget  w;
+        XtPointer cl, cd;
+{
+       SFstatus = SEL_FILE_CANCEL;
+}
+
+static XtCallbackRec SFcancelSelect[] = {
+       { SFcancelCallback, (XtPointer) NULL },
+       { NULL, (XtPointer) NULL },
+};
+
+/* ARGSUSED */
+static void
+SFdismissAction(w, event, params, num_params)
+       Widget  w;
+       XEvent *event;
+       String *params;
+       Cardinal *num_params;
+{
+       if (event->type == ClientMessage &&
+           event->xclient.data.l[0] != SFwmDeleteWindow) return;
+
+       SFstatus = SEL_FILE_CANCEL;
+}
+
+static char *wmDeleteWindowTranslation = "\
+       <Message>WM_PROTOCOLS:  SelFileDismiss()\n\
+";
+
+static XtActionsRec actions[] = {
+       {"SelFileDismiss",      SFdismissAction},
+};
+
+static void
+SFcreateWidgets(toplevel, prompt, ok, cancel)
+       Widget  toplevel;
+       char    *prompt;
+       char    *ok;
+       char    *cancel;
+{
+       Cardinal        i, n;
+       int             listWidth, listHeight;
+       int             listSpacing = 10;
+       int             scrollThickness = 15;
+       int             hScrollX, hScrollY;
+       int             vScrollX, vScrollY;
+       Cursor
+                       xtermCursor,
+                       sbRightArrowCursor,
+                       dotCursor;
+       Arg             arglist[20];
+
+       i = 0;
+       XtSetArg(arglist[i], XtNtransientFor, toplevel);                i++;
+
+       selFile = XtAppCreateShell("Browse", "SelFile",
+               transientShellWidgetClass, SFdisplay, arglist, i);
+
+       /* Add WM_DELETE_WINDOW protocol */
+       XtAppAddActions(XtWidgetToApplicationContext(selFile),
+               actions, XtNumber(actions));
+       XtOverrideTranslations(selFile,
+               XtParseTranslationTable(wmDeleteWindowTranslation));
+
+       i = 0;
+       XtSetArg(arglist[i], XtNdefaultDistance, 30);                   i++;
+       selFileForm = XtCreateManagedWidget("selFileForm",
+               formWidgetClass, selFile, arglist, i);
+
+       i = 0;
+       XtSetArg(arglist[i], XtNlabel, prompt);                         i++;
+       XtSetArg(arglist[i], XtNresizable, True);                       i++;
+       XtSetArg(arglist[i], XtNtop, XtChainTop);                       i++;
+       XtSetArg(arglist[i], XtNbottom, XtChainTop);                    i++;
+       XtSetArg(arglist[i], XtNleft, XtChainLeft);                     i++;
+       XtSetArg(arglist[i], XtNright, XtChainLeft);                    i++;
+       XtSetArg(arglist[i], XtNborderWidth, 0);                        i++;
+       selFilePrompt = XtCreateManagedWidget("selFilePrompt",
+               labelWidgetClass, selFileForm, arglist, i);
+
+       i = 0;
+       XtSetArg(arglist[i], XtNforeground, &SFfore);                   i++;
+       XtSetArg(arglist[i], XtNbackground, &SFback);                   i++;
+       XtGetValues(selFilePrompt, arglist, i);
+
+       SFinitFont();
+
+       SFentryWidth = SFbesideText + SFcharsPerEntry * SFcharWidth +
+                       SFbesideText;
+       SFentryHeight = SFaboveAndBelowText + SFcharHeight +
+                       SFaboveAndBelowText;
+
+       listWidth = SFlineToTextH + SFentryWidth + SFlineToTextH + 1 +
+                       scrollThickness;
+       listHeight = SFlineToTextV + SFentryHeight + SFlineToTextV + 1 +
+                       SFlineToTextV + SFlistSize * SFentryHeight +
+                       SFlineToTextV + 1 + scrollThickness;
+
+       SFpathScrollWidth = NR * listWidth + (NR-1) * listSpacing + 4;
+
+       hScrollX = -1;
+       hScrollY = SFlineToTextV + SFentryHeight + SFlineToTextV + 1 +
+                       SFlineToTextV + SFlistSize * SFentryHeight +
+                       SFlineToTextV;
+       SFhScrollWidth = SFlineToTextH + SFentryWidth + SFlineToTextH;
+
+       vScrollX = SFlineToTextH + SFentryWidth + SFlineToTextH;
+       vScrollY = SFlineToTextV + SFentryHeight + SFlineToTextV;
+       SFvScrollHeight = SFlineToTextV + SFlistSize * SFentryHeight +
+                       SFlineToTextV;
+
+       SFupperX = SFlineToTextH + SFentryWidth + SFlineToTextH - 1;
+       SFlowerY = SFlineToTextV + SFentryHeight + SFlineToTextV + 1 +
+                       SFlineToTextV;
+       SFupperY = SFlineToTextV + SFentryHeight + SFlineToTextV + 1 +
+                       SFlineToTextV + SFlistSize * SFentryHeight - 1;
+
+       SFtextX = SFlineToTextH + SFbesideText;
+       SFtextYoffset = SFlowerY + SFaboveAndBelowText + SFcharAscent;
+
+       SFsegs[0].x1 = 0;
+       SFsegs[0].y1 = vScrollY;
+       SFsegs[0].x2 = vScrollX - 1;
+       SFsegs[0].y2 = vScrollY;
+       SFsegs[1].x1 = vScrollX;
+       SFsegs[1].y1 = 0;
+       SFsegs[1].x2 = vScrollX;
+       SFsegs[1].y2 = vScrollY - 1;
+
+       SFcompletionSegs[0].x1 = SFcompletionSegs[0].x2 = SFlineToTextH;
+       SFcompletionSegs[1].x1 = SFcompletionSegs[1].x2 =
+               SFlineToTextH + SFentryWidth - 1;
+
+       i = 0;
+       XtSetArg(arglist[i], XtNwidth, NR * listWidth + (NR - 1) * listSpacing + 4);
+                                                                       i++;
+       XtSetArg(arglist[i], XtNborderColor, SFfore);                   i++;
+
+       XtSetArg(arglist[i], XtNfromVert, selFilePrompt);               i++;
+       XtSetArg(arglist[i], XtNvertDistance, 10);                      i++;
+       XtSetArg(arglist[i], XtNresizable, True);                       i++;
+       XtSetArg(arglist[i], XtNtop, XtChainTop);                       i++;
+       XtSetArg(arglist[i], XtNbottom, XtChainTop);                    i++;
+       XtSetArg(arglist[i], XtNleft, XtChainLeft);                     i++;
+       XtSetArg(arglist[i], XtNright, XtChainLeft);                    i++;
+       XtSetArg(arglist[i], XtNstring, SFtextBuffer);                  i++;
+       XtSetArg(arglist[i], XtNlength, MAXPATHLEN);                    i++;
+       XtSetArg(arglist[i], XtNeditType, XawtextEdit);                 i++;
+       XtSetArg(arglist[i], XtNwrap, XawtextWrapWord);                 i++;
+       XtSetArg(arglist[i], XtNresize, XawtextResizeHeight);           i++;
+       XtSetArg(arglist[i], XtNuseStringInPlace, True);                i++;
+       selFileField = XtCreateManagedWidget("selFileField",
+               asciiTextWidgetClass, selFileForm, arglist, i);
+
+       XtOverrideTranslations(selFileField,
+               XtParseTranslationTable(oneLineTextEditTranslations));
+       XtSetKeyboardFocus(selFileForm, selFileField);
+
+       i = 0;
+       XtSetArg(arglist[i], XtNorientation, XtorientHorizontal);       i++;
+       XtSetArg(arglist[i], XtNwidth, SFpathScrollWidth);              i++;
+       XtSetArg(arglist[i], XtNheight, scrollThickness);               i++;
+       XtSetArg(arglist[i], XtNborderColor, SFfore);                   i++;
+       XtSetArg(arglist[i], XtNfromVert, selFileField);                i++;
+       XtSetArg(arglist[i], XtNvertDistance, 30);                      i++;
+       XtSetArg(arglist[i], XtNtop, XtChainTop);                       i++;
+       XtSetArg(arglist[i], XtNbottom, XtChainTop);                    i++;
+       XtSetArg(arglist[i], XtNleft, XtChainLeft);                     i++;
+       XtSetArg(arglist[i], XtNright, XtChainLeft);                    i++;
+       selFileHScroll = XtCreateManagedWidget("selFileHScroll",
+               scrollbarWidgetClass, selFileForm, arglist, i);
+
+       XtAddCallback(selFileHScroll, XtNjumpProc,
+               SFpathSliderMovedCallback, (XtPointer) NULL);
+       XtAddCallback(selFileHScroll, XtNscrollProc,
+               SFpathAreaSelectedCallback, (XtPointer) NULL);
+
+       i = 0;
+       XtSetArg(arglist[i], XtNwidth, listWidth);                      i++;
+       XtSetArg(arglist[i], XtNheight, listHeight);                    i++;
+       XtSetArg(arglist[i], XtNborderColor, SFfore);                   i++;
+       XtSetArg(arglist[i], XtNfromVert, selFileHScroll);              i++;
+       XtSetArg(arglist[i], XtNvertDistance, 10);                      i++;
+       XtSetArg(arglist[i], XtNtop, XtChainTop);                       i++;
+       XtSetArg(arglist[i], XtNbottom, XtChainTop);                    i++;
+       XtSetArg(arglist[i], XtNleft, XtChainLeft);                     i++;
+       XtSetArg(arglist[i], XtNright, XtChainLeft);                    i++;
+       selFileLists[0] = XtCreateManagedWidget("selFileList1",
+               compositeWidgetClass, selFileForm, arglist, i);
+#if (NR == 3)
+       i = 0;
+       XtSetArg(arglist[i], XtNwidth, listWidth);                      i++;
+       XtSetArg(arglist[i], XtNheight, listHeight);                    i++;
+       XtSetArg(arglist[i], XtNborderColor, SFfore);                   i++;
+       XtSetArg(arglist[i], XtNfromHoriz, selFileLists[0]);            i++;
+       XtSetArg(arglist[i], XtNfromVert, selFileHScroll);              i++;
+       XtSetArg(arglist[i], XtNhorizDistance, listSpacing);            i++;
+       XtSetArg(arglist[i], XtNvertDistance, 10);                      i++;
+       XtSetArg(arglist[i], XtNtop, XtChainTop);                       i++;
+       XtSetArg(arglist[i], XtNbottom, XtChainTop);                    i++;
+       XtSetArg(arglist[i], XtNleft, XtChainLeft);                     i++;
+       XtSetArg(arglist[i], XtNright, XtChainLeft);                    i++;
+       selFileLists[1] = XtCreateManagedWidget("selFileList2",
+               compositeWidgetClass, selFileForm, arglist, i);
+
+       i = 0;
+       XtSetArg(arglist[i], XtNwidth, listWidth);                      i++;
+       XtSetArg(arglist[i], XtNheight, listHeight);                    i++;
+       XtSetArg(arglist[i], XtNborderColor, SFfore);                   i++;
+       XtSetArg(arglist[i], XtNfromHoriz, selFileLists[1]);            i++;
+       XtSetArg(arglist[i], XtNfromVert, selFileHScroll);              i++;
+       XtSetArg(arglist[i], XtNhorizDistance, listSpacing);            i++;
+       XtSetArg(arglist[i], XtNvertDistance, 10);                      i++;
+       XtSetArg(arglist[i], XtNtop, XtChainTop);                       i++;
+       XtSetArg(arglist[i], XtNbottom, XtChainTop);                    i++;
+       XtSetArg(arglist[i], XtNleft, XtChainLeft);                     i++;
+       XtSetArg(arglist[i], XtNright, XtChainLeft);                    i++;
+       selFileLists[2] = XtCreateManagedWidget("selFileList3",
+               compositeWidgetClass, selFileForm, arglist, i);
+#endif
+       for (n = 0; n < NR; n++) {
+
+               i = 0;
+               XtSetArg(arglist[i], XtNx, vScrollX);                   i++;
+               XtSetArg(arglist[i], XtNy, vScrollY);                   i++;
+               XtSetArg(arglist[i], XtNwidth, scrollThickness);        i++;
+               XtSetArg(arglist[i], XtNheight, SFvScrollHeight);       i++;
+               XtSetArg(arglist[i], XtNborderColor, SFfore);           i++;
+               selFileVScrolls[n] = XtCreateManagedWidget("selFileVScroll",
+                       scrollbarWidgetClass, selFileLists[n], arglist, i);
+
+               XtAddCallback(selFileVScrolls[n], XtNjumpProc,
+                       SFvFloatSliderMovedCallback, (XtPointer) n);
+               XtAddCallback(selFileVScrolls[n], XtNscrollProc,
+                       SFvAreaSelectedCallback, (XtPointer) n);
+
+               i = 0;
+
+               XtSetArg(arglist[i], XtNorientation, XtorientHorizontal);
+                                                                       i++;
+               XtSetArg(arglist[i], XtNx, hScrollX);                   i++;
+               XtSetArg(arglist[i], XtNy, hScrollY);                   i++;
+               XtSetArg(arglist[i], XtNwidth, SFhScrollWidth);         i++;
+               XtSetArg(arglist[i], XtNheight, scrollThickness);       i++;
+               XtSetArg(arglist[i], XtNborderColor, SFfore);           i++;
+               selFileHScrolls[n] = XtCreateManagedWidget("selFileHScroll",
+                       scrollbarWidgetClass, selFileLists[n], arglist, i);
+
+               XtAddCallback(selFileHScrolls[n], XtNjumpProc,
+                       SFhSliderMovedCallback, (XtPointer) n);
+               XtAddCallback(selFileHScrolls[n], XtNscrollProc,
+                       SFhAreaSelectedCallback, (XtPointer) n);
+       }
+
+       i = 0;
+       XtSetArg(arglist[i], XtNlabel, ok);                             i++;
+       XtSetArg(arglist[i], XtNresizable, True);                       i++;
+       XtSetArg(arglist[i], XtNcallback, SFokSelect);                  i++;
+       XtSetArg(arglist[i], XtNborderColor, SFfore);                   i++;
+       XtSetArg(arglist[i], XtNfromVert, selFileLists[0]);             i++;
+       XtSetArg(arglist[i], XtNvertDistance, 30);                      i++;
+       XtSetArg(arglist[i], XtNtop, XtChainTop);                       i++;
+       XtSetArg(arglist[i], XtNbottom, XtChainTop);                    i++;
+       XtSetArg(arglist[i], XtNleft, XtChainLeft);                     i++;
+       XtSetArg(arglist[i], XtNright, XtChainLeft);                    i++;
+       selFileOK = XtCreateManagedWidget("selFileOK", commandWidgetClass,
+               selFileForm, arglist, i);
+
+       i = 0;
+       XtSetArg(arglist[i], XtNlabel, cancel);                         i++;
+       XtSetArg(arglist[i], XtNresizable, True);                       i++;
+       XtSetArg(arglist[i], XtNcallback, SFcancelSelect);              i++;
+       XtSetArg(arglist[i], XtNborderColor, SFfore);                   i++;
+       XtSetArg(arglist[i], XtNfromHoriz, selFileOK);                  i++;
+       XtSetArg(arglist[i], XtNfromVert, selFileLists[0]);             i++;
+       XtSetArg(arglist[i], XtNhorizDistance, 30);                     i++;
+       XtSetArg(arglist[i], XtNvertDistance, 30);                      i++;
+       XtSetArg(arglist[i], XtNtop, XtChainTop);                       i++;
+       XtSetArg(arglist[i], XtNbottom, XtChainTop);                    i++;
+       XtSetArg(arglist[i], XtNleft, XtChainLeft);                     i++;
+       XtSetArg(arglist[i], XtNright, XtChainLeft);                    i++;
+       selFileCancel = XtCreateManagedWidget("selFileCancel",
+               commandWidgetClass, selFileForm, arglist, i);
+
+       XtSetMappedWhenManaged(selFile, False);
+       XtRealizeWidget(selFile);
+
+       /* Add WM_DELETE_WINDOW protocol */
+       SFwmDeleteWindow = XInternAtom(SFdisplay, "WM_DELETE_WINDOW", False);
+       XSetWMProtocols(SFdisplay, XtWindow(selFile), &SFwmDeleteWindow, 1);
+
+       SFcreateGC();
+
+       xtermCursor = XCreateFontCursor(SFdisplay, XC_xterm);
+
+       sbRightArrowCursor = XCreateFontCursor(SFdisplay, XC_sb_right_arrow);
+       dotCursor = XCreateFontCursor(SFdisplay, XC_dot);
+
+       XDefineCursor(SFdisplay, XtWindow(selFileForm), xtermCursor);
+       XDefineCursor(SFdisplay, XtWindow(selFileField), xtermCursor);
+
+       for (n = 0; n < NR; n++) {
+               XDefineCursor(SFdisplay, XtWindow(selFileLists[n]),
+                       sbRightArrowCursor);
+       }
+       XDefineCursor(SFdisplay, XtWindow(selFileOK), dotCursor);
+       XDefineCursor(SFdisplay, XtWindow(selFileCancel), dotCursor);
+
+       for (n = 0; n < NR; n++) {
+               XtAddEventHandler(selFileLists[n], ExposureMask, True,
+                       SFexposeList, (XtPointer) n);
+               XtAddEventHandler(selFileLists[n], EnterWindowMask, False,
+                       SFenterList, (XtPointer) n);
+               XtAddEventHandler(selFileLists[n], LeaveWindowMask, False,
+                       SFleaveList, (XtPointer) n);
+               XtAddEventHandler(selFileLists[n], PointerMotionMask, False,
+                       SFmotionList, (XtPointer) n);
+               XtAddEventHandler(selFileLists[n], ButtonPressMask, False,
+                       SFbuttonPressList, (XtPointer) n);
+               XtAddEventHandler(selFileLists[n], ButtonReleaseMask, False,
+                       SFbuttonReleaseList, (XtPointer) n);
+       }
+
+       XtAddEventHandler(selFileField, KeyPressMask, False,
+               SFmodVerifyCallback, (XtPointer) NULL);
+
+       SFapp = XtWidgetToApplicationContext(selFile);
+
+}
+
+/* position widget under the cursor */
+void
+SFpositionWidget(w)
+    Widget w;
+{
+    Arg args[3];
+    Cardinal num_args;
+    Dimension width, height, b_width;
+    int x, y, max_x, max_y;
+    Window root, child;
+    int dummyx, dummyy;
+    unsigned int dummymask;
+    
+    XQueryPointer(XtDisplay(w), XtWindow(w), &root, &child, &x, &y,
+                 &dummyx, &dummyy, &dummymask);
+    num_args = 0;
+    XtSetArg(args[num_args], XtNwidth, &width); num_args++;
+    XtSetArg(args[num_args], XtNheight, &height); num_args++;
+    XtSetArg(args[num_args], XtNborderWidth, &b_width); num_args++;
+    XtGetValues(w, args, num_args);
+
+    width += 2 * b_width;
+    height += 2 * b_width;
+
+    x -= ( (Position) width/2 );
+    if (x < 0) x = 0;
+    if ( x > (max_x = (Position) (XtScreen(w)->width - width)) ) x = max_x;
+
+    y -= ( (Position) height/2 );
+    if (y < 0) y = 0;
+    if ( y > (max_y = (Position) (XtScreen(w)->height - height)) ) y = max_y;
+    
+    num_args = 0;
+    XtSetArg(args[num_args], XtNx, x); num_args++;
+    XtSetArg(args[num_args], XtNy, y); num_args++;
+    XtSetValues(w, args, num_args);
+}
+
+FILE *
+SFopenFile(name, mode, prompt, failed)
+    char *name;
+    char *mode;
+    char *prompt;
+    char *failed;
+{
+    Arg args[1];
+    FILE *fp;
+
+    SFchdir(SFstartDir);
+    if ((fp = fopen(name, mode)) == NULL) {
+       char *buf;
+       if (errno <= sys_nerr) {
+           buf = XtMalloc(strlen(failed) + strlen(sys_errlist[errno]) + 
+                          strlen(prompt) + 2);
+           strcpy(buf, failed);
+           strcat(buf, sys_errlist[errno]);
+           strcat(buf, "\n");
+           strcat(buf, prompt);
+       } else {
+           buf = XtMalloc(strlen(failed) + strlen(prompt) + 2);
+           strcpy(buf, failed);
+           strcat(buf, "\n");
+           strcat(buf, prompt);
+       }
+       XtSetArg(args[0], XtNlabel, buf);
+       XtSetValues(selFilePrompt, args, ONE);
+       XtFree(buf);
+       return NULL;
+    }
+    return fp;
+}
+
+SFtextChanged()
+{
+
+       if ((SFtextBuffer[0] == '/') || (SFtextBuffer[0] == '~')) {
+               (void) strcpy(SFcurrentPath, SFtextBuffer);
+
+               SFtextPos = XawTextGetInsertionPoint(selFileField);
+       } else {
+               (void) strcat(strcpy(SFcurrentPath, SFstartDir), SFtextBuffer);
+
+               SFtextPos = XawTextGetInsertionPoint(selFileField) +
+                       strlen(SFstartDir);
+       }
+
+       if (!SFworkProcAdded) {
+               (void) XtAppAddWorkProc(SFapp, SFworkProc, NULL);
+               SFworkProcAdded = 1;
+       }
+
+       SFupdatePath();
+}
+
+static char *
+SFgetText()
+{
+       return strcpy(XtMalloc((unsigned) (strlen(SFtextBuffer) + 1)),
+               SFtextBuffer);
+}
+
+static
+SFprepareToReturn()
+{
+       SFstatus = SEL_FILE_NULL;
+       XtRemoveGrab(selFile);
+       XtUnmapWidget(selFile);
+       XtRemoveTimeOut(SFdirModTimerId);
+       if (SFchdir(SFstartDir)) {
+               XtAppError(
+                       SFapp,
+                       "XsraSelFile: can't return to current directory"
+               );
+       }
+}
+
+FILE *
+XsraSelFile(toplevel, prompt, ok, cancel, failed,
+           init_path, mode, show_entry, name_return)
+       Widget          toplevel;
+       char            *prompt;
+       char            *ok;
+       char            *cancel;
+       char            *failed;
+       char            *init_path;
+       char            *mode;
+       int             (*show_entry)();
+       char            **name_return;
+{
+       static int      firstTime = 1;
+       Cardinal        i;
+       Arg             arglist[20];
+       XEvent          event;
+       FILE            *fp;
+
+       if (!prompt) {
+               prompt = "Pathname:";
+       }
+
+       if (!ok) {
+               ok = "OK";
+       }
+
+       if (!cancel) {
+               cancel = "Cancel";
+       }
+
+       if (firstTime) {
+               firstTime = 0;
+               SFdisplay = XtDisplay(toplevel);
+               SFcreateWidgets(toplevel, prompt, ok, cancel);
+       } else {
+               i = 0;
+
+               XtSetArg(arglist[i], XtNlabel, prompt);                 i++;
+               XtSetValues(selFilePrompt, arglist, i);
+
+               i = 0;
+               XtSetArg(arglist[i], XtNlabel, ok);                     i++;
+               XtSetValues(selFileOK, arglist, i);
+
+               i = 0;
+               XtSetArg(arglist[i], XtNlabel, cancel);                 i++;
+               XtSetValues(selFileCancel, arglist, i);
+       }
+
+       SFpositionWidget(selFile);
+       XtMapWidget(selFile);
+
+#if defined(SVR4) || defined(SYSV) || defined(USG)
+       if (!getcwd(SFstartDir, MAXPATHLEN)) {
+#else /* defined(SVR4) || defined(SYSV) || defined(USG) */
+       if (!getwd(SFstartDir)) {
+#endif /* defined(SVR4) || defined(SYSV) || defined(USG) */
+
+               XtAppError(SFapp, "XsraSelFile: can't get current directory");
+       }
+       (void) strcat(SFstartDir, "/");
+       (void) strcpy(SFcurrentDir, SFstartDir);
+
+       if (init_path) {
+               if (init_path[0] == '/') {
+                       (void) strcpy(SFcurrentPath, init_path);
+                       if (strncmp(
+                               SFcurrentPath,
+                               SFstartDir,
+                               strlen(SFstartDir)
+                       )) {
+                               SFsetText(SFcurrentPath);
+                       } else {
+                               SFsetText(&(SFcurrentPath[strlen(SFstartDir)]));
+                       }
+               } else {
+                       (void) strcat(strcpy(SFcurrentPath, SFstartDir),
+                               init_path);
+                       SFsetText(&(SFcurrentPath[strlen(SFstartDir)]));
+               }
+       } else {
+               (void) strcpy(SFcurrentPath, SFstartDir);
+       }
+
+       SFfunc = show_entry;
+
+       SFtextChanged();
+
+       XtAddGrab(selFile, True, True);
+
+       SFdirModTimerId = XtAppAddTimeOut(SFapp, (unsigned long) 1000,
+               SFdirModTimer, (XtPointer) NULL);
+
+       while (1) {
+               XtAppNextEvent(SFapp, &event);
+               XtDispatchEvent(&event);
+               switch (SFstatus) {
+               case SEL_FILE_TEXT:
+                       SFstatus = SEL_FILE_NULL;
+                       SFtextChanged();
+                       break;
+               case SEL_FILE_OK:
+                       *name_return = SFgetText();
+                       if (fp = SFopenFile(*name_return, mode,
+                                           prompt, failed)) {
+                               SFprepareToReturn();
+                               return fp;
+                       }
+                       SFstatus = SEL_FILE_NULL;
+                       break;
+               case SEL_FILE_CANCEL:
+                       SFprepareToReturn();
+                       return NULL;
+               case SEL_FILE_NULL:
+                       break;
+               }
+       }
+}
diff --git a/selfile.h b/selfile.h
new file mode 100644 (file)
index 0000000..558175e
--- /dev/null
+++ b/selfile.h
@@ -0,0 +1,149 @@
+/*
+ * File-Selector dialog of GhostView 1.5, incorporated in XBoard by H.G.Muller
+ *
+ * Copyright 1989 Software Research Associates, Inc., Tokyo, Japan
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose and without fee is hereby granted, provided
+ * that the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of Software Research Associates not be used
+ * in advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.  Software Research Associates
+ * makes no representations about the suitability of this software for any
+ * purpose.  It is provided "as is" without express or implied warranty.
+ *
+ * SOFTWARE RESEARCH ASSOCIATES DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
+ * IN NO EVENT SHALL SOFTWARE RESEARCH ASSOCIATES BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Erik M. van der Poel
+ *         Software Research Associates, Inc., Tokyo, Japan
+ *         erik@sra.co.jp
+ */
+
+#include <X11/Intrinsic.h>
+#include <X11/StringDefs.h>
+#include <X11/Xos.h>
+#include <X11/Xaw/Text.h>
+#include <X11/Xaw/AsciiText.h>
+
+#define SEL_FILE_CANCEL                -1
+#define SEL_FILE_OK            0
+#define SEL_FILE_NULL          1
+#define SEL_FILE_TEXT          2
+
+#define SF_DO_SCROLL           1
+#define SF_DO_NOT_SCROLL       0
+
+#define NR 3 /* [HGM] (so far failed) attempt to suppress some of the director listings */
+
+typedef struct {
+       int     statDone;
+       char    *real;
+       char    *shown;
+} SFEntry;
+
+typedef struct {
+       char    *dir;
+       char    *path;
+       SFEntry *entries;
+       int     nEntries;
+       int     vOrigin;
+       int     nChars;
+       int     hOrigin;
+       int     changed;
+       int     beginSelection;
+       int     endSelection;
+       time_t  mtime;
+} SFDir;
+
+extern int SFstatus;
+
+extern char SFcurrentPath[], SFstartDir[], SFcurrentDir[];
+
+extern Widget
+               selFile,
+               selFileCancel,
+               selFileField,
+               selFileForm,
+               selFileHScroll,
+               selFileHScrolls[],
+               selFileLists[],
+               selFileOK,
+               selFilePrompt,
+               selFileVScrolls[];
+
+extern Display *SFdisplay;
+
+extern int SFcharWidth, SFcharHeight, SFcharAscent;
+
+extern SFDir *SFdirs;
+
+extern int SFdirEnd, SFdirPtr;
+
+extern Pixel SFfore, SFback;
+
+extern Atom SFwmDeleteWindow;
+
+extern XSegment SFsegs[], SFcompletionSegs[];
+
+extern XawTextPosition SFtextPos;
+
+extern void
+       SFenterList(),
+       SFleaveList(),
+       SFmotionList(),
+       SFbuttonPressList(),
+       SFbuttonReleaseList();
+
+extern void
+       SFvSliderMovedCallback(),
+       SFvFloatSliderMovedCallback(),
+       SFhSliderMovedCallback(),
+       SFpathSliderMovedCallback(),
+       SFvAreaSelectedCallback(),
+       SFhAreaSelectedCallback(),
+       SFpathAreaSelectedCallback();
+
+extern int SFupperX, SFlowerY, SFupperY;
+
+extern int SFtextX, SFtextYoffset;
+
+extern int SFentryWidth, SFentryHeight;
+
+extern int SFlineToTextH, SFlineToTextV;
+
+extern int SFbesideText, SFaboveAndBelowText;
+
+extern int SFcharsPerEntry;
+
+extern int SFlistSize;
+
+extern int SFcurrentInvert[];
+
+extern int SFworkProcAdded;
+
+extern Boolean SFworkProc();
+
+extern XtAppContext SFapp;
+
+extern int SFpathScrollWidth, SFvScrollHeight, SFhScrollWidth;
+
+extern char SFtextBuffer[];
+
+extern int SFbuttonPressed;
+
+extern int SFcompareEntries();
+
+extern void SFdirModTimer();
+
+extern char SFstatChar();
+
+extern XtIntervalId SFdirModTimerId;
+
+extern int (*SFfunc)();
index 88bc31d..9ddf884 100644 (file)
--- a/xboard.c
+++ b/xboard.c
@@ -4924,45 +4924,14 @@ void FileNamePopUp(label, def, proc, openMode)
 
     fileProc = proc;           /* I can't see a way not */
     fileOpenMode = openMode;   /*   to use globals here */
-
-    i = 0;
-    XtSetArg(args[i], XtNresizable, True); i++;
-    XtSetArg(args[i], XtNwidth, DIALOG_SIZE); i++;
-    XtSetArg(args[i], XtNtitle, XtNewString(_("File name prompt"))); i++;
-    fileNameShell = popup =
-      XtCreatePopupShell("File name prompt", transientShellWidgetClass,
-                        shellWidget, args, i);
-
-    layout =
-      XtCreateManagedWidget(layoutName, formWidgetClass, popup,
-                           layoutArgs, XtNumber(layoutArgs));
-
-    i = 0;
-    XtSetArg(args[i], XtNlabel, label); i++;
-    XtSetArg(args[i], XtNvalue, def); i++;
-    XtSetArg(args[i], XtNborderWidth, 0); i++;
-    dialog = XtCreateManagedWidget("fileName", dialogWidgetClass,
-                                  layout, args, i);
-
-    XawDialogAddButton(dialog, _("ok"), FileNameCallback, (XtPointer) dialog);
-    XawDialogAddButton(dialog, _("cancel"), FileNameCallback,
-                      (XtPointer) dialog);
-
-    XtRealizeWidget(popup);
-    CatchDeleteWindow(popup, "FileNamePopDown");
-
-    XQueryPointer(xDisplay, xBoardWindow, &root, &child,
-                 &x, &y, &win_x, &win_y, &mask);
-
-    XtSetArg(args[0], XtNx, x - 10);
-    XtSetArg(args[1], XtNy, y - 30);
-    XtSetValues(popup, args, 2);
-
-    XtPopup(popup, XtGrabExclusive);
-    filenameUp = True;
-
-    edit = XtNameToWidget(dialog, "*value");
-    XtSetKeyboardFocus(popup, edit);
+    {   // [HGM] use file-selector dialog stolen from Ghostview
+       char *name;
+       int index; // this is not supported yet
+       FILE *f;
+       if(f = XsraSelFile(shellWidget, label, NULL, NULL, "could not open: ",
+           NULL, openMode, NULL, &name))
+               (void) (*fileProc)(f, index=0, name);
+    }
 }
 
 void FileNamePopDown()
diff --git a/xstat.h b/xstat.h
new file mode 100644 (file)
index 0000000..2b4826e
--- /dev/null
+++ b/xstat.h
@@ -0,0 +1,23 @@
+#include <sys/stat.h>
+#if !defined(S_ISDIR) && defined(S_IFDIR)
+#define        S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
+#endif
+#if !defined(S_ISREG) && defined(S_IFREG)
+#define        S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
+#endif
+#if !defined(S_ISSOCK) && defined(S_IFSOCK)
+#define        S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK)
+#endif
+
+#ifndef S_IXUSR
+#define S_IXUSR 0100
+#endif
+#ifndef S_IXGRP
+#define S_IXGRP 0010
+#endif
+#ifndef S_IXOTH
+#define S_IXOTH 0001
+#endif
+
+#define S_ISXXX(m) ((m) & (S_IXUSR | S_IXGRP | S_IXOTH))
+