1 /*--------------------------------*-C-*---------------------------------*
3 *----------------------------------------------------------------------*
5 * Copyright (c) 1997,1998 mj olesen <olesen@me.QueensU.CA>
6 * Copyright (c) 2004 Marc Lehmann <pcg@goof.com>
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.
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.
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 *----------------------------------------------------------------------*/
26 #include "../config.h" /* NECESSARY */
30 #include "rxvt.h" /* NECESSARY */
35 #define Menu_PixelWidth(menu) \
36 (2 * SHADOW + Width2Pixel ((menu)->width + 3 * HSPACE))
40 const char name; /* (l)eft, (u)p, (d)own, (r)ight */
41 const unsigned char str[5]; /* str[0] = strlen (str+1) */
44 { 'l', "\003\033[D" },
45 { 'u', "\003\033[A" },
46 { 'd', "\003\033[B" },
53 draw_string (rxvt_drawable &d, GC gc, rxvt_fontset *fs, int x, int y, char *str, int len)
60 int l = mbrtowc (&w, str, len, mbs);
68 rxvt_font *font = (*fs)[fs->find_font (w)];
70 font->draw (d, x, y, &ch, 1, Color_bg, Color_scroll);
72 x += font->width * wcwidth (w);
77 * find an item called NAME in MENU
80 rxvt_menuitem_find (const menu_t *menu, const char *name)
85 assert (name != NULL);
86 assert (menu != NULL);
89 /* find the last item in the menu, this is good for separators */
90 for (item = menu->tail; item != NULL; item = item->prev)
92 if (item->entry.type == MenuSubMenu)
94 if (!strcmp (name, (item->entry.submenu.menu)->name))
97 else if ((isSeparator (name) && isSeparator (item->name))
98 || !strcmp (name, item->name))
105 * unlink ITEM from its MENU and free its memory
108 rxvt_term::menuitem_free (menu_t *menu, menuitem_t *item)
111 menuitem_t *prev, *next;
114 assert (menu != NULL);
125 if (menu->tail == item)
127 if (menu->head == item)
130 switch (item->entry.type)
133 case MenuTerminalAction:
134 free (item->entry.action.str);
137 menu_delete (item->entry.submenu.menu);
140 if (item->name != NULL)
142 if (item->name2 != NULL)
148 * sort command vs. terminal actions and
149 * remove the first character of STR if it's '\0'
152 rxvt_action_type (action_t *action, unsigned char *str)
156 #if defined (DEBUG_MENU) || defined (DEBUG_MENUARROWS)
158 fprintf (stderr, " (len %d) = %s\n", len, str);
160 len = rxvt_Str_escaped ((char *)str);
166 /* sort command vs. terminal actions */
167 action->type = MenuAction;
170 /* the functional equivalent: memmove (str, str+1, len); */
171 unsigned char *dst = (str);
172 unsigned char *src = (str + 1);
173 unsigned char *end = (str + len);
178 len--; /* decrement length */
180 action->type = MenuTerminalAction;
189 rxvt_term::action_dispatch (action_t *action)
191 switch (action->type)
193 case MenuTerminalAction:
194 cmd_write (action->str, action->len);
198 tt_write (action->str, action->len);
208 /* return the arrow index corresponding to NAME */
210 rxvt_menuarrow_find (char name)
214 for (i = 0; i < NARROWS; i++)
215 if (name == Arrows[i].name)
220 /* free the memory associated with arrow NAME of the current menubar */
222 rxvt_term::menuarrow_free (char name)
228 i = rxvt_menuarrow_find (name);
231 action_t *act = & (CurrentBar->arrows[i]);
236 case MenuTerminalAction:
242 act->type = MenuLabel;
247 for (i = 0; i < NARROWS; i++)
248 menuarrow_free (Arrows[i].name);
253 rxvt_term::menuarrow_add (char *string)
268 memset (parse, 0, sizeof (parse));
270 /* fprintf (stderr, "add arrows = `%s'\n", string); */
271 for (p = string; p != NULL && *p; string = p)
274 /* fprintf (stderr, "parsing at %s\n", string); */
285 i = rxvt_menuarrow_find (string[1]);
289 continue; /* not found */
299 p = strchr (string, '\0');
307 p = strchr (next, '<');
310 if (p[1] && p[2] == '>')
316 if (beg.str == NULL) /* no end needed */
317 p = strchr (next, '\0');
326 cur->len = (p - string);
329 #ifdef DEBUG_MENUARROWS
331 fprintf (stderr, "<b> (len %d) = %.*s\n",
332 cur->len, cur->len, (cur->str ? cur->str : ""));
333 for (i = 0; i < NARROWS; i++)
336 fprintf (stderr, "<%c> (len %d) = %.*s\n",
338 cur->len, cur->len, (cur->str ? cur->str : ""));
341 fprintf (stderr, "<e> (len %d) = %.*s\n",
342 cur->len, cur->len, (cur->str ? cur->str : ""));
345 xtra_len = (beg.len + end.len);
346 for (i = 0; i < NARROWS; i++)
348 if (xtra_len || parse[i].len)
349 menuarrow_free (Arrows[i].name);
352 for (i = 0; i < NARROWS; i++)
360 str = (unsigned char *) rxvt_malloc (parse[i].len + xtra_len + 1);
365 strncpy (str + len, beg.str, beg.len);
368 strncpy (str + len, parse[i].str, parse[i].len);
373 strncpy (str + len, end.str, end.len);
378 #ifdef DEBUG_MENUARROWS
379 fprintf (stderr, "<%c> (len %d) = %s\n", Arrows[i].name, len, str);
381 if (rxvt_action_type (& (CurrentBar->arrows[i]), str) < 0)
387 rxvt_menuitem_add (menu_t *menu, const char *name, const char *name2, const char *action)
393 assert (name != NULL);
394 assert (action != NULL);
400 if (isSeparator (name))
402 /* add separator, no action */
409 * add/replace existing menu item
411 item = rxvt_menuitem_find (menu, name);
414 if (item->name2 != NULL && name2 != NULL)
420 switch (item->entry.type)
423 case MenuTerminalAction:
424 free (item->entry.action.str);
425 item->entry.action.str = NULL;
431 /* allocate a new itemect */
432 item = (menuitem_t *) rxvt_malloc (sizeof (menuitem_t));
438 item->name = (char *)rxvt_malloc (len + 1);
439 strcpy (item->name, name);
440 if (name[0] == '.' && name[1] != '.')
441 len = 0; /* hidden menu name */
444 /* add to tail of list */
445 item->prev = menu->tail;
448 if (menu->tail != NULL)
449 (menu->tail)->next = item;
452 if (menu->head == NULL)
459 if (name2 != NULL && item->name2 == NULL)
461 len = strlen (name2);
466 item->name2 = (char *)rxvt_malloc (len + 1);
467 strcpy (item->name2, name2);
471 item->entry.type = MenuLabel;
472 len = strlen (action);
474 if (len == 0 && item->name2 != NULL)
476 action = item->name2;
481 unsigned char *str = (unsigned char *)rxvt_malloc (len + 1);
483 strcpy (str, action);
485 if (rxvt_action_type (& (item->entry.action), str) < 0)
488 /* new item and a possible increase in width */
489 if (menu->width < (item->len + item->len2))
490 menu->width = (item->len + item->len2);
496 * search for the base starting menu for NAME.
497 * return a pointer to the portion of NAME that remains
500 rxvt_term::menu_find_base (menu_t **menu, char *path)
506 assert (menu != NULL);
507 assert (CurrentBar != NULL);
513 if (strchr (path, '/') != NULL)
517 while ((p = strchr (p, '/')) != NULL)
530 while ((p = strchr (path, '/')) != NULL)
536 if (!strcmp (path, DOT))
540 else if (!strcmp (path, DOTS))
543 *menu = (*menu)->parent;
547 path = menu_find_base (menu, path);
550 p[0] = '/'; /* fix-up name again */
559 if (!strcmp (path, DOTS))
561 path += strlen (DOTS);
563 *menu = (*menu)->parent;
570 for (m = CurrentBar->tail; m != NULL; m = m->prev)
571 if (!strcmp (path, m->name))
577 for (item = (*menu)->tail; item != NULL; item = item->prev)
579 if (item->entry.type == MenuSubMenu
580 && !strcmp (path, (item->entry.submenu.menu)->name))
582 m = (item->entry.submenu.menu);
591 path += strlen (path);
598 * delete this entire menu
601 rxvt_term::menu_delete (menu_t *menu)
603 menu_t *parent = NULL, *prev, *next;
607 assert (CurrentBar != NULL);
610 /* delete the entire menu */
614 parent = menu->parent;
627 const int len = (menu->len + HSPACE);
629 if (CurrentBar->tail == menu)
630 CurrentBar->tail = prev;
631 if (CurrentBar->head == menu)
632 CurrentBar->head = next;
634 for (next = menu->next; next != NULL; next = next->next)
639 for (item = parent->tail; item != NULL; item = item->prev)
641 if (item->entry.type == MenuSubMenu
642 && item->entry.submenu.menu == menu)
644 item->entry.submenu.menu = NULL;
645 menuitem_free (menu->parent, item);
654 menuitem_t *p = item->prev;
656 menuitem_free (menu, item);
667 rxvt_term::menu_add (menu_t *parent, char *path)
672 assert (CurrentBar != NULL);
675 if (strchr (path, '/') != NULL)
681 /* shouldn't happen */
685 while ((p = strchr (path, '/')) != NULL)
691 parent = menu_add (parent, path);
695 if (!strcmp (path, DOTS))
696 return (parent != NULL ? parent->parent : parent);
698 if (!strcmp (path, DOT) || path[0] == '\0')
701 /* allocate a new menu */
702 menu = (menu_t *) rxvt_malloc (sizeof (menu_t));
705 menu->parent = parent;
706 menu->len = strlen (path);
707 menu->name = (char *)rxvt_malloc ((menu->len + 1));
708 strcpy (menu->name, path);
710 /* initialize head/tail */
711 menu->head = menu->tail = NULL;
712 menu->prev = menu->next = NULL;
716 menu->x = menu->y = menu->w = menu->h = 0;
719 /* add to tail of list */
722 menu->prev = CurrentBar->tail;
723 if (CurrentBar->tail != NULL)
724 CurrentBar->tail->next = menu;
725 CurrentBar->tail = menu;
726 if (CurrentBar->head == NULL)
727 CurrentBar->head = menu; /* fix head */
729 menu->x = (menu->prev->x + menu->prev->len + HSPACE);
735 item = rxvt_menuitem_add (parent, path, "", "");
742 assert (item->entry.type == MenuLabel);
744 item->entry.type = MenuSubMenu;
745 item->entry.submenu.menu = menu;
752 rxvt_term::drawbox_menubar (int x, int len, int state)
757 len = Width2Pixel (len + HSPACE);
758 if (x >= TermWin.width)
760 else if (x + len >= TermWin.width)
761 len = (TermWin_TotalWidth () - x);
763 #ifdef MENUBAR_SHADOW_IN
771 break; /* SHADOW_OUT */
775 break; /* SHADOW_IN */
777 top = bot = scrollbarGC;
781 rxvt_Draw_Shadow (display->display, menuBar.win, top, bot,
782 x, 0, len, menuBar_TotalHeight ());
786 rxvt_term::drawtriangle (int x, int y, int state)
791 #ifdef MENU_SHADOW_IN
799 break; /* SHADOW_OUT */
803 break; /* SHADOW_IN */
805 top = bot = scrollbarGC;
809 w = Height2Pixel (1) - 2 * SHADOW;
811 x -= SHADOW + (3 * w / 2);
814 rxvt_Draw_Triangle (display->display, ActiveMenu->win, top, bot, x, y, w, 'r');
818 rxvt_term::drawbox_menuitem (int y, int state)
822 #ifdef MENU_SHADOW_IN
830 break; /* SHADOW_OUT */
834 break; /* SHADOW_IN */
836 top = bot = scrollbarGC;
840 rxvt_Draw_Shadow (display->display, ActiveMenu->win, top, bot,
841 SHADOW + 0, SHADOW + y,
842 ActiveMenu->w - 2 * (SHADOW),
843 HEIGHT_TEXT + 2 * SHADOW);
844 XFlush (display->display);
847 #ifdef DEBUG_MENU_LAYOUT
849 rxvt_print_menu_ancestors (menu_t *menu)
853 fprintf (stderr, "Top Level menu\n");
857 fprintf (stderr, "menu %s ", menu->name);
858 if (menu->parent != NULL)
862 for (item = menu->parent->head; item != NULL; item = item->next)
864 if (item->entry.type == MenuSubMenu
865 && item->entry.submenu.menu == menu)
873 fprintf (stderr, "is an orphan!\n");
878 fprintf (stderr, "\n");
879 rxvt_print_menu_ancestors (menu->parent);
883 rxvt_print_menu_descendants (menu_t *menu)
893 parent = parent->parent;
895 while (parent != NULL);
897 for (i = 0; i < level; i++)
898 fprintf (stderr, ">");
899 fprintf (stderr, "%s\n", menu->name);
901 for (item = menu->head; item != NULL; item = item->next)
903 if (item->entry.type == MenuSubMenu)
905 if (item->entry.submenu.menu == NULL)
906 fprintf (stderr, "> %s == NULL\n", item->name);
908 rxvt_print_menu_descendants (item->entry.submenu.menu);
912 for (i = 0; i < level; i++)
913 fprintf (stderr, "+");
914 if (item->entry.type == MenuLabel)
915 fprintf (stderr, "label: ");
916 fprintf (stderr, "%s\n", item->name);
920 for (i = 0; i < level; i++)
921 fprintf (stderr, "<");
922 fprintf (stderr, "\n");
926 /* pop up/down the current menu and redraw the menuBar button */
928 rxvt_term::menu_show ()
933 if (ActiveMenu == NULL)
937 if (ActiveMenu->parent == NULL)
941 drawbox_menubar (x, ActiveMenu->len, -1);
945 ActiveMenu->w = Menu_PixelWidth (ActiveMenu);
947 if ((x + ActiveMenu->w) >= TermWin.width)
948 x = (TermWin_TotalWidth () - ActiveMenu->w);
950 /* find the height */
951 for (h = 0, item = ActiveMenu->head; item != NULL; item = item->next)
952 h += isSeparator (item->name) ? HEIGHT_SEPARATOR
953 : HEIGHT_TEXT + 2 * SHADOW;
954 ActiveMenu->h = h + 2 * SHADOW;
957 if (ActiveMenu->win == None)
959 ActiveMenu->win = XCreateSimpleWindow (display->display, TermWin.vt,
961 ActiveMenu->w, ActiveMenu->h,
963 pix_colors[Color_fg],
964 pix_colors[Color_scroll]);
965 ActiveMenu->drawable = new rxvt_drawable (display, ActiveMenu->win);
966 XMapWindow (display->display, ActiveMenu->win);
969 rxvt_Draw_Shadow (display->display, ActiveMenu->win,
970 topShadowGC, botShadowGC,
971 0, 0, ActiveMenu->w, ActiveMenu->h);
973 /* determine the correct right-alignment */
974 for (xright = 0, item = ActiveMenu->head; item != NULL; item = item->next)
975 if (item->len2 > xright)
978 for (y = 0, item = ActiveMenu->head; item != NULL; item = item->next)
980 const int xoff = (SHADOW + Width2Pixel (HSPACE) / 2);
984 if (isSeparator (item->name))
986 rxvt_Draw_Shadow (display->display, ActiveMenu->win,
987 topShadowGC, botShadowGC,
988 SHADOW, y + SHADOW + 1,
989 ActiveMenu->w - 2 * SHADOW, 0);
990 h = HEIGHT_SEPARATOR;
994 char *name = item->name;
997 if (item->entry.type == MenuLabel)
999 else if (item->entry.type == MenuSubMenu)
1003 menu_t *menu = item->entry.submenu.menu;
1005 drawtriangle (ActiveMenu->w, y, +1);
1010 y1 = ActiveMenu->y + y;
1012 menu->w = Menu_PixelWidth (menu);
1014 /* place sub-menu at midpoint of parent menu */
1015 x1 = ActiveMenu->w / 2;
1016 if (x1 > menu->w) /* right-flush menu if too small */
1017 x1 += (x1 - menu->w);
1020 /* find the height of this submenu */
1021 for (h = 0, it = menu->head; it != NULL; it = it->next)
1022 h += isSeparator (it->name) ? HEIGHT_SEPARATOR
1023 : HEIGHT_TEXT + 2 * SHADOW;
1024 menu->h = h + 2 * SHADOW;
1026 /* ensure menu is in window limits */
1027 if ((x1 + menu->w) >= TermWin.width)
1028 x1 = (TermWin_TotalWidth () - menu->w);
1030 if ((y1 + menu->h) >= TermWin.height)
1031 y1 = (TermWin_TotalHeight () - menu->h);
1033 menu->x = (x1 < 0 ? 0 : x1);
1034 menu->y = (y1 < 0 ? 0 : y1);
1036 else if (item->name2 && !strcmp (name, item->name2))
1040 draw_string (*ActiveMenu->drawable, gc, TermWin.fontset[0],
1041 xoff, 2 * SHADOW + y, name, len);
1047 draw_string (*ActiveMenu->drawable, gc, TermWin.fontset[0],
1048 ActiveMenu->w - (xoff + Width2Pixel (xright)), 2 * SHADOW + y, name, len);
1050 h = HEIGHT_TEXT + 2 * SHADOW;
1057 rxvt_term::menu_display (void (rxvt_term::*update) ())
1059 if (ActiveMenu == NULL)
1062 delete ActiveMenu->drawable;
1063 if (ActiveMenu->win != None)
1064 XDestroyWindow (display->display, ActiveMenu->win);
1065 ActiveMenu->win = None;
1066 ActiveMenu->item = NULL;
1068 if (ActiveMenu->parent == NULL)
1069 drawbox_menubar (ActiveMenu->x, ActiveMenu->len, +1);
1071 ActiveMenu = ActiveMenu->parent;
1076 rxvt_term::menu_hide_all ()
1078 menu_display (&rxvt_term::menu_hide_all);
1082 rxvt_term::menu_hide ()
1084 menu_display (&rxvt_term::menu_show);
1088 rxvt_term::menu_clear (menu_t *menu)
1092 menuitem_t *item = menu->tail;
1094 while (item != NULL)
1096 menuitem_free (menu, item);
1097 /* it didn't get freed ... why? */
1098 if (item == menu->tail)
1107 rxvt_term::menubar_clear ()
1109 if (CurrentBar != NULL)
1111 menu_t *menu = CurrentBar->tail;
1113 while (menu != NULL)
1115 menu_t *prev = menu->prev;
1120 CurrentBar->head = CurrentBar->tail = NULL;
1122 if (CurrentBar->title)
1124 free (CurrentBar->title);
1125 CurrentBar->title = NULL;
1128 menuarrow_free (0); /* remove all arrow functions */
1134 #if (MENUBAR_MAX > 1)
1135 /* find if menu already exists */
1137 rxvt_term::menubar_find (const char *name)
1139 bar_t *bar = CurrentBar;
1141 #ifdef DEBUG_MENUBAR_STACKING
1142 fprintf (stderr, "looking for [menu:%s] ...", name ? name : " (nil)");
1144 if (bar == NULL || name == NULL)
1147 if (strlen (name) && strcmp (name, "*"))
1151 if (!strcmp (bar->name, name))
1153 #ifdef DEBUG_MENUBAR_STACKING
1154 fprintf (stderr, " found!\n");
1160 while (bar != CurrentBar);
1163 #ifdef DEBUG_MENUBAR_STACKING
1164 fprintf (stderr, "%s found!\n", (bar ? "" : " NOT"));
1171 rxvt_term::menubar_push (const char *name)
1176 if (CurrentBar == NULL)
1178 /* allocate first one */
1179 bar = (bar_t *) rxvt_malloc (sizeof (bar_t));
1181 memset (bar, 0, sizeof (bar_t));
1182 /* circular linked-list */
1183 bar->next = bar->prev = bar;
1184 bar->head = bar->tail = NULL;
1193 /* find if menu already exists */
1194 bar = menubar_find (name);
1197 /* found it, use it */
1202 /* create if needed, or reuse the existing empty menubar */
1203 if (CurrentBar->head != NULL)
1205 /* need to malloc another one */
1206 if (Nbars < MENUBAR_MAX)
1207 bar = (bar_t *) rxvt_malloc (sizeof (bar_t));
1211 /* malloc failed or too many menubars, reuse another */
1214 bar = CurrentBar->next;
1219 bar->head = bar->tail = NULL;
1222 bar->next = CurrentBar->next;
1223 CurrentBar->next = bar;
1224 bar->prev = CurrentBar;
1225 bar->next->prev = bar;
1237 /* give menubar this name */
1238 strncpy (CurrentBar->name, name, MAXNAME);
1239 CurrentBar->name[MAXNAME - 1] = '\0';
1244 /* switch to a menu called NAME and remove it */
1246 rxvt_term::menubar_remove (const char *name)
1250 if ((bar = menubar_find (name)) == NULL)
1258 * pop a menubar, clean it up first
1260 if (CurrentBar != NULL)
1262 bar_t *prev = CurrentBar->prev;
1263 bar_t *next = CurrentBar->next;
1265 if (prev == next && prev == CurrentBar)
1268 Nbars = 0; /* safety */
1281 while (CurrentBar && !strcmp (name, "*"));
1285 rxvt_action_decode (FILE *fp, action_t *act)
1290 if (act == NULL || (len = act->len) == 0 || (str = act->str) == NULL)
1293 if (act->type == MenuTerminalAction)
1296 /* can strip trailing ^G from XTerm sequence */
1297 if (str[0] == C0_ESC && str[1] == ']' && str[len - 1] == C0_BEL)
1300 else if (str[0] == C0_ESC)
1309 /* can strip trailing '\r' from M-x sequence */
1310 if (str[len - 1] == '\r')
1315 fprintf (fp, "M-"); /* meta prefix */
1323 * control character form is preferred, since backslash-escaping
1324 * can be really ugly looking when the backslashes themselves also
1325 * have to be escaped to avoid Shell (or whatever scripting
1326 * language) interpretation
1330 unsigned char ch = *str++;
1335 fprintf (fp, "\\E");
1338 fprintf (fp, "\\r");
1339 break; /* carriage-return */
1341 fprintf (fp, "\\\\");
1342 break; /* backslash */
1344 fprintf (fp, "\\^");
1350 fprintf (fp, "^%c", ('@' + ch));
1352 fprintf (fp, "\\%o", ch);
1354 fprintf (fp, "%c", ch);
1365 rxvt_menu_dump (FILE *fp, menu_t *menu)
1369 /* create a new menu and clear it */
1370 fprintf (fp, (menu->parent ? "./%s/*\n" : "/%s/*\n"), menu->name);
1372 for (item = menu->head; item != NULL; item = item->next)
1374 switch (item->entry.type)
1377 if (item->entry.submenu.menu == NULL)
1378 fprintf (fp, "> %s == NULL\n", item->name);
1380 rxvt_menu_dump (fp, item->entry.submenu.menu);
1384 fprintf (fp, "{%s}\n", (strlen (item->name) ? item->name : "-"));
1387 case MenuTerminalAction:
1389 fprintf (fp, "{%s}", item->name);
1390 if (item->name2 != NULL && strlen (item->name2))
1391 fprintf (fp, "{%s}", item->name2);
1393 rxvt_action_decode (fp, & (item->entry.action));
1398 fprintf (fp, (menu->parent ? "../\n" : "/\n\n"));
1402 rxvt_term::menubar_dump (FILE *fp)
1404 bar_t *bar = CurrentBar;
1407 if (bar == NULL || fp == NULL)
1412 "# " RESCLASS " (%s) Pid: %u\n# Date: %s\n\n",
1413 rs[Rs_name], (unsigned int)getpid (), ctime (&t));
1415 /* dump in reverse order */
1416 bar = CurrentBar->prev;
1422 fprintf (fp, "[menu:%s]\n", bar->name);
1424 if (bar->title != NULL)
1425 fprintf (fp, "[title:%s]\n", bar->title);
1427 for (i = 0; i < NARROWS; i++)
1429 switch (bar->arrows[i].type)
1431 case MenuTerminalAction:
1433 fprintf (fp, "<%c>", Arrows[i].name);
1434 rxvt_action_decode (fp, & (bar->arrows[i]));
1440 for (menu = bar->head; menu != NULL; menu = menu->next)
1441 rxvt_menu_dump (fp, menu);
1443 fprintf (fp, "\n[done:%s]\n\n", bar->name);
1446 while (bar != CurrentBar->prev);
1448 #endif /* (MENUBAR_MAX > 1) */
1451 * read in menubar commands from FILENAME
1452 * ignore all input before the tag line [menu] or [menu:???]
1454 * Note that since File_find () is used, FILENAME can be semi-colon
1455 * delimited such that the second part can refer to a tag
1456 * so that a large `database' of menus can be collected together
1459 * FILENAME = "file;"
1460 * read `file' starting with first [menu] or [menu:???] line
1462 * FILENAME = "file;tag"
1463 * read `file' starting with [menu:tag]
1466 rxvt_term::menubar_read (const char *filename)
1468 /* read in a menu from a file */
1471 char *p, *file, *tag = NULL;
1473 file = (char *)rxvt_File_find (filename, ".menu", rs[Rs_path]);
1477 fp = fopen (file, "rb");
1482 #if (MENUBAR_MAX > 1)
1483 /* semi-colon delimited */
1484 if ((tag = strchr (filename, ';')) != NULL)
1490 #endif /* (MENUBAR_MAX > 1) */
1492 fprintf (stderr, "[read:%s]\n", p);
1494 fprintf (stderr, "looking for [menu:%s]\n", tag);
1497 while ((p = fgets (buffer, sizeof (buffer), fp)) != NULL)
1501 if ((n = rxvt_Str_match (p, "[menu")) != 0)
1505 /* looking for [menu:tag] */
1506 if (p[n] == ':' && p[n + 1] != ']')
1509 n += rxvt_Str_match (p + n, tag);
1513 fprintf (stderr, "[menu:%s]\n", tag);
1519 else if (p[n] == ':' || p[n] == ']')
1524 /* found [menu], [menu:???] tag */
1530 fprintf (stderr, "read line = %s\n", p);
1533 /* looking for [done:tag] or [done:] */
1534 if ((n = rxvt_Str_match (p, "[done")) != 0)
1541 else if (p[n] == ':')
1551 n += rxvt_Str_match (p + n, tag);
1555 fprintf (stderr, "[done:%s]\n", tag);
1563 /* what? ... skip this line */
1564 p[0] = COMMENT_CHAR;
1570 * remove leading/trailing space
1571 * skip blank or comment lines
1574 if (*p && *p != '#')
1576 menu_readonly = 0; /* if case we read another file */
1577 menubar_dispatch (p);
1579 /* get another line */
1580 p = fgets (buffer, sizeof (buffer), fp);
1587 * user interface for building/deleting and otherwise managing menus
1590 rxvt_term::menubar_dispatch (char *str)
1593 char *path, *name, *name2;
1595 if (menubar_visible () && ActiveMenu != NULL)
1604 case '/': /* absolute & relative path */
1605 case MENUITEM_BEG: /* menuitem */
1606 /* add `+' prefix for these cases */
1612 str++; /* skip cmd character */
1616 #if (MENUBAR_MAX > 1)
1617 if (CurrentBar == NULL)
1619 #endif /* (MENUBAR_MAX > 1) */
1620 if (str[1] && str[2] == '>') /* arrow commands */
1621 menuarrow_add (str);
1624 case '[': /* extended command */
1625 while (str[0] == '[')
1627 char *next = (++str); /* skip leading '[' */
1634 if ((next = strchr (next, ':')) == NULL)
1635 return; /* parse error */
1637 while (next[1] != ']');
1638 /* remove and skip ':]' */
1644 if ((next = strchr (next, ']')) == NULL)
1645 return; /* parse error */
1646 /* remove and skip ']' */
1655 /* try and dispatch it, regardless of read/write status */
1656 saved = menu_readonly;
1658 menubar_dispatch (str + 1);
1659 menu_readonly = saved;
1661 /* these ones don't require menu stacking */
1662 else if (!strcmp (str, "clear"))
1666 else if (!strcmp (str, "done") || rxvt_Str_match (str, "done:"))
1670 else if (!strcmp (str, "show"))
1675 else if (!strcmp (str, "hide"))
1680 else if ((n = rxvt_Str_match (str, "read:")) != 0)
1682 /* read in a menu from a file */
1686 else if ((n = rxvt_Str_match (str, "title:")) != 0)
1689 if (CurrentBar != NULL && !menu_readonly)
1693 name = (char *)rxvt_realloc (CurrentBar->title, strlen (str) + 1);
1697 CurrentBar->title = name;
1703 free (CurrentBar->title);
1704 CurrentBar->title = NULL;
1708 else if ((n = rxvt_Str_match (str, "pixmap:")) != 0)
1711 process_xterm_seq (XTerm_Pixmap, str, CHAR_ST);
1713 #if (MENUBAR_MAX > 1)
1714 else if ((n = rxvt_Str_match (str, "rm")) != 0)
1725 menubar_remove (str);
1730 else if ((n = rxvt_Str_match (str, "menu")) != 0)
1737 /* add/access menuBar */
1738 if (*str != '\0' && *str != '*')
1742 if (CurrentBar == NULL)
1744 menubar_push ("default");
1748 if (CurrentBar != NULL)
1749 menu_readonly = 0; /* allow menu build commands */
1751 else if (!strcmp (str, "dump"))
1753 /* dump current menubars to a file */
1756 /* enough space to hold the results */
1759 sprintf (buffer, "/tmp/" RESCLASS "-%u",
1760 (unsigned int)getpid ());
1762 if ((fp = fopen (buffer, "wb")) != NULL)
1764 process_xterm_seq (XTerm_title, buffer, CHAR_ST);
1769 else if (!strcmp (str, "next"))
1773 CurrentBar = CurrentBar->next;
1777 else if (!strcmp (str, "prev"))
1781 CurrentBar = CurrentBar->prev;
1785 else if (!strcmp (str, "swap"))
1787 /* swap the top 2 menus */
1790 bar_t *cbprev = CurrentBar->prev;
1791 bar_t *cbnext = CurrentBar->next;
1793 cbprev->next = cbnext;
1794 cbnext->prev = cbprev;
1796 CurrentBar->next = cbprev;
1797 CurrentBar->prev = cbprev->prev;
1799 cbprev->prev->next = CurrentBar;
1800 cbprev->prev = CurrentBar;
1802 CurrentBar = cbprev;
1806 #endif /* (MENUBAR_MAX > 1) */
1809 BuildMenu = ActiveMenu = NULL;
1811 #ifdef DEBUG_MENUBAR_STACKING
1812 fprintf (stderr, "menus are read%s\n",
1813 menu_readonly ? "only" : "/write");
1821 #if (MENUBAR_MAX > 1)
1822 if (CurrentBar == NULL)
1826 #ifdef DEBUG_MENUBAR_STACKING
1827 fprintf (stderr, "menus are read%s\n",
1828 menu_readonly ? "only" : "/write");
1832 #endif /* (MENUBAR_MAX > 1) */
1841 /* parse STR, allow spaces inside (name) */
1842 if (path[0] != '\0')
1844 name = strchr (path, MENUITEM_BEG);
1845 str = strchr (path, MENUITEM_END);
1846 if (name != NULL || str != NULL)
1848 if (name == NULL || str == NULL || str <= (name + 1)
1849 || (name > path && name[-1] != '/'))
1851 rxvt_warn ("menu error A<%s>, continuing.\n", path);
1854 if (str[1] == MENUITEM_BEG)
1857 str = strchr (name2, MENUITEM_END);
1861 rxvt_warn ("menu error B<%s>, continuing.\n", path);
1864 name2[-2] = '\0'; /* remove prev MENUITEM_END */
1866 if (name > path && name[-1] == '/')
1869 *name++ = '\0'; /* delimit */
1870 *str++ = '\0'; /* delimit */
1872 while (isspace (*str))
1873 str++; /* skip space */
1877 "`%c' path = <%s>, name = <%s>, name2 = <%s>, action = <%s>\n",
1878 cmd, (path ? path : " (nil)"), (name ? name : " (nil)"),
1879 (name2 ? name2 : " (nil)"), (str ? str : " (nil)")
1884 /* process the different commands */
1887 case '+': /* add/replace existing menu or menuitem */
1888 if (path[0] != '\0')
1892 path = menu_find_base (& (BuildMenu), path);
1893 len = strlen (path);
1895 /* don't allow menus called `*' */
1898 menu_clear (BuildMenu);
1901 else if (len >= 2 && !strcmp ((path + len - 2), "/*"))
1903 path[len - 2] = '\0';
1905 if (path[0] != '\0')
1906 BuildMenu = menu_add (BuildMenu, path);
1908 if (name != NULL && name[0] != '\0')
1909 rxvt_menuitem_add (BuildMenu,
1910 (strcmp (name, SEPARATOR_NAME) ? name : ""),
1914 case '-': /* delete menu entry */
1915 if (!strcmp (path, "/*") && (name == NULL || name[0] == '\0'))
1922 else if (path[0] != '\0')
1925 menu_t *menu = BuildMenu;
1927 path = menu_find_base (&menu, path);
1928 len = strlen (path);
1930 /* submenu called `*' clears all menu items */
1936 else if (len >= 2 && !strcmp (&path[len - 2], "/*"))
1941 else if (path[0] != '\0')
1949 if (BuildMenu != NULL)
1951 if (name == NULL || name[0] == '\0')
1952 BuildMenu = menu_delete (BuildMenu);
1957 menu_t *BuildMenu = BuildMenu;
1959 n1 = strcmp (name, SEPARATOR_NAME) ? name : "";
1960 item = rxvt_menuitem_find (BuildMenu, n1);
1961 if (item != NULL && item->entry.type != MenuSubMenu)
1963 menuitem_free (BuildMenu, item);
1965 /* fix up the width */
1966 BuildMenu->width = 0;
1967 for (item = BuildMenu->head; item != NULL;
1970 short l = item->len + item->len2;
1972 MAX_IT (BuildMenu->width, l);
1985 rxvt_term::draw_Arrows (int name, int state)
1991 #ifdef MENU_SHADOW_IN
1999 break; /* SHADOW_OUT */
2003 break; /* SHADOW_IN */
2005 top = bot = scrollbarGC;
2006 break; /* neutral */
2012 for (i = 0; i < NARROWS; i++)
2014 const int w = Width2Pixel (1);
2015 const int y = (menuBar_TotalHeight () - w) / 2;
2016 int x = Arrows_x + (5 * Width2Pixel (i)) / 4;
2018 if (!name || name == Arrows[i].name)
2019 rxvt_Draw_Triangle (display->display, menuBar.win, top, bot, x, y, w,
2022 XFlush (display->display);
2026 rxvt_term::menubar_expose ()
2031 if (!menubar_visible () || menuBar.win == 0)
2034 if (menubarGC == None)
2036 /* Create the graphics context */
2039 gcvalue.foreground = (display->depth <= 2 ? pix_colors[Color_fg]
2040 : pix_colors[Color_Black]);
2041 menubarGC = XCreateGC (display->display, menuBar.win,
2042 GCForeground, &gcvalue);
2045 /* make sure the font is correct */
2046 XClearWindow (display->display, menuBar.win);
2051 if (CurrentBar != NULL)
2053 for (menu = CurrentBar->head; menu != NULL; menu = menu->next)
2055 int len = menu->len;
2057 x = (menu->x + menu->len + HSPACE);
2059 #ifdef DEBUG_MENU_LAYOUT
2060 rxvt_print_menu_descendants (menu);
2063 if (x >= TermWin.ncol)
2064 len = (TermWin.ncol - (menu->x + HSPACE));
2066 drawbox_menubar (menu->x, len, +1);
2067 draw_string (*menuBar.drawable, menubarGC, TermWin.fontset[0],
2068 (Width2Pixel (menu->x) + Width2Pixel (HSPACE) / 2),
2069 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)
2130 draw_string (*menuBar.drawable, menubarGC, TermWin.fontset[0],
2131 Width2Pixel (x) + Width2Pixel (ncol + HSPACE) / 2,
2132 SHADOW, title, len);
2137 rxvt_term::menubar_mapping (int map)
2141 if (map && !menubar_visible ())
2144 if (menuBar.win == 0)
2146 XMapWindow (display->display, menuBar.win);
2149 else if (!map && menubar_visible ())
2153 XUnmapWindow (display->display, menuBar.win);
2163 rxvt_term::menu_select (XButtonEvent &ev)
2165 menuitem_t *thisitem, *item = NULL;
2168 Window unused_root, unused_child;
2169 int unused_root_x, unused_root_y;
2170 unsigned int unused_mask;
2172 if (ActiveMenu == NULL)
2175 XQueryPointer (display->display, ActiveMenu->win,
2176 &unused_root, &unused_child,
2177 &unused_root_x, &unused_root_y,
2178 &ev.x, &ev.y, &unused_mask);
2180 if (ActiveMenu->parent != NULL && (ev.x < 0 || ev.y < 0))
2186 /* determine the menu item corresponding to the Y index */
2188 if (ev.x >= 0 && ev.x <= (ActiveMenu->w - SHADOW))
2190 for (item = ActiveMenu->head; item != NULL; item = item->next)
2192 int h = HEIGHT_TEXT + 2 * SHADOW;
2194 if (isSeparator (item->name))
2195 h = HEIGHT_SEPARATOR;
2196 else if (ev.y >= y && ev.y < (y + h))
2203 if (item == NULL && ev.type == ButtonRelease)
2210 this_y = y - SHADOW;
2212 /* erase the last item */
2213 if (ActiveMenu->item != NULL)
2215 if (ActiveMenu->item != thisitem)
2217 for (y = 0, item = ActiveMenu->head; item != NULL; item = item->next)
2221 if (isSeparator (item->name))
2222 h = HEIGHT_SEPARATOR;
2223 else if (item == ActiveMenu->item)
2225 /* erase old menuitem */
2226 drawbox_menuitem (y, 0); /* No Shadow */
2227 if (item->entry.type == MenuSubMenu)
2228 drawtriangle (ActiveMenu->w, y, +1);
2233 h = HEIGHT_TEXT + 2 * SHADOW;
2243 switch (item->entry.type)
2251 case MenuTerminalAction:
2252 drawbox_menuitem (this_y, -1);
2253 #ifdef HAVE_NANOSLEEP
2254 struct timespec rqt;
2257 rqt.tv_nsec = MENU_DELAY_USEC * 1000;
2258 nanosleep (&rqt, NULL);
2260 /* use select for timing */
2264 tv.tv_usec = MENU_DELAY_USEC;
2265 select (0, NULL, NULL, NULL, &tv);
2267 /* remove menu before sending keys to the application */
2270 action_dispatch (& (item->entry.action));
2271 #else /* DEBUG_MENU */
2272 fprintf (stderr, "%s: %s\n", item->name,
2273 item->entry.action.str);
2274 #endif /* DEBUG_MENU */
2280 if (item->entry.type == MenuSubMenu)
2289 ActiveMenu->item = thisitem;
2292 if (thisitem != NULL)
2294 item = ActiveMenu->item;
2295 if (item->entry.type != MenuLabel)
2296 drawbox_menuitem (y, +1);
2298 if (item->entry.type == MenuSubMenu)
2302 drawtriangle (ActiveMenu->w, y, -1);
2304 x = ev.x + (ActiveMenu->parent
2306 : Width2Pixel (ActiveMenu->x));
2308 if (x >= item->entry.submenu.menu->x)
2310 ActiveMenu = item->entry.submenu.menu;
2320 rxvt_term::menubar_select (XButtonEvent &ev)
2322 menu_t *menu = NULL;
2324 /* determine the pulldown menu corresponding to the X index */
2325 if (ev.y >= 0 && ev.y <= menuBar_height () && CurrentBar != NULL)
2327 for (menu = CurrentBar->head; menu != NULL; menu = menu->next)
2329 int x = Width2Pixel (menu->x);
2330 int w = Width2Pixel (menu->len + HSPACE);
2332 if ((ev.x >= x && ev.x < x + w))
2343 if (menu == NULL && Arrows_x && ev.x >= Arrows_x)
2347 for (i = 0; i < NARROWS; i++)
2349 if (ev.x >= (Arrows_x + (Width2Pixel (4 * i + i)) / 4)
2351 + (Width2Pixel (4 * i + i + 4)) / 4))
2353 draw_Arrows (Arrows[i].name, -1);
2355 #ifdef HAVE_NANOSLEEP
2356 struct timespec rqt;
2359 rqt.tv_nsec = MENU_DELAY_USEC * 1000;
2360 nanosleep (&rqt, NULL);
2362 /* use select for timing */
2366 tv.tv_usec = MENU_DELAY_USEC;
2367 select (0, NULL, NULL, NULL, &tv);
2371 draw_Arrows (Arrows[i].name, +1);
2372 #ifdef DEBUG_MENUARROWS
2373 fprintf (stderr, "'%c': ", Arrows[i].name);
2375 if (CurrentBar == NULL
2376 || (CurrentBar->arrows[i].type != MenuAction
2377 && CurrentBar->arrows[i].type !=
2378 MenuTerminalAction))
2380 if (Arrows[i].str != NULL && Arrows[i].str[0])
2381 fprintf (stderr, " (default) \\033%s\n",
2382 & (Arrows[i].str[2]));
2386 fprintf (stderr, "%s\n",
2387 CurrentBar->arrows[i].str);
2389 #else /* DEBUG_MENUARROWS */
2390 if (CurrentBar == NULL || action_dispatch (&CurrentBar->arrows[i]))
2392 if (Arrows[i].str != NULL && Arrows[i].str[0] != 0)
2393 tt_write ((Arrows[i].str + 1),
2396 #endif /* DEBUG_MENUARROWS */
2405 * press menubar or move to a new entry
2407 if (menu != NULL && menu != ActiveMenu)
2409 menu_hide_all (); /* pop down old menu */
2411 menu_show (); /* pop up new menu */
2418 * general dispatch routine,
2419 * it would be nice to have `sticky' menus
2422 rxvt_term::menubar_control (XButtonEvent &ev)
2427 if (ev.button == Button1)
2428 menubar_select (ev);
2432 if (ev.button == Button1)
2437 while (XCheckTypedWindowEvent (display->display, TermWin.parent[0],
2438 MotionNotify, (XEvent *)&ev)) ;
2441 while (menu_select (ev)) ;
2446 Window unused_root, unused_child;
2447 int unused_root_x, unused_root_y;
2448 unsigned int unused_mask;
2450 XQueryPointer (display->display, menuBar.win,
2451 &unused_root, &unused_child,
2452 &unused_root_x, &unused_root_y,
2453 &ev.x, &ev.y, &unused_mask);
2454 menubar_select (ev);
2461 rxvt_term::map_menuBar (int map)
2463 if (menubar_mapping (map))
2464 resize_all_windows (0, 0, 0);
2467 /*----------------------- end-of-file (C source) -----------------------*/