ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/rxvt-unicode/src/menubar.C
Revision: 1.5
Committed: Sat Jan 31 03:27:45 2004 UTC (20 years, 3 months ago) by pcg
Content type: text/plain
Branch: MAIN
CVS Tags: before_astyle
Changes since 1.4: +0 -1 lines
Log Message:
*** empty log message ***

File Contents

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