* 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