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