Let file browser filter on extension
authorH.G. Muller <h.g.muller@hccnet.nl>
Tue, 5 Apr 2011 15:05:39 +0000 (17:05 +0200)
committerArun Persaud <apersaud@lbl.gov>
Thu, 7 Apr 2011 05:21:41 +0000 (22:21 -0700)
A new text field is added in the file-browse dialog, to hold (space-
separated) extensions, on which the filenames are then filtered. This
field is initialized by the caller, depending on what we need the file
for, but can be changed by the user. (Directories are always displayed!)
  Changes in the extension field  become effective after typing a return
in it. Escape typed in the extension field gives focus to the filename
field, and restores the contents. Escape in the filename field cancels
the dialog, return ther OKs it. The extension filter of the browser is
initialized to the extension (if any) of the file currently in the text
edit you are browsing for.
  When re-opening the file browser from the XBoard File menu, and there
is no suggested filename, it will now start with the last succesfully
opened name as suggestion. Calling the browser with a NULL argument
for the suggested filename requests that (unlike calling with an empty
string, which will start it in the current directory). To make this
work smoothly, the filebrowser saves the last used name on entry, so it
can be restored on cancel, so that a cancelled browse session really
erases all memory of it.

filebrowser/dir.c
filebrowser/selfile.c
filebrowser/selfile.h
xboard.c
xoptions.c

index 123d329..820422e 100644 (file)
@@ -128,6 +128,7 @@ SFgetDir(dir)
 #endif /* ndef S_IFLNK */
 
        while (dp = readdir(dirp)) {
+               struct stat statBuf;
                if (i >= alloc) {
                        alloc = 2 * (alloc + 1);
                        result = (SFEntry *) XtRealloc((char *) result,
@@ -143,8 +144,16 @@ SFgetDir(dir)
                }
                result[i].shown = result[i].real;
                if(SFpathFlag) { // [HGM] only show directories
-                       struct stat statBuf;
                        if (stat(str, &statBuf) || SFstatChar(&statBuf) != '/') continue;
+               } else if(SFfilterBuffer[0]) { // [HGM] filter on extension
+                   char *p = SFfilterBuffer, match, *q;
+                   match = !(stat(str, &statBuf) || SFstatChar(&statBuf) != '/');
+                   do {
+                       if(q = strchr(p, ' ')) *q = 0;
+                       if(strstr(str, p)) match++;
+                       if(q) *q = ' ';
+                   } while(q && (p = q+1));
+                   if(!match) continue;
                }
                i++;
        }
index 2e34955..477b022 100644 (file)
@@ -85,12 +85,15 @@ int SFstatus = SEL_FILE_NULL;
 char
        SFstartDir[MAXPATHLEN],
        SFcurrentPath[MAXPATHLEN],
+       SFlastPath[MAXPATHLEN],
        SFcurrentDir[MAXPATHLEN];
 
 Widget
        selFile,
        selFileCancel,
        selFileField,
+       selFileMess,
+       filterField,
        selFileForm,
        selFileHScroll,
        selFileHScrolls[3],
@@ -135,6 +138,8 @@ int SFpathScrollWidth, SFvScrollHeight, SFhScrollWidth;
 
 char SFtextBuffer[MAXPATHLEN];
 
+char SFfilterBuffer[MAXPATHLEN];
+
 XtIntervalId SFdirModTimerId;
 
 int (*SFfunc)();
@@ -161,6 +166,13 @@ SFexposeList(w, n, event, cont)
        SFdrawList((int)(intptr_t)n, SF_DO_NOT_SCROLL);
 }
 
+void
+SFpurge()
+{
+       if(SFdirs) XtFree((XtPointer) SFdirs);
+       SFdirs = NULL; // kludge to throw away all cached info
+}
+
 /* ARGSUSED */
 static void
 SFmodVerifyCallback(w, client_data, event, cont)
