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 +}