aewl
diff gridmenu.c @ 1:f10194d4b76d
added gridmenu
author | Anselm R. Garbe <garbeam@wmii.de> |
---|---|
date | Mon, 10 Jul 2006 16:49:43 +0200 |
parents | |
children | e969f3575b7a |
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/gridmenu.c Mon Jul 10 16:49:43 2006 +0200 1.3 @@ -0,0 +1,498 @@ 1.4 +/* 1.5 + * (C)opyright MMVI Anselm R. Garbe <garbeam at gmail dot com> 1.6 + * (C)opyright MMVI Sander van Dijk <a dot h dot vandijk at gmail dot com> 1.7 + * See LICENSE file for license details. 1.8 + */ 1.9 + 1.10 +#include <ctype.h> 1.11 +#include <stdlib.h> 1.12 +#include <stdio.h> 1.13 +#include <string.h> 1.14 +#include <sys/stat.h> 1.15 +#include <sys/wait.h> 1.16 +#include <time.h> 1.17 +#include <unistd.h> 1.18 +#include <X11/Xlib.h> 1.19 +#include <X11/cursorfont.h> 1.20 +#include <X11/Xutil.h> 1.21 +#include <X11/keysym.h> 1.22 + 1.23 +#include <blitz.h> 1.24 +#include <cext.h> 1.25 + 1.26 +typedef struct Item Item; 1.27 + 1.28 +struct Item { 1.29 + Item *next; /* traverses all items */ 1.30 + Item *left, *right; /* traverses items matching current search pattern */ 1.31 + char *text; 1.32 +}; 1.33 + 1.34 +static char *title = nil; 1.35 +static Bool done = False; 1.36 +static int ret = 0; 1.37 +static char text[4096]; 1.38 +static BlitzColor selcolor; 1.39 +static BlitzColor normcolor; 1.40 +static Window win; 1.41 +static XRectangle mrect; 1.42 +static Item *allitem = nil; /* first of all items */ 1.43 +static Item *item = nil; /* first of pattern matching items */ 1.44 +static Item *sel = nil; 1.45 +static Item *nextoff = nil; 1.46 +static Item *prevoff = nil; 1.47 +static Item *curroff = nil; 1.48 +static int nitem = 0; 1.49 +static unsigned int cmdw = 0; 1.50 +static unsigned int twidth = 0; 1.51 +static unsigned int cwidth = 0; 1.52 +static Blitz blz = {0}; 1.53 +static BlitzBrush brush = {0}; 1.54 +static const int seek = 30; /* 30px */ 1.55 + 1.56 +static void draw_menu(void); 1.57 +static void handle_kpress(XKeyEvent * e); 1.58 + 1.59 +static char version[] = "wmiimenu - " VERSION ", (C)opyright MMIV-MMVI Anselm R. Garbe\n"; 1.60 + 1.61 +static void 1.62 +usage() 1.63 +{ 1.64 + fprintf(stderr, "%s", "usage: wmiimenu [-v] [-t <title>]\n"); 1.65 + exit(1); 1.66 +} 1.67 + 1.68 +static void 1.69 +update_offsets() 1.70 +{ 1.71 + unsigned int tw, w = cmdw + 2 * seek; 1.72 + 1.73 + if(!curroff) 1.74 + return; 1.75 + 1.76 + for(nextoff = curroff; nextoff; nextoff=nextoff->right) { 1.77 + tw = blitz_textwidth(brush.font, nextoff->text); 1.78 + if(tw > mrect.width / 3) 1.79 + tw = mrect.width / 3; 1.80 + w += tw + mrect.height; 1.81 + if(w > mrect.width) 1.82 + break; 1.83 + } 1.84 + 1.85 + w = cmdw + 2 * seek; 1.86 + for(prevoff = curroff; prevoff && prevoff->left; prevoff=prevoff->left) { 1.87 + tw = blitz_textwidth(brush.font, prevoff->left->text); 1.88 + if(tw > mrect.width / 3) 1.89 + tw = mrect.width / 3; 1.90 + w += tw + mrect.height; 1.91 + if(w > mrect.width) 1.92 + break; 1.93 + } 1.94 +} 1.95 + 1.96 +static void 1.97 +update_items(char *pattern) 1.98 +{ 1.99 + unsigned int plen = strlen(pattern); 1.100 + Item *i, *j; 1.101 + 1.102 + if(!pattern) 1.103 + return; 1.104 + 1.105 + if(!title || *pattern) 1.106 + cmdw = cwidth; 1.107 + else 1.108 + cmdw = twidth; 1.109 + 1.110 + item = j = nil; 1.111 + nitem = 0; 1.112 + 1.113 + for(i = allitem; i; i=i->next) 1.114 + if(!plen || !strncmp(pattern, i->text, plen)) { 1.115 + if(!j) 1.116 + item = i; 1.117 + else 1.118 + j->right = i; 1.119 + i->left = j; 1.120 + i->right = nil; 1.121 + j = i; 1.122 + nitem++; 1.123 + } 1.124 + for(i = allitem; i; i=i->next) 1.125 + if(plen && strncmp(pattern, i->text, plen) 1.126 + && strstr(i->text, pattern)) { 1.127 + if(!j) 1.128 + item = i; 1.129 + else 1.130 + j->right = i; 1.131 + i->left = j; 1.132 + i->right = nil; 1.133 + j = i; 1.134 + nitem++; 1.135 + } 1.136 + 1.137 + curroff = prevoff = nextoff = sel = item; 1.138 + 1.139 + update_offsets(); 1.140 +} 1.141 + 1.142 +/* creates brush structs for brush mode drawing */ 1.143 +static void 1.144 +draw_menu() 1.145 +{ 1.146 + unsigned int offx = 0; 1.147 + 1.148 + Item *i; 1.149 + 1.150 + brush.align = WEST; 1.151 + 1.152 + brush.rect = mrect; 1.153 + brush.rect.x = 0; 1.154 + brush.rect.y = 0; 1.155 + brush.color = normcolor; 1.156 + brush.border = False; 1.157 + blitz_draw_tile(&brush); 1.158 + 1.159 + /* print command */ 1.160 + if(!title || text[0]) { 1.161 + brush.color = normcolor; 1.162 + cmdw = cwidth; 1.163 + if(cmdw && item) 1.164 + brush.rect.width = cmdw; 1.165 + blitz_draw_label(&brush, text); 1.166 + } 1.167 + else { 1.168 + cmdw = twidth; 1.169 + brush.color = selcolor; 1.170 + brush.rect.width = cmdw; 1.171 + blitz_draw_label(&brush, title); 1.172 + } 1.173 + offx += brush.rect.width; 1.174 + 1.175 + brush.align = CENTER; 1.176 + if(curroff) { 1.177 + brush.color = normcolor; 1.178 + brush.rect.x = offx; 1.179 + brush.rect.width = seek; 1.180 + offx += brush.rect.width; 1.181 + blitz_draw_label(&brush, (curroff && curroff->left) ? "<" : nil); 1.182 + 1.183 + /* determine maximum items */ 1.184 + for(i = curroff; i != nextoff; i=i->right) { 1.185 + brush.color = normcolor; 1.186 + brush.border = False; 1.187 + brush.rect.x = offx; 1.188 + brush.rect.width = blitz_textwidth(brush.font, i->text); 1.189 + if(brush.rect.width > mrect.width / 3) 1.190 + brush.rect.width = mrect.width / 3; 1.191 + brush.rect.width += mrect.height; 1.192 + if(sel == i) { 1.193 + brush.color = selcolor; 1.194 + brush.border = True; 1.195 + } 1.196 + blitz_draw_label(&brush, i->text); 1.197 + offx += brush.rect.width; 1.198 + } 1.199 + 1.200 + brush.color = normcolor; 1.201 + brush.border = False; 1.202 + brush.rect.x = mrect.width - seek; 1.203 + brush.rect.width = seek; 1.204 + blitz_draw_label(&brush, nextoff ? ">" : nil); 1.205 + } 1.206 + XCopyArea(blz.dpy, brush.drawable, win, brush.gc, 0, 0, mrect.width, 1.207 + mrect.height, 0, 0); 1.208 + XSync(blz.dpy, False); 1.209 +} 1.210 + 1.211 +static void 1.212 +handle_kpress(XKeyEvent * e) 1.213 +{ 1.214 + KeySym ksym; 1.215 + char buf[32]; 1.216 + int num, prev_nitem; 1.217 + unsigned int i, len = strlen(text); 1.218 + 1.219 + buf[0] = 0; 1.220 + num = XLookupString(e, buf, sizeof(buf), &ksym, 0); 1.221 + 1.222 + if(IsFunctionKey(ksym) || IsKeypadKey(ksym) 1.223 + || IsMiscFunctionKey(ksym) || IsPFKey(ksym) 1.224 + || IsPrivateKeypadKey(ksym)) 1.225 + return; 1.226 + 1.227 + /* first check if a control mask is omitted */ 1.228 + if(e->state & ControlMask) { 1.229 + switch (ksym) { 1.230 + case XK_H: 1.231 + case XK_h: 1.232 + ksym = XK_BackSpace; 1.233 + break; 1.234 + case XK_I: 1.235 + case XK_i: 1.236 + ksym = XK_Tab; 1.237 + break; 1.238 + case XK_J: 1.239 + case XK_j: 1.240 + ksym = XK_Return; 1.241 + break; 1.242 + case XK_N: 1.243 + case XK_n: 1.244 + ksym = XK_Right; 1.245 + break; 1.246 + case XK_P: 1.247 + case XK_p: 1.248 + ksym = XK_Left; 1.249 + break; 1.250 + case XK_U: 1.251 + case XK_u: 1.252 + text[0] = 0; 1.253 + update_items(text); 1.254 + draw_menu(); 1.255 + return; 1.256 + break; 1.257 + case XK_bracketleft: 1.258 + ksym = XK_Escape; 1.259 + break; 1.260 + default: /* ignore other control sequences */ 1.261 + return; 1.262 + break; 1.263 + } 1.264 + } 1.265 + switch (ksym) { 1.266 + case XK_Left: 1.267 + if(!(sel && sel->left)) 1.268 + return; 1.269 + sel=sel->left; 1.270 + if(sel->right == curroff) { 1.271 + curroff = prevoff; 1.272 + update_offsets(); 1.273 + } 1.274 + break; 1.275 + case XK_Tab: 1.276 + if(!sel) 1.277 + return; 1.278 + cext_strlcpy(text, sel->text, sizeof(text)); 1.279 + update_items(text); 1.280 + break; 1.281 + case XK_Right: 1.282 + if(!(sel && sel->right)) 1.283 + return; 1.284 + sel=sel->right; 1.285 + if(sel == nextoff) { 1.286 + curroff = nextoff; 1.287 + update_offsets(); 1.288 + } 1.289 + break; 1.290 + case XK_Return: 1.291 + if(e->state & ShiftMask) { 1.292 + if(text) 1.293 + fprintf(stdout, "%s", text); 1.294 + } 1.295 + else if(sel) 1.296 + fprintf(stdout, "%s", sel->text); 1.297 + else if(text) 1.298 + fprintf(stdout, "%s", text); 1.299 + fflush(stdout); 1.300 + done = True; 1.301 + break; 1.302 + case XK_Escape: 1.303 + ret = 1; 1.304 + done = True; 1.305 + break; 1.306 + case XK_BackSpace: 1.307 + if((i = len)) { 1.308 + prev_nitem = nitem; 1.309 + do { 1.310 + text[--i] = 0; 1.311 + update_items(text); 1.312 + } while(i && nitem && prev_nitem == nitem); 1.313 + update_items(text); 1.314 + } 1.315 + break; 1.316 + default: 1.317 + if((num == 1) && !iscntrl((int) buf[0])) { 1.318 + buf[num] = 0; 1.319 + if(len > 0) 1.320 + cext_strlcat(text, buf, sizeof(text)); 1.321 + else 1.322 + cext_strlcpy(text, buf, sizeof(text)); 1.323 + update_items(text); 1.324 + } 1.325 + } 1.326 + draw_menu(); 1.327 +} 1.328 + 1.329 +static char * 1.330 +read_allitems() 1.331 +{ 1.332 + static char *maxname = nil; 1.333 + char *p, buf[1024]; 1.334 + unsigned int len = 0, max = 0; 1.335 + Item *i, *new; 1.336 + 1.337 + i = nil; 1.338 + while(fgets(buf, sizeof(buf), stdin)) { 1.339 + len = strlen(buf); 1.340 + if (buf[len - 1] == '\n') 1.341 + buf[len - 1] = 0; 1.342 + p = cext_estrdup(buf); 1.343 + if(max < len) { 1.344 + maxname = p; 1.345 + max = len; 1.346 + } 1.347 + 1.348 + new = cext_emalloc(sizeof(Item)); 1.349 + new->next = new->left = new->right = nil; 1.350 + new->text = p; 1.351 + if(!i) 1.352 + allitem = new; 1.353 + else 1.354 + i->next = new; 1.355 + i = new; 1.356 + } 1.357 + 1.358 + return maxname; 1.359 +} 1.360 + 1.361 +int 1.362 +main(int argc, char *argv[]) 1.363 +{ 1.364 + int i; 1.365 + XSetWindowAttributes wa; 1.366 + char *maxname, *p; 1.367 + BlitzFont font = {0}; 1.368 + GC gc; 1.369 + Drawable pmap; 1.370 + XEvent ev; 1.371 + 1.372 + /* command line args */ 1.373 + for(i = 1; i < argc; i++) { 1.374 + if (argv[i][0] == '-') 1.375 + switch (argv[i][1]) { 1.376 + case 'v': 1.377 + fprintf(stdout, "%s", version); 1.378 + exit(0); 1.379 + break; 1.380 + case 't': 1.381 + if(++i < argc) 1.382 + title = argv[i]; 1.383 + else 1.384 + usage(); 1.385 + break; 1.386 + default: 1.387 + usage(); 1.388 + break; 1.389 + } 1.390 + else 1.391 + usage(); 1.392 + } 1.393 + 1.394 + blz.dpy = XOpenDisplay(0); 1.395 + if(!blz.dpy) { 1.396 + fprintf(stderr, "%s", "wmiimenu: cannot open dpy\n"); 1.397 + exit(1); 1.398 + } 1.399 + blz.screen = DefaultScreen(blz.dpy); 1.400 + blz.root = RootWindow(blz.dpy, blz.screen); 1.401 + 1.402 + maxname = read_allitems(); 1.403 + 1.404 + /* grab as early as possible, but after reading all items!!! */ 1.405 + while(XGrabKeyboard 1.406 + (blz.dpy, blz.root, True, GrabModeAsync, 1.407 + GrabModeAsync, CurrentTime) != GrabSuccess) 1.408 + usleep(1000); 1.409 + 1.410 + font.fontstr = getenv("WMII_FONT"); 1.411 + if (!font.fontstr) 1.412 + font.fontstr = cext_estrdup(BLITZ_FONT); 1.413 + blitz_loadfont(&blz, &font); 1.414 + 1.415 + if((p = getenv("WMII_NORMCOLORS"))) 1.416 + cext_strlcpy(normcolor.colstr, p, sizeof(normcolor.colstr)); 1.417 + if(strlen(normcolor.colstr) != 23) 1.418 + cext_strlcpy(normcolor.colstr, BLITZ_NORMCOLORS, sizeof(normcolor.colstr)); 1.419 + blitz_loadcolor(&blz, &normcolor); 1.420 + 1.421 + if((p = getenv("WMII_SELCOLORS"))) 1.422 + cext_strlcpy(selcolor.colstr, p, sizeof(selcolor.colstr)); 1.423 + if(strlen(selcolor.colstr) != 23) 1.424 + cext_strlcpy(selcolor.colstr, BLITZ_SELCOLORS, sizeof(selcolor.colstr)); 1.425 + blitz_loadcolor(&blz, &selcolor); 1.426 + 1.427 + wa.override_redirect = 1; 1.428 + wa.background_pixmap = ParentRelative; 1.429 + wa.event_mask = ExposureMask | ButtonPressMask | KeyPressMask 1.430 + | SubstructureRedirectMask | SubstructureNotifyMask; 1.431 + 1.432 + mrect.width = DisplayWidth(blz.dpy, blz.screen); 1.433 + mrect.height = font.ascent + font.descent + 4; 1.434 + mrect.y = DisplayHeight(blz.dpy, blz.screen) - mrect.height; 1.435 + mrect.x = 0; 1.436 + 1.437 + win = XCreateWindow(blz.dpy, blz.root, mrect.x, mrect.y, 1.438 + mrect.width, mrect.height, 0, DefaultDepth(blz.dpy, blz.screen), 1.439 + CopyFromParent, DefaultVisual(blz.dpy, blz.screen), 1.440 + CWOverrideRedirect | CWBackPixmap | CWEventMask, &wa); 1.441 + XDefineCursor(blz.dpy, win, XCreateFontCursor(blz.dpy, XC_xterm)); 1.442 + XSync(blz.dpy, False); 1.443 + 1.444 + /* pixmap */ 1.445 + gc = XCreateGC(blz.dpy, win, 0, 0); 1.446 + pmap = XCreatePixmap(blz.dpy, win, mrect.width, mrect.height, 1.447 + DefaultDepth(blz.dpy, blz.screen)); 1.448 + 1.449 + XSync(blz.dpy, False); 1.450 + 1.451 + brush.blitz = &blz; 1.452 + brush.color = normcolor; 1.453 + brush.drawable = pmap; 1.454 + brush.gc = gc; 1.455 + brush.font = &font; 1.456 + 1.457 + if(maxname) 1.458 + cwidth = blitz_textwidth(brush.font, maxname) + mrect.height; 1.459 + if(cwidth > mrect.width / 3) 1.460 + cwidth = mrect.width / 3; 1.461 + 1.462 + if(title) { 1.463 + twidth = blitz_textwidth(brush.font, title) + mrect.height; 1.464 + if(twidth > mrect.width / 3) 1.465 + twidth = mrect.width / 3; 1.466 + } 1.467 + 1.468 + cmdw = title ? twidth : cwidth; 1.469 + 1.470 + text[0] = 0; 1.471 + update_items(text); 1.472 + XMapRaised(blz.dpy, win); 1.473 + draw_menu(); 1.474 + XSync(blz.dpy, False); 1.475 + 1.476 + /* main event loop */ 1.477 + while(!XNextEvent(blz.dpy, &ev)) { 1.478 + switch (ev.type) { 1.479 + case KeyPress: 1.480 + handle_kpress(&ev.xkey); 1.481 + break; 1.482 + case Expose: 1.483 + if(ev.xexpose.count == 0) { 1.484 + draw_menu(); 1.485 + } 1.486 + break; 1.487 + default: 1.488 + break; 1.489 + } 1.490 + if(done) 1.491 + break; 1.492 + } 1.493 + 1.494 + XUngrabKeyboard(blz.dpy, CurrentTime); 1.495 + XFreePixmap(blz.dpy, pmap); 1.496 + XFreeGC(blz.dpy, gc); 1.497 + XDestroyWindow(blz.dpy, win); 1.498 + XCloseDisplay(blz.dpy); 1.499 + 1.500 + return ret; 1.501 +}