aboutsummaryrefslogtreecommitdiffstats
path: root/menu.c
diff options
context:
space:
mode:
authoroga2008-05-20 14:50:51 +0000
committeroga2008-05-20 14:50:51 +0000
commitc4a8f44931713f32f250264ca00520aae30fc0e3 (patch)
tree28a04445e99404bad46b8fbdc9070d8635e0fe72 /menu.c
parent073225cc6903924869d463a1014860a78f73b008 (diff)
downloadcwm-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@.
Diffstat (limited to '')
-rw-r--r--menu.c296
1 files changed, 296 insertions, 0 deletions
diff --git a/menu.c b/menu.c
new file mode 100644
index 0000000..a022f84
--- /dev/null
+++ b/menu.c
@@ -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);
+
+}