From: H.G. Muller Date: Tue, 5 Apr 2011 15:05:39 +0000 (+0200) Subject: Let file browser filter on extension X-Git-Url: http://winboard.nl/cgi-bin?a=commitdiff_plain;h=8c9c9b75176c6135bd37d190e20b6e1818ff108f;p=xboard.git Let file browser filter on extension 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. --- diff --git a/filebrowser/dir.c b/filebrowser/dir.c index 123d329..820422e 100644 --- a/filebrowser/dir.c +++ b/filebrowser/dir.c @@ -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++; } diff --git a/filebrowser/selfile.c b/filebrowser/selfile.c index 2e34955..477b022 100644 --- a/filebrowser/selfile.c +++ b/filebrowser/selfile.c @@ -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; diff --git a/filebrowser/selfile.h b/filebrowser/selfile.h index 9b905cf..a9a3af4 100644 --- a/filebrowser/selfile.h +++ b/filebrowser/selfile.h @@ -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(); diff --git a/xboard.c b/xboard.c index 3771130..cba9ca2 100644 --- 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); } diff --git a/xoptions.c b/xoptions.c index cb0be58..e66fed0 100644 --- a/xoptions.c +++ b/xoptions.c @@ -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;