ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/rxvt-unicode/src/menubar.C
Revision: 1.9
Committed: Tue Feb 24 00:02:44 2004 UTC (20 years, 3 months ago) by pcg
Content type: text/plain
Branch: MAIN
Changes since 1.8: +54 -99 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 pcg 1.8 rxvt_menuitem_find (const menu_t *menu, const char *name)
54 pcg 1.1 {
55 pcg 1.6 menuitem_t *item;
56 pcg 1.1
57     #ifdef DEBUG_STRICT
58 pcg 1.8 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 pcg 1.8 if (!STRCMP (name, (item->entry.submenu.menu)->name))
68 pcg 1.6 break;
69     }
70 pcg 1.8 else if ((isSeparator (name) && isSeparator (item->name))
71     || !STRCMP (name, item->name))
72 pcg 1.6 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.8 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 pcg 1.8 free (item->entry.action.str);
108 pcg 1.6 break;
109     case MenuSubMenu:
110     menu_delete (item->entry.submenu.menu);
111     break;
112     }
113     if (item->name != NULL)
114 pcg 1.8 free (item->name);
115 pcg 1.6 if (item->name2 != NULL)
116 pcg 1.8 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 pcg 1.8 rxvt_action_type (action_t *action, unsigned char *str)
126 pcg 1.1 {
127 pcg 1.6 unsigned int len;
128 pcg 1.1
129     #if defined (DEBUG_MENU) || defined (DEBUG_MENUARROWS)
130 pcg 1.8 len = STRLEN (str);
131     fprintf (stderr, " (len %d) = %s\n", len, str);
132 pcg 1.1 #else
133 pcg 1.8 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 pcg 1.8 rxvt_menuarrow_find (char name)
184 pcg 1.1 {
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 pcg 1.8 i = rxvt_menuarrow_find (name);
202 pcg 1.6 if (i >= 0)
203     {
204 pcg 1.8 action_t *act = & (CurrentBar->arrows[i]);
205 pcg 1.6
206     switch (act->type)
207     {
208     case MenuAction:
209     case MenuTerminalAction:
210 pcg 1.8 free (act->str);
211 pcg 1.6 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 pcg 1.8 MEMSET (parse, 0, sizeof (parse));
242 pcg 1.6
243 pcg 1.8 /* fprintf (stderr, "add arrows = `%s'\n", string); */
244 pcg 1.6 for (p = string; p != NULL && *p; string = p)
245     {
246     p = (string + 3);
247 pcg 1.8 /* fprintf (stderr, "parsing at %s\n", string); */
248 pcg 1.6 switch (string[1])
249     {
250     case 'b':
251     cur = &beg;
252     break;
253     case 'e':
254     cur = &end;
255     break;
256    
257     default:
258 pcg 1.8 i = rxvt_menuarrow_find (string[1]);
259 pcg 1.6 if (i >= 0)
260 pcg 1.8 cur = & (parse[i]);
261 pcg 1.6 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 pcg 1.8 p = STRCHR (string, '\0');
273 pcg 1.6 }
274     else
275     {
276     char *next = string;
277    
278     while (1)
279     {
280 pcg 1.8 p = STRCHR (next, '<');
281 pcg 1.6 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 pcg 1.8 p = STRCHR (next, '\0');
291 pcg 1.6 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 pcg 1.8 fprintf (stderr, "<b> (len %d) = %.*s\n",
305 pcg 1.6 cur->len, cur->len, (cur->str ? cur->str : ""));
306     for (i = 0; i < NARROWS; i++)
307     {
308 pcg 1.8 cur = & (parse[i]);
309     fprintf (stderr, "<%c> (len %d) = %.*s\n",
310 pcg 1.6 Arrows[i].name,
311     cur->len, cur->len, (cur->str ? cur->str : ""));
312     }
313     cur = &end;
314 pcg 1.8 fprintf (stderr, "<e> (len %d) = %.*s\n",
315 pcg 1.6 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 pcg 1.9 str = (unsigned char *) rxvt_malloc (parse[i].len + xtra_len + 1);
334 pcg 1.6
335     len = 0;
336     if (beg.len)
337     {
338 pcg 1.8 STRNCPY (str + len, beg.str, beg.len);
339 pcg 1.6 len += beg.len;
340     }
341 pcg 1.8 STRNCPY (str + len, parse[i].str, parse[i].len);
342 pcg 1.6 len += parse[i].len;
343    
344     if (end.len)
345     {
346 pcg 1.8 STRNCPY (str + len, end.str, end.len);
347 pcg 1.6 len += end.len;
348     }
349     str[len] = '\0';
350 pcg 1.1
351     #ifdef DEBUG_MENUARROWS
352 pcg 1.8 fprintf (stderr, "<%c> (len %d) = %s\n", Arrows[i].name, len, str);
353 pcg 1.1 #endif
354 pcg 1.8 if (rxvt_action_type (& (CurrentBar->arrows[i]), str) < 0)
355     free (str);
356 pcg 1.1 }
357     }
358    
359     menuitem_t *
360 pcg 1.8 rxvt_menuitem_add (menu_t *menu, const char *name, const char *name2, const char *action)
361 pcg 1.1 {
362 pcg 1.6 menuitem_t *item;
363     unsigned int len;
364 pcg 1.1
365     #ifdef DEBUG_STRICT
366 pcg 1.8 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.8 if (isSeparator (name))
374 pcg 1.6 {
375     /* add separator, no action */
376     name = "";
377     action = "";
378     }
379     else
380     {
381     /*
382     * add/replace existing menu item
383     */
384 pcg 1.8 item = rxvt_menuitem_find (menu, name);
385 pcg 1.6 if (item != NULL)
386     {
387     if (item->name2 != NULL && name2 != NULL)
388     {
389 pcg 1.8 free (item->name2);
390 pcg 1.6 item->len2 = 0;
391     item->name2 = NULL;
392     }
393     switch (item->entry.type)
394     {
395     case MenuAction:
396     case MenuTerminalAction:
397 pcg 1.8 free (item->entry.action.str);
398 pcg 1.6 item->entry.action.str = NULL;
399     break;
400     }
401     goto Item_Found;
402     }
403     }
404     /* allocate a new itemect */
405 pcg 1.8 item = (menuitem_t *) rxvt_malloc (sizeof (menuitem_t));
406 pcg 1.6
407     item->len2 = 0;
408     item->name2 = NULL;
409    
410 pcg 1.8 len = STRLEN (name);
411 pcg 1.9 item->name = (char *)rxvt_malloc (len + 1);
412 pcg 1.8 STRCPY (item->name, name);
413 pcg 1.6 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 pcg 1.8 len = STRLEN (name2);
435 pcg 1.6 if (len == 0)
436     item->name2 = NULL;
437     else
438     {
439 pcg 1.9 item->name2 = (char *)rxvt_malloc (len + 1);
440 pcg 1.8 STRCPY (item->name2, name2);
441 pcg 1.6 }
442     item->len2 = len;
443     }
444     item->entry.type = MenuLabel;
445 pcg 1.8 len = STRLEN (action);
446 pcg 1.6
447     if (len == 0 && item->name2 != NULL)
448     {
449     action = item->name2;
450     len = item->len2;
451     }
452     if (len)
453     {
454 pcg 1.9 unsigned char *str = (unsigned char *)rxvt_malloc (len + 1);
455 pcg 1.6
456 pcg 1.8 STRCPY (str, action);
457 pcg 1.6
458 pcg 1.8 if (rxvt_action_type (& (item->entry.action), str) < 0)
459     free (str);
460 pcg 1.6 }
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.8 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.8 if (STRCHR (path, '/') != NULL)
487 pcg 1.6 {
488     char *p = path;
489    
490 pcg 1.8 while ((p = STRCHR (p, '/')) != NULL)
491 pcg 1.6 {
492     p++;
493     if (*p == '/')
494     path = p;
495     }
496     if (path[0] == '/')
497     {
498     path++;
499     *menu = NULL;
500     }
501 pcg 1.8 while ((p = STRCHR (path, '/')) != NULL)
502 pcg 1.6 {
503     p[0] = '\0';
504     if (path[0] == '\0')
505     return NULL;
506 pcg 1.8 if (!STRCMP (path, DOT))
507 pcg 1.6 {
508     /* nothing to do */
509     }
510 pcg 1.8 else if (!STRCMP (path, DOTS))
511 pcg 1.6 {
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 pcg 1.8 if (!STRCMP (path, DOTS))
529 pcg 1.6 {
530 pcg 1.8 path += STRLEN (DOTS);
531 pcg 1.6 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 pcg 1.8 if (!STRCMP (path, m->name))
541 pcg 1.6 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 pcg 1.8 && !STRCMP (path, (item->entry.submenu.menu)->name))
551 pcg 1.6 {
552     m = (item->entry.submenu.menu);
553     break;
554     }
555     }
556     }
557     if (m != NULL)
558     {
559     *menu = m;
560 pcg 1.8 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.8 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 pcg 1.8 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.8 assert (CurrentBar != NULL);
644 pcg 1.1 #endif
645    
646 pcg 1.8 if (STRCHR (path, '/') != NULL)
647 pcg 1.6 {
648     char *p;
649    
650     if (path[0] == '/')
651     {
652     /* shouldn't happen */
653     path++;
654     parent = NULL;
655     }
656 pcg 1.8 while ((p = STRCHR (path, '/')) != NULL)
657 pcg 1.6 {
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 pcg 1.8 if (!STRCMP (path, DOTS))
667 pcg 1.6 return (parent != NULL ? parent->parent : parent);
668    
669 pcg 1.8 if (!STRCMP (path, DOT) || path[0] == '\0')
670 pcg 1.6 return parent;
671 pcg 1.1
672 pcg 1.6 /* allocate a new menu */
673 pcg 1.8 menu = (menu_t *) rxvt_malloc (sizeof (menu_t));
674 pcg 1.6
675     menu->width = 0;
676     menu->parent = parent;
677 pcg 1.8 menu->len = STRLEN (path);
678 pcg 1.9 menu->name = (char *)rxvt_malloc ((menu->len + 1));
679 pcg 1.8 STRCPY (menu->name, path);
680 pcg 1.6
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 pcg 1.8 item = rxvt_menuitem_add (parent, path, "", "");
706 pcg 1.6 if (item == NULL)
707     {
708 pcg 1.8 free (menu);
709 pcg 1.6 return parent;
710     }
711 pcg 1.1 #ifdef DEBUG_STRICT
712 pcg 1.8 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.8 x = Width2Pixel (x);
727     len = Width2Pixel (len + HSPACE);
728 pcg 1.6 if (x >= TermWin.width)
729     return;
730     else if (x + len >= TermWin.width)
731 pcg 1.8 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.8 rxvt_Draw_Shadow (display->display, 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.8 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.8 rxvt_Draw_Triangle (display->display, ActiveMenu->win, top, bot, x, y, w,
785 pcg 1.6 '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 pcg 1.8 rxvt_Draw_Shadow (display->display, ActiveMenu->win, top, bot,
812 pcg 1.6 SHADOW + 0, SHADOW + y,
813     ActiveMenu->w - 2 * (SHADOW),
814     HEIGHT_TEXT + 2 * SHADOW);
815 pcg 1.8 XFlush (display->display);
816 pcg 1.1 }
817    
818     #ifdef DEBUG_MENU_LAYOUT
819     void
820 pcg 1.8 rxvt_print_menu_ancestors (menu_t *menu)
821 pcg 1.1 {
822 pcg 1.6 if (menu == NULL)
823     {
824 pcg 1.8 fprintf (stderr, "Top Level menu\n");
825 pcg 1.6 return;
826     }
827 pcg 1.8 fprintf (stderr, "menu %s ", menu->name);
828 pcg 1.6 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 pcg 1.8 fprintf (stderr, "is an orphan!\n");
843 pcg 1.6 return;
844     }
845 pcg 1.1 }
846 pcg 1.8 fprintf (stderr, "\n");
847     rxvt_print_menu_ancestors (menu->parent);
848 pcg 1.1 }
849    
850     void
851 pcg 1.8 rxvt_print_menu_descendants (menu_t *menu)
852 pcg 1.1 {
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 pcg 1.8 fprintf (stderr, ">");
867     fprintf (stderr, "%s\n", menu->name);
868 pcg 1.6
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 pcg 1.8 fprintf (stderr, "> %s == NULL\n", item->name);
875 pcg 1.6 else
876 pcg 1.8 rxvt_print_menu_descendants (item->entry.submenu.menu);
877 pcg 1.6 }
878     else
879     {
880     for (i = 0; i < level; i++)
881 pcg 1.8 fprintf (stderr, "+");
882 pcg 1.6 if (item->entry.type == MenuLabel)
883 pcg 1.8 fprintf (stderr, "label: ");
884     fprintf (stderr, "%s\n", item->name);
885 pcg 1.6 }
886     }
887    
888     for (i = 0; i < level; i++)
889 pcg 1.8 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 pcg 1.8 x = Width2Pixel (x);
912 pcg 1.6
913     ActiveMenu->y = 1;
914 pcg 1.8 ActiveMenu->w = Menu_PixelWidth (ActiveMenu);
915 pcg 1.6
916     if ((x + ActiveMenu->w) >= TermWin.width)
917 pcg 1.8 x = (TermWin_TotalWidth () - ActiveMenu->w);
918 pcg 1.6
919     /* find the height */
920     for (h = 0, item = ActiveMenu->head; item != NULL; item = item->next)
921 pcg 1.8 h += isSeparator (item->name) ? HEIGHT_SEPARATOR
922 pcg 1.6 : HEIGHT_TEXT + 2 * SHADOW;
923     ActiveMenu->h = h + 2 * SHADOW;
924     }
925     if (ActiveMenu->win == None)
926     {
927 pcg 1.8 ActiveMenu->win = XCreateSimpleWindow (display->display, TermWin.vt,
928 pcg 1.6 x, ActiveMenu->y,
929     ActiveMenu->w, ActiveMenu->h,
930     0,
931     PixColors[Color_fg],
932     PixColors[Color_scroll]);
933 pcg 1.8 XMapWindow (display->display, ActiveMenu->win);
934 pcg 1.6 }
935 pcg 1.8 rxvt_Draw_Shadow (display->display, ActiveMenu->win,
936 pcg 1.6 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 pcg 1.8 const int xoff = (SHADOW + Width2Pixel (HSPACE) / 2);
947 pcg 1.6 register int h;
948     GC gc = menubarGC;
949    
950 pcg 1.8 if (isSeparator (item->name))
951 pcg 1.6 {
952 pcg 1.8 rxvt_Draw_Shadow (display->display, ActiveMenu->win,
953 pcg 1.6 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 pcg 1.8 menu->w = Menu_PixelWidth (menu);
981 pcg 1.6
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 pcg 1.8 h += isSeparator (it->name) ? HEIGHT_SEPARATOR
991 pcg 1.6 : 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 pcg 1.8 x1 = (TermWin_TotalWidth () - menu->w);
997 pcg 1.6
998     if ((y1 + menu->h) >= TermWin.height)
999 pcg 1.8 y1 = (TermWin_TotalHeight () - menu->h);
1000 pcg 1.6
1001     menu->x = (x1 < 0 ? 0 : x1);
1002     menu->y = (y1 < 0 ? 0 : y1);
1003     }
1004 pcg 1.8 else if (item->name2 && !STRCMP (name, item->name2))
1005 pcg 1.6 name = NULL;
1006 pcg 1.1
1007 pcg 1.6 if (len && name)
1008 pcg 1.9 draw_string (display->display, ActiveMenu->win, gc, xoff,
1009     2 * SHADOW + y + TermWin.font->ascent + 1,
1010     name, len);
1011 pcg 1.6
1012     len = item->len2;
1013     name = item->name2;
1014 pcg 1.9
1015 pcg 1.6 if (len && name)
1016 pcg 1.9 draw_string (display->display, ActiveMenu->win, gc,
1017     ActiveMenu->w - (xoff + Width2Pixel (xright)),
1018     2 * SHADOW + y + TermWin.font->ascent + 1,
1019     name, len);
1020    
1021 pcg 1.6 h = HEIGHT_TEXT + 2 * SHADOW;
1022     }
1023     y += h;
1024 pcg 1.1 }
1025     }
1026    
1027     void
1028 pcg 1.9 rxvt_term::menu_display (void (rxvt_term::*update) ())
1029 pcg 1.1 {
1030 pcg 1.6 menu_t *ActiveMenu = ActiveMenu;
1031 pcg 1.1
1032 pcg 1.6 if (ActiveMenu == NULL)
1033     return;
1034     if (ActiveMenu->win != None)
1035 pcg 1.8 XDestroyWindow (display->display, ActiveMenu->win);
1036 pcg 1.6 ActiveMenu->win = None;
1037     ActiveMenu->item = NULL;
1038    
1039     if (ActiveMenu->parent == NULL)
1040     drawbox_menubar (ActiveMenu->x, ActiveMenu->len, +1);
1041     ActiveMenu = ActiveMenu->parent;
1042 pcg 1.9 (this->*update) ();
1043 pcg 1.1 }
1044    
1045     void
1046 pcg 1.4 rxvt_term::menu_hide_all ()
1047 pcg 1.1 {
1048 pcg 1.9 menu_display (&rxvt_term::menu_hide_all);
1049 pcg 1.1 }
1050    
1051     void
1052 pcg 1.4 rxvt_term::menu_hide ()
1053 pcg 1.1 {
1054 pcg 1.9 menu_display (&rxvt_term::menu_show);
1055 pcg 1.1 }
1056    
1057     void
1058 pcg 1.4 rxvt_term::menu_clear (menu_t *menu)
1059 pcg 1.1 {
1060 pcg 1.6 if (menu != NULL)
1061     {
1062     menuitem_t *item = menu->tail;
1063    
1064     while (item != NULL)
1065     {
1066     menuitem_free (menu, item);
1067     /* it didn't get freed ... why? */
1068     if (item == menu->tail)
1069     return;
1070     item = menu->tail;
1071     }
1072     menu->width = 0;
1073 pcg 1.1 }
1074     }
1075    
1076     void
1077 pcg 1.4 rxvt_term::menubar_clear ()
1078 pcg 1.1 {
1079 pcg 1.6 bar_t *CurrentBar = CurrentBar;
1080 pcg 1.1
1081 pcg 1.6 if (CurrentBar != NULL)
1082     {
1083     menu_t *menu = CurrentBar->tail;
1084    
1085     while (menu != NULL)
1086     {
1087     menu_t *prev = menu->prev;
1088    
1089     menu_delete (menu);
1090     menu = prev;
1091     }
1092     CurrentBar->head = CurrentBar->tail = NULL;
1093    
1094     if (CurrentBar->title)
1095     {
1096 pcg 1.8 free (CurrentBar->title);
1097 pcg 1.6 CurrentBar->title = NULL;
1098     }
1099     menuarrow_free (0); /* remove all arrow functions */
1100 pcg 1.1 }
1101 pcg 1.6 ActiveMenu = NULL;
1102 pcg 1.1 }
1103    
1104     #if (MENUBAR_MAX > 1)
1105     /* find if menu already exists */
1106     bar_t *
1107 pcg 1.4 rxvt_term::menubar_find (const char *name)
1108 pcg 1.1 {
1109 pcg 1.6 bar_t *bar = CurrentBar;
1110 pcg 1.1
1111     #ifdef DEBUG_MENUBAR_STACKING
1112 pcg 1.8 fprintf (stderr, "looking for [menu:%s] ...", name ? name : " (nil)");
1113 pcg 1.1 #endif
1114 pcg 1.6 if (bar == NULL || name == NULL)
1115     return NULL;
1116 pcg 1.1
1117 pcg 1.8 if (STRLEN (name) && STRCMP (name, "*"))
1118 pcg 1.6 {
1119     do
1120     {
1121 pcg 1.8 if (!STRCMP (bar->name, name))
1122 pcg 1.6 {
1123 pcg 1.1 #ifdef DEBUG_MENUBAR_STACKING
1124 pcg 1.8 fprintf (stderr, " found!\n");
1125 pcg 1.1 #endif
1126 pcg 1.6 return bar;
1127     }
1128     bar = bar->next;
1129     }
1130     while (bar != CurrentBar);
1131     bar = NULL;
1132 pcg 1.1 }
1133     #ifdef DEBUG_MENUBAR_STACKING
1134 pcg 1.8 fprintf (stderr, "%s found!\n", (bar ? "" : " NOT"));
1135 pcg 1.1 #endif
1136    
1137 pcg 1.6 return bar;
1138 pcg 1.1 }
1139    
1140     int
1141 pcg 1.4 rxvt_term::menubar_push (const char *name)
1142 pcg 1.1 {
1143 pcg 1.6 int ret = 1;
1144     bar_t *bar;
1145 pcg 1.1
1146 pcg 1.6 if (CurrentBar == NULL)
1147     {
1148     /* allocate first one */
1149 pcg 1.8 bar = (bar_t *) rxvt_malloc (sizeof (bar_t));
1150 pcg 1.6
1151 pcg 1.8 MEMSET (bar, 0, sizeof (bar_t));
1152 pcg 1.6 /* circular linked-list */
1153     bar->next = bar->prev = bar;
1154     bar->head = bar->tail = NULL;
1155     bar->title = NULL;
1156     CurrentBar = bar;
1157     Nbars++;
1158    
1159     menubar_clear ();
1160     }
1161     else
1162     {
1163     /* find if menu already exists */
1164     bar = menubar_find (name);
1165     if (bar != NULL)
1166     {
1167     /* found it, use it */
1168     CurrentBar = bar;
1169     }
1170     else
1171     {
1172     /* create if needed, or reuse the existing empty menubar */
1173     if (CurrentBar->head != NULL)
1174     {
1175     /* need to malloc another one */
1176     if (Nbars < MENUBAR_MAX)
1177 pcg 1.8 bar = (bar_t *) rxvt_malloc (sizeof (bar_t));
1178 pcg 1.6 else
1179     bar = NULL;
1180    
1181     /* malloc failed or too many menubars, reuse another */
1182     if (bar == NULL)
1183     {
1184     bar = CurrentBar->next;
1185     ret = -1;
1186     }
1187     else
1188     {
1189     bar->head = bar->tail = NULL;
1190     bar->title = NULL;
1191    
1192     bar->next = CurrentBar->next;
1193     CurrentBar->next = bar;
1194     bar->prev = CurrentBar;
1195     bar->next->prev = bar;
1196    
1197     Nbars++;
1198     }
1199     CurrentBar = bar;
1200    
1201     }
1202     menubar_clear ();
1203     }
1204     }
1205    
1206     /* give menubar this name */
1207 pcg 1.8 STRNCPY (CurrentBar->name, name, MAXNAME);
1208 pcg 1.6 CurrentBar->name[MAXNAME - 1] = '\0';
1209 pcg 1.1
1210 pcg 1.6 return ret;
1211 pcg 1.1 }
1212    
1213     /* switch to a menu called NAME and remove it */
1214     void
1215 pcg 1.4 rxvt_term::menubar_remove (const char *name)
1216 pcg 1.1 {
1217 pcg 1.6 bar_t *bar;
1218 pcg 1.1
1219 pcg 1.6 if ((bar = menubar_find (name)) == NULL)
1220     return;
1221     CurrentBar = bar;
1222    
1223     do
1224     {
1225     menubar_clear ();
1226     /*
1227     * pop a menubar, clean it up first
1228     */
1229     if (CurrentBar != NULL)
1230     {
1231     bar_t *prev = CurrentBar->prev;
1232     bar_t *next = CurrentBar->next;
1233    
1234     if (prev == next && prev == CurrentBar)
1235     { /* only 1 left */
1236     prev = NULL;
1237     Nbars = 0; /* safety */
1238     }
1239     else
1240     {
1241     next->prev = prev;
1242     prev->next = next;
1243     Nbars--;
1244     }
1245    
1246 pcg 1.8 free (CurrentBar);
1247 pcg 1.6 CurrentBar = prev;
1248     }
1249 pcg 1.1 }
1250 pcg 1.8 while (CurrentBar && !STRCMP (name, "*"));
1251 pcg 1.1 }
1252    
1253     void
1254 pcg 1.8 rxvt_action_decode (FILE *fp, action_t *act)
1255 pcg 1.1 {
1256 pcg 1.6 unsigned char *str;
1257     short len;
1258 pcg 1.1
1259 pcg 1.6 if (act == NULL || (len = act->len) == 0 || (str = act->str) == NULL)
1260     return;
1261 pcg 1.1
1262 pcg 1.6 if (act->type == MenuTerminalAction)
1263     {
1264 pcg 1.8 fprintf (fp, "^@");
1265 pcg 1.6 /* can strip trailing ^G from XTerm sequence */
1266     if (str[0] == C0_ESC && str[1] == ']' && str[len - 1] == C0_BEL)
1267     len--;
1268     }
1269     else if (str[0] == C0_ESC)
1270     {
1271     switch (str[1])
1272     {
1273     case '[':
1274     case ']':
1275     break;
1276    
1277     case 'x':
1278     /* can strip trailing '\r' from M-x sequence */
1279     if (str[len - 1] == '\r')
1280     len--;
1281     /* FALLTHROUGH */
1282    
1283     default:
1284 pcg 1.8 fprintf (fp, "M-"); /* meta prefix */
1285 pcg 1.6 str++;
1286     len--;
1287     break;
1288     }
1289     }
1290     /*
1291     * control character form is preferred, since backslash-escaping
1292     * can be really ugly looking when the backslashes themselves also
1293     * have to be escaped to avoid Shell (or whatever scripting
1294     * language) interpretation
1295     */
1296     while (len > 0)
1297     {
1298     unsigned char ch = *str++;
1299    
1300     switch (ch)
1301     {
1302     case C0_ESC:
1303 pcg 1.8 fprintf (fp, "\\E");
1304 pcg 1.6 break; /* escape */
1305     case '\r':
1306 pcg 1.8 fprintf (fp, "\\r");
1307 pcg 1.6 break; /* carriage-return */
1308     case '\\':
1309 pcg 1.8 fprintf (fp, "\\\\");
1310 pcg 1.6 break; /* backslash */
1311     case '^':
1312 pcg 1.8 fprintf (fp, "\\^");
1313 pcg 1.6 break; /* caret */
1314     case 127:
1315 pcg 1.8 fprintf (fp, "^?");
1316 pcg 1.6 default:
1317     if (ch <= 31)
1318 pcg 1.8 fprintf (fp, "^%c", ('@' + ch));
1319 pcg 1.6 else if (ch > 127)
1320 pcg 1.8 fprintf (fp, "\\%o", ch);
1321 pcg 1.6 else
1322 pcg 1.8 fprintf (fp, "%c", ch);
1323 pcg 1.6 break;
1324     }
1325     len--;
1326 pcg 1.1 }
1327 pcg 1.8 fprintf (fp, "\n");
1328 pcg 1.1 }
1329    
1330     void
1331 pcg 1.8 rxvt_menu_dump (FILE *fp, menu_t *menu)
1332 pcg 1.1 {
1333 pcg 1.6 menuitem_t *item;
1334 pcg 1.1
1335 pcg 1.6 /* create a new menu and clear it */
1336 pcg 1.8 fprintf (fp, (menu->parent ? "./%s/*\n" : "/%s/*\n"), menu->name);
1337 pcg 1.1
1338 pcg 1.6 for (item = menu->head; item != NULL; item = item->next)
1339     {
1340     switch (item->entry.type)
1341     {
1342     case MenuSubMenu:
1343     if (item->entry.submenu.menu == NULL)
1344 pcg 1.8 fprintf (fp, "> %s == NULL\n", item->name);
1345 pcg 1.6 else
1346 pcg 1.8 rxvt_menu_dump (fp, item->entry.submenu.menu);
1347 pcg 1.6 break;
1348    
1349     case MenuLabel:
1350 pcg 1.8 fprintf (fp, "{%s}\n", (STRLEN (item->name) ? item->name : "-"));
1351 pcg 1.6 break;
1352    
1353     case MenuTerminalAction:
1354     case MenuAction:
1355 pcg 1.8 fprintf (fp, "{%s}", item->name);
1356     if (item->name2 != NULL && STRLEN (item->name2))
1357     fprintf (fp, "{%s}", item->name2);
1358     fprintf (fp, "\t");
1359     rxvt_action_decode (fp, & (item->entry.action));
1360 pcg 1.6 break;
1361     }
1362 pcg 1.1 }
1363    
1364 pcg 1.8 fprintf (fp, (menu->parent ? "../\n" : "/\n\n"));
1365 pcg 1.1 }
1366    
1367     void
1368 pcg 1.4 rxvt_term::menubar_dump (FILE *fp)
1369 pcg 1.1 {
1370 pcg 1.6 bar_t *bar = CurrentBar;
1371     time_t t;
1372 pcg 1.1
1373 pcg 1.6 if (bar == NULL || fp == NULL)
1374     return;
1375 pcg 1.8 time (&t);
1376 pcg 1.6
1377 pcg 1.8 fprintf (fp,
1378 pcg 1.6 "# " APL_SUBCLASS " (%s) Pid: %u\n# Date: %s\n\n",
1379 pcg 1.8 rs[Rs_name], (unsigned int)getpid (), ctime (&t));
1380 pcg 1.6
1381     /* dump in reverse order */
1382     bar = CurrentBar->prev;
1383     do
1384     {
1385     menu_t *menu;
1386     int i;
1387    
1388 pcg 1.8 fprintf (fp, "[menu:%s]\n", bar->name);
1389 pcg 1.6
1390     if (bar->title != NULL)
1391 pcg 1.8 fprintf (fp, "[title:%s]\n", bar->title);
1392 pcg 1.6
1393     for (i = 0; i < NARROWS; i++)
1394     {
1395     switch (bar->arrows[i].type)
1396     {
1397     case MenuTerminalAction:
1398     case MenuAction:
1399 pcg 1.8 fprintf (fp, "<%c>", Arrows[i].name);
1400     rxvt_action_decode (fp, & (bar->arrows[i]));
1401 pcg 1.6 break;
1402     }
1403     }
1404 pcg 1.8 fprintf (fp, "\n");
1405 pcg 1.1
1406 pcg 1.6 for (menu = bar->head; menu != NULL; menu = menu->next)
1407 pcg 1.8 rxvt_menu_dump (fp, menu);
1408 pcg 1.1
1409 pcg 1.8 fprintf (fp, "\n[done:%s]\n\n", bar->name);
1410 pcg 1.6 bar = bar->prev;
1411 pcg 1.1 }
1412 pcg 1.6 while (bar != CurrentBar->prev);
1413 pcg 1.1 }
1414     #endif /* (MENUBAR_MAX > 1) */
1415    
1416     /*
1417     * read in menubar commands from FILENAME
1418     * ignore all input before the tag line [menu] or [menu:???]
1419     *
1420     * Note that since File_find () is used, FILENAME can be semi-colon
1421     * delimited such that the second part can refer to a tag
1422     * so that a large `database' of menus can be collected together
1423     *
1424     * FILENAME = "file"
1425     * FILENAME = "file;"
1426     * read `file' starting with first [menu] or [menu:???] line
1427     *
1428     * FILENAME = "file;tag"
1429     * read `file' starting with [menu:tag]
1430     */
1431     void
1432 pcg 1.4 rxvt_term::menubar_read (const char *filename)
1433 pcg 1.1 {
1434 pcg 1.6 /* read in a menu from a file */
1435     FILE *fp;
1436     char buffer[256];
1437     char *p, *file, *tag = NULL;
1438    
1439 pcg 1.8 file = (char *)rxvt_File_find (filename, ".menu", rs[Rs_path]);
1440 pcg 1.6 if (file == NULL)
1441     return;
1442 pcg 1.8 fp = fopen (file, "rb");
1443     free (file);
1444 pcg 1.6 if (fp == NULL)
1445     return;
1446 pcg 1.1
1447     #if (MENUBAR_MAX > 1)
1448 pcg 1.6 /* semi-colon delimited */
1449 pcg 1.8 if ((tag = STRCHR (filename, ';')) != NULL)
1450 pcg 1.6 {
1451     tag++;
1452     if (*tag == '\0')
1453     tag = NULL;
1454 pcg 1.1 }
1455     #endif /* (MENUBAR_MAX > 1) */
1456     #ifdef DEBUG_MENU
1457 pcg 1.8 fprintf (stderr, "[read:%s]\n", p);
1458 pcg 1.6 if (tag)
1459 pcg 1.8 fprintf (stderr, "looking for [menu:%s]\n", tag);
1460 pcg 1.6 #endif
1461    
1462 pcg 1.8 while ((p = fgets (buffer, sizeof (buffer), fp)) != NULL)
1463 pcg 1.6 {
1464     int n;
1465    
1466 pcg 1.8 if ((n = rxvt_Str_match (p, "[menu")) != 0)
1467 pcg 1.6 {
1468     if (tag)
1469     {
1470     /* looking for [menu:tag] */
1471     if (p[n] == ':' && p[n + 1] != ']')
1472     {
1473     n++;
1474 pcg 1.8 n += rxvt_Str_match (p + n, tag);
1475 pcg 1.6 if (p[n] == ']')
1476     {
1477 pcg 1.1 #ifdef DEBUG_MENU
1478 pcg 1.8 fprintf (stderr, "[menu:%s]\n", tag);
1479 pcg 1.1 #endif
1480 pcg 1.6 break;
1481     }
1482     }
1483     }
1484     else if (p[n] == ':' || p[n] == ']')
1485     break;
1486     }
1487 pcg 1.1 }
1488    
1489 pcg 1.6 /* found [menu], [menu:???] tag */
1490     while (p != NULL)
1491     {
1492     int n;
1493 pcg 1.1
1494     #ifdef DEBUG_MENU
1495 pcg 1.8 fprintf (stderr, "read line = %s\n", p);
1496 pcg 1.1 #endif
1497    
1498 pcg 1.6 /* looking for [done:tag] or [done:] */
1499 pcg 1.8 if ((n = rxvt_Str_match (p, "[done")) != 0)
1500 pcg 1.6 {
1501     if (p[n] == ']')
1502     {
1503     menu_readonly = 1;
1504     break;
1505     }
1506     else if (p[n] == ':')
1507     {
1508     n++;
1509     if (p[n] == ']')
1510     {
1511     menu_readonly = 1;
1512     break;
1513     }
1514     else if (tag)
1515     {
1516 pcg 1.8 n += rxvt_Str_match (p + n, tag);
1517 pcg 1.6 if (p[n] == ']')
1518     {
1519 pcg 1.1 #ifdef DEBUG_MENU
1520 pcg 1.8 fprintf (stderr, "[done:%s]\n", tag);
1521 pcg 1.1 #endif
1522 pcg 1.6 menu_readonly = 1;
1523     break;
1524     }
1525     }
1526     else
1527     {
1528     /* what? ... skip this line */
1529     p[0] = COMMENT_CHAR;
1530     }
1531     }
1532     }
1533     /*
1534     * remove leading/trailing space
1535     * and strip-off leading/trailing quotes
1536     * skip blank or comment lines
1537     */
1538 pcg 1.8 rxvt_Str_trim (p);
1539 pcg 1.6 if (*p && *p != '#')
1540     {
1541     menu_readonly = 0; /* if case we read another file */
1542     menubar_dispatch (p);
1543     }
1544     /* get another line */
1545 pcg 1.8 p = fgets (buffer, sizeof (buffer), fp);
1546 pcg 1.1 }
1547    
1548 pcg 1.8 fclose (fp);
1549 pcg 1.1 }
1550    
1551     /*
1552     * user interface for building/deleting and otherwise managing menus
1553     */
1554     void
1555 pcg 1.4 rxvt_term::menubar_dispatch (char *str)
1556 pcg 1.1 {
1557 pcg 1.6 int n, cmd;
1558     char *path, *name, *name2;
1559 pcg 1.1
1560 pcg 1.9 if (menubar_visible () && ActiveMenu != NULL)
1561 pcg 1.6 menubar_expose ();
1562     else
1563     ActiveMenu = NULL;
1564 pcg 1.1
1565 pcg 1.6 cmd = *str;
1566     switch (cmd)
1567     {
1568     case '.':
1569     case '/': /* absolute & relative path */
1570     case MENUITEM_BEG: /* menuitem */
1571     /* add `+' prefix for these cases */
1572     cmd = '+';
1573     break;
1574    
1575     case '+':
1576     case '-':
1577     str++; /* skip cmd character */
1578     break;
1579    
1580     case '<':
1581 pcg 1.1 #if (MENUBAR_MAX > 1)
1582 pcg 1.6 if (CurrentBar == NULL)
1583     break;
1584 pcg 1.1 #endif /* (MENUBAR_MAX > 1) */
1585 pcg 1.6 if (str[1] && str[2] == '>') /* arrow commands */
1586     menuarrow_add (str);
1587     break;
1588    
1589     case '[': /* extended command */
1590     while (str[0] == '[')
1591     {
1592     char *next = (++str); /* skip leading '[' */
1593    
1594     if (str[0] == ':')
1595     { /* [:command:] */
1596     do
1597     {
1598     next++;
1599 pcg 1.8 if ((next = STRCHR (next, ':')) == NULL)
1600 pcg 1.6 return; /* parse error */
1601     }
1602     while (next[1] != ']');
1603     /* remove and skip ':]' */
1604     *next = '\0';
1605     next += 2;
1606     }
1607     else
1608     {
1609 pcg 1.8 if ((next = STRCHR (next, ']')) == NULL)
1610 pcg 1.6 return; /* parse error */
1611     /* remove and skip ']' */
1612     *next = '\0';
1613     next++;
1614     }
1615    
1616     if (str[0] == ':')
1617     {
1618     int saved;
1619    
1620     /* try and dispatch it, regardless of read/write status */
1621     saved = menu_readonly;
1622     menu_readonly = 0;
1623     menubar_dispatch (str + 1);
1624     menu_readonly = saved;
1625     }
1626     /* these ones don't require menu stacking */
1627 pcg 1.8 else if (!STRCMP (str, "clear"))
1628 pcg 1.6 {
1629     menubar_clear ();
1630     }
1631 pcg 1.8 else if (!STRCMP (str, "done") || rxvt_Str_match (str, "done:"))
1632 pcg 1.6 {
1633     menu_readonly = 1;
1634     }
1635 pcg 1.8 else if (!STRCMP (str, "show"))
1636 pcg 1.6 {
1637     map_menuBar (1);
1638     menu_readonly = 1;
1639     }
1640 pcg 1.8 else if (!STRCMP (str, "hide"))
1641 pcg 1.6 {
1642     map_menuBar (0);
1643     menu_readonly = 1;
1644     }
1645 pcg 1.8 else if ((n = rxvt_Str_match (str, "read:")) != 0)
1646 pcg 1.6 {
1647     /* read in a menu from a file */
1648     str += n;
1649     menubar_read (str);
1650     }
1651 pcg 1.8 else if ((n = rxvt_Str_match (str, "title:")) != 0)
1652 pcg 1.6 {
1653     str += n;
1654     if (CurrentBar != NULL && !menu_readonly)
1655     {
1656     if (*str)
1657     {
1658 pcg 1.9 name = (char *)rxvt_realloc (CurrentBar->title, STRLEN (str) + 1);
1659 pcg 1.6 if (name != NULL)
1660     {
1661 pcg 1.8 STRCPY (name, str);
1662 pcg 1.6 CurrentBar->title = name;
1663     }
1664     menubar_expose ();
1665     }
1666     else
1667     {
1668 pcg 1.8 free (CurrentBar->title);
1669 pcg 1.6 CurrentBar->title = NULL;
1670     }
1671     }
1672     }
1673 pcg 1.8 else if ((n = rxvt_Str_match (str, "pixmap:")) != 0)
1674 pcg 1.6 {
1675     str += n;
1676     xterm_seq (XTerm_Pixmap, str, CHAR_ST);
1677     }
1678 pcg 1.1 #if (MENUBAR_MAX > 1)
1679 pcg 1.8 else if ((n = rxvt_Str_match (str, "rm")) != 0)
1680 pcg 1.6 {
1681     str += n;
1682     switch (str[0])
1683     {
1684     case ':':
1685     str++;
1686     /* FALLTHROUGH */
1687     case '\0':
1688     /* FALLTHROUGH */
1689     case '*':
1690     menubar_remove (str);
1691     break;
1692     }
1693     menu_readonly = 1;
1694     }
1695 pcg 1.8 else if ((n = rxvt_Str_match (str, "menu")) != 0)
1696 pcg 1.6 {
1697     str += n;
1698     switch (str[0])
1699     {
1700     case ':':
1701     str++;
1702     /* add/access menuBar */
1703     if (*str != '\0' && *str != '*')
1704     menubar_push (str);
1705     break;
1706     default:
1707     if (CurrentBar == NULL)
1708     {
1709     menubar_push ("default");
1710     }
1711     }
1712    
1713     if (CurrentBar != NULL)
1714     menu_readonly = 0; /* allow menu build commands */
1715     }
1716 pcg 1.8 else if (!STRCMP (str, "dump"))
1717 pcg 1.6 {
1718     /* dump current menubars to a file */
1719     FILE *fp;
1720    
1721     /* enough space to hold the results */
1722     char buffer[32];
1723    
1724 pcg 1.8 sprintf (buffer, "/tmp/" APL_SUBCLASS "-%u",
1725     (unsigned int)getpid ());
1726 pcg 1.6
1727 pcg 1.8 if ((fp = fopen (buffer, "wb")) != NULL)
1728 pcg 1.6 {
1729     xterm_seq (XTerm_title, buffer, CHAR_ST);
1730     menubar_dump (fp);
1731 pcg 1.8 fclose (fp);
1732 pcg 1.6 }
1733     }
1734 pcg 1.8 else if (!STRCMP (str, "next"))
1735 pcg 1.6 {
1736     if (CurrentBar)
1737     {
1738     CurrentBar = CurrentBar->next;
1739     menu_readonly = 1;
1740     }
1741     }
1742 pcg 1.8 else if (!STRCMP (str, "prev"))
1743 pcg 1.6 {
1744     if (CurrentBar)
1745     {
1746     CurrentBar = CurrentBar->prev;
1747     menu_readonly = 1;
1748     }
1749     }
1750 pcg 1.8 else if (!STRCMP (str, "swap"))
1751 pcg 1.6 {
1752     /* swap the top 2 menus */
1753     if (CurrentBar)
1754     {
1755     bar_t *cbprev = CurrentBar->prev;
1756     bar_t *cbnext = CurrentBar->next;
1757    
1758     cbprev->next = cbnext;
1759     cbnext->prev = cbprev;
1760    
1761     CurrentBar->next = cbprev;
1762     CurrentBar->prev = cbprev->prev;
1763    
1764     cbprev->prev->next = CurrentBar;
1765     cbprev->prev = CurrentBar;
1766    
1767     CurrentBar = cbprev;
1768     menu_readonly = 1;
1769     }
1770     }
1771 pcg 1.1 #endif /* (MENUBAR_MAX > 1) */
1772 pcg 1.6 str = next;
1773 pcg 1.1
1774 pcg 1.6 BuildMenu = ActiveMenu = NULL;
1775     menubar_expose ();
1776 pcg 1.1 #ifdef DEBUG_MENUBAR_STACKING
1777 pcg 1.8 fprintf (stderr, "menus are read%s\n",
1778 pcg 1.6 menu_readonly ? "only" : "/write");
1779 pcg 1.1 #endif
1780 pcg 1.6
1781     }
1782     return;
1783     break;
1784 pcg 1.1 }
1785    
1786     #if (MENUBAR_MAX > 1)
1787 pcg 1.6 if (CurrentBar == NULL)
1788     return;
1789     if (menu_readonly)
1790     {
1791 pcg 1.1 #ifdef DEBUG_MENUBAR_STACKING
1792 pcg 1.8 fprintf (stderr, "menus are read%s\n",
1793 pcg 1.6 menu_readonly ? "only" : "/write");
1794 pcg 1.1 #endif
1795 pcg 1.6 return;
1796 pcg 1.1 }
1797     #endif /* (MENUBAR_MAX > 1) */
1798    
1799 pcg 1.6 switch (cmd)
1800     {
1801     case '+':
1802     case '-':
1803     path = name = str;
1804    
1805     name2 = NULL;
1806     /* parse STR, allow spaces inside (name) */
1807     if (path[0] != '\0')
1808     {
1809 pcg 1.8 name = STRCHR (path, MENUITEM_BEG);
1810     str = STRCHR (path, MENUITEM_END);
1811 pcg 1.6 if (name != NULL || str != NULL)
1812     {
1813     if (name == NULL || str == NULL || str <= (name + 1)
1814     || (name > path && name[-1] != '/'))
1815     {
1816 pcg 1.8 rxvt_print_error ("menu error <%s>\n", path);
1817 pcg 1.6 break;
1818     }
1819     if (str[1] == MENUITEM_BEG)
1820     {
1821     name2 = (str + 2);
1822 pcg 1.8 str = STRCHR (name2, MENUITEM_END);
1823 pcg 1.6
1824     if (str == NULL)
1825     {
1826 pcg 1.8 rxvt_print_error ("menu error <%s>\n", path);
1827 pcg 1.6 break;
1828     }
1829     name2[-2] = '\0'; /* remove prev MENUITEM_END */
1830     }
1831     if (name > path && name[-1] == '/')
1832     name[-1] = '\0';
1833    
1834     *name++ = '\0'; /* delimit */
1835     *str++ = '\0'; /* delimit */
1836    
1837 pcg 1.8 while (isspace (*str))
1838 pcg 1.6 str++; /* skip space */
1839     }
1840 pcg 1.1 #ifdef DEBUG_MENU
1841 pcg 1.8 fprintf (stderr,
1842 pcg 1.6 "`%c' path = <%s>, name = <%s>, name2 = <%s>, action = <%s>\n",
1843 pcg 1.8 cmd, (path ? path : " (nil)"), (name ? name : " (nil)"),
1844     (name2 ? name2 : " (nil)"), (str ? str : " (nil)")
1845 pcg 1.6 );
1846     #endif
1847    
1848     }
1849     /* process the different commands */
1850     switch (cmd)
1851     {
1852     case '+': /* add/replace existing menu or menuitem */
1853     if (path[0] != '\0')
1854     {
1855     int len;
1856    
1857 pcg 1.8 path = menu_find_base (& (BuildMenu), path);
1858     len = STRLEN (path);
1859 pcg 1.6
1860     /* don't allow menus called `*' */
1861     if (path[0] == '*')
1862     {
1863     menu_clear (BuildMenu);
1864     break;
1865     }
1866 pcg 1.8 else if (len >= 2 && !STRCMP ((path + len - 2), "/*"))
1867 pcg 1.6 {
1868     path[len - 2] = '\0';
1869     }
1870     if (path[0] != '\0')
1871     BuildMenu = menu_add (BuildMenu, path);
1872     }
1873     if (name != NULL && name[0] != '\0')
1874 pcg 1.8 rxvt_menuitem_add (BuildMenu,
1875     (STRCMP (name, SEPARATOR_NAME) ? name : ""),
1876 pcg 1.6 name2, str);
1877     break;
1878    
1879     case '-': /* delete menu entry */
1880 pcg 1.8 if (!STRCMP (path, "/*") && (name == NULL || name[0] == '\0'))
1881 pcg 1.6 {
1882     menubar_clear ();
1883     BuildMenu = NULL;
1884     menubar_expose ();
1885     break;
1886     }
1887     else if (path[0] != '\0')
1888     {
1889     int len;
1890     menu_t *menu = BuildMenu;
1891    
1892     path = menu_find_base (&menu, path);
1893 pcg 1.8 len = STRLEN (path);
1894 pcg 1.6
1895     /* submenu called `*' clears all menu items */
1896     if (path[0] == '*')
1897     {
1898     menu_clear (menu);
1899     break; /* done */
1900     }
1901 pcg 1.8 else if (len >= 2 && !STRCMP (&path[len - 2], "/*"))
1902 pcg 1.6 {
1903     /* done */
1904     break;
1905     }
1906     else if (path[0] != '\0')
1907     {
1908     BuildMenu = NULL;
1909     break;
1910     }
1911     else
1912     BuildMenu = menu;
1913     }
1914     if (BuildMenu != NULL)
1915     {
1916     if (name == NULL || name[0] == '\0')
1917     BuildMenu = menu_delete (BuildMenu);
1918     else
1919     {
1920     const char *n1;
1921     menuitem_t *item;
1922     menu_t *BuildMenu = BuildMenu;
1923    
1924 pcg 1.8 n1 = STRCMP (name, SEPARATOR_NAME) ? name : "";
1925     item = rxvt_menuitem_find (BuildMenu, n1);
1926 pcg 1.6 if (item != NULL && item->entry.type != MenuSubMenu)
1927     {
1928     menuitem_free (BuildMenu, item);
1929    
1930     /* fix up the width */
1931     BuildMenu->width = 0;
1932     for (item = BuildMenu->head; item != NULL;
1933     item = item->next)
1934     {
1935     short l = item->len + item->len2;
1936    
1937 pcg 1.8 MAX_IT (BuildMenu->width, l);
1938 pcg 1.6 }
1939     }
1940     }
1941     menubar_expose ();
1942     }
1943     break;
1944     }
1945     break;
1946 pcg 1.1 }
1947     }
1948    
1949     void
1950 pcg 1.4 rxvt_term::draw_Arrows (int name, int state)
1951 pcg 1.1 {
1952 pcg 1.6 GC top, bot;
1953 pcg 1.1
1954 pcg 1.6 int i;
1955 pcg 1.1
1956     #ifdef MENU_SHADOW_IN
1957 pcg 1.6 state = -state;
1958 pcg 1.1 #endif
1959 pcg 1.6 switch (state)
1960     {
1961     case +1:
1962     top = topShadowGC;
1963     bot = botShadowGC;
1964     break; /* SHADOW_OUT */
1965     case -1:
1966     top = botShadowGC;
1967     bot = topShadowGC;
1968     break; /* SHADOW_IN */
1969     default:
1970     top = bot = scrollbarGC;
1971     break; /* neutral */
1972     }
1973    
1974     if (!Arrows_x)
1975     return;
1976    
1977     for (i = 0; i < NARROWS; i++)
1978     {
1979 pcg 1.8 const int w = Width2Pixel (1);
1980     const int y = (menuBar_TotalHeight () - w) / 2;
1981     int x = Arrows_x + (5 * Width2Pixel (i)) / 4;
1982 pcg 1.6
1983     if (!name || name == Arrows[i].name)
1984 pcg 1.8 rxvt_Draw_Triangle (display->display, menuBar.win, top, bot, x, y, w,
1985 pcg 1.6 Arrows[i].name);
1986 pcg 1.1 }
1987 pcg 1.8 XFlush (display->display);
1988 pcg 1.1 }
1989    
1990     void
1991 pcg 1.4 rxvt_term::menubar_expose ()
1992 pcg 1.1 {
1993 pcg 1.6 menu_t *menu;
1994     int x;
1995 pcg 1.1
1996 pcg 1.9 if (!menubar_visible () || menuBar.win == 0)
1997 pcg 1.6 return;
1998 pcg 1.1
1999 pcg 1.6 if (menubarGC == None)
2000     {
2001     /* Create the graphics context */
2002     XGCValues gcvalue;
2003 pcg 1.1
2004 pcg 1.6 gcvalue.font = TermWin.font->fid;
2005 pcg 1.1
2006 pcg 1.6 gcvalue.foreground = (XDEPTH <= 2 ? PixColors[Color_fg]
2007     : PixColors[Color_Black]);
2008 pcg 1.8 menubarGC = XCreateGC (display->display, menuBar.win,
2009 pcg 1.6 GCForeground | GCFont, &gcvalue);
2010 pcg 1.1
2011     }
2012 pcg 1.6 /* make sure the font is correct */
2013 pcg 1.8 XSetFont (display->display, menubarGC, TermWin.font->fid);
2014     XSetFont (display->display, botShadowGC, TermWin.font->fid);
2015     XClearWindow (display->display, menuBar.win);
2016 pcg 1.1
2017 pcg 1.6 menu_hide_all ();
2018 pcg 1.1
2019 pcg 1.6 x = 0;
2020     if (CurrentBar != NULL)
2021     {
2022     for (menu = CurrentBar->head; menu != NULL; menu = menu->next)
2023     {
2024     int len = menu->len;
2025 pcg 1.1
2026 pcg 1.6 x = (menu->x + menu->len + HSPACE);
2027 pcg 1.1
2028     #ifdef DEBUG_MENU_LAYOUT
2029 pcg 1.8 rxvt_print_menu_descendants (menu);
2030 pcg 1.1 #endif
2031    
2032 pcg 1.6 if (x >= TermWin.ncol)
2033     len = (TermWin.ncol - (menu->x + HSPACE));
2034 pcg 1.1
2035 pcg 1.6 drawbox_menubar (menu->x, len, +1);
2036 pcg 1.9 draw_string (display->display, menuBar.win, menubarGC,
2037     (Width2Pixel (menu->x) + Width2Pixel (HSPACE) / 2),
2038     menuBar_height () - SHADOW, menu->name, len);
2039 pcg 1.6
2040     if (x >= TermWin.ncol)
2041     break;
2042     }
2043     }
2044     drawbox_menubar (x, TermWin.ncol, (CurrentBar ? +1 : -1));
2045    
2046     /* add the menuBar title, if it exists and there's plenty of room */
2047     Arrows_x = 0;
2048     if (x < TermWin.ncol)
2049     {
2050     const char *str;
2051     int ncol;
2052     unsigned int len;
2053     char title[256];
2054    
2055     ncol = (int)TermWin.ncol;
2056     if (x < (ncol - (NARROWS + 1)))
2057     {
2058     ncol -= (NARROWS + 1);
2059 pcg 1.8 Arrows_x = Width2Pixel (ncol);
2060 pcg 1.6 }
2061     draw_Arrows (0, +1);
2062    
2063     str = (CurrentBar
2064     && CurrentBar->title) ? CurrentBar->title : "%n-%v";
2065 pcg 1.8 for (len = 0; str[0] && len < sizeof (title) - 1; str++)
2066 pcg 1.6 {
2067     const char *s = NULL;
2068    
2069     switch (str[0])
2070     {
2071     case '%':
2072     str++;
2073     switch (str[0])
2074     {
2075     case 'n':
2076     s = rs[Rs_name];
2077     break; /* resource name */
2078     case 'v':
2079     s = VERSION;
2080     break; /* version number */
2081     case '%':
2082     s = "%";
2083     break; /* literal '%' */
2084     }
2085     if (s != NULL)
2086 pcg 1.8 while (*s && len < sizeof (title) - 1)
2087 pcg 1.6 title[len++] = *s++;
2088     break;
2089    
2090     default:
2091     title[len++] = str[0];
2092     break;
2093     }
2094     }
2095     title[len] = '\0';
2096    
2097     ncol -= (x + len + HSPACE);
2098     if (len > 0 && ncol >= 0)
2099 pcg 1.9 draw_string (display->display, menuBar.win, menubarGC,
2100     Width2Pixel (x) + Width2Pixel (ncol + HSPACE) / 2,
2101     menuBar_height () - SHADOW, title, len);
2102 pcg 1.1 }
2103     }
2104    
2105     int
2106 pcg 1.4 rxvt_term::menubar_mapping (int map)
2107 pcg 1.1 {
2108 pcg 1.6 int change = 0;
2109 pcg 1.1
2110 pcg 1.9 if (map && !menubar_visible ())
2111 pcg 1.6 {
2112     menuBar.state = 1;
2113     if (menuBar.win == 0)
2114     return 0;
2115 pcg 1.8 XMapWindow (display->display, menuBar.win);
2116 pcg 1.6 change = 1;
2117     }
2118 pcg 1.9 else if (!map && menubar_visible ())
2119 pcg 1.6 {
2120     menubar_expose ();
2121     menuBar.state = 0;
2122 pcg 1.8 XUnmapWindow (display->display, menuBar.win);
2123 pcg 1.6 change = 1;
2124     }
2125     else
2126     menubar_expose ();
2127 pcg 1.1
2128 pcg 1.6 return change;
2129 pcg 1.1 }
2130    
2131     int
2132 pcg 1.9 rxvt_term::menu_select (XButtonEvent &ev)
2133 pcg 1.1 {
2134 pcg 1.6 menuitem_t *thisitem, *item = NULL;
2135     int this_y, y;
2136     menu_t *ActiveMenu = ActiveMenu;
2137    
2138     Window unused_root, unused_child;
2139     int unused_root_x, unused_root_y;
2140     unsigned int unused_mask;
2141    
2142     if (ActiveMenu == NULL)
2143     return 0;
2144    
2145 pcg 1.8 XQueryPointer (display->display, ActiveMenu->win,
2146 pcg 1.6 &unused_root, &unused_child,
2147     &unused_root_x, &unused_root_y,
2148 pcg 1.9 &ev.x, &ev.y, &unused_mask);
2149 pcg 1.6
2150 pcg 1.9 if (ActiveMenu->parent != NULL && (ev.x < 0 || ev.y < 0))
2151 pcg 1.6 {
2152     menu_hide ();
2153     return 1;
2154     }
2155     /* determine the menu item corresponding to the Y index */
2156     y = SHADOW;
2157 pcg 1.9 if (ev.x >= 0 && ev.x <= (ActiveMenu->w - SHADOW))
2158 pcg 1.6 {
2159     for (item = ActiveMenu->head; item != NULL; item = item->next)
2160     {
2161     int h = HEIGHT_TEXT + 2 * SHADOW;
2162    
2163 pcg 1.8 if (isSeparator (item->name))
2164 pcg 1.6 h = HEIGHT_SEPARATOR;
2165 pcg 1.9 else if (ev.y >= y && ev.y < (y + h))
2166 pcg 1.6 break;
2167     y += h;
2168     }
2169     }
2170 pcg 1.9 if (item == NULL && ev.type == ButtonRelease)
2171 pcg 1.6 {
2172     menu_hide_all ();
2173     return 0;
2174     }
2175     thisitem = item;
2176     this_y = y - SHADOW;
2177    
2178     /* erase the last item */
2179     if (ActiveMenu->item != NULL)
2180     {
2181     if (ActiveMenu->item != thisitem)
2182     {
2183     for (y = 0, item = ActiveMenu->head; item != NULL;
2184     item = item->next)
2185     {
2186     int h;
2187    
2188 pcg 1.8 if (isSeparator (item->name))
2189 pcg 1.6 h = HEIGHT_SEPARATOR;
2190     else if (item == ActiveMenu->item)
2191     {
2192     /* erase old menuitem */
2193     drawbox_menuitem (y, 0); /* No Shadow */
2194     if (item->entry.type == MenuSubMenu)
2195     drawtriangle (ActiveMenu->w, y, +1);
2196     break;
2197     }
2198     else
2199     h = HEIGHT_TEXT + 2 * SHADOW;
2200     y += h;
2201     }
2202     }
2203     else
2204     {
2205 pcg 1.9 switch (ev.type)
2206 pcg 1.6 {
2207     case ButtonRelease:
2208     switch (item->entry.type)
2209     {
2210     case MenuLabel:
2211     case MenuSubMenu:
2212     menu_hide_all ();
2213     break;
2214    
2215     case MenuAction:
2216     case MenuTerminalAction:
2217 pcg 1.9 drawbox_menuitem (this_y, -1);
2218 pcg 1.6 {
2219 pcg 1.1 #ifdef HAVE_NANOSLEEP
2220 pcg 1.6 struct timespec rqt;
2221 pcg 1.1
2222 pcg 1.6 rqt.tv_sec = 0;
2223     rqt.tv_nsec = MENU_DELAY_USEC * 1000;
2224 pcg 1.8 nanosleep (&rqt, NULL);
2225 pcg 1.1 #else
2226 pcg 1.6 /* use select for timing */
2227     struct timeval tv;
2228 pcg 1.1
2229 pcg 1.6 tv.tv_sec = 0;
2230     tv.tv_usec = MENU_DELAY_USEC;
2231 pcg 1.8 select (0, NULL, NULL, NULL, &tv);
2232 pcg 1.6 #endif
2233    
2234     }
2235     /* remove menu before sending keys to the application */
2236     menu_hide_all ();
2237 pcg 1.1 #ifndef DEBUG_MENU
2238 pcg 1.8 action_dispatch (& (item->entry.action));
2239 pcg 1.1 #else /* DEBUG_MENU */
2240 pcg 1.8 fprintf (stderr, "%s: %s\n", item->name,
2241 pcg 1.6 item->entry.action.str);
2242 pcg 1.1 #endif /* DEBUG_MENU */
2243 pcg 1.6 break;
2244     }
2245     break;
2246    
2247     default:
2248     if (item->entry.type == MenuSubMenu)
2249     goto DoMenu;
2250     break;
2251     }
2252     return 0;
2253     }
2254     }
2255     DoMenu:
2256     ActiveMenu->item = thisitem;
2257     y = this_y;
2258     if (item != NULL)
2259     {
2260     item = ActiveMenu->item;
2261     if (item->entry.type != MenuLabel)
2262     drawbox_menuitem (y, +1);
2263     if (item->entry.type == MenuSubMenu)
2264     {
2265     int x;
2266    
2267     drawtriangle (ActiveMenu->w, y, -1);
2268    
2269 pcg 1.9 x = ev.x + (ActiveMenu->parent
2270 pcg 1.6 ? ActiveMenu->x
2271 pcg 1.8 : Width2Pixel (ActiveMenu->x));
2272 pcg 1.6
2273     if (x >= item->entry.submenu.menu->x)
2274     {
2275     ActiveMenu = item->entry.submenu.menu;
2276     menu_show ();
2277     return 1;
2278     }
2279     }
2280 pcg 1.1 }
2281 pcg 1.6 return 0;
2282 pcg 1.1 }
2283    
2284     void
2285 pcg 1.9 rxvt_term::menubar_select (XButtonEvent &ev)
2286 pcg 1.1 {
2287 pcg 1.6 menu_t *menu = NULL;
2288 pcg 1.1
2289 pcg 1.6 /* determine the pulldown menu corresponding to the X index */
2290 pcg 1.9 if (ev.y >= 0 && ev.y <= menuBar_height () && CurrentBar != NULL)
2291 pcg 1.6 {
2292     for (menu = CurrentBar->head; menu != NULL; menu = menu->next)
2293     {
2294 pcg 1.8 int x = Width2Pixel (menu->x);
2295     int w = Width2Pixel (menu->len + HSPACE);
2296 pcg 1.6
2297 pcg 1.9 if ((ev.x >= x && ev.x < x + w))
2298 pcg 1.6 break;
2299     }
2300     }
2301 pcg 1.9 switch (ev.type)
2302 pcg 1.6 {
2303     case ButtonRelease:
2304     menu_hide_all ();
2305     break;
2306    
2307     case ButtonPress:
2308 pcg 1.9 if (menu == NULL && Arrows_x && ev.x >= Arrows_x)
2309 pcg 1.6 {
2310     int i;
2311    
2312     for (i = 0; i < NARROWS; i++)
2313     {
2314 pcg 1.9 if (ev.x >= (Arrows_x + (Width2Pixel (4 * i + i)) / 4)
2315     && ev.x < (Arrows_x
2316 pcg 1.8 + (Width2Pixel (4 * i + i + 4)) / 4))
2317 pcg 1.6 {
2318     draw_Arrows (Arrows[i].name, -1);
2319     {
2320 pcg 1.1 #ifdef HAVE_NANOSLEEP
2321 pcg 1.6 struct timespec rqt;
2322 pcg 1.1
2323 pcg 1.6 rqt.tv_sec = 0;
2324     rqt.tv_nsec = MENU_DELAY_USEC * 1000;
2325 pcg 1.8 nanosleep (&rqt, NULL);
2326 pcg 1.1 #else
2327 pcg 1.6 /* use select for timing */
2328     struct timeval tv;
2329 pcg 1.1
2330 pcg 1.6 tv.tv_sec = 0;
2331     tv.tv_usec = MENU_DELAY_USEC;
2332 pcg 1.8 select (0, NULL, NULL, NULL, &tv);
2333 pcg 1.1 #endif
2334 pcg 1.6
2335     }
2336     draw_Arrows (Arrows[i].name, +1);
2337 pcg 1.1 #ifdef DEBUG_MENUARROWS
2338 pcg 1.8 fprintf (stderr, "'%c': ", Arrows[i].name);
2339 pcg 1.1
2340 pcg 1.6 if (CurrentBar == NULL
2341     || (CurrentBar->arrows[i].type != MenuAction
2342     && CurrentBar->arrows[i].type !=
2343     MenuTerminalAction))
2344     {
2345     if (Arrows[i].str != NULL && Arrows[i].str[0])
2346 pcg 1.8 fprintf (stderr, " (default) \\033%s\n",
2347     & (Arrows[i].str[2]));
2348 pcg 1.6 }
2349     else
2350     {
2351 pcg 1.8 fprintf (stderr, "%s\n",
2352 pcg 1.6 CurrentBar->arrows[i].str);
2353     }
2354 pcg 1.1 #else /* DEBUG_MENUARROWS */
2355 pcg 1.9 if (CurrentBar == NULL || action_dispatch (&CurrentBar->arrows[i]))
2356 pcg 1.6 {
2357     if (Arrows[i].str != NULL && Arrows[i].str[0] != 0)
2358     tt_write ((Arrows[i].str + 1),
2359     Arrows[i].str[0]);
2360     }
2361 pcg 1.1 #endif /* DEBUG_MENUARROWS */
2362 pcg 1.6 return;
2363     }
2364     }
2365     }
2366     /* FALLTHROUGH */
2367    
2368     default:
2369     /*
2370     * press menubar or move to a new entry
2371     */
2372     if (menu != NULL && menu != ActiveMenu)
2373     {
2374     menu_hide_all (); /* pop down old menu */
2375     ActiveMenu = menu;
2376     menu_show (); /* pop up new menu */
2377     }
2378     break;
2379 pcg 1.1 }
2380     }
2381    
2382     /*
2383     * general dispatch routine,
2384     * it would be nice to have `sticky' menus
2385     */
2386     void
2387 pcg 1.9 rxvt_term::menubar_control (XButtonEvent &ev)
2388 pcg 1.1 {
2389 pcg 1.9 switch (ev.type)
2390 pcg 1.6 {
2391     case ButtonPress:
2392 pcg 1.9 if (ev.button == Button1)
2393 pcg 1.6 menubar_select (ev);
2394     break;
2395    
2396     case ButtonRelease:
2397 pcg 1.9 if (ev.button == Button1)
2398 pcg 1.6 menu_select (ev);
2399     break;
2400    
2401     case MotionNotify:
2402 pcg 1.8 while (XCheckTypedWindowEvent (display->display, TermWin.parent[0],
2403 pcg 1.9 MotionNotify, (XEvent *)&ev)) ;
2404 pcg 1.6
2405     if (ActiveMenu)
2406     while (menu_select (ev)) ;
2407     else
2408 pcg 1.9 ev.y = -1;
2409     if (ev.y < 0)
2410 pcg 1.6 {
2411     Window unused_root, unused_child;
2412     int unused_root_x, unused_root_y;
2413     unsigned int unused_mask;
2414    
2415 pcg 1.8 XQueryPointer (display->display, menuBar.win,
2416 pcg 1.6 &unused_root, &unused_child,
2417     &unused_root_x, &unused_root_y,
2418 pcg 1.9 &ev.x, &ev.y, &unused_mask);
2419 pcg 1.6 menubar_select (ev);
2420     }
2421     break;
2422 pcg 1.1 }
2423     }
2424    
2425     void
2426 pcg 1.4 rxvt_term::map_menuBar (int map)
2427 pcg 1.1 {
2428 pcg 1.6 if (menubar_mapping (map))
2429     resize_all_windows (0, 0, 0);
2430 pcg 1.1 }
2431     #endif
2432     /*----------------------- end-of-file (C source) -----------------------*/