ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/rxvt-unicode/src/menubar.C
Revision: 1.6
Committed: Sun Feb 1 01:34:41 2004 UTC (20 years, 4 months ago) by pcg
Content type: text/plain
Branch: MAIN
CVS Tags: after_astyle
Changes since 1.5: +2020 -1757 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 = 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 = 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 = 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 = 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 = 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(Xdisplay, 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(Xdisplay, 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(Xdisplay, ActiveMenu->win, top, bot,
812 SHADOW + 0, SHADOW + y,
813 ActiveMenu->w - 2 * (SHADOW),
814 HEIGHT_TEXT + 2 * SHADOW);
815 XFlush(Xdisplay);
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(Xdisplay, TermWin.vt,
928 x, ActiveMenu->y,
929 ActiveMenu->w, ActiveMenu->h,
930 0,
931 PixColors[Color_fg],
932 PixColors[Color_scroll]);
933 XMapWindow(Xdisplay, ActiveMenu->win);
934 }
935 rxvt_Draw_Shadow(Xdisplay, 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(Xdisplay, 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 {
1009 #ifdef USE_XIM
1010 if (TermWin.fontset)
1011 XmbDrawString(Xdisplay,
1012 ActiveMenu->win, TermWin.fontset,
1013 gc, xoff,
1014 2 * SHADOW + y + TermWin.font->ascent + 1,
1015 name, len);
1016 else
1017 #endif
1018 XDrawString(Xdisplay, ActiveMenu->win, gc, xoff,
1019 2 * SHADOW + y + TermWin.font->ascent + 1,
1020 name, len);
1021 }
1022
1023 len = item->len2;
1024 name = item->name2;
1025 if (len && name)
1026 {
1027 #ifdef USE_XIM
1028 if (TermWin.fontset)
1029 XmbDrawString(Xdisplay,
1030 ActiveMenu->win, TermWin.fontset,
1031 gc,
1032 ActiveMenu->w - (xoff + Width2Pixel(xright)),
1033 2 * SHADOW + y + TermWin.font->ascent + 1,
1034 name, len);
1035 else
1036 #endif
1037 XDrawString(Xdisplay, ActiveMenu->win, gc,
1038 ActiveMenu->w - (xoff + Width2Pixel(xright)),
1039 2 * SHADOW + y + TermWin.font->ascent + 1,
1040 name, len);
1041 }
1042 h = HEIGHT_TEXT + 2 * SHADOW;
1043 }
1044 y += h;
1045 }
1046 }
1047
1048 void
1049 rxvt_term::menu_display (void (*update)(rxvt_t *))
1050 {
1051 menu_t *ActiveMenu = ActiveMenu;
1052
1053 if (ActiveMenu == NULL)
1054 return;
1055 if (ActiveMenu->win != None)
1056 XDestroyWindow(Xdisplay, ActiveMenu->win);
1057 ActiveMenu->win = None;
1058 ActiveMenu->item = NULL;
1059
1060 if (ActiveMenu->parent == NULL)
1061 drawbox_menubar (ActiveMenu->x, ActiveMenu->len, +1);
1062 ActiveMenu = ActiveMenu->parent;
1063 update(r);
1064 }
1065
1066 void
1067 rxvt_term::menu_hide_all ()
1068 {
1069 menu_display (rxvt_menu_hide_all);
1070 }
1071
1072 void
1073 rxvt_term::menu_hide ()
1074 {
1075 menu_display (rxvt_menu_show);
1076 }
1077
1078 void
1079 rxvt_term::menu_clear (menu_t *menu)
1080 {
1081 if (menu != NULL)
1082 {
1083 menuitem_t *item = menu->tail;
1084
1085 while (item != NULL)
1086 {
1087 menuitem_free (menu, item);
1088 /* it didn't get freed ... why? */
1089 if (item == menu->tail)
1090 return;
1091 item = menu->tail;
1092 }
1093 menu->width = 0;
1094 }
1095 }
1096
1097 void
1098 rxvt_term::menubar_clear ()
1099 {
1100 bar_t *CurrentBar = CurrentBar;
1101
1102 if (CurrentBar != NULL)
1103 {
1104 menu_t *menu = CurrentBar->tail;
1105
1106 while (menu != NULL)
1107 {
1108 menu_t *prev = menu->prev;
1109
1110 menu_delete (menu);
1111 menu = prev;
1112 }
1113 CurrentBar->head = CurrentBar->tail = NULL;
1114
1115 if (CurrentBar->title)
1116 {
1117 free(CurrentBar->title);
1118 CurrentBar->title = NULL;
1119 }
1120 menuarrow_free (0); /* remove all arrow functions */
1121 }
1122 ActiveMenu = NULL;
1123 }
1124
1125 #if (MENUBAR_MAX > 1)
1126 /* find if menu already exists */
1127 bar_t *
1128 rxvt_term::menubar_find (const char *name)
1129 {
1130 bar_t *bar = CurrentBar;
1131
1132 #ifdef DEBUG_MENUBAR_STACKING
1133 fprintf(stderr, "looking for [menu:%s] ...", name ? name : "(nil)");
1134 #endif
1135 if (bar == NULL || name == NULL)
1136 return NULL;
1137
1138 if (STRLEN(name) && STRCMP(name, "*"))
1139 {
1140 do
1141 {
1142 if (!STRCMP(bar->name, name))
1143 {
1144 #ifdef DEBUG_MENUBAR_STACKING
1145 fprintf(stderr, " found!\n");
1146 #endif
1147 return bar;
1148 }
1149 bar = bar->next;
1150 }
1151 while (bar != CurrentBar);
1152 bar = NULL;
1153 }
1154 #ifdef DEBUG_MENUBAR_STACKING
1155 fprintf(stderr, "%s found!\n", (bar ? "" : " NOT"));
1156 #endif
1157
1158 return bar;
1159 }
1160
1161 int
1162 rxvt_term::menubar_push (const char *name)
1163 {
1164 int ret = 1;
1165 bar_t *bar;
1166
1167 if (CurrentBar == NULL)
1168 {
1169 /* allocate first one */
1170 bar = (bar_t *) rxvt_malloc(sizeof(bar_t));
1171
1172 MEMSET(bar, 0, sizeof(bar_t));
1173 /* circular linked-list */
1174 bar->next = bar->prev = bar;
1175 bar->head = bar->tail = NULL;
1176 bar->title = NULL;
1177 CurrentBar = bar;
1178 Nbars++;
1179
1180 menubar_clear ();
1181 }
1182 else
1183 {
1184 /* find if menu already exists */
1185 bar = menubar_find (name);
1186 if (bar != NULL)
1187 {
1188 /* found it, use it */
1189 CurrentBar = bar;
1190 }
1191 else
1192 {
1193 /* create if needed, or reuse the existing empty menubar */
1194 if (CurrentBar->head != NULL)
1195 {
1196 /* need to malloc another one */
1197 if (Nbars < MENUBAR_MAX)
1198 bar = (bar_t *) rxvt_malloc(sizeof(bar_t));
1199 else
1200 bar = NULL;
1201
1202 /* malloc failed or too many menubars, reuse another */
1203 if (bar == NULL)
1204 {
1205 bar = CurrentBar->next;
1206 ret = -1;
1207 }
1208 else
1209 {
1210 bar->head = bar->tail = NULL;
1211 bar->title = NULL;
1212
1213 bar->next = CurrentBar->next;
1214 CurrentBar->next = bar;
1215 bar->prev = CurrentBar;
1216 bar->next->prev = bar;
1217
1218 Nbars++;
1219 }
1220 CurrentBar = bar;
1221
1222 }
1223 menubar_clear ();
1224 }
1225 }
1226
1227 /* give menubar this name */
1228 STRNCPY(CurrentBar->name, name, MAXNAME);
1229 CurrentBar->name[MAXNAME - 1] = '\0';
1230
1231 return ret;
1232 }
1233
1234 /* switch to a menu called NAME and remove it */
1235 void
1236 rxvt_term::menubar_remove (const char *name)
1237 {
1238 bar_t *bar;
1239
1240 if ((bar = menubar_find (name)) == NULL)
1241 return;
1242 CurrentBar = bar;
1243
1244 do
1245 {
1246 menubar_clear ();
1247 /*
1248 * pop a menubar, clean it up first
1249 */
1250 if (CurrentBar != NULL)
1251 {
1252 bar_t *prev = CurrentBar->prev;
1253 bar_t *next = CurrentBar->next;
1254
1255 if (prev == next && prev == CurrentBar)
1256 { /* only 1 left */
1257 prev = NULL;
1258 Nbars = 0; /* safety */
1259 }
1260 else
1261 {
1262 next->prev = prev;
1263 prev->next = next;
1264 Nbars--;
1265 }
1266
1267 free(CurrentBar);
1268 CurrentBar = prev;
1269 }
1270 }
1271 while (CurrentBar && !STRCMP(name, "*"));
1272 }
1273
1274 void
1275 rxvt_action_decode(FILE *fp, action_t *act)
1276 {
1277 unsigned char *str;
1278 short len;
1279
1280 if (act == NULL || (len = act->len) == 0 || (str = act->str) == NULL)
1281 return;
1282
1283 if (act->type == MenuTerminalAction)
1284 {
1285 fprintf(fp, "^@");
1286 /* can strip trailing ^G from XTerm sequence */
1287 if (str[0] == C0_ESC && str[1] == ']' && str[len - 1] == C0_BEL)
1288 len--;
1289 }
1290 else if (str[0] == C0_ESC)
1291 {
1292 switch (str[1])
1293 {
1294 case '[':
1295 case ']':
1296 break;
1297
1298 case 'x':
1299 /* can strip trailing '\r' from M-x sequence */
1300 if (str[len - 1] == '\r')
1301 len--;
1302 /* FALLTHROUGH */
1303
1304 default:
1305 fprintf(fp, "M-"); /* meta prefix */
1306 str++;
1307 len--;
1308 break;
1309 }
1310 }
1311 /*
1312 * control character form is preferred, since backslash-escaping
1313 * can be really ugly looking when the backslashes themselves also
1314 * have to be escaped to avoid Shell (or whatever scripting
1315 * language) interpretation
1316 */
1317 while (len > 0)
1318 {
1319 unsigned char ch = *str++;
1320
1321 switch (ch)
1322 {
1323 case C0_ESC:
1324 fprintf(fp, "\\E");
1325 break; /* escape */
1326 case '\r':
1327 fprintf(fp, "\\r");
1328 break; /* carriage-return */
1329 case '\\':
1330 fprintf(fp, "\\\\");
1331 break; /* backslash */
1332 case '^':
1333 fprintf(fp, "\\^");
1334 break; /* caret */
1335 case 127:
1336 fprintf(fp, "^?");
1337 default:
1338 if (ch <= 31)
1339 fprintf(fp, "^%c", ('@' + ch));
1340 else if (ch > 127)
1341 fprintf(fp, "\\%o", ch);
1342 else
1343 fprintf(fp, "%c", ch);
1344 break;
1345 }
1346 len--;
1347 }
1348 fprintf(fp, "\n");
1349 }
1350
1351 void
1352 rxvt_menu_dump(FILE *fp, menu_t *menu)
1353 {
1354 menuitem_t *item;
1355
1356 /* create a new menu and clear it */
1357 fprintf(fp, (menu->parent ? "./%s/*\n" : "/%s/*\n"), menu->name);
1358
1359 for (item = menu->head; item != NULL; item = item->next)
1360 {
1361 switch (item->entry.type)
1362 {
1363 case MenuSubMenu:
1364 if (item->entry.submenu.menu == NULL)
1365 fprintf(fp, "> %s == NULL\n", item->name);
1366 else
1367 rxvt_menu_dump(fp, item->entry.submenu.menu);
1368 break;
1369
1370 case MenuLabel:
1371 fprintf(fp, "{%s}\n", (STRLEN(item->name) ? item->name : "-"));
1372 break;
1373
1374 case MenuTerminalAction:
1375 case MenuAction:
1376 fprintf(fp, "{%s}", item->name);
1377 if (item->name2 != NULL && STRLEN(item->name2))
1378 fprintf(fp, "{%s}", item->name2);
1379 fprintf(fp, "\t");
1380 rxvt_action_decode(fp, &(item->entry.action));
1381 break;
1382 }
1383 }
1384
1385 fprintf(fp, (menu->parent ? "../\n" : "/\n\n"));
1386 }
1387
1388 void
1389 rxvt_term::menubar_dump (FILE *fp)
1390 {
1391 bar_t *bar = CurrentBar;
1392 time_t t;
1393
1394 if (bar == NULL || fp == NULL)
1395 return;
1396 time(&t);
1397
1398 fprintf(fp,
1399 "# " APL_SUBCLASS " (%s) Pid: %u\n# Date: %s\n\n",
1400 rs[Rs_name], (unsigned int)getpid(), ctime(&t));
1401
1402 /* dump in reverse order */
1403 bar = CurrentBar->prev;
1404 do
1405 {
1406 menu_t *menu;
1407 int i;
1408
1409 fprintf(fp, "[menu:%s]\n", bar->name);
1410
1411 if (bar->title != NULL)
1412 fprintf(fp, "[title:%s]\n", bar->title);
1413
1414 for (i = 0; i < NARROWS; i++)
1415 {
1416 switch (bar->arrows[i].type)
1417 {
1418 case MenuTerminalAction:
1419 case MenuAction:
1420 fprintf(fp, "<%c>", Arrows[i].name);
1421 rxvt_action_decode(fp, &(bar->arrows[i]));
1422 break;
1423 }
1424 }
1425 fprintf(fp, "\n");
1426
1427 for (menu = bar->head; menu != NULL; menu = menu->next)
1428 rxvt_menu_dump(fp, menu);
1429
1430 fprintf(fp, "\n[done:%s]\n\n", bar->name);
1431 bar = bar->prev;
1432 }
1433 while (bar != CurrentBar->prev);
1434 }
1435 #endif /* (MENUBAR_MAX > 1) */
1436
1437 /*
1438 * read in menubar commands from FILENAME
1439 * ignore all input before the tag line [menu] or [menu:???]
1440 *
1441 * Note that since File_find () is used, FILENAME can be semi-colon
1442 * delimited such that the second part can refer to a tag
1443 * so that a large `database' of menus can be collected together
1444 *
1445 * FILENAME = "file"
1446 * FILENAME = "file;"
1447 * read `file' starting with first [menu] or [menu:???] line
1448 *
1449 * FILENAME = "file;tag"
1450 * read `file' starting with [menu:tag]
1451 */
1452 void
1453 rxvt_term::menubar_read (const char *filename)
1454 {
1455 /* read in a menu from a file */
1456 FILE *fp;
1457 char buffer[256];
1458 char *p, *file, *tag = NULL;
1459
1460 file = (char *)rxvt_File_find(filename, ".menu", rs[Rs_path]);
1461 if (file == NULL)
1462 return;
1463 fp = fopen(file, "rb");
1464 free(file);
1465 if (fp == NULL)
1466 return;
1467
1468 #if (MENUBAR_MAX > 1)
1469 /* semi-colon delimited */
1470 if ((tag = STRCHR(filename, ';')) != NULL)
1471 {
1472 tag++;
1473 if (*tag == '\0')
1474 tag = NULL;
1475 }
1476 #endif /* (MENUBAR_MAX > 1) */
1477 #ifdef DEBUG_MENU
1478 fprintf(stderr, "[read:%s]\n", p);
1479 if (tag)
1480 fprintf(stderr, "looking for [menu:%s]\n", tag);
1481 #endif
1482
1483 while ((p = fgets(buffer, sizeof(buffer), fp)) != NULL)
1484 {
1485 int n;
1486
1487 if ((n = rxvt_Str_match(p, "[menu")) != 0)
1488 {
1489 if (tag)
1490 {
1491 /* looking for [menu:tag] */
1492 if (p[n] == ':' && p[n + 1] != ']')
1493 {
1494 n++;
1495 n += rxvt_Str_match(p + n, tag);
1496 if (p[n] == ']')
1497 {
1498 #ifdef DEBUG_MENU
1499 fprintf(stderr, "[menu:%s]\n", tag);
1500 #endif
1501 break;
1502 }
1503 }
1504 }
1505 else if (p[n] == ':' || p[n] == ']')
1506 break;
1507 }
1508 }
1509
1510 /* found [menu], [menu:???] tag */
1511 while (p != NULL)
1512 {
1513 int n;
1514
1515 #ifdef DEBUG_MENU
1516 fprintf(stderr, "read line = %s\n", p);
1517 #endif
1518
1519 /* looking for [done:tag] or [done:] */
1520 if ((n = rxvt_Str_match(p, "[done")) != 0)
1521 {
1522 if (p[n] == ']')
1523 {
1524 menu_readonly = 1;
1525 break;
1526 }
1527 else if (p[n] == ':')
1528 {
1529 n++;
1530 if (p[n] == ']')
1531 {
1532 menu_readonly = 1;
1533 break;
1534 }
1535 else if (tag)
1536 {
1537 n += rxvt_Str_match(p + n, tag);
1538 if (p[n] == ']')
1539 {
1540 #ifdef DEBUG_MENU
1541 fprintf(stderr, "[done:%s]\n", tag);
1542 #endif
1543 menu_readonly = 1;
1544 break;
1545 }
1546 }
1547 else
1548 {
1549 /* what? ... skip this line */
1550 p[0] = COMMENT_CHAR;
1551 }
1552 }
1553 }
1554 /*
1555 * remove leading/trailing space
1556 * and strip-off leading/trailing quotes
1557 * skip blank or comment lines
1558 */
1559 rxvt_Str_trim(p);
1560 if (*p && *p != '#')
1561 {
1562 menu_readonly = 0; /* if case we read another file */
1563 menubar_dispatch (p);
1564 }
1565 /* get another line */
1566 p = fgets(buffer, sizeof(buffer), fp);
1567 }
1568
1569 fclose(fp);
1570 }
1571
1572 /*
1573 * user interface for building/deleting and otherwise managing menus
1574 */
1575 void
1576 rxvt_term::menubar_dispatch (char *str)
1577 {
1578 int n, cmd;
1579 char *path, *name, *name2;
1580
1581 if (menubar_visible(r) && ActiveMenu != NULL)
1582 menubar_expose ();
1583 else
1584 ActiveMenu = NULL;
1585
1586 cmd = *str;
1587 switch (cmd)
1588 {
1589 case '.':
1590 case '/': /* absolute & relative path */
1591 case MENUITEM_BEG: /* menuitem */
1592 /* add `+' prefix for these cases */
1593 cmd = '+';
1594 break;
1595
1596 case '+':
1597 case '-':
1598 str++; /* skip cmd character */
1599 break;
1600
1601 case '<':
1602 #if (MENUBAR_MAX > 1)
1603 if (CurrentBar == NULL)
1604 break;
1605 #endif /* (MENUBAR_MAX > 1) */
1606 if (str[1] && str[2] == '>') /* arrow commands */
1607 menuarrow_add (str);
1608 break;
1609
1610 case '[': /* extended command */
1611 while (str[0] == '[')
1612 {
1613 char *next = (++str); /* skip leading '[' */
1614
1615 if (str[0] == ':')
1616 { /* [:command:] */
1617 do
1618 {
1619 next++;
1620 if ((next = STRCHR(next, ':')) == NULL)
1621 return; /* parse error */
1622 }
1623 while (next[1] != ']');
1624 /* remove and skip ':]' */
1625 *next = '\0';
1626 next += 2;
1627 }
1628 else
1629 {
1630 if ((next = STRCHR(next, ']')) == NULL)
1631 return; /* parse error */
1632 /* remove and skip ']' */
1633 *next = '\0';
1634 next++;
1635 }
1636
1637 if (str[0] == ':')
1638 {
1639 int saved;
1640
1641 /* try and dispatch it, regardless of read/write status */
1642 saved = menu_readonly;
1643 menu_readonly = 0;
1644 menubar_dispatch (str + 1);
1645 menu_readonly = saved;
1646 }
1647 /* these ones don't require menu stacking */
1648 else if (!STRCMP(str, "clear"))
1649 {
1650 menubar_clear ();
1651 }
1652 else if (!STRCMP(str, "done") || rxvt_Str_match(str, "done:"))
1653 {
1654 menu_readonly = 1;
1655 }
1656 else if (!STRCMP(str, "show"))
1657 {
1658 map_menuBar (1);
1659 menu_readonly = 1;
1660 }
1661 else if (!STRCMP(str, "hide"))
1662 {
1663 map_menuBar (0);
1664 menu_readonly = 1;
1665 }
1666 else if ((n = rxvt_Str_match(str, "read:")) != 0)
1667 {
1668 /* read in a menu from a file */
1669 str += n;
1670 menubar_read (str);
1671 }
1672 else if ((n = rxvt_Str_match(str, "title:")) != 0)
1673 {
1674 str += n;
1675 if (CurrentBar != NULL && !menu_readonly)
1676 {
1677 if (*str)
1678 {
1679 name = rxvt_realloc(CurrentBar->title,
1680 STRLEN(str) + 1);
1681 if (name != NULL)
1682 {
1683 STRCPY(name, str);
1684 CurrentBar->title = name;
1685 }
1686 menubar_expose ();
1687 }
1688 else
1689 {
1690 free(CurrentBar->title);
1691 CurrentBar->title = NULL;
1692 }
1693 }
1694 }
1695 else if ((n = rxvt_Str_match(str, "pixmap:")) != 0)
1696 {
1697 str += n;
1698 xterm_seq (XTerm_Pixmap, str, CHAR_ST);
1699 }
1700 #if (MENUBAR_MAX > 1)
1701 else if ((n = rxvt_Str_match(str, "rm")) != 0)
1702 {
1703 str += n;
1704 switch (str[0])
1705 {
1706 case ':':
1707 str++;
1708 /* FALLTHROUGH */
1709 case '\0':
1710 /* FALLTHROUGH */
1711 case '*':
1712 menubar_remove (str);
1713 break;
1714 }
1715 menu_readonly = 1;
1716 }
1717 else if ((n = rxvt_Str_match(str, "menu")) != 0)
1718 {
1719 str += n;
1720 switch (str[0])
1721 {
1722 case ':':
1723 str++;
1724 /* add/access menuBar */
1725 if (*str != '\0' && *str != '*')
1726 menubar_push (str);
1727 break;
1728 default:
1729 if (CurrentBar == NULL)
1730 {
1731 menubar_push ("default");
1732 }
1733 }
1734
1735 if (CurrentBar != NULL)
1736 menu_readonly = 0; /* allow menu build commands */
1737 }
1738 else if (!STRCMP(str, "dump"))
1739 {
1740 /* dump current menubars to a file */
1741 FILE *fp;
1742
1743 /* enough space to hold the results */
1744 char buffer[32];
1745
1746 sprintf(buffer, "/tmp/" APL_SUBCLASS "-%u",
1747 (unsigned int)getpid());
1748
1749 if ((fp = fopen(buffer, "wb")) != NULL)
1750 {
1751 xterm_seq (XTerm_title, buffer, CHAR_ST);
1752 menubar_dump (fp);
1753 fclose(fp);
1754 }
1755 }
1756 else if (!STRCMP(str, "next"))
1757 {
1758 if (CurrentBar)
1759 {
1760 CurrentBar = CurrentBar->next;
1761 menu_readonly = 1;
1762 }
1763 }
1764 else if (!STRCMP(str, "prev"))
1765 {
1766 if (CurrentBar)
1767 {
1768 CurrentBar = CurrentBar->prev;
1769 menu_readonly = 1;
1770 }
1771 }
1772 else if (!STRCMP(str, "swap"))
1773 {
1774 /* swap the top 2 menus */
1775 if (CurrentBar)
1776 {
1777 bar_t *cbprev = CurrentBar->prev;
1778 bar_t *cbnext = CurrentBar->next;
1779
1780 cbprev->next = cbnext;
1781 cbnext->prev = cbprev;
1782
1783 CurrentBar->next = cbprev;
1784 CurrentBar->prev = cbprev->prev;
1785
1786 cbprev->prev->next = CurrentBar;
1787 cbprev->prev = CurrentBar;
1788
1789 CurrentBar = cbprev;
1790 menu_readonly = 1;
1791 }
1792 }
1793 #endif /* (MENUBAR_MAX > 1) */
1794 str = next;
1795
1796 BuildMenu = ActiveMenu = NULL;
1797 menubar_expose ();
1798 #ifdef DEBUG_MENUBAR_STACKING
1799 fprintf(stderr, "menus are read%s\n",
1800 menu_readonly ? "only" : "/write");
1801 #endif
1802
1803 }
1804 return;
1805 break;
1806 }
1807
1808 #if (MENUBAR_MAX > 1)
1809 if (CurrentBar == NULL)
1810 return;
1811 if (menu_readonly)
1812 {
1813 #ifdef DEBUG_MENUBAR_STACKING
1814 fprintf(stderr, "menus are read%s\n",
1815 menu_readonly ? "only" : "/write");
1816 #endif
1817 return;
1818 }
1819 #endif /* (MENUBAR_MAX > 1) */
1820
1821 switch (cmd)
1822 {
1823 case '+':
1824 case '-':
1825 path = name = str;
1826
1827 name2 = NULL;
1828 /* parse STR, allow spaces inside (name) */
1829 if (path[0] != '\0')
1830 {
1831 name = STRCHR(path, MENUITEM_BEG);
1832 str = STRCHR(path, MENUITEM_END);
1833 if (name != NULL || str != NULL)
1834 {
1835 if (name == NULL || str == NULL || str <= (name + 1)
1836 || (name > path && name[-1] != '/'))
1837 {
1838 rxvt_print_error("menu error <%s>\n", path);
1839 break;
1840 }
1841 if (str[1] == MENUITEM_BEG)
1842 {
1843 name2 = (str + 2);
1844 str = STRCHR(name2, MENUITEM_END);
1845
1846 if (str == NULL)
1847 {
1848 rxvt_print_error("menu error <%s>\n", path);
1849 break;
1850 }
1851 name2[-2] = '\0'; /* remove prev MENUITEM_END */
1852 }
1853 if (name > path && name[-1] == '/')
1854 name[-1] = '\0';
1855
1856 *name++ = '\0'; /* delimit */
1857 *str++ = '\0'; /* delimit */
1858
1859 while (isspace(*str))
1860 str++; /* skip space */
1861 }
1862 #ifdef DEBUG_MENU
1863 fprintf(stderr,
1864 "`%c' path = <%s>, name = <%s>, name2 = <%s>, action = <%s>\n",
1865 cmd, (path ? path : "(nil)"), (name ? name : "(nil)"),
1866 (name2 ? name2 : "(nil)"), (str ? str : "(nil)")
1867 );
1868 #endif
1869
1870 }
1871 /* process the different commands */
1872 switch (cmd)
1873 {
1874 case '+': /* add/replace existing menu or menuitem */
1875 if (path[0] != '\0')
1876 {
1877 int len;
1878
1879 path = menu_find_base (&(BuildMenu), path);
1880 len = STRLEN(path);
1881
1882 /* don't allow menus called `*' */
1883 if (path[0] == '*')
1884 {
1885 menu_clear (BuildMenu);
1886 break;
1887 }
1888 else if (len >= 2 && !STRCMP((path + len - 2), "/*"))
1889 {
1890 path[len - 2] = '\0';
1891 }
1892 if (path[0] != '\0')
1893 BuildMenu = menu_add (BuildMenu, path);
1894 }
1895 if (name != NULL && name[0] != '\0')
1896 rxvt_menuitem_add(BuildMenu,
1897 (STRCMP(name, SEPARATOR_NAME) ? name : ""),
1898 name2, str);
1899 break;
1900
1901 case '-': /* delete menu entry */
1902 if (!STRCMP(path, "/*") && (name == NULL || name[0] == '\0'))
1903 {
1904 menubar_clear ();
1905 BuildMenu = NULL;
1906 menubar_expose ();
1907 break;
1908 }
1909 else if (path[0] != '\0')
1910 {
1911 int len;
1912 menu_t *menu = BuildMenu;
1913
1914 path = menu_find_base (&menu, path);
1915 len = STRLEN(path);
1916
1917 /* submenu called `*' clears all menu items */
1918 if (path[0] == '*')
1919 {
1920 menu_clear (menu);
1921 break; /* done */
1922 }
1923 else if (len >= 2 && !STRCMP(&path[len - 2], "/*"))
1924 {
1925 /* done */
1926 break;
1927 }
1928 else if (path[0] != '\0')
1929 {
1930 BuildMenu = NULL;
1931 break;
1932 }
1933 else
1934 BuildMenu = menu;
1935 }
1936 if (BuildMenu != NULL)
1937 {
1938 if (name == NULL || name[0] == '\0')
1939 BuildMenu = menu_delete (BuildMenu);
1940 else
1941 {
1942 const char *n1;
1943 menuitem_t *item;
1944 menu_t *BuildMenu = BuildMenu;
1945
1946 n1 = STRCMP(name, SEPARATOR_NAME) ? name : "";
1947 item = rxvt_menuitem_find(BuildMenu, n1);
1948 if (item != NULL && item->entry.type != MenuSubMenu)
1949 {
1950 menuitem_free (BuildMenu, item);
1951
1952 /* fix up the width */
1953 BuildMenu->width = 0;
1954 for (item = BuildMenu->head; item != NULL;
1955 item = item->next)
1956 {
1957 short l = item->len + item->len2;
1958
1959 MAX_IT(BuildMenu->width, l);
1960 }
1961 }
1962 }
1963 menubar_expose ();
1964 }
1965 break;
1966 }
1967 break;
1968 }
1969 }
1970
1971 void
1972 rxvt_term::draw_Arrows (int name, int state)
1973 {
1974 GC top, bot;
1975
1976 int i;
1977
1978 #ifdef MENU_SHADOW_IN
1979 state = -state;
1980 #endif
1981 switch (state)
1982 {
1983 case +1:
1984 top = topShadowGC;
1985 bot = botShadowGC;
1986 break; /* SHADOW_OUT */
1987 case -1:
1988 top = botShadowGC;
1989 bot = topShadowGC;
1990 break; /* SHADOW_IN */
1991 default:
1992 top = bot = scrollbarGC;
1993 break; /* neutral */
1994 }
1995
1996 if (!Arrows_x)
1997 return;
1998
1999 for (i = 0; i < NARROWS; i++)
2000 {
2001 const int w = Width2Pixel(1);
2002 const int y = (menuBar_TotalHeight() - w) / 2;
2003 int x = Arrows_x + (5 * Width2Pixel(i)) / 4;
2004
2005 if (!name || name == Arrows[i].name)
2006 rxvt_Draw_Triangle(Xdisplay, menuBar.win, top, bot, x, y, w,
2007 Arrows[i].name);
2008 }
2009 XFlush(Xdisplay);
2010 }
2011
2012 void
2013 rxvt_term::menubar_expose ()
2014 {
2015 menu_t *menu;
2016 int x;
2017
2018 if (!menubar_visible(r) || menuBar.win == 0)
2019 return;
2020
2021 if (menubarGC == None)
2022 {
2023 /* Create the graphics context */
2024 XGCValues gcvalue;
2025
2026 gcvalue.font = TermWin.font->fid;
2027
2028 gcvalue.foreground = (XDEPTH <= 2 ? PixColors[Color_fg]
2029 : PixColors[Color_Black]);
2030 menubarGC = XCreateGC(Xdisplay, menuBar.win,
2031 GCForeground | GCFont, &gcvalue);
2032
2033 }
2034 /* make sure the font is correct */
2035 XSetFont(Xdisplay, menubarGC, TermWin.font->fid);
2036 XSetFont(Xdisplay, botShadowGC, TermWin.font->fid);
2037 XClearWindow(Xdisplay, menuBar.win);
2038
2039 menu_hide_all ();
2040
2041 x = 0;
2042 if (CurrentBar != NULL)
2043 {
2044 for (menu = CurrentBar->head; menu != NULL; menu = menu->next)
2045 {
2046 int len = menu->len;
2047
2048 x = (menu->x + menu->len + HSPACE);
2049
2050 #ifdef DEBUG_MENU_LAYOUT
2051 rxvt_print_menu_descendants(menu);
2052 #endif
2053
2054 if (x >= TermWin.ncol)
2055 len = (TermWin.ncol - (menu->x + HSPACE));
2056
2057 drawbox_menubar (menu->x, len, +1);
2058 #ifdef USE_XIM
2059 if (TermWin.fontset)
2060 XmbDrawString(Xdisplay,
2061 menuBar.win, TermWin.fontset,
2062 menubarGC,
2063 (Width2Pixel(menu->x) + Width2Pixel(HSPACE) / 2),
2064 menuBar_height() - SHADOW, menu->name, len);
2065 else
2066 #endif
2067 XDrawString(Xdisplay, menuBar.win, menubarGC,
2068 (Width2Pixel(menu->x) + Width2Pixel(HSPACE) / 2),
2069 menuBar_height() - SHADOW, menu->name, len);
2070
2071 if (x >= TermWin.ncol)
2072 break;
2073 }
2074 }
2075 drawbox_menubar (x, TermWin.ncol, (CurrentBar ? +1 : -1));
2076
2077 /* add the menuBar title, if it exists and there's plenty of room */
2078 Arrows_x = 0;
2079 if (x < TermWin.ncol)
2080 {
2081 const char *str;
2082 int ncol;
2083 unsigned int len;
2084 char title[256];
2085
2086 ncol = (int)TermWin.ncol;
2087 if (x < (ncol - (NARROWS + 1)))
2088 {
2089 ncol -= (NARROWS + 1);
2090 Arrows_x = Width2Pixel(ncol);
2091 }
2092 draw_Arrows (0, +1);
2093
2094 str = (CurrentBar
2095 && CurrentBar->title) ? CurrentBar->title : "%n-%v";
2096 for (len = 0; str[0] && len < sizeof(title) - 1; str++)
2097 {
2098 const char *s = NULL;
2099
2100 switch (str[0])
2101 {
2102 case '%':
2103 str++;
2104 switch (str[0])
2105 {
2106 case 'n':
2107 s = rs[Rs_name];
2108 break; /* resource name */
2109 case 'v':
2110 s = VERSION;
2111 break; /* version number */
2112 case '%':
2113 s = "%";
2114 break; /* literal '%' */
2115 }
2116 if (s != NULL)
2117 while (*s && len < sizeof(title) - 1)
2118 title[len++] = *s++;
2119 break;
2120
2121 default:
2122 title[len++] = str[0];
2123 break;
2124 }
2125 }
2126 title[len] = '\0';
2127
2128 ncol -= (x + len + HSPACE);
2129 if (len > 0 && ncol >= 0)
2130 {
2131 #ifdef USE_XIM
2132 if (TermWin.fontset)
2133 XmbDrawString(Xdisplay,
2134 menuBar.win, TermWin.fontset,
2135 menubarGC,
2136 Width2Pixel(x) + Width2Pixel(ncol + HSPACE) / 2,
2137 menuBar_height() - SHADOW, title, len);
2138 else
2139 #endif
2140 XDrawString(Xdisplay, menuBar.win, menubarGC,
2141 Width2Pixel(x) + Width2Pixel(ncol + HSPACE) / 2,
2142 menuBar_height() - SHADOW, title, len);
2143 }
2144 }
2145 }
2146
2147 int
2148 rxvt_term::menubar_mapping (int map)
2149 {
2150 int change = 0;
2151
2152 if (map && !menubar_visible(r))
2153 {
2154 menuBar.state = 1;
2155 if (menuBar.win == 0)
2156 return 0;
2157 XMapWindow(Xdisplay, menuBar.win);
2158 change = 1;
2159 }
2160 else if (!map && menubar_visible(r))
2161 {
2162 menubar_expose ();
2163 menuBar.state = 0;
2164 XUnmapWindow(Xdisplay, menuBar.win);
2165 change = 1;
2166 }
2167 else
2168 menubar_expose ();
2169
2170 return change;
2171 }
2172
2173 int
2174 rxvt_term::menu_select (XButtonEvent *ev)
2175 {
2176 menuitem_t *thisitem, *item = NULL;
2177 int this_y, y;
2178 menu_t *ActiveMenu = ActiveMenu;
2179
2180 Window unused_root, unused_child;
2181 int unused_root_x, unused_root_y;
2182 unsigned int unused_mask;
2183
2184 if (ActiveMenu == NULL)
2185 return 0;
2186
2187 XQueryPointer(Xdisplay, ActiveMenu->win,
2188 &unused_root, &unused_child,
2189 &unused_root_x, &unused_root_y,
2190 &(ev->x), &(ev->y), &unused_mask);
2191
2192 if (ActiveMenu->parent != NULL && (ev->x < 0 || ev->y < 0))
2193 {
2194 menu_hide ();
2195 return 1;
2196 }
2197 /* determine the menu item corresponding to the Y index */
2198 y = SHADOW;
2199 if (ev->x >= 0 && ev->x <= (ActiveMenu->w - SHADOW))
2200 {
2201 for (item = ActiveMenu->head; item != NULL; item = item->next)
2202 {
2203 int h = HEIGHT_TEXT + 2 * SHADOW;
2204
2205 if (isSeparator(item->name))
2206 h = HEIGHT_SEPARATOR;
2207 else if (ev->y >= y && ev->y < (y + h))
2208 break;
2209 y += h;
2210 }
2211 }
2212 if (item == NULL && ev->type == ButtonRelease)
2213 {
2214 menu_hide_all ();
2215 return 0;
2216 }
2217 thisitem = item;
2218 this_y = y - SHADOW;
2219
2220 /* erase the last item */
2221 if (ActiveMenu->item != NULL)
2222 {
2223 if (ActiveMenu->item != thisitem)
2224 {
2225 for (y = 0, item = ActiveMenu->head; item != NULL;
2226 item = item->next)
2227 {
2228 int h;
2229
2230 if (isSeparator(item->name))
2231 h = HEIGHT_SEPARATOR;
2232 else if (item == ActiveMenu->item)
2233 {
2234 /* erase old menuitem */
2235 drawbox_menuitem (y, 0); /* No Shadow */
2236 if (item->entry.type == MenuSubMenu)
2237 drawtriangle (ActiveMenu->w, y, +1);
2238 break;
2239 }
2240 else
2241 h = HEIGHT_TEXT + 2 * SHADOW;
2242 y += h;
2243 }
2244 }
2245 else
2246 {
2247 switch (ev->type)
2248 {
2249 case ButtonRelease:
2250 switch (item->entry.type)
2251 {
2252 case MenuLabel:
2253 case MenuSubMenu:
2254 menu_hide_all ();
2255 break;
2256
2257 case MenuAction:
2258 case MenuTerminalAction:
2259 drawbox_menuitem (_y, -1);
2260 {
2261 #ifdef HAVE_NANOSLEEP
2262 struct timespec rqt;
2263
2264 rqt.tv_sec = 0;
2265 rqt.tv_nsec = MENU_DELAY_USEC * 1000;
2266 nanosleep(&rqt, NULL);
2267 #else
2268 /* use select for timing */
2269 struct timeval tv;
2270
2271 tv.tv_sec = 0;
2272 tv.tv_usec = MENU_DELAY_USEC;
2273 select(0, NULL, NULL, NULL, &tv);
2274 #endif
2275
2276 }
2277 /* remove menu before sending keys to the application */
2278 menu_hide_all ();
2279 #ifndef DEBUG_MENU
2280 action_dispatch (&(item->entry.action));
2281 #else /* DEBUG_MENU */
2282 fprintf(stderr, "%s: %s\n", item->name,
2283 item->entry.action.str);
2284 #endif /* DEBUG_MENU */
2285 break;
2286 }
2287 break;
2288
2289 default:
2290 if (item->entry.type == MenuSubMenu)
2291 goto DoMenu;
2292 break;
2293 }
2294 return 0;
2295 }
2296 }
2297 DoMenu:
2298 ActiveMenu->item = thisitem;
2299 y = this_y;
2300 if (item != NULL)
2301 {
2302 item = ActiveMenu->item;
2303 if (item->entry.type != MenuLabel)
2304 drawbox_menuitem (y, +1);
2305 if (item->entry.type == MenuSubMenu)
2306 {
2307 int x;
2308
2309 drawtriangle (ActiveMenu->w, y, -1);
2310
2311 x = ev->x + (ActiveMenu->parent
2312 ? ActiveMenu->x
2313 : Width2Pixel(ActiveMenu->x));
2314
2315 if (x >= item->entry.submenu.menu->x)
2316 {
2317 ActiveMenu = item->entry.submenu.menu;
2318 menu_show ();
2319 return 1;
2320 }
2321 }
2322 }
2323 return 0;
2324 }
2325
2326 void
2327 rxvt_term::menubar_select (XButtonEvent *ev)
2328 {
2329 menu_t *menu = NULL;
2330
2331 /* determine the pulldown menu corresponding to the X index */
2332 if (ev->y >= 0 && ev->y <= menuBar_height() && CurrentBar != NULL)
2333 {
2334 for (menu = CurrentBar->head; menu != NULL; menu = menu->next)
2335 {
2336 int x = Width2Pixel(menu->x);
2337 int w = Width2Pixel(menu->len + HSPACE);
2338
2339 if ((ev->x >= x && ev->x < x + w))
2340 break;
2341 }
2342 }
2343 switch (ev->type)
2344 {
2345 case ButtonRelease:
2346 menu_hide_all ();
2347 break;
2348
2349 case ButtonPress:
2350 if (menu == NULL && Arrows_x && ev->x >= Arrows_x)
2351 {
2352 int i;
2353
2354 for (i = 0; i < NARROWS; i++)
2355 {
2356 if (ev->x >= (Arrows_x + (Width2Pixel(4 * i + i)) / 4)
2357 && ev->x < (Arrows_x
2358 + (Width2Pixel(4 * i + i + 4)) / 4))
2359 {
2360 draw_Arrows (Arrows[i].name, -1);
2361 {
2362 #ifdef HAVE_NANOSLEEP
2363 struct timespec rqt;
2364
2365 rqt.tv_sec = 0;
2366 rqt.tv_nsec = MENU_DELAY_USEC * 1000;
2367 nanosleep(&rqt, NULL);
2368 #else
2369 /* use select for timing */
2370 struct timeval tv;
2371
2372 tv.tv_sec = 0;
2373 tv.tv_usec = MENU_DELAY_USEC;
2374 select(0, NULL, NULL, NULL, &tv);
2375 #endif
2376
2377 }
2378 draw_Arrows (Arrows[i].name, +1);
2379 #ifdef DEBUG_MENUARROWS
2380 fprintf(stderr, "'%c': ", Arrows[i].name);
2381
2382 if (CurrentBar == NULL
2383 || (CurrentBar->arrows[i].type != MenuAction
2384 && CurrentBar->arrows[i].type !=
2385 MenuTerminalAction))
2386 {
2387 if (Arrows[i].str != NULL && Arrows[i].str[0])
2388 fprintf(stderr, "(default) \\033%s\n",
2389 &(Arrows[i].str[2]));
2390 }
2391 else
2392 {
2393 fprintf(stderr, "%s\n",
2394 CurrentBar->arrows[i].str);
2395 }
2396 #else /* DEBUG_MENUARROWS */
2397 if (CurrentBar == NULL
2398 || rxvt_action_dispatch(r,
2399 &(CurrentBar->arrows[i]))
2400 )
2401 {
2402 if (Arrows[i].str != NULL && Arrows[i].str[0] != 0)
2403 tt_write ((Arrows[i].str + 1),
2404 Arrows[i].str[0]);
2405 }
2406 #endif /* DEBUG_MENUARROWS */
2407 return;
2408 }
2409 }
2410 }
2411 /* FALLTHROUGH */
2412
2413 default:
2414 /*
2415 * press menubar or move to a new entry
2416 */
2417 if (menu != NULL && menu != ActiveMenu)
2418 {
2419 menu_hide_all (); /* pop down old menu */
2420 ActiveMenu = menu;
2421 menu_show (); /* pop up new menu */
2422 }
2423 break;
2424 }
2425 }
2426
2427 /*
2428 * general dispatch routine,
2429 * it would be nice to have `sticky' menus
2430 */
2431 void
2432 rxvt_term::menubar_control (XButtonEvent *ev)
2433 {
2434 switch (ev->type)
2435 {
2436 case ButtonPress:
2437 if (ev->button == Button1)
2438 menubar_select (ev);
2439 break;
2440
2441 case ButtonRelease:
2442 if (ev->button == Button1)
2443 menu_select (ev);
2444 break;
2445
2446 case MotionNotify:
2447 while (XCheckTypedWindowEvent(Xdisplay, TermWin.parent[0],
2448 MotionNotify, (XEvent *) ev)) ;
2449
2450 if (ActiveMenu)
2451 while (menu_select (ev)) ;
2452 else
2453 ev->y = -1;
2454 if (ev->y < 0)
2455 {
2456 Window unused_root, unused_child;
2457 int unused_root_x, unused_root_y;
2458 unsigned int unused_mask;
2459
2460 XQueryPointer(Xdisplay, menuBar.win,
2461 &unused_root, &unused_child,
2462 &unused_root_x, &unused_root_y,
2463 &(ev->x), &(ev->y), &unused_mask);
2464 menubar_select (ev);
2465 }
2466 break;
2467 }
2468 }
2469
2470 void
2471 rxvt_term::map_menuBar (int map)
2472 {
2473 if (menubar_mapping (map))
2474 resize_all_windows (0, 0, 0);
2475 }
2476 #endif
2477 /*----------------------- end-of-file (C source) -----------------------*/