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