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