wmii/cmd/menu/menu.c

326 lines
6.1 KiB
C
Raw Normal View History

2008-10-14 05:38:03 +04:00
#include "dat.h"
#include <ctype.h>
#include <strings.h>
2008-10-16 07:59:17 +04:00
#include <unistd.h>
2008-10-14 05:38:03 +04:00
#include "fns.h"
static Handlers handlers;
static int ltwidth;
enum {
2008-10-15 00:57:48 +04:00
ACCEPT = CARET_LAST,
2008-10-14 05:38:03 +04:00
REJECT,
HIST,
2008-10-15 00:57:48 +04:00
KILL,
2008-10-14 05:38:03 +04:00
CMPL_NEXT,
CMPL_PREV,
CMPL_FIRST,
CMPL_LAST,
CMPL_NEXT_PAGE,
CMPL_PREV_PAGE,
};
void
menu_init(void) {
WinAttr wa;
wa.override_redirect = 1;
wa.background_pixmap = ParentRelative;
wa.event_mask = ExposureMask | KeyPressMask;
2008-10-16 07:59:17 +04:00
barwin = createwindow(&scr.root, Rect(-1, -1, 1, 1), scr.depth, InputOutput,
2008-10-14 05:38:03 +04:00
&wa, CWOverrideRedirect
| CWBackPixmap
| CWEventMask);
sethandler(barwin, &handlers);
2008-10-16 07:59:17 +04:00
mapwin(barwin);
int i = 0;
while(!grabkeyboard(barwin)) {
if(i++ > 1000)
fatal("can't grab keyboard");
usleep(1000);
}
2008-10-14 05:38:03 +04:00
}
static void
menu_unmap(long id, void *p) {
USED(id, p);
unmapwin(barwin);
XFlush(display);
}
static void
2008-10-15 00:57:48 +04:00
menu_cmd(int op, int motion) {
int n;
2008-10-14 05:38:03 +04:00
switch(op) {
case HIST:
n = input.pos - input.string;
caret_insert(history_search(motion, input.string, n), true);
input.pos = input.string + n;
2008-10-14 05:38:03 +04:00
break;
2008-10-15 00:57:48 +04:00
case KILL:
caret_delete(BACKWARD, motion);
2008-10-14 05:38:03 +04:00
break;
2008-10-14 08:02:30 +04:00
default:
goto next;
}
update_filter();
next:
switch(op) {
case ACCEPT:
srv.running = false;
2009-05-23 07:33:25 +04:00
if(!matchidx && matchfirst->retstring && !motion)
2009-05-25 23:19:55 +04:00
if(input.filter_start == 0 && input.pos == input.end)
2009-05-23 07:33:25 +04:00
menu_cmd(CMPL_FIRST, 0);
print("%s", input.string);
2008-10-14 08:02:30 +04:00
break;
case REJECT:
srv.running = false;
result = 1;
break;
2008-10-15 00:57:48 +04:00
case BACKWARD:
case FORWARD:
caret_move(op, motion);
break;
2008-10-14 05:38:03 +04:00
case CMPL_NEXT:
2009-05-23 07:33:25 +04:00
matchidx = matchidx ? matchidx->next : matchfirst;
2008-10-14 05:38:03 +04:00
break;
case CMPL_PREV:
2009-05-23 07:33:25 +04:00
if(!matchidx)
matchidx = matchfirst;
2008-10-14 05:38:03 +04:00
matchidx = matchidx->prev;
break;
case CMPL_FIRST:
2008-10-14 08:02:30 +04:00
matchstart = matchfirst;
2009-05-23 07:33:25 +04:00
matchidx = matchstart;
2008-10-14 08:02:30 +04:00
matchend = nil;
2008-10-14 05:38:03 +04:00
break;
case CMPL_LAST:
matchidx = matchfirst->prev;
break;
case CMPL_NEXT_PAGE:
2008-10-14 08:02:30 +04:00
matchidx = matchend->next;
2008-10-14 05:38:03 +04:00
break;
case CMPL_PREV_PAGE:
matchend = matchstart->prev;
2008-10-14 08:02:30 +04:00
matchidx = nil;
2008-10-14 05:38:03 +04:00
break;
}
2009-05-23 07:33:25 +04:00
if(matchidx) {
caret_set(input.filter_start, input.pos - input.string);
caret_insert(matchidx->retstring, 0);
}
2008-10-14 05:38:03 +04:00
menu_draw();
}
void
2008-10-14 05:38:03 +04:00
menu_draw(void) {
2009-03-30 07:01:07 +04:00
Rectangle r, rd, rp, r2;
2008-10-14 05:38:03 +04:00
CTuple *c;
Item *i;
2008-10-14 08:02:30 +04:00
int inputw, itemoff, end, pad, n;
2008-10-14 05:38:03 +04:00
r = barwin->r;
r = rectsetorigin(r, ZP);
2008-10-14 08:02:30 +04:00
pad = (font->height & ~1);
2009-03-30 07:01:07 +04:00
rd = r;
if (prompt) {
if (!promptw)
promptw = textwidth(font, prompt) + 2 * ltwidth;
rd.min.x += promptw;
rp = r;
rp.max.x = promptw;
}
inputw = min(Dx(rd) / 3, maxwidth);
inputw = max(inputw, textwidth(font, input.string)) + pad;
2008-10-14 05:38:03 +04:00
itemoff = inputw + 2 * ltwidth;
2009-03-30 07:01:07 +04:00
end = Dx(rd) - ltwidth;
2008-10-14 05:38:03 +04:00
fill(ibuf, r, cnorm.bg);
2008-10-14 08:02:30 +04:00
if(matchend && matchidx == matchend->next)
matchstart = matchidx;
else if(matchidx == matchstart->prev)
matchend = matchidx;
if(matchend == matchstart->prev && matchstart != matchidx) {
n = itemoff;
matchstart = matchend;
for(i=matchend; ; i=i->prev) {
n += i->width + pad;
if(n > end)
break;
matchstart = i;
if(i == matchfirst)
break;
}
}
2009-03-30 07:01:07 +04:00
r2 = rd;
2008-10-14 05:38:03 +04:00
for(i=matchstart; i->string; i=i->next) {
2009-03-30 07:01:07 +04:00
r2.min.x = promptw + itemoff;
2008-10-14 05:38:03 +04:00
itemoff = itemoff + i->width + pad;
2009-03-30 07:01:07 +04:00
r2.max.x = promptw + min(itemoff, end);
2008-10-14 05:38:03 +04:00
if(i != matchstart && itemoff > end)
break;
c = (i == matchidx) ? &csel : &cnorm;
fill(ibuf, r2, c->bg);
drawstring(ibuf, font, r2, Center, i->string, c->fg);
matchend = i;
if(i->next == matchfirst)
break;
}
2009-03-30 07:01:07 +04:00
r2 = rd;
r2.min.x = promptw + inputw;
2008-10-14 05:38:03 +04:00
if(matchstart != matchfirst)
drawstring(ibuf, font, r2, West, "<", cnorm.fg);
if(matchend->next != matchfirst)
drawstring(ibuf, font, r2, East, ">", cnorm.fg);
2009-05-23 07:33:25 +04:00
2009-03-30 07:01:07 +04:00
r2 = rd;
r2.max.x = promptw + inputw;
2008-10-15 00:57:48 +04:00
drawstring(ibuf, font, r2, West, input.string, cnorm.fg);
2008-10-14 05:38:03 +04:00
2009-03-30 07:01:07 +04:00
r2.min.x = promptw + textwidth_l(font, input.string, input.pos - input.string) + pad/2 - 1;
2008-10-14 05:38:03 +04:00
r2.max.x = r2.min.x + 2;
r2.min.y++;
r2.max.y--;
border(ibuf, r2, 1, cnorm.border);
2009-03-30 07:01:07 +04:00
if (prompt)
drawstring(ibuf, font, rp, West, prompt, cnorm.fg);
border(ibuf, rd, 1, cnorm.border);
2008-10-14 05:38:03 +04:00
copyimage(barwin, r, ibuf, ZP);
}
void
menu_show(void) {
Rectangle r;
int height, pad;
USED(menu_unmap);
2008-10-16 07:59:17 +04:00
ltwidth = textwidth(font, "<");
2008-10-14 05:38:03 +04:00
pad = (font->height & ~1)/2;
height = font->height + 2;
r = scr.rect;
if(ontop)
r.max.y = r.min.y + height;
else
r.min.y = r.max.y - height;
reshapewin(barwin, r);
freeimage(ibuf);
ibuf = allocimage(Dx(r), Dy(r), scr.depth);
mapwin(barwin);
raisewin(barwin);
menu_draw();
}
static void
kdown_event(Window *w, XKeyEvent *e) {
2009-05-14 07:30:20 +04:00
char **action, **p;
char *key;
2008-10-14 05:38:03 +04:00
char buf[32];
2008-10-15 00:57:48 +04:00
int num;
2008-10-14 05:38:03 +04:00
KeySym ksym;
buf[0] = 0;
num = XLookupString(e, buf, sizeof buf, &ksym, 0);
2009-05-14 07:30:20 +04:00
key = XKeysymToString(ksym);
2008-10-14 05:38:03 +04:00
if(IsKeypadKey(ksym))
if(ksym == XK_KP_Enter)
ksym = XK_Return;
else if(ksym >= XK_KP_0 && ksym <= XK_KP_9)
ksym = (ksym - XK_KP_0) + XK_0;
if(IsFunctionKey(ksym)
|| IsMiscFunctionKey(ksym)
2008-10-16 00:08:56 +04:00
|| IsKeypadKey(ksym)
2008-10-16 05:16:14 +04:00
|| IsPrivateKeypadKey(ksym)
|| IsPFKey(ksym))
2008-10-14 05:38:03 +04:00
return;
2009-05-14 07:30:20 +04:00
action = find_key(key, e->state);
if(action == nil || action[0] == nil) {
2008-10-14 05:38:03 +04:00
if(num && !iscntrl(buf[0])) {
2008-10-15 00:57:48 +04:00
caret_insert(buf, false);
2008-10-14 05:38:03 +04:00
update_filter();
menu_draw();
}
2009-05-14 07:30:20 +04:00
}
else {
long mask = 0;
# define have(val) !!(mask & (1 << val))
for(p=action+1; *p; p++)
mask |= 1 << getsym(*p);
int amount = (
have(LCHAR) ? CHAR :
have(LWORD) ? WORD :
have(LLINE) ? LINE :
-1);
switch(getsym(action[0])) {
case LACCEPT:
menu_cmd(ACCEPT, have(LLITERAL));
break;
case LBACKWARD:
menu_cmd(BACKWARD, amount);
break;
case LCOMPLETE:
amount = CMPL_NEXT;
if(have(LNEXT))
amount = CMPL_NEXT;
else if(have(LPREV))
amount = CMPL_PREV;
else if(have(LNEXTPAGE))
amount = CMPL_NEXT_PAGE;
else if(have(LPREVPAGE))
amount = CMPL_PREV_PAGE;
else if(have(LFIRST))
amount = CMPL_FIRST;
else if(have(LLAST))
amount = CMPL_LAST;
menu_cmd(amount, 0);
break;
case LFORWARD:
menu_cmd(FORWARD, amount);
break;
case LHISTORY:
menu_cmd(HIST, have(LBACKWARD) ? BACKWARD : FORWARD);
break;
case LKILL:
menu_cmd(KILL, amount);
break;
case LREJECT:
menu_cmd(REJECT, 0);
break;
}
2008-10-14 05:38:03 +04:00
}
}
static void
expose_event(Window *w, XExposeEvent *e) {
USED(w);
menu_draw();
}
static Handlers handlers = {
.expose = expose_event,
.kdown = kdown_event,
};