1 /*--------------------------------*-C-*---------------------------------*
3 *----------------------------------------------------------------------*
5 * Copyright (c) 1997,1998 mj olesen <olesen@me.QueensU.CA>
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.
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.
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 *----------------------------------------------------------------------*/
25 #include "../config.h" /* NECESSARY */
26 #include "rxvt.h" /* NECESSARY */
30 #include "menubar.intpro" /* PROTOS for internal routines */
32 #define Menu_PixelWidth(menu) \
33 (2 * SHADOW + Width2Pixel ((menu)->width + 3 * HSPACE))
37 const char name; /* (l)eft, (u)p, (d)own, (r)ight */
38 const unsigned char str[5]; /* str[0] = STRLEN (str+1) */
41 { 'l', "\003\033[D" },
42 { 'u', "\003\033[A" },
43 { 'd', "\003\033[B" },
50 * find an item called NAME in MENU
53 rxvt_menuitem_find(const menu_t *menu, const char *name)
62 /* find the last item in the menu, this is good for separators */
63 for (item = menu->tail; item != NULL; item = item->prev)
65 if (item->entry.type == MenuSubMenu)
67 if (!STRCMP(name, (item->entry.submenu.menu)->name))
70 else if ((isSeparator(name) && isSeparator(item->name))
71 || !STRCMP(name, item->name))
78 * unlink ITEM from its MENU and free its memory
81 rxvt_term::menuitem_free (menu_t *menu, menuitem_t *item)
84 menuitem_t *prev, *next;
98 if (menu->tail == item)
100 if (menu->head == item)
103 switch (item->entry.type)
106 case MenuTerminalAction:
107 free(item->entry.action.str);
110 menu_delete (item->entry.submenu.menu);
113 if (item->name != NULL)
115 if (item->name2 != NULL)
121 * sort command vs. terminal actions and
122 * remove the first character of STR if it's '\0'
125 rxvt_action_type(action_t *action, unsigned char *str)
129 #if defined (DEBUG_MENU) || defined (DEBUG_MENUARROWS)
131 fprintf(stderr, "(len %d) = %s\n", len, str);
133 len = rxvt_Str_escaped((char *)str);
139 /* sort command vs. terminal actions */
140 action->type = MenuAction;
143 /* the functional equivalent: memmove (str, str+1, len); */
144 unsigned char *dst = (str);
145 unsigned char *src = (str + 1);
146 unsigned char *end = (str + len);
151 len--; /* decrement length */
153 action->type = MenuTerminalAction;
162 rxvt_term::action_dispatch (action_t *action)
164 switch (action->type)
166 case MenuTerminalAction:
167 cmd_write (action->str, action->len);
171 tt_write (action->str, action->len);
181 /* return the arrow index corresponding to NAME */
183 rxvt_menuarrow_find(char name)
187 for (i = 0; i < NARROWS; i++)
188 if (name == Arrows[i].name)
193 /* free the memory associated with arrow NAME of the current menubar */
195 rxvt_term::menuarrow_free (char name)
201 i = rxvt_menuarrow_find(name);
204 action_t *act = &(CurrentBar->arrows[i]);
209 case MenuTerminalAction:
215 act->type = MenuLabel;
220 for (i = 0; i < NARROWS; i++)
221 menuarrow_free (Arrows[i].name);
226 rxvt_term::menuarrow_add (char *string)
241 MEMSET(parse, 0, sizeof(parse));
243 /* fprintf(stderr, "add arrows = `%s'\n", string); */
244 for (p = string; p != NULL && *p; string = p)
247 /* fprintf(stderr, "parsing at %s\n", string); */
258 i = rxvt_menuarrow_find(string[1]);
262 continue; /* not found */
272 p = STRCHR(string, '\0');
280 p = STRCHR(next, '<');
283 if (p[1] && p[2] == '>')
289 if (beg.str == NULL) /* no end needed */
290 p = STRCHR(next, '\0');
299 cur->len = (p - string);
302 #ifdef DEBUG_MENUARROWS
304 fprintf(stderr, "<b>(len %d) = %.*s\n",
305 cur->len, cur->len, (cur->str ? cur->str : ""));
306 for (i = 0; i < NARROWS; i++)
309 fprintf(stderr, "<%c>(len %d) = %.*s\n",
311 cur->len, cur->len, (cur->str ? cur->str : ""));
314 fprintf(stderr, "<e>(len %d) = %.*s\n",
315 cur->len, cur->len, (cur->str ? cur->str : ""));
318 xtra_len = (beg.len + end.len);
319 for (i = 0; i < NARROWS; i++)
321 if (xtra_len || parse[i].len)
322 menuarrow_free (Arrows[i].name);
325 for (i = 0; i < NARROWS; i++)
333 str = rxvt_malloc(parse[i].len + xtra_len + 1);
338 STRNCPY(str + len, beg.str, beg.len);
341 STRNCPY(str + len, parse[i].str, parse[i].len);
346 STRNCPY(str + len, end.str, end.len);
351 #ifdef DEBUG_MENUARROWS
352 fprintf(stderr, "<%c>(len %d) = %s\n", Arrows[i].name, len, str);
354 if (rxvt_action_type(&(CurrentBar->arrows[i]), str) < 0)
360 rxvt_menuitem_add(menu_t *menu, const char *name, const char *name2, const char *action)
366 assert(name != NULL);
367 assert(action != NULL);
373 if (isSeparator(name))
375 /* add separator, no action */
382 * add/replace existing menu item
384 item = rxvt_menuitem_find(menu, name);
387 if (item->name2 != NULL && name2 != NULL)
393 switch (item->entry.type)
396 case MenuTerminalAction:
397 free(item->entry.action.str);
398 item->entry.action.str = NULL;
404 /* allocate a new itemect */
405 item = (menuitem_t *) rxvt_malloc(sizeof(menuitem_t));
411 item->name = rxvt_malloc(len + 1);
412 STRCPY(item->name, name);
413 if (name[0] == '.' && name[1] != '.')
414 len = 0; /* hidden menu name */
417 /* add to tail of list */
418 item->prev = menu->tail;
421 if (menu->tail != NULL)
422 (menu->tail)->next = item;
425 if (menu->head == NULL)
432 if (name2 != NULL && item->name2 == NULL)
439 item->name2 = rxvt_malloc(len + 1);
440 STRCPY(item->name2, name2);
444 item->entry.type = MenuLabel;
445 len = STRLEN(action);
447 if (len == 0 && item->name2 != NULL)
449 action = item->name2;
454 unsigned char *str = rxvt_malloc(len + 1);
458 if (rxvt_action_type(&(item->entry.action), str) < 0)
461 /* new item and a possible increase in width */
462 if (menu->width < (item->len + item->len2))
463 menu->width = (item->len + item->len2);
469 * search for the base starting menu for NAME.
470 * return a pointer to the portion of NAME that remains
473 rxvt_term::menu_find_base (menu_t **menu, char *path)
479 assert(menu != NULL);
480 assert(CurrentBar != NULL);
486 if (STRCHR(path, '/') != NULL)
490 while ((p = STRCHR(p, '/')) != NULL)
501 while ((p = STRCHR(path, '/')) != NULL)
506 if (!STRCMP(path, DOT))
510 else if (!STRCMP(path, DOTS))
513 *menu = (*menu)->parent;
517 path = menu_find_base (menu, path);
520 p[0] = '/'; /* fix-up name again */
528 if (!STRCMP(path, DOTS))
530 path += STRLEN(DOTS);
532 *menu = (*menu)->parent;
538 for (m = CurrentBar->tail; m != NULL; m = m->prev)
540 if (!STRCMP(path, m->name))
547 for (item = (*menu)->tail; item != NULL; item = item->prev)
549 if (item->entry.type == MenuSubMenu
550 && !STRCMP(path, (item->entry.submenu.menu)->name))
552 m = (item->entry.submenu.menu);
560 path += STRLEN(path);
566 * delete this entire menu
569 rxvt_term::menu_delete (menu_t *menu)
571 menu_t *parent = NULL, *prev, *next;
573 bar_t *CurrentBar = CurrentBar;
576 assert(CurrentBar != NULL);
579 /* delete the entire menu */
583 parent = menu->parent;
596 const int len = (menu->len + HSPACE);
598 if (CurrentBar->tail == menu)
599 CurrentBar->tail = prev;
600 if (CurrentBar->head == menu)
601 CurrentBar->head = next;
603 for (next = menu->next; next != NULL; next = next->next)
608 for (item = parent->tail; item != NULL; item = item->prev)
610 if (item->entry.type == MenuSubMenu
611 && item->entry.submenu.menu == menu)
613 item->entry.submenu.menu = NULL;
614 menuitem_free (menu->parent, item);
623 menuitem_t *p = item->prev;
625 menuitem_free (menu, item);
629 if (menu->name != NULL)
637 rxvt_term::menu_add (menu_t *parent, char *path)
640 bar_t *CurrentBar = CurrentBar;
643 assert(CurrentBar != NULL);
646 if (STRCHR(path, '/') != NULL)
652 /* shouldn't happen */
656 while ((p = STRCHR(path, '/')) != NULL)
662 parent = menu_add (parent, path);
666 if (!STRCMP(path, DOTS))
667 return (parent != NULL ? parent->parent : parent);
669 if (!STRCMP(path, DOT) || path[0] == '\0')
672 /* allocate a new menu */
673 menu = (menu_t *) rxvt_malloc(sizeof(menu_t));
676 menu->parent = parent;
677 menu->len = STRLEN(path);
678 menu->name = rxvt_malloc((menu->len + 1));
679 STRCPY(menu->name, path);
681 /* initialize head/tail */
682 menu->head = menu->tail = NULL;
683 menu->prev = menu->next = NULL;
686 menu->x = menu->y = menu->w = menu->h = 0;
689 /* add to tail of list */
692 menu->prev = CurrentBar->tail;
693 if (CurrentBar->tail != NULL)
694 CurrentBar->tail->next = menu;
695 CurrentBar->tail = menu;
696 if (CurrentBar->head == NULL)
697 CurrentBar->head = menu; /* fix head */
699 menu->x = (menu->prev->x + menu->prev->len + HSPACE);
705 item = rxvt_menuitem_add(parent, path, "", "");
712 assert(item->entry.type == MenuLabel);
714 item->entry.type = MenuSubMenu;
715 item->entry.submenu.menu = menu;
722 rxvt_term::drawbox_menubar (int x, int len, int state)
727 len = Width2Pixel(len + HSPACE);
728 if (x >= TermWin.width)
730 else if (x + len >= TermWin.width)
731 len = (TermWin_TotalWidth() - x);
733 #ifdef MENUBAR_SHADOW_IN
741 break; /* SHADOW_OUT */
745 break; /* SHADOW_IN */
747 top = bot = scrollbarGC;
751 rxvt_Draw_Shadow(display->display, menuBar.win, top, bot,
752 x, 0, len, menuBar_TotalHeight());
756 rxvt_term::drawtriangle (int x, int y, int state)
761 #ifdef MENU_SHADOW_IN
769 break; /* SHADOW_OUT */
773 break; /* SHADOW_IN */
775 top = bot = scrollbarGC;
779 w = Height2Pixel(1) - 2 * SHADOW;
781 x -= SHADOW + (3 * w / 2);
784 rxvt_Draw_Triangle(display->display, ActiveMenu->win, top, bot, x, y, w,
789 rxvt_term::drawbox_menuitem (int y, int state)
793 #ifdef MENU_SHADOW_IN
801 break; /* SHADOW_OUT */
805 break; /* SHADOW_IN */
807 top = bot = scrollbarGC;
811 rxvt_Draw_Shadow(display->display, ActiveMenu->win, top, bot,
812 SHADOW + 0, SHADOW + y,
813 ActiveMenu->w - 2 * (SHADOW),
814 HEIGHT_TEXT + 2 * SHADOW);
815 XFlush(display->display);
818 #ifdef DEBUG_MENU_LAYOUT
820 rxvt_print_menu_ancestors(menu_t *menu)
824 fprintf(stderr, "Top Level menu\n");
827 fprintf(stderr, "menu %s ", menu->name);
828 if (menu->parent != NULL)
832 for (item = menu->parent->head; item != NULL; item = item->next)
834 if (item->entry.type == MenuSubMenu
835 && item->entry.submenu.menu == menu)
842 fprintf(stderr, "is an orphan!\n");
846 fprintf(stderr, "\n");
847 rxvt_print_menu_ancestors(menu->parent);
851 rxvt_print_menu_descendants(menu_t *menu)
861 parent = parent->parent;
863 while (parent != NULL);
865 for (i = 0; i < level; i++)
866 fprintf(stderr, ">");
867 fprintf(stderr, "%s\n", menu->name);
869 for (item = menu->head; item != NULL; item = item->next)
871 if (item->entry.type == MenuSubMenu)
873 if (item->entry.submenu.menu == NULL)
874 fprintf(stderr, "> %s == NULL\n", item->name);
876 rxvt_print_menu_descendants(item->entry.submenu.menu);
880 for (i = 0; i < level; i++)
881 fprintf(stderr, "+");
882 if (item->entry.type == MenuLabel)
883 fprintf(stderr, "label: ");
884 fprintf(stderr, "%s\n", item->name);
888 for (i = 0; i < level; i++)
889 fprintf(stderr, "<");
890 fprintf(stderr, "\n");
894 /* pop up/down the current menu and redraw the menuBar button */
896 rxvt_term::menu_show ()
899 menu_t *ActiveMenu = ActiveMenu;
902 if (ActiveMenu == NULL)
906 if (ActiveMenu->parent == NULL)
910 drawbox_menubar (x, ActiveMenu->len, -1);
914 ActiveMenu->w = Menu_PixelWidth(ActiveMenu);
916 if ((x + ActiveMenu->w) >= TermWin.width)
917 x = (TermWin_TotalWidth() - ActiveMenu->w);
919 /* find the height */
920 for (h = 0, item = ActiveMenu->head; item != NULL; item = item->next)
921 h += isSeparator(item->name) ? HEIGHT_SEPARATOR
922 : HEIGHT_TEXT + 2 * SHADOW;
923 ActiveMenu->h = h + 2 * SHADOW;
925 if (ActiveMenu->win == None)
927 ActiveMenu->win = XCreateSimpleWindow(display->display, TermWin.vt,
929 ActiveMenu->w, ActiveMenu->h,
932 PixColors[Color_scroll]);
933 XMapWindow(display->display, ActiveMenu->win);
935 rxvt_Draw_Shadow(display->display, ActiveMenu->win,
936 topShadowGC, botShadowGC,
937 0, 0, ActiveMenu->w, ActiveMenu->h);
939 /* determine the correct right-alignment */
940 for (xright = 0, item = ActiveMenu->head; item != NULL; item = item->next)
941 if (item->len2 > xright)
944 for (y = 0, item = ActiveMenu->head; item != NULL; item = item->next)
946 const int xoff = (SHADOW + Width2Pixel(HSPACE) / 2);
950 if (isSeparator(item->name))
952 rxvt_Draw_Shadow(display->display, ActiveMenu->win,
953 topShadowGC, botShadowGC,
954 SHADOW, y + SHADOW + 1,
955 ActiveMenu->w - 2 * SHADOW, 0);
956 h = HEIGHT_SEPARATOR;
960 char *name = item->name;
963 if (item->entry.type == MenuLabel)
967 else if (item->entry.type == MenuSubMenu)
971 menu_t *menu = item->entry.submenu.menu;
973 drawtriangle (ActiveMenu->w, y, +1);
978 y1 = ActiveMenu->y + y;
980 menu->w = Menu_PixelWidth(menu);
982 /* place sub-menu at midpoint of parent menu */
983 x1 = ActiveMenu->w / 2;
984 if (x1 > menu->w) /* right-flush menu if too small */
985 x1 += (x1 - menu->w);
988 /* find the height of this submenu */
989 for (h = 0, it = menu->head; it != NULL; it = it->next)
990 h += isSeparator(it->name) ? HEIGHT_SEPARATOR
991 : HEIGHT_TEXT + 2 * SHADOW;
992 menu->h = h + 2 * SHADOW;
994 /* ensure menu is in window limits */
995 if ((x1 + menu->w) >= TermWin.width)
996 x1 = (TermWin_TotalWidth() - menu->w);
998 if ((y1 + menu->h) >= TermWin.height)
999 y1 = (TermWin_TotalHeight() - menu->h);
1001 menu->x = (x1 < 0 ? 0 : x1);
1002 menu->y = (y1 < 0 ? 0 : y1);
1004 else if (item->name2 && !STRCMP(name, item->name2))
1010 if (TermWin.fontset)
1011 XmbDrawString(display->display,
1012 ActiveMenu->win, TermWin.fontset,
1014 2 * SHADOW + y + TermWin.font->ascent + 1,
1018 XDrawString(display->display, ActiveMenu->win, gc, xoff,
1019 2 * SHADOW + y + TermWin.font->ascent + 1,
1028 if (TermWin.fontset)
1029 XmbDrawString(display->display,
1030 ActiveMenu->win, TermWin.fontset,
1032 ActiveMenu->w - (xoff + Width2Pixel(xright)),
1033 2 * SHADOW + y + TermWin.font->ascent + 1,
1037 XDrawString(display->display, ActiveMenu->win, gc,
1038 ActiveMenu->w - (xoff + Width2Pixel(xright)),
1039 2 * SHADOW + y + TermWin.font->ascent + 1,
1042 h = HEIGHT_TEXT + 2 * SHADOW;
1049 rxvt_term::menu_display (void (*update)(rxvt_t *))
1051 menu_t *ActiveMenu = ActiveMenu;
1053 if (ActiveMenu == NULL)
1055 if (ActiveMenu->win != None)
1056 XDestroyWindow(display->display, ActiveMenu->win);
1057 ActiveMenu->win = None;
1058 ActiveMenu->item = NULL;
1060 if (ActiveMenu->parent == NULL)
1061 drawbox_menubar (ActiveMenu->x, ActiveMenu->len, +1);
1062 ActiveMenu = ActiveMenu->parent;
1067 rxvt_term::menu_hide_all ()
1069 menu_display (rxvt_menu_hide_all);
1073 rxvt_term::menu_hide ()
1075 menu_display (rxvt_menu_show);
1079 rxvt_term::menu_clear (menu_t *menu)
1083 menuitem_t *item = menu->tail;
1085 while (item != NULL)
1087 menuitem_free (menu, item);
1088 /* it didn't get freed ... why? */
1089 if (item == menu->tail)
1098 rxvt_term::menubar_clear ()
1100 bar_t *CurrentBar = CurrentBar;
1102 if (CurrentBar != NULL)
1104 menu_t *menu = CurrentBar->tail;
1106 while (menu != NULL)
1108 menu_t *prev = menu->prev;
1113 CurrentBar->head = CurrentBar->tail = NULL;
1115 if (CurrentBar->title)
1117 free(CurrentBar->title);
1118 CurrentBar->title = NULL;
1120 menuarrow_free (0); /* remove all arrow functions */
1125 #if (MENUBAR_MAX > 1)
1126 /* find if menu already exists */
1128 rxvt_term::menubar_find (const char *name)
1130 bar_t *bar = CurrentBar;
1132 #ifdef DEBUG_MENUBAR_STACKING
1133 fprintf(stderr, "looking for [menu:%s] ...", name ? name : "(nil)");
1135 if (bar == NULL || name == NULL)
1138 if (STRLEN(name) && STRCMP(name, "*"))
1142 if (!STRCMP(bar->name, name))
1144 #ifdef DEBUG_MENUBAR_STACKING
1145 fprintf(stderr, " found!\n");
1151 while (bar != CurrentBar);
1154 #ifdef DEBUG_MENUBAR_STACKING
1155 fprintf(stderr, "%s found!\n", (bar ? "" : " NOT"));
1162 rxvt_term::menubar_push (const char *name)
1167 if (CurrentBar == NULL)
1169 /* allocate first one */
1170 bar = (bar_t *) rxvt_malloc(sizeof(bar_t));
1172 MEMSET(bar, 0, sizeof(bar_t));
1173 /* circular linked-list */
1174 bar->next = bar->prev = bar;
1175 bar->head = bar->tail = NULL;
1184 /* find if menu already exists */
1185 bar = menubar_find (name);
1188 /* found it, use it */
1193 /* create if needed, or reuse the existing empty menubar */
1194 if (CurrentBar->head != NULL)
1196 /* need to malloc another one */
1197 if (Nbars < MENUBAR_MAX)
1198 bar = (bar_t *) rxvt_malloc(sizeof(bar_t));
1202 /* malloc failed or too many menubars, reuse another */
1205 bar = CurrentBar->next;
1210 bar->head = bar->tail = NULL;
1213 bar->next = CurrentBar->next;
1214 CurrentBar->next = bar;
1215 bar->prev = CurrentBar;
1216 bar->next->prev = bar;
1227 /* give menubar this name */
1228 STRNCPY(CurrentBar->name, name, MAXNAME);
1229 CurrentBar->name[MAXNAME - 1] = '\0';
1234 /* switch to a menu called NAME and remove it */
1236 rxvt_term::menubar_remove (const char *name)
1240 if ((bar = menubar_find (name)) == NULL)
1248 * pop a menubar, clean it up first
1250 if (CurrentBar != NULL)
1252 bar_t *prev = CurrentBar->prev;
1253 bar_t *next = CurrentBar->next;
1255 if (prev == next && prev == CurrentBar)
1258 Nbars = 0; /* safety */
1271 while (CurrentBar && !STRCMP(name, "*"));
1275 rxvt_action_decode(FILE *fp, action_t *act)
1280 if (act == NULL || (len = act->len) == 0 || (str = act->str) == NULL)
1283 if (act->type == MenuTerminalAction)
1286 /* can strip trailing ^G from XTerm sequence */
1287 if (str[0] == C0_ESC && str[1] == ']' && str[len - 1] == C0_BEL)
1290 else if (str[0] == C0_ESC)
1299 /* can strip trailing '\r' from M-x sequence */
1300 if (str[len - 1] == '\r')
1305 fprintf(fp, "M-"); /* meta prefix */
1312 * control character form is preferred, since backslash-escaping
1313 * can be really ugly looking when the backslashes themselves also
1314 * have to be escaped to avoid Shell (or whatever scripting
1315 * language) interpretation
1319 unsigned char ch = *str++;
1328 break; /* carriage-return */
1330 fprintf(fp, "\\\\");
1331 break; /* backslash */
1339 fprintf(fp, "^%c", ('@' + ch));
1341 fprintf(fp, "\\%o", ch);
1343 fprintf(fp, "%c", ch);
1352 rxvt_menu_dump(FILE *fp, menu_t *menu)
1356 /* create a new menu and clear it */
1357 fprintf(fp, (menu->parent ? "./%s/*\n" : "/%s/*\n"), menu->name);
1359 for (item = menu->head; item != NULL; item = item->next)
1361 switch (item->entry.type)
1364 if (item->entry.submenu.menu == NULL)
1365 fprintf(fp, "> %s == NULL\n", item->name);
1367 rxvt_menu_dump(fp, item->entry.submenu.menu);
1371 fprintf(fp, "{%s}\n", (STRLEN(item->name) ? item->name : "-"));
1374 case MenuTerminalAction:
1376 fprintf(fp, "{%s}", item->name);
1377 if (item->name2 != NULL && STRLEN(item->name2))
1378 fprintf(fp, "{%s}", item->name2);
1380 rxvt_action_decode(fp, &(item->entry.action));
1385 fprintf(fp, (menu->parent ? "../\n" : "/\n\n"));
1389 rxvt_term::menubar_dump (FILE *fp)
1391 bar_t *bar = CurrentBar;
1394 if (bar == NULL || fp == NULL)
1399 "# " APL_SUBCLASS " (%s) Pid: %u\n# Date: %s\n\n",
1400 rs[Rs_name], (unsigned int)getpid(), ctime(&t));
1402 /* dump in reverse order */
1403 bar = CurrentBar->prev;
1409 fprintf(fp, "[menu:%s]\n", bar->name);
1411 if (bar->title != NULL)
1412 fprintf(fp, "[title:%s]\n", bar->title);
1414 for (i = 0; i < NARROWS; i++)
1416 switch (bar->arrows[i].type)
1418 case MenuTerminalAction:
1420 fprintf(fp, "<%c>", Arrows[i].name);
1421 rxvt_action_decode(fp, &(bar->arrows[i]));
1427 for (menu = bar->head; menu != NULL; menu = menu->next)
1428 rxvt_menu_dump(fp, menu);
1430 fprintf(fp, "\n[done:%s]\n\n", bar->name);
1433 while (bar != CurrentBar->prev);
1435 #endif /* (MENUBAR_MAX > 1) */
1438 * read in menubar commands from FILENAME
1439 * ignore all input before the tag line [menu] or [menu:???]
1441 * Note that since File_find () is used, FILENAME can be semi-colon
1442 * delimited such that the second part can refer to a tag
1443 * so that a large `database' of menus can be collected together
1446 * FILENAME = "file;"
1447 * read `file' starting with first [menu] or [menu:???] line
1449 * FILENAME = "file;tag"
1450 * read `file' starting with [menu:tag]
1453 rxvt_term::menubar_read (const char *filename)
1455 /* read in a menu from a file */
1458 char *p, *file, *tag = NULL;
1460 file = (char *)rxvt_File_find(filename, ".menu", rs[Rs_path]);
1463 fp = fopen(file, "rb");
1468 #if (MENUBAR_MAX > 1)
1469 /* semi-colon delimited */
1470 if ((tag = STRCHR(filename, ';')) != NULL)
1476 #endif /* (MENUBAR_MAX > 1) */
1478 fprintf(stderr, "[read:%s]\n", p);
1480 fprintf(stderr, "looking for [menu:%s]\n", tag);
1483 while ((p = fgets(buffer, sizeof(buffer), fp)) != NULL)
1487 if ((n = rxvt_Str_match(p, "[menu")) != 0)
1491 /* looking for [menu:tag] */
1492 if (p[n] == ':' && p[n + 1] != ']')
1495 n += rxvt_Str_match(p + n, tag);
1499 fprintf(stderr, "[menu:%s]\n", tag);
1505 else if (p[n] == ':' || p[n] == ']')
1510 /* found [menu], [menu:???] tag */
1516 fprintf(stderr, "read line = %s\n", p);
1519 /* looking for [done:tag] or [done:] */
1520 if ((n = rxvt_Str_match(p, "[done")) != 0)
1527 else if (p[n] == ':')
1537 n += rxvt_Str_match(p + n, tag);
1541 fprintf(stderr, "[done:%s]\n", tag);
1549 /* what? ... skip this line */
1550 p[0] = COMMENT_CHAR;
1555 * remove leading/trailing space
1556 * and strip-off leading/trailing quotes
1557 * skip blank or comment lines
1560 if (*p && *p != '#')
1562 menu_readonly = 0; /* if case we read another file */
1563 menubar_dispatch (p);
1565 /* get another line */
1566 p = fgets(buffer, sizeof(buffer), fp);
1573 * user interface for building/deleting and otherwise managing menus
1576 rxvt_term::menubar_dispatch (char *str)
1579 char *path, *name, *name2;
1581 if (menubar_visible(r) && ActiveMenu != NULL)
1590 case '/': /* absolute & relative path */
1591 case MENUITEM_BEG: /* menuitem */
1592 /* add `+' prefix for these cases */
1598 str++; /* skip cmd character */
1602 #if (MENUBAR_MAX > 1)
1603 if (CurrentBar == NULL)
1605 #endif /* (MENUBAR_MAX > 1) */
1606 if (str[1] && str[2] == '>') /* arrow commands */
1607 menuarrow_add (str);
1610 case '[': /* extended command */
1611 while (str[0] == '[')
1613 char *next = (++str); /* skip leading '[' */
1620 if ((next = STRCHR(next, ':')) == NULL)
1621 return; /* parse error */
1623 while (next[1] != ']');
1624 /* remove and skip ':]' */
1630 if ((next = STRCHR(next, ']')) == NULL)
1631 return; /* parse error */
1632 /* remove and skip ']' */
1641 /* try and dispatch it, regardless of read/write status */
1642 saved = menu_readonly;
1644 menubar_dispatch (str + 1);
1645 menu_readonly = saved;
1647 /* these ones don't require menu stacking */
1648 else if (!STRCMP(str, "clear"))
1652 else if (!STRCMP(str, "done") || rxvt_Str_match(str, "done:"))
1656 else if (!STRCMP(str, "show"))
1661 else if (!STRCMP(str, "hide"))
1666 else if ((n = rxvt_Str_match(str, "read:")) != 0)
1668 /* read in a menu from a file */
1672 else if ((n = rxvt_Str_match(str, "title:")) != 0)
1675 if (CurrentBar != NULL && !menu_readonly)
1679 name = rxvt_realloc(CurrentBar->title,
1684 CurrentBar->title = name;
1690 free(CurrentBar->title);
1691 CurrentBar->title = NULL;
1695 else if ((n = rxvt_Str_match(str, "pixmap:")) != 0)
1698 xterm_seq (XTerm_Pixmap, str, CHAR_ST);
1700 #if (MENUBAR_MAX > 1)
1701 else if ((n = rxvt_Str_match(str, "rm")) != 0)
1712 menubar_remove (str);
1717 else if ((n = rxvt_Str_match(str, "menu")) != 0)
1724 /* add/access menuBar */
1725 if (*str != '\0' && *str != '*')
1729 if (CurrentBar == NULL)
1731 menubar_push ("default");
1735 if (CurrentBar != NULL)
1736 menu_readonly = 0; /* allow menu build commands */
1738 else if (!STRCMP(str, "dump"))
1740 /* dump current menubars to a file */
1743 /* enough space to hold the results */
1746 sprintf(buffer, "/tmp/" APL_SUBCLASS "-%u",
1747 (unsigned int)getpid());
1749 if ((fp = fopen(buffer, "wb")) != NULL)
1751 xterm_seq (XTerm_title, buffer, CHAR_ST);
1756 else if (!STRCMP(str, "next"))
1760 CurrentBar = CurrentBar->next;
1764 else if (!STRCMP(str, "prev"))
1768 CurrentBar = CurrentBar->prev;
1772 else if (!STRCMP(str, "swap"))
1774 /* swap the top 2 menus */
1777 bar_t *cbprev = CurrentBar->prev;
1778 bar_t *cbnext = CurrentBar->next;
1780 cbprev->next = cbnext;
1781 cbnext->prev = cbprev;
1783 CurrentBar->next = cbprev;
1784 CurrentBar->prev = cbprev->prev;
1786 cbprev->prev->next = CurrentBar;
1787 cbprev->prev = CurrentBar;
1789 CurrentBar = cbprev;
1793 #endif /* (MENUBAR_MAX > 1) */
1796 BuildMenu = ActiveMenu = NULL;
1798 #ifdef DEBUG_MENUBAR_STACKING
1799 fprintf(stderr, "menus are read%s\n",
1800 menu_readonly ? "only" : "/write");
1808 #if (MENUBAR_MAX > 1)
1809 if (CurrentBar == NULL)
1813 #ifdef DEBUG_MENUBAR_STACKING
1814 fprintf(stderr, "menus are read%s\n",
1815 menu_readonly ? "only" : "/write");
1819 #endif /* (MENUBAR_MAX > 1) */
1828 /* parse STR, allow spaces inside (name) */
1829 if (path[0] != '\0')
1831 name = STRCHR(path, MENUITEM_BEG);
1832 str = STRCHR(path, MENUITEM_END);
1833 if (name != NULL || str != NULL)
1835 if (name == NULL || str == NULL || str <= (name + 1)
1836 || (name > path && name[-1] != '/'))
1838 rxvt_print_error("menu error <%s>\n", path);
1841 if (str[1] == MENUITEM_BEG)
1844 str = STRCHR(name2, MENUITEM_END);
1848 rxvt_print_error("menu error <%s>\n", path);
1851 name2[-2] = '\0'; /* remove prev MENUITEM_END */
1853 if (name > path && name[-1] == '/')
1856 *name++ = '\0'; /* delimit */
1857 *str++ = '\0'; /* delimit */
1859 while (isspace(*str))
1860 str++; /* skip space */
1864 "`%c' path = <%s>, name = <%s>, name2 = <%s>, action = <%s>\n",
1865 cmd, (path ? path : "(nil)"), (name ? name : "(nil)"),
1866 (name2 ? name2 : "(nil)"), (str ? str : "(nil)")
1871 /* process the different commands */
1874 case '+': /* add/replace existing menu or menuitem */
1875 if (path[0] != '\0')
1879 path = menu_find_base (&(BuildMenu), path);
1882 /* don't allow menus called `*' */
1885 menu_clear (BuildMenu);
1888 else if (len >= 2 && !STRCMP((path + len - 2), "/*"))
1890 path[len - 2] = '\0';
1892 if (path[0] != '\0')
1893 BuildMenu = menu_add (BuildMenu, path);
1895 if (name != NULL && name[0] != '\0')
1896 rxvt_menuitem_add(BuildMenu,
1897 (STRCMP(name, SEPARATOR_NAME) ? name : ""),
1901 case '-': /* delete menu entry */
1902 if (!STRCMP(path, "/*") && (name == NULL || name[0] == '\0'))
1909 else if (path[0] != '\0')
1912 menu_t *menu = BuildMenu;
1914 path = menu_find_base (&menu, path);
1917 /* submenu called `*' clears all menu items */
1923 else if (len >= 2 && !STRCMP(&path[len - 2], "/*"))
1928 else if (path[0] != '\0')
1936 if (BuildMenu != NULL)
1938 if (name == NULL || name[0] == '\0')
1939 BuildMenu = menu_delete (BuildMenu);
1944 menu_t *BuildMenu = BuildMenu;
1946 n1 = STRCMP(name, SEPARATOR_NAME) ? name : "";
1947 item = rxvt_menuitem_find(BuildMenu, n1);
1948 if (item != NULL && item->entry.type != MenuSubMenu)
1950 menuitem_free (BuildMenu, item);
1952 /* fix up the width */
1953 BuildMenu->width = 0;
1954 for (item = BuildMenu->head; item != NULL;
1957 short l = item->len + item->len2;
1959 MAX_IT(BuildMenu->width, l);
1972 rxvt_term::draw_Arrows (int name, int state)
1978 #ifdef MENU_SHADOW_IN
1986 break; /* SHADOW_OUT */
1990 break; /* SHADOW_IN */
1992 top = bot = scrollbarGC;
1993 break; /* neutral */
1999 for (i = 0; i < NARROWS; i++)
2001 const int w = Width2Pixel(1);
2002 const int y = (menuBar_TotalHeight() - w) / 2;
2003 int x = Arrows_x + (5 * Width2Pixel(i)) / 4;
2005 if (!name || name == Arrows[i].name)
2006 rxvt_Draw_Triangle(display->display, menuBar.win, top, bot, x, y, w,
2009 XFlush(display->display);
2013 rxvt_term::menubar_expose ()
2018 if (!menubar_visible(r) || menuBar.win == 0)
2021 if (menubarGC == None)
2023 /* Create the graphics context */
2026 gcvalue.font = TermWin.font->fid;
2028 gcvalue.foreground = (XDEPTH <= 2 ? PixColors[Color_fg]
2029 : PixColors[Color_Black]);
2030 menubarGC = XCreateGC(display->display, menuBar.win,
2031 GCForeground | GCFont, &gcvalue);
2034 /* make sure the font is correct */
2035 XSetFont(display->display, menubarGC, TermWin.font->fid);
2036 XSetFont(display->display, botShadowGC, TermWin.font->fid);
2037 XClearWindow(display->display, menuBar.win);
2042 if (CurrentBar != NULL)
2044 for (menu = CurrentBar->head; menu != NULL; menu = menu->next)
2046 int len = menu->len;
2048 x = (menu->x + menu->len + HSPACE);
2050 #ifdef DEBUG_MENU_LAYOUT
2051 rxvt_print_menu_descendants(menu);
2054 if (x >= TermWin.ncol)
2055 len = (TermWin.ncol - (menu->x + HSPACE));
2057 drawbox_menubar (menu->x, len, +1);
2059 if (TermWin.fontset)
2060 XmbDrawString(display->display,
2061 menuBar.win, TermWin.fontset,
2063 (Width2Pixel(menu->x) + Width2Pixel(HSPACE) / 2),
2064 menuBar_height() - SHADOW, menu->name, len);
2067 XDrawString(display->display, menuBar.win, menubarGC,
2068 (Width2Pixel(menu->x) + Width2Pixel(HSPACE) / 2),
2069 menuBar_height() - SHADOW, menu->name, len);
2071 if (x >= TermWin.ncol)
2075 drawbox_menubar (x, TermWin.ncol, (CurrentBar ? +1 : -1));
2077 /* add the menuBar title, if it exists and there's plenty of room */
2079 if (x < TermWin.ncol)
2086 ncol = (int)TermWin.ncol;
2087 if (x < (ncol - (NARROWS + 1)))
2089 ncol -= (NARROWS + 1);
2090 Arrows_x = Width2Pixel(ncol);
2092 draw_Arrows (0, +1);
2095 && CurrentBar->title) ? CurrentBar->title : "%n-%v";
2096 for (len = 0; str[0] && len < sizeof(title) - 1; str++)
2098 const char *s = NULL;
2108 break; /* resource name */
2111 break; /* version number */
2114 break; /* literal '%' */
2117 while (*s && len < sizeof(title) - 1)
2118 title[len++] = *s++;
2122 title[len++] = str[0];
2128 ncol -= (x + len + HSPACE);
2129 if (len > 0 && ncol >= 0)
2132 if (TermWin.fontset)
2133 XmbDrawString(display->display,
2134 menuBar.win, TermWin.fontset,
2136 Width2Pixel(x) + Width2Pixel(ncol + HSPACE) / 2,
2137 menuBar_height() - SHADOW, title, len);
2140 XDrawString(display->display, menuBar.win, menubarGC,
2141 Width2Pixel(x) + Width2Pixel(ncol + HSPACE) / 2,
2142 menuBar_height() - SHADOW, title, len);
2148 rxvt_term::menubar_mapping (int map)
2152 if (map && !menubar_visible(r))
2155 if (menuBar.win == 0)
2157 XMapWindow(display->display, menuBar.win);
2160 else if (!map && menubar_visible(r))
2164 XUnmapWindow(display->display, menuBar.win);
2174 rxvt_term::menu_select (XButtonEvent *ev)
2176 menuitem_t *thisitem, *item = NULL;
2178 menu_t *ActiveMenu = ActiveMenu;
2180 Window unused_root, unused_child;
2181 int unused_root_x, unused_root_y;
2182 unsigned int unused_mask;
2184 if (ActiveMenu == NULL)
2187 XQueryPointer(display->display, ActiveMenu->win,
2188 &unused_root, &unused_child,
2189 &unused_root_x, &unused_root_y,
2190 &(ev->x), &(ev->y), &unused_mask);
2192 if (ActiveMenu->parent != NULL && (ev->x < 0 || ev->y < 0))
2197 /* determine the menu item corresponding to the Y index */
2199 if (ev->x >= 0 && ev->x <= (ActiveMenu->w - SHADOW))
2201 for (item = ActiveMenu->head; item != NULL; item = item->next)
2203 int h = HEIGHT_TEXT + 2 * SHADOW;
2205 if (isSeparator(item->name))
2206 h = HEIGHT_SEPARATOR;
2207 else if (ev->y >= y && ev->y < (y + h))
2212 if (item == NULL && ev->type == ButtonRelease)
2218 this_y = y - SHADOW;
2220 /* erase the last item */
2221 if (ActiveMenu->item != NULL)
2223 if (ActiveMenu->item != thisitem)
2225 for (y = 0, item = ActiveMenu->head; item != NULL;
2230 if (isSeparator(item->name))
2231 h = HEIGHT_SEPARATOR;
2232 else if (item == ActiveMenu->item)
2234 /* erase old menuitem */
2235 drawbox_menuitem (y, 0); /* No Shadow */
2236 if (item->entry.type == MenuSubMenu)
2237 drawtriangle (ActiveMenu->w, y, +1);
2241 h = HEIGHT_TEXT + 2 * SHADOW;
2250 switch (item->entry.type)
2258 case MenuTerminalAction:
2259 drawbox_menuitem (_y, -1);
2261 #ifdef HAVE_NANOSLEEP
2262 struct timespec rqt;
2265 rqt.tv_nsec = MENU_DELAY_USEC * 1000;
2266 nanosleep(&rqt, NULL);
2268 /* use select for timing */
2272 tv.tv_usec = MENU_DELAY_USEC;
2273 select(0, NULL, NULL, NULL, &tv);
2277 /* remove menu before sending keys to the application */
2280 action_dispatch (&(item->entry.action));
2281 #else /* DEBUG_MENU */
2282 fprintf(stderr, "%s: %s\n", item->name,
2283 item->entry.action.str);
2284 #endif /* DEBUG_MENU */
2290 if (item->entry.type == MenuSubMenu)
2298 ActiveMenu->item = thisitem;
2302 item = ActiveMenu->item;
2303 if (item->entry.type != MenuLabel)
2304 drawbox_menuitem (y, +1);
2305 if (item->entry.type == MenuSubMenu)
2309 drawtriangle (ActiveMenu->w, y, -1);
2311 x = ev->x + (ActiveMenu->parent
2313 : Width2Pixel(ActiveMenu->x));
2315 if (x >= item->entry.submenu.menu->x)
2317 ActiveMenu = item->entry.submenu.menu;
2327 rxvt_term::menubar_select (XButtonEvent *ev)
2329 menu_t *menu = NULL;
2331 /* determine the pulldown menu corresponding to the X index */
2332 if (ev->y >= 0 && ev->y <= menuBar_height() && CurrentBar != NULL)
2334 for (menu = CurrentBar->head; menu != NULL; menu = menu->next)
2336 int x = Width2Pixel(menu->x);
2337 int w = Width2Pixel(menu->len + HSPACE);
2339 if ((ev->x >= x && ev->x < x + w))
2350 if (menu == NULL && Arrows_x && ev->x >= Arrows_x)
2354 for (i = 0; i < NARROWS; i++)
2356 if (ev->x >= (Arrows_x + (Width2Pixel(4 * i + i)) / 4)
2357 && ev->x < (Arrows_x
2358 + (Width2Pixel(4 * i + i + 4)) / 4))
2360 draw_Arrows (Arrows[i].name, -1);
2362 #ifdef HAVE_NANOSLEEP
2363 struct timespec rqt;
2366 rqt.tv_nsec = MENU_DELAY_USEC * 1000;
2367 nanosleep(&rqt, NULL);
2369 /* use select for timing */
2373 tv.tv_usec = MENU_DELAY_USEC;
2374 select(0, NULL, NULL, NULL, &tv);
2378 draw_Arrows (Arrows[i].name, +1);
2379 #ifdef DEBUG_MENUARROWS
2380 fprintf(stderr, "'%c': ", Arrows[i].name);
2382 if (CurrentBar == NULL
2383 || (CurrentBar->arrows[i].type != MenuAction
2384 && CurrentBar->arrows[i].type !=
2385 MenuTerminalAction))
2387 if (Arrows[i].str != NULL && Arrows[i].str[0])
2388 fprintf(stderr, "(default) \\033%s\n",
2389 &(Arrows[i].str[2]));
2393 fprintf(stderr, "%s\n",
2394 CurrentBar->arrows[i].str);
2396 #else /* DEBUG_MENUARROWS */
2397 if (CurrentBar == NULL
2398 || rxvt_action_dispatch(r,
2399 &(CurrentBar->arrows[i]))
2402 if (Arrows[i].str != NULL && Arrows[i].str[0] != 0)
2403 tt_write ((Arrows[i].str + 1),
2406 #endif /* DEBUG_MENUARROWS */
2415 * press menubar or move to a new entry
2417 if (menu != NULL && menu != ActiveMenu)
2419 menu_hide_all (); /* pop down old menu */
2421 menu_show (); /* pop up new menu */
2428 * general dispatch routine,
2429 * it would be nice to have `sticky' menus
2432 rxvt_term::menubar_control (XButtonEvent *ev)
2437 if (ev->button == Button1)
2438 menubar_select (ev);
2442 if (ev->button == Button1)
2447 while (XCheckTypedWindowEvent(display->display, TermWin.parent[0],
2448 MotionNotify, (XEvent *) ev)) ;
2451 while (menu_select (ev)) ;
2456 Window unused_root, unused_child;
2457 int unused_root_x, unused_root_y;
2458 unsigned int unused_mask;
2460 XQueryPointer(display->display, menuBar.win,
2461 &unused_root, &unused_child,
2462 &unused_root_x, &unused_root_y,
2463 &(ev->x), &(ev->y), &unused_mask);
2464 menubar_select (ev);
2471 rxvt_term::map_menuBar (int map)
2473 if (menubar_mapping (map))
2474 resize_all_windows (0, 0, 0);
2477 /*----------------------- end-of-file (C source) -----------------------*/