Mercurial > aewl
comparison 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 |
comparison
equal
deleted
inserted
replaced
3:e969f3575b7a | 4:991bd8b0771e |
---|---|
1 /* | |
2 * (C)opyright MMVI Anselm R. Garbe <garbeam at gmail dot com> | |
3 * (C)opyright MMVI Sander van Dijk <a dot h dot vandijk at gmail dot com> | |
4 * See LICENSE file for license details. | |
5 */ | |
6 | |
7 #include "config.h" | |
8 #include "draw.h" | |
9 #include "util.h" | |
10 | |
11 #include <ctype.h> | |
12 #include <stdlib.h> | |
13 #include <stdio.h> | |
14 #include <string.h> | |
15 #include <sys/stat.h> | |
16 #include <sys/wait.h> | |
17 #include <time.h> | |
18 #include <unistd.h> | |
19 #include <X11/cursorfont.h> | |
20 #include <X11/Xutil.h> | |
21 #include <X11/keysym.h> | |
22 | |
23 typedef struct Item Item; | |
24 | |
25 struct Item { | |
26 Item *next; /* traverses all items */ | |
27 Item *left, *right; /* traverses items matching current search pattern */ | |
28 char *text; | |
29 }; | |
30 | |
31 static Display *dpy; | |
32 static Window root; | |
33 static Window win; | |
34 static XRectangle rect; | |
35 static Bool done = False; | |
36 | |
37 static Item *allitem = 0; /* first of all items */ | |
38 static Item *item = 0; /* first of pattern matching items */ | |
39 static Item *sel = 0; | |
40 static Item *nextoff = 0; | |
41 static Item *prevoff = 0; | |
42 static Item *curroff = 0; | |
43 | |
44 static int screen; | |
45 static char *title = 0; | |
46 static char text[4096]; | |
47 static int ret = 0; | |
48 static int nitem = 0; | |
49 static unsigned int cmdw = 0; | |
50 static unsigned int twidth = 0; | |
51 static unsigned int cwidth = 0; | |
52 static const int seek = 30; /* 30px */ | |
53 | |
54 static Brush brush = {0}; | |
55 | |
56 static void draw_menu(void); | |
57 static void kpress(XKeyEvent * e); | |
58 | |
59 static char version[] = "gridmenu - " VERSION ", (C)opyright MMVI Anselm R. Garbe\n"; | |
60 | |
61 static void | |
62 usage() | |
63 { | |
64 fprintf(stderr, "%s", "usage: gridmenu [-v] [-t <title>]\n"); | |
65 exit(1); | |
66 } | |
67 | |
68 static void | |
69 update_offsets() | |
70 { | |
71 unsigned int tw, w = cmdw + 2 * seek; | |
72 | |
73 if(!curroff) | |
74 return; | |
75 | |
76 for(nextoff = curroff; nextoff; nextoff=nextoff->right) { | |
77 tw = textwidth(&brush.font, nextoff->text); | |
78 if(tw > rect.width / 3) | |
79 tw = rect.width / 3; | |
80 w += tw + brush.font.height; | |
81 if(w > rect.width) | |
82 break; | |
83 } | |
84 | |
85 w = cmdw + 2 * seek; | |
86 for(prevoff = curroff; prevoff && prevoff->left; prevoff=prevoff->left) { | |
87 tw = textwidth(&brush.font, prevoff->left->text); | |
88 if(tw > rect.width / 3) | |
89 tw = rect.width / 3; | |
90 w += tw + brush.font.height; | |
91 if(w > rect.width) | |
92 break; | |
93 } | |
94 } | |
95 | |
96 static void | |
97 update_items(char *pattern) | |
98 { | |
99 unsigned int plen = strlen(pattern); | |
100 Item *i, *j; | |
101 | |
102 if(!pattern) | |
103 return; | |
104 | |
105 if(!title || *pattern) | |
106 cmdw = cwidth; | |
107 else | |
108 cmdw = twidth; | |
109 | |
110 item = j = 0; | |
111 nitem = 0; | |
112 | |
113 for(i = allitem; i; i=i->next) | |
114 if(!plen || !strncmp(pattern, i->text, plen)) { | |
115 if(!j) | |
116 item = i; | |
117 else | |
118 j->right = i; | |
119 i->left = j; | |
120 i->right = 0; | |
121 j = i; | |
122 nitem++; | |
123 } | |
124 for(i = allitem; i; i=i->next) | |
125 if(plen && strncmp(pattern, i->text, plen) | |
126 && strstr(i->text, pattern)) { | |
127 if(!j) | |
128 item = i; | |
129 else | |
130 j->right = i; | |
131 i->left = j; | |
132 i->right = 0; | |
133 j = i; | |
134 nitem++; | |
135 } | |
136 | |
137 curroff = prevoff = nextoff = sel = item; | |
138 | |
139 update_offsets(); | |
140 } | |
141 | |
142 /* creates brush structs for brush mode drawing */ | |
143 static void | |
144 draw_menu() | |
145 { | |
146 unsigned int offx = 0; | |
147 Item *i; | |
148 | |
149 brush.rect = rect; | |
150 brush.rect.x = 0; | |
151 brush.rect.y = 0; | |
152 draw(dpy, &brush, False, 0); | |
153 | |
154 /* print command */ | |
155 if(!title || text[0]) { | |
156 cmdw = cwidth; | |
157 if(cmdw && item) | |
158 brush.rect.width = cmdw; | |
159 draw(dpy, &brush, False, text); | |
160 } | |
161 else { | |
162 cmdw = twidth; | |
163 brush.rect.width = cmdw; | |
164 draw(dpy, &brush, False, title); | |
165 } | |
166 offx += brush.rect.width; | |
167 | |
168 if(curroff) { | |
169 brush.rect.x = offx; | |
170 brush.rect.width = seek; | |
171 offx += brush.rect.width; | |
172 draw(dpy, &brush, False, (curroff && curroff->left) ? "<" : 0); | |
173 | |
174 /* determine maximum items */ | |
175 for(i = curroff; i != nextoff; i=i->right) { | |
176 brush.border = False; | |
177 brush.rect.x = offx; | |
178 brush.rect.width = textwidth(&brush.font, i->text); | |
179 if(brush.rect.width > rect.width / 3) | |
180 brush.rect.width = rect.width / 3; | |
181 brush.rect.width += brush.font.height; | |
182 if(sel == i) { | |
183 swap((void **)&brush.fg, (void **)&brush.bg); | |
184 draw(dpy, &brush, True, i->text); | |
185 swap((void **)&brush.fg, (void **)&brush.bg); | |
186 } | |
187 else | |
188 draw(dpy, &brush, False, i->text); | |
189 offx += brush.rect.width; | |
190 } | |
191 | |
192 brush.rect.x = rect.width - seek; | |
193 brush.rect.width = seek; | |
194 draw(dpy, &brush, False, nextoff ? ">" : 0); | |
195 } | |
196 XCopyArea(dpy, brush.drawable, win, brush.gc, 0, 0, rect.width, | |
197 rect.height, 0, 0); | |
198 XFlush(dpy); | |
199 } | |
200 | |
201 static void | |
202 kpress(XKeyEvent * e) | |
203 { | |
204 KeySym ksym; | |
205 char buf[32]; | |
206 int num, prev_nitem; | |
207 unsigned int i, len = strlen(text); | |
208 | |
209 buf[0] = 0; | |
210 num = XLookupString(e, buf, sizeof(buf), &ksym, 0); | |
211 | |
212 if(IsFunctionKey(ksym) || IsKeypadKey(ksym) | |
213 || IsMiscFunctionKey(ksym) || IsPFKey(ksym) | |
214 || IsPrivateKeypadKey(ksym)) | |
215 return; | |
216 | |
217 /* first check if a control mask is omitted */ | |
218 if(e->state & ControlMask) { | |
219 switch (ksym) { | |
220 case XK_H: | |
221 case XK_h: | |
222 ksym = XK_BackSpace; | |
223 break; | |
224 case XK_I: | |
225 case XK_i: | |
226 ksym = XK_Tab; | |
227 break; | |
228 case XK_J: | |
229 case XK_j: | |
230 ksym = XK_Return; | |
231 break; | |
232 case XK_N: | |
233 case XK_n: | |
234 ksym = XK_Right; | |
235 break; | |
236 case XK_P: | |
237 case XK_p: | |
238 ksym = XK_Left; | |
239 break; | |
240 case XK_U: | |
241 case XK_u: | |
242 text[0] = 0; | |
243 update_items(text); | |
244 draw_menu(); | |
245 return; | |
246 break; | |
247 case XK_bracketleft: | |
248 ksym = XK_Escape; | |
249 break; | |
250 default: /* ignore other control sequences */ | |
251 return; | |
252 break; | |
253 } | |
254 } | |
255 switch (ksym) { | |
256 case XK_Left: | |
257 if(!(sel && sel->left)) | |
258 return; | |
259 sel=sel->left; | |
260 if(sel->right == curroff) { | |
261 curroff = prevoff; | |
262 update_offsets(); | |
263 } | |
264 break; | |
265 case XK_Tab: | |
266 if(!sel) | |
267 return; | |
268 strncpy(text, sel->text, sizeof(text)); | |
269 update_items(text); | |
270 break; | |
271 case XK_Right: | |
272 if(!(sel && sel->right)) | |
273 return; | |
274 sel=sel->right; | |
275 if(sel == nextoff) { | |
276 curroff = nextoff; | |
277 update_offsets(); | |
278 } | |
279 break; | |
280 case XK_Return: | |
281 if(e->state & ShiftMask) { | |
282 if(text) | |
283 fprintf(stdout, "%s", text); | |
284 } | |
285 else if(sel) | |
286 fprintf(stdout, "%s", sel->text); | |
287 else if(text) | |
288 fprintf(stdout, "%s", text); | |
289 fflush(stdout); | |
290 done = True; | |
291 break; | |
292 case XK_Escape: | |
293 ret = 1; | |
294 done = True; | |
295 break; | |
296 case XK_BackSpace: | |
297 if((i = len)) { | |
298 prev_nitem = nitem; | |
299 do { | |
300 text[--i] = 0; | |
301 update_items(text); | |
302 } while(i && nitem && prev_nitem == nitem); | |
303 update_items(text); | |
304 } | |
305 break; | |
306 default: | |
307 if((num == 1) && !iscntrl((int) buf[0])) { | |
308 buf[num] = 0; | |
309 if(len > 0) | |
310 strncat(text, buf, sizeof(text)); | |
311 else | |
312 strncpy(text, buf, sizeof(text)); | |
313 update_items(text); | |
314 } | |
315 } | |
316 draw_menu(); | |
317 } | |
318 | |
319 static char * | |
320 read_allitems() | |
321 { | |
322 static char *maxname = 0; | |
323 char *p, buf[1024]; | |
324 unsigned int len = 0, max = 0; | |
325 Item *i, *new; | |
326 | |
327 i = 0; | |
328 while(fgets(buf, sizeof(buf), stdin)) { | |
329 len = strlen(buf); | |
330 if (buf[len - 1] == '\n') | |
331 buf[len - 1] = 0; | |
332 p = estrdup(buf); | |
333 if(max < len) { | |
334 maxname = p; | |
335 max = len; | |
336 } | |
337 | |
338 new = emalloc(sizeof(Item)); | |
339 new->next = new->left = new->right = 0; | |
340 new->text = p; | |
341 if(!i) | |
342 allitem = new; | |
343 else | |
344 i->next = new; | |
345 i = new; | |
346 } | |
347 | |
348 return maxname; | |
349 } | |
350 | |
351 int | |
352 main(int argc, char *argv[]) | |
353 { | |
354 int i; | |
355 XSetWindowAttributes wa; | |
356 char *maxname; | |
357 XEvent ev; | |
358 | |
359 /* command line args */ | |
360 for(i = 1; i < argc; i++) { | |
361 if (argv[i][0] == '-') | |
362 switch (argv[i][1]) { | |
363 case 'v': | |
364 fprintf(stdout, "%s", version); | |
365 exit(0); | |
366 break; | |
367 case 't': | |
368 if(++i < argc) | |
369 title = argv[i]; | |
370 else | |
371 usage(); | |
372 break; | |
373 default: | |
374 usage(); | |
375 break; | |
376 } | |
377 else | |
378 usage(); | |
379 } | |
380 | |
381 dpy = XOpenDisplay(0); | |
382 if(!dpy) | |
383 error("gridmenu: cannot open dpy\n"); | |
384 screen = DefaultScreen(dpy); | |
385 root = RootWindow(dpy, screen); | |
386 | |
387 maxname = read_allitems(); | |
388 | |
389 /* grab as early as possible, but after reading all items!!! */ | |
390 while(XGrabKeyboard(dpy, root, True, GrabModeAsync, | |
391 GrabModeAsync, CurrentTime) != GrabSuccess) | |
392 usleep(1000); | |
393 | |
394 /* style */ | |
395 loadcolors(dpy, screen, &brush, BGCOLOR, FGCOLOR, BORDERCOLOR); | |
396 loadfont(dpy, &brush.font, FONT); | |
397 | |
398 wa.override_redirect = 1; | |
399 wa.background_pixmap = ParentRelative; | |
400 wa.event_mask = ExposureMask | ButtonPressMask | KeyPressMask | |
401 | SubstructureRedirectMask | SubstructureNotifyMask; | |
402 | |
403 rect.width = DisplayWidth(dpy, screen); | |
404 rect.height = brush.font.height + 4; | |
405 rect.y = DisplayHeight(dpy, screen) - rect.height; | |
406 rect.x = 0; | |
407 | |
408 win = XCreateWindow(dpy, root, rect.x, rect.y, | |
409 rect.width, rect.height, 0, DefaultDepth(dpy, screen), | |
410 CopyFromParent, DefaultVisual(dpy, screen), | |
411 CWOverrideRedirect | CWBackPixmap | CWEventMask, &wa); | |
412 XDefineCursor(dpy, win, XCreateFontCursor(dpy, XC_xterm)); | |
413 XFlush(dpy); | |
414 | |
415 /* pixmap */ | |
416 brush.gc = XCreateGC(dpy, win, 0, 0); | |
417 brush.drawable = XCreatePixmap(dpy, win, rect.width, rect.height, | |
418 DefaultDepth(dpy, screen)); | |
419 XFlush(dpy); | |
420 | |
421 if(maxname) | |
422 cwidth = textwidth(&brush.font, maxname) + brush.font.height; | |
423 if(cwidth > rect.width / 3) | |
424 cwidth = rect.width / 3; | |
425 | |
426 if(title) { | |
427 twidth = textwidth(&brush.font, title) + brush.font.height; | |
428 if(twidth > rect.width / 3) | |
429 twidth = rect.width / 3; | |
430 } | |
431 | |
432 cmdw = title ? twidth : cwidth; | |
433 | |
434 text[0] = 0; | |
435 update_items(text); | |
436 XMapRaised(dpy, win); | |
437 draw_menu(); | |
438 XFlush(dpy); | |
439 | |
440 /* main event loop */ | |
441 while(!XNextEvent(dpy, &ev)) { | |
442 switch (ev.type) { | |
443 case KeyPress: | |
444 kpress(&ev.xkey); | |
445 break; | |
446 case Expose: | |
447 if(ev.xexpose.count == 0) { | |
448 draw_menu(); | |
449 } | |
450 break; | |
451 default: | |
452 break; | |
453 } | |
454 if(done) | |
455 break; | |
456 } | |
457 | |
458 XUngrabKeyboard(dpy, CurrentTime); | |
459 XFreePixmap(dpy, brush.drawable); | |
460 XFreeGC(dpy, brush.gc); | |
461 XDestroyWindow(dpy, win); | |
462 XCloseDisplay(dpy); | |
463 | |
464 return ret; | |
465 } |