ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/rxvt-unicode/src/menubar.C
Revision: 1.3
Committed: Tue Nov 25 11:52:42 2003 UTC (20 years, 6 months ago) by pcg
Content type: text/plain
Branch: MAIN
CVS Tags: rel-1-3, rel-1-2
Changes since 1.2: +157 -157 lines
Log Message:
*** empty log message ***

File Contents

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