1 #include "rxvtlib.h"
2 
3 /*--------------------------------*-C-*---------------------------------*
4  * File:	menubar.c
5  *----------------------------------------------------------------------*
6  * $Id: menubar.c,v 1.27 1999/01/23 14:26:36 mason Exp $
7  *
8  * Copyright (C) 1997,1998  mj olesen <olesen@me.QueensU.CA>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23  *----------------------------------------------------------------------*
24  * refer.html (or refer.txt) contains up-to-date documentation.  The
25  * summary that appears at the end of this file was taken from there.
26  *----------------------------------------------------------------------*/
27 
28 #ifdef MENUBAR
29 
30 	/* okay to alter menu? */
31 
32 /* (l)eft, (u)p, (d)own, (r)ight *//* str[0] = strlen (str+1) */
33 
34 	/* currently active menu */
35 #endif
36 
37 /*}}} */
38 
39 #ifdef MENUBAR
40 /*
41  * find an item called NAME in MENU
42  */
43 /* INTPROTO */
menuitem_find(const menu_t * menu,const char * name)44 menuitem_t     *menuitem_find (const menu_t * menu, const char *name)
45 {
46     menuitem_t     *item;
47 
48     assert (name != NULL);
49     assert (menu != NULL);
50 
51 /* find the last item in the menu, this is good for separators */
52     for (item = menu->tail; item != NULL; item = item->prev) {
53 	if (item->entry.type == MenuSubMenu) {
54 	    if (!strcmp (name, (item->entry.submenu.menu)->name))
55 		break;
56 	} else if ((isSeparator (name) && isSeparator (item->name))
57 		   || !strcmp (name, item->name))
58 	    break;
59     }
60     return item;
61 }
62 #endif
63 
64 #ifdef MENUBAR
65 /*
66  * unlink ITEM from its MENU and free its memory
67  */
68 /* INTPROTO */
rxvtlib_menuitem_free(rxvtlib * o,menu_t * menu,menuitem_t * item)69 void            rxvtlib_menuitem_free (rxvtlib *o, menu_t * menu, menuitem_t * item)
70 {
71 /* disconnect */
72     menuitem_t     *prev, *next;
73 
74     assert (menu != NULL);
75 
76     prev = item->prev;
77     next = item->next;
78     if (prev != NULL)
79 	prev->next = next;
80     if (next != NULL)
81 	next->prev = prev;
82 
83 /* new head, tail */
84     if (menu->tail == item)
85 	menu->tail = prev;
86     if (menu->head == item)
87 	menu->head = next;
88 
89     switch (item->entry.type) {
90     case MenuAction:
91     case MenuTerminalAction:
92 	FREE (item->entry.action.str);
93 	break;
94     case MenuSubMenu:
95 	(void)rxvtlib_menu_delete (o, item->entry.submenu.menu);
96 	break;
97     }
98     if (item->name != NULL)
99 	FREE (item->name);
100     if (item->name2 != NULL)
101 	FREE (item->name2);
102     FREE (item);
103 }
104 #endif
105 
106 #ifdef MENUBAR
107 /*
108  * sort command vs. terminal actions and
109  * remove the first character of STR if it's '\0'
110  */
111 /* INTPROTO */
action_type(action_t * action,unsigned char * str)112 int             action_type (action_t * action, unsigned char *str)
113 {
114     unsigned int    len;
115 
116 #if defined (DEBUG_MENU) || defined (DEBUG_MENUARROWS)
117     len = strlen (str);
118     fprintf (stderr, "(len %d) = %s\n", len, str);
119 #else
120     len = Str_escaped ((char *)str);
121 #endif
122 
123     if (!len)
124 	return -1;
125 
126 /* sort command vs. terminal actions */
127     action->type = MenuAction;
128     if (str[0] == '\0') {
129 	/* the functional equivalent: memmove (str, str+1, len); */
130 	unsigned char  *dst = (str);
131 	unsigned char  *src = (str + 1);
132 	unsigned char  *end = (str + len);
133 
134 	while (src <= end)
135 	    *dst++ = *src++;
136 
137 	len--;			/* decrement length */
138 	if (str[0] != '\0')
139 	    action->type = MenuTerminalAction;
140     }
141     action->str = str;
142     action->len = len;
143 
144     return 0;
145 }
146 #endif
147 
148 #ifdef MENUBAR
149 /* INTPROTO */
rxvtlib_action_dispatch(rxvtlib * o,action_t * action)150 int             rxvtlib_action_dispatch (rxvtlib *o, action_t * action)
151 {
152     switch (action->type) {
153     case MenuTerminalAction:
154 	rxvtlib_cmd_write (o, action->str, action->len);
155 	break;
156 
157     case MenuAction:
158 	rxvtlib_tt_write (o, action->str, action->len);
159 	break;
160 
161     default:
162 	return -1;
163 	break;
164     }
165     return 0;
166 }
167 #endif
168 
169 #ifdef MENUBAR
170 /* return the arrow index corresponding to NAME */
171 /* INTPROTO */
rxvtlib_menuarrow_find(rxvtlib * o,char name)172 int             rxvtlib_menuarrow_find (rxvtlib *o, char name)
173 {
174     int             i;
175 
176     for (i = 0; i < NARROWS; i++)
177 	if (name == o->Arrows[i].name)
178 	    return i;
179     return -1;
180 }
181 #endif
182 
183 #ifdef MENUBAR
184 /* free the memory associated with arrow NAME of the current menubar */
185 /* INTPROTO */
rxvtlib_menuarrow_free(rxvtlib * o,char name)186 void            rxvtlib_menuarrow_free (rxvtlib *o, char name)
187 {
188     int             i;
189 
190     if (name) {
191 	i = rxvtlib_menuarrow_find (o, name);
192 	if (i >= 0) {
193 	    action_t       *act = &(o->CurrentBar->arrows[i]);
194 
195 	    switch (act->type) {
196 	    case MenuAction:
197 	    case MenuTerminalAction:
198 		FREE (act->str);
199 		act->str = NULL;
200 		act->len = 0;
201 		break;
202 	    }
203 	    act->type = MenuLabel;
204 	}
205     } else {
206 	for (i = 0; i < NARROWS; i++)
207 	    rxvtlib_menuarrow_free (o, o->Arrows[i].name);
208     }
209 }
210 #endif
211 
212 #ifdef MENUBAR
213 /* INTPROTO */
rxvtlib_menuarrow_add(rxvtlib * o,char * string)214 void            rxvtlib_menuarrow_add (rxvtlib *o, char *string)
215 {
216     int             i;
217     unsigned        xtra_len;
218     char           *p;
219 
220     struct {
221 	char           *str;
222 	int             len;
223     } beg = {
224     NULL, 0}
225     , end = {
226     NULL, 0}
227     , *cur, parse[NARROWS];
228 
229     MEMSET (parse, 0, sizeof (parse));
230 
231 /* fprintf(stderr, "add arrows = `%s'\n", string); */
232     for (p = string; p != NULL && *p; string = p) {
233 	p = (string + 3);
234 	/* fprintf(stderr, "parsing at %s\n", string); */
235 	switch (string[1]) {
236 	case 'b':
237 	    cur = &beg;
238 	    break;
239 	case 'e':
240 	    cur = &end;
241 	    break;
242 
243 	default:
244 	    i = rxvtlib_menuarrow_find (o, string[1]);
245 	    if (i >= 0)
246 		cur = &(parse[i]);
247 	    else
248 		continue;	/* not found */
249 	    break;
250 	}
251 
252 	string = p;
253 	cur->str = string;
254 	cur->len = 0;
255 
256 	if (cur == &end) {
257 	    p = strchr (string, '\0');
258 	} else {
259 	    char           *next = string;
260 
261 	    while (1) {
262 		p = strchr (next, '<');
263 		if (p != NULL) {
264 		    if (p[1] && p[2] == '>')
265 			break;
266 		    /* parsed */
267 		} else {
268 		    if (beg.str == NULL)	/* no end needed */
269 			p = strchr (next, '\0');
270 		    break;
271 		}
272 		next = (p + 1);
273 	    }
274 	}
275 
276 	if (p == NULL)
277 	    return;
278 	cur->len = (p - string);
279     }
280 
281 #ifdef DEBUG_MENUARROWS
282     cur = &beg;
283     fprintf (stderr, "<b>(len %d) = %.*s\n",
284 	     cur->len, cur->len, (cur->str ? cur->str : ""));
285     for (i = 0; i < NARROWS; i++) {
286 	cur = &(parse[i]);
287 	fprintf (stderr, "<%c>(len %d) = %.*s\n",
288 		 o->Arrows[i].name,
289 		 cur->len, cur->len, (cur->str ? cur->str : ""));
290     }
291     cur = &end;
292     fprintf (stderr, "<e>(len %d) = %.*s\n",
293 	     cur->len, cur->len, (cur->str ? cur->str : ""));
294 #endif
295 
296     xtra_len = (beg.len + end.len);
297     for (i = 0; i < NARROWS; i++) {
298 	if (xtra_len || parse[i].len)
299 	    rxvtlib_menuarrow_free (o, o->Arrows[i].name);
300     }
301 
302     for (i = 0; i < NARROWS; i++) {
303 	unsigned char  *str;
304 	unsigned int    len;
305 
306 	if (!parse[i].len)
307 	    continue;
308 
309 	str = MALLOC (parse[i].len + xtra_len + 1);
310 	if (str == NULL)
311 	    continue;
312 
313 	len = 0;
314 	if (beg.len) {
315 	    STRNCPY (str + len, beg.str, beg.len);
316 	    len += beg.len;
317 	}
318 	STRNCPY (str + len, parse[i].str, parse[i].len);
319 	len += parse[i].len;
320 
321 	if (end.len) {
322 	    STRNCPY (str + len, end.str, end.len);
323 	    len += end.len;
324 	}
325 	str[len] = '\0';
326 
327 #ifdef DEBUG_MENUARROWS
328 	fprintf (stderr, "<%c>(len %d) = %s\n", o->Arrows[i].name, len, str);
329 #endif
330 	if (action_type (&(o->CurrentBar->arrows[i]), str) < 0)
331 	    FREE (str);
332     }
333 }
334 #endif
335 
336 #ifdef MENUBAR
337 /* INTPROTO */
rxvtlib_menuitem_add(rxvtlib * o,menu_t * menu,const char * name,const char * name2,const char * action)338 menuitem_t     *rxvtlib_menuitem_add (rxvtlib *o, menu_t * menu, const char *name,
339 			      const char *name2, const char *action)
340 {
341     menuitem_t     *item;
342     unsigned int    len;
343 
344     assert (name != NULL);
345     assert (action != NULL);
346 
347     if (menu == NULL)
348 	return NULL;
349 
350     if (isSeparator (name)) {
351 	/* add separator, no action */
352 	name = "";
353 	action = "";
354     } else {
355 	/*
356 	 * add/replace existing menu item
357 	 */
358 	item = menuitem_find (menu, name);
359 	if (item != NULL) {
360 	    if (item->name2 != NULL && name2 != NULL) {
361 		FREE (item->name2);
362 		item->len2 = 0;
363 		item->name2 = NULL;
364 	    }
365 	    switch (item->entry.type) {
366 	    case MenuAction:
367 	    case MenuTerminalAction:
368 		FREE (item->entry.action.str);
369 		item->entry.action.str = NULL;
370 		break;
371 	    }
372 	    goto Item_Found;
373 	}
374     }
375 /* allocate a new itemect */
376     if ((item = (menuitem_t *) MALLOC (sizeof (menuitem_t))) == NULL)
377 	return NULL;
378 
379     item->len2 = 0;
380     item->name2 = NULL;
381 
382     len = strlen (name);
383     item->name = MALLOC (len + 1);
384     if (item->name != NULL) {
385 	STRCPY (item->name, name);
386 	if (name[0] == '.' && name[1] != '.')
387 	    len = 0;		/* hidden menu name */
388     } else {
389 	FREE (item);
390 	return NULL;
391     }
392     item->len = len;
393 
394 /* add to tail of list */
395     item->prev = menu->tail;
396     item->next = NULL;
397 
398     if (menu->tail != NULL)
399 	(menu->tail)->next = item;
400     menu->tail = item;
401 /* fix head */
402     if (menu->head == NULL)
403 	menu->head = item;
404 
405 /*
406  * add action
407  */
408   Item_Found:
409     if (name2 != NULL && item->name2 == NULL) {
410 	len = strlen (name2);
411 	if (len == 0 || (item->name2 = MALLOC (len + 1)) == NULL) {
412 	    len = 0;
413 	    item->name2 = NULL;
414 	} else {
415 	    STRCPY (item->name2, name2);
416 	}
417 	item->len2 = len;
418     }
419     item->entry.type = MenuLabel;
420     len = strlen (action);
421 
422     if (len == 0 && item->name2 != NULL) {
423 	action = item->name2;
424 	len = item->len2;
425     }
426     if (len) {
427 	unsigned char  *str = MALLOC (len + 1);
428 
429 	if (str == NULL) {
430 	    rxvtlib_menuitem_free (o, menu, item);
431 	    return NULL;
432 	}
433 	STRCPY (str, action);
434 
435 	if (action_type (&(item->entry.action), str) < 0)
436 	    FREE (str);
437     }
438 /* new item and a possible increase in width */
439     if (menu->width < (item->len + item->len2))
440 	menu->width = (item->len + item->len2);
441 
442     return item;
443 }
444 #endif
445 
446 #ifdef MENUBAR
447 /*
448  * search for the base starting menu for NAME.
449  * return a pointer to the portion of NAME that remains
450  */
451 /* INTPROTO */
rxvtlib_menu_find_base(rxvtlib * o,menu_t ** menu,char * path)452 char           *rxvtlib_menu_find_base (rxvtlib *o, menu_t ** menu, char *path)
453 {
454     menu_t         *m = NULL;
455     menuitem_t     *item;
456 
457     assert (menu != NULL);
458     assert (o->CurrentBar != NULL);
459 
460     if (path[0] == '\0')
461 	return path;
462 
463     if (strchr (path, '/') != NULL) {
464 	register char  *p = path;
465 
466 	while ((p = strchr (p, '/')) != NULL) {
467 	    p++;
468 	    if (*p == '/')
469 		path = p;
470 	}
471 	if (path[0] == '/') {
472 	    path++;
473 	    *menu = NULL;
474 	}
475 	while ((p = strchr (path, '/')) != NULL) {
476 	    p[0] = '\0';
477 	    if (path[0] == '\0')
478 		return NULL;
479 	    if (!strcmp (path, DOT)) {
480 		/* nothing to do */
481 	    } else if (!strcmp (path, DOTS)) {
482 		if (*menu != NULL)
483 		    *menu = (*menu)->parent;
484 	    } else {
485 		path = rxvtlib_menu_find_base (o, menu, path);
486 		if (path[0] != '\0') {	/* not found */
487 		    p[0] = '/';	/* fix-up name again */
488 		    return path;
489 		}
490 	    }
491 
492 	    path = (p + 1);
493 	}
494     }
495     if (!strcmp (path, DOTS)) {
496 	path += strlen (DOTS);
497 	if (*menu != NULL)
498 	    *menu = (*menu)->parent;
499 	return path;
500     }
501 /* find this menu */
502     if (*menu == NULL) {
503 	for (m = o->CurrentBar->tail; m != NULL; m = m->prev) {
504 	    if (!strcmp (path, m->name))
505 		break;
506 	}
507     } else {
508 	/* find this menu */
509 	for (item = (*menu)->tail; item != NULL; item = item->prev) {
510 	    if (item->entry.type == MenuSubMenu
511 		&& !strcmp (path, (item->entry.submenu.menu)->name)) {
512 		m = (item->entry.submenu.menu);
513 		break;
514 	    }
515 	}
516     }
517     if (m != NULL) {
518 	*menu = m;
519 	path += strlen (path);
520     }
521     return path;
522 }
523 #endif
524 
525 #ifdef MENUBAR
526 /*
527  * delete this entire menu
528  */
529 /* INTPROTO */
rxvtlib_menu_delete(rxvtlib * o,menu_t * menu)530 menu_t         *rxvtlib_menu_delete (rxvtlib *o, menu_t * menu)
531 {
532     menu_t         *parent = NULL, *prev, *next;
533     menuitem_t     *item;
534 
535     assert (o->CurrentBar != NULL);
536 
537 /* delete the entire menu */
538     if (menu == NULL)
539 	return NULL;
540 
541     parent = menu->parent;
542 
543 /* unlink MENU */
544     prev = menu->prev;
545     next = menu->next;
546     if (prev != NULL)
547 	prev->next = next;
548     if (next != NULL)
549 	next->prev = prev;
550 
551 /* fix the index */
552     if (parent == NULL) {
553 	const int       len = (menu->len + HSPACE);
554 
555 	if (o->CurrentBar->tail == menu)
556 	    o->CurrentBar->tail = prev;
557 	if (o->CurrentBar->head == menu)
558 	    o->CurrentBar->head = next;
559 
560 	for (next = menu->next; next != NULL; next = next->next)
561 	    next->x -= len;
562     } else {
563 	for (item = parent->tail; item != NULL; item = item->prev) {
564 	    if (item->entry.type == MenuSubMenu
565 		&& item->entry.submenu.menu == menu) {
566 		item->entry.submenu.menu = NULL;
567 		rxvtlib_menuitem_free (o, menu->parent, item);
568 		break;
569 	    }
570 	}
571     }
572 
573     item = menu->tail;
574     while (item != NULL) {
575 	menuitem_t     *p = item->prev;
576 
577 	rxvtlib_menuitem_free (o, menu, item);
578 	item = p;
579     }
580 
581     if (menu->name != NULL)
582 	FREE (menu->name);
583     FREE (menu);
584 
585     return parent;
586 }
587 #endif
588 
589 #ifdef MENUBAR
590 /* INTPROTO */
rxvtlib_menu_add(rxvtlib * o,menu_t * parent,char * path)591 menu_t         *rxvtlib_menu_add (rxvtlib *o, menu_t * parent, char *path)
592 {
593     menu_t         *menu;
594 
595     assert (o->CurrentBar != NULL);
596 
597     if (strchr (path, '/') != NULL) {
598 	register char  *p;
599 
600 	if (path[0] == '/') {
601 	    /* shouldn't happen */
602 	    path++;
603 	    parent = NULL;
604 	}
605 	while ((p = strchr (path, '/')) != NULL) {
606 	    p[0] = '\0';
607 	    if (path[0] == '\0')
608 		return NULL;
609 
610 	    parent = rxvtlib_menu_add (o, parent, path);
611 	    path = (p + 1);
612 	}
613     }
614     if (!strcmp (path, DOTS))
615 	return (parent != NULL ? parent->parent : parent);
616 
617     if (!strcmp (path, DOT) || path[0] == '\0')
618 	return parent;
619 
620 /* allocate a new menu */
621     if ((menu = (menu_t *) MALLOC (sizeof (menu_t))) == NULL)
622 	return parent;
623 
624     menu->width = 0;
625     menu->parent = parent;
626     menu->len = strlen (path);
627     menu->name = MALLOC ((menu->len + 1));
628     if (menu->name == NULL) {
629 	FREE (menu);
630 	return parent;
631     }
632     STRCPY (menu->name, path);
633 
634 /* initialize head/tail */
635     menu->head = menu->tail = NULL;
636     menu->prev = menu->next = NULL;
637 
638     menu->win = None;
639     menu->x = menu->y = menu->w = menu->h = 0;
640     menu->item = NULL;
641 
642 /* add to tail of list */
643     if (parent == NULL) {
644 	menu->prev = o->CurrentBar->tail;
645 	if (o->CurrentBar->tail != NULL)
646 	    o->CurrentBar->tail->next = menu;
647 	o->CurrentBar->tail = menu;
648 	if (o->CurrentBar->head == NULL)
649 	    o->CurrentBar->head = menu;	/* fix head */
650 	if (menu->prev)
651 	    menu->x = (menu->prev->x + menu->prev->len + HSPACE);
652     } else {
653 	menuitem_t     *item;
654 
655 	item = rxvtlib_menuitem_add (o, parent, path, "", "");
656 	if (item == NULL) {
657 	    FREE (menu);
658 	    return parent;
659 	}
660 	assert (item->entry.type == MenuLabel);
661 	item->entry.type = MenuSubMenu;
662 	item->entry.submenu.menu = menu;
663     }
664 
665     return menu;
666 }
667 #endif
668 
669 #ifdef MENUBAR
670 /* INTPROTO */
rxvtlib_drawbox_menubar(rxvtlib * o,int x,int len,int state)671 void            rxvtlib_drawbox_menubar (rxvtlib *o, int x, int len, int state)
672 {
673     GC              top, bot;
674 
675     x = Width2Pixel (x);
676     len = Width2Pixel (len + HSPACE);
677     if (x >= o->TermWin.width)
678 	return;
679     else if (x + len >= o->TermWin.width)
680 	len = (TermWin_TotalWidth () - x);
681 
682 #ifdef MENUBAR_SHADOW_IN
683     state = -state;
684 #endif
685     switch (state) {
686     case +1:
687 	top = o->topShadowGC;
688 	bot = o->botShadowGC;
689 	break;			/* SHADOW_OUT */
690     case -1:
691 	top = o->botShadowGC;
692 	bot = o->topShadowGC;
693 	break;			/* SHADOW_IN */
694     default:
695 	top = bot = o->neutralGC;
696 	break;			/* neutral */
697     }
698 
699     rxvtlib_Draw_Shadow (o, o->menuBar.win, top, bot, x, 0, len, menuBar_TotalHeight ());
700 }
701 #endif
702 
703 #ifdef MENUBAR
704 /* INTPROTO */
rxvtlib_drawtriangle(rxvtlib * o,int x,int y,int state)705 void            rxvtlib_drawtriangle (rxvtlib *o, int x, int y, int state)
706 {
707     GC              top, bot;
708     int             w;
709 
710 #ifdef MENU_SHADOW_IN
711     state = -state;
712 #endif
713     switch (state) {
714     case +1:
715 	top = o->topShadowGC;
716 	bot = o->botShadowGC;
717 	break;			/* SHADOW_OUT */
718     case -1:
719 	top = o->botShadowGC;
720 	bot = o->topShadowGC;
721 	break;			/* SHADOW_IN */
722     default:
723 	top = bot = o->neutralGC;
724 	break;			/* neutral */
725     }
726 
727     w = Height2Pixel (1) - 2 * SHADOW;
728 
729     x -= SHADOW + (3 * w / 2);
730     y += SHADOW * 3;
731 
732     rxvtlib_Draw_Triangle (o, o->ActiveMenu->win, top, bot, x, y, w, 'r');
733 }
734 #endif
735 
736 #ifdef MENUBAR
737 /* INTPROTO */
rxvtlib_drawbox_menuitem(rxvtlib * o,int y,int state)738 void            rxvtlib_drawbox_menuitem (rxvtlib *o, int y, int state)
739 {
740     GC              top, bot;
741 
742 #ifdef MENU_SHADOW_IN
743     state = -state;
744 #endif
745     switch (state) {
746     case +1:
747 	top = o->topShadowGC;
748 	bot = o->botShadowGC;
749 	break;			/* SHADOW_OUT */
750     case -1:
751 	top = o->botShadowGC;
752 	bot = o->topShadowGC;
753 	break;			/* SHADOW_IN */
754     default:
755 	top = bot = o->neutralGC;
756 	break;			/* neutral */
757     }
758 
759     rxvtlib_Draw_Shadow (o, o->ActiveMenu->win, top, bot,
760 		 SHADOW + 0,
761 		 SHADOW + y,
762 		 o->ActiveMenu->w - 2 * (SHADOW), HEIGHT_TEXT + 2 * SHADOW);
763     XFlush (o->Xdisplay);
764 }
765 #endif
766 
767 #ifdef DEBUG_MENU_LAYOUT
768 #ifdef MENUBAR
769 /* INTPROTO */
rxvtlib_print_menu_ancestors(rxvtlib * o,menu_t * menu)770 void            rxvtlib_print_menu_ancestors (rxvtlib *o, menu_t * menu)
771 {
772     if (menu == NULL) {
773 	fprintf (stderr, "Top Level menu\n");
774 	return;
775     }
776     fprintf (stderr, "menu %s ", menu->name);
777     if (menu->parent != NULL) {
778 	menuitem_t     *item;
779 
780 	for (item = menu->parent->head; item != NULL; item = item->next) {
781 	    if (item->entry.type == MenuSubMenu
782 		&& item->entry.submenu.menu == menu) {
783 		break;
784 	    }
785 	}
786 	if (item == NULL) {
787 	    fprintf (stderr, "is an orphan!\n");
788 	    return;
789 	}
790     }
791     fprintf (stderr, "\n");
792     rxvtlib_print_menu_ancestors (o, menu->parent);
793 }
794 #endif
795 
796 #ifdef MENUBAR
797 /* INTPROTO */
rxvtlib_print_menu_descendants(rxvtlib * o,menu_t * menu)798 void            rxvtlib_print_menu_descendants (rxvtlib *o, menu_t * menu)
799 {
800     menuitem_t     *item;
801     menu_t         *parent;
802     int             i, level = 0;
803 
804     parent = menu;
805     do {
806 	level++;
807 	parent = parent->parent;
808     }
809     while (parent != NULL);
810 
811     for (i = 0; i < level; i++)
812 	fprintf (stderr, ">");
813     fprintf (stderr, "%s\n", menu->name);
814 
815     for (item = menu->head; item != NULL; item = item->next) {
816 	if (item->entry.type == MenuSubMenu) {
817 	    if (item->entry.submenu.menu == NULL)
818 		fprintf (stderr, "> %s == NULL\n", item->name);
819 	    else
820 		rxvtlib_print_menu_descendants (o, item->entry.submenu.menu);
821 	} else {
822 	    for (i = 0; i < level; i++)
823 		fprintf (stderr, "+");
824 	    if (item->entry.type == MenuLabel)
825 		fprintf (stderr, "label: ");
826 	    fprintf (stderr, "%s\n", item->name);
827 	}
828     }
829 
830     for (i = 0; i < level; i++)
831 	fprintf (stderr, "<");
832     fprintf (stderr, "\n");
833 }
834 #endif
835 #endif
836 
837 #ifdef MENUBAR
838 /* pop up/down the current menu and redraw the menuBar button */
839 /* INTPROTO */
rxvtlib_menu_show(rxvtlib * o)840 void            rxvtlib_menu_show (rxvtlib *o)
841 {
842     int             x, y, xright;
843     menuitem_t     *item;
844 
845     if (o->ActiveMenu == NULL)
846 	return;
847 
848     x = o->ActiveMenu->x;
849     if (o->ActiveMenu->parent == NULL) {
850 	register int    h;
851 
852 	rxvtlib_drawbox_menubar (o, x, o->ActiveMenu->len, -1);
853 	x = Width2Pixel (x);
854 
855 	o->ActiveMenu->y = 1;
856 	o->ActiveMenu->w = Menu_PixelWidth (o->ActiveMenu);
857 
858 	if ((x + o->ActiveMenu->w) >= o->TermWin.width)
859 	    x = (TermWin_TotalWidth () - o->ActiveMenu->w);
860 
861 	/* find the height */
862 	for (h = 0, item = o->ActiveMenu->head; item != NULL; item = item->next)
863 	    h += isSeparator (item->name) ? HEIGHT_SEPARATOR
864 		: HEIGHT_TEXT + 2 * SHADOW;
865 	o->ActiveMenu->h = h + 2 * SHADOW;
866     }
867     if (o->ActiveMenu->win == None) {
868 	o->ActiveMenu->win = XCreateSimpleWindow (o->Xdisplay, o->TermWin.vt,
869 					       x,
870 					       o->ActiveMenu->y,
871 					       o->ActiveMenu->w,
872 					       o->ActiveMenu->h,
873 					       0,
874 					       o->PixColors[Color_fg],
875 					       o->PixColors[Color_scroll]);
876 	XMapWindow (o->Xdisplay, o->ActiveMenu->win);
877     }
878     rxvtlib_Draw_Shadow (o, o->ActiveMenu->win,
879 		 o->topShadowGC, o->botShadowGC, 0, 0, o->ActiveMenu->w, o->ActiveMenu->h);
880 
881 /* determine the correct right-alignment */
882     for (xright = 0, item = o->ActiveMenu->head; item != NULL; item = item->next)
883 	if (item->len2 > xright)
884 	    xright = item->len2;
885 
886     for (y = 0, item = o->ActiveMenu->head; item != NULL; item = item->next) {
887 	const int       xoff = (SHADOW + Width2Pixel (HSPACE) / 2);
888 	register int    h;
889 	GC              gc = o->menubarGC;
890 
891 	if (isSeparator (item->name)) {
892 	    rxvtlib_Draw_Shadow (o, o->ActiveMenu->win,
893 			 o->topShadowGC, o->botShadowGC,
894 			 SHADOW, y + SHADOW + 1, o->ActiveMenu->w - 2 * SHADOW,
895 			 0);
896 	    h = HEIGHT_SEPARATOR;
897 	} else {
898 	    char           *name = item->name;
899 	    int             len = item->len;
900 
901 	    if (item->entry.type == MenuLabel) {
902 		gc = o->botShadowGC;
903 	    } else if (item->entry.type == MenuSubMenu) {
904 		int             x1, y1;
905 		menuitem_t     *it;
906 		menu_t         *menu = item->entry.submenu.menu;
907 
908 		rxvtlib_drawtriangle (o, o->ActiveMenu->w, y, +1);
909 
910 		name = menu->name;
911 		len = menu->len;
912 
913 		y1 = o->ActiveMenu->y + y;
914 
915 		menu->w = Menu_PixelWidth (menu);
916 
917 		/* place sub-menu at midpoint of parent menu */
918 		x1 = o->ActiveMenu->w / 2;
919 		if (x1 > menu->w)	/* right-flush menu if too small */
920 		    x1 += (x1 - menu->w);
921 		x1 += x;
922 
923 		/* find the height of this submenu */
924 		for (h = 0, it = menu->head; it != NULL; it = it->next)
925 		    h += isSeparator (it->name) ? HEIGHT_SEPARATOR
926 			: HEIGHT_TEXT + 2 * SHADOW;
927 		menu->h = h + 2 * SHADOW;
928 
929 		/* ensure menu is in window limits */
930 		if ((x1 + menu->w) >= o->TermWin.width)
931 		    x1 = (TermWin_TotalWidth () - menu->w);
932 
933 		if ((y1 + menu->h) >= o->TermWin.height)
934 		    y1 = (TermWin_TotalHeight () - menu->h);
935 
936 		menu->x = (x1 < 0 ? 0 : x1);
937 		menu->y = (y1 < 0 ? 0 : y1);
938 	    } else if (item->name2 && !strcmp (name, item->name2))
939 		name = NULL;
940 
941 	    if (len && name)
942 		XDrawString (o->Xdisplay,
943 			     o->ActiveMenu->win, gc,
944 			     xoff,
945 			     2 * SHADOW + y + o->TermWin.font->ascent + 1,
946 			     name, len);
947 
948 	    len = item->len2;
949 	    name = item->name2;
950 	    if (len && name)
951 		XDrawString (o->Xdisplay,
952 			     o->ActiveMenu->win, gc,
953 			     o->ActiveMenu->w - (xoff + Width2Pixel (xright)),
954 			     2 * SHADOW + y + o->TermWin.font->ascent + 1,
955 			     name, len);
956 
957 	    h = HEIGHT_TEXT + 2 * SHADOW;
958 	}
959 	y += h;
960     }
961 }
962 #endif
963 
964 #ifdef MENUBAR
965 /* INTPROTO */
rxvtlib_menu_display(rxvtlib * o,void (* update)(rxvtlib *))966 void            rxvtlib_menu_display (rxvtlib *o, void (*update) (rxvtlib *))
967 {
968     if (o->ActiveMenu == NULL)
969 	return;
970     if (o->ActiveMenu->win != None)
971 	XDestroyWindow (o->Xdisplay, o->ActiveMenu->win);
972     o->ActiveMenu->win = None;
973     o->ActiveMenu->item = NULL;
974 
975     if (o->ActiveMenu->parent == NULL)
976 	rxvtlib_drawbox_menubar (o, o->ActiveMenu->x, o->ActiveMenu->len, +1);
977     o->ActiveMenu = o->ActiveMenu->parent;
978     update (o);
979 }
980 #endif
981 
982 #ifdef MENUBAR
983 /* INTPROTO */
rxvtlib_menu_hide_all(rxvtlib * o)984 void            rxvtlib_menu_hide_all (rxvtlib *o)
985 {
986     rxvtlib_menu_display (o, rxvtlib_menu_hide_all);
987 }
988 
989 #endif
990 
991 #ifdef MENUBAR
992 /* INTPROTO */
rxvtlib_menu_hide(rxvtlib * o)993 void            rxvtlib_menu_hide (rxvtlib *o)
994 {
995     rxvtlib_menu_display (o, rxvtlib_menu_show);
996 }
997 
998 #endif
999 
1000 #ifdef MENUBAR
1001 /* INTPROTO */
rxvtlib_menu_clear(rxvtlib * o,menu_t * menu)1002 void            rxvtlib_menu_clear (rxvtlib *o, menu_t * menu)
1003 {
1004     if (menu != NULL) {
1005 	menuitem_t     *item = menu->tail;
1006 
1007 	while (item != NULL) {
1008 	    rxvtlib_menuitem_free (o, menu, item);
1009 	    /* it didn't get freed ... why? */
1010 	    if (item == menu->tail)
1011 		return;
1012 	    item = menu->tail;
1013 	}
1014 	menu->width = 0;
1015     }
1016 }
1017 #endif
1018 
1019 #ifdef MENUBAR
1020 /* INTPROTO */
rxvtlib_menubar_clear(rxvtlib * o)1021 void            rxvtlib_menubar_clear (rxvtlib *o)
1022 {
1023     if (o->CurrentBar != NULL) {
1024 	menu_t         *menu = o->CurrentBar->tail;
1025 
1026 	while (menu != NULL) {
1027 	    menu_t         *prev = menu->prev;
1028 
1029 	    rxvtlib_menu_delete (o, menu);
1030 	    menu = prev;
1031 	}
1032 	o->CurrentBar->head = o->CurrentBar->tail = o->ActiveMenu = NULL;
1033 
1034 	if (o->CurrentBar->title) {
1035 	    FREE (o->CurrentBar->title);
1036 	    o->CurrentBar->title = NULL;
1037 	}
1038 	rxvtlib_menuarrow_free (o, 0);	/* remove all arrow functions */
1039     }
1040     o->ActiveMenu = NULL;
1041 }
1042 #endif
1043 
1044 #if (MENUBAR_MAX > 1)
1045 #ifdef MENUBAR
1046 /* find if menu already exists */
1047 /* INTPROTO */
rxvtlib_menubar_find(rxvtlib * o,const char * name)1048 bar_t          *rxvtlib_menubar_find (rxvtlib *o, const char *name)
1049 {
1050     bar_t          *bar = o->CurrentBar;
1051 
1052 #ifdef DEBUG_MENUBAR_STACKING
1053     fprintf (stderr, "looking for [menu:%s] ...", name ? name : "(nil)");
1054 #endif
1055     if (bar == NULL || name == NULL)
1056 	return NULL;
1057 
1058     if (strlen (name) && strcmp (name, "*")) {
1059 	do {
1060 	    if (!strcmp (bar->name, name)) {
1061 #ifdef DEBUG_MENUBAR_STACKING
1062 		fprintf (stderr, " found!\n");
1063 #endif
1064 		return bar;
1065 	    }
1066 	    bar = bar->next;
1067 	}
1068 	while (bar != o->CurrentBar);
1069 	bar = NULL;
1070     }
1071 #ifdef DEBUG_MENUBAR_STACKING
1072     fprintf (stderr, "%s found!\n", (bar ? "" : " NOT"));
1073 #endif
1074 
1075     return bar;
1076 }
1077 #endif
1078 
1079 #ifdef MENUBAR
1080 /* INTPROTO */
rxvtlib_menubar_push(rxvtlib * o,const char * name)1081 int             rxvtlib_menubar_push (rxvtlib *o, const char *name)
1082 {
1083     int             ret = 1;
1084     bar_t          *bar;
1085 
1086     if (o->CurrentBar == NULL) {
1087 	/* allocate first one */
1088 	bar = (bar_t *) MALLOC (sizeof (bar_t));
1089 
1090 	if (bar == NULL)
1091 	    return 0;
1092 
1093 	MEMSET (bar, 0, sizeof (bar_t));
1094 	/* circular linked-list */
1095 	bar->next = bar->prev = bar;
1096 	bar->head = bar->tail = NULL;
1097 	bar->title = NULL;
1098 	o->CurrentBar = bar;
1099 	o->Nbars++;
1100 
1101 	rxvtlib_menubar_clear (o);
1102     } else {
1103 	/* find if menu already exists */
1104 	bar = rxvtlib_menubar_find (o, name);
1105 	if (bar != NULL) {
1106 	    /* found it, use it */
1107 	    o->CurrentBar = bar;
1108 	} else {
1109 	    /* create if needed, or reuse the existing empty menubar */
1110 	    if (o->CurrentBar->head != NULL) {
1111 		/* need to malloc another one */
1112 		if (o->Nbars < MENUBAR_MAX)
1113 		    bar = (bar_t *) MALLOC (sizeof (bar_t));
1114 		else
1115 		    bar = NULL;
1116 
1117 		/* malloc failed or too many menubars, reuse another */
1118 		if (bar == NULL) {
1119 		    bar = o->CurrentBar->next;
1120 		    ret = -1;
1121 		} else {
1122 		    bar->head = bar->tail = NULL;
1123 		    bar->title = NULL;
1124 
1125 		    bar->next = o->CurrentBar->next;
1126 		    o->CurrentBar->next = bar;
1127 		    bar->prev = o->CurrentBar;
1128 		    bar->next->prev = bar;
1129 
1130 		    o->Nbars++;
1131 		}
1132 		o->CurrentBar = bar;
1133 
1134 	    }
1135 	    rxvtlib_menubar_clear (o);
1136 	}
1137     }
1138 
1139 /* give menubar this name */
1140     STRNCPY (o->CurrentBar->name, name, MAXNAME);
1141     o->CurrentBar->name[MAXNAME - 1] = '\0';
1142 
1143     return ret;
1144 }
1145 #endif
1146 
1147 #ifdef MENUBAR
1148 /* switch to a menu called NAME and remove it */
1149 /* INTPROTO */
rxvtlib_menubar_remove(rxvtlib * o,const char * name)1150 void            rxvtlib_menubar_remove (rxvtlib *o, const char *name)
1151 {
1152     bar_t          *bar;
1153 
1154     if ((bar = rxvtlib_menubar_find (o, name)) == NULL)
1155 	return;
1156     o->CurrentBar = bar;
1157 
1158     do {
1159 	rxvtlib_menubar_clear (o);
1160 	/*
1161 	 * pop a menubar, clean it up first
1162 	 */
1163 	if (o->CurrentBar != NULL) {
1164 	    bar_t          *prev = o->CurrentBar->prev;
1165 	    bar_t          *next = o->CurrentBar->next;
1166 
1167 	    if (prev == next && prev == o->CurrentBar) {	/* only 1 left */
1168 		prev = NULL;
1169 		o->Nbars = 0;	/* safety */
1170 	    } else {
1171 		next->prev = prev;
1172 		prev->next = next;
1173 		o->Nbars--;
1174 	    }
1175 
1176 	    FREE (o->CurrentBar);
1177 	    o->CurrentBar = prev;
1178 	}
1179     }
1180     while (o->CurrentBar && !strcmp (name, "*"));
1181 }
1182 #endif
1183 
1184 #ifdef MENUBAR
1185 /* INTPROTO */
action_decode(FILE * fp,action_t * act)1186 void            action_decode (FILE * fp, action_t * act)
1187 {
1188     unsigned char  *str;
1189     short           len;
1190 
1191     if (act == NULL || (len = act->len) == 0 || (str = act->str) == NULL)
1192 	return;
1193 
1194     if (act->type == MenuTerminalAction) {
1195 	fprintf (fp, "^@");
1196 	/* can strip trailing ^G from XTerm sequence */
1197 	if (str[0] == 033 && str[1] == ']' && str[len - 1] == 007)
1198 	    len--;
1199     } else if (str[0] == 033) {
1200 	switch (str[1]) {
1201 	case '[':
1202 	case ']':
1203 	    break;
1204 
1205 	case 'x':
1206 	    /* can strip trailing '\r' from M-x sequence */
1207 	    if (str[len - 1] == '\r')
1208 		len--;
1209 	    /* FALLTHROUGH */
1210 
1211 	default:
1212 	    fprintf (fp, "M-");	/* meta prefix */
1213 	    str++;
1214 	    len--;
1215 	    break;
1216 	}
1217     }
1218 /*
1219  * control character form is preferred, since backslash-escaping
1220  * can be really ugly looking when the backslashes themselves also
1221  * have to be escaped to avoid Shell (or whatever scripting
1222  * language) interpretation
1223  */
1224     while (len > 0) {
1225 	unsigned char   ch = *str++;
1226 
1227 	switch (ch) {
1228 	case 033:
1229 	    fprintf (fp, "\\E");
1230 	    break;		/* escape */
1231 	case '\r':
1232 	    fprintf (fp, "\\r");
1233 	    break;		/* carriage-return */
1234 	case '\\':
1235 	    fprintf (fp, "\\\\");
1236 	    break;		/* backslash */
1237 	case '^':
1238 	    fprintf (fp, "\\^");
1239 	    break;		/* caret */
1240 	case 127:
1241 	    fprintf (fp, "^?");
1242 	default:
1243 	    if (ch <= 31)
1244 		fprintf (fp, "^%c", ('@' + ch));
1245 	    else if (ch > 127)
1246 		fprintf (fp, "\\%o", ch);
1247 	    else
1248 		fprintf (fp, "%c", ch);
1249 	    break;
1250 	}
1251 	len--;
1252     }
1253     fprintf (fp, "\n");
1254 }
1255 #endif
1256 
1257 #ifdef MENUBAR
1258 /* INTPROTO */
rxvtlib_menu_dump(rxvtlib * o,FILE * fp,menu_t * menu)1259 void            rxvtlib_menu_dump (rxvtlib *o, FILE * fp, menu_t * menu)
1260 {
1261     menuitem_t     *item;
1262 
1263 /* create a new menu and clear it */
1264     fprintf (fp, (menu->parent ? "./%s/*\n" : "/%s/*\n"), menu->name);
1265 
1266     for (item = menu->head; item != NULL; item = item->next) {
1267 	switch (item->entry.type) {
1268 	case MenuSubMenu:
1269 	    if (item->entry.submenu.menu == NULL)
1270 		fprintf (fp, "> %s == NULL\n", item->name);
1271 	    else
1272 		rxvtlib_menu_dump (o, fp, item->entry.submenu.menu);
1273 	    break;
1274 
1275 	case MenuLabel:
1276 	    fprintf (fp, "{%s}\n", (strlen (item->name) ? item->name : "-"));
1277 	    break;
1278 
1279 	case MenuTerminalAction:
1280 	case MenuAction:
1281 	    fprintf (fp, "{%s}", item->name);
1282 	    if (item->name2 != NULL && strlen (item->name2))
1283 		fprintf (fp, "{%s}", item->name2);
1284 	    fprintf (fp, "\t");
1285 	    action_decode (fp, &(item->entry.action));
1286 	    break;
1287 	}
1288     }
1289 
1290     fprintf (fp, (menu->parent ? "../\n" : "/\n\n"));
1291 }
1292 #endif
1293 
1294 #ifdef MENUBAR
1295 /* INTPROTO */
rxvtlib_menubar_dump(rxvtlib * o,FILE * fp)1296 void            rxvtlib_menubar_dump (rxvtlib *o, FILE * fp)
1297 {
1298     bar_t          *bar = o->CurrentBar;
1299     time_t          t;
1300 
1301     if (bar == NULL || fp == NULL)
1302 	return;
1303     time (&t);
1304 
1305     fprintf (fp,
1306 	     "# " APL_SUBCLASS " (%s)  Pid: %u\n# Date: %s\n\n",
1307 	     o->rs[Rs_name], (unsigned int)getpid (), ctime (&t));
1308 
1309 /* dump in reverse order */
1310     bar = o->CurrentBar->prev;
1311     do {
1312 	menu_t         *menu;
1313 	int             i;
1314 
1315 	fprintf (fp, "[menu:%s]\n", bar->name);
1316 
1317 	if (bar->title != NULL)
1318 	    fprintf (fp, "[title:%s]\n", bar->title);
1319 
1320 	for (i = 0; i < NARROWS; i++) {
1321 	    switch (bar->arrows[i].type) {
1322 	    case MenuTerminalAction:
1323 	    case MenuAction:
1324 		fprintf (fp, "<%c>", o->Arrows[i].name);
1325 		action_decode (fp, &(bar->arrows[i]));
1326 		break;
1327 	    }
1328 	}
1329 	fprintf (fp, "\n");
1330 
1331 	for (menu = bar->head; menu != NULL; menu = menu->next)
1332 	    rxvtlib_menu_dump (o, fp, menu);
1333 
1334 	fprintf (fp, "\n[done:%s]\n\n", bar->name);
1335 	bar = bar->prev;
1336     }
1337     while (bar != o->CurrentBar->prev);
1338 }
1339 #endif
1340 #endif				/* (MENUBAR_MAX > 1) */
1341 
1342 /*
1343  * read in menubar commands from FILENAME
1344  * ignore all input before the tag line [menu] or [menu:???]
1345  *
1346  * Note that since File_find () is used, FILENAME can be semi-colon
1347  * delimited such that the second part can refer to a tag
1348  * so that a large `database' of menus can be collected together
1349  *
1350  * FILENAME = "file"
1351  * FILENAME = "file;"
1352  *      read `file' starting with first [menu] or [menu:???] line
1353  *
1354  * FILENAME = "file;tag"
1355  *      read `file' starting with [menu:tag]
1356  */
1357 /* EXTPROTO */
rxvtlib_menubar_read(rxvtlib * o,const char * filename)1358 void            rxvtlib_menubar_read (rxvtlib *o, const char *filename)
1359 {
1360 #ifdef MENUBAR
1361 /* read in a menu from a file */
1362     FILE           *fp;
1363     char            buffer[256];
1364     char           *p, *file, *tag = NULL;
1365 
1366     file = (char *)rxvtlib_File_find (o, filename, ".menu");
1367     if (file == NULL)
1368 	return;
1369     fp = fopen (file, "rb");
1370     FREE (file);
1371     if (fp == NULL)
1372 	return;
1373 
1374 #if (MENUBAR_MAX > 1)
1375 /* semi-colon delimited */
1376     if ((tag = strchr (filename, ';')) != NULL) {
1377 	tag++;
1378 	if (*tag == '\0')
1379 	    tag = NULL;
1380     }
1381 #endif				/* (MENUBAR_MAX > 1) */
1382 #ifdef DEBUG_MENU
1383     fprintf (stderr, "[read:%s]\n", p);
1384     if (tag)
1385 	fprintf (stderr, "looking for [menu:%s]\n", tag);
1386 #endif
1387 
1388     while ((p = fgets (buffer, sizeof (buffer), fp)) != NULL) {
1389 	int             n;
1390 
1391 	if ((n = Str_match (p, "[menu")) != 0) {
1392 	    if (tag) {
1393 		/* looking for [menu:tag] */
1394 		if (p[n] == ':' && p[n + 1] != ']') {
1395 		    n++;
1396 		    n += Str_match (p + n, tag);
1397 		    if (p[n] == ']') {
1398 #ifdef DEBUG_MENU
1399 			fprintf (stderr, "[menu:%s]\n", tag);
1400 #endif
1401 			break;
1402 		    }
1403 		}
1404 	    } else if (p[n] == ':' || p[n] == ']')
1405 		break;
1406 	}
1407     }
1408 
1409 /* found [menu], [menu:???] tag */
1410     while (p != NULL) {
1411 	int             n;
1412 
1413 #ifdef DEBUG_MENU
1414 	fprintf (stderr, "read line = %s\n", p);
1415 #endif
1416 
1417 	/* looking for [done:tag] or [done:] */
1418 	if ((n = Str_match (p, "[done")) != 0) {
1419 	    if (p[n] == ']') {
1420 		o->menu_readonly = 1;
1421 		break;
1422 	    } else if (p[n] == ':') {
1423 		n++;
1424 		if (p[n] == ']') {
1425 		    o->menu_readonly = 1;
1426 		    break;
1427 		} else if (tag) {
1428 		    n += Str_match (p + n, tag);
1429 		    if (p[n] == ']') {
1430 #ifdef DEBUG_MENU
1431 			fprintf (stderr, "[done:%s]\n", tag);
1432 #endif
1433 			o->menu_readonly = 1;
1434 			break;
1435 		    }
1436 		} else {
1437 		    /* what? ... skip this line */
1438 		    p[0] = COMMENT_CHAR;
1439 		}
1440 	    }
1441 	}
1442 	/*
1443 	 * remove leading/trailing space
1444 	 * and strip-off leading/trailing quotes
1445 	 * skip blank or comment lines
1446 	 */
1447 	(void)Str_trim (p);
1448 	if (*p && *p != '#') {
1449 	    o->menu_readonly = 0;	/* if case we read another file */
1450 	    rxvtlib_menubar_dispatch (o, p);
1451 	}
1452 	/* get another line */
1453 	p = fgets (buffer, sizeof (buffer), fp);
1454     }
1455 
1456     fclose (fp);
1457 #endif
1458 }
1459 
1460 /*
1461  * user interface for building/deleting and otherwise managing menus
1462  */
1463 /* EXTPROTO */
rxvtlib_menubar_dispatch(rxvtlib * o,char * str)1464 void            rxvtlib_menubar_dispatch (rxvtlib *o, char *str)
1465 {
1466 #ifdef MENUBAR
1467     int             n, cmd;
1468     char           *path, *name, *name2;
1469 
1470     if (menubar_visible () && o->ActiveMenu != NULL)
1471 	rxvtlib_menubar_expose (o);
1472     else
1473 	o->ActiveMenu = NULL;
1474 
1475     cmd = *str;
1476     switch (cmd) {
1477     case '.':
1478     case '/':			/* absolute & relative path */
1479     case MENUITEM_BEG:		/* menuitem */
1480 	/* add `+' prefix for these cases */
1481 	cmd = '+';
1482 	break;
1483 
1484     case '+':
1485     case '-':
1486 	str++;			/* skip cmd character */
1487 	break;
1488 
1489     case '<':
1490 #if (MENUBAR_MAX > 1)
1491 	if (o->CurrentBar == NULL)
1492 	    break;
1493 #endif				/* (MENUBAR_MAX > 1) */
1494 	if (str[1] && str[2] == '>')	/* arrow commands */
1495 	    rxvtlib_menuarrow_add (o, str);
1496 	break;
1497 
1498     case '[':			/* extended command */
1499 	while (str[0] == '[') {
1500 	    char           *next = (++str);	/* skip leading '[' */
1501 
1502 	    if (str[0] == ':') {	/* [:command:] */
1503 		do {
1504 		    next++;
1505 		    if ((next = strchr (next, ':')) == NULL)
1506 			return;	/* parse error */
1507 		}
1508 		while (next[1] != ']');
1509 		/* remove and skip ':]' */
1510 		*next = '\0';
1511 		next += 2;
1512 	    } else {
1513 		if ((next = strchr (next, ']')) == NULL)
1514 		    return;	/* parse error */
1515 		/* remove and skip ']' */
1516 		*next = '\0';
1517 		next++;
1518 	    }
1519 
1520 	    if (str[0] == ':') {
1521 		int             saved;
1522 
1523 		/* try and dispatch it, regardless of read/write status */
1524 		saved = o->menu_readonly;
1525 		o->menu_readonly = 0;
1526 		rxvtlib_menubar_dispatch (o, str + 1);
1527 		o->menu_readonly = saved;
1528 	    }
1529 	    /* these ones don't require menu stacking */
1530 	    else if (!strcmp (str, "clear")) {
1531 		rxvtlib_menubar_clear (o);
1532 	    } else if (!strcmp (str, "done") || Str_match (str, "done:")) {
1533 		o->menu_readonly = 1;
1534 	    } else if (!strcmp (str, "show")) {
1535 		rxvtlib_map_menuBar (o, 1);
1536 		o->menu_readonly = 1;
1537 	    } else if (!strcmp (str, "hide")) {
1538 		rxvtlib_map_menuBar (o, 0);
1539 		o->menu_readonly = 1;
1540 	    } else if ((n = Str_match (str, "read:")) != 0) {
1541 		/* read in a menu from a file */
1542 		str += n;
1543 		rxvtlib_menubar_read (o, str);
1544 	    } else if ((n = Str_match (str, "title:")) != 0) {
1545 		str += n;
1546 		if (o->CurrentBar != NULL && !o->menu_readonly) {
1547 		    if (*str) {
1548 			name = REALLOC (o->CurrentBar->title, strlen (str) + 1);
1549 			if (name != NULL) {
1550 			    STRCPY (name, str);
1551 			    o->CurrentBar->title = name;
1552 			}
1553 			rxvtlib_menubar_expose (o);
1554 		    } else {
1555 			FREE (o->CurrentBar->title);
1556 			o->CurrentBar->title = NULL;
1557 		    }
1558 		}
1559 	    } else if ((n = Str_match (str, "pixmap:")) != 0) {
1560 		str += n;
1561 		rxvtlib_xterm_seq (o, XTerm_Pixmap, str);
1562 	    }
1563 #if (MENUBAR_MAX > 1)
1564 	    else if ((n = Str_match (str, "rm")) != 0) {
1565 		str += n;
1566 		switch (str[0]) {
1567 		case ':':
1568 		    str++;
1569 		    rxvtlib_menubar_remove (o, str);
1570 		    break;
1571 
1572 		case '\0':
1573 		    rxvtlib_menubar_remove (o, str);
1574 		    break;
1575 
1576 		case '*':
1577 		    rxvtlib_menubar_remove (o, str);
1578 		    break;
1579 		}
1580 		o->menu_readonly = 1;
1581 	    } else if ((n = Str_match (str, "menu")) != 0) {
1582 		str += n;
1583 		switch (str[0]) {
1584 		case ':':
1585 		    str++;
1586 		    /* add/access menuBar */
1587 		    if (*str != '\0' && *str != '*')
1588 			rxvtlib_menubar_push (o, str);
1589 		    break;
1590 		default:
1591 		    if (o->CurrentBar == NULL) {
1592 			rxvtlib_menubar_push (o, "default");
1593 		    }
1594 		}
1595 
1596 		if (o->CurrentBar != NULL)
1597 		    o->menu_readonly = 0;	/* allow menu build commands */
1598 	    } else if (!strcmp (str, "dump")) {
1599 		/* dump current menubars to a file */
1600 		FILE           *fp;
1601 
1602 		/* enough space to hold the results */
1603 		char            buffer[32];
1604 
1605 		sprintf (buffer, "/tmp/" APL_SUBCLASS "-%u",
1606 			 (unsigned int)getpid ());
1607 
1608 		if ((fp = fopen (buffer, "wb")) != NULL) {
1609 		    rxvtlib_xterm_seq (o, XTerm_title, buffer);
1610 		    rxvtlib_menubar_dump (o, fp);
1611 		    fclose (fp);
1612 		}
1613 	    } else if (!strcmp (str, "next")) {
1614 		if (o->CurrentBar) {
1615 		    o->CurrentBar = o->CurrentBar->next;
1616 		    o->menu_readonly = 1;
1617 		}
1618 	    } else if (!strcmp (str, "prev")) {
1619 		if (o->CurrentBar) {
1620 		    o->CurrentBar = o->CurrentBar->prev;
1621 		    o->menu_readonly = 1;
1622 		}
1623 	    } else if (!strcmp (str, "swap")) {
1624 		/* swap the top 2 menus */
1625 		if (o->CurrentBar) {
1626 		    bar_t          *prev = o->CurrentBar->prev;
1627 		    bar_t          *next = o->CurrentBar->next;
1628 
1629 		    prev->next = next;
1630 		    next->prev = prev;
1631 
1632 		    o->CurrentBar->next = prev;
1633 		    o->CurrentBar->prev = prev->prev;
1634 
1635 		    prev->prev->next = o->CurrentBar;
1636 		    prev->prev = o->CurrentBar;
1637 
1638 		    o->CurrentBar = prev;
1639 		    o->menu_readonly = 1;
1640 		}
1641 	    }
1642 #endif				/* (MENUBAR_MAX > 1) */
1643 	    str = next;
1644 
1645 	    o->BuildMenu = o->ActiveMenu = NULL;
1646 	    rxvtlib_menubar_expose (o);
1647 #ifdef DEBUG_MENUBAR_STACKING
1648 	    fprintf (stderr, "menus are read%s\n",
1649 		     o->menu_readonly ? "only" : "/write");
1650 #endif
1651 	}
1652 	return;
1653 	break;
1654     }
1655 
1656 #if (MENUBAR_MAX > 1)
1657     if (o->CurrentBar == NULL)
1658 	return;
1659     if (o->menu_readonly) {
1660 #ifdef DEBUG_MENUBAR_STACKING
1661 	fprintf (stderr, "menus are read%s\n",
1662 		 o->menu_readonly ? "only" : "/write");
1663 #endif
1664 	return;
1665     }
1666 #endif				/* (MENUBAR_MAX > 1) */
1667 
1668     switch (cmd) {
1669     case '+':
1670     case '-':
1671 	path = name = str;
1672 
1673 	name2 = NULL;
1674 	/* parse STR, allow spaces inside (name)  */
1675 	if (path[0] != '\0') {
1676 	    name = strchr (path, MENUITEM_BEG);
1677 	    str = strchr (path, MENUITEM_END);
1678 	    if (name != NULL || str != NULL) {
1679 		if (name == NULL || str == NULL || str <= (name + 1)
1680 		    || (name > path && name[-1] != '/')) {
1681 		    print_error ("menu error <%s>\n", path);
1682 		    break;
1683 		}
1684 		if (str[1] == MENUITEM_BEG) {
1685 		    name2 = (str + 2);
1686 		    str = strchr (name2, MENUITEM_END);
1687 
1688 		    if (str == NULL) {
1689 			print_error ("menu error <%s>\n", path);
1690 			break;
1691 		    }
1692 		    name2[-2] = '\0';	/* remove prev MENUITEM_END */
1693 		}
1694 		if (name > path && name[-1] == '/')
1695 		    name[-1] = '\0';
1696 
1697 		*name++ = '\0';	/* delimit */
1698 		*str++ = '\0';	/* delimit */
1699 
1700 		while (isspace (*str))
1701 		    str++;	/* skip space */
1702 	    }
1703 #ifdef DEBUG_MENU
1704 	    fprintf (stderr,
1705 		     "`%c' path = <%s>, name = <%s>, name2 = <%s>, action = <%s>\n",
1706 		     cmd, (path ? path : "(nil)"), (name ? name : "(nil)"),
1707 		     (name2 ? name2 : "(nil)"), (str ? str : "(nil)")
1708 		);
1709 #endif
1710 	}
1711 	/* process the different commands */
1712 	switch (cmd) {
1713 	case '+':		/* add/replace existing menu or menuitem */
1714 	    if (path[0] != '\0') {
1715 		int             len;
1716 
1717 		path = rxvtlib_menu_find_base (o, &o->BuildMenu, path);
1718 		len = strlen (path);
1719 
1720 		/* don't allow menus called `*' */
1721 		if (path[0] == '*') {
1722 		    rxvtlib_menu_clear (o, o->BuildMenu);
1723 		    break;
1724 		} else if (len >= 2 && !strcmp ((path + len - 2), "/*")) {
1725 		    path[len - 2] = '\0';
1726 		}
1727 		if (path[0] != '\0')
1728 		    o->BuildMenu = rxvtlib_menu_add (o, o->BuildMenu, path);
1729 	    }
1730 	    if (name != NULL && name[0] != '\0')
1731 		rxvtlib_menuitem_add (o, o->BuildMenu,
1732 			      (strcmp (name, SEPARATOR_NAME) ? name : ""),
1733 			      name2, str);
1734 	    break;
1735 
1736 	case '-':		/* delete menu entry */
1737 	    if (!strcmp (path, "/*") && (name == NULL || name[0] == '\0')) {
1738 		rxvtlib_menubar_clear (o);
1739 		o->BuildMenu = NULL;
1740 		rxvtlib_menubar_expose (o);
1741 		break;
1742 	    } else if (path[0] != '\0') {
1743 		int             len;
1744 		menu_t         *menu = o->BuildMenu;
1745 
1746 		path = rxvtlib_menu_find_base (o, &menu, path);
1747 		len = strlen (path);
1748 
1749 		/* submenu called `*' clears all menu items */
1750 		if (path[0] == '*') {
1751 		    rxvtlib_menu_clear (o, menu);
1752 		    break;	/* done */
1753 		} else if (len >= 2 && !strcmp (&path[len - 2], "/*")) {
1754 		    /* done */
1755 		    break;
1756 		} else if (path[0] != '\0') {
1757 		    o->BuildMenu = NULL;
1758 		    break;
1759 		} else {
1760 		    o->BuildMenu = menu;
1761 		}
1762 	    }
1763 	    if (o->BuildMenu != NULL) {
1764 		if (name == NULL || name[0] == '\0') {
1765 		    o->BuildMenu = rxvtlib_menu_delete (o, o->BuildMenu);
1766 		} else {
1767 		    menuitem_t     *item;
1768 
1769 		    item = menuitem_find (o->BuildMenu,
1770 					  strcmp (name, SEPARATOR_NAME) ? name
1771 					  : "");
1772 
1773 		    if (item != NULL && item->entry.type != MenuSubMenu) {
1774 			rxvtlib_menuitem_free (o, o->BuildMenu, item);
1775 
1776 			/* fix up the width */
1777 			o->BuildMenu->width = 0;
1778 			for (item = o->BuildMenu->head;
1779 			     item != NULL; item = item->next) {
1780 			    if (o->BuildMenu->width < (item->len + item->len2))
1781 				o->BuildMenu->width = (item->len + item->len2);
1782 			}
1783 		    }
1784 		}
1785 		rxvtlib_menubar_expose (o);
1786 	    }
1787 	    break;
1788 	}
1789 	break;
1790     }
1791 #endif
1792 }
1793 
1794 #ifdef MENUBAR
1795 /* INTPROTO */
rxvtlib_draw_Arrows(rxvtlib * o,int name,int state)1796 void            rxvtlib_draw_Arrows (rxvtlib *o, int name, int state)
1797 {
1798     GC              top, bot;
1799 
1800     int             i;
1801 
1802 #ifdef MENU_SHADOW_IN
1803     state = -state;
1804 #endif
1805     switch (state) {
1806     case +1:
1807 	top = o->topShadowGC;
1808 	bot = o->botShadowGC;
1809 	break;			/* SHADOW_OUT */
1810     case -1:
1811 	top = o->botShadowGC;
1812 	bot = o->topShadowGC;
1813 	break;			/* SHADOW_IN */
1814     default:
1815 	top = bot = o->neutralGC;
1816 	break;			/* neutral */
1817     }
1818 
1819     if (!o->Arrows_x)
1820 	return;
1821 
1822     for (i = 0; i < NARROWS; i++) {
1823 	const int       w = Width2Pixel (1);
1824 	const int       y = (menuBar_TotalHeight () - w) / 2;
1825 	int             x = o->Arrows_x + (5 * Width2Pixel (i)) / 4;
1826 
1827 	if (!name || name == o->Arrows[i].name)
1828 	    rxvtlib_Draw_Triangle (o, o->menuBar.win, top, bot, x, y, w, o->Arrows[i].name);
1829     }
1830     XFlush (o->Xdisplay);
1831 }
1832 #endif
1833 
1834 /* EXTPROTO */
rxvtlib_menubar_expose(rxvtlib * o)1835 void            rxvtlib_menubar_expose (rxvtlib *o)
1836 {
1837 #ifdef MENUBAR
1838     menu_t         *menu;
1839     int             x;
1840 
1841     if (!menubar_visible () || o->menuBar.win == 0)
1842 	return;
1843 
1844     if (o->menubarGC == None) {
1845 	/* Create the graphics context */
1846 	XGCValues       gcvalue;
1847 
1848 	gcvalue.font = o->TermWin.font->fid;
1849 
1850 	gcvalue.foreground = (o->Xdepth <= 2 ?
1851 			      o->PixColors[Color_fg] : o->PixColors[Color_Black]);
1852 	o->menubarGC = XCreateGC (o->Xdisplay, o->menuBar.win,
1853 			       GCForeground | GCFont, &gcvalue);
1854 
1855 	gcvalue.foreground = o->PixColors[Color_scroll];
1856 	o->neutralGC = XCreateGC (o->Xdisplay, o->menuBar.win, GCForeground, &gcvalue);
1857 
1858 	gcvalue.foreground = o->PixColors[Color_bottomShadow];
1859 	o->botShadowGC = XCreateGC (o->Xdisplay, o->menuBar.win,
1860 				 GCForeground | GCFont, &gcvalue);
1861 
1862 	gcvalue.foreground = o->PixColors[Color_topShadow];
1863 	o->topShadowGC =
1864 	    XCreateGC (o->Xdisplay, o->menuBar.win, GCForeground, &gcvalue);
1865     }
1866 /* make sure the font is correct */
1867     XSetFont (o->Xdisplay, o->menubarGC, o->TermWin.font->fid);
1868     XSetFont (o->Xdisplay, o->botShadowGC, o->TermWin.font->fid);
1869     XClearWindow (o->Xdisplay, o->menuBar.win);
1870 
1871     rxvtlib_menu_hide_all (o);
1872 
1873     x = 0;
1874     if (o->CurrentBar != NULL) {
1875 	for (menu = o->CurrentBar->head; menu != NULL; menu = menu->next) {
1876 	    int             len = menu->len;
1877 
1878 	    x = (menu->x + menu->len + HSPACE);
1879 
1880 #ifdef DEBUG_MENU_LAYOUT
1881 	    rxvtlib_print_menu_descendants (o, menu);
1882 #endif
1883 
1884 	    if (x >= o->TermWin.ncol)
1885 		len = (o->TermWin.ncol - (menu->x + HSPACE));
1886 
1887 	    rxvtlib_drawbox_menubar (o, menu->x, len, +1);
1888 
1889 	    XDrawString (o->Xdisplay,
1890 			 o->menuBar.win, o->menubarGC,
1891 			 (Width2Pixel (menu->x) + Width2Pixel (HSPACE) / 2),
1892 			 menuBar_height () - SHADOW, menu->name, len);
1893 
1894 	    if (x >= o->TermWin.ncol)
1895 		break;
1896 	}
1897     }
1898     rxvtlib_drawbox_menubar (o, x, o->TermWin.ncol, (o->CurrentBar ? +1 : -1));
1899 
1900 /* add the menuBar title, if it exists and there's plenty of room */
1901     o->Arrows_x = 0;
1902     if (x < o->TermWin.ncol) {
1903 	const char     *str;
1904 	int             len, ncol = o->TermWin.ncol;
1905 	char            title[256];
1906 
1907 	if (x < (ncol - (NARROWS + 1))) {
1908 	    ncol -= (NARROWS + 1);
1909 	    o->Arrows_x = Width2Pixel (ncol);
1910 	}
1911 	rxvtlib_draw_Arrows (o, 0, +1);
1912 
1913 	str = (o->CurrentBar && o->CurrentBar->title) ? o->CurrentBar->title : "%n-%v";
1914 	for (len = 0; str[0] && len < sizeof (title) - 1; str++) {
1915 	    const char     *s = NULL;
1916 
1917 	    switch (str[0]) {
1918 	    case '%':
1919 		str++;
1920 		switch (str[0]) {
1921 		case 'n':
1922 		    s = o->rs[Rs_name];
1923 		    break;	/* resource name */
1924 		case 'v':
1925 		    s = VERSION;
1926 		    break;	/* version number */
1927 		case '%':
1928 		    s = "%";
1929 		    break;	/* literal '%' */
1930 		}
1931 		if (s != NULL)
1932 		    while (*s && len < sizeof (title) - 1)
1933 			title[len++] = *s++;
1934 		break;
1935 
1936 	    default:
1937 		title[len++] = str[0];
1938 		break;
1939 	    }
1940 	}
1941 	title[len] = '\0';
1942 
1943 	ncol -= (x + len + HSPACE);
1944 	if (len > 0 && ncol >= 0)
1945 	    XDrawString (o->Xdisplay,
1946 			 o->menuBar.win, o->menubarGC,
1947 			 Width2Pixel (x) + Width2Pixel (ncol + HSPACE) / 2,
1948 			 menuBar_height () - SHADOW, title, len);
1949     }
1950 #endif
1951 }
1952 
1953 /* EXTPROTO */
rxvtlib_menubar_mapping(rxvtlib * o,int map)1954 int             rxvtlib_menubar_mapping (rxvtlib *o, int map)
1955 {
1956 #ifdef MENUBAR
1957     int             change = 0;
1958 
1959     if (map && !menubar_visible ()) {
1960 	o->menuBar.state = 1;
1961 	if (o->menuBar.win == 0)
1962 	    return 0;
1963 	XMapWindow (o->Xdisplay, o->menuBar.win);
1964 	change = 1;
1965     } else if (!map && menubar_visible ()) {
1966 	rxvtlib_menubar_expose (o);
1967 	o->menuBar.state = 0;
1968 	XUnmapWindow (o->Xdisplay, o->menuBar.win);
1969 	change = 1;
1970     } else
1971 	rxvtlib_menubar_expose (o);
1972 
1973     return change;
1974 #else
1975     return 0;
1976 #endif
1977 }
1978 
1979 #ifdef MENUBAR
1980 /* INTPROTO */
rxvtlib_menu_select(rxvtlib * o,XButtonEvent * ev)1981 int             rxvtlib_menu_select (rxvtlib *o, XButtonEvent * ev)
1982 {
1983     menuitem_t     *thisitem, *item = NULL;
1984     int             this_y, y;
1985 
1986     Window          unused_root, unused_child;
1987     int             unused_root_x, unused_root_y;
1988     unsigned int    unused_mask;
1989 
1990     if (o->ActiveMenu == NULL)
1991 	return 0;
1992 
1993     XQueryPointer (o->Xdisplay, o->ActiveMenu->win,
1994 		   &unused_root, &unused_child,
1995 		   &unused_root_x, &unused_root_y,
1996 		   &(ev->x), &(ev->y), &unused_mask);
1997 
1998     if (o->ActiveMenu->parent != NULL && (ev->x < 0 || ev->y < 0)) {
1999 	rxvtlib_menu_hide (o);
2000 	return 1;
2001     }
2002 /* determine the menu item corresponding to the Y index */
2003     y = SHADOW;
2004     if (ev->x >= 0 && ev->x <= (o->ActiveMenu->w - SHADOW)) {
2005 	for (item = o->ActiveMenu->head; item != NULL; item = item->next) {
2006 	    int             h = HEIGHT_TEXT + 2 * SHADOW;
2007 
2008 	    if (isSeparator (item->name))
2009 		h = HEIGHT_SEPARATOR;
2010 	    else if (ev->y >= y && ev->y < (y + h))
2011 		break;
2012 	    y += h;
2013 	}
2014     }
2015     if (item == NULL && ev->type == ButtonRelease) {
2016 	rxvtlib_menu_hide_all (o);
2017 	return 0;
2018     }
2019     thisitem = item;
2020     this_y = y - SHADOW;
2021 
2022 /* erase the last item */
2023     if (o->ActiveMenu->item != NULL) {
2024 	if (o->ActiveMenu->item != thisitem) {
2025 	    for (y = 0, item = o->ActiveMenu->head;
2026 		 item != NULL; item = item->next) {
2027 		int             h;
2028 
2029 		if (isSeparator (item->name))
2030 		    h = HEIGHT_SEPARATOR;
2031 		else if (item == o->ActiveMenu->item) {
2032 		    /* erase old menuitem */
2033 		    rxvtlib_drawbox_menuitem (o, y, 0);	/* No Shadow */
2034 		    if (item->entry.type == MenuSubMenu)
2035 			rxvtlib_drawtriangle (o, o->ActiveMenu->w, y, +1);
2036 		    break;
2037 		} else
2038 		    h = HEIGHT_TEXT + 2 * SHADOW;
2039 		y += h;
2040 	    }
2041 	} else {
2042 	    switch (ev->type) {
2043 	    case ButtonRelease:
2044 		switch (item->entry.type) {
2045 		case MenuLabel:
2046 		case MenuSubMenu:
2047 		    rxvtlib_menu_hide_all (o);
2048 		    break;
2049 
2050 		case MenuAction:
2051 		case MenuTerminalAction:
2052 		    rxvtlib_drawbox_menuitem (o, this_y, -1);
2053 		    /*
2054 		     * use select for timing
2055 		     * remove menu before sending keys to the application
2056 		     */
2057 		    {
2058 			struct timeval  tv;
2059 
2060 			tv.tv_sec = 0;
2061 			tv.tv_usec = MENU_DELAY_USEC;
2062 			select (0, NULL, NULL, NULL, &tv);
2063 		    }
2064 		    rxvtlib_menu_hide_all (o);
2065 #ifndef DEBUG_MENU
2066 		    rxvtlib_action_dispatch (o, &(item->entry.action));
2067 #else				/* DEBUG_MENU */
2068 		    fprintf (stderr, "%s: %s\n", item->name,
2069 			     item->entry.action.str);
2070 #endif				/* DEBUG_MENU */
2071 		    break;
2072 		}
2073 		break;
2074 
2075 	    default:
2076 		if (item->entry.type == MenuSubMenu)
2077 		    goto DoMenu;
2078 		break;
2079 	    }
2080 	    return 0;
2081 	}
2082     }
2083   DoMenu:
2084     o->ActiveMenu->item = thisitem;
2085     y = this_y;
2086     if (thisitem != NULL) {
2087 	item = o->ActiveMenu->item;
2088 	if (item->entry.type != MenuLabel)
2089 	    rxvtlib_drawbox_menuitem (o, y, +1);
2090 	if (item->entry.type == MenuSubMenu) {
2091 	    int             x;
2092 
2093 	    rxvtlib_drawtriangle (o, o->ActiveMenu->w, y, -1);
2094 
2095 	    x = ev->x + (o->ActiveMenu->parent ?
2096 			 o->ActiveMenu->x : Width2Pixel (o->ActiveMenu->x));
2097 
2098 	    if (x >= item->entry.submenu.menu->x) {
2099 		o->ActiveMenu = item->entry.submenu.menu;
2100 		rxvtlib_menu_show (o);
2101 		return 1;
2102 	    }
2103 	}
2104     }
2105     return 0;
2106 }
2107 #endif
2108 
2109 #ifdef MENUBAR
2110 /* INTPROTO */
rxvtlib_menubar_select(rxvtlib * o,XButtonEvent * ev)2111 void            rxvtlib_menubar_select (rxvtlib *o, XButtonEvent * ev)
2112 {
2113     menu_t         *menu = NULL;
2114 
2115 /* determine the pulldown menu corresponding to the X index */
2116     if (ev->y >= 0 && ev->y <= menuBar_height () && o->CurrentBar != NULL) {
2117 	for (menu = o->CurrentBar->head; menu != NULL; menu = menu->next) {
2118 	    int             x = Width2Pixel (menu->x);
2119 	    int             w = Width2Pixel (menu->len + HSPACE);
2120 
2121 	    if ((ev->x >= x && ev->x < x + w))
2122 		break;
2123 	}
2124     }
2125     switch (ev->type) {
2126     case ButtonRelease:
2127 	rxvtlib_menu_hide_all (o);
2128 	break;
2129 
2130     case ButtonPress:
2131 	if (menu == NULL && o->Arrows_x && ev->x >= o->Arrows_x) {
2132 	    int             i;
2133 
2134 	    for (i = 0; i < NARROWS; i++) {
2135 		if (ev->x >= (o->Arrows_x + (Width2Pixel (4 * i + i)) / 4)
2136 		    && ev->x < (o->Arrows_x + (Width2Pixel (4 * i + i + 4)) / 4)) {
2137 		    rxvtlib_draw_Arrows (o, o->Arrows[i].name, -1);
2138 		    /*
2139 		     * use select for timing
2140 		     */
2141 		    {
2142 			struct timeval  tv;
2143 
2144 			tv.tv_sec = 0;
2145 			tv.tv_usec = MENU_DELAY_USEC;
2146 			select (0, NULL, NULL, NULL, &tv);
2147 		    }
2148 		    rxvtlib_draw_Arrows (o, o->Arrows[i].name, +1);
2149 #ifdef DEBUG_MENUARROWS
2150 		    fprintf (stderr, "'%c': ", o->Arrows[i].name);
2151 
2152 		    if (o->CurrentBar == NULL
2153 			|| (o->CurrentBar->arrows[i].type != MenuAction
2154 			    && o->CurrentBar->arrows[i].type !=
2155 			    MenuTerminalAction)) {
2156 			if (o->Arrows[i].str != NULL && o->Arrows[i].str[0])
2157 			    fprintf (stderr, "(default) \\033%s\n",
2158 				     &(o->Arrows[i].str[2]));
2159 		    } else {
2160 			fprintf (stderr, "%s\n", o->CurrentBar->arrows[i].str);
2161 		    }
2162 #else				/* DEBUG_MENUARROWS */
2163 		    if (o->CurrentBar == NULL
2164 			|| rxvtlib_action_dispatch (o, &(o->CurrentBar->arrows[i]))) {
2165 			if (o->Arrows[i].str != NULL && o->Arrows[i].str[0] != 0)
2166 			    rxvtlib_tt_write (o, o->Arrows[i].str + 1, o->Arrows[i].str[0]);
2167 		    }
2168 #endif				/* DEBUG_MENUARROWS */
2169 		    return;
2170 		}
2171 	    }
2172 	}
2173 	/* FALLTHROUGH */
2174 
2175     default:
2176 	/*
2177 	 * press menubar or move to a new entry
2178 	 */
2179 	if (menu != NULL && menu != o->ActiveMenu) {
2180 	    rxvtlib_menu_hide_all (o);	/* pop down old menu */
2181 	    o->ActiveMenu = menu;
2182 	    rxvtlib_menu_show (o);	/* pop up new menu */
2183 	}
2184 	break;
2185     }
2186 }
2187 #endif
2188 
2189 /*
2190  * general dispatch routine,
2191  * it would be nice to have `sticky' menus
2192  */
2193 /* EXTPROTO */
rxvtlib_menubar_control(rxvtlib * o,XButtonEvent * ev)2194 void            rxvtlib_menubar_control (rxvtlib *o, XButtonEvent * ev)
2195 {
2196 #ifdef MENUBAR
2197     switch (ev->type) {
2198     case ButtonPress:
2199 	if (ev->button == Button1)
2200 	    rxvtlib_menubar_select (o, ev);
2201 	break;
2202 
2203     case ButtonRelease:
2204 	if (ev->button == Button1)
2205 	    rxvtlib_menu_select (o, ev);
2206 	break;
2207 
2208     case MotionNotify:
2209 	while (XCheckTypedWindowEvent (o->Xdisplay, o->TermWin.parent[0],
2210 				       MotionNotify, (XEvent *) ev)) ;
2211 
2212 	if (o->ActiveMenu)
2213 	    while (rxvtlib_menu_select (o, ev)) ;
2214 	else
2215 	    ev->y = -1;
2216 	if (ev->y < 0) {
2217 	    Window          unused_root, unused_child;
2218 	    int             unused_root_x, unused_root_y;
2219 	    unsigned int    unused_mask;
2220 
2221 	    XQueryPointer (o->Xdisplay, o->menuBar.win,
2222 			   &unused_root, &unused_child,
2223 			   &unused_root_x, &unused_root_y,
2224 			   &(ev->x), &(ev->y), &unused_mask);
2225 	    rxvtlib_menubar_select (o, ev);
2226 	}
2227 	break;
2228     }
2229 #endif
2230 }
2231 
2232 /* EXTPROTO */
rxvtlib_map_menuBar(rxvtlib * o,int map)2233 void            rxvtlib_map_menuBar (rxvtlib *o, int map)
2234 {
2235 #ifdef MENUBAR
2236     if (rxvtlib_menubar_mapping (o, map))
2237 	rxvtlib_resize_all_windows (o);
2238 #endif
2239 }
2240 
2241 /* EXTPROTO */
rxvtlib_create_menuBar(rxvtlib * o,Cursor cursor)2242 void            rxvtlib_create_menuBar (rxvtlib *o, Cursor cursor)
2243 {
2244 #ifdef MENUBAR
2245 /* menuBar: size doesn't matter */
2246     o->menuBar.win = XCreateSimpleWindow (o->Xdisplay, o->TermWin.parent[0],
2247 				       0, 0,
2248 				       1, 1,
2249 				       0,
2250 				       o->PixColors[Color_fg],
2251 				       o->PixColors[Color_scroll]);
2252     XDefineCursor (o->Xdisplay, o->menuBar.win, cursor);
2253     XSelectInput (o->Xdisplay, o->menuBar.win,
2254 		  (ExposureMask | ButtonPressMask | ButtonReleaseMask |
2255 		   Button1MotionMask));
2256 
2257 #endif
2258 }
2259 
2260 /* EXTPROTO */
rxvtlib_Resize_menuBar(rxvtlib * o,int x,int y,unsigned int width,unsigned int height)2261 void            rxvtlib_Resize_menuBar (rxvtlib *o, int x, int y, unsigned int width,
2262 				unsigned int height)
2263 {
2264 #ifdef MENUBAR
2265     XMoveResizeWindow (o->Xdisplay, o->menuBar.win, x, y, width, height);
2266 #endif
2267 }
2268 
2269 /*----------------------- end-of-file (C source) -----------------------*/
2270