From 05720d97076ffc1569e50d904b998ec99c3d3d4e Mon Sep 17 00:00:00 2001 From: Alexey Voinov Date: Tue, 14 Aug 2007 15:15:37 +0400 Subject: [PATCH 1/1] Add history to some dialog boxes This patch adds history to some dialog boxes. To use it replace %a with %A in menu file (like in "Run..." menu item). You can specify third parameter to %A to use different histories for differen dialogs. All history files is kept in ~/GNUstep/.AppInfo/WindowMaker/ and the number of history lines controlled by DialogHistoryLines parameter (one for all dialogs) defaults to 500 lines. It also adds Tab completion. Origin: ALT/Sisyphus Linux, Alexey Voinov --- WINGs/WINGs/WINGs.h | 2 + WINGs/wtextfield.c | 7 ++ src/WindowMaker.h | 1 + src/defaults.c | 4 +- src/dialog.c | 302 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/dialog.h | 1 + src/misc.c | 134 +++++++++-------------- 7 files changed, 367 insertions(+), 84 deletions(-) diff --git a/WINGs/WINGs/WINGs.h b/WINGs/WINGs/WINGs.h index 503968c8..ba3297ef 100644 --- a/WINGs/WINGs/WINGs.h +++ b/WINGs/WINGs/WINGs.h @@ -1125,6 +1125,8 @@ void WMSelectTextFieldRange(WMTextField *tPtr, WMRange range); void WMSetTextFieldCursorPosition(WMTextField *tPtr, unsigned int position); +unsigned WMGetTextFieldCursorPosition(WMTextField *tPtr); + void WMSetTextFieldNextTextField(WMTextField *tPtr, WMTextField *next); void WMSetTextFieldPrevTextField(WMTextField *tPtr, WMTextField *prev); diff --git a/WINGs/wtextfield.c b/WINGs/wtextfield.c index 70b3efe0..e353ee6c 100644 --- a/WINGs/wtextfield.c +++ b/WINGs/wtextfield.c @@ -578,6 +578,13 @@ void WMSetTextFieldCursorPosition(WMTextField * tPtr, unsigned int position) } } +unsigned WMGetTextFieldCursorPosition(WMTextField *tPtr) +{ + CHECK_CLASS(tPtr, WC_TextField); + + return tPtr->cursorPosition; +} + void WMSetTextFieldNextTextField(WMTextField * tPtr, WMTextField * next) { CHECK_CLASS(tPtr, WC_TextField); diff --git a/src/WindowMaker.h b/src/WindowMaker.h index d295b4bf..90f0eebe 100644 --- a/src/WindowMaker.h +++ b/src/WindowMaker.h @@ -472,6 +472,7 @@ typedef struct WPreferences { unsigned int workspace_border_size; /* Size in pixels of the workspace border */ char workspace_border_position; /* Where to leave a workspace border */ char single_click; /* single click to lauch applications */ + int history_lines; /* history of "Run..." dialog */ RImage *swtileImage; RImage *swbackImage[9]; diff --git a/src/defaults.c b/src/defaults.c index 8fdb3990..61756b0e 100644 --- a/src/defaults.c +++ b/src/defaults.c @@ -692,7 +692,9 @@ WDefaultEntry optionList[] = { {"TextCursor", "(builtin, xterm)", (void *)WCUR_TEXT, NULL, getCursor, setCursor}, {"SelectCursor", "(builtin, cross)", (void *)WCUR_SELECT, - NULL, getCursor, setCursor} + NULL, getCursor, setCursor}, + {"DialogHistoryLines", "500", NULL, + &wPreferences.history_lines, getInt, NULL} }; #if 0 diff --git a/src/dialog.c b/src/dialog.c index 2f3f7939..f08fdf4e 100644 --- a/src/dialog.c +++ b/src/dialog.c @@ -158,6 +158,308 @@ int wExitDialog(WScreen * scr, char *title, char *message, char *defBtn, char *a return result; } +typedef struct _WMInputPanelWithHistory { + WMInputPanel *panel; + WMArray *history; + int histpos; + char *prefix; + char *suffix; + char *rest; + WMArray *variants; + int varpos; +} WMInputPanelWithHistory; + +static char *HistoryFileName(char *name) +{ + char *filename = NULL; + + filename = wstrdup(wusergnusteppath()); + filename = wstrappend(filename, "/.AppInfo/WindowMaker/History"); + if (name && strlen(name)) { + filename = wstrappend(filename, "."); + filename = wstrappend(filename, name); + } + return filename; +} + +static int matchString(void *str1, void *str2) +{ + return (strcmp((char *)str1, (char *)str2) == 0 ? 1 : 0); +} + +static WMArray *LoadHistory(char *filename, int max) +{ + WMPropList *plhistory; + WMPropList *plitem; + WMArray *history; + int i, num; + + history = WMCreateArrayWithDestructor(1, wfree); + WMAddToArray(history, wstrdup("")); + + plhistory = WMReadPropListFromFile((char *)filename); + + if (plhistory && WMIsPLArray(plhistory)) { + num = WMGetPropListItemCount(plhistory); + if (num > max) + num = max; + + for (i = 0; i < num; ++i) { + plitem = WMGetFromPLArray(plhistory, i); + if (WMIsPLString(plitem) && WMFindInArray(history, matchString, + WMGetFromPLString(plitem)) == WANotFound) + WMAddToArray(history, WMGetFromPLString(plitem)); + } + } + + return history; +} + +static void SaveHistory(WMArray * history, char *filename) +{ + int i; + WMPropList *plhistory; + + plhistory = WMCreatePLArray(NULL); + + for (i = 0; i < WMGetArrayItemCount(history); ++i) + WMAddToPLArray(plhistory, WMCreatePLString(WMGetFromArray(history, i))); + + WMWritePropListToFile(plhistory, (char *)filename, True); + WMReleasePropList(plhistory); +} + +static int strmatch(const char *str1, const char *str2) +{ + return !strcmp(str1, str2); +} + +static int pstrcmp(const char **str1, const char **str2) +{ + return strcmp(*str1, *str2); +} + +static void +ScanFiles(const char *dir, const char *prefix, unsigned acceptmask, unsigned declinemask, WMArray * result) +{ + int prefixlen; + DIR *d; + struct dirent *de; + struct stat sb; + char *fullfilename, *suffix; + + prefixlen = strlen(prefix); + if ((d = opendir(dir)) != NULL) { + while ((de = readdir(d)) != NULL) { + if (strlen(de->d_name) > prefixlen && + !strncmp(prefix, de->d_name, prefixlen) && + strcmp(de->d_name, ".") != 0 && strcmp(de->d_name, "..")) { + fullfilename = wstrconcat((char *)dir, "/"); + fullfilename = wstrappend(fullfilename, de->d_name); + + if (stat(fullfilename, &sb) == 0 && + (sb.st_mode & acceptmask) && + !(sb.st_mode & declinemask) && + WMFindInArray(result, (WMMatchDataProc *) strmatch, + de->d_name + prefixlen) == WANotFound) { + suffix = wstrdup(de->d_name + prefixlen); + WMAddToArray(result, suffix); + } + wfree(fullfilename); + } + } + closedir(d); + } +} + +static WMArray *GenerateVariants(const char *complete) +{ + Bool firstWord = True; + WMArray *variants = NULL; + char *pos = NULL, *path = NULL, *tmp = NULL, *dir = NULL, *prefix = NULL; + + variants = WMCreateArrayWithDestructor(0, wfree); + + while (*complete == ' ') + ++complete; + + if ((pos = strrchr(complete, ' ')) != NULL) { + complete = pos + 1; + firstWord = False; + } + + if ((pos = strrchr(complete, '/')) != NULL) { + tmp = wstrndup((char *)complete, pos - complete + 1); + if (*tmp == '~' && *(tmp + 1) == '/' && getenv("HOME")) { + dir = wstrdup(getenv("HOME")); + dir = wstrappend(dir, tmp + 1); + wfree(tmp); + } else { + dir = tmp; + } + prefix = wstrdup(pos + 1); + ScanFiles(dir, prefix, (unsigned)-1, 0, variants); + wfree(dir); + wfree(prefix); + } else if (*complete == '~') { + WMAddToArray(variants, wstrdup("/")); + } else if (firstWord) { + path = getenv("PATH"); + while (path) { + pos = strchr(path, ':'); + if (pos) { + tmp = wstrndup(path, pos - path); + path = pos + 1; + } else if (*path != '\0') { + tmp = wstrdup(path); + path = NULL; + } else + break; + ScanFiles(tmp, complete, S_IXOTH | S_IXGRP | S_IXUSR, S_IFDIR, variants); + wfree(tmp); + } + } + + WMSortArray(variants, (WMCompareDataProc *) pstrcmp); + return variants; +} + +static void handleHistoryKeyPress(XEvent * event, void *clientData) +{ + char *text; + unsigned pos; + WMInputPanelWithHistory *p = (WMInputPanelWithHistory *) clientData; + KeySym ksym; + + ksym = XLookupKeysym(&event->xkey, 0); + + switch (ksym) { + case XK_Up: + if (p->histpos < WMGetArrayItemCount(p->history) - 1) { + if (p->histpos == 0) + wfree(WMReplaceInArray(p->history, 0, WMGetTextFieldText(p->panel->text))); + p->histpos++; + WMSetTextFieldText(p->panel->text, WMGetFromArray(p->history, p->histpos)); + } + break; + case XK_Down: + if (p->histpos > 0) { + p->histpos--; + WMSetTextFieldText(p->panel->text, WMGetFromArray(p->history, p->histpos)); + } + break; + case XK_Tab: + if (!p->variants) { + text = WMGetTextFieldText(p->panel->text); + pos = WMGetTextFieldCursorPosition(p->panel->text); + p->prefix = wstrndup(text, pos); + p->suffix = wstrdup(text + pos); + wfree(text); + p->variants = GenerateVariants(p->prefix); + p->varpos = 0; + if (!p->variants) { + wfree(p->prefix); + wfree(p->suffix); + p->prefix = NULL; + p->suffix = NULL; + } + } + if (p->variants && p->prefix && p->suffix) { + p->varpos++; + if (p->varpos > WMGetArrayItemCount(p->variants)) + p->varpos = 0; + if (p->varpos > 0) + text = wstrconcat(p->prefix, WMGetFromArray(p->variants, p->varpos - 1)); + else + text = wstrdup(p->prefix); + pos = strlen(text); + text = wstrappend(text, p->suffix); + WMSetTextFieldText(p->panel->text, text); + WMSetTextFieldCursorPosition(p->panel->text, pos); + wfree(text); + } + break; + } + if (ksym != XK_Tab) { + if (p->prefix) { + wfree(p->prefix); + p->prefix = NULL; + } + if (p->suffix) { + wfree(p->suffix); + p->suffix = NULL; + } + if (p->variants) { + WMFreeArray(p->variants); + p->variants = NULL; + } + } +} + +int wAdvancedInputDialog(WScreen * scr, char *title, char *message, char *name, char **text) +{ + WWindow *wwin; + Window parent; + char *result; + WMPoint center; + WMInputPanelWithHistory *p; + char *filename; + + filename = HistoryFileName(name); + p = wmalloc(sizeof(WMInputPanelWithHistory)); + p->panel = WMCreateInputPanel(scr->wmscreen, NULL, title, message, *text, _("OK"), _("Cancel")); + p->history = LoadHistory(filename, wPreferences.history_lines); + p->histpos = 0; + p->prefix = NULL; + p->suffix = NULL; + p->rest = NULL; + p->variants = NULL; + p->varpos = 0; + WMCreateEventHandler(WMWidgetView(p->panel->text), KeyPressMask, handleHistoryKeyPress, p); + + parent = XCreateSimpleWindow(dpy, scr->root_win, 0, 0, 320, 160, 0, 0, 0); + XSelectInput(dpy, parent, KeyPressMask | KeyReleaseMask); + + XReparentWindow(dpy, WMWidgetXID(p->panel->win), parent, 0, 0); + + center = getCenter(scr, 320, 160); + wwin = wManageInternalWindow(scr, parent, None, NULL, center.x, center.y, 320, 160); + + wwin->client_leader = WMWidgetXID(p->panel->win); + + WMMapWidget(p->panel->win); + + wWindowMap(wwin); + + WMRunModalLoop(WMWidgetScreen(p->panel->win), WMWidgetView(p->panel->win)); + + if (p->panel->result == WAPRDefault) { + result = WMGetTextFieldText(p->panel->text); + wfree(WMReplaceInArray(p->history, 0, wstrdup(result))); + SaveHistory(p->history, filename); + } else + result = NULL; + + wUnmanageWindow(wwin, False, False); + + WMDestroyInputPanel(p->panel); + WMFreeArray(p->history); + wfree(p); + wfree(filename); + + XDestroyWindow(dpy, parent); + + if (result == NULL) + return False; + else { + if (*text) + wfree(*text); + *text = result; + + return True; + } +} + int wInputDialog(WScreen * scr, char *title, char *message, char **text) { WWindow *wwin; diff --git a/src/dialog.h b/src/dialog.h index 1c4af3b1..61f842dd 100644 --- a/src/dialog.h +++ b/src/dialog.h @@ -33,6 +33,7 @@ enum { int wMessageDialog(WScreen *scr, char *title, char *message, char *defBtn, char *altBtn, char *othBtn); +int wAdvancedInputDialog(WScreen *scr, char *title, char *message, char *name, char **text); int wInputDialog(WScreen *scr, char *title, char *message, char **text); int wExitDialog(WScreen *scr, char *title, char *message, char *defBtn, diff --git a/src/misc.c b/src/misc.c index 2cf4af7b..60a62ee8 100644 --- a/src/misc.c +++ b/src/misc.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include @@ -563,93 +564,59 @@ static char *getselection(WScreen * scr) return tmp; } -static char *getuserinput(WScreen * scr, char *line, int *ptr) +static char* +parseuserinputpart(char *line, int *ptr, char *endchars) { - char *ret; - char *title; - char *prompt; - int j, state; - int begin = 0; -#define BUFSIZE 512 - char tbuffer[BUFSIZE], pbuffer[BUFSIZE]; - - title = _("Program Arguments"); - prompt = _("Enter command arguments:"); - ret = NULL; - -#define _STARTING 0 -#define _TITLE 1 -#define _PROMPT 2 -#define _DONE 3 - - state = _STARTING; - j = 0; - for (; line[*ptr] != 0 && state != _DONE; (*ptr)++) { - switch (state) { - case _STARTING: - if (line[*ptr] == '(') { - state = _TITLE; - begin = *ptr + 1; - } else { - state = _DONE; - } - break; - - case _TITLE: - if (j <= 0 && line[*ptr] == ',') { - - j = 0; - if (*ptr > begin) { - strncpy(tbuffer, &line[begin], WMIN(*ptr - begin, BUFSIZE)); - tbuffer[WMIN(*ptr - begin, BUFSIZE)] = 0; - title = (char *)tbuffer; - } - begin = *ptr + 1; - state = _PROMPT; - - } else if (j <= 0 && line[*ptr] == ')') { - - if (*ptr > begin) { - strncpy(tbuffer, &line[begin], WMIN(*ptr - begin, BUFSIZE)); - tbuffer[WMIN(*ptr - begin, BUFSIZE)] = 0; - title = (char *)tbuffer; - } - state = _DONE; - - } else if (line[*ptr] == '(') { - j++; - } else if (line[*ptr] == ')') { - j--; - } - - break; - - case _PROMPT: - if (line[*ptr] == ')' && j == 0) { - - if (*ptr - begin > 1) { - strncpy(pbuffer, &line[begin], WMIN(*ptr - begin, BUFSIZE)); - pbuffer[WMIN(*ptr - begin, BUFSIZE)] = 0; - prompt = (char *)pbuffer; - } - state = _DONE; - } else if (line[*ptr] == '(') - j++; - else if (line[*ptr] == ')') - j--; + int depth = 0, begin; + char *value = NULL; + begin = ++*ptr; + + while(line[*ptr] != '\0') { + if(line[*ptr] == '(') { + ++depth; + } else if(depth > 0 && line[*ptr] == ')') { + --depth; + } else if(depth == 0 && strchr(endchars, line[*ptr]) != NULL) { + value = wmalloc(*ptr - begin + 1); + strncpy(value, line + begin, *ptr - begin); + value[*ptr - begin] = '\0'; break; } + ++*ptr; } - (*ptr)--; -#undef _STARTING -#undef _TITLE -#undef _PROMPT -#undef _DONE - if (!wInputDialog(scr, title, prompt, &ret)) - return NULL; - else - return ret; + return value; +} + +static char* +getuserinput(WScreen *scr, char *line, int *ptr, Bool advanced) +{ + char *ret = NULL, *title = NULL, *prompt = NULL, *name = NULL; + int rv; + + if(line[*ptr] == '(') + title = parseuserinputpart(line, ptr, ",)"); + if(title != NULL && line[*ptr] == ',') + prompt = parseuserinputpart(line, ptr, ",)"); + if(prompt != NULL && line[*ptr] == ',') + name = parseuserinputpart(line, ptr, ")"); + + if(advanced) + rv = wAdvancedInputDialog(scr, + title ? gettext(title):_("Program Arguments"), + prompt ? gettext(prompt):_("Enter command arguments:"), + name, &ret); + else + rv = wInputDialog(scr, + title ? gettext(title):_("Program Arguments"), + prompt ? gettext(prompt):_("Enter command arguments:"), + &ret); + + if(title) wfree(title); + if(prompt) wfree(prompt); + if(name) wfree(name); + + return rv ? ret : NULL; } #define S_NORMAL 0 @@ -765,8 +732,9 @@ char *ExpandOptions(WScreen * scr, char *cmdline) break; case 'a': + case 'A': ptr++; - user_input = getuserinput(scr, cmdline, &ptr); + user_input = getuserinput(scr, cmdline, &ptr, cmdline[ptr-1] == 'A'); if (user_input) { slen = strlen(user_input); olen += slen; -- 2.11.4.GIT