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