* Alexander Polakov <p...@sdf.org> [121107 02:20]: > I think this one is ready for wider testing. > > How to use: hit tab in exec menu to complete the command (start > with / if you want something not in $PATH). When you're ready, > hit tab again. This will open "file menu", which can be used to > complete file argument. > > Completion works for other menus as well.
Oops, sorry, I sent the wrong diff (previous revision). Index: calmwm.h =================================================================== RCS file: /cvs/xenocara/app/cwm/calmwm.h,v retrieving revision 1.155 diff -u -p -u -r1.155 calmwm.h --- calmwm.h 31 Oct 2012 22:06:24 -0000 1.155 +++ calmwm.h 7 Nov 2012 01:06:13 -0000 @@ -73,6 +73,10 @@ #define CWM_RCYCLE 0x0002 #define CWM_INGROUP 0x0004 +/* menu */ +#define CWM_MENU_DUMMY 0x0001 +#define CWM_MENU_FILE 0x0002 + #define KBTOGROUP(X) ((X) - 1) union arg { @@ -260,7 +264,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 +358,10 @@ void group_update_names(struct screen void search_match_client(struct menu_q *, struct menu_q *, char *); void search_match_exec(struct menu_q *, struct menu_q *, + char *); +void search_match_exec_path(struct menu_q *, struct menu_q *, + char *); +void search_match_path_any(struct menu_q *, struct menu_q *, char *); void search_match_text(struct menu_q *, struct menu_q *, char *); Index: kbfunc.c =================================================================== RCS file: /cvs/xenocara/app/cwm/kbfunc.c,v retrieving revision 1.64 diff -u -p -u -r1.64 kbfunc.c --- kbfunc.c 31 Oct 2012 19:30:19 -0000 1.64 +++ kbfunc.c 7 Nov 2012 01:06:13 -0000 @@ -298,8 +298,9 @@ kbfunc_exec(struct client_ctx *cc, union } xfree(path); - if ((mi = menu_filter(sc, &menuq, label, NULL, 1, - search_match_exec, NULL)) != NULL) { + if ((mi = menu_filter(sc, &menuq, label, NULL, + CWM_MENU_DUMMY | CWM_MENU_FILE, + search_match_exec_path, NULL)) != NULL) { if (mi->text[0] == '\0') goto out; switch (cmd) { @@ -376,7 +377,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, CWM_MENU_DUMMY, search_match_exec, NULL)) != NULL) { if (mi->text[0] == '\0') goto out; @@ -403,7 +404,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, CWM_MENU_DUMMY, search_match_text, NULL); if (!mi->abort) { Index: menu.c =================================================================== RCS file: /cvs/xenocara/app/cwm/menu.c,v retrieving revision 1.41 diff -u -p -u -r1.41 menu.c --- menu.c 31 Oct 2012 19:30:19 -0000 1.41 +++ menu.c 7 Nov 2012 01:06:13 -0000 @@ -28,6 +28,7 @@ #include <string.h> #include <stdio.h> #include <unistd.h> +#include <ctype.h> #include "calmwm.h" @@ -37,10 +38,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]; @@ -54,6 +56,7 @@ struct menu_ctx { int height; int width; int num; + int flags; int x; int y; void (*match)(struct menu_q *, struct menu_q *, char *); @@ -93,7 +96,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 flags, void (*match)(struct menu_q *, struct menu_q *, char *), void (*print)(struct menu *, int)) { @@ -114,6 +117,8 @@ menu_filter(struct screen_ctx *sc, struc xsave = mc.x; ysave = mc.y; + mc.sc = sc; + mc.flags = flags; if (prompt == NULL) { evmask = MENUMASK; mc.promptstr[0] = '\0'; @@ -181,7 +186,8 @@ menu_filter(struct screen_ctx *sc, struc } } out: - if (dummy == 0 && mi->dummy) { /* no mouse based match */ + if ((mc.flags & CWM_MENU_DUMMY) == 0 && mi->dummy) { + /* no mouse based match */ xfree(mi); mi = NULL; } @@ -200,6 +206,38 @@ out: } static struct menu * +menu_complete_path(struct menu_ctx *mc) +{ + struct menu *mi, *mr; + struct menu_q menuq; + char *path = NULL; + + path = xcalloc(1, sizeof(mr->text)); + mr = xcalloc(1, sizeof(*mr)); + + TAILQ_INIT(&menuq); + if ((mi = menu_filter(mc->sc, &menuq, mc->searchstr, NULL, + CWM_MENU_DUMMY, search_match_path_any, NULL)) != NULL) { + mr->abort = mi->abort; + mr->dummy = mi->dummy; + strlcpy(path, mi->text, sizeof(mi->text)); + } + + while ((mi = TAILQ_FIRST(&menuq)) != NULL) { + TAILQ_REMOVE(&menuq, mi, entry); + xfree(mi); + } + + if (path[0] != '\0') + snprintf(mr->text, sizeof(mr->text), "%s \"%s\"", + mc->searchstr, path); + else if (!mr->abort) + strlcpy(mr->text, mc->searchstr, sizeof(mr->text)); + xfree(path); + return (mr); +} + +static struct menu * menu_handle_key(XEvent *e, struct menu_ctx *mc, struct menu_q *menuq, struct menu_q *resultq) { @@ -257,6 +295,35 @@ 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) { + /* + * - We are in exec_path menu mode + * - There's only one result + * - It is equal to the input + * We got a command, launch the file menu + */ + if ((mc->flags & CWM_MENU_FILE) && + (TAILQ_NEXT(mi, resultentry) == NULL) && + (strncmp(mc->searchstr, mi->text, + strlen(mi->text))) == 0) + return (menu_complete_path(mc)); + + /* + * Put common prefix of the results into searchstr + */ + (void)strlcpy(mc->searchstr, + mi->text, sizeof(mc->searchstr)); + while ((mi = TAILQ_NEXT(mi, resultentry)) != NULL) { + i = 0; + while (tolower(mc->searchstr[i]) == + tolower(mi->text[i])) + i++; + mc->searchstr[i] = '\0'; + } + mc->changed = 1; + } + break; case CTL_ALL: mc->list = !mc->list; break; @@ -483,6 +550,9 @@ menu_keycode(XKeyEvent *ev, enum ctltype break; case XK_Return: *ctl = CTL_RETURN; + break; + case XK_Tab: + *ctl = CTL_TAB; break; case XK_Up: *ctl = CTL_UP; Index: search.c =================================================================== RCS file: /cvs/xenocara/app/cwm/search.c,v retrieving revision 1.24 diff -u -p -u -r1.24 search.c --- search.c 25 Jul 2011 15:10:24 -0000 1.24 +++ search.c 7 Nov 2012 01:06:13 -0000 @@ -29,9 +29,12 @@ #include <string.h> #include <stdio.h> #include <unistd.h> +#include <glob.h> #include "calmwm.h" +#define PATH_EXEC 0x1 + static int strsubmatch(char *, char *, int); /* @@ -161,6 +164,43 @@ search_print_client(struct menu *mi, int } } +static void +search_match_path(struct menu_q *menuq, struct menu_q *resultq, char *search, int flag) +{ + struct menu *mi; + char pattern[MAXPATHLEN]; + glob_t g; + int i; + + TAILQ_INIT(resultq); + + (void)strlcpy(pattern, search, sizeof(pattern)); + (void)strlcat(pattern, "*", sizeof(pattern)); + + if (glob(pattern, GLOB_MARK, NULL, &g) != 0) + return; + for (i = 0; i < g.gl_pathc; i++) { + if ((flag & PATH_EXEC) && access(g.gl_pathv[i], X_OK)) + continue; + mi = xcalloc(1, sizeof(*mi)); + (void)strlcpy(mi->text, g.gl_pathv[i], sizeof(mi->text)); + TAILQ_INSERT_TAIL(resultq, mi, resultentry); + } + globfree(&g); +} + +void +search_match_path_exec(struct menu_q *menuq, struct menu_q *resultq, char *search) +{ + return (search_match_path(menuq, resultq, search, PATH_EXEC)); +} + +void +search_match_path_any(struct menu_q *menuq, struct menu_q *resultq, char *search) +{ + return (search_match_path(menuq, resultq, search, 0)); +} + void search_match_text(struct menu_q *menuq, struct menu_q *resultq, char *search) { @@ -194,6 +234,14 @@ search_match_exec(struct menu_q *menuq, if (mj == NULL) TAILQ_INSERT_TAIL(resultq, mi, resultentry); } +} + +void +search_match_exec_path(struct menu_q *menuq, struct menu_q *resultq, char *search) +{ + search_match_exec(menuq, resultq, search); + if (TAILQ_EMPTY(resultq)) + search_match_path_exec(menuq, resultq, search); } static int -- Alexander Polakov | plhk.ru