1 /*--------------------------------*-C-*---------------------------------*
2  * File:    menubar.c
3  *----------------------------------------------------------------------*
4  *
5  * All portions of code are copyright by their respective author/s.
6  * Copyright (c) 1997,1998   mj olesen <olesen@me.QueensU.CA>
7  * Copyright (c) 2004        Jingmin Zhou <jimmyzhou@users.sourceforge.net>
8  * Copyright (c) 2005-6      Gautam Iyer <gi1242@users.sourceforge.net>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23  *----------------------------------------------------------------------*/
24 
25 #include "../config.h"
26 #include "rxvt.h"
27 
28 #define NEWARGLIM 500
29 
30 
31 #ifdef HAVE_MENUBAR
32 
33 
34 #define CHOOSE_GC_FG(R, PIXCOL)	\
35     XSetForeground ((R)->Xdisplay, (R)->menuBar.gc, (PIXCOL))
36 
37 #define TRIANGLE_WIDTH	(HEIGHT_TEXT * 3 / 5 - 2 * SHADOW)
38 
39 
40 /*--------------------------------------------------------------------*
41  *         BEGIN `INTERNAL' ROUTINE PROTOTYPES                        *
42  *--------------------------------------------------------------------*/
43 menuitem_t*   rxvt_menuitem_find          (const menu_t*, const unsigned char*);
44 void          rxvt_menuitem_free          (rxvt_t*, menu_t*, menuitem_t*);
45 /* int           rxvt_action_type            (action_t*, unsigned char*); */
46 /* int           rxvt_action_dispatch        (rxvt_t*, action_t*); */
47 int           rxvt_menuarrow_find         (char);
48 void          rxvt_menuarrow_free         (rxvt_t*, unsigned char);
49 void          rxvt_menuarrow_add          (rxvt_t*, unsigned char*);
50 menuitem_t*   rxvt_menuitem_add           (rxvt_t*, menu_t*, const unsigned char*, const unsigned char*, const unsigned char*);
51 unsigned char* rxvt_menu_find_base         (rxvt_t*, menu_t**, unsigned char*);
52 menu_t*       rxvt_menu_delete            (rxvt_t*, menu_t*);
53 menu_t*       rxvt_menu_add               (rxvt_t*, menu_t*, unsigned char*);
54 void          rxvt_drawbox_menubar        (rxvt_t*, int, int, int);
55 void          rxvt_menubar_draw_triangle  (rxvt_t*, int, int, int);
56 void          rxvt_drawbox_menuitem       (rxvt_t*, int, int);
57 void	      rxvt_build_tablist	  (rxvt_t*, menu_t *);
58 void          rxvt_menu_display           (rxvt_t*, void (*update)(rxvt_t*));
59 void          rxvt_menu_hide_all          (rxvt_t*);
60 void          rxvt_menu_hide              (rxvt_t*);
61 void          rxvt_menu_clear             (rxvt_t*, menu_t*);
62 void          rxvt_menubar_clear          (rxvt_t*);
63 void          rxvt_draw_arrows            (rxvt_t*, int, int);
64 void          rxvt_menubar_select         (rxvt_t*, XButtonEvent*);
65 void	      rxvt_menubar_draw_labels	  (rxvt_t*);
66 void	      resizeSubMenus		  (rxvt_t*, menu_t*);
67 #ifdef DEBUG
68 void          rxvt_print_menu_ancestors   (menu_t*);
69 void          rxvt_print_menu_descendants (menu_t*);
70 #endif
71 /*--------------------------------------------------------------------*
72  *         END   `INTERNAL' ROUTINE PROTOTYPES                        *
73  *--------------------------------------------------------------------*/
74 
75 
76 static const struct {
77     const char	  name;	/* (l)eft, (u)p, (d)own, (r)ight */
78     const unsigned char str[5];	/* str[0] = STRLEN (str+1) */
79 } Arrows[NARROWS] = {
80     { 'l', "\003\033[D" },
81     { 'u', "\003\033[A" },
82     { 'd', "\003\033[B" },
83     { 'r', "\003\033[C" }
84 };
85 
86 #ifdef XFT_SUPPORT
87 #define PTEXTWIDTH( R, s, len)				    \
88     ( (ISSET_OPTION(R, Opt_xft) && r->TermWin.xftpfont ) ?  \
89 	( xftPfontTextWidth( (R), (s), (len)) )	:	    \
90 	Width2Pixel(len))
91 
92 int
xftPfontTextWidth(rxvt_t * r,const unsigned char * s,int len)93 xftPfontTextWidth( rxvt_t *r, const unsigned char *s, int len)
94 {
95     if( s && len )
96     {
97 	XGlyphInfo ginfo;
98 
99 	XftTextExtents8( r->Xdisplay, r->TermWin.xftpfont, s, len, &ginfo);
100 	return ginfo.width;
101     }
102     else
103 	return 0;
104 }
105 
106 # else
107 # define PTEXTWIDTH( r, s, len) (Width2Pixel((len)))
108 #endif
109 
110 #define MENUWIDTH( menu )			    \
111     ( MENU_ITEM_WIDTH( (menu)->lwidth, (menu)->rwidth ) )
112 
113 #define MENU_ITEM_WIDTH( item_lwidth, item_rwidth )		    \
114     ((item_lwidth) + (item_rwidth) + 2*SHADOW + 3*HSPACE_PIXEL)
115 
116 
117 /*
118  * find an item called NAME in MENU
119  */
120 /* INTPROTO */
121 menuitem_t*
rxvt_menuitem_find(const menu_t * menu,const unsigned char * name)122 rxvt_menuitem_find(const menu_t *menu, const unsigned char *name)
123 {
124     menuitem_t	 *item;
125 
126 #ifdef DEBUG
127     assert(NOT_NULL(name));
128     assert(NOT_NULL(menu));
129 #endif
130 
131     rxvt_dbgmsg ((DBG_DEBUG, DBG_MENUBAR, "rxvt_menuitem_find(%s) ... ", name));
132 
133     /* find the last item in the menu, this is good for separators */
134     for (item = menu->tail; NOT_NULL(item); item = item->prev)
135     {
136 	rxvt_dbgmsg ((DBG_DEBUG, DBG_MENUBAR, "%d", item->entry.itemType));
137 
138 	if (item->entry.itemType == MenuSubMenu)
139 	{
140 	    if (!STRCMP(name, (item->entry.submenu.menu)->name))
141 		break;
142 	}
143 	else if ((isSeparator(name) && isSeparator(item->name))
144 	       || !STRCMP(name, item->name))
145 	    break;
146 
147 	rxvt_dbgmsg ((DBG_DEBUG, DBG_MENUBAR, ","));
148     }
149 
150     rxvt_dbgmsg ((DBG_DEBUG, DBG_MENUBAR, "Returning %p\n", item));
151     return item;
152 }
153 
154 /*
155  * unlink ITEM from its MENU and free its memory
156  */
157 /* INTPROTO */
158 void
rxvt_menuitem_free(rxvt_t * r,menu_t * menu,menuitem_t * item)159 rxvt_menuitem_free(rxvt_t *r, menu_t *menu, menuitem_t *item)
160 {
161     /* disconnect */
162     menuitem_t	 *prev, *next;
163 
164 #ifdef DEBUG
165     assert(NOT_NULL(menu));
166 #endif
167     rxvt_dbgmsg ((DBG_DEBUG, DBG_MENUBAR, "rxvt_menuitem_free()\n"));
168 
169     prev = item->prev;
170     next = item->next;
171     if (NOT_NULL(prev)) prev->next = next;
172     if (NOT_NULL(next)) next->prev = prev;
173 
174     /* new head, tail */
175     if (menu->tail == item) menu->tail = prev;
176     if (menu->head == item) menu->head = next;
177 
178     switch (item->entry.itemType)
179     {
180 	case MenuSubMenu:
181 	    rxvt_menu_delete(r, item->entry.submenu.menu);
182 	    SET_NULL(item->entry.submenu.menu);
183 	    break;
184 
185 	case MenuItem:
186 	    rxvt_free(item->entry.action.str);
187 	    SET_NULL(item->entry.action.str);
188 	    break;
189     }
190 
191     if (NOT_NULL(item->name)) rxvt_free(item->name);
192     if (NOT_NULL(item->name2)) rxvt_free(item->name2);
193     rxvt_free(item);
194 }
195 
196 
197 /* return the arrow index corresponding to NAME */
198 /* INTPROTO */
199 int
rxvt_menuarrow_find(char name)200 rxvt_menuarrow_find(char name)
201 {
202     int		 i;
203 
204     rxvt_dbgmsg ((DBG_DEBUG, DBG_MENUBAR, "rxvt_menuarrow_find()\n"));
205 
206     for (i = 0; i < NARROWS; i++)
207     if (name == Arrows[i].name)
208 	return i;
209     return -1;
210 }
211 
212 /* free the memory associated with arrow NAME of the current menubar */
213 /* INTPROTO */
214 void
rxvt_menuarrow_free(rxvt_t * r,unsigned char name)215 rxvt_menuarrow_free(rxvt_t *r, unsigned char name)
216 {
217     int		 i;
218     rxvt_dbgmsg ((DBG_DEBUG, DBG_MENUBAR, "rxvt_menuarrow_free()\n"));
219 
220     if (name)
221     {
222 	i = rxvt_menuarrow_find(name);
223 	if (i >= 0)
224 	{
225 	    action_t	   *act = &(r->h->MenuBar.arrows[i]);
226 
227 	    if (act->len)
228 	    {
229 		assert (NOT_NULL(act->str));
230 		rxvt_free(act->str);
231 		SET_NULL(act->str);
232 		act->len = 0;
233 	    }
234 
235 	    act->type = MenuLabel;
236 	}
237     }
238     else
239     {
240 	/* Free all arrows */
241 	/*
242 	 * 2006-02-22 gi1242 XXX: If Arrows[i].name is NULL this will lead to an
243 	 * infinite loop. WTF.
244 	 */
245 	for (i = 0; i < NARROWS; i++)
246 	    rxvt_menuarrow_free(r, Arrows[i].name);
247     }
248 }
249 
250 /* INTPROTO */
251 void
rxvt_menuarrow_add(rxvt_t * r,unsigned char * string)252 rxvt_menuarrow_add(rxvt_t *r, unsigned char *string)
253 {
254     int		 	i;
255     unsigned short	xtra_len;
256     unsigned char	*p;
257 
258     struct
259     {
260 	unsigned char	*str;
261 	unsigned short	len;
262     }
263 	    beg = { NULL, 0 },
264 	    end = { NULL, 0 },
265 	    *cur,
266 	    parse[NARROWS];
267 
268     rxvt_dbgmsg ((DBG_DEBUG, DBG_MENUBAR, "rxvt_menuarrow_add()\n"));
269     MEMSET(parse, 0, sizeof(parse));
270 
271     for (p = string; NOT_NULL(p) && *p; string = p)
272     {
273     p = (string + 3);
274     switch (string[1])
275     {
276 	case 'b':
277 	    cur = &beg;
278 	    break;
279 	case 'e':
280 	    cur = &end;
281 	    break;
282 
283 	default:
284 	    i = rxvt_menuarrow_find(string[1]);
285 	    if (i >= 0)
286 	    cur = &(parse[i]);
287 	    else
288 	    continue;	/* not found */
289 	    break;
290     }
291 
292     string = p;
293     cur->str = string;
294     cur->len = 0;
295 
296     if (cur == &end)
297     {
298 	p = (unsigned char*) STRCHR(string, '\0');
299     }
300     else
301     {
302 	unsigned char	*next = string;
303 
304 	while (1)
305 	{
306 	    p = (unsigned char*) STRCHR(next, '<');
307 	    if (NOT_NULL(p))
308 	    {
309 		if (p[1] && p[2] == '>')
310 		break;
311 		/* parsed */
312 	    }
313 	    else
314 	    {
315 		if (IS_NULL(beg.str))	/* no end needed */
316 		p = (unsigned char*) STRCHR(next, '\0');
317 		break;
318 	    }
319 	    next = (p + 1);
320 	}
321     }
322 
323     if (IS_NULL(p))
324 	return;
325     cur->len = (p - string);
326     }
327 
328 #ifdef DEBUG
329     cur = &beg;
330     rxvt_dbgmsg ((DBG_DEBUG, DBG_MENUBAR, "<b>(len %d) = %.*s\n", cur->len, (cur->str ? (char*) cur->str : "")));
331     for (i = 0; i < NARROWS; i++)
332     {
333 	cur = &(parse[i]);
334 	rxvt_dbgmsg ((DBG_DEBUG, DBG_MENUBAR, "<%c>(len %d) = %.*s\n", Arrows[i].name, cur->len, (cur->str ? (char*) cur->str : "")));
335     }
336     cur = &end;
337     rxvt_dbgmsg ((DBG_DEBUG, DBG_MENUBAR, "<e>(len %d) = %.*s\n", cur->len, (cur->str ? (char*) cur->str : "")));
338 #endif
339 
340     xtra_len = (beg.len + end.len);
341     for (i = 0; i < NARROWS; i++)
342     {
343 	if (xtra_len || parse[i].len)
344 	    rxvt_menuarrow_free(r, Arrows[i].name);
345     }
346 
347     for (i = 0; i < NARROWS; i++)
348     {
349 	char  		str[NEWARGLIM];
350 	unsigned short	len;
351 
352 	if (!parse[i].len)
353 	    continue;
354 
355 	/* possible integer overflow? */
356 	assert (parse[i].len <= 0xffff - xtra_len - 1);
357 	/* str = rxvt_malloc(parse[i].len + xtra_len + 1); */
358 
359 	len = 0;
360 	if (beg.len)
361 	{
362 	    STRNCPY(str + len, beg.str, beg.len);
363 	    len += beg.len;
364 	}
365 	STRNCPY(str + len, parse[i].str, parse[i].len);
366 	len += parse[i].len;
367 
368 	if (end.len)
369 	{
370 	    STRNCPY(str + len, end.str, end.len);
371 	    len += end.len;
372 	}
373 	str[len] = '\0';
374 
375 #ifdef DEBUG
376 	rxvt_dbgmsg ((DBG_DEBUG, DBG_MENUBAR, "<%c>(len %d) = %s\n", Arrows[i].name, len, str));
377 #endif
378 	rxvt_set_action( &(r->h->MenuBar.arrows[i]), str);
379     }
380 }
381 
382 /* INTPROTO */
383 menuitem_t   *
rxvt_menuitem_add(rxvt_t * r,menu_t * menu,const unsigned char * name,const unsigned char * name2,const unsigned char * action)384 rxvt_menuitem_add(rxvt_t *r, menu_t *menu,
385 	const unsigned char *name, const unsigned char *name2,
386 	const unsigned char *action)
387 {
388     menuitem_t	 *item;
389     unsigned int    len;
390     rxvt_dbgmsg ((DBG_DEBUG, DBG_MENUBAR, "rxvt_menuitem_add()\n"));
391 
392 #ifdef DEBUG
393     assert(NOT_NULL(name));
394     assert(NOT_NULL(action));
395 #endif
396 
397     if (IS_NULL(menu)) return NULL;
398 
399     if (isSeparator(name))
400     {
401 	/* add separator, no action */
402 	name	= (unsigned char *) "";
403 	action	= (unsigned char *) "";
404     }
405     else
406     {
407 	/*
408 	 * add/replace existing menu item
409 	 */
410 	item = rxvt_menuitem_find(menu, name);
411 	if (NOT_NULL(item))
412 	{
413 	    if (NOT_NULL(item->name2) && NOT_NULL(name2))
414 	    {
415 		rxvt_free(item->name2);
416 		item->len2 = 0;
417 		SET_NULL(item->name2);
418 	    }
419 	    switch (item->entry.itemType)
420 	    {
421 		case MenuSubMenu:
422 		    rxvt_menu_delete(r, item->entry.submenu.menu);
423 		    SET_NULL(item->entry.submenu.menu);
424 		    break;
425 
426 		case MenuItem:
427 		    rxvt_free(item->entry.action.str);
428 		    SET_NULL(item->entry.action.str);
429 		break;
430 	    }
431 	    goto Item_Found;
432 	}
433     }
434 
435     /* allocate a new itemect */
436     item = (menuitem_t *) rxvt_malloc( sizeof( menuitem_t) );
437 
438     SET_NULL(item->entry.action.str); /* If not null will be freed by
439 				     rxvt_action_set */
440 
441     item->len2 = 0;
442     SET_NULL(item->name2);
443 
444     len = STRLEN(name);
445     /* possible integer overflow? */
446     assert (len >= 0 && len + 1 >= 0);
447     item->name = rxvt_malloc(len + 1);
448     STRCPY(item->name, name);
449     if (name[0] == '.' && name[1] != '.')
450 	len = 0;	/* hidden menu name */
451     item->len = len;
452 
453     /* add to tail of list */
454     item->prev = menu->tail;
455     SET_NULL(item->next);
456 
457     if (NOT_NULL(menu->tail))
458 	(menu->tail)->next = item;
459     menu->tail = item;
460 
461     /* fix head */
462     if (IS_NULL(menu->head))
463 	menu->head = item;
464 
465     /*
466      * add action
467      */
468 Item_Found:
469     if (NOT_NULL(name2) && IS_NULL(item->name2))
470     {
471 	len = STRLEN(name2);
472 	if (len == 0)
473 	    SET_NULL(item->name2);
474 	else
475 	{
476 	    /* possible integer overflow? */
477 	    assert (len > 0 && len + 1 > 0);
478 	    item->name2 = rxvt_malloc(len + 1);
479 	    STRCPY(item->name2, name2);
480 	}
481 	item->len2 = len;
482     }
483     item->entry.itemType = MenuLabel;
484     len = STRLEN(action);
485 
486     if (len == 0 && NOT_NULL(item->name2))
487     {
488 	action = item->name2;
489 	len = item->len2;
490     }
491     if (len)
492     {
493 	char	str[NEWARGLIM];
494 	/* possible integer overflow? */
495 	assert (len > 0 && len + 1 > 0);
496 	/* str = rxvt_malloc(len + 1); */
497 
498 	STRCPY(str, action);
499 	SET_NULL(item->entry.action.str);
500 
501 	if( rxvt_set_action( &(item->entry.action), str) )
502 	    item->entry.itemType = MenuItem;
503     }
504 
505     /* new item and a possible increase in width */
506     len = PTEXTWIDTH( r, item->name, item->len );   /* Left text width */
507     if( menu->lwidth < len ) menu->lwidth = len;
508 
509     len = PTEXTWIDTH( r, item->name2, item->len2);  /* Right text width */
510     if( menu->rwidth < len ) menu->rwidth = len;
511 
512     return item;
513 }
514 
515 /*
516  * search for the base starting menu for NAME.
517  * return a pointer to the portion of NAME that remains
518  */
519 /* INTPROTO */
520 unsigned char*
rxvt_menu_find_base(rxvt_t * r,menu_t ** menu,unsigned char * path)521 rxvt_menu_find_base(rxvt_t *r, menu_t **menu, unsigned char *path)
522 {
523     menu_t	 *m = NULL;
524     menuitem_t	 *item;
525     rxvt_dbgmsg ((DBG_DEBUG, DBG_MENUBAR, "rxvt_menu_find_base()\n"));
526 
527 #ifdef DEBUG
528     assert(NOT_NULL(menu));
529 #endif
530 
531     if (path[0] == '\0')
532     return path;
533 
534     if (NOT_NULL(STRCHR(path, '/')))
535     {
536 	unsigned char	       *p = path;
537 
538 	while (NOT_NULL(p = (unsigned char*) STRCHR(p, '/')))
539 	{
540 	    p++;
541 	    if (*p == '/')
542 	    path = p;
543 	}
544 	if (path[0] == '/')
545 	{
546 	    path++;
547 	    SET_NULL(*menu);
548 	}
549 	while (NOT_NULL(p = (unsigned char*) STRCHR(path, '/')))
550 	{
551 	    p[0] = '\0';
552 	    if (path[0] == '\0')
553 	    return NULL;
554 	    if (!STRCMP(path, DOT))
555 	    {
556 		/* nothing to do */
557 	    }
558 	    else if (!STRCMP(path, DOTS))
559 	    {
560 		if (NOT_NULL(*menu))
561 		    *menu = (*menu)->parent;
562 	    }
563 	    else
564 	    {
565 		path = rxvt_menu_find_base(r, menu, path);
566 		if (path[0] != '\0')		/* not found */
567 		{
568 		    p[0] = '/';	/* fix-up name again */
569 		    return path;
570 		}
571 	    }
572 
573 	    path = (p + 1);
574 	}
575     }
576 
577     if (!STRCMP(path, DOTS))
578     {
579 	path += STRLEN(DOTS);
580 	if (NOT_NULL(*menu))
581 	    *menu = (*menu)->parent;
582 	return path;
583     }
584 
585     /* find this menu */
586     if (IS_NULL(*menu))
587     {
588 	for (m = r->h->MenuBar.tail; NOT_NULL(m); m = m->prev)
589 	{
590 	    if (!STRCMP(path, m->name))
591 	    break;
592 	}
593     }
594     else
595     {
596 	/* find this menu */
597 	for (item = (*menu)->tail; NOT_NULL(item); item = item->prev)
598 	{
599 	    if (
600 		    item->entry.itemType == MenuSubMenu
601 		    && !STRCMP(path, (item->entry.submenu.menu)->name)
602 	       )
603 	    {
604 		m = (item->entry.submenu.menu);
605 		break;
606 	    }
607 	}
608     }
609     if (NOT_NULL(m))
610     {
611 	*menu = m;
612 	path += STRLEN(path);
613     }
614     return path;
615 }
616 
617 /*
618  * delete this entire menu
619  */
620 /* INTPROTO */
621 menu_t*
rxvt_menu_delete(rxvt_t * r,menu_t * menu)622 rxvt_menu_delete(rxvt_t *r, menu_t *menu)
623 {
624     menu_t	 *parent = NULL, *prev, *next;
625     menuitem_t	 *item;
626 
627     rxvt_dbgmsg ((DBG_DEBUG, DBG_MENUBAR, "rxvt_menu_delete()\n"));
628     assert( menu != r->h->ActiveMenu ); /* Shouldn't delete current menu */
629 
630     /* delete the entire menu */
631     if (IS_NULL(menu)) return NULL;
632 
633     parent = menu->parent;
634 
635     /* unlink MENU */
636     prev = menu->prev;
637     next = menu->next;
638     if (NOT_NULL(prev)) prev->next = next;
639     if (NOT_NULL(next)) next->prev = prev;
640 
641     if (IS_NULL(parent))
642     {
643 	/*
644 	 * If we got here, then this menu is either a popup menu, or a pulldown
645 	 * submenu of the main menubar.
646 	 */
647 	const int width = 2 * HSPACE_PIXEL +
648 	    PTEXTWIDTH( r, menu->name, menu->len);
649 
650 	/*
651 	 * Reset start / end of menus in the menubar if we deleted the first /
652 	 * last pulldown menu.
653 	 */
654 	if (r->h->MenuBar.tail == menu) r->h->MenuBar.tail = prev;
655 	if (r->h->MenuBar.head == menu) r->h->MenuBar.head = next;
656 
657 	/*
658 	 * Move all subsequent entries of the menubar to the left. Popups don't
659 	 * have next or prev set, so no worries doing this
660 	 */
661 	for (next = menu->next; NOT_NULL(next); next = next->next)
662 	    next->x -= width;
663     }
664     else
665     {
666 	for (item = parent->tail; NOT_NULL(item); item = item->prev)
667 	{
668 	    if (item->entry.itemType == MenuSubMenu
669 		    && item->entry.submenu.menu == menu)
670 	    {
671 		/* Unlink from the parent menu */
672 		SET_NULL(item->entry.submenu.menu);
673 		rxvt_menuitem_free(r, menu->parent, item);
674 		break;
675 	    }
676 	}
677     }
678 
679     item = menu->tail;
680     while (NOT_NULL(item))
681     {
682 	menuitem_t   *p = item->prev;
683 
684 	rxvt_menuitem_free(r, menu, item);
685 	item = p;
686     }
687 
688     rxvt_free(menu->name);
689     rxvt_free(menu);
690 
691     return parent;
692 }
693 
694 /* INTPROTO */
695 menu_t*
rxvt_menu_add(rxvt_t * r,menu_t * parent,unsigned char * path)696 rxvt_menu_add(rxvt_t *r, menu_t *parent, unsigned char *path)
697 {
698     menu_t	 *menu;
699 
700     rxvt_dbgmsg ((DBG_DEBUG, DBG_MENUBAR, "rxvt_menu_add(): Adding menu %s to parent %s\n", path, NOT_NULL(parent) ? (char *)parent->name : "(nil)"));
701 
702     if (NOT_NULL(STRCHR(path, '/')))
703     {
704 	unsigned char	*p;
705 
706 	if (path[0] == '/')
707 	{
708 	    /* shouldn't happen */
709 	    path++;
710 	    SET_NULL(parent);
711 	}
712 	while (NOT_NULL(p = (unsigned char*) STRCHR(path, '/')))
713 	{
714 	    p[0] = '\0';
715 	    if (path[0] == '\0')
716 	    return NULL;
717 
718 	    parent = rxvt_menu_add(r, parent, path);
719 	    path = (p + 1);
720 	}
721     }
722     if (!STRCMP(path, DOTS))
723 	return (NOT_NULL(parent) ? parent->parent : parent);
724 
725     if (!STRCMP(path, DOT) || path[0] == '\0')
726 	return parent;
727 
728     /* allocate a new menu */
729     menu = (menu_t *) rxvt_malloc(sizeof(menu_t));
730 
731     menu->lwidth = menu->rwidth = 0;
732     menu->parent = parent;
733     menu->len = STRLEN(path);
734     /* possible integer overflow? */
735     assert (menu->len > 0 && menu->len+1 > 0);
736     menu->name = rxvt_malloc((menu->len + 1));
737     STRCPY(menu->name, path);
738 
739     /* initialize head/tail */
740     SET_NULL(menu->head);
741     SET_NULL(menu->tail);
742     SET_NULL(menu->prev);
743     SET_NULL(menu->next);
744 
745     UNSET_WIN(menu->win);
746     menu->x = menu->y = menu->height = 0;
747     SET_NULL(menu->item);
748 
749     if (IS_NULL(parent))
750     {
751 	/* Add menus named PopupButton%d to popupMenu[%d] */
752 	if(
753 	    !STRNCASECMP("popupbutton", (char*) path, (sizeof("popupbutton")-1)) &&
754 	    path[sizeof("popupbutton")-1] >= '1' &&
755 	    path[sizeof("popupbutton")-1] <= '3' &&
756 	    path[sizeof("popupbutton")] == '\0'
757 	  )
758 	{
759 	    int i = path[sizeof("popupbutton")-1] - '1';
760 
761 	    /* Free the menu */
762 	    if( r->h->popupMenu[i] )
763 		rxvt_menu_delete( r, r->h->popupMenu[i] );
764 
765 	    r->h->popupMenu[i] = menu;
766 	}
767 	else
768 	{
769 	    /*
770 	     * Add this menu as a the last pulldown menu in the menubar.
771 	     */
772 	    menu->prev = r->h->MenuBar.tail;
773 	    if (NOT_NULL(r->h->MenuBar.tail))
774 		r->h->MenuBar.tail->next = menu;
775 	    r->h->MenuBar.tail = menu;
776 
777 	    /* fix head */
778 	    if (IS_NULL(r->h->MenuBar.head))
779 		r->h->MenuBar.head = menu;
780 
781 	    if (menu->prev)
782 		menu->x = menu->prev->x + 2 * HSPACE_PIXEL
783 		    + PTEXTWIDTH( r, menu->prev->name, menu->prev->len);
784 	}
785     }
786     else
787     {
788 	menuitem_t   *item;
789 
790 	item = rxvt_menuitem_add(r, parent, path, (unsigned char*) "",
791 		(unsigned char*) "");
792 
793 	if (IS_NULL(item))
794 	{
795 	    if (NOT_NULL(menu->name))
796 		rxvt_free( menu->name );
797 	    rxvt_free(menu);
798 	    return parent;
799 	}
800 #ifdef DEBUG
801 	assert(item->entry.itemType == MenuLabel);
802 #endif
803 	item->entry.itemType = MenuSubMenu;
804 	item->entry.submenu.menu = menu;
805     }
806 
807     return menu;
808 }
809 
810 /* INTPROTO */
811 void
rxvt_drawbox_menubar(rxvt_t * r,int x,int len,int state)812 rxvt_drawbox_menubar(rxvt_t *r, int x, int len, int state)
813 {
814     unsigned long   top, bot;
815 
816     rxvt_dbgmsg ((DBG_DEBUG, DBG_MENUBAR, "rxvt_drawbox_menubar()\n"));
817 
818     /* XXXMENU */
819     len += HSPACE_PIXEL + SHADOW;
820 
821     if (x >= TWIN_WIDTH(r))
822 	return;
823     else if (x + len >= TWIN_WIDTH(r))
824 	len = (TWIN_WIDTH(r) - x);
825 
826 #ifdef MENUBAR_SHADOW_IN
827     state = -state;
828 #endif
829     switch (state)
830     {
831 	case +1:
832 	    top = r->menuBar.topshadow;
833 	    bot = r->menuBar.botshadow;
834 	    break;	    /* SHADOW_OUT */
835 	case -1:
836 	    top = r->menuBar.botshadow;
837 	    bot = r->menuBar.topshadow;
838 	    break;	    /* SHADOW_IN */
839 	default:
840 	    top = bot = r->menuBar.bg;
841 	    break;	    /* neutral */
842     }
843 
844     rxvt_draw_shadow(r->Xdisplay, r->menuBar.win, r->menuBar.gc,
845 	top, bot, x, 0, len, rxvt_menubar_height(r));
846 }
847 
848 
849 /* INTPROTO */
850 void
rxvt_menubar_draw_triangle(rxvt_t * r,int x,int y,int state)851 rxvt_menubar_draw_triangle(rxvt_t *r, int x, int y, int state)
852 {
853     unsigned long   top, bot;
854     int		    w;
855 
856     rxvt_dbgmsg ((DBG_DEBUG, DBG_MENUBAR, "rxvt_menubar_draw_triangle()\n"));
857 
858 #ifdef MENU_SHADOW_IN
859     state = -state;
860 #endif
861     switch (state)
862     {
863 	case +1:
864 	    top = r->menuBar.topshadow;
865 	    bot = r->menuBar.botshadow;
866 	    break;	    /* SHADOW_OUT */
867 	case -1:
868 	    top = r->menuBar.botshadow;
869 	    bot = r->menuBar.topshadow;
870 	    break;	    /* SHADOW_IN */
871 	default:
872 	    top = bot = r->menuBar.bg;
873 	    break;	    /* neutral */
874     }
875 
876     w = TRIANGLE_WIDTH;
877 
878     x -= SHADOW + (3 * w / 2);
879     y += SHADOW*2 + (HEIGHT_TEXT - TRIANGLE_WIDTH)/2;
880 
881     rxvt_draw_triangle (r->Xdisplay, r->h->ActiveMenu->win, r->menuBar.gc, top, bot, x, y, w, 'r');
882 }
883 
884 
885 /* INTPROTO */
886 void
rxvt_drawbox_menuitem(rxvt_t * r,int y,int state)887 rxvt_drawbox_menuitem(rxvt_t *r, int y, int state)
888 {
889     unsigned long   top, bot;
890 
891     rxvt_dbgmsg ((DBG_DEBUG, DBG_MENUBAR, "rxvt_drawbox_menuitem()\n"));
892 
893 #ifdef MENU_SHADOW_IN
894     state = -state;
895 #endif
896     switch (state)
897     {
898 	case +1:
899 	    top = r->menuBar.topshadow;
900 	    bot = r->menuBar.botshadow;
901 	    break;	    /* SHADOW_OUT */
902 	case -1:
903 	    top = r->menuBar.botshadow;
904 	    bot = r->menuBar.topshadow;
905 	    break;	    /* SHADOW_IN */
906 	default:
907 	    top = bot = r->menuBar.bg;
908 	    break;	    /* neutral */
909     }
910 
911     rxvt_draw_shadow (r->Xdisplay, r->h->ActiveMenu->win, r->menuBar.gc,
912 	top, bot,
913 	SHADOW + 0, SHADOW + y,
914 	MENUWIDTH( r->h->ActiveMenu ) - 2 * (SHADOW),
915 	HEIGHT_TEXT + 2 * SHADOW);
916     XFlush(r->Xdisplay);
917 }
918 
919 #ifdef DEBUG
920 /* INTPROTO */
921 void
rxvt_print_menu_ancestors(menu_t * menu)922 rxvt_print_menu_ancestors(menu_t *menu)
923 {
924     rxvt_dbgmsg ((DBG_DEBUG, DBG_MENUBAR, "rxvt_print_menu_ancestors()\n"));
925 
926     if (IS_NULL(menu))
927     {
928 	rxvt_dbgmsg ((DBG_DEBUG, DBG_MENUBAR, "Top Level menu\n"));
929 	return;
930     }
931     rxvt_dbgmsg ((DBG_DEBUG, DBG_MENUBAR, "menu %s ", menu->name));
932     if (NOT_NULL(menu->parent))
933     {
934 	menuitem_t   *item;
935 
936 	for (item = menu->parent->head; NOT_NULL(item); item = item->next)
937 	{
938 	    if (item->entry.itemType == MenuSubMenu &&
939 		item->entry.submenu.menu == menu)
940 	    {
941 		break;
942 	    }
943 	}
944 	if (IS_NULL(item))
945 	{
946 	    rxvt_dbgmsg ((DBG_DEBUG, DBG_MENUBAR, "is an orphan!\n"));
947 	    return;
948 	}
949     }
950     rxvt_dbgmsg ((DBG_DEBUG, DBG_MENUBAR, "\n"));
951     rxvt_print_menu_ancestors(menu->parent);
952 }
953 
954 /* INTPROTO */
955 void
rxvt_print_menu_descendants(menu_t * menu)956 rxvt_print_menu_descendants(menu_t *menu)
957 {
958     rxvt_dbgmsg ((DBG_DEBUG, DBG_MENUBAR, "rxvt_print_menu_descendants()\n"));
959 
960     menuitem_t	 *item;
961     menu_t	 *parent;
962     int		 i, level = 0;
963 
964     parent = menu;
965     do
966       {
967 	level++;
968 	parent = parent->parent;
969       }
970     while (NOT_NULL(parent));
971 
972     for (i = 0; i < level; i++)
973     rxvt_dbgmsg ((DBG_DEBUG, DBG_MENUBAR, ">"));
974     rxvt_dbgmsg ((DBG_DEBUG, DBG_MENUBAR, "%s\n", menu->name));
975 
976     for (item = menu->head; NOT_NULL(item); item = item->next)
977     {
978 	if (item->entry.itemType == MenuSubMenu)
979 	{
980 	    if (IS_NULL(item->entry.submenu.menu))
981 		rxvt_dbgmsg ((DBG_DEBUG, DBG_MENUBAR, "> %s == NULL\n", item->name));
982 	    else
983 		rxvt_print_menu_descendants(item->entry.submenu.menu);
984 	}
985 	else
986 	{
987 	    for (i = 0; i < level; i++)
988 		rxvt_dbgmsg ((DBG_DEBUG, DBG_MENUBAR, "+"));
989 	    if (item->entry.itemType == MenuLabel)
990 		rxvt_dbgmsg ((DBG_DEBUG, DBG_MENUBAR, "label: "));
991 	    rxvt_dbgmsg ((DBG_DEBUG, DBG_MENUBAR, "%s\n", item->name));
992 	}
993     }
994 
995     for (i = 0; i < level; i++)
996     rxvt_dbgmsg ((DBG_DEBUG, DBG_MENUBAR, "<"));
997     rxvt_dbgmsg ((DBG_DEBUG, DBG_MENUBAR, "\n"));
998 }
999 #endif	/* DEBUG */
1000 
1001 /*
1002  * Build a menu with all the titles of current tabs.
1003  */
1004 /* INTPROTO */
1005 void
rxvt_build_tablist(rxvt_t * r,menu_t * menu)1006 rxvt_build_tablist(rxvt_t *r, menu_t *menu)
1007 {
1008     int i;
1009     unsigned char   action[16],
1010 		    title[MAX_DISPLAY_TAB_TXT + 4];
1011 
1012     assert( menu);
1013     rxvt_dbgmsg ((DBG_DEBUG, DBG_MENUBAR, "rxvt_build_tablist()\n"));
1014 
1015     /*
1016      * Empty menus should get renamed to "Switch to tab".
1017      */
1018     if( !menu->head && STRCMP( menu->name, "Switch to tab"))
1019     {
1020 	rxvt_dbgmsg ((DBG_DEBUG, DBG_MENUBAR, "Renaming menu to Switch to tab\n"));
1021 
1022 	menu->name = rxvt_realloc( menu->name, 14);
1023 	STRCPY( menu->name, "Switch to tab");
1024     }
1025 
1026     rxvt_menu_clear( r, menu);
1027     for( i=0; i <= LTAB(r); i++)
1028     {
1029 	sprintf( (char*) title, "%2d. %.*s", i+1, MAX_DISPLAY_TAB_TXT-1,
1030 		PVTS(r, i)->tab_title);
1031 	/* sprintf( action, "]\e]%d;%d\a", Xterm_switchtab, i+1); */
1032 	sprintf( (char*) action, "GotoTab %d", i + 1);
1033 
1034 	rxvt_menuitem_add( r, menu, title, NULL, action);
1035 
1036 	rxvt_dbgmsg ((DBG_DEBUG, DBG_MENUBAR, "Added menuentry for %s\n", PVTS(r, i)->tab_title));
1037     }
1038 }
1039 
1040 /* pop up/down the current menu and redraw the menuBar button */
1041 /* EXTPROTO */
1042 void
rxvt_menu_show(rxvt_t * r)1043 rxvt_menu_show(rxvt_t *r)
1044 {
1045     int		 y, xright;
1046     menu_t	 *ActiveMenu = r->h->ActiveMenu;
1047     menuitem_t	 *item;
1048 
1049     unsigned short amenu_width;
1050 
1051 
1052     rxvt_dbgmsg ((DBG_DEBUG, DBG_MENUBAR, "rxvt_menu_show()\n"));
1053 
1054     if (IS_NULL(ActiveMenu))
1055 	return;
1056 
1057     /*
1058      * Popup tablist for menus named tablist, or empty popupbutton menus.
1059      */
1060     if(
1061 	 !STRCASECMP( (char*) ActiveMenu->name, "Switch to tab")
1062 	 || (
1063 	      !STRNCASECMP( (char*) ActiveMenu->name, "popupbutton", 11 )
1064 	      && ActiveMenu->head == NULL
1065 	    )
1066       )
1067 	rxvt_build_tablist( r, ActiveMenu );
1068 
1069     if (IS_NULL(ActiveMenu->parent))
1070     {
1071 	register int	h;
1072 
1073 	/* find the height */
1074 	for (h = 0, item = ActiveMenu->head; NOT_NULL(item); item = item->next)
1075 	    h += isSeparator(item->name) ? HEIGHT_SEPARATOR
1076 		     : HEIGHT_TEXT + 2 * SHADOW;
1077 	ActiveMenu->height = h + 2 * SHADOW;
1078 
1079 	if( !( r->h->showingMenu & POPUP_MENU ))
1080 	{
1081 	    rxvt_drawbox_menubar(r, ActiveMenu->x,
1082 		    PTEXTWIDTH( r, ActiveMenu->name, ActiveMenu->len), -1);
1083 
1084 	    ActiveMenu->y = rxvt_menubar_height( r );
1085 	}
1086     }
1087 
1088     amenu_width = MENUWIDTH( ActiveMenu );
1089 
1090     /*
1091      * Move popup menus, or submenus back on screen if they are not completely
1092      * on screen.
1093      *
1094      * XXX 2006-02-02 gi1242: It would be a LOT nicer if we could move all
1095      * parents of the menu too. We should only do this for popup menus. This is
1096      * what fvwm does :)
1097      *
1098      * BUG: If a moved menu obscures a grandparent menu, then that menu is not
1099      * redrawn when the child is removed.
1100      */
1101     if( (r->h->showingMenu & POPUP_MENU) || ActiveMenu->parent != NULL)
1102     {
1103 	int	 unused_x, unused_y;
1104 	unsigned rootWidth, rootHeight, unused_depth, unused_border_width;
1105 	Window	 unused_win;
1106 
1107 	XGetGeometry( r->Xdisplay, XROOT, &unused_win, &unused_x, &unused_y,
1108 		&rootWidth, &rootHeight,
1109 		&unused_border_width, &unused_depth);
1110 
1111 	if( r->szHint.x + ActiveMenu->x + amenu_width >= rootWidth
1112 		&& amenu_width < rootWidth)
1113 	    ActiveMenu->x = rootWidth - amenu_width - r->szHint.x;
1114 
1115 	if( r->szHint.y + ActiveMenu->y + ActiveMenu->height >= rootHeight
1116 		&& ActiveMenu->height < rootHeight)
1117 	    ActiveMenu->y = rootHeight - ActiveMenu->height - r->szHint.y;
1118     }
1119 
1120     /*
1121      * Create the menu window.
1122      */
1123     if (NOT_WIN(ActiveMenu->win))
1124     {
1125 #if 0
1126 	ActiveMenu->win = XCreateSimpleWindow(r->Xdisplay,
1127 			    r->TermWin.parent,
1128 			    x, ActiveMenu->y,
1129 			    ActiveMenu->width, ActiveMenu->height,
1130 			    0,
1131 			    r->pixColorsFocus[Color_fg],
1132 			    r->pixColorsFocus[Color_scroll]);
1133 #endif
1134 	/*
1135 	 * 2006-02-05 gi1242: We should make menu windows childs of the root
1136 	 * window. That way they can go outside the terminal window's
1137 	 * boundaries.
1138 	 */
1139 	XSetWindowAttributes attrs;
1140 
1141 	attrs.override_redirect = True;
1142 	attrs.save_under = True;
1143 	attrs.cursor = r->h->bar_pointer;
1144 	attrs.background_pixel = r->pixColorsFocus[ Color_scroll ];
1145 	attrs.border_pixel = r->pixColorsFocus[Color_fg];
1146 
1147 	ActiveMenu->win = XCreateWindow( r->Xdisplay, XROOT,
1148 		r->szHint.x + ActiveMenu->x, r->szHint.y + ActiveMenu->y,
1149 		amenu_width, ActiveMenu->height, 0,
1150 		CopyFromParent, CopyFromParent, CopyFromParent,
1151 		CWOverrideRedirect | CWSaveUnder | CWCursor
1152 		    | CWBackPixel | CWBorderPixel, &attrs);
1153 
1154 
1155 	XMapWindow(r->Xdisplay, ActiveMenu->win);
1156     }
1157 #ifdef XFT_SUPPORT
1158     else if (ISSET_OPTION(r, Opt_xft))
1159 	XClearWindow( r->Xdisplay, ActiveMenu->win );
1160 #endif
1161 
1162     rxvt_draw_shadow (r->Xdisplay, ActiveMenu->win, r->menuBar.gc,
1163 	r->menuBar.topshadow, r->menuBar.botshadow,
1164 	0, 0, amenu_width, ActiveMenu->height);
1165 
1166     /* determine the correct right-alignment */
1167     for (xright = 0, item = ActiveMenu->head; NOT_NULL(item); item = item->next)
1168 	if( item->name2 && item->len )
1169 	{
1170 	    int width = PTEXTWIDTH( r, item->name2, item->len2);
1171 
1172 	    if( xright < width ) xright = width;
1173 	}
1174 
1175     for (y = 0, item = ActiveMenu->head; NOT_NULL(item); item = item->next)
1176     {
1177 	const int	xoff = (SHADOW + HSPACE_PIXEL/2);
1178 	register int	h;
1179 
1180 	if (isSeparator(item->name))
1181 	{
1182 	    rxvt_draw_shadow (r->Xdisplay, ActiveMenu->win,
1183 		r->menuBar.gc,
1184 		r->menuBar.topshadow, r->menuBar.botshadow,
1185 		SHADOW, y + SHADOW + 1,
1186 		amenu_width - 2 * SHADOW, 0);
1187 	    h = HEIGHT_SEPARATOR;
1188 	}
1189 	else
1190 	{
1191 	    unsigned char	*name	= item->name;
1192 	    unsigned short	len	= item->len;
1193 
1194 	    if (item->entry.itemType == MenuSubMenu)
1195 	    {
1196 		int	     x1, y1;
1197 		menuitem_t   *it;
1198 		menu_t	     *menu = item->entry.submenu.menu;
1199 
1200 		rxvt_menubar_draw_triangle(r, amenu_width, y, +1);
1201 
1202 		name = menu->name;
1203 		len = menu->len;
1204 
1205 		if( name && !STRCMP( menu->name, "Switch to tab"))
1206 		    rxvt_build_tablist( r, menu);
1207 
1208 		y1 = ActiveMenu->y + y;
1209 #if 0
1210 		/* place sub-menu at midpoint of parent menu */
1211 		x1 = ActiveMenu->width / 2;
1212 
1213 		if (x1 > menu->width)	/* right-flush menu if too small */
1214 		    x1 += (x1 - menu->width);
1215 		x1 += x;
1216 #endif
1217 		/*
1218 		 * 2006-01-28 gi1242: Oh my god. This is so ugly. Place
1219 		 * sub-menus near the triangle. That'll look a *little* better.
1220 		 */
1221 		x1 = ActiveMenu->x + amenu_width - TRIANGLE_WIDTH - xoff;
1222 
1223 		/* find the height of this submenu */
1224 		for (h = 0, it = menu->head; NOT_NULL(it); it = it->next)
1225 		    h += isSeparator(it->name) ? HEIGHT_SEPARATOR
1226 				   : HEIGHT_TEXT + 2 * SHADOW;
1227 		menu->height = h + 2 * SHADOW;
1228 
1229 		/*
1230 		 * XXX 2006-02-02 gi1242: Should we really move the menu back
1231 		 * over the terminal window? It's a better idea to do what fvwm
1232 		 * does.
1233 		 */
1234 #if 0
1235 		/* ensure menu is in window limits */
1236 		if ((x1 + menu->width) >= TWIN_WIDTH(r))
1237 		    x1 = (TWIN_WIDTH(r) - menu->width);
1238 
1239 		if ((y1 + menu->height) >= TWIN_HEIGHT(r))
1240 		    y1 = (TWIN_HEIGHT(r) - menu->height);
1241 #endif
1242 
1243 		menu->x = (x1 < 0 ? 0 : x1);
1244 		menu->y = (y1 < 0 ? 0 : y1);
1245 	    }
1246 	    else if (item->name2 && !STRCMP(name, item->name2))
1247 		SET_NULL(name);
1248 
1249 
1250 	    if (item->entry.itemType == MenuLabel)
1251 	    {
1252 		CHOOSE_GC_FG (r, r->menuBar.botshadow);
1253 	    }
1254 	    else
1255 	    {
1256 		CHOOSE_GC_FG (r, r->menuBar.fg);
1257 	    }
1258 
1259 
1260 	    if (len && name)
1261 	    {
1262 # ifdef XFT_SUPPORT
1263 		/*
1264 		 * TODO: Add multichar support.
1265 		 */
1266 		if (ISSET_OPTION(r, Opt_xft))
1267 		{
1268 		    XftFont *font = r->TermWin.xftpfont ?
1269 			r->TermWin.xftpfont : r->TermWin.xftfont;
1270 
1271 		    XftDrawChange( r->menuBar.xftDraw, ActiveMenu->win);
1272 		    XftDrawString8( r->menuBar.xftDraw, &r->menuBar.xftFore,
1273 			    font, xoff, 2 * SHADOW + y + font->ascent + 1,
1274 			    name, len);
1275 		}
1276 		else
1277 #endif
1278 #ifdef USE_XIM
1279 		if (r->TermWin.fontset)
1280 		{
1281 		    XmbDrawString(r->Xdisplay,
1282 			  ActiveMenu->win, r->TermWin.fontset,
1283 			  r->menuBar.gc, xoff,
1284 			  2 * SHADOW + y + r->TermWin.font->ascent + 1,
1285 			  (char*) name, len);
1286 		}
1287 		else
1288 #endif
1289 		XDrawString(r->Xdisplay, ActiveMenu->win,
1290 		    r->menuBar.gc, xoff,
1291 		    2 * SHADOW + y + r->TermWin.font->ascent + 1,
1292 		    (char*) name, len);
1293 	    }
1294 
1295 	    len = item->len2;
1296 	    name = item->name2;
1297 	    if (len && name)
1298 	    {
1299 # ifdef XFT_SUPPORT
1300 		/*
1301 		 * XXX Add multichar support.
1302 		 */
1303 		if (ISSET_OPTION(r, Opt_xft))
1304 		{
1305 		    XftFont *font = r->TermWin.xftpfont ?
1306 			r->TermWin.xftpfont : r->TermWin.xftfont;
1307 
1308 		    XftDrawChange( r->menuBar.xftDraw, ActiveMenu->win);
1309 		    XftDrawString8( r->menuBar.xftDraw, &r->menuBar.xftFore,
1310 			    font,
1311 			    amenu_width - (xoff + xright),
1312 			    2 * SHADOW + y + font->ascent + 1,
1313 			    name, len);
1314 		}
1315 		else
1316 #endif
1317 #ifdef USE_XIM
1318 		if (r->TermWin.fontset)
1319 		    XmbDrawString(r->Xdisplay,
1320 			ActiveMenu->win, r->TermWin.fontset,
1321 			r->menuBar.gc,
1322 			amenu_width - (xoff + xright),
1323 			2 * SHADOW + y + r->TermWin.font->ascent + 1,
1324 			(char*) name, len);
1325 		else
1326 #endif
1327 		XDrawString(r->Xdisplay, ActiveMenu->win,
1328 		    r->menuBar.gc,
1329 		    amenu_width - (xoff + xright),
1330 		    2 * SHADOW + y + r->TermWin.font->ascent + 1,
1331 		    (char*) name, len);
1332 	    }
1333 	    h = HEIGHT_TEXT + 2 * SHADOW;
1334 	}
1335 	y += h;
1336     }
1337 #ifdef XFT_SUPPORT
1338     if (ISSET_OPTION(r, Opt_xft))
1339 	/*
1340 	 * 2006-01-29 gi1242: For some reason if we leave xftDraw
1341 	 * with drawable ActiveMenu->win, we get a RenderBadPicture
1342 	 * error (I'm not sure on what function call). Thus we reset
1343 	 * it.
1344 	 */
1345 	XftDrawChange( r->menuBar.xftDraw, r->menuBar.win);
1346 #endif
1347 }
1348 
1349 
1350 /*
1351  * Destroy the ActiveMenu window if any, and redisplay (using update) the
1352  * parent. Setting updated to recursively call this function will hide all menus
1353  * (as in rxvt_menu_hide_all)
1354  */
1355 /* INTPROTO */
1356 void
rxvt_menu_display(rxvt_t * r,void (* update)(rxvt_t *))1357 rxvt_menu_display(rxvt_t *r, void (*update)(rxvt_t *))
1358 {
1359     menu_t	 *ActiveMenu = r->h->ActiveMenu;
1360 
1361     rxvt_dbgmsg ((DBG_DEBUG, DBG_MENUBAR, "rxvt_menu_display()\n"));
1362 
1363     if (IS_NULL(ActiveMenu))
1364 	return;
1365 
1366     if (IS_WIN(ActiveMenu->win))
1367     {
1368 	XDestroyWindow(r->Xdisplay, ActiveMenu->win);
1369 	UNSET_WIN(ActiveMenu->win);
1370     }
1371     SET_NULL(ActiveMenu->item);
1372 
1373     if (IS_NULL(ActiveMenu->parent) &&
1374 	    !(r->h->showingMenu & POPUP_MENU) )
1375 	/*
1376 	 * 2006-01-30 gi1242: Just clearing the shadow will be enough. But with
1377 	 * transparency, drawbox_menubar(0) is not enough.
1378 	 */
1379 #if 0
1380 	rxvt_drawbox_menubar(r, ActiveMenu->x, ActiveMenu->len, 0);
1381 #endif
1382 	rxvt_menubar_draw_labels(r);
1383 
1384     r->h->ActiveMenu = ActiveMenu->parent;
1385     update(r);
1386 }
1387 
1388 /* INTPROTO */
1389 void
rxvt_menu_hide_all(rxvt_t * r)1390 rxvt_menu_hide_all(rxvt_t *r)
1391 {
1392     rxvt_dbgmsg ((DBG_DEBUG, DBG_MENUBAR, "rxvt_menu_hide_all()\n"));
1393 
1394     rxvt_menu_display(r, rxvt_menu_hide_all);
1395     /* r->h->showingMenu &= ~PULLDOWN_MENU; */
1396 }
1397 
1398 /*
1399  * Hide current menu window, and redisplay the parent.
1400  */
1401 /* INTPROTO */
1402 void
rxvt_menu_hide(rxvt_t * r)1403 rxvt_menu_hide(rxvt_t *r)
1404 {
1405     rxvt_dbgmsg ((DBG_DEBUG, DBG_MENUBAR, "rxvt_menu_hide()\n"));
1406 
1407     /*
1408      * rxvt_menu_display just destroys the current menu window, and reshow's the
1409      * parent (if rxvt_menu_show is passed as update function).
1410      */
1411     rxvt_menu_display(r, rxvt_menu_show);
1412 }
1413 
1414 /* INTPROTO */
1415 void
rxvt_menu_clear(rxvt_t * r,menu_t * menu)1416 rxvt_menu_clear(rxvt_t *r, menu_t *menu)
1417 {
1418     rxvt_dbgmsg ((DBG_DEBUG, DBG_MENUBAR, "rxvt_menu_clear()\n"));
1419 
1420     if (NOT_NULL(menu))
1421     {
1422 	menuitem_t   *item = menu->tail;
1423 
1424 	while (NOT_NULL(item))
1425 	{
1426 	    rxvt_menuitem_free(r, menu, item);
1427 	    /* it didn't get freed ... why? */
1428 	    if (item == menu->tail) return;
1429 
1430 	    item = menu->tail;
1431 	}
1432 	menu->lwidth = menu->rwidth = 0;
1433     }
1434 }
1435 
1436 /* INTPROTO */
1437 void
rxvt_menubar_clear(rxvt_t * r)1438 rxvt_menubar_clear(rxvt_t *r)
1439 {
1440     menu_t	 *menu = r->h->MenuBar.tail;
1441     rxvt_dbgmsg ((DBG_DEBUG, DBG_MENUBAR, "rxvt_menubar_clear()\n"));
1442 
1443     while (NOT_NULL(menu))
1444     {
1445 	menu_t	     *prev = menu->prev;
1446 
1447 	rxvt_menu_delete(r, menu);
1448 	menu = prev;
1449     }
1450     SET_NULL(r->h->MenuBar.head);
1451     SET_NULL(r->h->MenuBar.tail);
1452 
1453     if (r->h->MenuBar.title)
1454     {
1455 	rxvt_free(r->h->MenuBar.title);
1456 	SET_NULL(r->h->MenuBar.title);
1457     }
1458     rxvt_menuarrow_free(r, 0);	/* remove all arrow functions */
1459 }
1460 
1461 
1462 /* INTPROTO */
1463 void
rxvt_draw_arrows(rxvt_t * r,int name,int state)1464 rxvt_draw_arrows(rxvt_t *r, int name, int state)
1465 {
1466     unsigned long   top, bot;
1467 
1468     int		 i;
1469 
1470     rxvt_dbgmsg ((DBG_DEBUG, DBG_MENUBAR, "rxvt_draw_arrows()\n"));
1471 
1472 #ifdef MENU_SHADOW_IN
1473     state = -state;
1474 #endif
1475     switch (state)
1476     {
1477 	case +1:
1478 	    top = r->menuBar.topshadow;
1479 	    bot = r->menuBar.botshadow;
1480 	    break;	    /* SHADOW_OUT */
1481 
1482 	case -1:
1483 	    top = r->menuBar.botshadow;
1484 	    bot = r->menuBar.topshadow;
1485 	    break;	    /* SHADOW_IN */
1486 
1487 	default:
1488 	    top = bot = r->menuBar.bg;
1489 	    break;	    /* neutral */
1490     }
1491 
1492     if (!r->h->Arrows_x)
1493 	return;
1494 
1495     for (i = 0; i < NARROWS; i++)
1496     {
1497 	const int   w = MENUBAR_ARROW_WIDTH;
1498 	const int   y = (rxvt_menubar_height(r) - w) / 2;
1499 
1500 	int	    x = r->h->Arrows_x + (5 * MENUBAR_ARROW_WIDTH * i) / 4;
1501 
1502 	if (!name || name == Arrows[i].name)
1503 	    rxvt_draw_triangle (r->Xdisplay, r->menuBar.win,
1504 		r->menuBar.gc, top, bot,
1505 		x, y, w, Arrows[i].name);
1506     }
1507     XFlush(r->Xdisplay);
1508 }
1509 
1510 
1511 /* EXTPROTO */
1512 int
rxvt_menu_select(rxvt_t * r,XButtonEvent * ev)1513 rxvt_menu_select(rxvt_t *r, XButtonEvent *ev)
1514 {
1515     menuitem_t	    *thisitem, *item = NULL;
1516     int		    this_y, y;
1517     menu_t	    *ActiveMenu = r->h->ActiveMenu;
1518     unsigned short  amenu_width = MENUWIDTH( ActiveMenu );
1519 
1520     Window	    unused_root, unused_child;
1521     int		    unused_root_x, unused_root_y;
1522     unsigned int    unused_mask;
1523 
1524     rxvt_dbgmsg ((DBG_DEBUG, DBG_MENUBAR, "rxvt_menu_select() (%d)\n", ev->type));
1525 
1526     if (IS_NULL(ActiveMenu)) return 0;
1527 
1528     XQueryPointer(r->Xdisplay, ActiveMenu->win,
1529 	  &unused_root, &unused_child,
1530 	  &unused_root_x, &unused_root_y,
1531 	  &(ev->x), &(ev->y), &unused_mask);
1532 
1533     if (NOT_NULL(ActiveMenu->parent) && (ev->x < 0 || ev->y < 0))
1534     {
1535 	rxvt_menu_hide(r);
1536 	return 1;
1537     }
1538 
1539     /* determine the menu item corresponding to the Y index */
1540     y = SHADOW;
1541     if (ev->x >= 0 && ev->x <= (amenu_width - SHADOW))
1542     {
1543 	for (item = ActiveMenu->head; NOT_NULL(item); item = item->next)
1544 	{
1545 	    int		 h = HEIGHT_TEXT + 2 * SHADOW;
1546 
1547 	    if (isSeparator(item->name)) h = HEIGHT_SEPARATOR;
1548 	    else if (ev->y >= y && ev->y < (y + h))
1549 		break;
1550 	    y += h;
1551 	}
1552     }
1553     if (IS_NULL(item) && ev->type == ButtonRelease)
1554     {
1555 	rxvt_menu_hide_all(r);
1556 	return 0;
1557     }
1558 
1559     thisitem = item;
1560     this_y = y - SHADOW;
1561 
1562     /* erase the last item */
1563     if (NOT_NULL(ActiveMenu->item) || ev->type == ButtonRelease)
1564     {
1565 	if (ActiveMenu->item && ActiveMenu->item != thisitem)
1566 	{
1567 	    /*
1568 	     * Erase shadow of old menuitem. We can not get here during a button
1569 	     * release.
1570 	     */
1571 	    for (y = 0, item = ActiveMenu->head;
1572 		    NOT_NULL(item);
1573 		    item = item->next)
1574 	    {
1575 		int	     h;
1576 
1577 		if (isSeparator(item->name))
1578 		    h = HEIGHT_SEPARATOR;
1579 		else if (item == ActiveMenu->item)
1580 		{
1581 		    /* erase old menuitem shadow */
1582 		    rxvt_drawbox_menuitem(r, y, 0); /* No Shadow */
1583 		    if (item->entry.itemType == MenuSubMenu)
1584 			rxvt_menubar_draw_triangle(r, amenu_width, y, +1);
1585 		    break;
1586 		}
1587 		else
1588 		    h = HEIGHT_TEXT + 2 * SHADOW;
1589 		y += h;
1590 	    }
1591 	}
1592 	else
1593 	{
1594 	    switch (ev->type)
1595 	    {
1596 		case ButtonRelease:
1597 		    rxvt_dbgmsg ((DBG_VERBOSE, DBG_MENUBAR, "Menuitem released\n"));
1598 		    switch (item->entry.itemType)
1599 		    {
1600 			case MenuLabel:
1601 			case MenuSubMenu:
1602 			    rxvt_menu_hide_all(r);
1603 			    break;
1604 
1605 			case MenuItem:
1606 			    rxvt_drawbox_menuitem(r, this_y, -1);
1607 			    {
1608 #ifdef HAVE_NANOSLEEP
1609 				struct timespec rqt;
1610 
1611 				rqt.tv_sec = 0;
1612 				rqt.tv_nsec = MENU_DELAY_USEC * 1000;
1613 				nanosleep(&rqt, NULL);
1614 #else
1615 				/* use select for timing */
1616 				struct timeval  tv;
1617 
1618 				tv.tv_sec = 0;
1619 				tv.tv_usec = MENU_DELAY_USEC;
1620 				select(0, NULL, NULL, NULL, &tv);
1621 #endif
1622 			    }
1623 			    /* remove menu before sending keys to the application */
1624 			    rxvt_menu_hide_all(r);
1625 			    rxvt_dispatch_action(r, &(item->entry.action),
1626 				    (XEvent *) ev );
1627 #ifdef DEBUG        /* DEBUG */
1628 			    rxvt_dbgmsg ((DBG_VERBOSE, DBG_MENUBAR, "%s: %s\n", item->name, item->entry.action.str));
1629 #endif		    /* DEBUG */
1630 			    break;
1631 		    }
1632 		    break;
1633 
1634 		default:
1635 		    if (item->entry.itemType == MenuSubMenu)
1636 			goto DoMenu;
1637 		    break;
1638 	    }
1639 	    return 0;
1640 	}
1641     }
1642 
1643 DoMenu:
1644     ActiveMenu->item = thisitem;
1645     y = this_y;
1646     if (NOT_NULL(thisitem))
1647     {
1648 	item = ActiveMenu->item;
1649 	if (item->entry.itemType != MenuLabel)
1650 	    rxvt_drawbox_menuitem(r, y, +1);
1651 	if (item->entry.itemType == MenuSubMenu)
1652 	{
1653 	    int		 x;
1654 
1655 	    rxvt_menubar_draw_triangle(r, amenu_width, y, -1);
1656 
1657 	    x = ev->x + ActiveMenu->x;
1658 
1659 	    if (x >= item->entry.submenu.menu->x)
1660 	    {
1661 		r->h->ActiveMenu = item->entry.submenu.menu;
1662 		rxvt_menu_show(r);
1663 		return 1;
1664 	    }
1665 	}
1666     }
1667     return 0;
1668 }
1669 
1670 /* INTPROTO */
1671 void
rxvt_menubar_select(rxvt_t * r,XButtonEvent * ev)1672 rxvt_menubar_select(rxvt_t *r, XButtonEvent *ev)
1673 {
1674     menu_t	 *menu = NULL;
1675 
1676     rxvt_dbgmsg ((DBG_DEBUG, DBG_MENUBAR, "rxvt_menubar_select()\n"));
1677 
1678     /* determine the pulldown menu corresponding to the X index */
1679     if ( ev->y >= 0 && ev->y <= (rxvt_menubar_height(r)-MENUBAR_MARGIN) )
1680     {
1681 	for (menu = r->h->MenuBar.head; NOT_NULL(menu); menu = menu->next)
1682 	{
1683 	    int w = HSPACE_PIXEL + PTEXTWIDTH( r, menu->name, menu->len);
1684 
1685 	    if ((ev->x >= menu->x && ev->x < menu->x + w)) break;
1686 	}
1687     }
1688     switch (ev->type)
1689     {
1690 	case ButtonRelease:
1691 	    rxvt_menu_hide_all(r);
1692 	    break;
1693 
1694 	case ButtonPress:
1695 	    if (IS_NULL(menu) && r->h->Arrows_x && ev->x >= r->h->Arrows_x)
1696 	    {
1697 		int	     i;
1698 
1699 		for (i = 0; i < NARROWS; i++)
1700 		{
1701 		    if (ev->x >= (r->h->Arrows_x + (Width2Pixel(4 * i + i)) / 4)
1702 			&& ev->x < (r->h->Arrows_x
1703 				+ (Width2Pixel(4 * i + i + 4)) / 4))
1704 		    {
1705 			rxvt_draw_arrows(r, Arrows[i].name, -1);
1706 			{
1707 #ifdef HAVE_NANOSLEEP
1708 			    struct timespec rqt;
1709 
1710 			    rqt.tv_sec = 0;
1711 			    rqt.tv_nsec = MENU_DELAY_USEC * 1000;
1712 			    nanosleep(&rqt, NULL);
1713 #else
1714 			    /* use select for timing */
1715 			    struct timeval  tv;
1716 
1717 			    tv.tv_sec = 0;
1718 			    tv.tv_usec = MENU_DELAY_USEC;
1719 			    select(0, NULL, NULL, NULL, &tv);
1720 #endif
1721 			}
1722 			rxvt_draw_arrows(r, Arrows[i].name, +1);
1723 
1724 			rxvt_dispatch_action(r, &(r->h->MenuBar.arrows[i]),
1725 				(XEvent *) ev);
1726 			return;
1727 		    }
1728 		}
1729 	    }
1730 	    /* FALLTHROUGH */
1731 
1732 	default:
1733 	/*
1734 	 * press menubar or move to a new entry
1735 	 */
1736 	if (NOT_NULL(menu) && menu != r->h->ActiveMenu)
1737 	{
1738 	    rxvt_menu_hide_all(r);  /* pop down old menu */
1739 	    r->h->ActiveMenu = menu;
1740 	    rxvt_menu_show(r);	/* pop up new menu */
1741 	}
1742 	break;
1743     }
1744 }
1745 
1746 
1747 
1748 /* EXTPROTO */
1749 void
rxvt_menubar_create(rxvt_t * r)1750 rxvt_menubar_create (rxvt_t* r)
1751 {
1752     XGCValues	    gcvalue;
1753     unsigned long   gcmask;
1754 
1755     rxvt_dbgmsg ((DBG_DEBUG, DBG_MENUBAR,"rxvt_menubar_create\n"));
1756     /*
1757      * Only create a menubar if it's non-empty.
1758      */
1759 
1760     r->menuBar.state = 0;
1761 
1762     r->menuBar.win = XCreateSimpleWindow(r->Xdisplay,
1763 		    r->TermWin.parent,
1764 		    0, 0,
1765 		    TWIN_WIDTH(r), rxvt_menubar_rheight (r),
1766 		    0, r->pixColorsFocus[Color_fg],
1767 		    r->pixColorsFocus[Color_scroll]);
1768     assert (IS_WIN(r->menuBar.win));
1769 
1770 #  ifdef DEBUG_X
1771     rxvt_set_win_title (r, r->menuBar.win, "menubar");
1772 #  endif
1773 
1774     XDefineCursor(r->Xdisplay, r->menuBar.win, r->h->bar_pointer);
1775     XSelectInput(r->Xdisplay, r->menuBar.win,
1776 	      (ExposureMask | ButtonPressMask | ButtonReleaseMask
1777 	      | Button1MotionMask));
1778 
1779 #  ifdef BACKGROUND_IMAGE
1780     UNSET_PIXMAP(r->menuBar.pixmap);	/* Initialize it to None */
1781 #   ifdef TRANSPARENT
1782     if (!(ISSET_OPTION(r, Opt_transparent) &&
1783 	  ISSET_OPTION(r, Opt_transparent_menubar)
1784 	))
1785 #   endif
1786     if (r->h->rs[Rs_menubarPixmap])
1787     {
1788 	long	w = 0, h = 0;
1789 	r->menuBar.pixmap = rxvt_load_pixmap (r,
1790 				r->h->rs[Rs_menubarPixmap], &w, &h);
1791 	if (IS_PIXMAP(r->menuBar.pixmap))
1792 	    XSetWindowBackgroundPixmap (r->Xdisplay, r->menuBar.win,
1793 		r->menuBar.pixmap);
1794     }
1795 #  endif
1796 
1797 #  ifdef TRANSPARENT
1798     if (ISSET_OPTION(r, Opt_transparent) &&
1799 	ISSET_OPTION(r, Opt_transparent_menubar))
1800     {
1801 	XSetWindowBackgroundPixmap (r->Xdisplay, r->menuBar.win,
1802 	    ParentRelative);
1803     }
1804 #  endif
1805 
1806     /*
1807      * Initialize the colors. TODO: Add a sperate resource for this, instead of
1808      * mooching of the scroll bar resources.
1809      */
1810     if( XDEPTH > 2 )
1811     {
1812 	/*
1813 	 *  If Color_scroll is too dark, then we should use White for the menu
1814 	 *  foreground.
1815 	 */
1816 	XColor xcol;
1817 
1818 	xcol.pixel = r->pixColorsFocus[Color_scroll];
1819 	XQueryColor( r->Xdisplay, XCMAP, &xcol);
1820 
1821 	r->menuBar.fg = r->pixColorsFocus[
1822 		(xcol.red <= 0x60 && xcol.green <= 0x60 && xcol.blue <= 0x60 )
1823 		    ? Color_White : Color_Black ];
1824     }
1825     else
1826 	r->menuBar.fg = r->pixColorsFocus[ Color_bg ]; /* Reverse video */
1827 
1828     r->menuBar.bg = r->pixColorsFocus[XDEPTH <= 2 ? Color_fg : Color_scroll];
1829     r->menuBar.topshadow = r->pixColorsFocus[Color_topShadow];
1830     r->menuBar.botshadow = r->pixColorsFocus[Color_bottomShadow];
1831 
1832 
1833     gcvalue.foreground = r->menuBar.fg;
1834 #  ifdef TRANSPARENT
1835     if (!(ISSET_OPTION(r, Opt_transparent) &&
1836 	  ISSET_OPTION(r, Opt_transparent_menubar)
1837 	))
1838 #  endif
1839 #  ifdef BACKGROUND_IMAGE
1840     if (NOT_PIXMAP(r->menuBar.pixmap))
1841 #  endif
1842     gcvalue.background = r->menuBar.bg;
1843     gcmask = GCForeground;
1844 
1845 #  ifdef TRANSPARENT
1846     if (!(ISSET_OPTION(r, Opt_transparent) &&
1847 	  ISSET_OPTION(r, Opt_transparent_menubar)
1848 	))
1849 #  endif
1850 #  ifdef BACKGROUND_IMAGE
1851     if (NOT_PIXMAP(r->menuBar.pixmap))
1852 #  endif
1853     gcmask |= GCBackground;
1854     r->menuBar.gc = XCreateGC (r->Xdisplay, r->menuBar.win,
1855 			gcmask, &gcvalue);
1856     assert (IS_GC(r->menuBar.gc));
1857 
1858 #  ifdef XFT_SUPPORT
1859     if (ISSET_OPTION(r, Opt_xft))
1860     {
1861 	/*
1862 	 * Set up Xft stuff here.
1863 	 */
1864 	XColor	xcol;
1865 
1866 	r->menuBar.xftDraw = XftDrawCreate( r->Xdisplay, r->menuBar.win,
1867 		XVISUAL, XCMAP);
1868 
1869 	xcol.pixel = r->menuBar.fg;
1870 	XQueryColor( r->Xdisplay, XCMAP, &xcol );
1871 	rxvt_alloc_xft_color( r, &xcol, &r->menuBar.xftFore);
1872     }
1873     else
1874 #  endif
1875     {
1876 	XSetFont(r->Xdisplay, r->menuBar.gc, r->TermWin.font->fid);
1877     }
1878 }
1879 
1880 
1881 /* EXTPROTO */
1882 void
rxvt_menubar_clean_exit(rxvt_t * r)1883 rxvt_menubar_clean_exit (rxvt_t* r)
1884 {
1885     rxvt_dbgmsg ((DBG_DEBUG, DBG_MENUBAR, "rxvt_menubar_clean_exit()\n"));
1886 
1887 #  ifdef XFT_SUPPORT
1888     /*
1889      * Sometimes gives a RenderBadPicture error, so don't destroy it.
1890      */
1891 #   if 0
1892     if (ISSET_OPTION(r, Opt_xft))
1893     {
1894 	XftDrawDestroy( r->menuBar.xftDraw);
1895     }
1896 #   endif
1897 #  endif
1898 
1899     UNSET_WIN(r->menuBar.win);	/* Destroyed by XDestroySubwindows */
1900 
1901     if (IS_GC(r->menuBar.gc))
1902     {
1903 	XFreeGC (r->Xdisplay, r->menuBar.gc);
1904 	UNSET_GC(r->menuBar.gc);
1905     }
1906 #  ifdef BACKGROUND_IMAGE
1907     if (IS_PIXMAP(r->menuBar.pixmap))
1908     {
1909 	XFreePixmap (r->Xdisplay, r->menuBar.pixmap);
1910 	UNSET_PIXMAP(r->menuBar.pixmap);
1911     }
1912 #  endif
1913 }
1914 
1915 
1916 /*
1917 ** Is the menubar visible
1918 */
1919 /* EXTPROTO */
1920 int
rxvt_menubar_visible(rxvt_t * r)1921 rxvt_menubar_visible (rxvt_t* r)
1922 {
1923     rxvt_dbgmsg ((DBG_DEBUG, DBG_MENUBAR, "rxvt_menubar_visible()\n"));
1924 
1925     return (IS_WIN(r->menuBar.win) && r->menuBar.state);
1926 }
1927 
1928 
1929 /*
1930 ** Hide the menubar
1931 */
1932 /* EXTPROTO */
1933 int
rxvt_menubar_hide(rxvt_t * r)1934 rxvt_menubar_hide (rxvt_t* r)
1935 {
1936     int	    changed = 0;
1937 
1938     rxvt_dbgmsg ((DBG_DEBUG, DBG_MENUBAR, "rxvt_menubar_hide()\n"));
1939 
1940     assert (IS_WIN(r->menuBar.win));
1941     changed = r->menuBar.state;
1942     XUnmapWindow(r->Xdisplay, r->menuBar.win);
1943     r->menuBar.state = 0;
1944 
1945     return (changed);
1946 }
1947 
1948 
1949 /*
1950 ** Show the menubar
1951 */
1952 /* EXTPROTO */
1953 int
rxvt_menubar_show(rxvt_t * r)1954 rxvt_menubar_show (rxvt_t* r)
1955 {
1956     int	    changed = 0;
1957 
1958     rxvt_dbgmsg ((DBG_DEBUG, DBG_MENUBAR, "rxvt_menubar_show()\n"));
1959     assert (IS_WIN(r->menuBar.win));
1960 
1961     changed = !r->menuBar.state;
1962     XMapWindow(r->Xdisplay, r->menuBar.win);
1963     r->menuBar.state = 1;
1964 
1965     return (changed);
1966 }
1967 
1968 
1969 /*
1970  * Menubar expose handler
1971  */
1972 /* EXTPROTO */
1973 void
rxvt_menubar_expose(rxvt_t * r)1974 rxvt_menubar_expose(rxvt_t *r)
1975 {
1976     rxvt_dbgmsg ((DBG_DEBUG, DBG_MENUBAR, "rxvt_menubar_expose()\n"));
1977 
1978     if (!r->menuBar.state || NOT_WIN(r->menuBar.win))
1979 	return;
1980 
1981     rxvt_menu_hide_all(r);
1982     rxvt_menubar_draw_labels(r);
1983 }
1984 
1985 
1986 /* INTPROTO */
1987 void
rxvt_menubar_draw_labels(rxvt_t * r)1988 rxvt_menubar_draw_labels( rxvt_t *r)
1989 {
1990     menu_t	 *menu;
1991     int		 x;
1992 
1993     XClearWindow(r->Xdisplay, r->menuBar.win);
1994 
1995     x = 0;
1996     for (menu = r->h->MenuBar.head; NOT_NULL(menu); menu = menu->next)
1997     {
1998 	unsigned short	 len = menu->len;
1999 
2000 	x = (menu->x + menu->len + HSPACE);
2001 
2002 # ifdef DEBUG
2003 	rxvt_print_menu_descendants(menu);
2004 # endif
2005 
2006 	/* if (x >= r->TermWin.ncol)
2007 	    len = (r->TermWin.ncol - (menu->x + HSPACE)); */
2008 
2009 	/* 2006-01-29 gi1242: Boxes in the menubar are ugly */
2010 #if 0
2011 	rxvt_drawbox_menubar(r, menu->x, len, +1);
2012 #endif
2013 
2014 	CHOOSE_GC_FG (r, r->menuBar.fg);
2015 # ifdef XFT_SUPPORT
2016 	/*
2017 	 * XXX Add multichar support.
2018 	 */
2019 	if (ISSET_OPTION(r, Opt_xft))
2020 	{
2021 	    XftFont *font = r->TermWin.xftpfont ?
2022 		r->TermWin.xftpfont : r->TermWin.xftfont;
2023 
2024 	    XftDrawString8( r->menuBar.xftDraw, &r->menuBar.xftFore, font,
2025 		  (menu->x + HSPACE_PIXEL / 2),
2026 		  rxvt_menubar_height(r) - SHADOW - MENUBAR_MARGIN
2027 			- font->descent,
2028 		  menu->name, len);
2029 	}
2030 	else
2031 # endif
2032 # ifdef USE_XIM
2033 	if (r->TermWin.fontset)
2034 	    XmbDrawString(r->Xdisplay,
2035 		  r->menuBar.win, r->TermWin.fontset, r->menuBar.gc,
2036 		  (menu->x + HSPACE_PIXEL / 2),
2037 		  rxvt_menubar_height(r) - SHADOW - MENUBAR_MARGIN
2038 			- r->TermWin.font->descent,
2039 		  (char*) menu->name, len);
2040 	else
2041 # endif	/* USE_XIM */
2042 	XDrawString(r->Xdisplay, r->menuBar.win, r->menuBar.gc,
2043 		  (menu->x + HSPACE_PIXEL / 2),
2044 		  rxvt_menubar_height(r) - SHADOW - MENUBAR_MARGIN
2045 			- r->TermWin.font->descent,
2046 		  (char*) menu->name, len);
2047 
2048 	if (x >= TWIN_WIDTH(r) ) break;
2049     }
2050 #if 0
2051     rxvt_drawbox_menubar(r, x, r->TermWin.ncol, (r->h->CurrentBar ? +1 : -1));
2052 #endif
2053 
2054     /* add the menuBar title, if it exists and there's plenty of room */
2055     r->h->Arrows_x = 0;
2056     if (x < TWIN_WIDTH(r) )
2057     {
2058 	const unsigned char  	*str;
2059 	int	     	npixels;    /* used to be ncol */
2060 	unsigned short	len;
2061 	unsigned char	title[256];
2062 
2063 	npixels = TWIN_WIDTH(r);
2064 	if (x < (npixels - NARROWS * MENUBAR_ARROW_WIDTH - HSPACE_PIXEL))
2065 	{
2066 	    npixels -= NARROWS * MENUBAR_ARROW_WIDTH + HSPACE_PIXEL;
2067 	    r->h->Arrows_x = npixels;
2068 	}
2069 	rxvt_draw_arrows(r, 0, +1);
2070 
2071 	str = ( r->h->MenuBar.title) ?
2072 	    r->h->MenuBar.title : (unsigned char*) "%n-%v";
2073 	for (len = 0; str[0] && len < sizeof(title) - 1; str++)
2074 	{
2075 	    const char	 *s = NULL;
2076 
2077 	    switch (str[0])
2078 	    {
2079 		case '%':
2080 		    str++;
2081 
2082 		    switch (str[0])
2083 		    {
2084 			case 'n':
2085 			    s = r->h->rs[Rs_name];
2086 			    break;  /* resource name */
2087 
2088 			case 'v':
2089 			    s = VERSION;
2090 			    break;  /* version number */
2091 
2092 			case '%':
2093 			    s = "%";
2094 			    break;  /* literal '%' */
2095 		    }
2096 
2097 		    if (NOT_NULL(s))
2098 			while (*s && len < sizeof(title) - 1)
2099 			title[len++] = *s++;
2100 		    break;
2101 
2102 		    default:
2103 			title[len++] = str[0];
2104 			break;
2105 	    }
2106 	}
2107 	title[len] = '\0';
2108 
2109 	npixels -= x + HSPACE_PIXEL + PTEXTWIDTH( r, title, len);
2110 
2111 	if (len > 0 && npixels >= 0)
2112 	{
2113 	    CHOOSE_GC_FG (r, r->menuBar.fg);
2114 # ifdef XFT_SUPPORT
2115 	    /*
2116 	     * XXX Add multichar support.
2117 	     */
2118 	    if (ISSET_OPTION(r, Opt_xft))
2119 	    {
2120 		XftFont *font = r->TermWin.xftpfont ?
2121 		    r->TermWin.xftpfont : r->TermWin.xftfont;
2122 
2123 		XftDrawString8( r->menuBar.xftDraw, &r->menuBar.xftFore, font,
2124 		      x + (npixels + HSPACE_PIXEL) / 2,
2125 		      rxvt_menubar_height(r) - SHADOW - MENUBAR_MARGIN
2126 				- font->descent,
2127 		      title, len);
2128 	    }
2129 	    else
2130 # endif
2131 # ifdef USE_XIM
2132 	    if (r->TermWin.fontset)
2133 	    XmbDrawString(r->Xdisplay,
2134 		      r->menuBar.win, r->TermWin.fontset, r->menuBar.gc,
2135 		      x + (npixels + HSPACE_PIXEL) / 2,
2136 		      rxvt_menubar_height(r) - SHADOW - MENUBAR_MARGIN
2137 				- r->TermWin.font->descent,
2138 		      (char*) title, len);
2139 	    else
2140 # endif	/* USE_XIM */
2141 	    XDrawString(r->Xdisplay, r->menuBar.win, r->menuBar.gc,
2142 		      x + (npixels + HSPACE_PIXEL) / 2,
2143 		      rxvt_menubar_height(r) - SHADOW - MENUBAR_MARGIN
2144 				- r->TermWin.font->descent,
2145 		      (char*) title, len);
2146 	    }
2147     }
2148 }
2149 
2150 
2151 /*
2152 ** user interface for building/deleting and otherwise managing menus
2153 */
2154 /* EXTPROTO */
2155 void
rxvt_menubar_dispatcher(rxvt_t * r,unsigned char * str)2156 rxvt_menubar_dispatcher(rxvt_t *r, unsigned char *str)
2157 {
2158     int		    n, cmd;
2159     unsigned char   *path, *name, *name2;
2160 
2161     rxvt_dbgmsg ((DBG_DEBUG, DBG_MENUBAR, "rxvt_menubar_dispatcher()\n"));
2162 
2163 #if 0
2164     if (rxvt_menubar_visible(r) && NOT_NULL(r->h->ActiveMenu))
2165 	rxvt_menubar_expose(r);
2166     else
2167 	SET_NULL(r->h->ActiveMenu);
2168 #endif
2169 
2170     cmd = *str;
2171     switch (cmd)
2172     {
2173 	case '.':
2174 	case '/':		/* absolute & relative path */
2175 	case MENUITEM_BEG:	/* menuitem */
2176 	    /* add `+' prefix for these cases */
2177 	    cmd = '+';
2178 	    break;
2179 
2180 	case '+':
2181 	case '-':
2182 	    str++;	    /* skip cmd character */
2183 	    break;
2184 
2185 	case '<':
2186 	    if (str[1] && str[2] == '>')    /* arrow commands */
2187 		rxvt_menuarrow_add(r, str);
2188 	    break;
2189 
2190 	case '[':	    /* extended command */
2191 	    while (str[0] == '[')
2192 	    {
2193 		unsigned char	       *next = (++str);	/* skip leading '[' */
2194 
2195 		if (str[0] == ':')	/* [:command:] */
2196 		{
2197 		    do
2198 		      {
2199 			next++;
2200 			if (IS_NULL(next = (unsigned char*) STRCHR(next, ':')))
2201 			    return; /* parse error */
2202 		      }
2203 		    while (next[1] != ']');
2204 
2205 		    /* remove and skip ':]' */
2206 		    *next = '\0';
2207 		    next += 2;
2208 		}
2209 		else
2210 		{
2211 		    if (IS_NULL(next = (unsigned char*) STRCHR(next, ']')))
2212 			return;	/* parse error */
2213 		    /* remove and skip ']' */
2214 		    *next = '\0';
2215 		    next++;
2216 		}
2217 
2218 		if (str[0] == ':')
2219 		    rxvt_menubar_dispatcher(r, str + 1);
2220 
2221 		else if (!STRCMP(str, "clear"))
2222 		    rxvt_menubar_clear(r);
2223 
2224 		else if (
2225 			    !STRCMP((char*) str, "done")
2226 			    || rxvt_str_match( (char*) str, "done:")
2227 			)
2228 		{
2229 		    /* We shouldn't ever get here */
2230 		    assert(0);
2231 		}
2232 		/*
2233 		 * 2006-02-02 gi1242: Using this command in config files will
2234 		 * override the users --showmenu option. Since there are other
2235 		 * escape sequences and shortcuts to do the same, we don't need
2236 		 * this here.
2237 		 */
2238 #if 0
2239 		else if (!STRCMP(str, "show"))
2240 		{
2241 		    if (rxvt_menubar_show(r))
2242 			rxvt_resize_on_subwin (r, SHOW_MENUBAR);
2243 		}
2244 		else if (!STRCMP(str, "hide"))
2245 		{
2246 		    if (rxvt_menubar_hide(r))
2247 			rxvt_resize_on_subwin (r, HIDE_MENUBAR);
2248 		}
2249 #endif
2250 		else if ((n = rxvt_str_match( (char*) str, "read:")) != 0)
2251 		{
2252 		    /* read in a menu from a file */
2253 		    str += n;
2254 		    rxvt_menubar_load_file(r, str);
2255 		}
2256 		else if ((n = rxvt_str_match( (char*) str, "title:")) != 0)
2257 		{
2258 		    str += n;
2259 		    if (*str)
2260 		    {
2261 			name = rxvt_realloc(r->h->MenuBar.title,
2262 			    STRLEN(str) + 1);
2263 
2264 			if (NOT_NULL(name))
2265 			{
2266 			    STRCPY(name, str);
2267 			    r->h->MenuBar.title = name;
2268 			}
2269 		    }
2270 		    else
2271 		    {
2272 			rxvt_free(r->h->MenuBar.title);
2273 			SET_NULL(r->h->MenuBar.title);
2274 		    }
2275 		}
2276 		else if ((n = rxvt_str_match( (char*) str, "pixmap:")) != 0)
2277 		{
2278 		    str += n;
2279 		    rxvt_xterm_seq(r, ATAB(r), XTerm_Pixmap,
2280 			    (char*) str, CHAR_ST);
2281 		}
2282 		str = next;
2283 
2284 		/*
2285 		 * 2006-02-04 gi1242: Don't clear the menu currently bieng
2286 		 * built. This will enable [read:...] commands to add to the
2287 		 * current menu, instead of the menubar.
2288 		 */
2289 #if 0
2290 		SET_NULL(r->h->BuildMenu);
2291 		SET_NULL(r->h->ActiveMenu);
2292 		rxvt_menubar_expose(r);
2293 #endif
2294 	    }
2295 	    return;
2296 	    break;
2297     }
2298 
2299     switch (cmd)
2300     {
2301 	case '+':
2302 	case '-':
2303 	    path = name = str;
2304 
2305 	    SET_NULL(name2);
2306 	    /* parse STR, allow spaces inside (name)  */
2307 	    if (path[0] != '\0')
2308 	    {
2309 		name = (unsigned char*) STRCHR(path, MENUITEM_BEG);
2310 		str = (unsigned char*) STRCHR(path, MENUITEM_END);
2311 		if (NOT_NULL(name) || NOT_NULL(str))
2312 		{
2313 		    if (IS_NULL(name) || IS_NULL(str) || str <= (name + 1) ||
2314 			(name > path && name[-1] != '/')
2315 		       )
2316 		    {
2317 			rxvt_msg (DBG_ERROR, DBG_MENUBAR, "menu error <%s>\n", path);
2318 			break;
2319 		    }
2320 		    if (str[1] == MENUITEM_BEG)
2321 		    {
2322 			name2 = (str + 2);
2323 			str = (unsigned char*) STRCHR(name2, MENUITEM_END);
2324 
2325 			if (IS_NULL(str))
2326 			{
2327 			    rxvt_msg (DBG_ERROR, DBG_MENUBAR, "menu error <%s>\n", path);
2328 			    break;
2329 			}
2330 			name2[-2] = '\0';   /* remove prev MENUITEM_END */
2331 		    }
2332 		    if (name > path && name[-1] == '/')
2333 			name[-1] = '\0';
2334 
2335 		    *name++ = '\0'; /* delimit */
2336 		    *str++ = '\0';  /* delimit */
2337 
2338 		    while (isspace((int) *str))
2339 			str++;	/* skip space */
2340 		}
2341 
2342 		rxvt_dbgmsg ((DBG_DEBUG, DBG_MENUBAR, "`%c' path = <%s>, name = <%s>, name2 = <%s>, action = <%s>\n", cmd, (path ? (char*) path : "(nil)"), (name ? (char*) name : "(nil)"), (name2 ? (char*) name2 : "(nil)"), (str ? (char*) str : "(nil)")));
2343 	    }
2344 
2345 	    /* process the different commands */
2346 	    switch (cmd)
2347 	    {
2348 		case '+':	/* add/replace existing menu or menuitem */
2349 		    if (path[0] != '\0')
2350 		    {
2351 			int	     len;
2352 
2353 			rxvt_dbgmsg ((DBG_DEBUG, DBG_MENUBAR, "Current menu %s\n", NOT_NULL(r->h->BuildMenu) ? (char *)r->h->BuildMenu->name : "(nil)"));
2354 
2355 			path = rxvt_menu_find_base(r, &(r->h->BuildMenu), path);
2356 			len = STRLEN(path);
2357 
2358 			/* don't allow menus called `*' */
2359 			if (path[0] == '*')
2360 			{
2361 			    rxvt_menu_clear(r, r->h->BuildMenu);
2362 			    break;
2363 			}
2364 			else if (len >= 2 && !STRCMP((path + len - 2), "/*"))
2365 			{
2366 			    path[len - 2] = '\0';
2367 			}
2368 
2369 			if (path[0] != '\0')
2370 			    r->h->BuildMenu =
2371 				rxvt_menu_add(r, r->h->BuildMenu, path);
2372 		    }
2373 
2374 		    if (NOT_NULL(name) && name[0] != '\0')
2375 			rxvt_menuitem_add(r, r->h->BuildMenu,
2376 			      (STRCMP(name, SEPARATOR_NAME) ?
2377 				    name : (unsigned char *) ""),
2378 			      name2, str);
2379 		    break;
2380 
2381 		case '-':	/* delete menu entry */
2382 		    if ( !STRCMP(path, "/*") &&
2383 			(IS_NULL(name) || name[0] == '\0')
2384 		       )
2385 		    {
2386 			rxvt_menubar_clear(r);
2387 			SET_NULL(r->h->BuildMenu);
2388 			break;
2389 		    }
2390 		    else if (path[0] != '\0')
2391 		    {
2392 			int	     len;
2393 			menu_t	     *menu = r->h->BuildMenu;
2394 
2395 			path = rxvt_menu_find_base(r, &menu, path);
2396 			len = STRLEN(path);
2397 
2398 			/* submenu called `*' clears all menu items */
2399 			if (path[0] == '*')
2400 			{
2401 			    rxvt_menu_clear(r, menu);
2402 			    break;  /* done */
2403 			}
2404 			else if (len >= 2 && !STRCMP(&path[len - 2], "/*"))
2405 			{
2406 			    /* done */
2407 			    break;
2408 			}
2409 			else if (path[0] != '\0')
2410 			{
2411 			    SET_NULL(r->h->BuildMenu);
2412 			    break;
2413 			}
2414 			else
2415 			    r->h->BuildMenu = menu;
2416 		    }
2417 
2418 		    if (NOT_NULL(r->h->BuildMenu))
2419 		    {
2420 			if (IS_NULL(name) || name[0] == '\0')
2421 			    r->h->BuildMenu =
2422 				rxvt_menu_delete(r, r->h->BuildMenu);
2423 			else
2424 			{
2425 			    const unsigned char	 *n1;
2426 			    menuitem_t	 	 *item;
2427 			    menu_t	 	 *BuildMenu = r->h->BuildMenu;
2428 
2429 			    n1 = STRCMP(name, SEPARATOR_NAME)
2430 				    ? name : (unsigned char*) "";
2431 			    item = rxvt_menuitem_find(BuildMenu, n1);
2432 			    if (NOT_NULL(item) &&
2433 				item->entry.itemType != MenuSubMenu
2434 			       )
2435 			    {
2436 				rxvt_menuitem_free(r, BuildMenu, item);
2437 
2438 				/* fix up the width */
2439 				BuildMenu->lwidth = BuildMenu->rwidth = 0;
2440 				for (item = BuildMenu->head; NOT_NULL(item);
2441 					item = item->next)
2442 				{
2443 				    unsigned short l;
2444 
2445 				    l = PTEXTWIDTH( r, item->name, item->len);
2446 				    MAX_IT(BuildMenu->lwidth, l);
2447 
2448 				    l = PTEXTWIDTH( r, item->name2, item->len2);
2449 				    MAX_IT(BuildMenu->rwidth, l);
2450 
2451 				}
2452 			    }
2453 			}
2454 		    }
2455 		    break;
2456 	    }
2457 	    break;
2458     }
2459 }
2460 
2461 
2462 /*
2463 ** general dispatch routine,
2464 ** it would be nice to have `sticky' menus
2465 */
2466 /* EXTPROTO */
2467 void
rxvt_menubar_control(rxvt_t * r,XButtonEvent * ev)2468 rxvt_menubar_control(rxvt_t *r, XButtonEvent *ev)
2469 {
2470     rxvt_dbgmsg ((DBG_DEBUG, DBG_MENUBAR, "rxvt_menubar_control()\n"));
2471 
2472     switch (ev->type)
2473     {
2474 	case ButtonPress:
2475 	    if (ev->button == Button1)
2476 		rxvt_menubar_select(r, ev);
2477 	    break;
2478 
2479 	case ButtonRelease:
2480 	    if (ev->button == Button1 && r->h->ActiveMenu)
2481 		rxvt_menu_select(r, ev);
2482 	    break;
2483 
2484 	case MotionNotify:
2485 	    while (XCheckTypedWindowEvent(r->Xdisplay, r->TermWin.parent,
2486 			      MotionNotify, (XEvent *) ev)) ;
2487 
2488 	    if (r->h->ActiveMenu)
2489 		while (rxvt_menu_select(r, ev)) ;
2490 	    else
2491 		ev->y = -1;
2492 
2493 	    if (ev->y < 0)
2494 	    {
2495 		Window	      unused_root, unused_child;
2496 		int	     unused_root_x, unused_root_y;
2497 		unsigned int	unused_mask;
2498 
2499 		XQueryPointer(r->Xdisplay, r->menuBar.win,
2500 		      &unused_root, &unused_child,
2501 		      &unused_root_x, &unused_root_y,
2502 		      &(ev->x), &(ev->y), &unused_mask);
2503 		rxvt_menubar_select(r, ev);
2504 	    }
2505 	    break;
2506     }
2507 }
2508 
2509 
2510 /*
2511  * read in menubar commands from FILENAME
2512  * ignore all input before the tag line [menu] or [menu:???]
2513  *
2514  * Note that since File_find () is used, FILENAME can be semi-colon
2515  * delimited such that the second part can refer to a tag
2516  * so that a large `database' of menus can be collected together
2517  *
2518  * FILENAME = "file"
2519  * FILENAME = "file;"
2520  *    read `file' starting with first [menu] or [menu:???] line
2521  *
2522  * FILENAME = "file;tag"
2523  *    read `file' starting with [menu:tag]
2524  */
2525 /* EXTPROTO */
2526 void
rxvt_menubar_load_file(rxvt_t * r,const unsigned char * filename)2527 rxvt_menubar_load_file(rxvt_t *r, const unsigned char *filename)
2528 {
2529 /* read in a menu from a file */
2530     FILE	   *fp;
2531     unsigned char   buffer[256];
2532     unsigned char  *p, *file, *tag = NULL;
2533 
2534     rxvt_dbgmsg ((DBG_DEBUG, DBG_MENUBAR, "rxvt_menubar_load_file()\n"));
2535 
2536     file = (unsigned char*) rxvt_File_find( (char*) filename,
2537 	    ".menu", r->h->rs[Rs_path]);
2538     if (IS_NULL(file))
2539     {
2540 	rxvt_msg (DBG_ERROR, DBG_MENUBAR,  "Could not open file %s\n",
2541 		filename);
2542 	return;
2543     }
2544 
2545     fp = fopen( (char*) file, "rb");
2546     rxvt_free(file);
2547     if (IS_NULL(fp))
2548 	return;
2549 
2550     /* semi-colon delimited */
2551     if (NOT_NULL(tag = (unsigned char*) STRCHR(filename, ';')))
2552     {
2553 	tag++;
2554 	if (*tag == '\0')
2555 	    SET_NULL(tag);
2556     }
2557 
2558     rxvt_dbgmsg ((DBG_DEBUG, DBG_MENUBAR, "[read:%s]\n", filename));
2559     if (tag)
2560 	rxvt_dbgmsg ((DBG_DEBUG, DBG_MENUBAR, "looking for [menu:%s]\n", tag));
2561 
2562     while (NOT_NULL(p = (unsigned char*) fgets( (char*) buffer, sizeof(buffer), fp)))
2563     {
2564 	int	     n;
2565 
2566 	if ((n = rxvt_str_match( (char*) p, "[menu")) != 0)
2567 	{
2568 	    if (tag)
2569 	    {
2570 		/* looking for [menu:tag] */
2571 		if (p[n] == ':' && p[n + 1] != ']')
2572 		{
2573 		    n++;
2574 		    n += rxvt_str_match( (char*) p + n, (char*) tag);
2575 		    if (p[n] == ']')
2576 		    {
2577 			rxvt_dbgmsg ((DBG_DEBUG, DBG_MENUBAR, "[menu:%s]\n", tag));
2578 			break;
2579 		    }
2580 		}
2581 	    }
2582 	    else if (p[n] == ':' || p[n] == ']')
2583 		break;
2584 	}
2585     }
2586 
2587 
2588     /* found [menu], [menu:???] tag */
2589     while (NOT_NULL(p))
2590     {
2591 	int	     n;
2592 
2593 	rxvt_dbgmsg ((DBG_DEBUG, DBG_MENUBAR, "read line = %s\n", p));
2594 
2595 	/* looking for [done:tag] or [done:] */
2596 	if ((n = rxvt_str_match( (char*) p, "[done")) != 0)
2597 	{
2598 	    if (p[n] == ']')
2599 	    {
2600 		break;
2601 	    }
2602 	    else if (p[n] == ':')
2603 	    {
2604 		n++;
2605 		if (p[n] == ']')
2606 		{
2607 		    break;
2608 		}
2609 		else if (tag)
2610 		{
2611 		    n += rxvt_str_match( (char*) p + n, (char*) tag);
2612 		    if (p[n] == ']')
2613 		    {
2614 			rxvt_dbgmsg ((DBG_DEBUG, DBG_MENUBAR, "[done:%s]\n", tag));
2615 			break;
2616 		    }
2617 		}
2618 		else
2619 		{
2620 		    /* what? ... skip this line */
2621 		    p[0] = COMMENT_CHAR;
2622 		}
2623 	    }
2624 	}
2625 
2626 	/*
2627 	 * remove leading/trailing space and strip-off leading/trailing quotes
2628 	 * skip blank or comment lines
2629 	 */
2630 	rxvt_str_trim( (char*) p);
2631 	if (*p && *p != '#') rxvt_menubar_dispatcher(r, p);
2632 
2633 	/* get another line */
2634 	p = (unsigned char*) fgets( (char*) buffer, sizeof(buffer), fp);
2635     }
2636 
2637     fclose(fp);
2638 }
2639 
2640 
2641 
2642 /* EXTPROTO */
2643 unsigned short
rxvt_menubar_height(rxvt_t * r)2644 rxvt_menubar_height(rxvt_t *r)
2645 {
2646     rxvt_dbgmsg ((DBG_DEBUG, DBG_MENUBAR, "rxvt_menubar_height()\n"));
2647 
2648     /* If menubar is not created or not mapped, return 0 */
2649     return  (NOT_WIN(r->menuBar.win) || !r->menuBar.state) ?
2650 	    0 : rxvt_menubar_rheight(r);
2651 }
2652 
2653 
2654 /* EXTPROTO */
2655 unsigned short
rxvt_menubar_rheight(rxvt_t * r)2656 rxvt_menubar_rheight(rxvt_t *r)
2657 {
2658     rxvt_dbgmsg ((DBG_DEBUG, DBG_MENUBAR, "rxvt_menubar_rheight()\n"));
2659 
2660 # ifdef XFT_SUPPORT
2661     if (ISSET_OPTION(r, Opt_xft))
2662 	return (r->TermWin.pheight + 2*SHADOW + 2*MENUBAR_MARGIN);
2663     else
2664 # endif
2665 	return (r->TermWin.fheight + 2*SHADOW + 2*MENUBAR_MARGIN);
2666 }
2667 
2668 
2669 /* EXTPROTO */
2670 int
rxvt_is_menubar_win(rxvt_t * r,Window w)2671 rxvt_is_menubar_win(rxvt_t *r, Window w)
2672 {
2673     rxvt_dbgmsg ((DBG_DEBUG, DBG_MENUBAR, "rxvt_is_menubar_win()\n"));
2674 
2675     return (w == r->menuBar.win);
2676 }
2677 
2678 /* EXTPROTO */
2679 void
rxvt_menubar_resize(rxvt_t * r)2680 rxvt_menubar_resize(rxvt_t *r)
2681 {
2682     menu_t  *menu;
2683     int	    i;
2684 
2685     rxvt_dbgmsg ((DBG_DEBUG, DBG_MENUBAR, "rxvt_menubar_resize()\n"));
2686 
2687     if (IS_WIN(r->menuBar.win) && r->menuBar.state)
2688 	XMoveResizeWindow(r->Xdisplay, r->menuBar.win,
2689 	    0, 0, TWIN_WIDTH(r), rxvt_menubar_rheight(r));
2690 
2691     /*
2692      * All submenus now need to be traversed and resized.
2693      */
2694     for (menu = r->h->MenuBar.head; NOT_NULL(menu); menu = menu->next)
2695     {
2696 	resizeSubMenus( r, menu );
2697 
2698 	/* X coordinate of menu names in menubar need to be updated */
2699 	if( menu->prev )
2700 	    menu->x = menu->prev->x + 2 * HSPACE_PIXEL
2701 		+ PTEXTWIDTH( r, menu->prev->name, menu->prev->len);
2702     }
2703 
2704     /*
2705      * Resize the popup menus if any.
2706      */
2707     for( i=0; i < 3; i++)
2708 	if( r->h->popupMenu[i] ) resizeSubMenus( r, r->h->popupMenu[i]);
2709 }
2710 
2711 /*
2712  * Update menu->width for all submenus.
2713  */
2714 /* INTPROTO */
2715 void
resizeSubMenus(rxvt_t * r,menu_t * menu)2716 resizeSubMenus( rxvt_t *r, menu_t *menu)
2717 {
2718     menuitem_t *item;
2719 
2720     menu->lwidth = menu->rwidth = 0;
2721 
2722     for( item = menu->head; NOT_NULL(item); item = item->next)
2723     {
2724 	unsigned short width;
2725 
2726 	width = PTEXTWIDTH( r, item->name, item->len);
2727 	if( menu->lwidth < width ) menu->lwidth = width;
2728 
2729 	width = PTEXTWIDTH( r, item->name2, item->len2);
2730 	if( menu->rwidth < width ) menu->rwidth = width;
2731 
2732 	if( item->entry.itemType == MenuSubMenu && item->entry.submenu.menu)
2733 	    resizeSubMenus( r, item->entry.submenu.menu );
2734     }
2735 }
2736 
2737 #endif	/* HAVE_MENUBAR */
2738 /*----------------------- end-of-file (C source) -----------------------*/
2739