ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/rxvt-unicode/src/menubar.C
Revision: 1.32
Committed: Mon Jan 16 14:48:39 2006 UTC (18 years, 3 months ago) by root
Content type: text/plain
Branch: MAIN
CVS Tags: HEAD
Changes since 1.31: +0 -0 lines
State: FILE REMOVED
Log Message:
*** empty log message ***

File Contents

# Content
1 /*--------------------------------*-C-*---------------------------------*
2 * File: menubar.C
3 *----------------------------------------------------------------------*
4 *
5 * Copyright (c) 1997,1998 mj olesen <olesen@me.QueensU.CA>
6 * Copyright (c) 2004 Marc Lehmann <pcg@goof.com>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 *----------------------------------------------------------------------*
22 * refer.html (or refer.txt) contains up-to-date documentation. The
23 * summary that appears at the end of this file was taken from there.
24 *----------------------------------------------------------------------*/
25
26 #include "../config.h" /* NECESSARY */
27
28 #include <cstdlib>
29
30 #include "rxvt.h" /* NECESSARY */
31 #ifdef MENUBAR
32 #include "version.h"
33 #include "menubar.h"
34
35 #define Menu_PixelWidth(menu) \
36 (2 * MENU_SHADOW + Width2Pixel ((menu)->width + 3 * HSPACE))
37
38 static const struct
39 {
40 const char name; /* (l)eft, (u)p, (d)own, (r)ight */
41 const char str[5]; /* str[0] = strlen (str+1) */
42 }
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
50 /*}}} */
51
52 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 /*
77 * find an item called NAME in MENU
78 */
79 menuitem_t *
80 rxvt_menuitem_find (const menu_t *menu, const char *name)
81 {
82 menuitem_t *item;
83
84 #ifdef DEBUG_STRICT
85 assert (name != NULL);
86 assert (menu != NULL);
87 #endif
88
89 /* 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 if (!strcmp (name, (item->entry.submenu.menu)->name))
95 break;
96 }
97 else if ((isSeparator (name) && isSeparator (item->name))
98 || !strcmp (name, item->name))
99 break;
100 }
101 return item;
102 }
103
104 /*
105 * unlink ITEM from its MENU and free its memory
106 */
107 void
108 rxvt_term::menuitem_free (menu_t *menu, menuitem_t *item)
109 {
110 /* disconnect */
111 menuitem_t *prev, *next;
112
113 #ifdef DEBUG_STRICT
114 assert (menu != NULL);
115 #endif
116
117 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 free (item->entry.action.str);
135 break;
136 case MenuSubMenu:
137 menu_delete (item->entry.submenu.menu);
138 break;
139 }
140 if (item->name != NULL)
141 free (item->name);
142 if (item->name2 != NULL)
143 free (item->name2);
144 free (item);
145 }
146
147 /*
148 * sort command vs. terminal actions and
149 * remove the first character of STR if it's '\0'
150 */
151 int
152 rxvt_action_type (action_t *action, char *str)
153 {
154 unsigned int len;
155
156 #if defined (DEBUG_MENU) || defined (DEBUG_MENUARROWS)
157 len = strlen (str);
158 fprintf (stderr, " (len %d) = %s\n", len, str);
159 #else
160 len = rxvt_Str_escaped ((char *)str);
161 #endif
162
163 if (!len)
164 return -1;
165
166 /* 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 char *dst = (str);
172 char *src = (str + 1);
173 char *end = (str + len);
174
175 while (src <= end)
176 *dst++ = *src++;
177
178 len--; /* decrement length */
179 if (str[0] != '\0')
180 action->type = MenuTerminalAction;
181 }
182
183 action->str = str;
184 action->len = len;
185
186 return 0;
187 }
188
189 int
190 rxvt_term::action_dispatch (action_t *action)
191 {
192 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 }
206 return 0;
207 }
208
209 /* return the arrow index corresponding to NAME */
210 int
211 rxvt_menuarrow_find (char name)
212 {
213 int i;
214
215 for (i = 0; i < NARROWS; i++)
216 if (name == Arrows[i].name)
217 return i;
218 return -1;
219 }
220
221 /* free the memory associated with arrow NAME of the current menubar */
222 void
223 rxvt_term::menuarrow_free (char name)
224 {
225 int i;
226
227 if (name)
228 {
229 i = rxvt_menuarrow_find (name);
230 if (i >= 0)
231 {
232 action_t *act = & (CurrentBar->arrows[i]);
233
234 switch (act->type)
235 {
236 case MenuAction:
237 case MenuTerminalAction:
238 free (act->str);
239 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 }
251 }
252
253 void
254 rxvt_term::menuarrow_add (char *string)
255 {
256 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 memset (parse, 0, sizeof (parse));
270
271 /* fprintf (stderr, "add arrows = `%s'\n", string); */
272 for (p = string; p != NULL && *p; string = p)
273 {
274 p = (string + 3);
275 /* fprintf (stderr, "parsing at %s\n", string); */
276 switch (string[1])
277 {
278 case 'b':
279 cur = &beg;
280 break;
281 case 'e':
282 cur = &end;
283 break;
284
285 default:
286 i = rxvt_menuarrow_find (string[1]);
287 if (i >= 0)
288 cur = & (parse[i]);
289 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 p = strchr (string, '\0');
301 }
302 else
303 {
304 char *next = string;
305
306 while (1)
307 {
308 p = strchr (next, '<');
309 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 p = strchr (next, '\0');
319 break;
320 }
321 next = (p + 1);
322 }
323 }
324
325 if (p == NULL)
326 return;
327 cur->len = (p - string);
328 }
329
330 #ifdef DEBUG_MENUARROWS
331 cur = &beg;
332 fprintf (stderr, "<b> (len %d) = %.*s\n",
333 cur->len, cur->len, (cur->str ? cur->str : ""));
334 for (i = 0; i < NARROWS; i++)
335 {
336 cur = & (parse[i]);
337 fprintf (stderr, "<%c> (len %d) = %.*s\n",
338 Arrows[i].name,
339 cur->len, cur->len, (cur->str ? cur->str : ""));
340 }
341 cur = &end;
342 fprintf (stderr, "<e> (len %d) = %.*s\n",
343 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 char *str;
356 unsigned int len;
357
358 if (!parse[i].len)
359 continue;
360
361 str = (char *)rxvt_malloc (parse[i].len + xtra_len + 1);
362
363 len = 0;
364 if (beg.len)
365 {
366 strncpy (str + len, beg.str, beg.len);
367 len += beg.len;
368 }
369 strncpy (str + len, parse[i].str, parse[i].len);
370 len += parse[i].len;
371
372 if (end.len)
373 {
374 strncpy (str + len, end.str, end.len);
375 len += end.len;
376 }
377 str[len] = '\0';
378
379 #ifdef DEBUG_MENUARROWS
380 fprintf (stderr, "<%c> (len %d) = %s\n", Arrows[i].name, len, str);
381 #endif
382 if (rxvt_action_type (& (CurrentBar->arrows[i]), str) < 0)
383 free (str);
384 }
385 }
386
387 menuitem_t *
388 rxvt_menuitem_add (menu_t *menu, const char *name, const char *name2, const char *action)
389 {
390 menuitem_t *item;
391 unsigned int len;
392
393 #ifdef DEBUG_STRICT
394 assert (name != NULL);
395 assert (action != NULL);
396 #endif
397
398 if (menu == NULL)
399 return NULL;
400
401 if (isSeparator (name))
402 {
403 /* add separator, no action */
404 name = "";
405 action = "";
406 }
407 else
408 {
409 /*
410 * add/replace existing menu item
411 */
412 item = rxvt_menuitem_find (menu, name);
413 if (item != NULL)
414 {
415 if (item->name2 != NULL && name2 != NULL)
416 {
417 free (item->name2);
418 item->len2 = 0;
419 item->name2 = NULL;
420 }
421 switch (item->entry.type)
422 {
423 case MenuAction:
424 case MenuTerminalAction:
425 free (item->entry.action.str);
426 item->entry.action.str = NULL;
427 break;
428 }
429 goto Item_Found;
430 }
431 }
432 /* allocate a new itemect */
433 item = (menuitem_t *) rxvt_malloc (sizeof (menuitem_t));
434
435 item->len2 = 0;
436 item->name2 = NULL;
437
438 len = strlen (name);
439 item->name = (char *)rxvt_malloc (len + 1);
440 strcpy (item->name, name);
441 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 len = strlen (name2);
463 if (len == 0)
464 item->name2 = NULL;
465 else
466 {
467 item->name2 = (char *)rxvt_malloc (len + 1);
468 strcpy (item->name2, name2);
469 }
470 item->len2 = len;
471 }
472 item->entry.type = MenuLabel;
473 len = strlen (action);
474
475 if (len == 0 && item->name2 != NULL)
476 {
477 action = item->name2;
478 len = item->len2;
479 }
480 if (len)
481 {
482 char *str = (char *)rxvt_malloc (len + 1);
483
484 strcpy (str, action);
485
486 if (rxvt_action_type (& (item->entry.action), str) < 0)
487 free (str);
488 }
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
493 return item;
494 }
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 rxvt_term::menu_find_base (menu_t **menu, char *path)
502 {
503 menu_t *m = NULL;
504 menuitem_t *item;
505
506 #ifdef DEBUG_STRICT
507 assert (menu != NULL);
508 assert (CurrentBar != NULL);
509 #endif
510
511 if (path[0] == '\0')
512 return path;
513
514 if (strchr (path, '/') != NULL)
515 {
516 char *p = path;
517
518 while ((p = strchr (p, '/')) != NULL)
519 {
520 p++;
521 if (*p == '/')
522 path = p;
523 }
524
525 if (path[0] == '/')
526 {
527 path++;
528 *menu = NULL;
529 }
530
531 while ((p = strchr (path, '/')) != NULL)
532 {
533 p[0] = '\0';
534 if (path[0] == '\0')
535 return NULL;
536
537 if (!strcmp (path, DOT))
538 {
539 /* nothing to do */
540 }
541 else if (!strcmp (path, DOTS))
542 {
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
560 if (!strcmp (path, DOTS))
561 {
562 path += strlen (DOTS);
563 if (*menu != NULL)
564 *menu = (*menu)->parent;
565 return path;
566 }
567
568 /* find this menu */
569 if (*menu == NULL)
570 {
571 for (m = CurrentBar->tail; m != NULL; m = m->prev)
572 if (!strcmp (path, m->name))
573 break;
574 }
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 && !strcmp (path, (item->entry.submenu.menu)->name))
582 {
583 m = (item->entry.submenu.menu);
584 break;
585 }
586 }
587 }
588
589 if (m != NULL)
590 {
591 *menu = m;
592 path += strlen (path);
593 }
594
595 return path;
596 }
597
598 /*
599 * delete this entire menu
600 */
601 menu_t *
602 rxvt_term::menu_delete (menu_t *menu)
603 {
604 menu_t *parent = NULL, *prev, *next;
605 menuitem_t *item;
606
607 #ifdef DEBUG_STRICT
608 assert (CurrentBar != NULL);
609 #endif
610
611 /* 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 menuitem_t *p = item->prev;
656
657 menuitem_free (menu, item);
658 item = p;
659 }
660
661 free (menu->name);
662 free (menu);
663
664 return parent;
665 }
666
667 menu_t *
668 rxvt_term::menu_add (menu_t *parent, char *path)
669 {
670 menu_t *menu;
671
672 #ifdef DEBUG_STRICT
673 assert (CurrentBar != NULL);
674 #endif
675
676 if (strchr (path, '/') != NULL)
677 {
678 char *p;
679
680 if (path[0] == '/')
681 {
682 /* shouldn't happen */
683 path++;
684 parent = NULL;
685 }
686 while ((p = strchr (path, '/')) != NULL)
687 {
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 if (!strcmp (path, DOTS))
697 return (parent != NULL ? parent->parent : parent);
698
699 if (!strcmp (path, DOT) || path[0] == '\0')
700 return parent;
701
702 /* allocate a new menu */
703 menu = (menu_t *) rxvt_malloc (sizeof (menu_t));
704
705 menu->width = 0;
706 menu->parent = parent;
707 menu->len = strlen (path);
708 menu->name = (char *)rxvt_malloc ((menu->len + 1));
709 strcpy (menu->name, path);
710
711 /* initialize head/tail */
712 menu->head = menu->tail = NULL;
713 menu->prev = menu->next = NULL;
714
715 menu->win = None;
716 menu->drawable = 0;
717 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 item = rxvt_menuitem_add (parent, path, "", "");
737 if (item == NULL)
738 {
739 free (menu);
740 return parent;
741 }
742 #ifdef DEBUG_STRICT
743 assert (item->entry.type == MenuLabel);
744 #endif
745 item->entry.type = MenuSubMenu;
746 item->entry.submenu.menu = menu;
747 }
748
749 return menu;
750 }
751
752 void
753 rxvt_term::drawbox_menubar (int x, int len, int state)
754 {
755 GC top, bot;
756
757 x = Width2Pixel (x);
758 len = Width2Pixel (len + HSPACE);
759 if (x >= width)
760 return;
761 else if (x + len >= width)
762 len = (this->width - x);
763
764 #ifdef MENUBAR_SHADOW_IN
765 state = -state;
766 #endif
767 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 }
781
782 rxvt_Draw_Shadow (display->display, menuBar.win, top, bot,
783 x, 0, len, menuBar_TotalHeight ());
784 }
785
786 void
787 rxvt_term::drawtriangle (int x, int y, int state)
788 {
789 GC top, bot;
790 int w;
791
792 #ifdef MENU_SHADOW_IN
793 state = -state;
794 #endif
795 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 }
809
810 w = Height2Pixel (1) - 2 * MENU_SHADOW;
811
812 x -= MENU_SHADOW + (3 * w / 2);
813 y += MENU_SHADOW * 3;
814
815 rxvt_Draw_Triangle (display->display, ActiveMenu->win, top, bot, x, y, w, 'r');
816 }
817
818 void
819 rxvt_term::drawbox_menuitem (int y, int state)
820 {
821 GC top, bot;
822
823 #ifdef MENU_SHADOW_IN
824 state = -state;
825 #endif
826 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 rxvt_Draw_Shadow (display->display, ActiveMenu->win, top, bot,
842 MENU_SHADOW + 0, MENU_SHADOW + y,
843 ActiveMenu->w - 2 * (MENU_SHADOW),
844 HEIGHT_TEXT + 2 * MENU_SHADOW);
845 XFlush (display->display);
846 }
847
848 #ifdef DEBUG_MENU_LAYOUT
849 void
850 rxvt_print_menu_ancestors (menu_t *menu)
851 {
852 if (menu == NULL)
853 {
854 fprintf (stderr, "Top Level menu\n");
855 return;
856 }
857
858 fprintf (stderr, "menu %s ", menu->name);
859 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
872 if (item == NULL)
873 {
874 fprintf (stderr, "is an orphan!\n");
875 return;
876 }
877 }
878
879 fprintf (stderr, "\n");
880 rxvt_print_menu_ancestors (menu->parent);
881 }
882
883 void
884 rxvt_print_menu_descendants (menu_t *menu)
885 {
886 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 fprintf (stderr, ">");
900 fprintf (stderr, "%s\n", menu->name);
901
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 fprintf (stderr, "> %s == NULL\n", item->name);
908 else
909 rxvt_print_menu_descendants (item->entry.submenu.menu);
910 }
911 else
912 {
913 for (i = 0; i < level; i++)
914 fprintf (stderr, "+");
915 if (item->entry.type == MenuLabel)
916 fprintf (stderr, "label: ");
917 fprintf (stderr, "%s\n", item->name);
918 }
919 }
920
921 for (i = 0; i < level; i++)
922 fprintf (stderr, "<");
923 fprintf (stderr, "\n");
924 }
925 #endif
926
927 /* pop up/down the current menu and redraw the menuBar button */
928 void
929 rxvt_term::menu_show ()
930 {
931 int x, y, xright;
932 menuitem_t *item;
933
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 x = Width2Pixel (x);
944
945 ActiveMenu->y = 1;
946 ActiveMenu->w = Menu_PixelWidth (ActiveMenu);
947
948 if ((x + ActiveMenu->w) >= width)
949 x = (this->width - ActiveMenu->w);
950
951 /* find the height */
952 for (h = 0, item = ActiveMenu->head; item != NULL; item = item->next)
953 h += isSeparator (item->name) ? HEIGHT_SEPARATOR
954 : HEIGHT_TEXT + 2 * MENU_SHADOW;
955 ActiveMenu->h = h + 2 * MENU_SHADOW;
956 }
957
958 if (ActiveMenu->win == None)
959 {
960 ActiveMenu->win = XCreateSimpleWindow (display->display, vt,
961 x, ActiveMenu->y,
962 ActiveMenu->w, ActiveMenu->h,
963 0,
964 pix_colors[Color_fg],
965 pix_colors[Color_scroll]);
966 ActiveMenu->drawable = new rxvt_drawable (display, ActiveMenu->win);
967 XMapWindow (display->display, ActiveMenu->win);
968 }
969
970 rxvt_Draw_Shadow (display->display, ActiveMenu->win,
971 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 const int xoff = (MENU_SHADOW + Width2Pixel (HSPACE) / 2);
982 register int h;
983 GC gc = menubarGC;
984
985 if (isSeparator (item->name))
986 {
987 rxvt_Draw_Shadow (display->display, ActiveMenu->win,
988 topShadowGC, botShadowGC,
989 MENU_SHADOW, y + MENU_SHADOW + 1,
990 ActiveMenu->w - 2 * MENU_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 gc = botShadowGC;
1000 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 menu->w = Menu_PixelWidth (menu);
1014
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 h += isSeparator (it->name) ? HEIGHT_SEPARATOR
1024 : HEIGHT_TEXT + 2 * MENU_SHADOW;
1025 menu->h = h + 2 * MENU_SHADOW;
1026
1027 /* ensure menu is in window limits */
1028 if ((x1 + menu->w) >= width)
1029 x1 = (this->width - menu->w);
1030
1031 if ((y1 + menu->h) >= height)
1032 y1 = (this->height - menu->h);
1033
1034 menu->x = (x1 < 0 ? 0 : x1);
1035 menu->y = (y1 < 0 ? 0 : y1);
1036 }
1037 else if (item->name2 && !strcmp (name, item->name2))
1038 name = NULL;
1039
1040 if (len && name)
1041 draw_string (*ActiveMenu->drawable, gc, fontset[0],
1042 xoff, 2 * MENU_SHADOW + y, name, len);
1043
1044 len = item->len2;
1045 name = item->name2;
1046
1047 if (len && name)
1048 draw_string (*ActiveMenu->drawable, gc, fontset[0],
1049 ActiveMenu->w - (xoff + Width2Pixel (xright)), 2 * MENU_SHADOW + y, name, len);
1050
1051 h = HEIGHT_TEXT + 2 * MENU_SHADOW;
1052 }
1053 y += h;
1054 }
1055 }
1056
1057 void
1058 rxvt_term::menu_display (void (rxvt_term::*update) ())
1059 {
1060 if (ActiveMenu == NULL)
1061 return;
1062
1063 delete ActiveMenu->drawable;
1064 if (ActiveMenu->win != None)
1065 XDestroyWindow (display->display, ActiveMenu->win);
1066 ActiveMenu->win = None;
1067 ActiveMenu->item = NULL;
1068
1069 if (ActiveMenu->parent == NULL)
1070 drawbox_menubar (ActiveMenu->x, ActiveMenu->len, +1);
1071
1072 ActiveMenu = ActiveMenu->parent;
1073 (this->*update) ();
1074 }
1075
1076 void
1077 rxvt_term::menu_hide_all ()
1078 {
1079 menu_display (&rxvt_term::menu_hide_all);
1080 }
1081
1082 void
1083 rxvt_term::menu_hide ()
1084 {
1085 menu_display (&rxvt_term::menu_show);
1086 }
1087
1088 void
1089 rxvt_term::menu_clear (menu_t *menu)
1090 {
1091 if (menu != NULL)
1092 {
1093 menuitem_t *item = menu->tail;
1094
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 }
1105 }
1106
1107 void
1108 rxvt_term::menubar_clear ()
1109 {
1110 if (CurrentBar != NULL)
1111 {
1112 menu_t *menu = CurrentBar->tail;
1113
1114 while (menu != NULL)
1115 {
1116 menu_t *prev = menu->prev;
1117
1118 menu_delete (menu);
1119 menu = prev;
1120 }
1121 CurrentBar->head = CurrentBar->tail = NULL;
1122
1123 if (CurrentBar->title)
1124 {
1125 free (CurrentBar->title);
1126 CurrentBar->title = NULL;
1127 }
1128
1129 menuarrow_free (0); /* remove all arrow functions */
1130 }
1131
1132 ActiveMenu = NULL;
1133 }
1134
1135 #if (MENUBAR_MAX > 1)
1136 /* find if menu already exists */
1137 bar_t *
1138 rxvt_term::menubar_find (const char *name)
1139 {
1140 bar_t *bar = CurrentBar;
1141
1142 #ifdef DEBUG_MENUBAR_STACKING
1143 fprintf (stderr, "looking for [menu:%s] ...", name ? name : " (nil)");
1144 #endif
1145 if (bar == NULL || name == NULL)
1146 return NULL;
1147
1148 if (strlen (name) && strcmp (name, "*"))
1149 {
1150 do
1151 {
1152 if (!strcmp (bar->name, name))
1153 {
1154 #ifdef DEBUG_MENUBAR_STACKING
1155 fprintf (stderr, " found!\n");
1156 #endif
1157 return bar;
1158 }
1159 bar = bar->next;
1160 }
1161 while (bar != CurrentBar);
1162 bar = NULL;
1163 }
1164 #ifdef DEBUG_MENUBAR_STACKING
1165 fprintf (stderr, "%s found!\n", (bar ? "" : " NOT"));
1166 #endif
1167
1168 return bar;
1169 }
1170
1171 int
1172 rxvt_term::menubar_push (const char *name)
1173 {
1174 int ret = 1;
1175 bar_t *bar;
1176
1177 if (CurrentBar == NULL)
1178 {
1179 /* allocate first one */
1180 bar = (bar_t *) rxvt_malloc (sizeof (bar_t));
1181
1182 memset (bar, 0, sizeof (bar_t));
1183 /* 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 bar = (bar_t *) rxvt_malloc (sizeof (bar_t));
1209 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
1234 menubar_clear ();
1235 }
1236 }
1237
1238 /* give menubar this name */
1239 strncpy (CurrentBar->name, name, MAXNAME);
1240 CurrentBar->name[MAXNAME - 1] = '\0';
1241
1242 return ret;
1243 }
1244
1245 /* switch to a menu called NAME and remove it */
1246 void
1247 rxvt_term::menubar_remove (const char *name)
1248 {
1249 bar_t *bar;
1250
1251 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 free (CurrentBar);
1279 CurrentBar = prev;
1280 }
1281 }
1282 while (CurrentBar && !strcmp (name, "*"));
1283 }
1284
1285 void
1286 rxvt_action_decode (FILE *fp, action_t *act)
1287 {
1288 char *str;
1289 short len;
1290
1291 if (act == NULL || (len = act->len) == 0 || (str = act->str) == NULL)
1292 return;
1293
1294 if (act->type == MenuTerminalAction)
1295 {
1296 fprintf (fp, "^@");
1297 /* 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 fprintf (fp, "M-"); /* meta prefix */
1317 str++;
1318 len--;
1319 break;
1320 }
1321 }
1322
1323 /*
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 char ch = *str++;
1332
1333 switch (ch)
1334 {
1335 case C0_ESC:
1336 fprintf (fp, "\\E");
1337 break; /* escape */
1338 case '\r':
1339 fprintf (fp, "\\r");
1340 break; /* carriage-return */
1341 case '\\':
1342 fprintf (fp, "\\\\");
1343 break; /* backslash */
1344 case '^':
1345 fprintf (fp, "\\^");
1346 break; /* caret */
1347 case 127:
1348 fprintf (fp, "^?");
1349 default:
1350 if (ch <= 31)
1351 fprintf (fp, "^%c", ('@' + ch));
1352 else if ((unsigned char)ch > 127)
1353 fprintf (fp, "\\%o", ch);
1354 else
1355 fprintf (fp, "%c", ch);
1356 break;
1357 }
1358
1359 len--;
1360 }
1361
1362 fprintf (fp, "\n");
1363 }
1364
1365 void
1366 rxvt_menu_dump (FILE *fp, menu_t *menu)
1367 {
1368 menuitem_t *item;
1369
1370 /* create a new menu and clear it */
1371 fprintf (fp, (menu->parent ? "./%s/*\n" : "/%s/*\n"), menu->name);
1372
1373 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 fprintf (fp, "> %s == NULL\n", item->name);
1380 else
1381 rxvt_menu_dump (fp, item->entry.submenu.menu);
1382 break;
1383
1384 case MenuLabel:
1385 fprintf (fp, "{%s}\n", (strlen (item->name) ? item->name : "-"));
1386 break;
1387
1388 case MenuTerminalAction:
1389 case MenuAction:
1390 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 break;
1396 }
1397 }
1398
1399 fprintf (fp, (menu->parent ? "../\n" : "/\n\n"));
1400 }
1401
1402 void
1403 rxvt_term::menubar_dump (FILE *fp)
1404 {
1405 bar_t *bar = CurrentBar;
1406 time_t t;
1407
1408 if (bar == NULL || fp == NULL)
1409 return;
1410 time (&t);
1411
1412 fprintf (fp,
1413 "# " RESCLASS " (%s) Pid: %u\n# Date: %s\n\n",
1414 rs[Rs_name], (unsigned int)getpid (), ctime (&t));
1415
1416 /* dump in reverse order */
1417 bar = CurrentBar->prev;
1418 do
1419 {
1420 menu_t *menu;
1421 int i;
1422
1423 fprintf (fp, "[menu:%s]\n", bar->name);
1424
1425 if (bar->title != NULL)
1426 fprintf (fp, "[title:%s]\n", bar->title);
1427
1428 for (i = 0; i < NARROWS; i++)
1429 {
1430 switch (bar->arrows[i].type)
1431 {
1432 case MenuTerminalAction:
1433 case MenuAction:
1434 fprintf (fp, "<%c>", Arrows[i].name);
1435 rxvt_action_decode (fp, & (bar->arrows[i]));
1436 break;
1437 }
1438 }
1439 fprintf (fp, "\n");
1440
1441 for (menu = bar->head; menu != NULL; menu = menu->next)
1442 rxvt_menu_dump (fp, menu);
1443
1444 fprintf (fp, "\n[done:%s]\n\n", bar->name);
1445 bar = bar->prev;
1446 }
1447 while (bar != CurrentBar->prev);
1448 }
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 rxvt_term::menubar_read (const char *filename)
1468 {
1469 /* read in a menu from a file */
1470 FILE *fp;
1471 char buffer[256];
1472 char *p, *file, *tag = NULL;
1473
1474 file = (char *)rxvt_File_find (filename, ".menu", rs[Rs_path]);
1475 if (file == NULL)
1476 return;
1477
1478 fp = fopen (file, "rb");
1479 free (file);
1480 if (fp == NULL)
1481 return;
1482
1483 #if (MENUBAR_MAX > 1)
1484 /* semi-colon delimited */
1485 if ((tag = strchr (filename, ';')) != NULL)
1486 {
1487 tag++;
1488 if (*tag == '\0')
1489 tag = NULL;
1490 }
1491 #endif /* (MENUBAR_MAX > 1) */
1492 #ifdef DEBUG_MENU
1493 fprintf (stderr, "[read:%s]\n", p);
1494 if (tag)
1495 fprintf (stderr, "looking for [menu:%s]\n", tag);
1496 #endif
1497
1498 while ((p = fgets (buffer, sizeof (buffer), fp)) != NULL)
1499 {
1500 int n;
1501
1502 if ((n = rxvt_Str_match (p, "[menu")) != 0)
1503 {
1504 if (tag)
1505 {
1506 /* looking for [menu:tag] */
1507 if (p[n] == ':' && p[n + 1] != ']')
1508 {
1509 n++;
1510 n += rxvt_Str_match (p + n, tag);
1511 if (p[n] == ']')
1512 {
1513 #ifdef DEBUG_MENU
1514 fprintf (stderr, "[menu:%s]\n", tag);
1515 #endif
1516 break;
1517 }
1518 }
1519 }
1520 else if (p[n] == ':' || p[n] == ']')
1521 break;
1522 }
1523 }
1524
1525 /* found [menu], [menu:???] tag */
1526 while (p != NULL)
1527 {
1528 int n;
1529
1530 #ifdef DEBUG_MENU
1531 fprintf (stderr, "read line = %s\n", p);
1532 #endif
1533
1534 /* looking for [done:tag] or [done:] */
1535 if ((n = rxvt_Str_match (p, "[done")) != 0)
1536 {
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 n += rxvt_Str_match (p + n, tag);
1553 if (p[n] == ']')
1554 {
1555 #ifdef DEBUG_MENU
1556 fprintf (stderr, "[done:%s]\n", tag);
1557 #endif
1558 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 /*
1571 * remove leading/trailing space
1572 * skip blank or comment lines
1573 */
1574 rxvt_Str_trim (p);
1575 if (*p && *p != '#')
1576 {
1577 menu_readonly = 0; /* if case we read another file */
1578 menubar_dispatch (p);
1579 }
1580 /* get another line */
1581 p = fgets (buffer, sizeof (buffer), fp);
1582 }
1583
1584 fclose (fp);
1585 }
1586
1587 /*
1588 * user interface for building/deleting and otherwise managing menus
1589 */
1590 void
1591 rxvt_term::menubar_dispatch (char *str)
1592 {
1593 int n, cmd;
1594 char *path, *name, *name2;
1595
1596 if (menubar_visible () && ActiveMenu != NULL)
1597 menubar_expose ();
1598 else
1599 ActiveMenu = NULL;
1600
1601 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 #if (MENUBAR_MAX > 1)
1618 if (CurrentBar == NULL)
1619 break;
1620 #endif /* (MENUBAR_MAX > 1) */
1621 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 if ((next = strchr (next, ':')) == NULL)
1636 return; /* parse error */
1637 }
1638 while (next[1] != ']');
1639 /* remove and skip ':]' */
1640 *next = '\0';
1641 next += 2;
1642 }
1643 else
1644 {
1645 if ((next = strchr (next, ']')) == NULL)
1646 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 else if (!strcmp (str, "clear"))
1664 {
1665 menubar_clear ();
1666 }
1667 else if (!strcmp (str, "done") || rxvt_Str_match (str, "done:"))
1668 {
1669 menu_readonly = 1;
1670 }
1671 else if (!strcmp (str, "show"))
1672 {
1673 map_menuBar (1);
1674 menu_readonly = 1;
1675 }
1676 else if (!strcmp (str, "hide"))
1677 {
1678 map_menuBar (0);
1679 menu_readonly = 1;
1680 }
1681 else if ((n = rxvt_Str_match (str, "read:")) != 0)
1682 {
1683 /* read in a menu from a file */
1684 str += n;
1685 menubar_read (str);
1686 }
1687 else if ((n = rxvt_Str_match (str, "title:")) != 0)
1688 {
1689 str += n;
1690 if (CurrentBar != NULL && !menu_readonly)
1691 {
1692 if (*str)
1693 {
1694 name = (char *)rxvt_realloc (CurrentBar->title, strlen (str) + 1);
1695 if (name != NULL)
1696 {
1697 strcpy (name, str);
1698 CurrentBar->title = name;
1699 }
1700 menubar_expose ();
1701 }
1702 else
1703 {
1704 free (CurrentBar->title);
1705 CurrentBar->title = NULL;
1706 }
1707 }
1708 }
1709 else if ((n = rxvt_Str_match (str, "pixmap:")) != 0)
1710 {
1711 str += n;
1712 process_xterm_seq (Rxvt_Pixmap, str, CHAR_ST);
1713 }
1714 #if (MENUBAR_MAX > 1)
1715 else if ((n = rxvt_Str_match (str, "rm")) != 0)
1716 {
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 else if ((n = rxvt_Str_match (str, "menu")) != 0)
1732 {
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 else if (!strcmp (str, "dump"))
1753 {
1754 /* dump current menubars to a file */
1755 FILE *fp;
1756
1757 /* enough space to hold the results */
1758 char buffer[32];
1759
1760 sprintf (buffer, "/tmp/" RESCLASS "-%u",
1761 (unsigned int)getpid ());
1762
1763 if ((fp = fopen (buffer, "wb")) != NULL)
1764 {
1765 process_xterm_seq (XTerm_title, buffer, CHAR_ST);
1766 menubar_dump (fp);
1767 fclose (fp);
1768 }
1769 }
1770 else if (!strcmp (str, "next"))
1771 {
1772 if (CurrentBar)
1773 {
1774 CurrentBar = CurrentBar->next;
1775 menu_readonly = 1;
1776 }
1777 }
1778 else if (!strcmp (str, "prev"))
1779 {
1780 if (CurrentBar)
1781 {
1782 CurrentBar = CurrentBar->prev;
1783 menu_readonly = 1;
1784 }
1785 }
1786 else if (!strcmp (str, "swap"))
1787 {
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 #endif /* (MENUBAR_MAX > 1) */
1808 str = next;
1809
1810 BuildMenu = ActiveMenu = NULL;
1811 menubar_expose ();
1812 #ifdef DEBUG_MENUBAR_STACKING
1813 fprintf (stderr, "menus are read%s\n",
1814 menu_readonly ? "only" : "/write");
1815 #endif
1816
1817 }
1818 return;
1819 break;
1820 }
1821
1822 #if (MENUBAR_MAX > 1)
1823 if (CurrentBar == NULL)
1824 return;
1825 if (menu_readonly)
1826 {
1827 #ifdef DEBUG_MENUBAR_STACKING
1828 fprintf (stderr, "menus are read%s\n",
1829 menu_readonly ? "only" : "/write");
1830 #endif
1831 return;
1832 }
1833 #endif /* (MENUBAR_MAX > 1) */
1834
1835 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 name = strchr (path, MENUITEM_BEG);
1846 str = strchr (path, MENUITEM_END);
1847 if (name != NULL || str != NULL)
1848 {
1849 if (name == NULL || str == NULL || str <= (name + 1)
1850 || (name > path && name[-1] != '/'))
1851 {
1852 rxvt_warn ("menu error A<%s>, continuing.\n", path);
1853 break;
1854 }
1855 if (str[1] == MENUITEM_BEG)
1856 {
1857 name2 = (str + 2);
1858 str = strchr (name2, MENUITEM_END);
1859
1860 if (str == NULL)
1861 {
1862 rxvt_warn ("menu error B<%s>, continuing.\n", path);
1863 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 while (isspace (*str))
1874 str++; /* skip space */
1875 }
1876 #ifdef DEBUG_MENU
1877 fprintf (stderr,
1878 "`%c' path = <%s>, name = <%s>, name2 = <%s>, action = <%s>\n",
1879 cmd, (path ? path : " (nil)"), (name ? name : " (nil)"),
1880 (name2 ? name2 : " (nil)"), (str ? str : " (nil)")
1881 );
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 path = menu_find_base (& (BuildMenu), path);
1894 len = strlen (path);
1895
1896 /* don't allow menus called `*' */
1897 if (path[0] == '*')
1898 {
1899 menu_clear (BuildMenu);
1900 break;
1901 }
1902 else if (len >= 2 && !strcmp ((path + len - 2), "/*"))
1903 {
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 rxvt_menuitem_add (BuildMenu,
1911 (strcmp (name, SEPARATOR_NAME) ? name : ""),
1912 name2, str);
1913 break;
1914
1915 case '-': /* delete menu entry */
1916 if (!strcmp (path, "/*") && (name == NULL || name[0] == '\0'))
1917 {
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 len = strlen (path);
1930
1931 /* submenu called `*' clears all menu items */
1932 if (path[0] == '*')
1933 {
1934 menu_clear (menu);
1935 break; /* done */
1936 }
1937 else if (len >= 2 && !strcmp (&path[len - 2], "/*"))
1938 {
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
1951 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 n1 = strcmp (name, SEPARATOR_NAME) ? name : "";
1961 item = rxvt_menuitem_find (BuildMenu, n1);
1962 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 max_it (BuildMenu->width, l);
1974 }
1975 }
1976 }
1977
1978 menubar_expose ();
1979 }
1980 break;
1981 }
1982 break;
1983 }
1984 }
1985
1986 void
1987 rxvt_term::draw_Arrows (int name, int state)
1988 {
1989 GC top, bot;
1990
1991 int i;
1992
1993 #ifdef MENU_SHADOW_IN
1994 state = -state;
1995 #endif
1996 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 const int w = Width2Pixel (1);
2017 const int y = (menuBar_TotalHeight () - w) / 2;
2018 int x = Arrows_x + (5 * Width2Pixel (i)) / 4;
2019
2020 if (!name || name == Arrows[i].name)
2021 rxvt_Draw_Triangle (display->display, menuBar.win, top, bot, x, y, w,
2022 Arrows[i].name);
2023 }
2024 XFlush (display->display);
2025 }
2026
2027 void
2028 rxvt_term::menubar_expose ()
2029 {
2030 menu_t *menu;
2031 int x;
2032
2033 if (!menubar_visible () || menuBar.win == 0)
2034 return;
2035
2036 if (menubarGC == None)
2037 {
2038 /* Create the graphics context */
2039 XGCValues gcvalue;
2040
2041 gcvalue.foreground = (display->depth <= 2 ? pix_colors[Color_fg]
2042 : pix_colors[Color_Black]);
2043 menubarGC = XCreateGC (display->display, menuBar.win,
2044 GCForeground, &gcvalue);
2045
2046 }
2047 /* make sure the font is correct */
2048 XClearWindow (display->display, menuBar.win);
2049
2050 menu_hide_all ();
2051
2052 x = 0;
2053 if (CurrentBar != NULL)
2054 {
2055 for (menu = CurrentBar->head; menu != NULL; menu = menu->next)
2056 {
2057 int len = menu->len;
2058
2059 x = (menu->x + menu->len + HSPACE);
2060
2061 #ifdef DEBUG_MENU_LAYOUT
2062 rxvt_print_menu_descendants (menu);
2063 #endif
2064
2065 if (x >= ncol)
2066 len = (ncol - (menu->x + HSPACE));
2067
2068 drawbox_menubar (menu->x, len, +1);
2069 draw_string (*menuBar.drawable, menubarGC, fontset[0],
2070 (Width2Pixel (menu->x) + Width2Pixel (HSPACE) / 2),
2071 MENU_SHADOW, menu->name, len);
2072
2073 if (x >= ncol)
2074 break;
2075 }
2076 }
2077 drawbox_menubar (x, ncol, (CurrentBar ? +1 : -1));
2078
2079 /* add the menuBar title, if it exists and there's plenty of room */
2080 Arrows_x = 0;
2081 if (x < ncol)
2082 {
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 Arrows_x = Width2Pixel (ncol);
2091 }
2092
2093 draw_Arrows (0, +1);
2094
2095 str = (CurrentBar
2096 && CurrentBar->title) ? CurrentBar->title : "%n-%v";
2097 for (len = 0; str[0] && len < sizeof (title) - 1; str++)
2098 {
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 while (*s && len < sizeof (title) - 1)
2119 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 draw_string (*menuBar.drawable, menubarGC, fontset[0],
2132 Width2Pixel (x) + Width2Pixel (ncol + HSPACE) / 2,
2133 MENU_SHADOW, title, len);
2134 }
2135 }
2136
2137 int
2138 rxvt_term::menubar_mapping (int map)
2139 {
2140 int change = 0;
2141
2142 if (map && !menubar_visible ())
2143 {
2144 menuBar.state = 1;
2145 if (menuBar.win == 0)
2146 return 0;
2147 XMapWindow (display->display, menuBar.win);
2148 change = 1;
2149 }
2150 else if (!map && menubar_visible ())
2151 {
2152 menubar_expose ();
2153 menuBar.state = 0;
2154 XUnmapWindow (display->display, menuBar.win);
2155 change = 1;
2156 }
2157 else
2158 menubar_expose ();
2159
2160 return change;
2161 }
2162
2163 int
2164 rxvt_term::menu_select (XButtonEvent &ev)
2165 {
2166 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
2173 if (ActiveMenu == NULL)
2174 return 0;
2175
2176 XQueryPointer (display->display, ActiveMenu->win,
2177 &unused_root, &unused_child,
2178 &unused_root_x, &unused_root_y,
2179 &ev.x, &ev.y, &unused_mask);
2180
2181 if (ActiveMenu->parent != NULL && (ev.x < 0 || ev.y < 0))
2182 {
2183 menu_hide ();
2184 return 1;
2185 }
2186
2187 /* determine the menu item corresponding to the Y index */
2188 y = MENU_SHADOW;
2189 if (ev.x >= 0 && ev.x <= (ActiveMenu->w - MENU_SHADOW))
2190 {
2191 for (item = ActiveMenu->head; item != NULL; item = item->next)
2192 {
2193 int h = HEIGHT_TEXT + 2 * MENU_SHADOW;
2194
2195 if (isSeparator (item->name))
2196 h = HEIGHT_SEPARATOR;
2197 else if (ev.y >= y && ev.y < (y + h))
2198 break;
2199
2200 y += h;
2201 }
2202 }
2203
2204 if (item == NULL && ev.type == ButtonRelease)
2205 {
2206 menu_hide_all ();
2207 return 0;
2208 }
2209
2210 thisitem = item;
2211 this_y = y - MENU_SHADOW;
2212
2213 /* erase the last item */
2214 if (ActiveMenu->item != NULL)
2215 {
2216 if (ActiveMenu->item != thisitem)
2217 {
2218 for (y = 0, item = ActiveMenu->head; item != NULL; item = item->next)
2219 {
2220 int h;
2221
2222 if (isSeparator (item->name))
2223 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
2231 break;
2232 }
2233 else
2234 h = HEIGHT_TEXT + 2 * MENU_SHADOW;
2235
2236 y += h;
2237 }
2238 }
2239 else
2240 {
2241 switch (ev.type)
2242 {
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 drawbox_menuitem (this_y, -1);
2254 rxvt_usleep (MENU_DELAY_USEC);
2255 /* remove menu before sending keys to the application */
2256 menu_hide_all ();
2257 #ifndef DEBUG_MENU
2258 action_dispatch (& (item->entry.action));
2259 #else /* DEBUG_MENU */
2260 fprintf (stderr, "%s: %s\n", item->name,
2261 item->entry.action.str);
2262 #endif /* DEBUG_MENU */
2263 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
2276 DoMenu:
2277 ActiveMenu->item = thisitem;
2278 y = this_y;
2279
2280 if (thisitem != NULL)
2281 {
2282 item = ActiveMenu->item;
2283 if (item->entry.type != MenuLabel)
2284 drawbox_menuitem (y, +1);
2285
2286 if (item->entry.type == MenuSubMenu)
2287 {
2288 int x;
2289
2290 drawtriangle (ActiveMenu->w, y, -1);
2291
2292 x = ev.x + (ActiveMenu->parent
2293 ? ActiveMenu->x
2294 : Width2Pixel (ActiveMenu->x));
2295
2296 if (x >= item->entry.submenu.menu->x)
2297 {
2298 ActiveMenu = item->entry.submenu.menu;
2299 menu_show ();
2300 return 1;
2301 }
2302 }
2303 }
2304 return 0;
2305 }
2306
2307 void
2308 rxvt_term::menubar_select (XButtonEvent &ev)
2309 {
2310 menu_t *menu = NULL;
2311
2312 /* determine the pulldown menu corresponding to the X index */
2313 if (ev.y >= 0 && ev.y <= menuBar_height () && CurrentBar != NULL)
2314 {
2315 for (menu = CurrentBar->head; menu != NULL; menu = menu->next)
2316 {
2317 int x = Width2Pixel (menu->x);
2318 int w = Width2Pixel (menu->len + HSPACE);
2319
2320 if ((ev.x >= x && ev.x < x + w))
2321 break;
2322 }
2323 }
2324 switch (ev.type)
2325 {
2326 case ButtonRelease:
2327 menu_hide_all ();
2328 break;
2329
2330 case ButtonPress:
2331 if (menu == NULL && Arrows_x && ev.x >= Arrows_x)
2332 {
2333 int i;
2334
2335 for (i = 0; i < NARROWS; i++)
2336 {
2337 if (ev.x >= (Arrows_x + (Width2Pixel (4 * i + i)) / 4)
2338 && ev.x < (Arrows_x
2339 + (Width2Pixel (4 * i + i + 4)) / 4))
2340 {
2341 draw_Arrows (Arrows[i].name, -1);
2342 rxvt_usleep (MENU_DELAY_USEC);
2343 draw_Arrows (Arrows[i].name, +1);
2344 #ifdef DEBUG_MENUARROWS
2345 fprintf (stderr, "'%c': ", Arrows[i].name);
2346
2347 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 fprintf (stderr, " (default) \\033%s\n",
2354 & (Arrows[i].str[2]));
2355 }
2356 else
2357 {
2358 fprintf (stderr, "%s\n",
2359 CurrentBar->arrows[i].str);
2360 }
2361 #else /* DEBUG_MENUARROWS */
2362 if (CurrentBar == NULL || action_dispatch (&CurrentBar->arrows[i]))
2363 {
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 #endif /* DEBUG_MENUARROWS */
2369 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 }
2387 }
2388
2389 /*
2390 * general dispatch routine,
2391 * it would be nice to have `sticky' menus
2392 */
2393 void
2394 rxvt_term::menubar_control (XButtonEvent &ev)
2395 {
2396 switch (ev.type)
2397 {
2398 case ButtonPress:
2399 if (ev.button == Button1)
2400 menubar_select (ev);
2401 break;
2402
2403 case ButtonRelease:
2404 if (ev.button == Button1)
2405 menu_select (ev);
2406 break;
2407
2408 case MotionNotify:
2409 while (XCheckTypedWindowEvent (display->display, parent[0],
2410 MotionNotify, (XEvent *)&ev));
2411
2412 if (ActiveMenu)
2413 while (menu_select (ev)) ;
2414 else
2415 ev.y = -1;
2416 if (ev.y < 0)
2417 {
2418 Window unused_root, unused_child;
2419 int unused_root_x, unused_root_y;
2420 unsigned int unused_mask;
2421
2422 XQueryPointer (display->display, menuBar.win,
2423 &unused_root, &unused_child,
2424 &unused_root_x, &unused_root_y,
2425 &ev.x, &ev.y, &unused_mask);
2426 menubar_select (ev);
2427 }
2428 break;
2429 }
2430 }
2431
2432 void
2433 rxvt_term::map_menuBar (int map)
2434 {
2435 if (menubar_mapping (map))
2436 resize_all_windows (0, 0, 0);
2437 }
2438 #endif
2439 /*----------------------- end-of-file (C source) -----------------------*/