ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/rxvt-unicode/src/menubar.C
Revision: 1.12
Committed: Wed Mar 3 04:07:52 2004 UTC (20 years, 2 months ago) by pcg
Content type: text/plain
Branch: MAIN
CVS Tags: rel-2_1_0
Changes since 1.11: +2 -2 lines
Log Message:
*** empty log message ***

File Contents

# User Rev Content
1 pcg 1.1 /*--------------------------------*-C-*---------------------------------*
2     * File: menubar.c
3     *----------------------------------------------------------------------*
4     *
5     * Copyright (c) 1997,1998 mj olesen <olesen@me.QueensU.CA>
6     *
7     * This program is free software; you can redistribute it and/or modify
8     * it under the terms of the GNU General Public License as published by
9     * the Free Software Foundation; either version 2 of the License, or
10     * (at your option) any later version.
11     *
12     * This program is distributed in the hope that it will be useful,
13     * but WITHOUT ANY WARRANTY; without even the implied warranty of
14     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15     * GNU General Public License for more details.
16     *
17     * You should have received a copy of the GNU General Public License
18     * along with this program; if not, write to the Free Software
19     * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20     *----------------------------------------------------------------------*
21     * refer.html (or refer.txt) contains up-to-date documentation. The
22     * summary that appears at the end of this file was taken from there.
23     *----------------------------------------------------------------------*/
24    
25     #include "../config.h" /* NECESSARY */
26 pcg 1.10
27     #include <stdlib.h>
28 pcg 1.11 #include <wchar.h>
29 pcg 1.10
30 pcg 1.1 #include "rxvt.h" /* NECESSARY */
31     #ifdef MENUBAR
32     #include "version.h"
33     #include "menubar.h"
34     #include "menubar.intpro" /* PROTOS for internal routines */
35    
36     #define Menu_PixelWidth(menu) \
37     (2 * SHADOW + Width2Pixel ((menu)->width + 3 * HSPACE))
38    
39 pcg 1.6 static const struct
40     {
41 pcg 1.1 const char name; /* (l)eft, (u)p, (d)own, (r)ight */
42     const unsigned char str[5]; /* str[0] = STRLEN (str+1) */
43 pcg 1.6 }
44     Arrows[NARROWS] = {
45     { 'l', "\003\033[D" },
46     { 'u', "\003\033[A" },
47     { 'd', "\003\033[B" },
48     { 'r', "\003\033[C" }
49     };
50 pcg 1.1
51     /*}}} */
52    
53 pcg 1.10 static void
54     draw_string (rxvt_drawable &d, GC gc, rxvt_fontset *fs, int x, int y, char *str, int len)
55     {
56     mbstate mbs;
57    
58     while (len)
59     {
60     wchar_t w;
61     int l = mbrtowc (&w, str, len, mbs);
62    
63     if (l <= 0)
64     break;
65    
66     len -= l;
67     str += l;
68    
69     rxvt_font *font = (*fs)[fs->find_font (w)];
70     text_t ch = w;
71     font->draw (d, x, y, &ch, 1, Color_bg, Color_scroll);
72    
73     x += font->width * wcwidth (w);
74     }
75     }
76    
77 pcg 1.1 /*
78     * find an item called NAME in MENU
79     */
80     menuitem_t *
81 pcg 1.8 rxvt_menuitem_find (const menu_t *menu, const char *name)
82 pcg 1.1 {
83 pcg 1.6 menuitem_t *item;
84 pcg 1.1
85     #ifdef DEBUG_STRICT
86 pcg 1.8 assert (name != NULL);
87     assert (menu != NULL);
88 pcg 1.1 #endif
89    
90 pcg 1.6 /* find the last item in the menu, this is good for separators */
91     for (item = menu->tail; item != NULL; item = item->prev)
92     {
93     if (item->entry.type == MenuSubMenu)
94     {
95 pcg 1.8 if (!STRCMP (name, (item->entry.submenu.menu)->name))
96 pcg 1.6 break;
97     }
98 pcg 1.8 else if ((isSeparator (name) && isSeparator (item->name))
99     || !STRCMP (name, item->name))
100 pcg 1.6 break;
101 pcg 1.1 }
102 pcg 1.6 return item;
103 pcg 1.1 }
104    
105     /*
106     * unlink ITEM from its MENU and free its memory
107     */
108     void
109 pcg 1.4 rxvt_term::menuitem_free (menu_t *menu, menuitem_t *item)
110 pcg 1.1 {
111 pcg 1.6 /* disconnect */
112     menuitem_t *prev, *next;
113 pcg 1.1
114     #ifdef DEBUG_STRICT
115 pcg 1.8 assert (menu != NULL);
116 pcg 1.1 #endif
117    
118 pcg 1.6 prev = item->prev;
119     next = item->next;
120     if (prev != NULL)
121     prev->next = next;
122     if (next != NULL)
123     next->prev = prev;
124    
125     /* new head, tail */
126     if (menu->tail == item)
127     menu->tail = prev;
128     if (menu->head == item)
129     menu->head = next;
130    
131     switch (item->entry.type)
132     {
133     case MenuAction:
134     case MenuTerminalAction:
135 pcg 1.8 free (item->entry.action.str);
136 pcg 1.6 break;
137     case MenuSubMenu:
138     menu_delete (item->entry.submenu.menu);
139     break;
140     }
141     if (item->name != NULL)
142 pcg 1.8 free (item->name);
143 pcg 1.6 if (item->name2 != NULL)
144 pcg 1.8 free (item->name2);
145     free (item);
146 pcg 1.1 }
147    
148     /*
149     * sort command vs. terminal actions and
150     * remove the first character of STR if it's '\0'
151     */
152     int
153 pcg 1.8 rxvt_action_type (action_t *action, unsigned char *str)
154 pcg 1.1 {
155 pcg 1.6 unsigned int len;
156 pcg 1.1
157     #if defined (DEBUG_MENU) || defined (DEBUG_MENUARROWS)
158 pcg 1.8 len = STRLEN (str);
159     fprintf (stderr, " (len %d) = %s\n", len, str);
160 pcg 1.1 #else
161 pcg 1.8 len = rxvt_Str_escaped ((char *)str);
162 pcg 1.1 #endif
163    
164 pcg 1.6 if (!len)
165     return -1;
166 pcg 1.1
167 pcg 1.6 /* sort command vs. terminal actions */
168     action->type = MenuAction;
169     if (str[0] == '\0')
170     {
171     /* the functional equivalent: memmove (str, str+1, len); */
172     unsigned char *dst = (str);
173     unsigned char *src = (str + 1);
174     unsigned char *end = (str + len);
175    
176     while (src <= end)
177     *dst++ = *src++;
178    
179     len--; /* decrement length */
180     if (str[0] != '\0')
181     action->type = MenuTerminalAction;
182 pcg 1.1 }
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 pcg 1.8 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 pcg 1.8 p = STRCHR (string, '\0');
301 pcg 1.6 }
302     else
303     {
304     char *next = string;
305    
306     while (1)
307     {
308 pcg 1.8 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 pcg 1.8 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     unsigned char *str;
356     unsigned int len;
357    
358     if (!parse[i].len)
359     continue;
360    
361 pcg 1.9 str = (unsigned char *) rxvt_malloc (parse[i].len + xtra_len + 1);
362 pcg 1.6
363     len = 0;
364     if (beg.len)
365     {
366 pcg 1.8 STRNCPY (str + len, beg.str, beg.len);
367 pcg 1.6 len += beg.len;
368     }
369 pcg 1.8 STRNCPY (str + len, parse[i].str, parse[i].len);
370 pcg 1.6 len += parse[i].len;
371    
372     if (end.len)
373     {
374 pcg 1.8 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 pcg 1.8 len = STRLEN (name);
439 pcg 1.9 item->name = (char *)rxvt_malloc (len + 1);
440 pcg 1.8 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 pcg 1.8 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 pcg 1.8 STRCPY (item->name2, name2);
469 pcg 1.6 }
470     item->len2 = len;
471     }
472     item->entry.type = MenuLabel;
473 pcg 1.8 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 pcg 1.9 unsigned char *str = (unsigned char *)rxvt_malloc (len + 1);
483 pcg 1.6
484 pcg 1.8 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 pcg 1.8 if (STRCHR (path, '/') != NULL)
515 pcg 1.6 {
516     char *p = path;
517    
518 pcg 1.8 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 pcg 1.8 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 pcg 1.8 if (!STRCMP (path, DOT))
538 pcg 1.6 {
539     /* nothing to do */
540     }
541 pcg 1.8 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 pcg 1.8 if (!STRCMP (path, DOTS))
561 pcg 1.6 {
562 pcg 1.8 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 pcg 1.10 if (!STRCMP (path, m->name))
573     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 pcg 1.8 && !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 pcg 1.8 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 pcg 1.8 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 pcg 1.8 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 pcg 1.8 if (!STRCMP (path, DOTS))
697 pcg 1.6 return (parent != NULL ? parent->parent : parent);
698    
699 pcg 1.8 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 pcg 1.8 menu->len = STRLEN (path);
708 pcg 1.9 menu->name = (char *)rxvt_malloc ((menu->len + 1));
709 pcg 1.8 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 pcg 1.6 if (x >= TermWin.width)
760     return;
761     else if (x + len >= TermWin.width)
762 pcg 1.8 len = (TermWin_TotalWidth () - 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 pcg 1.8 w = Height2Pixel (1) - 2 * SHADOW;
811 pcg 1.1
812 pcg 1.6 x -= SHADOW + (3 * w / 2);
813     y += 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 pcg 1.6 SHADOW + 0, SHADOW + y,
843     ActiveMenu->w - 2 * (SHADOW),
844     HEIGHT_TEXT + 2 * 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     if ((x + ActiveMenu->w) >= TermWin.width)
949 pcg 1.8 x = (TermWin_TotalWidth () - 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 pcg 1.6 : HEIGHT_TEXT + 2 * SHADOW;
955     ActiveMenu->h = h + 2 * SHADOW;
956     }
957 pcg 1.10
958 pcg 1.6 if (ActiveMenu->win == None)
959     {
960 pcg 1.8 ActiveMenu->win = XCreateSimpleWindow (display->display, TermWin.vt,
961 pcg 1.6 x, ActiveMenu->y,
962     ActiveMenu->w, ActiveMenu->h,
963     0,
964     PixColors[Color_fg],
965     PixColors[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 pcg 1.10 const int xoff = (SHADOW + Width2Pixel (HSPACE) / 2);
982     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 pcg 1.6 topShadowGC, botShadowGC,
989     SHADOW, y + SHADOW + 1,
990     ActiveMenu->w - 2 * SHADOW, 0);
991     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 pcg 1.6 : HEIGHT_TEXT + 2 * SHADOW;
1025     menu->h = h + 2 * SHADOW;
1026    
1027     /* ensure menu is in window limits */
1028     if ((x1 + menu->w) >= TermWin.width)
1029 pcg 1.8 x1 = (TermWin_TotalWidth () - menu->w);
1030 pcg 1.6
1031     if ((y1 + menu->h) >= TermWin.height)
1032 pcg 1.8 y1 = (TermWin_TotalHeight () - menu->h);
1033 pcg 1.6
1034     menu->x = (x1 < 0 ? 0 : x1);
1035     menu->y = (y1 < 0 ? 0 : y1);
1036     }
1037 pcg 1.8 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 pcg 1.10 draw_string (*ActiveMenu->drawable, gc, TermWin.fontset,
1042     xoff, 2 * 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 pcg 1.10 draw_string (*ActiveMenu->drawable, gc, TermWin.fontset,
1049     ActiveMenu->w - (xoff + Width2Pixel (xright)), 2 * SHADOW + y, name, len);
1050 pcg 1.9
1051 pcg 1.6 h = HEIGHT_TEXT + 2 * SHADOW;
1052     }
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 pcg 1.8 if (STRLEN (name) && STRCMP (name, "*"))
1149 pcg 1.6 {
1150     do
1151     {
1152 pcg 1.8 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 pcg 1.8 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 pcg 1.8 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 pcg 1.8 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 pcg 1.10 unsigned char *str;
1289     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     unsigned char ch = *str++;
1332    
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 pcg 1.6 else if (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 pcg 1.8 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     if (item->name2 != NULL && STRLEN (item->name2))
1392     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 pcg 1.8 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     /*
1570     * remove leading/trailing space
1571     * and strip-off leading/trailing quotes
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 pcg 1.8 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 pcg 1.8 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 pcg 1.8 else if (!STRCMP (str, "clear"))
1664 pcg 1.6 {
1665     menubar_clear ();
1666     }
1667 pcg 1.8 else if (!STRCMP (str, "done") || rxvt_Str_match (str, "done:"))
1668 pcg 1.6 {
1669     menu_readonly = 1;
1670     }
1671 pcg 1.8 else if (!STRCMP (str, "show"))
1672 pcg 1.6 {
1673     map_menuBar (1);
1674     menu_readonly = 1;
1675     }
1676 pcg 1.8 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 pcg 1.9 name = (char *)rxvt_realloc (CurrentBar->title, STRLEN (str) + 1);
1695 pcg 1.6 if (name != NULL)
1696     {
1697 pcg 1.8 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     xterm_seq (XTerm_Pixmap, str, CHAR_ST);
1713     }
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 pcg 1.8 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     xterm_seq (XTerm_title, buffer, CHAR_ST);
1766     menubar_dump (fp);
1767 pcg 1.8 fclose (fp);
1768 pcg 1.6 }
1769     }
1770 pcg 1.8 else if (!STRCMP (str, "next"))
1771 pcg 1.6 {
1772     if (CurrentBar)
1773     {
1774     CurrentBar = CurrentBar->next;
1775     menu_readonly = 1;
1776     }
1777     }
1778 pcg 1.8 else if (!STRCMP (str, "prev"))
1779 pcg 1.6 {
1780     if (CurrentBar)
1781     {
1782     CurrentBar = CurrentBar->prev;
1783     menu_readonly = 1;
1784     }
1785     }
1786 pcg 1.8 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 pcg 1.8 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.8 rxvt_print_error ("menu error <%s>\n", path);
1853 pcg 1.6 break;
1854     }
1855     if (str[1] == MENUITEM_BEG)
1856     {
1857     name2 = (str + 2);
1858 pcg 1.8 str = STRCHR (name2, MENUITEM_END);
1859 pcg 1.6
1860     if (str == NULL)
1861     {
1862 pcg 1.8 rxvt_print_error ("menu error <%s>\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     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 pcg 1.8 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     (STRCMP (name, SEPARATOR_NAME) ? name : ""),
1912 pcg 1.6 name2, str);
1913     break;
1914    
1915     case '-': /* delete menu entry */
1916 pcg 1.8 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 pcg 1.8 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 pcg 1.8 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     if (BuildMenu != NULL)
1951     {
1952     if (name == NULL || name[0] == '\0')
1953     BuildMenu = menu_delete (BuildMenu);
1954     else
1955     {
1956     const char *n1;
1957     menuitem_t *item;
1958     menu_t *BuildMenu = BuildMenu;
1959    
1960 pcg 1.8 n1 = STRCMP (name, SEPARATOR_NAME) ? name : "";
1961     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 pcg 1.8 MAX_IT (BuildMenu->width, l);
1974 pcg 1.6 }
1975     }
1976     }
1977     menubar_expose ();
1978     }
1979     break;
1980     }
1981     break;
1982 pcg 1.1 }
1983     }
1984    
1985     void
1986 pcg 1.4 rxvt_term::draw_Arrows (int name, int state)
1987 pcg 1.1 {
1988 pcg 1.6 GC top, bot;
1989 pcg 1.1
1990 pcg 1.6 int i;
1991 pcg 1.1
1992     #ifdef MENU_SHADOW_IN
1993 pcg 1.6 state = -state;
1994 pcg 1.1 #endif
1995 pcg 1.6 switch (state)
1996     {
1997     case +1:
1998     top = topShadowGC;
1999     bot = botShadowGC;
2000     break; /* SHADOW_OUT */
2001     case -1:
2002     top = botShadowGC;
2003     bot = topShadowGC;
2004     break; /* SHADOW_IN */
2005     default:
2006     top = bot = scrollbarGC;
2007     break; /* neutral */
2008     }
2009    
2010     if (!Arrows_x)
2011     return;
2012    
2013     for (i = 0; i < NARROWS; i++)
2014     {
2015 pcg 1.10 const int w = Width2Pixel (1);
2016     const int y = (menuBar_TotalHeight () - w) / 2;
2017     int x = Arrows_x + (5 * Width2Pixel (i)) / 4;
2018 pcg 1.6
2019     if (!name || name == Arrows[i].name)
2020 pcg 1.8 rxvt_Draw_Triangle (display->display, menuBar.win, top, bot, x, y, w,
2021 pcg 1.6 Arrows[i].name);
2022 pcg 1.1 }
2023 pcg 1.8 XFlush (display->display);
2024 pcg 1.1 }
2025    
2026     void
2027 pcg 1.4 rxvt_term::menubar_expose ()
2028 pcg 1.1 {
2029 pcg 1.10 menu_t *menu;
2030     int x;
2031 pcg 1.1
2032 pcg 1.9 if (!menubar_visible () || menuBar.win == 0)
2033 pcg 1.6 return;
2034 pcg 1.1
2035 pcg 1.6 if (menubarGC == None)
2036     {
2037     /* Create the graphics context */
2038     XGCValues gcvalue;
2039 pcg 1.1
2040 pcg 1.6 gcvalue.foreground = (XDEPTH <= 2 ? PixColors[Color_fg]
2041     : PixColors[Color_Black]);
2042 pcg 1.8 menubarGC = XCreateGC (display->display, menuBar.win,
2043 pcg 1.10 GCForeground, &gcvalue);
2044 pcg 1.1
2045     }
2046 pcg 1.6 /* make sure the font is correct */
2047 pcg 1.8 XClearWindow (display->display, menuBar.win);
2048 pcg 1.1
2049 pcg 1.6 menu_hide_all ();
2050 pcg 1.1
2051 pcg 1.6 x = 0;
2052     if (CurrentBar != NULL)
2053     {
2054     for (menu = CurrentBar->head; menu != NULL; menu = menu->next)
2055     {
2056     int len = menu->len;
2057 pcg 1.1
2058 pcg 1.6 x = (menu->x + menu->len + HSPACE);
2059 pcg 1.1
2060     #ifdef DEBUG_MENU_LAYOUT
2061 pcg 1.8 rxvt_print_menu_descendants (menu);
2062 pcg 1.1 #endif
2063    
2064 pcg 1.6 if (x >= TermWin.ncol)
2065     len = (TermWin.ncol - (menu->x + HSPACE));
2066 pcg 1.1
2067 pcg 1.6 drawbox_menubar (menu->x, len, +1);
2068 pcg 1.10 draw_string (*menuBar.drawable, menubarGC, TermWin.fontset,
2069 pcg 1.9 (Width2Pixel (menu->x) + Width2Pixel (HSPACE) / 2),
2070 pcg 1.10 SHADOW, menu->name, len);
2071 pcg 1.6
2072     if (x >= TermWin.ncol)
2073     break;
2074     }
2075     }
2076     drawbox_menubar (x, TermWin.ncol, (CurrentBar ? +1 : -1));
2077    
2078     /* add the menuBar title, if it exists and there's plenty of room */
2079     Arrows_x = 0;
2080     if (x < TermWin.ncol)
2081     {
2082     const char *str;
2083     int ncol;
2084     unsigned int len;
2085     char title[256];
2086    
2087     ncol = (int)TermWin.ncol;
2088     if (x < (ncol - (NARROWS + 1)))
2089     {
2090     ncol -= (NARROWS + 1);
2091 pcg 1.8 Arrows_x = Width2Pixel (ncol);
2092 pcg 1.6 }
2093     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 pcg 1.10 draw_string (*menuBar.drawable, menubarGC, TermWin.fontset,
2132 pcg 1.9 Width2Pixel (x) + Width2Pixel (ncol + HSPACE) / 2,
2133 pcg 1.10 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     y = SHADOW;
2189 pcg 1.9 if (ev.x >= 0 && ev.x <= (ActiveMenu->w - SHADOW))
2190 pcg 1.6 {
2191     for (item = ActiveMenu->head; item != NULL; item = item->next)
2192     {
2193 pcg 1.10 int h = HEIGHT_TEXT + 2 * 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     this_y = y - SHADOW;
2212    
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     h = HEIGHT_TEXT + 2 * 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 pcg 1.1 #ifdef HAVE_NANOSLEEP
2255 pcg 1.10 struct timespec rqt;
2256 pcg 1.1
2257 pcg 1.10 rqt.tv_sec = 0;
2258     rqt.tv_nsec = MENU_DELAY_USEC * 1000;
2259     nanosleep (&rqt, NULL);
2260 pcg 1.1 #else
2261 pcg 1.10 /* use select for timing */
2262     struct timeval tv;
2263 pcg 1.1
2264 pcg 1.10 tv.tv_sec = 0;
2265     tv.tv_usec = MENU_DELAY_USEC;
2266     select (0, NULL, NULL, NULL, &tv);
2267 pcg 1.6 #endif
2268     /* remove menu before sending keys to the application */
2269     menu_hide_all ();
2270 pcg 1.1 #ifndef DEBUG_MENU
2271 pcg 1.8 action_dispatch (& (item->entry.action));
2272 pcg 1.1 #else /* DEBUG_MENU */
2273 pcg 1.8 fprintf (stderr, "%s: %s\n", item->name,
2274 pcg 1.6 item->entry.action.str);
2275 pcg 1.1 #endif /* DEBUG_MENU */
2276 pcg 1.6 break;
2277     }
2278     break;
2279    
2280     default:
2281     if (item->entry.type == MenuSubMenu)
2282     goto DoMenu;
2283     break;
2284     }
2285     return 0;
2286     }
2287     }
2288 pcg 1.10
2289 pcg 1.6 DoMenu:
2290     ActiveMenu->item = thisitem;
2291     y = this_y;
2292 pcg 1.10
2293     if (thisitem != NULL)
2294 pcg 1.6 {
2295     item = ActiveMenu->item;
2296     if (item->entry.type != MenuLabel)
2297     drawbox_menuitem (y, +1);
2298 pcg 1.10
2299 pcg 1.6 if (item->entry.type == MenuSubMenu)
2300     {
2301 pcg 1.10 int x;
2302 pcg 1.6
2303     drawtriangle (ActiveMenu->w, y, -1);
2304    
2305 pcg 1.9 x = ev.x + (ActiveMenu->parent
2306 pcg 1.6 ? ActiveMenu->x
2307 pcg 1.8 : Width2Pixel (ActiveMenu->x));
2308 pcg 1.6
2309     if (x >= item->entry.submenu.menu->x)
2310     {
2311     ActiveMenu = item->entry.submenu.menu;
2312     menu_show ();
2313     return 1;
2314     }
2315     }
2316 pcg 1.1 }
2317 pcg 1.6 return 0;
2318 pcg 1.1 }
2319    
2320     void
2321 pcg 1.9 rxvt_term::menubar_select (XButtonEvent &ev)
2322 pcg 1.1 {
2323 pcg 1.10 menu_t *menu = NULL;
2324 pcg 1.1
2325 pcg 1.6 /* determine the pulldown menu corresponding to the X index */
2326 pcg 1.9 if (ev.y >= 0 && ev.y <= menuBar_height () && CurrentBar != NULL)
2327 pcg 1.6 {
2328     for (menu = CurrentBar->head; menu != NULL; menu = menu->next)
2329     {
2330 pcg 1.10 int x = Width2Pixel (menu->x);
2331     int w = Width2Pixel (menu->len + HSPACE);
2332 pcg 1.6
2333 pcg 1.9 if ((ev.x >= x && ev.x < x + w))
2334 pcg 1.6 break;
2335     }
2336     }
2337 pcg 1.9 switch (ev.type)
2338 pcg 1.6 {
2339     case ButtonRelease:
2340     menu_hide_all ();
2341     break;
2342    
2343     case ButtonPress:
2344 pcg 1.9 if (menu == NULL && Arrows_x && ev.x >= Arrows_x)
2345 pcg 1.6 {
2346     int i;
2347    
2348     for (i = 0; i < NARROWS; i++)
2349     {
2350 pcg 1.9 if (ev.x >= (Arrows_x + (Width2Pixel (4 * i + i)) / 4)
2351     && ev.x < (Arrows_x
2352 pcg 1.8 + (Width2Pixel (4 * i + i + 4)) / 4))
2353 pcg 1.6 {
2354     draw_Arrows (Arrows[i].name, -1);
2355     {
2356 pcg 1.1 #ifdef HAVE_NANOSLEEP
2357 pcg 1.6 struct timespec rqt;
2358 pcg 1.1
2359 pcg 1.6 rqt.tv_sec = 0;
2360     rqt.tv_nsec = MENU_DELAY_USEC * 1000;
2361 pcg 1.8 nanosleep (&rqt, NULL);
2362 pcg 1.1 #else
2363 pcg 1.6 /* use select for timing */
2364     struct timeval tv;
2365 pcg 1.1
2366 pcg 1.6 tv.tv_sec = 0;
2367     tv.tv_usec = MENU_DELAY_USEC;
2368 pcg 1.8 select (0, NULL, NULL, NULL, &tv);
2369 pcg 1.1 #endif
2370 pcg 1.6
2371     }
2372     draw_Arrows (Arrows[i].name, +1);
2373 pcg 1.1 #ifdef DEBUG_MENUARROWS
2374 pcg 1.8 fprintf (stderr, "'%c': ", Arrows[i].name);
2375 pcg 1.1
2376 pcg 1.6 if (CurrentBar == NULL
2377     || (CurrentBar->arrows[i].type != MenuAction
2378     && CurrentBar->arrows[i].type !=
2379     MenuTerminalAction))
2380     {
2381     if (Arrows[i].str != NULL && Arrows[i].str[0])
2382 pcg 1.8 fprintf (stderr, " (default) \\033%s\n",
2383     & (Arrows[i].str[2]));
2384 pcg 1.6 }
2385     else
2386     {
2387 pcg 1.8 fprintf (stderr, "%s\n",
2388 pcg 1.6 CurrentBar->arrows[i].str);
2389     }
2390 pcg 1.1 #else /* DEBUG_MENUARROWS */
2391 pcg 1.9 if (CurrentBar == NULL || action_dispatch (&CurrentBar->arrows[i]))
2392 pcg 1.6 {
2393     if (Arrows[i].str != NULL && Arrows[i].str[0] != 0)
2394     tt_write ((Arrows[i].str + 1),
2395     Arrows[i].str[0]);
2396     }
2397 pcg 1.1 #endif /* DEBUG_MENUARROWS */
2398 pcg 1.6 return;
2399     }
2400     }
2401     }
2402     /* FALLTHROUGH */
2403    
2404     default:
2405     /*
2406     * press menubar or move to a new entry
2407     */
2408     if (menu != NULL && menu != ActiveMenu)
2409     {
2410     menu_hide_all (); /* pop down old menu */
2411     ActiveMenu = menu;
2412     menu_show (); /* pop up new menu */
2413     }
2414     break;
2415 pcg 1.1 }
2416     }
2417    
2418     /*
2419     * general dispatch routine,
2420     * it would be nice to have `sticky' menus
2421     */
2422     void
2423 pcg 1.9 rxvt_term::menubar_control (XButtonEvent &ev)
2424 pcg 1.1 {
2425 pcg 1.9 switch (ev.type)
2426 pcg 1.6 {
2427     case ButtonPress:
2428 pcg 1.9 if (ev.button == Button1)
2429 pcg 1.6 menubar_select (ev);
2430     break;
2431    
2432     case ButtonRelease:
2433 pcg 1.9 if (ev.button == Button1)
2434 pcg 1.6 menu_select (ev);
2435     break;
2436    
2437     case MotionNotify:
2438 pcg 1.8 while (XCheckTypedWindowEvent (display->display, TermWin.parent[0],
2439 pcg 1.10 MotionNotify, (XEvent *)&ev)) ;
2440 pcg 1.6
2441     if (ActiveMenu)
2442     while (menu_select (ev)) ;
2443     else
2444 pcg 1.9 ev.y = -1;
2445     if (ev.y < 0)
2446 pcg 1.6 {
2447     Window unused_root, unused_child;
2448     int unused_root_x, unused_root_y;
2449     unsigned int unused_mask;
2450    
2451 pcg 1.8 XQueryPointer (display->display, menuBar.win,
2452 pcg 1.6 &unused_root, &unused_child,
2453     &unused_root_x, &unused_root_y,
2454 pcg 1.9 &ev.x, &ev.y, &unused_mask);
2455 pcg 1.6 menubar_select (ev);
2456     }
2457     break;
2458 pcg 1.1 }
2459     }
2460    
2461     void
2462 pcg 1.4 rxvt_term::map_menuBar (int map)
2463 pcg 1.1 {
2464 pcg 1.6 if (menubar_mapping (map))
2465     resize_all_windows (0, 0, 0);
2466 pcg 1.1 }
2467     #endif
2468     /*----------------------- end-of-file (C source) -----------------------*/