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@.
This commit is contained in:
oga
2008-05-20 14:50:51 +00:00
parent 3bb0b451f7
commit 1e46ba72f7
8 changed files with 340 additions and 389 deletions

265
search.c
View File

@@ -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.
*/