aewl

changeset 4:991bd8b0771e

renamed gridmenu.c into menu.c
author Anselm R. Garbe <garbeam@wmii.de>
date Mon, 10 Jul 2006 19:47:35 +0200
parents e969f3575b7a
children e5018cae273f
files Makefile menu.c
diffstat 3 files changed, 466 insertions(+), 466 deletions(-) [+]
line diff
     1.1 --- a/Makefile	Mon Jul 10 19:46:24 2006 +0200
     1.2 +++ b/Makefile	Mon Jul 10 19:47:35 2006 +0200
     1.3 @@ -5,7 +5,7 @@
     1.4  
     1.5  WMSRC = wm.c draw.c util.c
     1.6  WMOBJ = ${WMSRC:.c=.o}
     1.7 -MENSRC = gridmenu.c draw.c util.c
     1.8 +MENSRC = menu.c draw.c util.c
     1.9  MENOBJ = ${MENSRC:.c=.o}
    1.10  MAN = gridwm.1
    1.11  BIN = gridwm gridmenu     
     2.1 --- a/gridmenu.c	Mon Jul 10 19:46:24 2006 +0200
     2.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     2.3 @@ -1,465 +0,0 @@
     2.4 -/*
     2.5 - * (C)opyright MMVI Anselm R. Garbe <garbeam at gmail dot com>
     2.6 - * (C)opyright MMVI Sander van Dijk <a dot h dot vandijk at gmail dot com>
     2.7 - * See LICENSE file for license details.
     2.8 - */
     2.9 -
    2.10 -#include "config.h"
    2.11 -#include "draw.h"
    2.12 -#include "util.h"
    2.13 -
    2.14 -#include <ctype.h>
    2.15 -#include <stdlib.h>
    2.16 -#include <stdio.h>
    2.17 -#include <string.h>
    2.18 -#include <sys/stat.h>
    2.19 -#include <sys/wait.h>
    2.20 -#include <time.h>
    2.21 -#include <unistd.h>
    2.22 -#include <X11/cursorfont.h>
    2.23 -#include <X11/Xutil.h>
    2.24 -#include <X11/keysym.h>
    2.25 -
    2.26 -typedef struct Item Item;
    2.27 -
    2.28 -struct Item {
    2.29 -	Item *next;		/* traverses all items */
    2.30 -	Item *left, *right;	/* traverses items matching current search pattern */
    2.31 -	char *text;
    2.32 -};
    2.33 -
    2.34 -static Display *dpy;
    2.35 -static Window root;
    2.36 -static Window win;
    2.37 -static XRectangle rect;
    2.38 -static Bool done = False;
    2.39 -
    2.40 -static Item *allitem = 0;	/* first of all items */
    2.41 -static Item *item = 0;	/* first of pattern matching items */
    2.42 -static Item *sel = 0;
    2.43 -static Item *nextoff = 0;
    2.44 -static Item *prevoff = 0;
    2.45 -static Item *curroff = 0;
    2.46 -
    2.47 -static int screen;
    2.48 -static char *title = 0;
    2.49 -static char text[4096];
    2.50 -static int ret = 0;
    2.51 -static int nitem = 0;
    2.52 -static unsigned int cmdw = 0;
    2.53 -static unsigned int twidth = 0;
    2.54 -static unsigned int cwidth = 0;
    2.55 -static const int seek = 30;		/* 30px */
    2.56 -
    2.57 -static Brush brush = {0};
    2.58 -
    2.59 -static void draw_menu(void);
    2.60 -static void kpress(XKeyEvent * e);
    2.61 -
    2.62 -static char version[] = "gridmenu - " VERSION ", (C)opyright MMVI Anselm R. Garbe\n";
    2.63 -
    2.64 -static void
    2.65 -usage()
    2.66 -{
    2.67 -	fprintf(stderr, "%s", "usage: gridmenu [-v] [-t <title>]\n");
    2.68 -	exit(1);
    2.69 -}
    2.70 -
    2.71 -static void
    2.72 -update_offsets()
    2.73 -{
    2.74 -	unsigned int tw, w = cmdw + 2 * seek;
    2.75 -
    2.76 -	if(!curroff)
    2.77 -		return;
    2.78 -
    2.79 -	for(nextoff = curroff; nextoff; nextoff=nextoff->right) {
    2.80 -		tw = textwidth(&brush.font, nextoff->text);
    2.81 -		if(tw > rect.width / 3)
    2.82 -			tw = rect.width / 3;
    2.83 -		w += tw + brush.font.height;
    2.84 -		if(w > rect.width)
    2.85 -			break;
    2.86 -	}
    2.87 -
    2.88 -	w = cmdw + 2 * seek;
    2.89 -	for(prevoff = curroff; prevoff && prevoff->left; prevoff=prevoff->left) {
    2.90 -		tw = textwidth(&brush.font, prevoff->left->text);
    2.91 -		if(tw > rect.width / 3)
    2.92 -			tw = rect.width / 3;
    2.93 -		w += tw + brush.font.height;
    2.94 -		if(w > rect.width)
    2.95 -			break;
    2.96 -	}
    2.97 -}
    2.98 -
    2.99 -static void
   2.100 -update_items(char *pattern)
   2.101 -{
   2.102 -	unsigned int plen = strlen(pattern);
   2.103 -	Item *i, *j;
   2.104 -
   2.105 -	if(!pattern)
   2.106 -		return;
   2.107 -
   2.108 -	if(!title || *pattern)
   2.109 -		cmdw = cwidth;
   2.110 -	else
   2.111 -		cmdw = twidth;
   2.112 -
   2.113 -	item = j = 0;
   2.114 -	nitem = 0;
   2.115 -
   2.116 -	for(i = allitem; i; i=i->next)
   2.117 -		if(!plen || !strncmp(pattern, i->text, plen)) {
   2.118 -			if(!j)
   2.119 -				item = i;
   2.120 -			else
   2.121 -				j->right = i;
   2.122 -			i->left = j;
   2.123 -			i->right = 0;
   2.124 -			j = i;
   2.125 -			nitem++;
   2.126 -		}
   2.127 -	for(i = allitem; i; i=i->next)
   2.128 -		if(plen && strncmp(pattern, i->text, plen)
   2.129 -				&& strstr(i->text, pattern)) {
   2.130 -			if(!j)
   2.131 -				item = i;
   2.132 -			else
   2.133 -				j->right = i;
   2.134 -			i->left = j;
   2.135 -			i->right = 0;
   2.136 -			j = i;
   2.137 -			nitem++;
   2.138 -		}
   2.139 -
   2.140 -	curroff = prevoff = nextoff = sel = item;
   2.141 -
   2.142 -	update_offsets();
   2.143 -}
   2.144 -
   2.145 -/* creates brush structs for brush mode drawing */
   2.146 -static void
   2.147 -draw_menu()
   2.148 -{
   2.149 -	unsigned int offx = 0;
   2.150 -	Item *i;
   2.151 -
   2.152 -	brush.rect = rect;
   2.153 -	brush.rect.x = 0;
   2.154 -	brush.rect.y = 0;
   2.155 -	draw(dpy, &brush, False, 0);
   2.156 -
   2.157 -	/* print command */
   2.158 -	if(!title || text[0]) {
   2.159 -		cmdw = cwidth;
   2.160 -		if(cmdw && item)
   2.161 -			brush.rect.width = cmdw;
   2.162 -		draw(dpy, &brush, False, text);
   2.163 -	}
   2.164 -	else {
   2.165 -		cmdw = twidth;
   2.166 -		brush.rect.width = cmdw;
   2.167 -		draw(dpy, &brush, False, title);
   2.168 -	}
   2.169 -	offx += brush.rect.width;
   2.170 -
   2.171 -	if(curroff) {
   2.172 -		brush.rect.x = offx;
   2.173 -		brush.rect.width = seek;
   2.174 -		offx += brush.rect.width;
   2.175 -		draw(dpy, &brush, False, (curroff && curroff->left) ? "<" : 0);
   2.176 -
   2.177 -		/* determine maximum items */
   2.178 -		for(i = curroff; i != nextoff; i=i->right) {
   2.179 -			brush.border = False;
   2.180 -			brush.rect.x = offx;
   2.181 -			brush.rect.width = textwidth(&brush.font, i->text);
   2.182 -			if(brush.rect.width > rect.width / 3)
   2.183 -				brush.rect.width = rect.width / 3;
   2.184 -			brush.rect.width += brush.font.height;
   2.185 -			if(sel == i) {
   2.186 -				swap((void **)&brush.fg, (void **)&brush.bg);
   2.187 -				draw(dpy, &brush, True, i->text);
   2.188 -				swap((void **)&brush.fg, (void **)&brush.bg);
   2.189 -			}
   2.190 -			else
   2.191 -				draw(dpy, &brush, False, i->text);
   2.192 -			offx += brush.rect.width;
   2.193 -		}
   2.194 -
   2.195 -		brush.rect.x = rect.width - seek;
   2.196 -		brush.rect.width = seek;
   2.197 -		draw(dpy, &brush, False, nextoff ? ">" : 0);
   2.198 -	}
   2.199 -	XCopyArea(dpy, brush.drawable, win, brush.gc, 0, 0, rect.width,
   2.200 -			rect.height, 0, 0);
   2.201 -	XFlush(dpy);
   2.202 -}
   2.203 -
   2.204 -static void
   2.205 -kpress(XKeyEvent * e)
   2.206 -{
   2.207 -	KeySym ksym;
   2.208 -	char buf[32];
   2.209 -	int num, prev_nitem;
   2.210 -	unsigned int i, len = strlen(text);
   2.211 -
   2.212 -	buf[0] = 0;
   2.213 -	num = XLookupString(e, buf, sizeof(buf), &ksym, 0);
   2.214 -
   2.215 -	if(IsFunctionKey(ksym) || IsKeypadKey(ksym)
   2.216 -			|| IsMiscFunctionKey(ksym) || IsPFKey(ksym)
   2.217 -			|| IsPrivateKeypadKey(ksym))
   2.218 -		return;
   2.219 -
   2.220 -	/* first check if a control mask is omitted */
   2.221 -	if(e->state & ControlMask) {
   2.222 -		switch (ksym) {
   2.223 -		case XK_H:
   2.224 -		case XK_h:
   2.225 -			ksym = XK_BackSpace;
   2.226 -			break;
   2.227 -		case XK_I:
   2.228 -		case XK_i:
   2.229 -			ksym = XK_Tab;
   2.230 -			break;
   2.231 -		case XK_J:
   2.232 -		case XK_j:
   2.233 -			ksym = XK_Return;
   2.234 -			break;
   2.235 -		case XK_N:
   2.236 -		case XK_n:
   2.237 -			ksym = XK_Right;
   2.238 -			break;
   2.239 -		case XK_P:
   2.240 -		case XK_p:
   2.241 -			ksym = XK_Left;
   2.242 -			break;
   2.243 -		case XK_U:
   2.244 -		case XK_u:
   2.245 -			text[0] = 0;
   2.246 -			update_items(text);
   2.247 -			draw_menu();
   2.248 -			return;
   2.249 -			break;
   2.250 -		case XK_bracketleft:
   2.251 -			ksym = XK_Escape;
   2.252 -			break;
   2.253 -		default:	/* ignore other control sequences */
   2.254 -			return;
   2.255 -			break;
   2.256 -		}
   2.257 -	}
   2.258 -	switch (ksym) {
   2.259 -	case XK_Left:
   2.260 -		if(!(sel && sel->left))
   2.261 -			return;
   2.262 -		sel=sel->left;
   2.263 -		if(sel->right == curroff) {
   2.264 -			curroff = prevoff;
   2.265 -			update_offsets();
   2.266 -		}
   2.267 -		break;
   2.268 -	case XK_Tab:
   2.269 -		if(!sel)
   2.270 -			return;
   2.271 -		strncpy(text, sel->text, sizeof(text));
   2.272 -		update_items(text);
   2.273 -		break;
   2.274 -	case XK_Right:
   2.275 -		if(!(sel && sel->right))
   2.276 -			return;
   2.277 -		sel=sel->right;
   2.278 -		if(sel == nextoff) {
   2.279 -			curroff = nextoff;
   2.280 -			update_offsets();
   2.281 -		}
   2.282 -		break;
   2.283 -	case XK_Return:
   2.284 -		if(e->state & ShiftMask) {
   2.285 -			if(text)
   2.286 -				fprintf(stdout, "%s", text);
   2.287 -		}
   2.288 -		else if(sel)
   2.289 -			fprintf(stdout, "%s", sel->text);
   2.290 -		else if(text)
   2.291 -			fprintf(stdout, "%s", text);
   2.292 -		fflush(stdout);
   2.293 -		done = True;
   2.294 -		break;
   2.295 -	case XK_Escape:
   2.296 -		ret = 1;
   2.297 -		done = True;
   2.298 -		break;
   2.299 -	case XK_BackSpace:
   2.300 -		if((i = len)) {
   2.301 -			prev_nitem = nitem;
   2.302 -			do {
   2.303 -				text[--i] = 0;
   2.304 -				update_items(text);
   2.305 -			} while(i && nitem && prev_nitem == nitem);
   2.306 -			update_items(text);
   2.307 -		}
   2.308 -		break;
   2.309 -	default:
   2.310 -		if((num == 1) && !iscntrl((int) buf[0])) {
   2.311 -			buf[num] = 0;
   2.312 -			if(len > 0)
   2.313 -				strncat(text, buf, sizeof(text));
   2.314 -			else
   2.315 -				strncpy(text, buf, sizeof(text));
   2.316 -			update_items(text);
   2.317 -		}
   2.318 -	}
   2.319 -	draw_menu();
   2.320 -}
   2.321 -
   2.322 -static char *
   2.323 -read_allitems()
   2.324 -{
   2.325 -	static char *maxname = 0;
   2.326 -	char *p, buf[1024];
   2.327 -	unsigned int len = 0, max = 0;
   2.328 -	Item *i, *new;
   2.329 -
   2.330 -	i = 0;
   2.331 -	while(fgets(buf, sizeof(buf), stdin)) {
   2.332 -		len = strlen(buf);
   2.333 -		if (buf[len - 1] == '\n')
   2.334 -			buf[len - 1] = 0;
   2.335 -		p = estrdup(buf);
   2.336 -		if(max < len) {
   2.337 -			maxname = p;
   2.338 -			max = len;
   2.339 -		}
   2.340 -
   2.341 -		new = emalloc(sizeof(Item));
   2.342 -		new->next = new->left = new->right = 0;
   2.343 -		new->text = p;
   2.344 -		if(!i)
   2.345 -			allitem = new;
   2.346 -		else 
   2.347 -			i->next = new;
   2.348 -		i = new;
   2.349 -	}
   2.350 -
   2.351 -	return maxname;
   2.352 -}
   2.353 -
   2.354 -int
   2.355 -main(int argc, char *argv[])
   2.356 -{
   2.357 -	int i;
   2.358 -	XSetWindowAttributes wa;
   2.359 -	char *maxname;
   2.360 -	XEvent ev;
   2.361 -
   2.362 -	/* command line args */
   2.363 -	for(i = 1; i < argc; i++) {
   2.364 -		if (argv[i][0] == '-')
   2.365 -			switch (argv[i][1]) {
   2.366 -			case 'v':
   2.367 -				fprintf(stdout, "%s", version);
   2.368 -				exit(0);
   2.369 -				break;
   2.370 -			case 't':
   2.371 -				if(++i < argc)
   2.372 -					title = argv[i];
   2.373 -				else
   2.374 -					usage();
   2.375 -				break;
   2.376 -			default:
   2.377 -				usage();
   2.378 -				break;
   2.379 -			}
   2.380 -		else
   2.381 -			usage();
   2.382 -	}
   2.383 -
   2.384 -	dpy = XOpenDisplay(0);
   2.385 -	if(!dpy)
   2.386 -		error("gridmenu: cannot open dpy\n");
   2.387 -	screen = DefaultScreen(dpy);
   2.388 -	root = RootWindow(dpy, screen);
   2.389 -
   2.390 -	maxname = read_allitems();
   2.391 -
   2.392 -	/* grab as early as possible, but after reading all items!!! */
   2.393 -	while(XGrabKeyboard(dpy, root, True, GrabModeAsync,
   2.394 -			 GrabModeAsync, CurrentTime) != GrabSuccess)
   2.395 -		usleep(1000);
   2.396 -
   2.397 -	/* style */
   2.398 -	loadcolors(dpy, screen, &brush, BGCOLOR, FGCOLOR, BORDERCOLOR);
   2.399 -	loadfont(dpy, &brush.font, FONT);
   2.400 -
   2.401 -	wa.override_redirect = 1;
   2.402 -	wa.background_pixmap = ParentRelative;
   2.403 -	wa.event_mask = ExposureMask | ButtonPressMask | KeyPressMask
   2.404 -		| SubstructureRedirectMask | SubstructureNotifyMask;
   2.405 -
   2.406 -	rect.width = DisplayWidth(dpy, screen);
   2.407 -	rect.height = brush.font.height + 4;
   2.408 -	rect.y = DisplayHeight(dpy, screen) - rect.height;
   2.409 -	rect.x = 0;
   2.410 -
   2.411 -	win = XCreateWindow(dpy, root, rect.x, rect.y,
   2.412 -			rect.width, rect.height, 0, DefaultDepth(dpy, screen),
   2.413 -			CopyFromParent, DefaultVisual(dpy, screen),
   2.414 -			CWOverrideRedirect | CWBackPixmap | CWEventMask, &wa);
   2.415 -	XDefineCursor(dpy, win, XCreateFontCursor(dpy, XC_xterm));
   2.416 -	XFlush(dpy);
   2.417 -
   2.418 -	/* pixmap */
   2.419 -	brush.gc = XCreateGC(dpy, win, 0, 0);
   2.420 -	brush.drawable = XCreatePixmap(dpy, win, rect.width, rect.height,
   2.421 -			DefaultDepth(dpy, screen));
   2.422 -	XFlush(dpy);
   2.423 -
   2.424 -	if(maxname)
   2.425 -		cwidth = textwidth(&brush.font, maxname) + brush.font.height;
   2.426 -	if(cwidth > rect.width / 3)
   2.427 -		cwidth = rect.width / 3;
   2.428 -
   2.429 -	if(title) {
   2.430 -		twidth = textwidth(&brush.font, title) + brush.font.height;
   2.431 -		if(twidth > rect.width / 3)
   2.432 -			twidth = rect.width / 3;
   2.433 -	}
   2.434 -
   2.435 -	cmdw = title ? twidth : cwidth;
   2.436 -
   2.437 -	text[0] = 0;
   2.438 -	update_items(text);
   2.439 -	XMapRaised(dpy, win);
   2.440 -	draw_menu();
   2.441 -	XFlush(dpy);
   2.442 -
   2.443 -	/* main event loop */
   2.444 -	while(!XNextEvent(dpy, &ev)) {
   2.445 -		switch (ev.type) {
   2.446 -			case KeyPress:
   2.447 -				kpress(&ev.xkey);
   2.448 -				break;
   2.449 -			case Expose:
   2.450 -				if(ev.xexpose.count == 0) {
   2.451 -					draw_menu();
   2.452 -				}
   2.453 -				break;
   2.454 -			default:
   2.455 -				break;
   2.456 -		}
   2.457 -		if(done)
   2.458 -			break;
   2.459 -	}
   2.460 -
   2.461 -	XUngrabKeyboard(dpy, CurrentTime);
   2.462 -	XFreePixmap(dpy, brush.drawable);
   2.463 -	XFreeGC(dpy, brush.gc);
   2.464 -	XDestroyWindow(dpy, win);
   2.465 -	XCloseDisplay(dpy);
   2.466 -
   2.467 -	return ret;
   2.468 -}
     3.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.2 +++ b/menu.c	Mon Jul 10 19:47:35 2006 +0200
     3.3 @@ -0,0 +1,465 @@
     3.4 +/*
     3.5 + * (C)opyright MMVI Anselm R. Garbe <garbeam at gmail dot com>
     3.6 + * (C)opyright MMVI Sander van Dijk <a dot h dot vandijk at gmail dot com>
     3.7 + * See LICENSE file for license details.
     3.8 + */
     3.9 +
    3.10 +#include "config.h"
    3.11 +#include "draw.h"
    3.12 +#include "util.h"
    3.13 +
    3.14 +#include <ctype.h>
    3.15 +#include <stdlib.h>
    3.16 +#include <stdio.h>
    3.17 +#include <string.h>
    3.18 +#include <sys/stat.h>
    3.19 +#include <sys/wait.h>
    3.20 +#include <time.h>
    3.21 +#include <unistd.h>
    3.22 +#include <X11/cursorfont.h>
    3.23 +#include <X11/Xutil.h>
    3.24 +#include <X11/keysym.h>
    3.25 +
    3.26 +typedef struct Item Item;
    3.27 +
    3.28 +struct Item {
    3.29 +	Item *next;		/* traverses all items */
    3.30 +	Item *left, *right;	/* traverses items matching current search pattern */
    3.31 +	char *text;
    3.32 +};
    3.33 +
    3.34 +static Display *dpy;
    3.35 +static Window root;
    3.36 +static Window win;
    3.37 +static XRectangle rect;
    3.38 +static Bool done = False;
    3.39 +
    3.40 +static Item *allitem = 0;	/* first of all items */
    3.41 +static Item *item = 0;	/* first of pattern matching items */
    3.42 +static Item *sel = 0;
    3.43 +static Item *nextoff = 0;
    3.44 +static Item *prevoff = 0;
    3.45 +static Item *curroff = 0;
    3.46 +
    3.47 +static int screen;
    3.48 +static char *title = 0;
    3.49 +static char text[4096];
    3.50 +static int ret = 0;
    3.51 +static int nitem = 0;
    3.52 +static unsigned int cmdw = 0;
    3.53 +static unsigned int twidth = 0;
    3.54 +static unsigned int cwidth = 0;
    3.55 +static const int seek = 30;		/* 30px */
    3.56 +
    3.57 +static Brush brush = {0};
    3.58 +
    3.59 +static void draw_menu(void);
    3.60 +static void kpress(XKeyEvent * e);
    3.61 +
    3.62 +static char version[] = "gridmenu - " VERSION ", (C)opyright MMVI Anselm R. Garbe\n";
    3.63 +
    3.64 +static void
    3.65 +usage()
    3.66 +{
    3.67 +	fprintf(stderr, "%s", "usage: gridmenu [-v] [-t <title>]\n");
    3.68 +	exit(1);
    3.69 +}
    3.70 +
    3.71 +static void
    3.72 +update_offsets()
    3.73 +{
    3.74 +	unsigned int tw, w = cmdw + 2 * seek;
    3.75 +
    3.76 +	if(!curroff)
    3.77 +		return;
    3.78 +
    3.79 +	for(nextoff = curroff; nextoff; nextoff=nextoff->right) {
    3.80 +		tw = textwidth(&brush.font, nextoff->text);
    3.81 +		if(tw > rect.width / 3)
    3.82 +			tw = rect.width / 3;
    3.83 +		w += tw + brush.font.height;
    3.84 +		if(w > rect.width)
    3.85 +			break;
    3.86 +	}
    3.87 +
    3.88 +	w = cmdw + 2 * seek;
    3.89 +	for(prevoff = curroff; prevoff && prevoff->left; prevoff=prevoff->left) {
    3.90 +		tw = textwidth(&brush.font, prevoff->left->text);
    3.91 +		if(tw > rect.width / 3)
    3.92 +			tw = rect.width / 3;
    3.93 +		w += tw + brush.font.height;
    3.94 +		if(w > rect.width)
    3.95 +			break;
    3.96 +	}
    3.97 +}
    3.98 +
    3.99 +static void
   3.100 +update_items(char *pattern)
   3.101 +{
   3.102 +	unsigned int plen = strlen(pattern);
   3.103 +	Item *i, *j;
   3.104 +
   3.105 +	if(!pattern)
   3.106 +		return;
   3.107 +
   3.108 +	if(!title || *pattern)
   3.109 +		cmdw = cwidth;
   3.110 +	else
   3.111 +		cmdw = twidth;
   3.112 +
   3.113 +	item = j = 0;
   3.114 +	nitem = 0;
   3.115 +
   3.116 +	for(i = allitem; i; i=i->next)
   3.117 +		if(!plen || !strncmp(pattern, i->text, plen)) {
   3.118 +			if(!j)
   3.119 +				item = i;
   3.120 +			else
   3.121 +				j->right = i;
   3.122 +			i->left = j;
   3.123 +			i->right = 0;
   3.124 +			j = i;
   3.125 +			nitem++;
   3.126 +		}
   3.127 +	for(i = allitem; i; i=i->next)
   3.128 +		if(plen && strncmp(pattern, i->text, plen)
   3.129 +				&& strstr(i->text, pattern)) {
   3.130 +			if(!j)
   3.131 +				item = i;
   3.132 +			else
   3.133 +				j->right = i;
   3.134 +			i->left = j;
   3.135 +			i->right = 0;
   3.136 +			j = i;
   3.137 +			nitem++;
   3.138 +		}
   3.139 +
   3.140 +	curroff = prevoff = nextoff = sel = item;
   3.141 +
   3.142 +	update_offsets();
   3.143 +}
   3.144 +
   3.145 +/* creates brush structs for brush mode drawing */
   3.146 +static void
   3.147 +draw_menu()
   3.148 +{
   3.149 +	unsigned int offx = 0;
   3.150 +	Item *i;
   3.151 +
   3.152 +	brush.rect = rect;
   3.153 +	brush.rect.x = 0;
   3.154 +	brush.rect.y = 0;
   3.155 +	draw(dpy, &brush, False, 0);
   3.156 +
   3.157 +	/* print command */
   3.158 +	if(!title || text[0]) {
   3.159 +		cmdw = cwidth;
   3.160 +		if(cmdw && item)
   3.161 +			brush.rect.width = cmdw;
   3.162 +		draw(dpy, &brush, False, text);
   3.163 +	}
   3.164 +	else {
   3.165 +		cmdw = twidth;
   3.166 +		brush.rect.width = cmdw;
   3.167 +		draw(dpy, &brush, False, title);
   3.168 +	}
   3.169 +	offx += brush.rect.width;
   3.170 +
   3.171 +	if(curroff) {
   3.172 +		brush.rect.x = offx;
   3.173 +		brush.rect.width = seek;
   3.174 +		offx += brush.rect.width;
   3.175 +		draw(dpy, &brush, False, (curroff && curroff->left) ? "<" : 0);
   3.176 +
   3.177 +		/* determine maximum items */
   3.178 +		for(i = curroff; i != nextoff; i=i->right) {
   3.179 +			brush.border = False;
   3.180 +			brush.rect.x = offx;
   3.181 +			brush.rect.width = textwidth(&brush.font, i->text);
   3.182 +			if(brush.rect.width > rect.width / 3)
   3.183 +				brush.rect.width = rect.width / 3;
   3.184 +			brush.rect.width += brush.font.height;
   3.185 +			if(sel == i) {
   3.186 +				swap((void **)&brush.fg, (void **)&brush.bg);
   3.187 +				draw(dpy, &brush, True, i->text);
   3.188 +				swap((void **)&brush.fg, (void **)&brush.bg);
   3.189 +			}
   3.190 +			else
   3.191 +				draw(dpy, &brush, False, i->text);
   3.192 +			offx += brush.rect.width;
   3.193 +		}
   3.194 +
   3.195 +		brush.rect.x = rect.width - seek;
   3.196 +		brush.rect.width = seek;
   3.197 +		draw(dpy, &brush, False, nextoff ? ">" : 0);
   3.198 +	}
   3.199 +	XCopyArea(dpy, brush.drawable, win, brush.gc, 0, 0, rect.width,
   3.200 +			rect.height, 0, 0);
   3.201 +	XFlush(dpy);
   3.202 +}
   3.203 +
   3.204 +static void
   3.205 +kpress(XKeyEvent * e)
   3.206 +{
   3.207 +	KeySym ksym;
   3.208 +	char buf[32];
   3.209 +	int num, prev_nitem;
   3.210 +	unsigned int i, len = strlen(text);
   3.211 +
   3.212 +	buf[0] = 0;
   3.213 +	num = XLookupString(e, buf, sizeof(buf), &ksym, 0);
   3.214 +
   3.215 +	if(IsFunctionKey(ksym) || IsKeypadKey(ksym)
   3.216 +			|| IsMiscFunctionKey(ksym) || IsPFKey(ksym)
   3.217 +			|| IsPrivateKeypadKey(ksym))
   3.218 +		return;
   3.219 +
   3.220 +	/* first check if a control mask is omitted */
   3.221 +	if(e->state & ControlMask) {
   3.222 +		switch (ksym) {
   3.223 +		case XK_H:
   3.224 +		case XK_h:
   3.225 +			ksym = XK_BackSpace;
   3.226 +			break;
   3.227 +		case XK_I:
   3.228 +		case XK_i:
   3.229 +			ksym = XK_Tab;
   3.230 +			break;
   3.231 +		case XK_J:
   3.232 +		case XK_j:
   3.233 +			ksym = XK_Return;
   3.234 +			break;
   3.235 +		case XK_N:
   3.236 +		case XK_n:
   3.237 +			ksym = XK_Right;
   3.238 +			break;
   3.239 +		case XK_P:
   3.240 +		case XK_p:
   3.241 +			ksym = XK_Left;
   3.242 +			break;
   3.243 +		case XK_U:
   3.244 +		case XK_u:
   3.245 +			text[0] = 0;
   3.246 +			update_items(text);
   3.247 +			draw_menu();
   3.248 +			return;
   3.249 +			break;
   3.250 +		case XK_bracketleft:
   3.251 +			ksym = XK_Escape;
   3.252 +			break;
   3.253 +		default:	/* ignore other control sequences */
   3.254 +			return;
   3.255 +			break;
   3.256 +		}
   3.257 +	}
   3.258 +	switch (ksym) {
   3.259 +	case XK_Left:
   3.260 +		if(!(sel && sel->left))
   3.261 +			return;
   3.262 +		sel=sel->left;
   3.263 +		if(sel->right == curroff) {
   3.264 +			curroff = prevoff;
   3.265 +			update_offsets();
   3.266 +		}
   3.267 +		break;
   3.268 +	case XK_Tab:
   3.269 +		if(!sel)
   3.270 +			return;
   3.271 +		strncpy(text, sel->text, sizeof(text));
   3.272 +		update_items(text);
   3.273 +		break;
   3.274 +	case XK_Right:
   3.275 +		if(!(sel && sel->right))
   3.276 +			return;
   3.277 +		sel=sel->right;
   3.278 +		if(sel == nextoff) {
   3.279 +			curroff = nextoff;
   3.280 +			update_offsets();
   3.281 +		}
   3.282 +		break;
   3.283 +	case XK_Return:
   3.284 +		if(e->state & ShiftMask) {
   3.285 +			if(text)
   3.286 +				fprintf(stdout, "%s", text);
   3.287 +		}
   3.288 +		else if(sel)
   3.289 +			fprintf(stdout, "%s", sel->text);
   3.290 +		else if(text)
   3.291 +			fprintf(stdout, "%s", text);
   3.292 +		fflush(stdout);
   3.293 +		done = True;
   3.294 +		break;
   3.295 +	case XK_Escape:
   3.296 +		ret = 1;
   3.297 +		done = True;
   3.298 +		break;
   3.299 +	case XK_BackSpace:
   3.300 +		if((i = len)) {
   3.301 +			prev_nitem = nitem;
   3.302 +			do {
   3.303 +				text[--i] = 0;
   3.304 +				update_items(text);
   3.305 +			} while(i && nitem && prev_nitem == nitem);
   3.306 +			update_items(text);
   3.307 +		}
   3.308 +		break;
   3.309 +	default:
   3.310 +		if((num == 1) && !iscntrl((int) buf[0])) {
   3.311 +			buf[num] = 0;
   3.312 +			if(len > 0)
   3.313 +				strncat(text, buf, sizeof(text));
   3.314 +			else
   3.315 +				strncpy(text, buf, sizeof(text));
   3.316 +			update_items(text);
   3.317 +		}
   3.318 +	}
   3.319 +	draw_menu();
   3.320 +}
   3.321 +
   3.322 +static char *
   3.323 +read_allitems()
   3.324 +{
   3.325 +	static char *maxname = 0;
   3.326 +	char *p, buf[1024];
   3.327 +	unsigned int len = 0, max = 0;
   3.328 +	Item *i, *new;
   3.329 +
   3.330 +	i = 0;
   3.331 +	while(fgets(buf, sizeof(buf), stdin)) {
   3.332 +		len = strlen(buf);
   3.333 +		if (buf[len - 1] == '\n')
   3.334 +			buf[len - 1] = 0;
   3.335 +		p = estrdup(buf);
   3.336 +		if(max < len) {
   3.337 +			maxname = p;
   3.338 +			max = len;
   3.339 +		}
   3.340 +
   3.341 +		new = emalloc(sizeof(Item));
   3.342 +		new->next = new->left = new->right = 0;
   3.343 +		new->text = p;
   3.344 +		if(!i)
   3.345 +			allitem = new;
   3.346 +		else 
   3.347 +			i->next = new;
   3.348 +		i = new;
   3.349 +	}
   3.350 +
   3.351 +	return maxname;
   3.352 +}
   3.353 +
   3.354 +int
   3.355 +main(int argc, char *argv[])
   3.356 +{
   3.357 +	int i;
   3.358 +	XSetWindowAttributes wa;
   3.359 +	char *maxname;
   3.360 +	XEvent ev;
   3.361 +
   3.362 +	/* command line args */
   3.363 +	for(i = 1; i < argc; i++) {
   3.364 +		if (argv[i][0] == '-')
   3.365 +			switch (argv[i][1]) {
   3.366 +			case 'v':
   3.367 +				fprintf(stdout, "%s", version);
   3.368 +				exit(0);
   3.369 +				break;
   3.370 +			case 't':
   3.371 +				if(++i < argc)
   3.372 +					title = argv[i];
   3.373 +				else
   3.374 +					usage();
   3.375 +				break;
   3.376 +			default:
   3.377 +				usage();
   3.378 +				break;
   3.379 +			}
   3.380 +		else
   3.381 +			usage();
   3.382 +	}
   3.383 +
   3.384 +	dpy = XOpenDisplay(0);
   3.385 +	if(!dpy)
   3.386 +		error("gridmenu: cannot open dpy\n");
   3.387 +	screen = DefaultScreen(dpy);
   3.388 +	root = RootWindow(dpy, screen);
   3.389 +
   3.390 +	maxname = read_allitems();
   3.391 +
   3.392 +	/* grab as early as possible, but after reading all items!!! */
   3.393 +	while(XGrabKeyboard(dpy, root, True, GrabModeAsync,
   3.394 +			 GrabModeAsync, CurrentTime) != GrabSuccess)
   3.395 +		usleep(1000);
   3.396 +
   3.397 +	/* style */
   3.398 +	loadcolors(dpy, screen, &brush, BGCOLOR, FGCOLOR, BORDERCOLOR);
   3.399 +	loadfont(dpy, &brush.font, FONT);
   3.400 +
   3.401 +	wa.override_redirect = 1;
   3.402 +	wa.background_pixmap = ParentRelative;
   3.403 +	wa.event_mask = ExposureMask | ButtonPressMask | KeyPressMask
   3.404 +		| SubstructureRedirectMask | SubstructureNotifyMask;
   3.405 +
   3.406 +	rect.width = DisplayWidth(dpy, screen);
   3.407 +	rect.height = brush.font.height + 4;
   3.408 +	rect.y = DisplayHeight(dpy, screen) - rect.height;
   3.409 +	rect.x = 0;
   3.410 +
   3.411 +	win = XCreateWindow(dpy, root, rect.x, rect.y,
   3.412 +			rect.width, rect.height, 0, DefaultDepth(dpy, screen),
   3.413 +			CopyFromParent, DefaultVisual(dpy, screen),
   3.414 +			CWOverrideRedirect | CWBackPixmap | CWEventMask, &wa);
   3.415 +	XDefineCursor(dpy, win, XCreateFontCursor(dpy, XC_xterm));
   3.416 +	XFlush(dpy);
   3.417 +
   3.418 +	/* pixmap */
   3.419 +	brush.gc = XCreateGC(dpy, win, 0, 0);
   3.420 +	brush.drawable = XCreatePixmap(dpy, win, rect.width, rect.height,
   3.421 +			DefaultDepth(dpy, screen));
   3.422 +	XFlush(dpy);
   3.423 +
   3.424 +	if(maxname)
   3.425 +		cwidth = textwidth(&brush.font, maxname) + brush.font.height;
   3.426 +	if(cwidth > rect.width / 3)
   3.427 +		cwidth = rect.width / 3;
   3.428 +
   3.429 +	if(title) {
   3.430 +		twidth = textwidth(&brush.font, title) + brush.font.height;
   3.431 +		if(twidth > rect.width / 3)
   3.432 +			twidth = rect.width / 3;
   3.433 +	}
   3.434 +
   3.435 +	cmdw = title ? twidth : cwidth;
   3.436 +
   3.437 +	text[0] = 0;
   3.438 +	update_items(text);
   3.439 +	XMapRaised(dpy, win);
   3.440 +	draw_menu();
   3.441 +	XFlush(dpy);
   3.442 +
   3.443 +	/* main event loop */
   3.444 +	while(!XNextEvent(dpy, &ev)) {
   3.445 +		switch (ev.type) {
   3.446 +			case KeyPress:
   3.447 +				kpress(&ev.xkey);
   3.448 +				break;
   3.449 +			case Expose:
   3.450 +				if(ev.xexpose.count == 0) {
   3.451 +					draw_menu();
   3.452 +				}
   3.453 +				break;
   3.454 +			default:
   3.455 +				break;
   3.456 +		}
   3.457 +		if(done)
   3.458 +			break;
   3.459 +	}
   3.460 +
   3.461 +	XUngrabKeyboard(dpy, CurrentTime);
   3.462 +	XFreePixmap(dpy, brush.drawable);
   3.463 +	XFreeGC(dpy, brush.gc);
   3.464 +	XDestroyWindow(dpy, win);
   3.465 +	XCloseDisplay(dpy);
   3.466 +
   3.467 +	return ret;
   3.468 +}