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