Original: WindowMaker-0.80.2-cvs-alt-adialog.patch From: Courtesy of 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/. Number of histroy lines controlled by DialogHistoryLines parameter (one for all dialogs). Default is to keep 500 lines. Also commands and directories completion added. Press Tab to complete something. You need to apply WindowMaker-0.92.0-textfield.diff before applying this patch. --- ./src/misc.c.vns 2005-05-18 18:08:11 +0400 +++ ./src/misc.c 2005-05-18 18:11:20 +0400 @@ -607,96 +607,59 @@ static char* -getuserinput(WScreen *scr, char *line, int *ptr) +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; + 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; + } - case _PROMPT: - if (line[*ptr]==')' && j==0) { + return value; +} - 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--; - break; - } - } - (*ptr)--; -#undef _STARTING -#undef _TITLE -#undef _PROMPT -#undef _DONE +static char* +getuserinput(WScreen *scr, char *line, int *ptr, Bool advanced) +{ + char *ret = NULL, *title = NULL, *prompt = NULL, *name = NULL; + int rv; - if (!wInputDialog(scr, title, prompt, &ret)) - return NULL; + 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 - return ret; -} + 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 #define S_ESCAPE 1 @@ -814,8 +777,9 @@ 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; --- ./src/dialog.c.vns 2005-05-18 18:08:11 +0400 +++ ./src/dialog.c 2005-05-18 18:08:11 +0400 @@ -178,6 +178,328 @@ 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, False); + 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) --- ./src/dialog.h.vns 2004-10-12 21:54:37 +0400 +++ ./src/dialog.h 2005-05-18 18:11:54 +0400 @@ -33,6 +33,7 @@ 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, --- ./src/defaults.c.vns 2005-05-18 18:08:11 +0400 +++ ./src/defaults.c 2005-05-18 18:08:11 +0400 @@ -863,6 +863,9 @@ }, {"SelectCursor", "(builtin, cross)", (void*)WCUR_SELECT, NULL, getCursor, setCursor + }, + {"DialogHistoryLines", "500", NULL, + &wPreferences.history_lines, getInt, NULL } }; --- ./src/WindowMaker.h.00 2008-06-06 19:45:03.000000000 +0200 +++ ./src/WindowMaker.h 2008-06-06 19:45:18.000000000 +0200 @@ -475,6 +475,8 @@ RImage *swtileImage; RImage *swbackImage[9]; + int history_lines; + struct { unsigned int nodock:1; /* don't display the dock */ unsigned int noclip:1; /* don't display the clip */