ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/rxvt-unicode/src/menubar.C
Revision: 1.4
Committed: Sat Jan 31 00:20:21 2004 UTC (20 years, 4 months ago) by pcg
Content type: text/plain
Branch: MAIN
Changes since 1.3: +311 -348 lines
Log Message:
*** empty log message ***

File Contents

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