Add history to some dialog boxes
authorAlexey Voinov <voins@altlinux.ru>
Tue, 14 Aug 2007 11:15:37 +0000 (14 15:15 +0400)
committerCarlos R. Mafra <crmafra@gmail.com>
Thu, 3 Sep 2009 13:44:11 +0000 (3 15:44 +0200)
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 <voins@altlinux.ru>

WINGs/WINGs/WINGs.h
WINGs/wtextfield.c
src/WindowMaker.h
src/defaults.c
src/dialog.c
src/dialog.h
src/misc.c

index 503968c..ba3297e 100644 (file)
@@ -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);
index 70b3efe..e353ee6 100644 (file)
@@ -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);
index d295b4b..90f0eeb 100644 (file)
@@ -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];
index 8fdb399..61756b0 100644 (file)
@@ -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
index 2f3f793..f08fdf4 100644 (file)
@@ -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;
index 1c4af3b..61f842d 100644 (file)
@@ -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,
index 2cf4af7..60a62ee 100644 (file)
@@ -32,6 +32,7 @@
 #include <pwd.h>
 #include <math.h>
 #include <time.h>
+#include <libintl.h>
 
 #include <WINGs/WUtil.h>
 #include <wraster.h>
@@ -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;