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 */
30 #include "rxvt.h" /* NECESSARY */
34 #include "menubar.intpro" /* PROTOS for internal routines */
36 #define Menu_PixelWidth(menu) \
37 (2 * SHADOW + Width2Pixel ((menu)->width + 3 * HSPACE))
41 const char name; /* (l)eft, (u)p, (d)own, (r)ight */
42 const unsigned char str[5]; /* str[0] = STRLEN (str+1) */
45 { 'l', "\003\033[D" },
46 { 'u', "\003\033[A" },
47 { 'd', "\003\033[B" },
54 draw_string (rxvt_drawable &d, GC gc, rxvt_fontset *fs, int x, int y, char *str, int len)
61 int l = mbrtowc (&w, str, len, mbs);
69 rxvt_font *font = (*fs)[fs->find_font (w)];
71 font->draw (d, x, y, &ch, 1, Color_bg, Color_scroll);
73 x += font->width * wcwidth (w);
78 * find an item called NAME in MENU
81 rxvt_menuitem_find (const menu_t *menu, const char *name)
86 assert (name != NULL);
87 assert (menu != NULL);
90 /* find the last item in the menu, this is good for separators */
91 for (item = menu->tail; item != NULL; item = item->prev)
93 if (item->entry.type == MenuSubMenu)
95 if (!STRCMP (name, (item->entry.submenu.menu)->name))
98 else if ((isSeparator (name) && isSeparator (item->name))
99 || !STRCMP (name, item->name))
106 * unlink ITEM from its MENU and free its memory
109 rxvt_term::menuitem_free (menu_t *menu, menuitem_t *item)
112 menuitem_t *prev, *next;
115 assert (menu != NULL);
126 if (menu->tail == item)
128 if (menu->head == item)
131 switch (item->entry.type)
134 case MenuTerminalAction:
135 free (item->entry.action.str);
138 menu_delete (item->entry.submenu.menu);
141 if (item->name != NULL)
143 if (item->name2 != NULL)
149 * sort command vs. terminal actions and
150 * remove the first character of STR if it's '\0'
153 rxvt_action_type (action_t *action, unsigned char *str)
157 #if defined (DEBUG_MENU) || defined (DEBUG_MENUARROWS)
159 fprintf (stderr, " (len %d) = %s\n", len, str);
161 len = rxvt_Str_escaped ((char *)str);
167 /* sort command vs. terminal actions */
168 action->type = MenuAction;
171 /* the functional equivalent: memmove (str, str+1, len); */
172 unsigned char *dst = (str);
173 unsigned char *src = (str + 1);
174 unsigned char *end = (str + len);
179 len--; /* decrement length */
181 action->type = MenuTerminalAction;
190 rxvt_term::action_dispatch (action_t *action)
192 switch (action->type)
194 case MenuTerminalAction:
195 cmd_write (action->str, action->len);
199 tt_write (action->str, action->len);
209 /* return the arrow index corresponding to NAME */
211 rxvt_menuarrow_find (char name)
215 for (i = 0; i < NARROWS; i++)
216 if (name == Arrows[i].name)
221 /* free the memory associated with arrow NAME of the current menubar */
223 rxvt_term::menuarrow_free (char name)
229 i = rxvt_menuarrow_find (name);
232 action_t *act = & (CurrentBar->arrows[i]);
237 case MenuTerminalAction:
243 act->type = MenuLabel;
248 for (i = 0; i < NARROWS; i++)
249 menuarrow_free (Arrows[i].name);
254 rxvt_term::menuarrow_add (char *string)
269 MEMSET (parse, 0, sizeof (parse));
271 /* fprintf (stderr, "add arrows = `%s'\n", string); */
272 for (p = string; p != NULL && *p; string = p)
275 /* fprintf (stderr, "parsing at %s\n", string); */
286 i = rxvt_menuarrow_find (string[1]);
290 continue; /* not found */
300 p = STRCHR (string, '\0');
308 p = STRCHR (next, '<');
311 if (p[1] && p[2] == '>')
317 if (beg.str == NULL) /* no end needed */
318 p = STRCHR (next, '\0');
327 cur->len = (p - string);
330 #ifdef DEBUG_MENUARROWS
332 fprintf (stderr, "<b> (len %d) = %.*s\n",
333 cur->len, cur->len, (cur->str ? cur->str : ""));
334 for (i = 0; i < NARROWS; i++)
337 fprintf (stderr, "<%c> (len %d) = %.*s\n",
339 cur->len, cur->len, (cur->str ? cur->str : ""));
342 fprintf (stderr, "<e> (len %d) = %.*s\n",
343 cur->len, cur->len, (cur->str ? cur->str : ""));
346 xtra_len = (beg.len + end.len);
347 for (i = 0; i < NARROWS; i++)
349 if (xtra_len || parse[i].len)
350 menuarrow_free (Arrows[i].name);
353 for (i = 0; i < NARROWS; i++)
361 str = (unsigned char *) rxvt_malloc (parse[i].len + xtra_len + 1);
366 STRNCPY (str + len, beg.str, beg.len);
369 STRNCPY (str + len, parse[i].str, parse[i].len);
374 STRNCPY (str + len, end.str, end.len);
379 #ifdef DEBUG_MENUARROWS
380 fprintf (stderr, "<%c> (len %d) = %s\n", Arrows[i].name, len, str);
382 if (rxvt_action_type (& (CurrentBar->arrows[i]), str) < 0)
388 rxvt_menuitem_add (menu_t *menu, const char *name, const char *name2, const char *action)
394 assert (name != NULL);
395 assert (action != NULL);
401 if (isSeparator (name))
403 /* add separator, no action */
410 * add/replace existing menu item
412 item = rxvt_menuitem_find (menu, name);
415 if (item->name2 != NULL && name2 != NULL)
421 switch (item->entry.type)
424 case MenuTerminalAction:
425 free (item->entry.action.str);
426 item->entry.action.str = NULL;
432 /* allocate a new itemect */
433 item = (menuitem_t *) rxvt_malloc (sizeof (menuitem_t));
439 item->name = (char *)rxvt_malloc (len + 1);
440 STRCPY (item->name, name);
441 if (name[0] == '.' && name[1] != '.')
442 len = 0; /* hidden menu name */
445 /* add to tail of list */
446 item->prev = menu->tail;
449 if (menu->tail != NULL)
450 (menu->tail)->next = item;
453 if (menu->head == NULL)
460 if (name2 != NULL && item->name2 == NULL)
462 len = STRLEN (name2);
467 item->name2 = (char *)rxvt_malloc (len + 1);
468 STRCPY (item->name2, name2);
472 item->entry.type = MenuLabel;
473 len = STRLEN (action);
475 if (len == 0 && item->name2 != NULL)
477 action = item->name2;
482 unsigned char *str = (unsigned char *)rxvt_malloc (len + 1);
484 STRCPY (str, action);
486 if (rxvt_action_type (& (item->entry.action), str) < 0)
489 /* new item and a possible increase in width */
490 if (menu->width < (item->len + item->len2))
491 menu->width = (item->len + item->len2);
497 * search for the base starting menu for NAME.
498 * return a pointer to the portion of NAME that remains
501 rxvt_term::menu_find_base (menu_t **menu, char *path)
507 assert (menu != NULL);
508 assert (CurrentBar != NULL);
514 if (STRCHR (path, '/') != NULL)
518 while ((p = STRCHR (p, '/')) != NULL)
531 while ((p = STRCHR (path, '/')) != NULL)
537 if (!STRCMP (path, DOT))
541 else if (!STRCMP (path, DOTS))
544 *menu = (*menu)->parent;
548 path = menu_find_base (menu, path);
551 p[0] = '/'; /* fix-up name again */
560 if (!STRCMP (path, DOTS))
562 path += STRLEN (DOTS);
564 *menu = (*menu)->parent;
571 for (m = CurrentBar->tail; m != NULL; m = m->prev)
572 if (!STRCMP (path, m->name))
578 for (item = (*menu)->tail; item != NULL; item = item->prev)
580 if (item->entry.type == MenuSubMenu
581 && !STRCMP (path, (item->entry.submenu.menu)->name))
583 m = (item->entry.submenu.menu);
592 path += STRLEN (path);
599 * delete this entire menu
602 rxvt_term::menu_delete (menu_t *menu)
604 menu_t *parent = NULL, *prev, *next;
608 assert (CurrentBar != NULL);
611 /* delete the entire menu */
615 parent = menu->parent;
628 const int len = (menu->len + HSPACE);
630 if (CurrentBar->tail == menu)
631 CurrentBar->tail = prev;
632 if (CurrentBar->head == menu)
633 CurrentBar->head = next;
635 for (next = menu->next; next != NULL; next = next->next)
640 for (item = parent->tail; item != NULL; item = item->prev)
642 if (item->entry.type == MenuSubMenu
643 && item->entry.submenu.menu == menu)
645 item->entry.submenu.menu = NULL;
646 menuitem_free (menu->parent, item);
655 menuitem_t *p = item->prev;
657 menuitem_free (menu, item);
668 rxvt_term::menu_add (menu_t *parent, char *path)
673 assert (CurrentBar != NULL);
676 if (STRCHR (path, '/') != NULL)
682 /* shouldn't happen */
686 while ((p = STRCHR (path, '/')) != NULL)
692 parent = menu_add (parent, path);
696 if (!STRCMP (path, DOTS))
697 return (parent != NULL ? parent->parent : parent);
699 if (!STRCMP (path, DOT) || path[0] == '\0')
702 /* allocate a new menu */
703 menu = (menu_t *) rxvt_malloc (sizeof (menu_t));
706 menu->parent = parent;
707 menu->len = STRLEN (path);
708 menu->name = (char *)rxvt_malloc ((menu->len + 1));
709 STRCPY (menu->name, path);
711 /* initialize head/tail */
712 menu->head = menu->tail = NULL;
713 menu->prev = menu->next = NULL;
717 menu->x = menu->y = menu->w = menu->h = 0;
720 /* add to tail of list */
723 menu->prev = CurrentBar->tail;
724 if (CurrentBar->tail != NULL)
725 CurrentBar->tail->next = menu;
726 CurrentBar->tail = menu;
727 if (CurrentBar->head == NULL)
728 CurrentBar->head = menu; /* fix head */
730 menu->x = (menu->prev->x + menu->prev->len + HSPACE);
736 item = rxvt_menuitem_add (parent, path, "", "");
743 assert (item->entry.type == MenuLabel);
745 item->entry.type = MenuSubMenu;
746 item->entry.submenu.menu = menu;
753 rxvt_term::drawbox_menubar (int x, int len, int state)
758 len = Width2Pixel (len + HSPACE);
759 if (x >= TermWin.width)
761 else if (x + len >= TermWin.width)
762 len = (TermWin_TotalWidth () - x);
764 #ifdef MENUBAR_SHADOW_IN
772 break; /* SHADOW_OUT */
776 break; /* SHADOW_IN */
778 top = bot = scrollbarGC;
782 rxvt_Draw_Shadow (display->display, menuBar.win, top, bot,
783 x, 0, len, menuBar_TotalHeight ());
787 rxvt_term::drawtriangle (int x, int y, int state)
792 #ifdef MENU_SHADOW_IN
800 break; /* SHADOW_OUT */
804 break; /* SHADOW_IN */
806 top = bot = scrollbarGC;
810 w = Height2Pixel (1) - 2 * SHADOW;
812 x -= SHADOW + (3 * w / 2);
815 rxvt_Draw_Triangle (display->display, ActiveMenu->win, top, bot, x, y, w, 'r');
819 rxvt_term::drawbox_menuitem (int y, int state)
823 #ifdef MENU_SHADOW_IN
831 break; /* SHADOW_OUT */
835 break; /* SHADOW_IN */
837 top = bot = scrollbarGC;
841 rxvt_Draw_Shadow (display->display, ActiveMenu->win, top, bot,
842 SHADOW + 0, SHADOW + y,
843 ActiveMenu->w - 2 * (SHADOW),
844 HEIGHT_TEXT + 2 * SHADOW);
845 XFlush (display->display);
848 #ifdef DEBUG_MENU_LAYOUT
850 rxvt_print_menu_ancestors (menu_t *menu)
854 fprintf (stderr, "Top Level menu\n");
858 fprintf (stderr, "menu %s ", menu->name);
859 if (menu->parent != NULL)
863 for (item = menu->parent->head; item != NULL; item = item->next)
865 if (item->entry.type == MenuSubMenu
866 && item->entry.submenu.menu == menu)
874 fprintf (stderr, "is an orphan!\n");
879 fprintf (stderr, "\n");
880 rxvt_print_menu_ancestors (menu->parent);
884 rxvt_print_menu_descendants (menu_t *menu)
894 parent = parent->parent;
896 while (parent != NULL);
898 for (i = 0; i < level; i++)
899 fprintf (stderr, ">");
900 fprintf (stderr, "%s\n", menu->name);
902 for (item = menu->head; item != NULL; item = item->next)
904 if (item->entry.type == MenuSubMenu)
906 if (item->entry.submenu.menu == NULL)
907 fprintf (stderr, "> %s == NULL\n", item->name);
909 rxvt_print_menu_descendants (item->entry.submenu.menu);
913 for (i = 0; i < level; i++)
914 fprintf (stderr, "+");
915 if (item->entry.type == MenuLabel)
916 fprintf (stderr, "label: ");
917 fprintf (stderr, "%s\n", item->name);
921 for (i = 0; i < level; i++)
922 fprintf (stderr, "<");
923 fprintf (stderr, "\n");
927 /* pop up/down the current menu and redraw the menuBar button */
929 rxvt_term::menu_show ()
934 if (ActiveMenu == NULL)
938 if (ActiveMenu->parent == NULL)
942 drawbox_menubar (x, ActiveMenu->len, -1);
946 ActiveMenu->w = Menu_PixelWidth (ActiveMenu);
948 if ((x + ActiveMenu->w) >= TermWin.width)
949 x = (TermWin_TotalWidth () - ActiveMenu->w);
951 /* find the height */
952 for (h = 0, item = ActiveMenu->head; item != NULL; item = item->next)
953 h += isSeparator (item->name) ? HEIGHT_SEPARATOR
954 : HEIGHT_TEXT + 2 * SHADOW;
955 ActiveMenu->h = h + 2 * SHADOW;
958 if (ActiveMenu->win == None)
960 ActiveMenu->win = XCreateSimpleWindow (display->display, TermWin.vt,
962 ActiveMenu->w, ActiveMenu->h,
965 PixColors[Color_scroll]);
966 ActiveMenu->drawable = new rxvt_drawable (display, ActiveMenu->win);
967 XMapWindow (display->display, ActiveMenu->win);
970 rxvt_Draw_Shadow (display->display, ActiveMenu->win,
971 topShadowGC, botShadowGC,
972 0, 0, ActiveMenu->w, ActiveMenu->h);
974 /* determine the correct right-alignment */
975 for (xright = 0, item = ActiveMenu->head; item != NULL; item = item->next)
976 if (item->len2 > xright)
979 for (y = 0, item = ActiveMenu->head; item != NULL; item = item->next)
981 const int xoff = (SHADOW + Width2Pixel (HSPACE) / 2);
985 if (isSeparator (item->name))
987 rxvt_Draw_Shadow (display->display, ActiveMenu->win,
988 topShadowGC, botShadowGC,
989 SHADOW, y + SHADOW + 1,
990 ActiveMenu->w - 2 * SHADOW, 0);
991 h = HEIGHT_SEPARATOR;
995 char *name = item->name;
998 if (item->entry.type == MenuLabel)
1000 else if (item->entry.type == MenuSubMenu)
1004 menu_t *menu = item->entry.submenu.menu;
1006 drawtriangle (ActiveMenu->w, y, +1);
1011 y1 = ActiveMenu->y + y;
1013 menu->w = Menu_PixelWidth (menu);
1015 /* place sub-menu at midpoint of parent menu */
1016 x1 = ActiveMenu->w / 2;
1017 if (x1 > menu->w) /* right-flush menu if too small */
1018 x1 += (x1 - menu->w);
1021 /* find the height of this submenu */
1022 for (h = 0, it = menu->head; it != NULL; it = it->next)
1023 h += isSeparator (it->name) ? HEIGHT_SEPARATOR
1024 : HEIGHT_TEXT + 2 * SHADOW;
1025 menu->h = h + 2 * SHADOW;
1027 /* ensure menu is in window limits */
1028 if ((x1 + menu->w) >= TermWin.width)
1029 x1 = (TermWin_TotalWidth () - menu->w);
1031 if ((y1 + menu->h) >= TermWin.height)
1032 y1 = (TermWin_TotalHeight () - menu->h);
1034 menu->x = (x1 < 0 ? 0 : x1);
1035 menu->y = (y1 < 0 ? 0 : y1);
1037 else if (item->name2 && !STRCMP (name, item->name2))
1041 draw_string (*ActiveMenu->drawable, gc, TermWin.fontset,
1042 xoff, 2 * SHADOW + y, name, len);
1048 draw_string (*ActiveMenu->drawable, gc, TermWin.fontset,
1049 ActiveMenu->w - (xoff + Width2Pixel (xright)), 2 * SHADOW + y, name, len);
1051 h = HEIGHT_TEXT + 2 * SHADOW;
1058 rxvt_term::menu_display (void (rxvt_term::*update) ())
1060 if (ActiveMenu == NULL)
1063 delete ActiveMenu->drawable;
1064 if (ActiveMenu->win != None)
1065 XDestroyWindow (display->display, ActiveMenu->win);
1066 ActiveMenu->win = None;
1067 ActiveMenu->item = NULL;
1069 if (ActiveMenu->parent == NULL)
1070 drawbox_menubar (ActiveMenu->x, ActiveMenu->len, +1);
1072 ActiveMenu = ActiveMenu->parent;
1077 rxvt_term::menu_hide_all ()
1079 menu_display (&rxvt_term::menu_hide_all);
1083 rxvt_term::menu_hide ()
1085 menu_display (&rxvt_term::menu_show);
1089 rxvt_term::menu_clear (menu_t *menu)
1093 menuitem_t *item = menu->tail;
1095 while (item != NULL)
1097 menuitem_free (menu, item);
1098 /* it didn't get freed ... why? */
1099 if (item == menu->tail)
1108 rxvt_term::menubar_clear ()
1110 if (CurrentBar != NULL)
1112 menu_t *menu = CurrentBar->tail;
1114 while (menu != NULL)
1116 menu_t *prev = menu->prev;
1121 CurrentBar->head = CurrentBar->tail = NULL;
1123 if (CurrentBar->title)
1125 free (CurrentBar->title);
1126 CurrentBar->title = NULL;
1129 menuarrow_free (0); /* remove all arrow functions */
1135 #if (MENUBAR_MAX > 1)
1136 /* find if menu already exists */
1138 rxvt_term::menubar_find (const char *name)
1140 bar_t *bar = CurrentBar;
1142 #ifdef DEBUG_MENUBAR_STACKING
1143 fprintf (stderr, "looking for [menu:%s] ...", name ? name : " (nil)");
1145 if (bar == NULL || name == NULL)
1148 if (STRLEN (name) && STRCMP (name, "*"))
1152 if (!STRCMP (bar->name, name))
1154 #ifdef DEBUG_MENUBAR_STACKING
1155 fprintf (stderr, " found!\n");
1161 while (bar != CurrentBar);
1164 #ifdef DEBUG_MENUBAR_STACKING
1165 fprintf (stderr, "%s found!\n", (bar ? "" : " NOT"));
1172 rxvt_term::menubar_push (const char *name)
1177 if (CurrentBar == NULL)
1179 /* allocate first one */
1180 bar = (bar_t *) rxvt_malloc (sizeof (bar_t));
1182 MEMSET (bar, 0, sizeof (bar_t));
1183 /* circular linked-list */
1184 bar->next = bar->prev = bar;
1185 bar->head = bar->tail = NULL;
1194 /* find if menu already exists */
1195 bar = menubar_find (name);
1198 /* found it, use it */
1203 /* create if needed, or reuse the existing empty menubar */
1204 if (CurrentBar->head != NULL)
1206 /* need to malloc another one */
1207 if (Nbars < MENUBAR_MAX)
1208 bar = (bar_t *) rxvt_malloc (sizeof (bar_t));
1212 /* malloc failed or too many menubars, reuse another */
1215 bar = CurrentBar->next;
1220 bar->head = bar->tail = NULL;
1223 bar->next = CurrentBar->next;
1224 CurrentBar->next = bar;
1225 bar->prev = CurrentBar;
1226 bar->next->prev = bar;
1238 /* give menubar this name */
1239 STRNCPY (CurrentBar->name, name, MAXNAME);
1240 CurrentBar->name[MAXNAME - 1] = '\0';
1245 /* switch to a menu called NAME and remove it */
1247 rxvt_term::menubar_remove (const char *name)
1251 if ((bar = menubar_find (name)) == NULL)
1259 * pop a menubar, clean it up first
1261 if (CurrentBar != NULL)
1263 bar_t *prev = CurrentBar->prev;
1264 bar_t *next = CurrentBar->next;
1266 if (prev == next && prev == CurrentBar)
1269 Nbars = 0; /* safety */
1282 while (CurrentBar && !STRCMP (name, "*"));
1286 rxvt_action_decode (FILE *fp, action_t *act)
1291 if (act == NULL || (len = act->len) == 0 || (str = act->str) == NULL)
1294 if (act->type == MenuTerminalAction)
1297 /* can strip trailing ^G from XTerm sequence */
1298 if (str[0] == C0_ESC && str[1] == ']' && str[len - 1] == C0_BEL)
1301 else if (str[0] == C0_ESC)
1310 /* can strip trailing '\r' from M-x sequence */
1311 if (str[len - 1] == '\r')
1316 fprintf (fp, "M-"); /* meta prefix */
1324 * control character form is preferred, since backslash-escaping
1325 * can be really ugly looking when the backslashes themselves also
1326 * have to be escaped to avoid Shell (or whatever scripting
1327 * language) interpretation
1331 unsigned char ch = *str++;
1336 fprintf (fp, "\\E");
1339 fprintf (fp, "\\r");
1340 break; /* carriage-return */
1342 fprintf (fp, "\\\\");
1343 break; /* backslash */
1345 fprintf (fp, "\\^");
1351 fprintf (fp, "^%c", ('@' + ch));
1353 fprintf (fp, "\\%o", ch);
1355 fprintf (fp, "%c", ch);
1366 rxvt_menu_dump (FILE *fp, menu_t *menu)
1370 /* create a new menu and clear it */
1371 fprintf (fp, (menu->parent ? "./%s/*\n" : "/%s/*\n"), menu->name);
1373 for (item = menu->head; item != NULL; item = item->next)
1375 switch (item->entry.type)
1378 if (item->entry.submenu.menu == NULL)
1379 fprintf (fp, "> %s == NULL\n", item->name);
1381 rxvt_menu_dump (fp, item->entry.submenu.menu);
1385 fprintf (fp, "{%s}\n", (STRLEN (item->name) ? item->name : "-"));
1388 case MenuTerminalAction:
1390 fprintf (fp, "{%s}", item->name);
1391 if (item->name2 != NULL && STRLEN (item->name2))
1392 fprintf (fp, "{%s}", item->name2);
1394 rxvt_action_decode (fp, & (item->entry.action));
1399 fprintf (fp, (menu->parent ? "../\n" : "/\n\n"));
1403 rxvt_term::menubar_dump (FILE *fp)
1405 bar_t *bar = CurrentBar;
1408 if (bar == NULL || fp == NULL)
1413 "# " RESCLASS " (%s) Pid: %u\n# Date: %s\n\n",
1414 rs[Rs_name], (unsigned int)getpid (), ctime (&t));
1416 /* dump in reverse order */
1417 bar = CurrentBar->prev;
1423 fprintf (fp, "[menu:%s]\n", bar->name);
1425 if (bar->title != NULL)
1426 fprintf (fp, "[title:%s]\n", bar->title);
1428 for (i = 0; i < NARROWS; i++)
1430 switch (bar->arrows[i].type)
1432 case MenuTerminalAction:
1434 fprintf (fp, "<%c>", Arrows[i].name);
1435 rxvt_action_decode (fp, & (bar->arrows[i]));
1441 for (menu = bar->head; menu != NULL; menu = menu->next)
1442 rxvt_menu_dump (fp, menu);
1444 fprintf (fp, "\n[done:%s]\n\n", bar->name);
1447 while (bar != CurrentBar->prev);
1449 #endif /* (MENUBAR_MAX > 1) */
1452 * read in menubar commands from FILENAME
1453 * ignore all input before the tag line [menu] or [menu:???]
1455 * Note that since File_find () is used, FILENAME can be semi-colon
1456 * delimited such that the second part can refer to a tag
1457 * so that a large `database' of menus can be collected together
1460 * FILENAME = "file;"
1461 * read `file' starting with first [menu] or [menu:???] line
1463 * FILENAME = "file;tag"
1464 * read `file' starting with [menu:tag]
1467 rxvt_term::menubar_read (const char *filename)
1469 /* read in a menu from a file */
1472 char *p, *file, *tag = NULL;
1474 file = (char *)rxvt_File_find (filename, ".menu", rs[Rs_path]);
1478 fp = fopen (file, "rb");
1483 #if (MENUBAR_MAX > 1)
1484 /* semi-colon delimited */
1485 if ((tag = STRCHR (filename, ';')) != NULL)
1491 #endif /* (MENUBAR_MAX > 1) */
1493 fprintf (stderr, "[read:%s]\n", p);
1495 fprintf (stderr, "looking for [menu:%s]\n", tag);
1498 while ((p = fgets (buffer, sizeof (buffer), fp)) != NULL)
1502 if ((n = rxvt_Str_match (p, "[menu")) != 0)
1506 /* looking for [menu:tag] */
1507 if (p[n] == ':' && p[n + 1] != ']')
1510 n += rxvt_Str_match (p + n, tag);
1514 fprintf (stderr, "[menu:%s]\n", tag);
1520 else if (p[n] == ':' || p[n] == ']')
1525 /* found [menu], [menu:???] tag */
1531 fprintf (stderr, "read line = %s\n", p);
1534 /* looking for [done:tag] or [done:] */
1535 if ((n = rxvt_Str_match (p, "[done")) != 0)
1542 else if (p[n] == ':')
1552 n += rxvt_Str_match (p + n, tag);
1556 fprintf (stderr, "[done:%s]\n", tag);
1564 /* what? ... skip this line */
1565 p[0] = COMMENT_CHAR;
1570 * remove leading/trailing space
1571 * and strip-off leading/trailing quotes
1572 * skip blank or comment lines
1575 if (*p && *p != '#')
1577 menu_readonly = 0; /* if case we read another file */
1578 menubar_dispatch (p);
1580 /* get another line */
1581 p = fgets (buffer, sizeof (buffer), fp);
1588 * user interface for building/deleting and otherwise managing menus
1591 rxvt_term::menubar_dispatch (char *str)
1594 char *path, *name, *name2;
1596 if (menubar_visible () && ActiveMenu != NULL)
1605 case '/': /* absolute & relative path */
1606 case MENUITEM_BEG: /* menuitem */
1607 /* add `+' prefix for these cases */
1613 str++; /* skip cmd character */
1617 #if (MENUBAR_MAX > 1)
1618 if (CurrentBar == NULL)
1620 #endif /* (MENUBAR_MAX > 1) */
1621 if (str[1] && str[2] == '>') /* arrow commands */
1622 menuarrow_add (str);
1625 case '[': /* extended command */
1626 while (str[0] == '[')
1628 char *next = (++str); /* skip leading '[' */
1635 if ((next = STRCHR (next, ':')) == NULL)
1636 return; /* parse error */
1638 while (next[1] != ']');
1639 /* remove and skip ':]' */
1645 if ((next = STRCHR (next, ']')) == NULL)
1646 return; /* parse error */
1647 /* remove and skip ']' */
1656 /* try and dispatch it, regardless of read/write status */
1657 saved = menu_readonly;
1659 menubar_dispatch (str + 1);
1660 menu_readonly = saved;
1662 /* these ones don't require menu stacking */
1663 else if (!STRCMP (str, "clear"))
1667 else if (!STRCMP (str, "done") || rxvt_Str_match (str, "done:"))
1671 else if (!STRCMP (str, "show"))
1676 else if (!STRCMP (str, "hide"))
1681 else if ((n = rxvt_Str_match (str, "read:")) != 0)
1683 /* read in a menu from a file */
1687 else if ((n = rxvt_Str_match (str, "title:")) != 0)
1690 if (CurrentBar != NULL && !menu_readonly)
1694 name = (char *)rxvt_realloc (CurrentBar->title, STRLEN (str) + 1);
1698 CurrentBar->title = name;
1704 free (CurrentBar->title);
1705 CurrentBar->title = NULL;
1709 else if ((n = rxvt_Str_match (str, "pixmap:")) != 0)
1712 xterm_seq (XTerm_Pixmap, str, CHAR_ST);
1714 #if (MENUBAR_MAX > 1)
1715 else if ((n = rxvt_Str_match (str, "rm")) != 0)
1726 menubar_remove (str);
1731 else if ((n = rxvt_Str_match (str, "menu")) != 0)
1738 /* add/access menuBar */
1739 if (*str != '\0' && *str != '*')
1743 if (CurrentBar == NULL)
1745 menubar_push ("default");
1749 if (CurrentBar != NULL)
1750 menu_readonly = 0; /* allow menu build commands */
1752 else if (!STRCMP (str, "dump"))
1754 /* dump current menubars to a file */
1757 /* enough space to hold the results */
1760 sprintf (buffer, "/tmp/" RESCLASS "-%u",
1761 (unsigned int)getpid ());
1763 if ((fp = fopen (buffer, "wb")) != NULL)
1765 xterm_seq (XTerm_title, buffer, CHAR_ST);
1770 else if (!STRCMP (str, "next"))
1774 CurrentBar = CurrentBar->next;
1778 else if (!STRCMP (str, "prev"))
1782 CurrentBar = CurrentBar->prev;
1786 else if (!STRCMP (str, "swap"))
1788 /* swap the top 2 menus */
1791 bar_t *cbprev = CurrentBar->prev;
1792 bar_t *cbnext = CurrentBar->next;
1794 cbprev->next = cbnext;
1795 cbnext->prev = cbprev;
1797 CurrentBar->next = cbprev;
1798 CurrentBar->prev = cbprev->prev;
1800 cbprev->prev->next = CurrentBar;
1801 cbprev->prev = CurrentBar;
1803 CurrentBar = cbprev;
1807 #endif /* (MENUBAR_MAX > 1) */
1810 BuildMenu = ActiveMenu = NULL;
1812 #ifdef DEBUG_MENUBAR_STACKING
1813 fprintf (stderr, "menus are read%s\n",
1814 menu_readonly ? "only" : "/write");
1822 #if (MENUBAR_MAX > 1)
1823 if (CurrentBar == NULL)
1827 #ifdef DEBUG_MENUBAR_STACKING
1828 fprintf (stderr, "menus are read%s\n",
1829 menu_readonly ? "only" : "/write");
1833 #endif /* (MENUBAR_MAX > 1) */
1842 /* parse STR, allow spaces inside (name) */
1843 if (path[0] != '\0')
1845 name = STRCHR (path, MENUITEM_BEG);
1846 str = STRCHR (path, MENUITEM_END);
1847 if (name != NULL || str != NULL)
1849 if (name == NULL || str == NULL || str <= (name + 1)
1850 || (name > path && name[-1] != '/'))
1852 rxvt_print_error ("menu error <%s>\n", path);
1855 if (str[1] == MENUITEM_BEG)
1858 str = STRCHR (name2, MENUITEM_END);
1862 rxvt_print_error ("menu error <%s>\n", path);
1865 name2[-2] = '\0'; /* remove prev MENUITEM_END */
1867 if (name > path && name[-1] == '/')
1870 *name++ = '\0'; /* delimit */
1871 *str++ = '\0'; /* delimit */
1873 while (isspace (*str))
1874 str++; /* skip space */
1878 "`%c' path = <%s>, name = <%s>, name2 = <%s>, action = <%s>\n",
1879 cmd, (path ? path : " (nil)"), (name ? name : " (nil)"),
1880 (name2 ? name2 : " (nil)"), (str ? str : " (nil)")
1885 /* process the different commands */
1888 case '+': /* add/replace existing menu or menuitem */
1889 if (path[0] != '\0')
1893 path = menu_find_base (& (BuildMenu), path);
1894 len = STRLEN (path);
1896 /* don't allow menus called `*' */
1899 menu_clear (BuildMenu);
1902 else if (len >= 2 && !STRCMP ((path + len - 2), "/*"))
1904 path[len - 2] = '\0';
1906 if (path[0] != '\0')
1907 BuildMenu = menu_add (BuildMenu, path);
1909 if (name != NULL && name[0] != '\0')
1910 rxvt_menuitem_add (BuildMenu,
1911 (STRCMP (name, SEPARATOR_NAME) ? name : ""),
1915 case '-': /* delete menu entry */
1916 if (!STRCMP (path, "/*") && (name == NULL || name[0] == '\0'))
1923 else if (path[0] != '\0')
1926 menu_t *menu = BuildMenu;
1928 path = menu_find_base (&menu, path);
1929 len = STRLEN (path);
1931 /* submenu called `*' clears all menu items */
1937 else if (len >= 2 && !STRCMP (&path[len - 2], "/*"))
1942 else if (path[0] != '\0')
1950 if (BuildMenu != NULL)
1952 if (name == NULL || name[0] == '\0')
1953 BuildMenu = menu_delete (BuildMenu);
1958 menu_t *BuildMenu = BuildMenu;
1960 n1 = STRCMP (name, SEPARATOR_NAME) ? name : "";
1961 item = rxvt_menuitem_find (BuildMenu, n1);
1962 if (item != NULL && item->entry.type != MenuSubMenu)
1964 menuitem_free (BuildMenu, item);
1966 /* fix up the width */
1967 BuildMenu->width = 0;
1968 for (item = BuildMenu->head; item != NULL;
1971 short l = item->len + item->len2;
1973 MAX_IT (BuildMenu->width, l);
1986 rxvt_term::draw_Arrows (int name, int state)
1992 #ifdef MENU_SHADOW_IN
2000 break; /* SHADOW_OUT */
2004 break; /* SHADOW_IN */
2006 top = bot = scrollbarGC;
2007 break; /* neutral */
2013 for (i = 0; i < NARROWS; i++)
2015 const int w = Width2Pixel (1);
2016 const int y = (menuBar_TotalHeight () - w) / 2;
2017 int x = Arrows_x + (5 * Width2Pixel (i)) / 4;
2019 if (!name || name == Arrows[i].name)
2020 rxvt_Draw_Triangle (display->display, menuBar.win, top, bot, x, y, w,
2023 XFlush (display->display);
2027 rxvt_term::menubar_expose ()
2032 if (!menubar_visible () || menuBar.win == 0)
2035 if (menubarGC == None)
2037 /* Create the graphics context */
2040 gcvalue.foreground = (XDEPTH <= 2 ? PixColors[Color_fg]
2041 : PixColors[Color_Black]);
2042 menubarGC = XCreateGC (display->display, menuBar.win,
2043 GCForeground, &gcvalue);
2046 /* make sure the font is correct */
2047 XClearWindow (display->display, menuBar.win);
2052 if (CurrentBar != NULL)
2054 for (menu = CurrentBar->head; menu != NULL; menu = menu->next)
2056 int len = menu->len;
2058 x = (menu->x + menu->len + HSPACE);
2060 #ifdef DEBUG_MENU_LAYOUT
2061 rxvt_print_menu_descendants (menu);
2064 if (x >= TermWin.ncol)
2065 len = (TermWin.ncol - (menu->x + HSPACE));
2067 drawbox_menubar (menu->x, len, +1);
2068 draw_string (*menuBar.drawable, menubarGC, TermWin.fontset,
2069 (Width2Pixel (menu->x) + Width2Pixel (HSPACE) / 2),
2070 SHADOW, menu->name, len);
2072 if (x >= TermWin.ncol)
2076 drawbox_menubar (x, TermWin.ncol, (CurrentBar ? +1 : -1));
2078 /* add the menuBar title, if it exists and there's plenty of room */
2080 if (x < TermWin.ncol)
2087 ncol = (int)TermWin.ncol;
2088 if (x < (ncol - (NARROWS + 1)))
2090 ncol -= (NARROWS + 1);
2091 Arrows_x = Width2Pixel (ncol);
2093 draw_Arrows (0, +1);
2096 && CurrentBar->title) ? CurrentBar->title : "%n-%v";
2097 for (len = 0; str[0] && len < sizeof (title) - 1; str++)
2099 const char *s = NULL;
2109 break; /* resource name */
2112 break; /* version number */
2115 break; /* literal '%' */
2118 while (*s && len < sizeof (title) - 1)
2119 title[len++] = *s++;
2123 title[len++] = str[0];
2129 ncol -= (x + len + HSPACE);
2130 if (len > 0 && ncol >= 0)
2131 draw_string (*menuBar.drawable, menubarGC, TermWin.fontset,
2132 Width2Pixel (x) + Width2Pixel (ncol + HSPACE) / 2,
2133 SHADOW, title, len);
2138 rxvt_term::menubar_mapping (int map)
2142 if (map && !menubar_visible ())
2145 if (menuBar.win == 0)
2147 XMapWindow (display->display, menuBar.win);
2150 else if (!map && menubar_visible ())
2154 XUnmapWindow (display->display, menuBar.win);
2164 rxvt_term::menu_select (XButtonEvent &ev)
2166 menuitem_t *thisitem, *item = NULL;
2169 Window unused_root, unused_child;
2170 int unused_root_x, unused_root_y;
2171 unsigned int unused_mask;
2173 if (ActiveMenu == NULL)
2176 XQueryPointer (display->display, ActiveMenu->win,
2177 &unused_root, &unused_child,
2178 &unused_root_x, &unused_root_y,
2179 &ev.x, &ev.y, &unused_mask);
2181 if (ActiveMenu->parent != NULL && (ev.x < 0 || ev.y < 0))
2187 /* determine the menu item corresponding to the Y index */
2189 if (ev.x >= 0 && ev.x <= (ActiveMenu->w - SHADOW))
2191 for (item = ActiveMenu->head; item != NULL; item = item->next)
2193 int h = HEIGHT_TEXT + 2 * SHADOW;
2195 if (isSeparator (item->name))
2196 h = HEIGHT_SEPARATOR;
2197 else if (ev.y >= y && ev.y < (y + h))
2204 if (item == NULL && ev.type == ButtonRelease)
2211 this_y = y - SHADOW;
2213 /* erase the last item */
2214 if (ActiveMenu->item != NULL)
2216 if (ActiveMenu->item != thisitem)
2218 for (y = 0, item = ActiveMenu->head; item != NULL; item = item->next)
2222 if (isSeparator (item->name))
2223 h = HEIGHT_SEPARATOR;
2224 else if (item == ActiveMenu->item)
2226 /* erase old menuitem */
2227 drawbox_menuitem (y, 0); /* No Shadow */
2228 if (item->entry.type == MenuSubMenu)
2229 drawtriangle (ActiveMenu->w, y, +1);
2234 h = HEIGHT_TEXT + 2 * SHADOW;
2244 switch (item->entry.type)
2252 case MenuTerminalAction:
2253 drawbox_menuitem (this_y, -1);
2254 #ifdef HAVE_NANOSLEEP
2255 struct timespec rqt;
2258 rqt.tv_nsec = MENU_DELAY_USEC * 1000;
2259 nanosleep (&rqt, NULL);
2261 /* use select for timing */
2265 tv.tv_usec = MENU_DELAY_USEC;
2266 select (0, NULL, NULL, NULL, &tv);
2268 /* remove menu before sending keys to the application */
2271 action_dispatch (& (item->entry.action));
2272 #else /* DEBUG_MENU */
2273 fprintf (stderr, "%s: %s\n", item->name,
2274 item->entry.action.str);
2275 #endif /* DEBUG_MENU */
2281 if (item->entry.type == MenuSubMenu)
2290 ActiveMenu->item = thisitem;
2293 if (thisitem != NULL)
2295 item = ActiveMenu->item;
2296 if (item->entry.type != MenuLabel)
2297 drawbox_menuitem (y, +1);
2299 if (item->entry.type == MenuSubMenu)
2303 drawtriangle (ActiveMenu->w, y, -1);
2305 x = ev.x + (ActiveMenu->parent
2307 : Width2Pixel (ActiveMenu->x));
2309 if (x >= item->entry.submenu.menu->x)
2311 ActiveMenu = item->entry.submenu.menu;
2321 rxvt_term::menubar_select (XButtonEvent &ev)
2323 menu_t *menu = NULL;
2325 /* determine the pulldown menu corresponding to the X index */
2326 if (ev.y >= 0 && ev.y <= menuBar_height () && CurrentBar != NULL)
2328 for (menu = CurrentBar->head; menu != NULL; menu = menu->next)
2330 int x = Width2Pixel (menu->x);
2331 int w = Width2Pixel (menu->len + HSPACE);
2333 if ((ev.x >= x && ev.x < x + w))
2344 if (menu == NULL && Arrows_x && ev.x >= Arrows_x)
2348 for (i = 0; i < NARROWS; i++)
2350 if (ev.x >= (Arrows_x + (Width2Pixel (4 * i + i)) / 4)
2352 + (Width2Pixel (4 * i + i + 4)) / 4))
2354 draw_Arrows (Arrows[i].name, -1);
2356 #ifdef HAVE_NANOSLEEP
2357 struct timespec rqt;
2360 rqt.tv_nsec = MENU_DELAY_USEC * 1000;
2361 nanosleep (&rqt, NULL);
2363 /* use select for timing */
2367 tv.tv_usec = MENU_DELAY_USEC;
2368 select (0, NULL, NULL, NULL, &tv);
2372 draw_Arrows (Arrows[i].name, +1);
2373 #ifdef DEBUG_MENUARROWS
2374 fprintf (stderr, "'%c': ", Arrows[i].name);
2376 if (CurrentBar == NULL
2377 || (CurrentBar->arrows[i].type != MenuAction
2378 && CurrentBar->arrows[i].type !=
2379 MenuTerminalAction))
2381 if (Arrows[i].str != NULL && Arrows[i].str[0])
2382 fprintf (stderr, " (default) \\033%s\n",
2383 & (Arrows[i].str[2]));
2387 fprintf (stderr, "%s\n",
2388 CurrentBar->arrows[i].str);
2390 #else /* DEBUG_MENUARROWS */
2391 if (CurrentBar == NULL || action_dispatch (&CurrentBar->arrows[i]))
2393 if (Arrows[i].str != NULL && Arrows[i].str[0] != 0)
2394 tt_write ((Arrows[i].str + 1),
2397 #endif /* DEBUG_MENUARROWS */
2406 * press menubar or move to a new entry
2408 if (menu != NULL && menu != ActiveMenu)
2410 menu_hide_all (); /* pop down old menu */
2412 menu_show (); /* pop up new menu */
2419 * general dispatch routine,
2420 * it would be nice to have `sticky' menus
2423 rxvt_term::menubar_control (XButtonEvent &ev)
2428 if (ev.button == Button1)
2429 menubar_select (ev);
2433 if (ev.button == Button1)
2438 while (XCheckTypedWindowEvent (display->display, TermWin.parent[0],
2439 MotionNotify, (XEvent *)&ev)) ;
2442 while (menu_select (ev)) ;
2447 Window unused_root, unused_child;
2448 int unused_root_x, unused_root_y;
2449 unsigned int unused_mask;
2451 XQueryPointer (display->display, menuBar.win,
2452 &unused_root, &unused_child,
2453 &unused_root_x, &unused_root_y,
2454 &ev.x, &ev.y, &unused_mask);
2455 menubar_select (ev);
2462 rxvt_term::map_menuBar (int map)
2464 if (menubar_mapping (map))
2465 resize_all_windows (0, 0, 0);
2468 /*----------------------- end-of-file (C source) -----------------------*/