ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/rxvt-unicode/src/menubar.C
Revision: 1.3
Committed: Tue Nov 25 11:52:42 2003 UTC (20 years, 6 months ago) by pcg
Content type: text/plain
Branch: MAIN
CVS Tags: rel-1-3, rel-1-2
Changes since 1.2: +157 -157 lines
Log Message:
*** empty log message ***

File Contents

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