wmii/cmd/menu/menu.c

351 lines
6.8 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;
2009-05-27 21:22:24 +04:00
static void _menu_draw(bool);
2008-10-14 05:38:03 +04:00
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.event_mask = ExposureMask | KeyPressMask;
2008-10-16 07:59:17 +04:00
barwin = createwindow(&scr.root, Rect(-1, -1, 1, 1), scr.depth, InputOutput,
&wa, CWEventMask);
changeprop_long(barwin, Net("WM_WINDOW_TYPE"), "ATOM",
(long[]){ TYPE("MENU") }, 1);
changeprop_string(barwin, "_WMII_TAGS", "sel");
changeprop_textlist(barwin, "WM_CLASS", "STRING",
(char*[3]){ "wimenu", "wimenu", nil });
2008-10-14 05:38:03 +04:00
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);
}
2009-05-27 21:22:24 +04:00
static void
selectitem(Item *i) {
if(i != matchidx) {
caret_set(input.filter_start, input.pos - input.string);
2009-10-17 11:10:37 +04:00
caret_insert(i->string, 0);
2009-05-27 21:22:24 +04:00
matchidx = i;
}
}
2008-10-14 05:38:03 +04:00
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;
}
2009-05-30 02:54:38 +04:00
update_filter(true);
2008-10-14 08:02:30 +04:00
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);
2009-10-17 11:10:37 +04:00
if(!motion && matchidx && !strcmp(input.string, matchidx->string))
2010-06-06 13:07:24 +04:00
lprint(1, "%s", matchidx->retstring);
2009-10-17 11:10:37 +04:00
else
2010-06-06 13:07:24 +04:00
lprint(1, "%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);
update_input();
2008-10-15 00:57:48 +04:00
break;
2008-10-14 05:38:03 +04:00
case CMPL_NEXT:
2009-05-27 21:22:24 +04:00
selectitem(matchidx ? matchidx->next : matchfirst);
2008-10-14 05:38:03 +04:00
break;
case CMPL_PREV:
2009-05-27 21:22:24 +04:00
selectitem((matchidx ? matchidx : matchstart)->prev);
2008-10-14 05:38:03 +04:00
break;
case CMPL_FIRST:
2008-10-14 08:02:30 +04:00
matchstart = matchfirst;
matchend = nil;
2009-05-27 21:22:24 +04:00
selectitem(matchstart);
2008-10-14 05:38:03 +04:00
break;
case CMPL_LAST:
2009-05-27 21:22:24 +04:00
selectitem(matchfirst->prev);
2008-10-14 05:38:03 +04:00
break;
case CMPL_NEXT_PAGE:
2009-05-27 21:22:24 +04:00
if(matchend)
selectitem(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;
2009-05-27 21:22:24 +04:00
_menu_draw(false);
selectitem(matchstart);
2008-10-14 05:38:03 +04:00
break;
}
menu_draw();
}
2009-05-27 21:22:24 +04:00
static void
_menu_draw(bool draw) {
2009-10-30 10:06:14 +03:00
Rectangle r, rd, rp, r2, extent;
2008-10-14 05:38:03 +04:00
CTuple *c;
Item *i;
2009-10-30 10:06:14 +03:00
int inputw, itemoff, end, pad, n, offset;
2008-10-14 05:38:03 +04:00
r = barwin->r;
r = rectsetorigin(r, ZP);
2009-10-30 10:06:14 +03:00
pad = (font->height & ~1) + font->pad.min.x + font->pad.max.x;
2009-03-30 07:01:07 +04:00
rd = r;
2009-10-02 00:42:54 +04:00
rp = ZR; // SET(rp)
2009-03-30 07:01:07 +04:00
if (prompt) {
if (!promptw)
promptw = textwidth(font, prompt) + 2 * ltwidth + pad;
2009-03-30 07:01:07 +04:00
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
2010-06-20 22:24:04 +04:00
fill(ibuf, r, &cnorm.bg);
2008-10-14 05:38:03 +04:00
2008-10-14 08:02:30 +04:00
if(matchend && matchidx == matchend->next)
matchstart = matchidx;
else if(matchidx == matchstart->prev)
matchend = matchidx;
if (matchend == nil)
matchend = matchstart;
2008-10-14 08:02:30 +04:00
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-05-27 21:22:24 +04:00
if(!draw)
return;
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;
2010-06-20 22:24:04 +04:00
fill(ibuf, r2, &c->bg);
drawstring(ibuf, font, r2, Center, i->string, &c->fg);
2008-10-14 05:38:03 +04:00
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)
2010-06-20 22:24:04 +04:00
drawstring(ibuf, font, r2, West, "<", &cnorm.fg);
2008-10-14 05:38:03 +04:00
if(matchend->next != matchfirst)
2010-06-20 22:24:04 +04:00
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;
2010-06-20 22:24:04 +04:00
drawstring(ibuf, font, r2, West, input.string, &cnorm.fg);
2008-10-14 05:38:03 +04:00
2009-10-30 10:06:14 +03:00
extent = textextents_l(font, input.string, input.pos - input.string, &offset);
r2.min.x = promptw + offset + font->pad.min.x - extent.min.x + pad/2 - 1;
2008-10-14 05:38:03 +04:00
r2.max.x = r2.min.x + 2;
r2.min.y++;
r2.max.y--;
2010-06-20 22:24:04 +04:00
border(ibuf, r2, 1, &cnorm.border);
2008-10-14 05:38:03 +04:00
2009-03-30 07:01:07 +04:00
if (prompt)
2010-06-20 22:24:04 +04:00
drawstring(ibuf, font, rp, West, prompt, &cnorm.fg);
2009-03-30 07:01:07 +04:00
2010-06-20 22:24:04 +04:00
border(ibuf, rd, 1, &cnorm.border);
2008-10-14 05:38:03 +04:00
copyimage(barwin, r, ibuf, ZP);
}
2009-05-27 21:22:24 +04:00
void
menu_draw(void) {
_menu_draw(true);
}
2008-10-14 05:38:03 +04:00
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;
2009-10-30 10:06:14 +03:00
height = labelh(font);
2008-10-14 05:38:03 +04:00
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();
}
2010-05-28 02:57:08 +04:00
static bool
2010-05-27 11:58:02 +04:00
kdown_event(Window *w, void *aux, 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';
2008-10-14 05:38:03 +04:00
num = XLookupString(e, buf, sizeof buf, &ksym, 0);
buf[num] = '\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))
2010-05-28 02:57:08 +04:00
return false;
2008-10-14 05:38:03 +04:00
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);
2009-05-30 02:54:38 +04:00
update_filter(true);
2008-10-14 05:38:03 +04:00
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 = (
have(LNEXT) ? CMPL_NEXT :
have(LPREV) ? CMPL_PREV :
have(LNEXTPAGE) ? CMPL_NEXT_PAGE :
have(LPREVPAGE) ? CMPL_PREV_PAGE :
have(LFIRST) ? CMPL_FIRST :
have(LLAST) ? CMPL_LAST :
CMPL_NEXT);
2009-05-14 07:30:20 +04:00
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
}
2010-05-28 02:57:08 +04:00
return false;
2008-10-14 05:38:03 +04:00
}
2010-05-28 02:57:08 +04:00
static bool
2010-05-27 11:58:02 +04:00
expose_event(Window *w, void *aux, XExposeEvent *e) {
2008-10-14 05:38:03 +04:00
USED(w);
menu_draw();
2010-05-28 02:57:08 +04:00
return false;
2008-10-14 05:38:03 +04:00
}
static Handlers handlers = {
.expose = expose_event,
.kdown = kdown_event,
};