* Alexander Polakov <polac...@gmail.com> [120120 01:11]:
> Sometimes I want to type something like "xterm -e top" in "exec" menu,
> and feel the need for tab completion.

And now a bigger thing on top of that. One can tab-complete command with
the first menu entry, once ready, just hit tab again and start typing
file path. It uses glob(3), so asterisks are allowed.

I think diff needs more work, posting now to get comments and check if
there's any interest in this.

Index: calmwm.h
===================================================================
RCS file: /cvs/xenocara/app/cwm/calmwm.h,v
retrieving revision 1.142
diff -u -p -r1.142 calmwm.h
--- calmwm.h    13 Sep 2011 08:41:57 -0000      1.142
+++ calmwm.h    22 Jan 2012 10:26:55 -0000
@@ -257,7 +257,7 @@ TAILQ_HEAD(cmd_q, cmd);
 struct menu {
        TAILQ_ENTRY(menu)        entry;
        TAILQ_ENTRY(menu)        resultentry;
-#define MENU_MAXENTRY           50
+#define MENU_MAXENTRY           200
        char                     text[MENU_MAXENTRY + 1];
        char                     print[MENU_MAXENTRY + 1];
        void                    *ctx;
@@ -354,6 +354,8 @@ void                         search_match_exec(struct 
menu_q 
                             char *);
 void                    search_match_text(struct menu_q *, struct menu_q *,
                             char *);
+void                    search_match_path(struct menu_q *, struct menu_q *,
+                            char *);
 void                    search_print_client(struct menu *, int);
 
 XineramaScreenInfo     *screen_find_xinerama(struct screen_ctx *, int, int);
@@ -410,7 +412,7 @@ void                         mousefunc_window_raise(struct 
cl
 void                    mousefunc_window_resize(struct client_ctx *, void *);
 
 struct menu            *menu_filter(struct screen_ctx *, struct menu_q *,
-                            char *, char *, int,
+                            char *, char *, int, int,
                             void (*)(struct menu_q *, struct menu_q *, char *),
                             void (*)(struct menu *, int));
 void                    menu_init(struct screen_ctx *);
Index: group.c
===================================================================
RCS file: /cvs/xenocara/app/cwm/group.c,v
retrieving revision 1.55
diff -u -p -r1.55 group.c
--- group.c     29 Dec 2011 20:48:38 -0000      1.55
+++ group.c     22 Jan 2012 10:26:55 -0000
@@ -395,7 +395,7 @@ group_menu(XButtonEvent *e)
        if (TAILQ_EMPTY(&menuq))
                return;
 
-       mi = menu_filter(sc, &menuq, NULL, NULL, 0, NULL, NULL);
+       mi = menu_filter(sc, &menuq, NULL, NULL, 0, 0, NULL, NULL);
 
        if (mi == NULL || mi->ctx == NULL)
                goto cleanup;
Index: kbfunc.c
===================================================================
RCS file: /cvs/xenocara/app/cwm/kbfunc.c,v
retrieving revision 1.58
diff -u -p -r1.58 kbfunc.c
--- kbfunc.c    29 Aug 2011 09:09:45 -0000      1.58
+++ kbfunc.c    22 Jan 2012 10:26:55 -0000
@@ -157,7 +157,7 @@ kbfunc_client_search(struct client_ctx *
                TAILQ_INSERT_TAIL(&menuq, mi, entry);
        }
 
-       if ((mi = menu_filter(sc, &menuq, "window", NULL, 0,
+       if ((mi = menu_filter(sc, &menuq, "window", NULL, 0, 0,
            search_match_client, search_print_client)) != NULL) {
                cc = (struct client_ctx *)mi->ctx;
                if (cc->flags & CLIENT_HIDDEN)
@@ -192,7 +192,7 @@ kbfunc_menu_search(struct client_ctx *cc
                TAILQ_INSERT_TAIL(&menuq, mi, entry);
        }
 
-       if ((mi = menu_filter(sc, &menuq, "application", NULL, 0,
+       if ((mi = menu_filter(sc, &menuq, "application", NULL, 0, 0,
            search_match_text, NULL)) != NULL)
                u_spawn(((struct cmd *)mi->ctx)->image);
 
@@ -303,7 +303,7 @@ kbfunc_exec(struct client_ctx *cc, union
        }
        xfree(path);
 
-       if ((mi = menu_filter(sc, &menuq, label, NULL, 1,
+       if ((mi = menu_filter(sc, &menuq, label, NULL, 1, 1,
            search_match_exec, NULL)) != NULL) {
                if (mi->text[0] == '\0')
                        goto out;
@@ -383,7 +383,7 @@ kbfunc_ssh(struct client_ctx *cc, union 
        xfree(lbuf);
        (void)fclose(fp);
 
-       if ((mi = menu_filter(sc, &menuq, "ssh", NULL, 1,
+       if ((mi = menu_filter(sc, &menuq, "ssh", NULL, 1, 0,
            search_match_exec, NULL)) != NULL) {
                if (mi->text[0] == '\0')
                        goto out;
@@ -410,7 +410,7 @@ kbfunc_client_label(struct client_ctx *c
        TAILQ_INIT(&menuq);
 
        /* dummy is set, so this will always return */
-       mi = menu_filter(cc->sc, &menuq, "label", cc->label, 1,
+       mi = menu_filter(cc->sc, &menuq, "label", cc->label, 1, 0,
            search_match_text, NULL);
 
        if (!mi->abort) {
Index: menu.c
===================================================================
RCS file: /cvs/xenocara/app/cwm/menu.c,v
retrieving revision 1.33
diff -u -p -r1.33 menu.c
--- menu.c      8 Sep 2011 12:00:50 -0000       1.33
+++ menu.c      22 Jan 2012 10:26:55 -0000
@@ -37,10 +37,11 @@
 enum ctltype {
        CTL_NONE = -1,
        CTL_ERASEONE = 0, CTL_WIPE, CTL_UP, CTL_DOWN, CTL_RETURN,
-       CTL_ABORT, CTL_ALL
+       CTL_TAB, CTL_ABORT, CTL_ALL
 };
 
 struct menu_ctx {
+       struct screen_ctx       *sc;
        char                     searchstr[MENU_MAXENTRY + 1];
        char                     dispstr[MENU_MAXENTRY*2 + 1];
        char                     promptstr[MENU_MAXENTRY + 1];
@@ -53,6 +54,7 @@ struct menu_ctx {
        int                      entry;
        int                      width;
        int                      num;
+       int                      filecomplete;
        int                      x;
        int                      y;
        void (*match)(struct menu_q *, struct menu_q *, char *);
@@ -96,7 +98,7 @@ menu_init(struct screen_ctx *sc)
 
 struct menu *
 menu_filter(struct screen_ctx *sc, struct menu_q *menuq, char *prompt,
-    char *initial, int dummy,
+    char *initial, int dummy, int filecomplete,
     void (*match)(struct menu_q *, struct menu_q *, char *),
     void (*print)(struct menu *, int))
 {
@@ -117,6 +119,8 @@ menu_filter(struct screen_ctx *sc, struc
        xsave = mc.x;
        ysave = mc.y;
 
+       mc.sc = sc;
+       mc.filecomplete = filecomplete;
        if (prompt == NULL) {
                evmask = MENUMASK;
                mc.promptstr[0] = '\0';
@@ -202,13 +206,40 @@ out:
        return (mi);
 }
 
+static char *
+menu_complete_path(struct screen_ctx *sc, char *label, char *space)
+{
+       struct menu             *mi;
+       struct menu_q            menuq;
+       char *path = NULL;
+
+       TAILQ_INIT(&menuq);
+       if ((mi = menu_filter(sc, &menuq, label, space, 1, 0,
+           search_match_path, NULL)) != NULL) {
+               if (mi->text[0] == '\0')
+                       goto out;
+               path = xcalloc(1, MAXPATHLEN);
+               path[0] = '"';
+               strlcpy(path+1, mi->text, MAXPATHLEN);
+               strlcat(path, "\"", MAXPATHLEN);
+       }
+out:
+       if (mi != NULL && mi->dummy)
+               xfree(mi);
+       while ((mi = TAILQ_FIRST(&menuq)) != NULL) {
+               TAILQ_REMOVE(&menuq, mi, entry);
+               xfree(mi);
+       }
+       return path;
+}
+
 static struct menu *
 menu_handle_key(XEvent *e, struct menu_ctx *mc, struct menu_q *menuq,
     struct menu_q *resultq)
 {
        struct menu     *mi;
        enum ctltype     ctl;
-       char             chr;
+       char             chr, *fcp, *sp;
        size_t           len;
 
        if (menu_keycode(e->xkey.keycode, e->xkey.state, &ctl, &chr) < 0)
@@ -254,6 +285,40 @@ menu_handle_key(XEvent *e, struct menu_c
                mc->searchstr[0] = '\0';
                mc->changed = 1;
                break;
+       case CTL_TAB:
+               if ((mi = TAILQ_FIRST(resultq)) != NULL) {
+                       if (strncmp(mc->searchstr, mi->text,
+                                        strlen(mi->text)) == 0) {
+                               goto filecomplete;
+                       }
+                       (void)strlcpy(mc->searchstr,
+                                       mi->text, sizeof(mc->searchstr));
+                       mc->changed = 1;
+                       break;
+               }
+filecomplete:
+               if (mc->filecomplete && mc->searchstr[0] != '\0') {
+                       mi = xcalloc(1, sizeof *mi);
+                       sp = strrchr(mc->searchstr, ' ');
+                       if (sp != NULL) {
+                               strlcpy(mi->text, mc->searchstr, sp - 
mc->searchstr + 1);
+                               if (sp + 1 < mc->searchstr + 
strlen(mc->searchstr)-1)
+                                       sp++;
+                               else
+                                       sp = NULL;
+                       } else
+                               strlcpy(mi->text, mc->searchstr, 
sizeof(mi->text));
+                       fcp = menu_complete_path(mc->sc, mi->text, sp);
+                       if (fcp) {
+                               strlcat(mi->text, " ", sizeof(mi->text));
+                               strlcat(mi->text, fcp, sizeof(mi->text));
+                               xfree(fcp);
+                       }
+                       mi->dummy = 1;
+                       return (mi);
+               }
+               mc->changed = 1;
+               /* FALLTHROUGH */
        case CTL_ALL:
                mc->list = !mc->list;
                break;
@@ -475,6 +540,9 @@ menu_keycode(KeyCode kc, u_int state, en
                break;
        case XK_Return:
                *ctl = CTL_RETURN;
+               break;
+       case XK_Tab:
+               *ctl = CTL_TAB;
                break;
        case XK_Up:
                *ctl = CTL_UP;
Index: mousefunc.c
===================================================================
RCS file: /cvs/xenocara/app/cwm/mousefunc.c,v
retrieving revision 1.34
diff -u -p -r1.34 mousefunc.c
--- mousefunc.c 17 Oct 2011 18:18:38 -0000      1.34
+++ mousefunc.c 22 Jan 2012 10:26:55 -0000
@@ -242,7 +242,7 @@ mousefunc_menu_unhide(struct client_ctx 
        if (TAILQ_EMPTY(&menuq))
                return;
 
-       mi = menu_filter(sc, &menuq, NULL, NULL, 0, NULL, NULL);
+       mi = menu_filter(sc, &menuq, NULL, NULL, 0, 0, NULL, NULL);
        if (mi != NULL) {
                cc = (struct client_ctx *)mi->ctx;
                client_unhide(cc);
@@ -278,7 +278,7 @@ mousefunc_menu_cmd(struct client_ctx *cc
        if (TAILQ_EMPTY(&menuq))
                return;
 
-       mi = menu_filter(sc, &menuq, NULL, NULL, 0, NULL, NULL);
+       mi = menu_filter(sc, &menuq, NULL, NULL, 0, 0, NULL, NULL);
        if (mi != NULL)
                u_spawn(((struct cmd *)mi->ctx)->image);
        else
Index: search.c
===================================================================
RCS file: /cvs/xenocara/app/cwm/search.c,v
retrieving revision 1.24
diff -u -p -r1.24 search.c
--- search.c    25 Jul 2011 15:10:24 -0000      1.24
+++ search.c    22 Jan 2012 10:26:55 -0000
@@ -29,6 +29,7 @@
 #include <string.h>
 #include <stdio.h>
 #include <unistd.h>
+#include <glob.h>
 
 #include "calmwm.h"
 
@@ -159,6 +160,31 @@ search_print_client(struct menu *mi, int
                (void)snprintf(mi->print, sizeof(mi->print),
                    "%s:%.*s%s", buf, diff, cc->name, marker);
        }
+}
+
+void
+search_match_path(struct menu_q *menuq, struct menu_q *resultq, char *search)
+{
+       struct menu *mi;
+       char pattern[MAXPATHLEN] = "*";
+       glob_t g;
+       int i;
+
+       TAILQ_INIT(resultq);
+
+       strlcpy(pattern, search, sizeof(pattern));
+       strlcat(pattern, "*", sizeof(pattern));
+
+       if (glob(pattern, GLOB_MARK, NULL, &g) != 0)
+               return;
+       for (i = 0; i < g.gl_matchc; i++) {
+               mi = xcalloc(1, sizeof(*mi));
+               (void)strlcpy(mi->text, g.gl_pathv[i],
+                               sizeof(mi->text));
+               TAILQ_INSERT_TAIL(resultq, mi, resultentry);
+               TAILQ_INSERT_TAIL(menuq, mi, entry);
+       }
+       globfree(&g);
 }
 
 void
-- 
Alexander Polakov | plhk.ru

Reply via email to