diff options
author | oga | 2008-05-20 14:50:51 +0000 |
---|---|---|
committer | oga | 2008-05-20 14:50:51 +0000 |
commit | c4a8f44931713f32f250264ca00520aae30fc0e3 (patch) | |
tree | 28a04445e99404bad46b8fbdc9070d8635e0fe72 | |
parent | 073225cc6903924869d463a1014860a78f73b008 (diff) | |
download | cwm-c4a8f44931713f32f250264ca00520aae30fc0e3.tar.gz |
Pull out the behaviour in grab_label and search_start into one utility
function menu_filter(). The plan is to eventually merge in grab_menu too.
Shrinks the code a fair bit.
Also, change XMaskEvent for XWindowEvent to prevent getting exposes for other
windows. This is particuarly noticable on slow machines with a LOT of xterms
(todd, you're an odd man).
ok okan@, todd@.
-rw-r--r-- | Makefile | 6 | ||||
-rw-r--r-- | calmwm.c | 9 | ||||
-rw-r--r-- | calmwm.h | 14 | ||||
-rw-r--r-- | client.c | 14 | ||||
-rw-r--r-- | grab.c | 99 | ||||
-rw-r--r-- | kbfunc.c | 38 | ||||
-rw-r--r-- | menu.c | 296 | ||||
-rw-r--r-- | search.c | 267 |
8 files changed, 347 insertions, 396 deletions
@@ -1,11 +1,11 @@ -# $OpenBSD: Makefile,v 1.6 2008/03/25 23:41:50 matthieu Exp $ +# $OpenBSD: Makefile,v 1.7 2008/05/20 14:50:51 oga Exp $ .include <bsd.xconf.mk> PROG= cwm -SRCS= calmwm.c screen.c xmalloc.c client.c grab.c search.c \ - util.c xutil.c conf.c input.c xevents.c group.c \ +SRCS= calmwm.c screen.c xmalloc.c client.c grab.c menu.c \ + search.c util.c xutil.c conf.c input.c xevents.c group.c \ kbfunc.c font.c parse.y CPPFLAGS+= -I${X11BASE}/include -I${X11BASE}/include/freetype2 -I${.CURDIR} @@ -15,7 +15,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $Id: calmwm.c,v 1.18 2008/05/19 18:53:09 oga Exp $ + * $Id: calmwm.c,v 1.19 2008/05/20 14:50:51 oga Exp $ */ #include "headers.h" @@ -152,6 +152,10 @@ x_setupscreen(struct screen_ctx *sc, u_int which) sc->display = x_screenname(which); sc->which = which; sc->rootwin = RootWindow(X_Dpy, which); + + sc->xmax = DisplayWidth(X_Dpy, sc->which); + sc->ymax = DisplayHeight(X_Dpy, sc->which); + XAllocNamedColor(X_Dpy, DefaultColormap(X_Dpy, which), "black", &sc->fgcolor, &tmp); XAllocNamedColor(X_Dpy, DefaultColormap(X_Dpy, which), @@ -204,6 +208,8 @@ x_setupscreen(struct screen_ctx *sc, u_int which) font_init(sc); DefaultFont = font_getx(sc, Conf.DefaultFontName); + sc->fontheight = font_ascent(DefaultFont) + + font_descent(DefaultFont) + 1; /* * XXX - this should *really* be in screen_init(). ordering @@ -213,7 +219,6 @@ x_setupscreen(struct screen_ctx *sc, u_int which) /* Initialize menu window. */ grab_menuinit(sc); - search_init(sc); /* Deal with existing clients. */ XQueryTree(X_Dpy, sc->rootwin, &w0, &w1, &wins, &nwins); @@ -15,7 +15,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $Id: calmwm.h,v 1.48 2008/05/19 18:07:53 okan Exp $ + * $Id: calmwm.h,v 1.49 2008/05/20 14:50:51 oga Exp $ */ #ifndef _CALMWM_H_ @@ -60,7 +60,6 @@ struct screen_ctx { u_int which; Window rootwin; Window menuwin; - Window searchwin; Colormap colormap; XColor bgcolor, fgcolor, fccolor, redcolor, cyancolor, whitecolor, blackcolor; @@ -72,13 +71,13 @@ struct screen_ctx { int altpersist; - int maxinitialised; int xmax; int ymax; struct cycle_entry_q mruq; struct fonthash fonthash; + u_int fontheight; XftDraw *xftdraw; XftColor xftcolor; }; @@ -354,6 +353,10 @@ void client_gethints(struct client_ctx *); void client_freehints(struct client_ctx *); void client_do_shape(struct client_ctx *); +struct menu *menu_filter(struct menu_q *, char *, char *, int, + void (*)(struct menu_q *, struct menu_q *, char *), + void (*)(struct menu *, int)); + void xev_handle_maprequest(struct xevent *, XEvent *); void xev_handle_unmapnotify(struct xevent *, XEvent *); void xev_handle_destroynotify(struct xevent *, XEvent *); @@ -449,11 +452,6 @@ void kbfunc_ssh(struct client_ctx *, void *); void kbfunc_term(struct client_ctx *, void *); void kbfunc_lock(struct client_ctx *, void *); -void search_init(struct screen_ctx *); -struct menu *search_start(struct menu_q *, - void (*)(struct menu_q *, struct menu_q *, char *), - void (*)(struct menu *, int), - char *, int); void search_match_client(struct menu_q *, struct menu_q *, char *); void search_print_client(struct menu *, int); @@ -15,7 +15,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $Id: client.c,v 1.26 2008/05/19 17:24:19 okan Exp $ + * $Id: client.c,v 1.27 2008/05/20 14:50:51 oga Exp $ */ #include "headers.h" @@ -657,7 +657,7 @@ client_placecalc(struct client_ctx *cc) { struct screen_ctx *sc = CCTOSC(cc); int yslack, xslack; - int x, y, height, width, ymax, xmax, mousex, mousey; + int x, y, height, width, mousex, mousey; y = cc->geom.y; x = cc->geom.x; @@ -665,11 +665,9 @@ client_placecalc(struct client_ctx *cc) height = cc->geom.height; width = cc->geom.width; - ymax = DisplayHeight(X_Dpy, sc->which) - cc->bwidth; - xmax = DisplayWidth(X_Dpy, sc->which) - cc->bwidth; - yslack = ymax - cc->geom.height; - xslack = xmax - cc->geom.width; + yslack = sc->ymax - cc->geom.height; + xslack = sc->xmax - cc->geom.width; xu_ptr_getpos(sc->rootwin, &mousex, &mousey); @@ -695,7 +693,7 @@ client_placecalc(struct client_ctx *cc) } else { if (yslack < 0) { y = cc->bwidth; - height = ymax; + height = sc->ymax; } else { if (y == 0 || y > yslack) y = MIN(mousey, yslack); @@ -704,7 +702,7 @@ client_placecalc(struct client_ctx *cc) if (xslack < 0) { x = cc->bwidth; - width = xmax; + width = sc->xmax; } else { if (x == 0 || x > xslack) x = MIN(mousex, xslack); @@ -15,7 +15,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $Id: grab.c,v 1.12 2008/05/19 18:07:53 okan Exp $ + * $Id: grab.c,v 1.13 2008/05/20 14:50:51 oga Exp $ */ #include "headers.h" @@ -171,11 +171,6 @@ grab_menu(XButtonEvent *e, struct menu_q *menuq) no++; } - if (!sc->maxinitialised) { - sc->xmax = DisplayWidth(X_Dpy, sc->which); - sc->ymax = DisplayHeight(X_Dpy, sc->which); - } - height = font_ascent(font) + font_descent(font) + 1; tothigh = height * no; @@ -261,98 +256,6 @@ grab_menuinit(struct screen_ctx *sc) 1, 1, 1, sc->blackpixl, sc->whitepixl); } -#define LABEL_MAXLEN 256 -#define LabelMask (KeyPressMask|ExposureMask) - -void -grab_label(struct client_ctx *cc) -{ - struct screen_ctx *sc = screen_current(); - int x, y, dx, dy, fontheight, focusrevert; - XEvent e; - char labelstr[LABEL_MAXLEN]; - char dispstr[LABEL_MAXLEN + sizeof("label>") - 1]; - Window focuswin; - char chr; - enum ctltype ctl; - size_t len; - struct fontdesc *font = DefaultFont; - - if (cc->label != NULL) - strlcpy(labelstr, cc->label, sizeof(labelstr)); - else - labelstr[0] = '\0'; - - xu_ptr_getpos(sc->rootwin, &x, &y); - - dy = fontheight = font_ascent(font) + font_descent(font) + 1; - dx = font_width(font, "label>", 6); - - XMoveResizeWindow(X_Dpy, sc->searchwin, x, y, dx, dy); - XSelectInput(X_Dpy, sc->searchwin, LabelMask); - XMapRaised(X_Dpy, sc->searchwin); - - XGetInputFocus(X_Dpy, &focuswin, &focusrevert); - XSetInputFocus(X_Dpy, sc->searchwin, RevertToPointerRoot, CurrentTime); - - for (;;) { - XMaskEvent(X_Dpy, LabelMask, &e); - - switch (e.type) { - case KeyPress: - if (input_keycodetrans(e.xkey.keycode, e.xkey.state, - &ctl, &chr) < 0) - continue; - - switch (ctl) { - case CTL_ERASEONE: - if ((len = strlen(labelstr)) > 0) - labelstr[len - 1] = '\0'; - break; - case CTL_RETURN: - /* Done */ - if (strlen(labelstr) == 0) - goto out; - - if (cc->label != NULL) - xfree(cc->label); - - cc->label = xstrdup(labelstr); - /* FALLTHROUGH */ - case CTL_ABORT: - goto out; - default: - break; - } - - if (chr != '\0') { - char str[2]; - - str[0] = chr; - str[1] = '\0'; - strlcat(labelstr, str, sizeof(labelstr)); - } - - case Expose: - snprintf(dispstr, sizeof(dispstr), "label>%s", - labelstr); - dx = font_width(font, dispstr, strlen(dispstr)); - dy = fontheight; - - XClearWindow(X_Dpy, sc->searchwin); - XResizeWindow(X_Dpy, sc->searchwin, dx, dy); - - font_draw(font, dispstr, strlen(dispstr), - sc->searchwin, 0, font_ascent(font) + 1); - break; - } - } - -out: - XSetInputFocus(X_Dpy, focuswin, focusrevert, CurrentTime); - XUnmapWindow(X_Dpy, sc->searchwin); -} - static int _sweepcalc(struct client_ctx *cc, int x0, int y0, int motionx, int motiony) { @@ -15,7 +15,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $Id: kbfunc.c,v 1.27 2008/05/19 18:53:09 oga Exp $ + * $Id: kbfunc.c,v 1.28 2008/05/20 14:50:51 oga Exp $ */ #include <paths.h> @@ -128,9 +128,8 @@ kbfunc_client_search(struct client_ctx *scratch, void *arg) TAILQ_INSERT_TAIL(&menuq, mi, entry); } - if ((mi = search_start(&menuq, - search_match_client, search_print_client, - "window", 0)) != NULL) { + if ((mi = menu_filter(&menuq, "window", NULL, 0, + search_match_client, search_print_client)) != NULL) { cc = (struct client_ctx *)mi->ctx; if (cc->flags & CLIENT_HIDDEN) client_unhide(cc); @@ -163,8 +162,8 @@ kbfunc_menu_search(struct client_ctx *scratch, void *arg) TAILQ_INSERT_TAIL(&menuq, mi, entry); } - if ((mi = search_start(&menuq, - search_match_text, NULL, "application", 0)) != NULL) + if ((mi = menu_filter(&menuq, "application", NULL, 0, + search_match_text, NULL)) != NULL) u_spawn(((struct cmd *)mi->ctx)->image); while ((mi = TAILQ_FIRST(&menuq)) != NULL) { @@ -301,8 +300,8 @@ kbfunc_exec(struct client_ctx *scratch, void *arg) } xfree(path); - if ((mi = search_start(&menuq, - search_match_exec, NULL, label, 1)) != NULL) { + if ((mi = menu_filter(&menuq, label, NULL, 1, + search_match_exec, NULL)) != NULL) { switch (cmd) { case CWM_EXEC_PROGRAM: u_spawn(mi->text); @@ -376,8 +375,8 @@ kbfunc_ssh(struct client_ctx *scratch, void *arg) fclose(fp); - if ((mi = search_start(&menuq, - search_match_exec, NULL, "ssh", 1)) != NULL) { + if ((mi = menu_filter(&menuq, "ssh", NULL, 1, + search_match_exec, NULL)) != NULL) { conf_reload(&Conf); l = snprintf(cmd, sizeof(cmd), "%s -e ssh %s", Conf.termpath, mi->text); @@ -396,7 +395,24 @@ kbfunc_ssh(struct client_ctx *scratch, void *arg) void kbfunc_client_label(struct client_ctx *cc, void *arg) { - grab_label(cc); + struct menu *mi; + char *current; + struct menu_q menuq; + + TAILQ_INIT(&menuq); + + if (cc->label != NULL) + current = cc->label; + else + current = NULL; + + if ((mi = menu_filter(&menuq, "label", current, 1, + search_match_text, NULL)) != NULL) { + if (cc->label != NULL) + xfree(cc->label); + cc->label = xstrdup(mi->text); + xfree(mi); + } } void @@ -0,0 +1,296 @@ +/* + * Copyright (c) 2008 Owain G. Ainsworth <oga@openbsd.org> + * Copyright (c) 2004 Marius Aamodt Eriksen <marius@monkey.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "headers.h" +#include "calmwm.h" + +#define KeyMask (KeyPressMask|ExposureMask) + +struct menu_ctx { + char searchstr[MENU_MAXENTRY + 1]; + char dispstr[MENU_MAXENTRY*2 + 1]; + char promptstr[MENU_MAXENTRY + 1]; + int list; + int listing; + int changed; + int noresult; + int x; + int y; /* location */ + void (*match)(struct menu_q *, struct menu_q *, char *); + void (*print)(struct menu *, int); +}; +static struct menu *menu_handle_key(XEvent *, struct menu_ctx *, + struct menu_q *, struct menu_q *); +static void menu_draw(struct screen_ctx *, struct menu_ctx *, + struct menu_q *, struct menu_q *); + +struct menu * +menu_filter(struct menu_q *menuq, char *prompt, char *initial, int dummy, + void (*match)(struct menu_q *, struct menu_q *, char *), + void (*print)(struct menu *, int)) +{ + struct screen_ctx *sc = screen_current(); + struct menu_ctx mc; + struct menu_q resultq; + struct menu *mi = NULL; + XEvent e; + Window focuswin; + int dx, dy, focusrevert; + char endchar = '«'; + struct fontdesc *font = DefaultFont; + + TAILQ_INIT(&resultq); + + bzero(&mc, sizeof(mc)); + + xu_ptr_getpos(sc->rootwin, &mc.x, &mc.y); + + if (prompt == NULL) + prompt = "search"; + + if (initial != NULL) + strlcpy(mc.searchstr, initial, sizeof(mc.searchstr)); + else + mc.searchstr[0] = '\0'; + + mc.match = match; + mc.print = print; + + snprintf(mc.promptstr, sizeof(mc.promptstr), "%s»", prompt); + snprintf(mc.dispstr, sizeof(mc.dispstr), "%s%s%c", mc.promptstr, + mc.searchstr, endchar); + dx = font_width(font, mc.dispstr, strlen(mc.dispstr)); + dy = sc->fontheight; + + XMoveResizeWindow(X_Dpy, sc->menuwin, mc.x, mc.y, dx, dy); + XSelectInput(X_Dpy, sc->menuwin, KeyMask); + XMapRaised(X_Dpy, sc->menuwin); + + if (xu_ptr_grab(sc->menuwin, 0, Cursor_question) < 0) { + XUnmapWindow(X_Dpy, sc->menuwin); + return (NULL); + } + + XGetInputFocus(X_Dpy, &focuswin, &focusrevert); + XSetInputFocus(X_Dpy, sc->menuwin, RevertToPointerRoot, CurrentTime); + + for (;;) { + mc.changed = 0; + + XWindowEvent(X_Dpy, sc->menuwin, KeyMask, &e); + + switch (e.type) { + case KeyPress: + if ((mi = menu_handle_key(&e, &mc, menuq, &resultq)) + != NULL) + goto out; + /* FALLTHROUGH */ + case Expose: + menu_draw(sc, &mc, menuq, &resultq); + break; + } + } +out: + if (dummy == 0 && mi->dummy) { /* no match */ + xfree (mi); + mi = NULL; + xu_ptr_ungrab(); + XSetInputFocus(X_Dpy, focuswin, focusrevert, CurrentTime); + } + + XUnmapWindow(X_Dpy, sc->menuwin); + + return (mi); +} + +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; + size_t len; + + if (input_keycodetrans(e->xkey.keycode, e->xkey.state, + &ctl, &chr) < 0) + return (NULL); + + switch (ctl) { + case CTL_ERASEONE: + if ((len = strlen(mc->searchstr)) > 0) { + mc->searchstr[len - 1] = '\0'; + mc->changed = 1; + } + break; + case CTL_UP: + mi = TAILQ_LAST(resultq, menu_q); + if (mi == NULL) + break; + + TAILQ_REMOVE(resultq, mi, resultentry); + TAILQ_INSERT_HEAD(resultq, mi, resultentry); + break; + case CTL_DOWN: + mi = TAILQ_FIRST(resultq); + if (mi == NULL) + break; + + TAILQ_REMOVE(resultq, mi, resultentry); + TAILQ_INSERT_TAIL(resultq, mi, resultentry); + break; + case CTL_RETURN: + /* + * Return whatever the cursor is currently on. Else + * even if dummy is zero, we need to return something. + */ + if ((mi = TAILQ_FIRST(resultq)) == NULL) { + mi = xmalloc(sizeof *mi); + (void)strlcpy(mi->text, + mc->searchstr, sizeof(mi->text)); + mi->dummy = 1; + } + return (mi); + case CTL_WIPE: + mc->searchstr[0] = '\0'; + mc->changed = 1; + break; + case CTL_ALL: + mc->list = !mc->list; + break; + case CTL_ABORT: + mi = xmalloc(sizeof *mi); + mi->text[0] = '\0'; + mi->dummy = 1; + return (mi); + default: + break; + } + + if (chr != '\0') { + char str[2]; + + str[0] = chr; + str[1] = '\0'; + mc->changed = 1; + strlcat(mc->searchstr, str, sizeof(mc->searchstr)); + } + + mc->noresult = 0; + if (mc->changed && strlen(mc->searchstr) > 0) { + (*mc->match)(menuq, resultq, mc->searchstr); + /* If menuq is empty, never show we've failed */ + mc->noresult = TAILQ_EMPTY(resultq) && !TAILQ_EMPTY(menuq); + } else if (mc->changed) + TAILQ_INIT(resultq); + + if (!mc->list && mc->listing && !mc->changed) { + TAILQ_INIT(resultq); + mc->listing = 0; + } + + return (NULL); +} + +static void +menu_draw(struct screen_ctx *sc, struct menu_ctx *mc, struct menu_q *menuq, + struct menu_q *resultq) +{ + struct menu *mi; + char endchar = '«'; + int n = 0; + int dx, dy; + int xsave, ysave; + int warp; + struct fontdesc *font = DefaultFont; + + if (mc->list) { + if (TAILQ_EMPTY(resultq) && mc->list) { + /* Copy them all over. */ + TAILQ_FOREACH(mi, menuq, entry) + TAILQ_INSERT_TAIL(resultq, mi, + resultentry); + + mc->listing = 1; + } else if (mc->changed) + mc->listing = 0; + } + + snprintf(mc->dispstr, sizeof(mc->dispstr), "%s%s%c", + mc->promptstr, mc->searchstr, endchar); + dx = font_width(font, mc->dispstr, strlen(mc->dispstr)); + dy = sc->fontheight; + + TAILQ_FOREACH(mi, resultq, resultentry) { + char *text; + + if (mc->print != NULL) { + (*mc->print)(mi, mc->listing); + text = mi->print; + } else { + mi->print[0] = '\0'; + text = mi->text; + } + + dx = MAX(dx, font_width(font, text, + MIN(strlen(text), MENU_MAXENTRY))); + dy += sc->fontheight; + } + + xsave = mc->x; + ysave = mc->y; + if (mc->x < 0) + mc->x = 0; + else if (mc->x + dx >= sc->xmax) + mc->x = sc->xmax - dx; + + if (mc->y + dy >= sc->ymax) + mc->y = sc->ymax - dy; + /* never hide the top of the menu */ + if (mc->y < 0) + mc->y = 0; + + if (mc->x != xsave || mc->y != ysave) + xu_ptr_setpos(sc->rootwin, mc->x, mc->y); + + XClearWindow(X_Dpy, sc->menuwin); + XMoveResizeWindow(X_Dpy, sc->menuwin, mc->x, mc->y, dx, dy); + + font_draw(font, mc->dispstr, strlen(mc->dispstr), sc->menuwin, + 0, font_ascent(font) + 1); + + n = 1; + TAILQ_FOREACH(mi, resultq, resultentry) { + char *text = mi->print[0] != '\0' ? + mi->print : mi->text; + + font_draw(font, text, + MIN(strlen(text), MENU_MAXENTRY), + sc->menuwin, + 0, n*sc->fontheight + font_ascent(font) + 1); + n++; + } + + if (n > 1) + XFillRectangle(X_Dpy, sc->menuwin, sc->gc, + 0, sc->fontheight, dx, sc->fontheight); + + if (mc->noresult) + XFillRectangle(X_Dpy, sc->menuwin, sc->gc, + 0, 0, dx, sc->fontheight); + +} @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $Id: search.c,v 1.10 2008/05/19 18:07:53 okan Exp $ + * $Id: search.c,v 1.11 2008/05/20 14:50:51 oga Exp $ */ #include "headers.h" @@ -24,271 +24,6 @@ static int _strsubmatch(char *, char *, int); -void -search_init(struct screen_ctx *sc) -{ - sc->searchwin = XCreateSimpleWindow(X_Dpy, sc->rootwin, 0, 0, - 1, 1, 1, sc->blackpixl, sc->whitepixl); -} - -/* - * Input: list of items, - * Output: choose one - * so, exactly like menus - */ - -struct menu * -search_start(struct menu_q *menuq, - void (*match)(struct menu_q *, struct menu_q *, char *), - void (*print)(struct menu *mi, int print), - char *prompt, int dummy) -{ - struct screen_ctx *sc = screen_current(); - int x, y, dx, dy, fontheight, - focusrevert, mutated, xmax, ymax, warp, added, beobnoxious = 0; - XEvent e; - char searchstr[MENU_MAXENTRY + 1]; - char dispstr[MENU_MAXENTRY*2 + 1]; - char promptstr[MENU_MAXENTRY + 1]; - Window focuswin; - struct menu *mi = NULL, *dummy_mi = NULL; - struct menu_q resultq; - char chr; - enum ctltype ctl; - size_t len; - u_int n; - static int list = 0; - int listing = 0; - char endchar = '«'; - struct fontdesc *font = DefaultFont; - - if (prompt == NULL) - prompt = "search"; - - TAILQ_INIT(&resultq); - - xmax = DisplayWidth(X_Dpy, sc->which); - ymax = DisplayHeight(X_Dpy, sc->which); - - xu_ptr_getpos(sc->rootwin, &x, &y); - - searchstr[0] = '\0'; - - snprintf(promptstr, sizeof(promptstr), "%s »", prompt); - dy = fontheight = font_ascent(font) + font_descent(font) + 1; - snprintf(dispstr, sizeof(dispstr), "%s%c", promptstr, endchar); - dx = font_width(font, dispstr, strlen(dispstr)); - - XMoveResizeWindow(X_Dpy, sc->searchwin, x, y, dx, dy); - XSelectInput(X_Dpy, sc->searchwin, SearchMask); - XMapRaised(X_Dpy, sc->searchwin); - - /* - * TODO: eventually, the mouse should be able to select - * results as well. Right now we grab it only to set a fancy - * cursor. - */ - if (xu_ptr_grab(sc->searchwin, 0, Cursor_question) < 0) { - XUnmapWindow(X_Dpy, sc->searchwin); - return (NULL); - } - - XGetInputFocus(X_Dpy, &focuswin, &focusrevert); - XSetInputFocus(X_Dpy, sc->searchwin, RevertToPointerRoot, CurrentTime); - - for (;;) { - added = mutated = 0; - - XMaskEvent(X_Dpy, SearchMask, &e); - - switch (e.type) { - case KeyPress: - if (input_keycodetrans(e.xkey.keycode, e.xkey.state, - &ctl, &chr) < 0) - continue; - - switch (ctl) { - case CTL_ERASEONE: - if ((len = strlen(searchstr)) > 0) { - searchstr[len - 1] = '\0'; - mutated = 1; - } - break; - case CTL_UP: - mi = TAILQ_LAST(&resultq, menu_q); - if (mi == NULL) - break; - - TAILQ_REMOVE(&resultq, mi, resultentry); - TAILQ_INSERT_HEAD(&resultq, mi, resultentry); - break; - case CTL_DOWN: - mi = TAILQ_FIRST(&resultq); - if (mi == NULL) - break; - - TAILQ_REMOVE(&resultq, mi, resultentry); - TAILQ_INSERT_TAIL(&resultq, mi, resultentry); - break; - case CTL_RETURN: - /* This is just picking the match the - * cursor is over. */ - if ((mi = TAILQ_FIRST(&resultq)) != NULL) { - goto found; - } else if (dummy) { - dummy_mi = xmalloc(sizeof *dummy_mi); - (void) strlcpy(dummy_mi->text, - searchstr, sizeof(dummy_mi->text)); - dummy_mi->dummy = 1; - goto found; - } - goto out; - case CTL_WIPE: - searchstr[0] = '\0'; - mutated = 1; - break; - case CTL_ALL: - list = !list; - break; - case CTL_ABORT: - goto out; - default: - break; - } - - if (chr != '\0') { - char str[2]; - - str[0] = chr; - str[1] = '\0'; - mutated = 1; - added = - strlcat(searchstr, str, sizeof(searchstr)); - } - - beobnoxious = 0; - if (mutated && strlen(searchstr) > 0) { - (*match)(menuq, &resultq, searchstr); - beobnoxious = TAILQ_EMPTY(&resultq); - } else if (mutated) - TAILQ_INIT(&resultq); - - - if (!list && listing && !mutated) { - TAILQ_INIT(&resultq); - listing = 0; - } - - case Expose: - if (list) { - if (TAILQ_EMPTY(&resultq) && list) { - /* Copy them all over. */ - TAILQ_FOREACH(mi, menuq, entry) - TAILQ_INSERT_TAIL(&resultq, mi, - resultentry); - - listing = 1; - } else if (mutated) - listing = 0; - } - - snprintf(dispstr, sizeof(dispstr), "%s%s%c", - promptstr, searchstr, endchar); - dx = font_width(font, dispstr, strlen(dispstr)); - dy = fontheight; - - TAILQ_FOREACH(mi, &resultq, resultentry) { - char *text; - - if (print != NULL) { - (*print)(mi, listing); - text = mi->print; - } else { - mi->print[0] = '\0'; - text = mi->text; - } - - dx = MAX(dx, font_width(font, text, - MIN(strlen(text), MENU_MAXENTRY))); - dy += fontheight; - } - - /* - * Calculate new geometry. - * - * XXX - put this into a util function -- it's - * used elsewhere, too. - */ - warp = 0; - if (x < 0) { - x = 0; - warp = 1; - } - if (x + dx >= xmax) { - x = xmax - dx; - warp = 1; - } - - if (y < 0) { - y = 0; - warp = 1; - } - if (y + dy >= ymax) { - y = ymax - dy; - /* If the menu is too high, never hide the - * top of the menu. - */ - if (y < 0) - y = 0; - warp = 1; - } - - if (warp) - xu_ptr_setpos(sc->rootwin, x, y); - - XClearWindow(X_Dpy, sc->searchwin); - XMoveResizeWindow(X_Dpy, sc->searchwin, x, y, dx, dy); - - font_draw(font, dispstr, strlen(dispstr), sc->searchwin, - 0, font_ascent(font) + 1); - - n = 1; - TAILQ_FOREACH(mi, &resultq, resultentry) { - char *text = mi->print[0] != '\0' ? - mi->print : mi->text; - - font_draw(font, text, - MIN(strlen(text), MENU_MAXENTRY), - sc->searchwin, - 0, n*fontheight + font_ascent(font) + 1); - n++; - } - - if (n > 1) - XFillRectangle(X_Dpy, sc->searchwin, sc->gc, - 0, fontheight, dx, fontheight); - - if (beobnoxious) - XFillRectangle(X_Dpy, sc->searchwin, sc->gc, - 0, 0, dx, fontheight); - - break; - } - } - -out: - /* (if no match) */ - xu_ptr_ungrab(); - XSetInputFocus(X_Dpy, focuswin, focusrevert, CurrentTime); - -found: - XUnmapWindow(X_Dpy, sc->searchwin); - - if (dummy && dummy_mi != NULL) - return (dummy_mi); - return (mi); -} - /* * Match: label, title, class. */ |