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