ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/rxvt-unicode/src/menubar.C
Revision: 1.22
Committed: Fri Feb 4 11:41:23 2005 UTC (19 years, 3 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.21: +9 -9 lines
Log Message:
*** empty log message ***

File Contents

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