@@ -173,11 +185,30 @@ SFmodVerifyCallback(w, client_data, event, cont)
 
        if (
                (XLookupString(&(event->xkey), buf, 2, NULL, NULL) == 1) &&
-               ((*buf) == '\r')
+               ((*buf) == '\r' || *buf == 033)
        ) {
-               SFstatus = SEL_FILE_OK;
+               if(client_data) {
+                   Arg args[10]; char *p;
+                   if(*buf == 033) { // [HGM] esc in filter: restore and give focus to path
+                       XtSetArg(args[0], XtNstring, SFfilterBuffer);
+                       XtSetValues(filterField, args, 1);
+                       XtSetKeyboardFocus(selFileForm, selFileField);
+                       SFstatus = SEL_FILE_TEXT;
+                       return;
+                   } else
+                   if(!SFpathFlag) // [HGM] cr: fetch current extenson filter
+                   {   
+                       XtSetArg(args[0], XtNstring, &p);
+                       XtGetValues(filterField, args, 1);
+                       if(strcmp(SFfilterBuffer, p)) SFpurge();
+                       strncpy(SFfilterBuffer, p, 40);
+                       SFstatus = SEL_FILE_TEXT;
+                   }
+                   return;
+               }
+               SFstatus = (*buf == 033 ? SEL_FILE_CANCEL : SEL_FILE_OK);
        } else {
-               SFstatus = SEL_FILE_TEXT;
+               if(!client_data) SFstatus = SEL_FILE_TEXT;
        }
 }
 
@@ -231,6 +262,11 @@ static XtActionsRec actions[] = {
        {"SelFileDismiss",      SFdismissAction},
 };
 
+void SFsetFocus(Widget w, XtPointer data, XEvent *event, Boolean *b)
+{
+    XtSetKeyboardFocus((Widget) data, w);
+}
+
 static void
 SFcreateWidgets(toplevel, prompt, ok, cancel)
        Widget  toplevel;
@@ -337,7 +373,7 @@ SFcreateWidgets(toplevel, prompt, ok, cancel)
        XtSetArg(arglist[i], XtNborderColor, SFfore);                   i++;
 
        XtSetArg(arglist[i], XtNfromVert, selFilePrompt);               i++;
-       XtSetArg(arglist[i], XtNvertDistance, 10);                      i++;
+       XtSetArg(arglist[i], XtNvertDistance, 5);                       i++;
        XtSetArg(arglist[i], XtNresizable, True);                       i++;
        XtSetArg(arglist[i], XtNtop, XtChainTop);                       i++;
        XtSetArg(arglist[i], XtNbottom, XtChainTop);                    i++;
@@ -354,15 +390,51 @@ SFcreateWidgets(toplevel, prompt, ok, cancel)
 
        XtOverrideTranslations(selFileField,
                XtParseTranslationTable(oneLineTextEditTranslations));
-       XtSetKeyboardFocus(selFileForm, selFileField);
+       XtAddEventHandler(selFileField, ButtonPressMask, False, SFsetFocus, (XtPointer) selFileForm);
+
+       i = 0;
+       XtSetArg(arglist[i], XtNlabel, "Filter on extensions:");        i++;
+       XtSetArg(arglist[i], XtNvertDistance, 5);                       i++;
+       XtSetArg(arglist[i], XtNfromVert, selFileField);                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++;
+       selFileMess = XtCreateManagedWidget("selFileMess",
+               labelWidgetClass, selFileForm, arglist, i);
+
+       i = 0;
+       XtSetArg(arglist[i], XtNwidth, NR * listWidth + (NR - 1) * listSpacing + 4);
+                                                                       i++;
+       XtSetArg(arglist[i], XtNborderColor, SFfore);                   i++;
+       XtSetArg(arglist[i], XtNvertDistance, 5);                       i++;
+       XtSetArg(arglist[i], XtNfromVert, selFileMess);                 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], 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, False);               i++;
+       filterField = XtCreateManagedWidget("filterField",
+               asciiTextWidgetClass, selFileForm, arglist, i);
+
+       XtOverrideTranslations(filterField,
+               XtParseTranslationTable(oneLineTextEditTranslations));
+       XtAddEventHandler(filterField, ButtonPressMask, False, SFsetFocus, (XtPointer) selFileForm);
 
        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], XtNfromVert, filterField);                 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++;
@@ -498,6 +570,7 @@ SFcreateWidgets(toplevel, prompt, ok, cancel)
 
        XDefineCursor(SFdisplay, XtWindow(selFileForm), xtermCursor);
        XDefineCursor(SFdisplay, XtWindow(selFileField), xtermCursor);
