ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/rxvt-unicode/src/menubar.C
Revision: 1.9
Committed: Tue Feb 24 00:02:44 2004 UTC (20 years, 3 months ago) by pcg
Content type: text/plain
Branch: MAIN
Changes since 1.8: +54 -99 lines
Log Message:
*** empty log message ***

File Contents

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