aewl

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