+       XDefineCursor(SFdisplay, XtWindow(filterField), xtermCursor);
 
        for (n = 0; n < NR; n++) {
                XDefineCursor(SFdisplay, XtWindow(selFileLists[n]),
@@ -523,6 +596,9 @@ SFcreateWidgets(toplevel, prompt, ok, cancel)
 
        XtAddEventHandler(selFileField, KeyPressMask, False,
                SFmodVerifyCallback, (XtPointer) NULL);
+       XtAddEventHandler(filterField, KeyReleaseMask, False,
+               SFmodVerifyCallback, (XtPointer) 1);
+       XtSetKeyboardFocus(selFileForm, selFileField);
 
        SFapp = XtWidgetToApplicationContext(selFile);
 
@@ -649,13 +725,14 @@ SFprepareToReturn()
 
 FILE *
 XsraSelFile(toplevel, prompt, ok, cancel, failed,
-           init_path, mode, show_entry, name_return)
+           init_path, filter, mode, show_entry, name_return)
        Widget          toplevel;
        char            *prompt;
        char            *ok;
        char            *cancel;
        char            *failed;
        char            *init_path;
+       char            *filter;
        char            *mode;
        int             (*show_entry)();
        char            **name_return;
@@ -678,10 +755,9 @@ XsraSelFile(toplevel, prompt, ok, cancel, failed,
                cancel = "Cancel";
        }
 
-       if(SFpathFlag != (mode && mode[0] == 'p')) { // [HGM] ignore everything that is not a directory
-               if(SFdirs) XtFree(SFdirs);
-               SFdirs = NULL; // kludge to throw away all cached info
-               SFpathFlag = !SFpathFlag;
+       if(SFpathFlag != (mode && mode[0] == 'p') || strcmp(SFfilterBuffer, filter)) {
+               SFpurge();
+               SFpathFlag = (mode && mode[0] == 'p'); // [HGM] ignore everything that is not a directory
        }
 
        if (firstTime) {
@@ -703,6 +779,13 @@ XsraSelFile(toplevel, prompt, ok, cancel, failed,
                XtSetValues(selFileCancel, arglist, i);
        }
 
+       i = 0;
+       XtSetArg(arglist[i], XtNstring, filter);                        i++;
+       XtSetValues(filterField, arglist, i);
+
+       safeStrCpy(SFfilterBuffer, filter, MAXPATHLEN);
+       safeStrCpy(SFlastPath, SFtextBuffer, MAXPATHLEN); // remember for cancel
+
        SFpositionWidget(selFile);
        XtMapWidget(selFile);
 
@@ -770,6 +853,7 @@ XsraSelFile(toplevel, prompt, ok, cancel, failed,
                        break;
                case SEL_FILE_CANCEL:
                        SFprepareToReturn();
+                       SFsetText(SFlastPath);
                        return NULL;
                case SEL_FILE_NULL:
                        break;
index 9b905cf..a9a3af4 100644 (file)
@@ -70,6 +70,7 @@ extern Widget
                selFile,
                selFileCancel,
                selFileField,
+               filterField,
                selFileForm,
                selFileHScroll,
                selFileHScrolls[],
@@ -136,6 +137,8 @@ extern int SFpathScrollWidth, SFvScrollHeight, SFhScrollWidth;
 
 extern char SFtextBuffer[];
 
+extern char SFfilterBuffer[];
+
 extern int SFbuttonPressed;
 
 extern int SFcompareEntries();
index 3771130..cba9ca2 100644 (file)
--- a/xboard.c
+++ b/xboard.c
@@ -238,7 +238,7 @@ typedef struct {
 
 int main P((int argc, char **argv));
 FILE * XsraSelFile P((Widget w, char *prompt, char *ok, char *cancel, char *failed,
-               char *init_path, char *mode, int (*show_entry)(), char **name_return));
+               char *init_path, char *filter, char *mode, int (*show_entry)(), char **name_return));
 RETSIGTYPE CmailSigHandler P((int sig));
 RETSIGTYPE IntSigHandler P((int sig));
 RETSIGTYPE TermSizeSigHandler P((int sig));
@@ -287,7 +287,7 @@ void CommentCallback P((Widget w, XtPointer client_data,
                        XtPointer call_data));
 void ICSInputBoxPopUp P((void));
 void ICSInputBoxPopDown P((void));
-void FileNamePopUp P((char *label, char *def,
+void FileNamePopUp P((char *label, char *def, char *filter,
                      FileProc proc, char *openMode));
 void FileNamePopDown P((void));
 void FileNameCallback P((Widget w, XtPointer client_data,
@@ -5146,9 +5146,10 @@ void CommentPopDown()
     commentUp = False;
 }
 
-void FileNamePopUp(label, def, proc, openMode)
+void FileNamePopUp(label, def, filter, proc, openMode)
      char *label;
      char *def;
+     char *filter;
      FileProc proc;
      char *openMode;
 {
@@ -5159,7 +5160,7 @@ void FileNamePopUp(label, def, proc, openMode)
        int index; // this is not supported yet
        FILE *f;
        if(f = XsraSelFile(shellWidget, label, NULL, NULL, "could not open: ",
-                          def, openMode, NULL, &name))
+                          (def[0] ? def : NULL), filter, openMode, NULL, &name))
          (void) (*fileProc)(f, index=0, name);
     }
 }
@@ -5623,7 +5624,7 @@ void LoadGameProc(w, event, prms, nprms)
     if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
        Reset(FALSE, TRUE);
     }
-    FileNamePopUp(_("Load game file name?"), "", LoadGamePopUp, "rb");
+    FileNamePopUp(_("Load game file name?"), "", ".pgn .game", LoadGamePopUp, "rb");
 }
 
 void LoadNextGameProc(w, event, prms, nprms)
@@ -5689,7 +5690,7 @@ void LoadPositionProc(w, event, prms, nprms)
     if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
        Reset(FALSE, TRUE);
     }
-    FileNamePopUp(_("Load position file name?"), "", LoadPosition, "rb");
+    FileNamePopUp(_("Load position file name?"), "", ".fen .epd .pos", LoadPosition, "rb");
 }
 
 void SaveGameProc(w, event, prms, nprms)
@@ -5700,6 +5701,7 @@ void SaveGameProc(w, event, prms, nprms)
 {
     FileNamePopUp(_("Save game file name?"),
                  DefaultFileName(appData.oldSaveStyle ? "game" : "pgn"),
+                 appData.oldSaveStyle ? ".game" : ".pgn",
                  SaveGame, "a");
 }
 
@@ -5711,6 +5713,7 @@ void SavePositionProc(w, event, prms, nprms)
 {
     FileNamePopUp(_("Save position file name?"),
                  DefaultFileName(appData.oldSaveStyle ? "pos" : "fen"),
+                 appData.oldSaveStyle ? ".pos" : ".fen",
                  SavePosition, "a");
 }
 
@@ -6061,7 +6064,7 @@ void AnalyzeFileProc(w, event, prms, nprms)
       ShowThinkingProc(w,event,prms,nprms);
 #endif
     AnalyzeFileEvent();
-    FileNamePopUp(_("File to analyze"), "", LoadGamePopUp, "rb");
+    FileNamePopUp(_("File to analyze"), "", ".pgn .game", LoadGamePopUp, "rb");
     AnalysisPeriodicEvent(1);
 }
 
index cb0be58..e66fed0 100644 (file)
@@ -82,7 +82,7 @@ extern char *getenv();
 
 extern void SendToProgram P((char *message, ChessProgramState *cps));
 FILE * XsraSelFile P((Widget w, char *prompt, char *ok, char *cancel, char *failed,
-               char *init_path, char *mode, int (*show_entry)(), char **name_return));
+               char *init_path, char *filter, char *mode, int (*show_entry)(), char **name_return));
 
 extern Widget formWidget, shellWidget, boardWidget, menuBarWidget;
 extern Display *xDisplay;
@@ -643,7 +643,11 @@ void SpinCallback(w, client_data, call_data)
     XtGetValues(currentOption[data].handle, args, 1);
     sscanf(val, "%d", &j);
     if (strcmp(name, "browse") == 0) {
-       if(XsraSelFile(shells[0], currentOption[data].name, NULL, NULL, "", "", 
+       char *q, *r;
+       XtSetArg(args[0], XtNstring, &q);
+       XtGetValues(currentOption[data].handle, args, 1);
+       for(r = ""; *q; q++) if(*q == '.') r = q; else if(*q == '/') r = ""; // last dot after last slash
+       if(XsraSelFile(shells[0], currentOption[data].name, NULL, NULL, "", "", r,
                                  currentOption[data].type == PathName ? "p" : "f", NULL, &p)) {
                int len = strlen(p);
                if(len && p[len-1] == '/') p[len-1] = NULLCHAR;