This source file includes following definitions.
- ungrab_all
- abort_gracefully
- push_new_stack
- pop_new_stack_if_no_contents
- make_old_stack_space
- string_width
- arrow_width
- toggle_button_width
- radio_button_width
- resource_widget_value
- size_menu_item
- size_menu
- draw_arrow
- draw_shadow_rectangle
- draw_shadow_rhombus
- draw_toggle
- draw_radio
- draw_separator
- separator_height
- draw_highlight
- display_menu_item
- display_menu
- set_new_state
- expose_cb
- set_window_type
- make_windows_if_needed
- xlwmenu_window_p
- fit_to_screen
- create_pixmap_for_menu
- remap_menubar
- motion_event_is_in_menu
- map_event_to_widget_value
- make_drawing_gcs
- release_drawing_gcs
- compute_shadow_colors
- make_shadow_gcs
- release_shadow_gcs
- getDefaultXftFont
- openXftFont
- update_xft_colors
- XlwMenuInitialize
- XlwMenuClassInitialize
- XlwMenuRealize
- XlwMenuRedisplay
- xlwmenu_redisplay
- XlwMenuDestroy
- fontname_changed
- XlwMenuSetValues
- XlwMenuResize
- handle_single_motion_event
- handle_motion_event
- Start
- Drag
- Nothing
- find_first_selectable
- find_next_selectable
- find_prev_selectable
- Down
- Up
- Left
- Right
- Key
- Select
- pop_up_menu
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 #include <config.h>
24
25 #include <setjmp.h>
26 #include <lisp.h>
27
28 #include <stdio.h>
29
30 #include <sys/types.h>
31 #include <X11/Xos.h>
32 #include <X11/IntrinsicP.h>
33 #include <X11/ObjectP.h>
34 #include <X11/StringDefs.h>
35 #include <X11/cursorfont.h>
36 #include <X11/Shell.h>
37 #include "xlwmenuP.h"
38
39 #ifdef emacs
40
41 #include <xterm.h>
42 #include "bitmaps/gray.xbm"
43
44 #else
45
46 #include <X11/bitmaps/gray>
47
48 #endif
49
50 static int pointer_grabbed;
51 static int keyboard_grabbed;
52 static XEvent menu_post_event;
53
54 static char
55 xlwMenuTranslations [] =
56 "<BtnDown>: start()\n\
57 <Motion>: drag()\n\
58 <BtnUp>: select()\n\
59 <Key>Shift_L: nothing()\n\
60 <Key>Shift_R: nothing()\n\
61 <Key>Meta_L: nothing()\n\
62 <Key>Meta_R: nothing()\n\
63 <Key>Control_L: nothing()\n\
64 <Key>Control_R: nothing()\n\
65 <Key>Hyper_L: nothing()\n\
66 <Key>Hyper_R: nothing()\n\
67 <Key>Super_L: nothing()\n\
68 <Key>Super_R: nothing()\n\
69 <Key>Alt_L: nothing()\n\
70 <Key>Alt_R: nothing()\n\
71 <Key>Caps_Lock: nothing()\n\
72 <Key>Shift_Lock: nothing()\n\
73 <KeyUp>Shift_L: nothing()\n\
74 <KeyUp>Shift_R: nothing()\n\
75 <KeyUp>Meta_L: nothing()\n\
76 <KeyUp>Meta_R: nothing()\n\
77 <KeyUp>Control_L: nothing()\n\
78 <KeyUp>Control_R: nothing()\n\
79 <KeyUp>Hyper_L: nothing()\n\
80 <KeyUp>Hyper_R: nothing()\n\
81 <KeyUp>Super_L: nothing()\n\
82 <KeyUp>Super_R: nothing()\n\
83 <KeyUp>Alt_L: nothing()\n\
84 <KeyUp>Alt_R: nothing()\n\
85 <KeyUp>Caps_Lock: nothing()\n\
86 <KeyUp>Shift_Lock:nothing()\n\
87 <Key>Return: select()\n\
88 <Key>Down: down()\n\
89 <Key>Up: up()\n\
90 <Key>Left: left()\n\
91 <Key>Right: right()\n\
92 <Key>: key()\n\
93 <KeyUp>: key()\n\
94 ";
95
96
97
98
99
100
101
102
103 #define offset(field) offsetof (XlwMenuRec, field)
104 static XtResource
105 xlwMenuResources[] =
106 {
107 #ifdef HAVE_X_I18N
108 {XtNfontSet, XtCFontSet, XtRFontSet, sizeof(XFontSet),
109 offset(menu.fontSet), XtRFontSet, NULL},
110 #endif
111 #if defined USE_CAIRO || defined HAVE_XFT
112 #define DEFAULT_FONTNAME "Sans-10"
113 #else
114 #define DEFAULT_FONTNAME "XtDefaultFont"
115 #endif
116 {XtNfont, XtCFont, XtRString, sizeof(String),
117 offset(menu.fontName), XtRString, DEFAULT_FONTNAME },
118 {XtNforeground, XtCForeground, XtRPixel, sizeof(Pixel),
119 offset(menu.foreground), XtRString, "XtDefaultForeground"},
120 {XtNdisabledForeground, XtCDisabledForeground, XtRPixel, sizeof(Pixel),
121 offset(menu.disabled_foreground), XtRString, (XtPointer)NULL},
122 {XtNbuttonForeground, XtCButtonForeground, XtRPixel, sizeof(Pixel),
123 offset(menu.button_foreground), XtRString, "XtDefaultForeground"},
124 {XtNhighlightForeground, XtCHighlightForeground, XtRPixel, sizeof(Pixel),
125 offset(menu.highlight_foreground), XtRImmediate, (XtPointer) -1},
126 {XtNhighlightBackground, XtCHighlightBackground, XtRPixel, sizeof(Pixel),
127 offset(menu.highlight_background), XtRImmediate, (XtPointer)-1},
128 {XtNmargin, XtCMargin, XtRDimension, sizeof(Dimension),
129 offset(menu.margin), XtRImmediate, (XtPointer)1},
130 {XtNhorizontalSpacing, XtCMargin, XtRDimension, sizeof(Dimension),
131 offset(menu.horizontal_spacing), XtRImmediate, (XtPointer)3},
132 {XtNverticalSpacing, XtCMargin, XtRDimension, sizeof(Dimension),
133 offset(menu.vertical_spacing), XtRImmediate, (XtPointer)2},
134 {XtNarrowSpacing, XtCMargin, XtRDimension, sizeof(Dimension),
135 offset(menu.arrow_spacing), XtRImmediate, (XtPointer)10},
136
137 {XmNshadowThickness, XmCShadowThickness, XtRDimension,
138 sizeof (Dimension), offset (menu.shadow_thickness),
139 XtRImmediate, (XtPointer)1},
140 {XmNtopShadowColor, XmCTopShadowColor, XtRPixel, sizeof (Pixel),
141 offset (menu.top_shadow_color), XtRImmediate, (XtPointer)-1},
142 {XmNbottomShadowColor, XmCBottomShadowColor, XtRPixel, sizeof (Pixel),
143 offset (menu.bottom_shadow_color), XtRImmediate, (XtPointer)-1},
144 {XmNtopShadowPixmap, XmCTopShadowPixmap, XtRPixmap, sizeof (Pixmap),
145 offset (menu.top_shadow_pixmap), XtRImmediate, (XtPointer)None},
146 {XmNbottomShadowPixmap, XmCBottomShadowPixmap, XtRPixmap, sizeof (Pixmap),
147 offset (menu.bottom_shadow_pixmap), XtRImmediate, (XtPointer)None},
148
149 {XtNopen, XtCCallback, XtRCallback, sizeof(XtPointer),
150 offset(menu.open), XtRCallback, (XtPointer)NULL},
151 {XtNselect, XtCCallback, XtRCallback, sizeof(XtPointer),
152 offset(menu.select), XtRCallback, (XtPointer)NULL},
153 {XtNhighlightCallback, XtCCallback, XtRCallback, sizeof(XtPointer),
154 offset(menu.highlight), XtRCallback, (XtPointer)NULL},
155 {XtNenterCallback, XtCCallback, XtRCallback, sizeof(XtPointer),
156 offset(menu.enter), XtRCallback, (XtPointer)NULL},
157 {XtNleaveCallback, XtCCallback, XtRCallback, sizeof(XtPointer),
158 offset(menu.leave), XtRCallback, (XtPointer)NULL},
159 {XtNmenu, XtCMenu, XtRPointer, sizeof(XtPointer),
160 offset(menu.contents), XtRImmediate, (XtPointer)NULL},
161 {XtNcursor, XtCCursor, XtRCursor, sizeof(Cursor),
162 offset(menu.cursor_shape), XtRString, (XtPointer)"right_ptr"},
163 {XtNhorizontal, XtCHorizontal, XtRInt, sizeof(int),
164 offset(menu.horizontal), XtRImmediate, (XtPointer)True},
165 {XtNborderThickness, XtCBorderThickness, XtRDimension,
166 sizeof (Dimension), offset (menu.border_thickness),
167 XtRImmediate, (XtPointer)1}
168 };
169 #undef offset
170
171 static Boolean XlwMenuSetValues(Widget current, Widget request, Widget new,
172 ArgList args, Cardinal *num_args);
173 static void XlwMenuRealize(Widget, Mask *, XSetWindowAttributes *);
174 static void XlwMenuResize(Widget w);
175 static void XlwMenuInitialize(Widget, Widget, ArgList, Cardinal *);
176 static void XlwMenuRedisplay(Widget w, XEvent *ev, Region region);
177 static void XlwMenuDestroy(Widget w);
178 static void XlwMenuClassInitialize(void);
179 static void Start(Widget w, XEvent *ev, String *params, Cardinal *num_params);
180 static void Drag(Widget w, XEvent *ev, String *params, Cardinal *num_params);
181 static void Down(Widget w, XEvent *ev, String *params, Cardinal *num_params);
182 static void Up(Widget w, XEvent *ev, String *params, Cardinal *num_params);
183 static void Left(Widget w, XEvent *ev, String *params, Cardinal *num_params);
184 static void Right(Widget w, XEvent *ev, String *params, Cardinal *num_params);
185 static void Select(Widget w, XEvent *ev, String *params, Cardinal *num_params);
186 static void Key(Widget w, XEvent *ev, String *params, Cardinal *num_params);
187 static void Nothing(Widget w, XEvent *ev, String *params, Cardinal *num_params);
188 static int separator_height (enum menu_separator);
189 static void pop_up_menu (XlwMenuWidget, XButtonPressedEvent *);
190
191 static XtActionsRec
192 xlwMenuActionsList [] =
193 {
194 {"start", Start},
195 {"drag", Drag},
196 {"down", Down},
197 {"up", Up},
198 {"left", Left},
199 {"right", Right},
200 {"select", Select},
201 {"key", Key},
202 {"MenuGadgetEscape", Key},
203 {"nothing", Nothing},
204 };
205
206 #define SuperClass ((CoreWidgetClass)&coreClassRec)
207
208 XlwMenuClassRec xlwMenuClassRec =
209 {
210 {
211 (WidgetClass) SuperClass,
212 "XlwMenu",
213 sizeof(XlwMenuRec),
214 XlwMenuClassInitialize,
215 NULL,
216 FALSE,
217 XlwMenuInitialize,
218 NULL,
219 XlwMenuRealize,
220 xlwMenuActionsList,
221 XtNumber(xlwMenuActionsList),
222 xlwMenuResources,
223 XtNumber(xlwMenuResources),
224 NULLQUARK,
225 TRUE,
226 XtExposeCompressMaximal,
227 TRUE,
228 FALSE,
229 XlwMenuDestroy,
230 XlwMenuResize,
231 XlwMenuRedisplay,
232 XlwMenuSetValues,
233 NULL,
234 XtInheritSetValuesAlmost,
235 NULL,
236 NULL,
237 XtVersion,
238 NULL,
239 xlwMenuTranslations,
240 XtInheritQueryGeometry,
241 XtInheritDisplayAccelerator,
242 NULL
243 },
244 {
245 0
246 },
247 };
248
249 WidgetClass xlwMenuWidgetClass = (WidgetClass) &xlwMenuClassRec;
250
251 int submenu_destroyed;
252
253 static int next_release_must_exit;
254
255
256
257
258 static void
259 ungrab_all (Widget w, Time ungrabtime)
260 {
261 XtUngrabPointer (w, ungrabtime);
262
263 if (keyboard_grabbed)
264 XtUngrabKeyboard (w, ungrabtime);
265 }
266
267
268
269 static _Noreturn void
270 abort_gracefully (Widget w)
271 {
272 if (XtIsShell (XtParent (w)))
273 XtRemoveGrab (w);
274 ungrab_all (w, CurrentTime);
275 emacs_abort ();
276 }
277
278 static void
279 push_new_stack (XlwMenuWidget mw, widget_value *val)
280 {
281 if (!mw->menu.new_stack)
282 {
283 mw->menu.new_stack_length = 10;
284 mw->menu.new_stack =
285 (widget_value**)XtCalloc (mw->menu.new_stack_length,
286 sizeof (widget_value*));
287 }
288 else if (mw->menu.new_depth == mw->menu.new_stack_length)
289 {
290 mw->menu.new_stack_length *= 2;
291 mw->menu.new_stack =
292 (widget_value**)XtRealloc ((char*)mw->menu.new_stack,
293 mw->menu.new_stack_length * sizeof (widget_value*));
294 }
295 mw->menu.new_stack [mw->menu.new_depth++] = val;
296 }
297
298 static void
299 pop_new_stack_if_no_contents (XlwMenuWidget mw)
300 {
301 if (mw->menu.new_depth > 1)
302 {
303 if (!mw->menu.new_stack [mw->menu.new_depth - 1]->contents)
304 mw->menu.new_depth -= 1;
305 }
306 }
307
308 static void
309 make_old_stack_space (XlwMenuWidget mw, int n)
310 {
311 if (!mw->menu.old_stack)
312 {
313 mw->menu.old_stack_length = 10;
314 mw->menu.old_stack =
315 (widget_value**)XtCalloc (mw->menu.old_stack_length,
316 sizeof (widget_value*));
317 }
318 else if (mw->menu.old_stack_length < n)
319 {
320 mw->menu.old_stack_length *= 2;
321 mw->menu.old_stack =
322 (widget_value**)XtRealloc ((char*)mw->menu.old_stack,
323 mw->menu.old_stack_length * sizeof (widget_value*));
324 }
325 }
326
327
328 static int
329 string_width (XlwMenuWidget mw, char *s)
330 {
331 XCharStruct xcs;
332 int drop;
333 #if defined USE_CAIRO || defined HAVE_XFT
334 if (mw->menu.xft_font)
335 {
336 XGlyphInfo gi;
337 XftTextExtentsUtf8 (XtDisplay (mw), mw->menu.xft_font,
338 (FcChar8 *) s,
339 strlen (s), &gi);
340 return gi.xOff;
341 }
342 #endif
343 #ifdef HAVE_X_I18N
344 if (mw->menu.fontSet)
345 {
346 XRectangle ink, logical;
347 XmbTextExtents (mw->menu.fontSet, s, strlen (s), &ink, &logical);
348 return logical.width;
349 }
350 #endif
351
352 XTextExtents (mw->menu.font, s, strlen (s), &drop, &drop, &drop, &xcs);
353 return xcs.width;
354
355 }
356
357 #if defined USE_CAIRO || defined HAVE_XFT
358 #define MENU_FONT_HEIGHT(mw) \
359 ((mw)->menu.xft_font != NULL \
360 ? (mw)->menu.xft_font->height \
361 : ((mw)->menu.fontSet != NULL \
362 ? (mw)->menu.font_extents->max_logical_extent.height \
363 : (mw)->menu.font->ascent + (mw)->menu.font->descent))
364 #define MENU_FONT_ASCENT(mw) \
365 ((mw)->menu.xft_font != NULL \
366 ? (mw)->menu.xft_font->ascent \
367 : ((mw)->menu.fontSet != NULL \
368 ? - (mw)->menu.font_extents->max_logical_extent.y \
369 : (mw)->menu.font->ascent))
370 #else
371 #ifdef HAVE_X_I18N
372 #define MENU_FONT_HEIGHT(mw) \
373 ((mw)->menu.fontSet != NULL \
374 ? (mw)->menu.font_extents->max_logical_extent.height \
375 : (mw)->menu.font->ascent + (mw)->menu.font->descent)
376 #define MENU_FONT_ASCENT(mw) \
377 ((mw)->menu.fontSet != NULL \
378 ? - (mw)->menu.font_extents->max_logical_extent.y \
379 : (mw)->menu.font->ascent)
380 #else
381 #define MENU_FONT_HEIGHT(mw) \
382 ((mw)->menu.font->ascent + (mw)->menu.font->descent)
383 #define MENU_FONT_ASCENT(mw) ((mw)->menu.font->ascent)
384 #endif
385 #endif
386
387 static int
388 arrow_width (XlwMenuWidget mw)
389 {
390 return (MENU_FONT_ASCENT (mw) * 3/4) | 1;
391 }
392
393
394
395 static int
396 toggle_button_width (XlwMenuWidget mw)
397 {
398 return (MENU_FONT_HEIGHT (mw) * 2 / 3) | 1;
399 }
400
401
402
403
404 static int
405 radio_button_width (XlwMenuWidget mw)
406 {
407 return toggle_button_width (mw) * 1.41;
408 }
409
410
411 static XtResource
412 nameResource[] =
413 {
414 {"labelString", "LabelString", XtRString, sizeof(String),
415 0, XtRImmediate, 0},
416 };
417
418 static char*
419 resource_widget_value (XlwMenuWidget mw, widget_value *val)
420 {
421 if (!val->toolkit_data)
422 {
423 char* resourced_name = NULL;
424 char* complete_name;
425 XtGetSubresources ((Widget) mw,
426 (XtPointer) &resourced_name,
427 val->name, val->name,
428 nameResource, 1, NULL, 0);
429 if (!resourced_name)
430 resourced_name = val->name;
431 if (!val->value)
432 {
433 complete_name = (char *) XtMalloc (strlen (resourced_name) + 1);
434 strcpy (complete_name, resourced_name);
435 }
436 else
437 {
438 int complete_length =
439 strlen (resourced_name) + strlen (val->value) + 2;
440 complete_name = XtMalloc (complete_length);
441 char *z = stpcpy (complete_name, resourced_name);
442 *z++ = ' ';
443 strcpy (z, val->value);
444 }
445
446 val->toolkit_data = complete_name;
447 val->free_toolkit_data = True;
448 }
449 return (char*)val->toolkit_data;
450 }
451
452
453 static void
454 size_menu_item (XlwMenuWidget mw,
455 widget_value* val,
456 int horizontal_p,
457 int* label_width,
458 int* rest_width,
459 int* button_width,
460 int* height)
461 {
462 enum menu_separator separator;
463
464 if (lw_separator_p (val->name, &separator, 0))
465 {
466 *height = separator_height (separator);
467 *label_width = 1;
468 *rest_width = 0;
469 *button_width = 0;
470 }
471 else
472 {
473 *height = MENU_FONT_HEIGHT (mw)
474 + 2 * mw->menu.vertical_spacing + 2 * mw->menu.shadow_thickness;
475
476 *label_width =
477 string_width (mw, resource_widget_value (mw, val))
478 + mw->menu.horizontal_spacing + mw->menu.shadow_thickness;
479
480 *rest_width = mw->menu.horizontal_spacing + mw->menu.shadow_thickness;
481 if (!horizontal_p)
482 {
483 if (val->contents)
484
485 *rest_width += arrow_width (mw) + mw->menu.arrow_spacing;
486 else if (val->key)
487
488 *rest_width += (string_width (mw, val->key)
489 + mw->menu.arrow_spacing);
490
491 if (val->button_type == BUTTON_TYPE_TOGGLE)
492 *button_width = (toggle_button_width (mw)
493 + mw->menu.horizontal_spacing);
494 else if (val->button_type == BUTTON_TYPE_RADIO)
495 *button_width = (radio_button_width (mw)
496 + mw->menu.horizontal_spacing);
497 }
498 }
499 }
500
501 static void
502 size_menu (XlwMenuWidget mw, int level)
503 {
504 int label_width = 0;
505 int rest_width = 0;
506 int button_width = 0;
507 int max_rest_width = 0;
508 int max_button_width = 0;
509 int height = 0;
510 int horizontal_p = mw->menu.horizontal && (level == 0);
511 widget_value* val;
512 window_state* ws;
513
514 if (level >= mw->menu.old_depth)
515 abort_gracefully ((Widget) mw);
516
517 ws = &mw->menu.windows [level];
518 ws->width = 0;
519 ws->height = 0;
520 ws->label_width = 0;
521 ws->button_width = 0;
522
523 for (val = mw->menu.old_stack [level]->contents; val; val = val->next)
524 {
525 size_menu_item (mw, val, horizontal_p, &label_width, &rest_width,
526 &button_width, &height);
527 if (horizontal_p)
528 {
529 ws->width += label_width + rest_width;
530 if (height > ws->height)
531 ws->height = height;
532 }
533 else
534 {
535 if (label_width > ws->label_width)
536 ws->label_width = label_width;
537 if (rest_width > max_rest_width)
538 max_rest_width = rest_width;
539 if (button_width > max_button_width)
540 max_button_width = button_width;
541 ws->height += height;
542 }
543 }
544
545 if (horizontal_p)
546 ws->label_width = ws->button_width = 0;
547 else
548 {
549 ws->width = ws->label_width + max_rest_width + max_button_width;
550 ws->button_width = max_button_width;
551 }
552
553 ws->width += 2 * mw->menu.shadow_thickness;
554 ws->height += 2 * mw->menu.shadow_thickness;
555 ws->max_rest_width = max_rest_width;
556
557 if (horizontal_p)
558 {
559 ws->width += 2 * mw->menu.margin;
560 ws->height += 2 * mw->menu.margin;
561 }
562 }
563
564
565
566
567 static void
568 draw_arrow (XlwMenuWidget mw,
569 Window window,
570 GC gc,
571 int x,
572 int y,
573 int width,
574 int down_p)
575 {
576 Display *dpy = XtDisplay (mw);
577 GC top_gc, bottom_gc;
578 int thickness = mw->menu.shadow_thickness;
579 int height = width;
580 XPoint pt[10];
581
582
583 double factor = 1.62;
584 int thickness2 = thickness * factor;
585
586 y += (MENU_FONT_HEIGHT (mw) - height) / 2;
587
588 if (down_p)
589 {
590 top_gc = mw->menu.highlight_shadow_bottom_gc;
591 bottom_gc = mw->menu.highlight_shadow_top_gc;
592 }
593 else
594 {
595 top_gc = mw->menu.shadow_top_gc;
596 bottom_gc = mw->menu.shadow_bottom_gc;
597 }
598
599 pt[0].x = x;
600 pt[0].y = y + height;
601 pt[1].x = x + thickness;
602 pt[1].y = y + height - thickness2;
603 pt[2].x = x + thickness2;
604 pt[2].y = y + thickness2;
605 pt[3].x = x;
606 pt[3].y = y;
607 XFillPolygon (dpy, window, top_gc, pt, 4, Convex, CoordModeOrigin);
608
609 pt[0].x = x;
610 pt[0].y = y;
611 pt[1].x = x + thickness;
612 pt[1].y = y + thickness2;
613 pt[2].x = x + width - thickness2;
614 pt[2].y = y + height / 2;
615 pt[3].x = x + width;
616 pt[3].y = y + height / 2;
617 XFillPolygon (dpy, window, top_gc, pt, 4, Convex, CoordModeOrigin);
618
619 pt[0].x = x;
620 pt[0].y = y + height;
621 pt[1].x = x + thickness;
622 pt[1].y = y + height - thickness2;
623 pt[2].x = x + width - thickness2;
624 pt[2].y = y + height / 2;
625 pt[3].x = x + width;
626 pt[3].y = y + height / 2;
627 XFillPolygon (dpy, window, bottom_gc, pt, 4, Convex, CoordModeOrigin);
628 }
629
630
631
632
633
634
635 static void
636 draw_shadow_rectangle (XlwMenuWidget mw, Window window, int x, int y,
637 int width, int height, int erase_p, int down_p,
638 GC top_gc, GC bottom_gc)
639 {
640 Display *dpy = XtDisplay (mw);
641 int thickness = !x && !y ? mw->menu.border_thickness : mw->menu.shadow_thickness;
642 XPoint points [4];
643
644
645 if (erase_p)
646 {
647 top_gc = mw->menu.background_gc;
648 bottom_gc = mw->menu.background_gc;
649 }
650 else
651 {
652 if (top_gc == NULL)
653 top_gc = mw->menu.shadow_top_gc;
654 if (bottom_gc == NULL)
655 bottom_gc = mw->menu.shadow_bottom_gc;
656 }
657
658 if (!erase_p && width == height && width == toggle_button_width (mw))
659 {
660 points [0].x = x;
661 points [0].y = y;
662 points [1].x = x + width;
663 points [1].y = y;
664 points [2].x = x + width;
665 points [2].y = y + height;
666 points [3].x = x;
667 points [3].y = y + height;
668 XFillPolygon (dpy, window,
669 down_p ? mw->menu.button_gc : mw->menu.inactive_button_gc,
670 points, 4, Convex, CoordModeOrigin);
671 }
672
673 if (!erase_p && down_p)
674 {
675 GC temp;
676 temp = top_gc;
677 top_gc = bottom_gc;
678 bottom_gc = temp;
679 }
680
681
682 points [0].x = x;
683 points [0].y = y;
684 points [1].x = x + width;
685 points [1].y = y;
686 points [2].x = x + width - thickness;
687 points [2].y = y + thickness;
688 points [3].x = x;
689 points [3].y = y + thickness;
690 XFillPolygon (dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
691 points [0].x = x;
692 points [0].y = y + thickness;
693 points [1].x = x;
694 points [1].y = y + height;
695 points [2].x = x + thickness;
696 points [2].y = y + height - thickness;
697 points [3].x = x + thickness;
698 points [3].y = y + thickness;
699 XFillPolygon (dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
700 points [0].x = x + width;
701 points [0].y = y;
702 points [1].x = x + width - thickness;
703 points [1].y = y + thickness;
704 points [2].x = x + width - thickness;
705 points [2].y = y + height - thickness;
706 points [3].x = x + width;
707 points [3].y = y + height - thickness;
708 XFillPolygon (dpy, window, bottom_gc, points, 4, Convex, CoordModeOrigin);
709 points [0].x = x;
710 points [0].y = y + height;
711 points [1].x = x + width;
712 points [1].y = y + height;
713 points [2].x = x + width;
714 points [2].y = y + height - thickness;
715 points [3].x = x + thickness;
716 points [3].y = y + height - thickness;
717 XFillPolygon (dpy, window, bottom_gc, points, 4, Convex, CoordModeOrigin);
718 }
719
720
721 static void
722 draw_shadow_rhombus (XlwMenuWidget mw, Window window, int x, int y,
723 int width, int height, int erase_p, int down_p,
724 GC top_gc, GC bottom_gc)
725 {
726 Display *dpy = XtDisplay (mw);
727 int thickness = mw->menu.shadow_thickness;
728 XPoint points [4];
729
730
731 if (erase_p)
732 {
733 top_gc = mw->menu.background_gc;
734 bottom_gc = mw->menu.background_gc;
735 }
736 else
737 {
738 if (top_gc == NULL)
739 top_gc = mw->menu.shadow_top_gc;
740 if (bottom_gc == NULL)
741 top_gc = mw->menu.shadow_bottom_gc;
742 }
743
744 if (!erase_p && width == height && width == radio_button_width (mw))
745 {
746 points [0].x = x;
747 points [0].y = y + width / 2;
748 points [1].x = x + height / 2;
749 points [1].y = y + width;
750 points [2].x = x + height;
751 points [2].y = y + width / 2;
752 points [3].x = x + height / 2;
753 points [3].y = y;
754 XFillPolygon (dpy, window,
755 down_p ? mw->menu.button_gc : mw->menu.inactive_button_gc,
756 points, 4, Convex, CoordModeOrigin);
757 }
758
759 if (!erase_p && down_p)
760 {
761 GC temp;
762 temp = top_gc;
763 top_gc = bottom_gc;
764 bottom_gc = temp;
765 }
766
767 points [0].x = x;
768 points [0].y = y + height / 2;
769 points [1].x = x + thickness;
770 points [1].y = y + height / 2;
771 points [2].x = x + width / 2;
772 points [2].y = y + thickness;
773 points [3].x = x + width / 2;
774 points [3].y = y;
775 XFillPolygon (dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
776 points [0].x = x + width / 2;
777 points [0].y = y;
778 points [1].x = x + width / 2;
779 points [1].y = y + thickness;
780 points [2].x = x + width - thickness;
781 points [2].y = y + height / 2;
782 points [3].x = x + width;
783 points [3].y = y + height / 2;
784 XFillPolygon (dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
785 points [0].x = x;
786 points [0].y = y + height / 2;
787 points [1].x = x + thickness;
788 points [1].y = y + height / 2;
789 points [2].x = x + width / 2;
790 points [2].y = y + height - thickness;
791 points [3].x = x + width / 2;
792 points [3].y = y + height;
793 XFillPolygon (dpy, window, bottom_gc, points, 4, Convex, CoordModeOrigin);
794 points [0].x = x + width / 2;
795 points [0].y = y + height;
796 points [1].x = x + width / 2;
797 points [1].y = y + height - thickness;
798 points [2].x = x + width - thickness;
799 points [2].y = y + height / 2;
800 points [3].x = x + width;
801 points [3].y = y + height / 2;
802 XFillPolygon (dpy, window, bottom_gc, points, 4, Convex, CoordModeOrigin);
803 }
804
805
806
807
808
809
810 static void
811 draw_toggle (XlwMenuWidget mw, Window window, int x, int y, int selected_p,
812 int highlighted_p)
813 {
814 int width, height;
815 GC top_gc, bottom_gc;
816
817 if (highlighted_p)
818 {
819 top_gc = mw->menu.highlight_shadow_top_gc;
820 bottom_gc = mw->menu.highlight_shadow_bottom_gc;
821 }
822 else
823 {
824 top_gc = mw->menu.shadow_top_gc;
825 bottom_gc = mw->menu.shadow_bottom_gc;
826 }
827
828 width = toggle_button_width (mw);
829 height = width;
830 x += mw->menu.horizontal_spacing;
831 y += (MENU_FONT_ASCENT (mw) - height) / 2;
832 draw_shadow_rectangle (mw, window, x, y, width, height, False,
833 selected_p, top_gc, bottom_gc);
834 }
835
836
837
838
839
840
841 static void
842 draw_radio (XlwMenuWidget mw, Window window, int x, int y, int selected_p,
843 int highlighted_p)
844 {
845 int width, height;
846 GC top_gc, bottom_gc;
847
848 if (highlighted_p)
849 {
850 top_gc = mw->menu.highlight_shadow_top_gc;
851 bottom_gc = mw->menu.highlight_shadow_bottom_gc;
852 }
853 else
854 {
855 top_gc = mw->menu.shadow_top_gc;
856 bottom_gc = mw->menu.shadow_bottom_gc;
857 }
858
859 width = radio_button_width (mw);
860 height = width;
861 x += mw->menu.horizontal_spacing;
862 y += (MENU_FONT_ASCENT (mw) - height) / 2;
863 draw_shadow_rhombus (mw, window, x, y, width, height, False, selected_p,
864 top_gc, bottom_gc);
865 }
866
867
868
869
870
871
872 static void
873 draw_separator (XlwMenuWidget mw,
874 Window window,
875 int x,
876 int y,
877 int width,
878 enum menu_separator type)
879 {
880 Display *dpy = XtDisplay (mw);
881 XGCValues xgcv;
882
883 switch (type)
884 {
885 case SEPARATOR_NO_LINE:
886 break;
887
888 case SEPARATOR_SINGLE_LINE:
889 XDrawLine (dpy, window, mw->menu.foreground_gc,
890 x, y, x + width, y);
891 break;
892
893 case SEPARATOR_DOUBLE_LINE:
894 draw_separator (mw, window, x, y, width, SEPARATOR_SINGLE_LINE);
895 draw_separator (mw, window, x, y + 2, width, SEPARATOR_SINGLE_LINE);
896 break;
897
898 case SEPARATOR_SINGLE_DASHED_LINE:
899 xgcv.line_style = LineOnOffDash;
900 XChangeGC (dpy, mw->menu.foreground_gc, GCLineStyle, &xgcv);
901 XDrawLine (dpy, window, mw->menu.foreground_gc,
902 x, y, x + width, y);
903 xgcv.line_style = LineSolid;
904 XChangeGC (dpy, mw->menu.foreground_gc, GCLineStyle, &xgcv);
905 break;
906
907 case SEPARATOR_DOUBLE_DASHED_LINE:
908 draw_separator (mw, window, x, y, width,
909 SEPARATOR_SINGLE_DASHED_LINE);
910 draw_separator (mw, window, x, y + 2, width,
911 SEPARATOR_SINGLE_DASHED_LINE);
912 break;
913
914 case SEPARATOR_SHADOW_ETCHED_IN:
915 XDrawLine (dpy, window, mw->menu.shadow_bottom_gc,
916 x, y, x + width, y);
917 XDrawLine (dpy, window, mw->menu.shadow_top_gc,
918 x, y + 1, x + width, y + 1);
919 break;
920
921 case SEPARATOR_SHADOW_ETCHED_OUT:
922 XDrawLine (dpy, window, mw->menu.shadow_top_gc,
923 x, y, x + width, y);
924 XDrawLine (dpy, window, mw->menu.shadow_bottom_gc,
925 x, y + 1, x + width, y + 1);
926 break;
927
928 case SEPARATOR_SHADOW_ETCHED_IN_DASH:
929 xgcv.line_style = LineOnOffDash;
930 XChangeGC (dpy, mw->menu.shadow_bottom_gc, GCLineStyle, &xgcv);
931 XChangeGC (dpy, mw->menu.shadow_top_gc, GCLineStyle, &xgcv);
932 draw_separator (mw, window, x, y, width, SEPARATOR_SHADOW_ETCHED_IN);
933 xgcv.line_style = LineSolid;
934 XChangeGC (dpy, mw->menu.shadow_bottom_gc, GCLineStyle, &xgcv);
935 XChangeGC (dpy, mw->menu.shadow_top_gc, GCLineStyle, &xgcv);
936 break;
937
938 case SEPARATOR_SHADOW_ETCHED_OUT_DASH:
939 xgcv.line_style = LineOnOffDash;
940 XChangeGC (dpy, mw->menu.shadow_bottom_gc, GCLineStyle, &xgcv);
941 XChangeGC (dpy, mw->menu.shadow_top_gc, GCLineStyle, &xgcv);
942 draw_separator (mw, window, x, y, width, SEPARATOR_SHADOW_ETCHED_OUT);
943 xgcv.line_style = LineSolid;
944 XChangeGC (dpy, mw->menu.shadow_bottom_gc, GCLineStyle, &xgcv);
945 XChangeGC (dpy, mw->menu.shadow_top_gc, GCLineStyle, &xgcv);
946 break;
947
948 case SEPARATOR_SHADOW_DOUBLE_ETCHED_IN:
949 draw_separator (mw, window, x, y, width, SEPARATOR_SHADOW_ETCHED_IN);
950 draw_separator (mw, window, x, y + 3, width, SEPARATOR_SHADOW_ETCHED_IN);
951 break;
952
953 case SEPARATOR_SHADOW_DOUBLE_ETCHED_OUT:
954 draw_separator (mw, window, x, y, width,
955 SEPARATOR_SHADOW_ETCHED_OUT);
956 draw_separator (mw, window, x, y + 3, width,
957 SEPARATOR_SHADOW_ETCHED_OUT);
958 break;
959
960 case SEPARATOR_SHADOW_DOUBLE_ETCHED_IN_DASH:
961 xgcv.line_style = LineOnOffDash;
962 XChangeGC (dpy, mw->menu.shadow_bottom_gc, GCLineStyle, &xgcv);
963 XChangeGC (dpy, mw->menu.shadow_top_gc, GCLineStyle, &xgcv);
964 draw_separator (mw, window, x, y, width,
965 SEPARATOR_SHADOW_DOUBLE_ETCHED_IN);
966 xgcv.line_style = LineSolid;
967 XChangeGC (dpy, mw->menu.shadow_bottom_gc, GCLineStyle, &xgcv);
968 XChangeGC (dpy, mw->menu.shadow_top_gc, GCLineStyle, &xgcv);
969 break;
970
971 case SEPARATOR_SHADOW_DOUBLE_ETCHED_OUT_DASH:
972 xgcv.line_style = LineOnOffDash;
973 XChangeGC (dpy, mw->menu.shadow_bottom_gc, GCLineStyle, &xgcv);
974 XChangeGC (dpy, mw->menu.shadow_top_gc, GCLineStyle, &xgcv);
975 draw_separator (mw, window, x, y, width,
976 SEPARATOR_SHADOW_DOUBLE_ETCHED_OUT);
977 xgcv.line_style = LineSolid;
978 XChangeGC (dpy, mw->menu.shadow_bottom_gc, GCLineStyle, &xgcv);
979 XChangeGC (dpy, mw->menu.shadow_top_gc, GCLineStyle, &xgcv);
980 break;
981
982 default:
983 emacs_abort ();
984 }
985 }
986
987
988
989
990 static int
991 separator_height (enum menu_separator separator)
992 {
993 switch (separator)
994 {
995 case SEPARATOR_NO_LINE:
996 return 2;
997
998 case SEPARATOR_SINGLE_LINE:
999 case SEPARATOR_SINGLE_DASHED_LINE:
1000 return 1;
1001
1002 case SEPARATOR_DOUBLE_LINE:
1003 case SEPARATOR_DOUBLE_DASHED_LINE:
1004 return 3;
1005
1006 case SEPARATOR_SHADOW_ETCHED_IN:
1007 case SEPARATOR_SHADOW_ETCHED_OUT:
1008 case SEPARATOR_SHADOW_ETCHED_IN_DASH:
1009 case SEPARATOR_SHADOW_ETCHED_OUT_DASH:
1010 return 2;
1011
1012 case SEPARATOR_SHADOW_DOUBLE_ETCHED_IN:
1013 case SEPARATOR_SHADOW_DOUBLE_ETCHED_OUT:
1014 case SEPARATOR_SHADOW_DOUBLE_ETCHED_IN_DASH:
1015 case SEPARATOR_SHADOW_DOUBLE_ETCHED_OUT_DASH:
1016 return 5;
1017
1018 default:
1019 emacs_abort ();
1020 }
1021 }
1022
1023
1024
1025 static void
1026 draw_highlight (XlwMenuWidget mw, Window window, int x, int y, int width,
1027 int height)
1028 {
1029 Display *dpy = XtDisplay (mw);
1030 XPoint points [4];
1031
1032 points [0].x = x;
1033 points [0].y = y;
1034 points [1].x = x + width;
1035 points [1].y = y;
1036 points [2].x = x + width;
1037 points [2].y = y + height;
1038 points [3].x = x;
1039 points [3].y = y + height;
1040 XFillPolygon (dpy, window,
1041 mw->menu.highlight_background_gc,
1042 points, 4, Convex, CoordModeOrigin);
1043
1044 draw_shadow_rectangle(mw, window, x, y, width, height, False, False,
1045 mw->menu.highlight_shadow_top_gc,
1046 mw->menu.highlight_shadow_bottom_gc);
1047 }
1048
1049
1050
1051
1052 static void
1053 display_menu_item (XlwMenuWidget mw,
1054 widget_value* val,
1055 window_state* ws,
1056 XPoint* where,
1057 Boolean highlighted_p,
1058 Boolean horizontal_p,
1059 Boolean just_compute_p)
1060 {
1061 GC deco_gc;
1062 GC text_gc;
1063 int font_ascent = MENU_FONT_ASCENT (mw);
1064 int shadow = mw->menu.shadow_thickness;
1065 int margin = mw->menu.margin;
1066 int h_spacing = mw->menu.horizontal_spacing;
1067 int v_spacing = mw->menu.vertical_spacing;
1068 int label_width;
1069 int rest_width;
1070 int button_width;
1071 int height;
1072 int width;
1073 enum menu_separator separator;
1074 int separator_p = lw_separator_p (val->name, &separator, 0);
1075 #if defined USE_CAIRO || defined HAVE_XFT
1076 XftColor *xftfg;
1077 #endif
1078
1079
1080 size_menu_item (mw, val, horizontal_p, &label_width, &rest_width,
1081 &button_width, &height);
1082
1083 if (horizontal_p)
1084 width = label_width + rest_width;
1085 else
1086 {
1087 label_width = ws->label_width;
1088 width = ws->width - 2 * shadow;
1089 }
1090
1091
1092 if (highlighted_p)
1093 if (!val->enabled || !(val->call_data || val->contents))
1094 highlighted_p = 0;
1095
1096
1097 if (!just_compute_p)
1098 {
1099
1100 int x = where->x + shadow;
1101 int y = where->y + shadow;
1102
1103 if (horizontal_p)
1104 {
1105 x += margin;
1106 y += margin;
1107 }
1108
1109
1110 if (val->enabled)
1111 if (highlighted_p)
1112 text_gc = mw->menu.highlight_foreground_gc;
1113 else
1114 text_gc = mw->menu.foreground_gc;
1115 else
1116 text_gc = mw->menu.disabled_gc;
1117 deco_gc = mw->menu.foreground_gc;
1118 #if defined USE_CAIRO || defined HAVE_XFT
1119 if (val->enabled)
1120 if (highlighted_p)
1121 xftfg = &mw->menu.xft_highlight_fg;
1122 else
1123 xftfg = &mw->menu.xft_fg;
1124 else
1125 xftfg = &mw->menu.xft_disabled_fg;
1126 #endif
1127
1128 if (separator_p)
1129 draw_separator (mw, ws->pixmap, x, y, width, separator);
1130 else
1131 {
1132 int x_offset = x + h_spacing + shadow;
1133 char* display_string = resource_widget_value (mw, val);
1134
1135 draw_shadow_rectangle (mw, ws->pixmap, x, y, width, height,
1136 True, False, NULL, NULL);
1137 if (highlighted_p)
1138 draw_highlight (mw, ws->pixmap, x, y, width, height);
1139
1140
1141 if (!horizontal_p && !val->contents && !val->call_data)
1142 {
1143 int l = string_width (mw, display_string);
1144
1145 if (width > l)
1146 x_offset = (width - l) >> 1;
1147 }
1148 else if (!horizontal_p && ws->button_width)
1149 x_offset += ws->button_width;
1150
1151
1152 #if defined USE_CAIRO || defined HAVE_XFT
1153 if (ws->xft_draw)
1154 {
1155 int draw_y = y + v_spacing + shadow;
1156 #ifdef USE_CAIRO
1157 cairo_surface_mark_dirty (cairo_get_target (ws->xft_draw));
1158 #endif
1159 XftDrawStringUtf8 (ws->xft_draw, xftfg,
1160 mw->menu.xft_font,
1161 x_offset, draw_y + font_ascent,
1162 (unsigned char *) display_string,
1163 strlen (display_string));
1164 }
1165 else
1166 #endif
1167 #ifdef HAVE_X_I18N
1168 if (mw->menu.fontSet)
1169 XmbDrawString (XtDisplay (mw), ws->pixmap, mw->menu.fontSet,
1170 text_gc, x_offset,
1171 y + v_spacing + shadow + font_ascent,
1172 display_string, strlen (display_string));
1173 else
1174 #endif
1175 XDrawString (XtDisplay (mw), ws->pixmap,
1176 text_gc, x_offset,
1177 y + v_spacing + shadow + font_ascent,
1178 display_string, strlen (display_string));
1179
1180 if (!horizontal_p)
1181 {
1182 if (val->button_type == BUTTON_TYPE_TOGGLE)
1183 draw_toggle (mw, ws->pixmap, x, y + v_spacing + shadow,
1184 val->selected, highlighted_p);
1185 else if (val->button_type == BUTTON_TYPE_RADIO)
1186 draw_radio (mw, ws->pixmap, x, y + v_spacing + shadow,
1187 val->selected, highlighted_p);
1188
1189 if (val->contents)
1190 {
1191 int a_w = arrow_width (mw);
1192 draw_arrow (mw, ws->pixmap, deco_gc,
1193 x + width - a_w
1194 - mw->menu.horizontal_spacing
1195 - mw->menu.shadow_thickness,
1196 y + v_spacing + shadow, a_w,
1197 highlighted_p);
1198 }
1199 else if (val->key)
1200 {
1201 #if defined USE_CAIRO || defined HAVE_XFT
1202 if (ws->xft_draw)
1203 {
1204 int draw_x = ws->width - ws->max_rest_width
1205 + mw->menu.arrow_spacing;
1206 int draw_y = y + v_spacing + shadow + font_ascent;
1207 XftDrawStringUtf8 (ws->xft_draw, xftfg,
1208 mw->menu.xft_font,
1209 draw_x, draw_y,
1210 (unsigned char *) val->key,
1211 strlen (val->key));
1212 }
1213 else
1214 #endif
1215 #ifdef HAVE_X_I18N
1216 if (mw->menu.fontSet)
1217 XmbDrawString (XtDisplay (mw), ws->pixmap,
1218 mw->menu.fontSet,
1219 text_gc,
1220 x + label_width + mw->menu.arrow_spacing,
1221 y + v_spacing + shadow + font_ascent,
1222 val->key, strlen (val->key));
1223 else
1224 #endif
1225 XDrawString (XtDisplay (mw), ws->pixmap,
1226 text_gc,
1227 x + label_width + mw->menu.arrow_spacing,
1228 y + v_spacing + shadow + font_ascent,
1229 val->key, strlen (val->key));
1230 }
1231 }
1232 else
1233 {
1234
1235
1236 if (!highlighted_p)
1237 draw_shadow_rectangle (mw, ws->pixmap, x, y, width, height,
1238 True, False, NULL, NULL);
1239 }
1240 #ifdef USE_CAIRO
1241 if (ws->xft_draw)
1242 cairo_surface_flush (cairo_get_target (ws->xft_draw));
1243 #endif
1244 }
1245 }
1246 where->x += width;
1247 where->y += height;
1248 }
1249
1250 static void
1251 display_menu (XlwMenuWidget mw,
1252 int level,
1253 Boolean just_compute_p,
1254 XPoint *highlighted_pos,
1255 XPoint *hit,
1256 widget_value **hit_return)
1257 {
1258 widget_value* val;
1259 widget_value* following_item;
1260 window_state* ws;
1261 XPoint where;
1262 int horizontal_p = mw->menu.horizontal && (level == 0);
1263 int highlighted_p;
1264 int no_return = 0;
1265 enum menu_separator separator;
1266
1267 if (level >= mw->menu.old_depth)
1268 abort_gracefully ((Widget) mw);
1269
1270 if (level < mw->menu.old_depth - 1)
1271 following_item = mw->menu.old_stack [level + 1];
1272 else
1273 following_item = NULL;
1274
1275 if (hit)
1276 *hit_return = NULL;
1277
1278 where.x = 0;
1279 where.y = 0;
1280
1281 ws = &mw->menu.windows [level];
1282
1283 if (!just_compute_p)
1284 XFillRectangle (XtDisplay (mw), ws->pixmap, mw->menu.background_gc,
1285 0, 0, ws->width, ws->height);
1286
1287 for (val = mw->menu.old_stack [level]->contents; val; val = val->next)
1288 {
1289 highlighted_p = val == following_item;
1290 if (highlighted_p && highlighted_pos)
1291 {
1292 if (horizontal_p)
1293 highlighted_pos->x = where.x;
1294 else
1295 highlighted_pos->y = where.y;
1296 }
1297
1298 display_menu_item (mw, val, ws, &where, highlighted_p, horizontal_p,
1299 just_compute_p);
1300
1301 if (highlighted_p && highlighted_pos)
1302 {
1303 if (horizontal_p)
1304 highlighted_pos->y = where.y;
1305 else
1306 highlighted_pos->x = where.x;
1307 }
1308
1309 if (hit
1310 && !*hit_return
1311 && (horizontal_p ? hit->x < where.x : hit->y < where.y)
1312 && !lw_separator_p (val->name, &separator, 0)
1313 && !no_return)
1314 {
1315 if (val->enabled)
1316 *hit_return = val;
1317 else
1318 no_return = 1;
1319 if (mw->menu.inside_entry != val)
1320 {
1321 if (mw->menu.inside_entry)
1322 XtCallCallbackList ((Widget)mw, mw->menu.leave,
1323 (XtPointer) mw->menu.inside_entry);
1324 mw->menu.inside_entry = val;
1325 XtCallCallbackList ((Widget)mw, mw->menu.enter,
1326 (XtPointer) mw->menu.inside_entry);
1327 }
1328 }
1329
1330 if (horizontal_p)
1331 where.y = 0;
1332 else
1333 where.x = 0;
1334 }
1335
1336 if (!just_compute_p)
1337 {
1338 draw_shadow_rectangle (mw, ws->pixmap, 0, 0, ws->width, ws->height,
1339 False, False, NULL, NULL);
1340 XCopyArea (XtDisplay (mw), ws->pixmap, ws->window,
1341 mw->menu.foreground_gc, 0, 0, ws->width, ws->height, 0, 0);
1342 }
1343 }
1344
1345
1346 static void
1347 set_new_state (XlwMenuWidget mw, widget_value *val, int level)
1348 {
1349 int i;
1350
1351 mw->menu.new_depth = 0;
1352 for (i = 0; i < level; i++)
1353 push_new_stack (mw, mw->menu.old_stack [i]);
1354 push_new_stack (mw, val);
1355 }
1356
1357 static void
1358 expose_cb (Widget widget,
1359 XtPointer closure,
1360 XEvent* event,
1361 Boolean* continue_to_dispatch)
1362 {
1363 XlwMenuWidget mw = (XlwMenuWidget) closure;
1364 int i;
1365
1366 *continue_to_dispatch = False;
1367 for (i = 0; i < mw->menu.windows_length; ++i)
1368 if (mw->menu.windows [i].w == widget) break;
1369 if (i < mw->menu.windows_length && i < mw->menu.old_depth)
1370 display_menu (mw, i, False, NULL, NULL, NULL);
1371 }
1372
1373 static void
1374 set_window_type (Widget w, XlwMenuWidget mw)
1375 {
1376 int popup_menu_p = mw->menu.top_depth == 1;
1377 Atom type = XInternAtom (XtDisplay (w),
1378 popup_menu_p
1379 ? "_NET_WM_WINDOW_TYPE_POPUP_MENU"
1380 : "_NET_WM_WINDOW_TYPE_DROPDOWN_MENU",
1381 False);
1382
1383 XChangeProperty (XtDisplay (w), XtWindow (w),
1384 XInternAtom (XtDisplay (w), "_NET_WM_WINDOW_TYPE", False),
1385 XA_ATOM, 32, PropModeReplace,
1386 (unsigned char *)&type, 1);
1387 }
1388
1389
1390 static void
1391 make_windows_if_needed (XlwMenuWidget mw, int n)
1392 {
1393 int i;
1394 int start_at;
1395 window_state* windows;
1396
1397 if (mw->menu.windows_length >= n)
1398 return;
1399
1400 if (!mw->menu.windows)
1401 {
1402 mw->menu.windows =
1403 (window_state*)XtMalloc (n * sizeof (window_state));
1404 start_at = 0;
1405 }
1406 else
1407 {
1408 mw->menu.windows =
1409 (window_state*)XtRealloc ((char*)mw->menu.windows,
1410 n * sizeof (window_state));
1411 start_at = mw->menu.windows_length;
1412 }
1413 mw->menu.windows_length = n;
1414
1415 windows = mw->menu.windows;
1416
1417 for (i = start_at; i < n; i++)
1418 {
1419 Arg av[10];
1420 int ac = 0;
1421 windows [i].x = 0;
1422 windows [i].y = 0;
1423 windows [i].width = 1;
1424 windows [i].height = 1;
1425 windows [i].max_rest_width = 0;
1426 XtSetArg (av[ac], XtNwidth, 1); ++ac;
1427 XtSetArg (av[ac], XtNheight, 1); ++ac;
1428 XtSetArg (av[ac], XtNsaveUnder, True); ++ac;
1429 XtSetArg (av[ac], XtNbackground, mw->core.background_pixel); ++ac;
1430 XtSetArg (av[ac], XtNborderColor, mw->core.border_pixel); ++ac;
1431 XtSetArg (av[ac], XtNborderWidth, mw->core.border_width); ++ac;
1432 XtSetArg (av[ac], XtNcursor, mw->menu.cursor_shape); ++ac;
1433 windows [i].w =
1434 XtCreatePopupShell ("sub", overrideShellWidgetClass,
1435 (Widget) mw, av, ac);
1436 XtRealizeWidget (windows [i].w);
1437 XtAddEventHandler (windows [i].w, ExposureMask, False, expose_cb, mw);
1438 windows [i].window = XtWindow (windows [i].w);
1439 windows [i].pixmap = None;
1440 #if defined USE_CAIRO || defined HAVE_XFT
1441 windows [i].xft_draw = 0;
1442 #endif
1443 set_window_type (windows [i].w, mw);
1444 }
1445 XFlush (XtDisplay (mw));
1446 }
1447
1448
1449
1450 int
1451 xlwmenu_window_p (Widget w, Window window)
1452 {
1453 XlwMenuWidget mw = (XlwMenuWidget) w;
1454 int i;
1455
1456 for (i = 0; i < mw->menu.windows_length; ++i)
1457 if (window == mw->menu.windows[i].window)
1458 break;
1459
1460 return i < mw->menu.windows_length;
1461 }
1462
1463
1464 static void
1465 fit_to_screen (XlwMenuWidget mw,
1466 window_state *ws,
1467 window_state *previous_ws,
1468 Boolean horizontal_p)
1469 {
1470 int screen_width, screen_height;
1471 int screen_x, screen_y;
1472 int prev_screen_x, prev_screen_y;
1473
1474 #ifdef emacs
1475 xlw_monitor_dimensions_at_pos (XtDisplay (mw), XtScreen (mw),
1476 previous_ws->x, previous_ws->y,
1477 &prev_screen_x, &prev_screen_y,
1478 &screen_width, &screen_height);
1479 xlw_monitor_dimensions_at_pos (XtDisplay (mw), XtScreen (mw),
1480 ws->x, ws->y, &screen_x, &screen_y,
1481 &screen_width, &screen_height);
1482 #else
1483 screen_width = WidthOfScreen (XtScreen (mw));
1484 screen_height = HeightOfScreen (XtScreen (mw));
1485 prev_screen_x = screen_x = 0;
1486 prev_screen_y = screen_y = 0;
1487 #endif
1488
1489
1490 int horizontal_overlap = 0;
1491
1492 if (ws->x < screen_x)
1493 ws->x = screen_x;
1494 else if (ws->x + ws->width > screen_x + screen_width)
1495 {
1496 if (!horizontal_p)
1497
1498
1499
1500
1501 ws->x = screen_x + (previous_ws->x
1502 - prev_screen_x
1503 - ws->width
1504 + mw->menu.shadow_thickness);
1505 else
1506 ws->x = screen_x + (screen_width - ws->width);
1507 if (ws->x < screen_x)
1508 {
1509 ws->x = screen_x;
1510 horizontal_overlap = 1;
1511 }
1512 }
1513
1514 if (horizontal_overlap
1515 && ws->y < previous_ws->y + previous_ws->height
1516 && previous_ws->y < ws->y + ws->height)
1517 {
1518
1519
1520 if (previous_ws->y + previous_ws->height + ws->height < screen_height)
1521 ws->y = previous_ws->y + previous_ws->height;
1522 else if (previous_ws->y - ws->height > 0)
1523 ws->y = previous_ws->y - ws->height;
1524 }
1525
1526 if (ws->y < screen_y)
1527 ws->y = screen_y;
1528 else if (ws->y + ws->height > screen_y + screen_height)
1529 {
1530 if (horizontal_p)
1531 ws->y = screen_y + (previous_ws->y
1532 - prev_screen_y
1533 - ws->height);
1534 else
1535 ws->y = screen_y + (screen_height - ws->height);
1536 if (ws->y < screen_y)
1537 ws->y = screen_y;
1538 }
1539 }
1540
1541 static void
1542 create_pixmap_for_menu (window_state* ws, XlwMenuWidget mw)
1543 {
1544 if (ws->pixmap != None)
1545 {
1546 XFreePixmap (XtDisplay (ws->w), ws->pixmap);
1547 ws->pixmap = None;
1548 }
1549 ws->pixmap = XCreatePixmap (XtDisplay (ws->w), ws->window,
1550 ws->width, ws->height,
1551 DefaultDepthOfScreen (XtScreen (ws->w)));
1552 #if defined USE_CAIRO || defined HAVE_XFT
1553 if (ws->xft_draw)
1554 XftDrawDestroy (ws->xft_draw);
1555 if (mw->menu.xft_font)
1556 {
1557 int screen = XScreenNumberOfScreen (mw->core.screen);
1558 ws->xft_draw = XftDrawCreate (XtDisplay (ws->w),
1559 ws->pixmap,
1560 DefaultVisual (XtDisplay (ws->w), screen),
1561 mw->core.colormap);
1562 }
1563 else
1564 ws->xft_draw = 0;
1565 #endif
1566 }
1567
1568
1569 static void
1570 remap_menubar (XlwMenuWidget mw)
1571 {
1572 int i;
1573 int last_same;
1574 XPoint selection_position;
1575 int old_depth = mw->menu.old_depth;
1576 int new_depth = mw->menu.new_depth;
1577 widget_value** old_stack;
1578 widget_value** new_stack;
1579 window_state* windows;
1580 widget_value* old_selection;
1581 widget_value* new_selection;
1582
1583
1584 make_windows_if_needed (mw, new_depth);
1585 make_old_stack_space (mw, new_depth);
1586 windows = mw->menu.windows;
1587 old_stack = mw->menu.old_stack;
1588 new_stack = mw->menu.new_stack;
1589
1590
1591 for (i = 1; i < old_depth && i < new_depth; i++)
1592 if (old_stack [i] != new_stack [i])
1593 break;
1594 last_same = i - 1;
1595
1596
1597 old_selection = last_same + 1 < old_depth ? old_stack [last_same + 1] : NULL;
1598 if (old_selection && !old_selection->enabled)
1599 old_selection = NULL;
1600 new_selection = last_same + 1 < new_depth ? new_stack [last_same + 1] : NULL;
1601 if (new_selection && !new_selection->enabled)
1602 new_selection = NULL;
1603
1604
1605 if (old_selection || new_selection)
1606 XtCallCallbackList ((Widget)mw, mw->menu.highlight,
1607 (XtPointer) new_selection);
1608
1609
1610
1611 for (i = last_same + 1; i < new_depth; i++)
1612 {
1613 XtPopdown (mw->menu.windows [i].w);
1614 old_stack [i] = new_stack [i];
1615 }
1616 mw->menu.old_depth = new_depth;
1617
1618
1619 selection_position.x = 0;
1620 selection_position.y = 0;
1621 display_menu (mw, last_same, new_selection == old_selection,
1622 &selection_position, NULL, NULL);
1623
1624
1625 for (i = last_same + 1; i < new_depth && new_stack[i]->contents; i++)
1626 {
1627 window_state *previous_ws = &windows[i - 1];
1628 window_state *ws = &windows[i];
1629
1630 ws->x = (previous_ws->x + selection_position.x
1631 + mw->menu.shadow_thickness);
1632 if (mw->menu.horizontal && i == 1)
1633 ws->x += mw->menu.margin;
1634
1635 #if 0
1636 if (!mw->menu.horizontal || i > 1)
1637 ws->x += mw->menu.shadow_thickness;
1638 #endif
1639
1640 ws->y = (previous_ws->y + selection_position.y
1641 + mw->menu.shadow_thickness);
1642 if (mw->menu.horizontal && i == 1)
1643 ws->y += mw->menu.margin;
1644
1645
1646
1647 XtUnrealizeWidget (ws->w);
1648 XtRealizeWidget (ws->w);
1649 ws->window = XtWindow (ws->w);
1650
1651 size_menu (mw, i);
1652
1653 fit_to_screen (mw, ws, previous_ws, mw->menu.horizontal && i == 1);
1654
1655 create_pixmap_for_menu (ws, mw);
1656 XtConfigureWidget (ws->w, ws->x, ws->y, ws->width, ws->height,
1657 ws->w->core.border_width);
1658 display_menu (mw, i, False, &selection_position, NULL, NULL);
1659 XtPopup (ws->w, XtGrabNone);
1660 }
1661
1662
1663 for (i = new_depth - 1; i < old_depth; i++)
1664 if (i >= new_depth || (i > 0 && !new_stack[i]->contents))
1665 XtPopdown (windows[i].w);
1666 }
1667
1668 static Boolean
1669 motion_event_is_in_menu (XlwMenuWidget mw,
1670 XMotionEvent *ev,
1671 int level,
1672 XPoint *relative_pos)
1673 {
1674 window_state* ws = &mw->menu.windows [level];
1675 int shadow = level == 0 ? 0 : mw->menu.shadow_thickness;
1676 int x = ws->x + shadow;
1677 int y = ws->y + shadow;
1678 relative_pos->x = ev->x_root - x;
1679 relative_pos->y = ev->y_root - y;
1680 return (x - shadow < ev->x_root && ev->x_root < x + ws->width
1681 && y - shadow < ev->y_root && ev->y_root < y + ws->height);
1682 }
1683
1684 static Boolean
1685 map_event_to_widget_value (XlwMenuWidget mw,
1686 XMotionEvent *ev,
1687 widget_value **val,
1688 int *level)
1689 {
1690 int i;
1691 XPoint relative_pos;
1692 window_state* ws;
1693 int inside = 0;
1694
1695 *val = NULL;
1696
1697
1698 for (i = mw->menu.old_depth - 1; i >= 0; i--)
1699 {
1700 ws = &mw->menu.windows [i];
1701 if (ws && motion_event_is_in_menu (mw, ev, i, &relative_pos))
1702 {
1703 inside = 1;
1704 display_menu (mw, i, True, NULL, &relative_pos, val);
1705
1706 if (*val)
1707 {
1708 *level = i + 1;
1709 return True;
1710 }
1711 }
1712 }
1713
1714 if (!inside)
1715 {
1716 if (mw->menu.inside_entry != NULL)
1717 XtCallCallbackList ((Widget)mw, mw->menu.leave,
1718 (XtPointer) mw->menu.inside_entry);
1719 mw->menu.inside_entry = NULL;
1720 }
1721
1722 return False;
1723 }
1724
1725
1726 static void
1727 make_drawing_gcs (XlwMenuWidget mw)
1728 {
1729 XGCValues xgcv;
1730 float scale;
1731 XtGCMask mask = GCForeground | GCBackground;
1732
1733 #ifdef HAVE_X_I18N
1734 if (!mw->menu.fontSet && mw->menu.font)
1735 {
1736 xgcv.font = mw->menu.font->fid;
1737 mask |= GCFont;
1738 }
1739 #else
1740 if (mw->menu.font)
1741 {
1742 xgcv.font = mw->menu.font->fid;
1743 mask |= GCFont;
1744 }
1745 #endif
1746 xgcv.foreground = mw->menu.foreground;
1747 xgcv.background = mw->core.background_pixel;
1748 mw->menu.foreground_gc = XtGetGC ((Widget)mw, mask, &xgcv);
1749
1750 xgcv.foreground = mw->menu.button_foreground;
1751 mw->menu.button_gc = XtGetGC ((Widget)mw, mask, &xgcv);
1752
1753 xgcv.background = mw->core.background_pixel;
1754
1755 #define BRIGHTNESS(color) (((color) & 0xff) + (((color) >> 8) & 0xff) + (((color) >> 16) & 0xff))
1756
1757
1758 if (BRIGHTNESS(mw->menu.foreground) < BRIGHTNESS(mw->core.background_pixel))
1759 scale = 2.3;
1760 else
1761 scale = 0.55;
1762
1763 x_alloc_lighter_color_for_widget ((Widget) mw, XtDisplay ((Widget) mw),
1764 mw->core.colormap,
1765 &mw->menu.disabled_foreground,
1766 scale,
1767 0x8000);
1768
1769 if (mw->menu.foreground == mw->menu.disabled_foreground
1770 || mw->core.background_pixel == mw->menu.disabled_foreground)
1771 {
1772
1773 xgcv.foreground = mw->menu.foreground;
1774 xgcv.fill_style = FillStippled;
1775 xgcv.stipple = mw->menu.gray_pixmap;
1776 mw->menu.disabled_gc = XtGetGC ((Widget)mw, mask
1777 | GCFillStyle | GCStipple, &xgcv);
1778 }
1779 else
1780 {
1781
1782 xgcv.foreground = mw->menu.disabled_foreground;
1783 mw->menu.disabled_gc = XtGetGC ((Widget)mw, mask, &xgcv);
1784 }
1785
1786 xgcv.foreground = mw->menu.button_foreground;
1787 xgcv.background = mw->core.background_pixel;
1788 xgcv.fill_style = FillStippled;
1789 xgcv.stipple = mw->menu.gray_pixmap;
1790 mw->menu.inactive_button_gc = XtGetGC ((Widget)mw, mask
1791 | GCFillStyle | GCStipple, &xgcv);
1792
1793 xgcv.foreground = mw->core.background_pixel;
1794 xgcv.background = mw->menu.foreground;
1795 mw->menu.background_gc = XtGetGC ((Widget)mw, mask, &xgcv);
1796
1797 xgcv.foreground = ((mw->menu.highlight_foreground == -1)
1798 ? mw->menu.foreground
1799 : mw->menu.highlight_foreground);
1800 xgcv.background = ((mw->menu.highlight_background == -1)
1801 ? mw->core.background_pixel
1802 : mw->menu.highlight_background);
1803 mw->menu.highlight_foreground_gc = XtGetGC ((Widget)mw, mask, &xgcv);
1804
1805 xgcv.foreground = ((mw->menu.highlight_background == -1)
1806 ? mw->core.background_pixel
1807 : mw->menu.highlight_background);
1808 xgcv.background = mw->menu.foreground;
1809 mw->menu.highlight_background_gc = XtGetGC ((Widget)mw, mask, &xgcv);
1810 }
1811
1812 static void
1813 release_drawing_gcs (XlwMenuWidget mw)
1814 {
1815 XtReleaseGC ((Widget) mw, mw->menu.foreground_gc);
1816 XtReleaseGC ((Widget) mw, mw->menu.button_gc);
1817 XtReleaseGC ((Widget) mw, mw->menu.disabled_gc);
1818 XtReleaseGC ((Widget) mw, mw->menu.inactive_button_gc);
1819 XtReleaseGC ((Widget) mw, mw->menu.background_gc);
1820 XtReleaseGC ((Widget) mw, mw->menu.highlight_foreground_gc);
1821 XtReleaseGC ((Widget) mw, mw->menu.highlight_background_gc);
1822
1823 mw->menu.foreground_gc = (GC) -1;
1824 mw->menu.button_gc = (GC) -1;
1825 mw->menu.disabled_gc = (GC) -1;
1826 mw->menu.inactive_button_gc = (GC) -1;
1827 mw->menu.background_gc = (GC) -1;
1828 mw->menu.highlight_foreground_gc = (GC) -1;
1829 mw->menu.highlight_background_gc = (GC) -1;
1830 }
1831
1832 #ifndef emacs
1833 #define MINL(x,y) ((((unsigned long) (x)) < ((unsigned long) (y))) \
1834 ? ((unsigned long) (x)) : ((unsigned long) (y)))
1835 #endif
1836
1837 static void
1838 compute_shadow_colors (XlwMenuWidget mw, Pixel *top_color, Pixel *bottom_color,
1839 Boolean *free_top_p, Boolean *free_bottom_p,
1840 Pixmap *top_pixmap, Pixmap *bottom_pixmap,
1841 Pixel fore_color, Pixel back_color)
1842 {
1843 Display *dpy = XtDisplay ((Widget) mw);
1844 Screen *screen = XtScreen ((Widget) mw);
1845 Colormap cmap = mw->core.colormap;
1846 XColor topc, botc;
1847 int top_frobbed = 0, bottom_frobbed = 0;
1848
1849 *free_top_p = False;
1850 *free_bottom_p = False;
1851
1852 if (*top_color == -1)
1853 *top_color = back_color;
1854
1855 if (*bottom_color == -1)
1856 *bottom_color = fore_color;
1857
1858 if (*top_color == back_color || *top_color == fore_color)
1859 {
1860 topc.pixel = back_color;
1861 #ifdef emacs
1862 if (x_alloc_lighter_color_for_widget ((Widget) mw, dpy, cmap,
1863 &topc.pixel,
1864 1.2, 0x8000))
1865 #else
1866 XQueryColor (dpy, cmap, &topc);
1867
1868 topc.red = MINL (65535, topc.red * 1.2);
1869 topc.green = MINL (65535, topc.green * 1.2);
1870 topc.blue = MINL (65535, topc.blue * 1.2);
1871 if (XAllocColor (dpy, cmap, &topc))
1872 #endif
1873 {
1874 *top_color = topc.pixel;
1875 *free_top_p = True;
1876 top_frobbed = 1;
1877 }
1878 }
1879 if (*bottom_color == fore_color || *bottom_color == back_color)
1880 {
1881 botc.pixel = back_color;
1882 #ifdef emacs
1883 if (x_alloc_lighter_color_for_widget ((Widget) mw, dpy, cmap,
1884 &botc.pixel,
1885 0.6, 0x4000))
1886 #else
1887 XQueryColor (dpy, cmap, &botc);
1888 botc.red *= 0.6;
1889 botc.green *= 0.6;
1890 botc.blue *= 0.6;
1891 if (XAllocColor (dpy, cmap, &botc))
1892 #endif
1893 {
1894 *bottom_color = botc.pixel;
1895 *free_bottom_p = True;
1896 bottom_frobbed = 1;
1897 }
1898 }
1899
1900 if (top_frobbed && bottom_frobbed)
1901 {
1902 if (topc.pixel == botc.pixel)
1903 {
1904 if (botc.pixel == fore_color)
1905 {
1906 if (*free_top_p)
1907 {
1908 x_free_dpy_colors (dpy, screen, cmap, top_color, 1);
1909 *free_top_p = False;
1910 }
1911 *top_color = back_color;
1912 }
1913 else
1914 {
1915 if (*free_bottom_p)
1916 {
1917 x_free_dpy_colors (dpy, screen, cmap, bottom_color, 1);
1918 *free_bottom_p = False;
1919 }
1920 *bottom_color = fore_color;
1921 }
1922 }
1923 }
1924
1925 if (!*top_pixmap && *top_color == back_color)
1926 {
1927 *top_pixmap = mw->menu.gray_pixmap;
1928 if (*free_top_p)
1929 {
1930 x_free_dpy_colors (dpy, screen, cmap, top_color, 1);
1931 *free_top_p = False;
1932 }
1933 *top_color = fore_color;
1934 }
1935 if (!*bottom_pixmap && *bottom_color == back_color)
1936 {
1937 *bottom_pixmap = mw->menu.gray_pixmap;
1938 if (*free_bottom_p)
1939 {
1940 x_free_dpy_colors (dpy, screen, cmap, bottom_color, 1);
1941 *free_bottom_p = False;
1942 }
1943 *bottom_color = fore_color;
1944 }
1945 }
1946
1947 static void
1948 make_shadow_gcs (XlwMenuWidget mw)
1949 {
1950 XGCValues xgcv;
1951 unsigned long pm = 0;
1952 Pixel highlight_fg;
1953
1954 highlight_fg = mw->menu.highlight_foreground;
1955
1956 if (highlight_fg == -1)
1957 highlight_fg = mw->menu.foreground;
1958
1959
1960 compute_shadow_colors (mw, &(mw->menu.top_shadow_color),
1961 &(mw->menu.bottom_shadow_color),
1962 &(mw->menu.free_top_shadow_color_p),
1963 &(mw->menu.free_bottom_shadow_color_p),
1964 &(mw->menu.top_shadow_pixmap),
1965 &(mw->menu.bottom_shadow_pixmap),
1966 mw->menu.foreground, mw->core.background_pixel);
1967
1968
1969 compute_shadow_colors (mw, &(mw->menu.top_highlight_shadow_color),
1970 &(mw->menu.bottom_highlight_shadow_color),
1971 &(mw->menu.free_top_highlight_shadow_color_p),
1972 &(mw->menu.free_bottom_highlight_shadow_color_p),
1973 &(mw->menu.top_highlight_shadow_pixmap),
1974 &(mw->menu.bottom_highlight_shadow_pixmap),
1975 highlight_fg, mw->menu.highlight_background);
1976
1977 xgcv.fill_style = FillStippled;
1978 xgcv.foreground = mw->menu.top_shadow_color;
1979 xgcv.stipple = mw->menu.top_shadow_pixmap;
1980 pm = (xgcv.stipple ? GCStipple | GCFillStyle : 0);
1981 mw->menu.shadow_top_gc = XtGetGC ((Widget) mw, GCForeground | pm, &xgcv);
1982
1983 xgcv.foreground = mw->menu.bottom_shadow_color;
1984 xgcv.stipple = mw->menu.bottom_shadow_pixmap;
1985 pm = (xgcv.stipple ? GCStipple | GCFillStyle : 0);
1986 mw->menu.shadow_bottom_gc = XtGetGC ((Widget) mw, GCForeground | pm, &xgcv);
1987
1988 xgcv.foreground = mw->menu.top_highlight_shadow_color;
1989 xgcv.stipple = mw->menu.top_highlight_shadow_pixmap;
1990 pm = (xgcv.stipple ? GCStipple | GCFillStyle : 0);
1991 mw->menu.highlight_shadow_top_gc = XtGetGC ((Widget) mw, GCForeground | pm, &xgcv);
1992
1993 xgcv.foreground = mw->menu.bottom_highlight_shadow_color;
1994 xgcv.stipple = mw->menu.bottom_highlight_shadow_pixmap;
1995 pm = (xgcv.stipple ? GCStipple | GCFillStyle : 0);
1996 mw->menu.highlight_shadow_bottom_gc = XtGetGC ((Widget) mw, GCForeground | pm, &xgcv);
1997 }
1998
1999 static void
2000 release_shadow_gcs (XlwMenuWidget mw)
2001 {
2002 Display *dpy = XtDisplay ((Widget) mw);
2003 Screen *screen = XtScreen ((Widget) mw);
2004 Colormap cmap = mw->core.colormap;
2005 Pixel px[4];
2006 int i = 0;
2007
2008 if (mw->menu.free_top_shadow_color_p)
2009 px[i++] = mw->menu.top_shadow_color;
2010 if (mw->menu.free_bottom_shadow_color_p)
2011 px[i++] = mw->menu.bottom_shadow_color;
2012 if (mw->menu.free_top_highlight_shadow_color_p)
2013 px[i++] = mw->menu.top_highlight_shadow_color;
2014 if (mw->menu.free_bottom_highlight_shadow_color_p)
2015 px[i++] = mw->menu.bottom_highlight_shadow_color;
2016 if (i > 0)
2017 x_free_dpy_colors (dpy, screen, cmap, px, i);
2018
2019 XtReleaseGC ((Widget) mw, mw->menu.shadow_top_gc);
2020 XtReleaseGC ((Widget) mw, mw->menu.shadow_bottom_gc);
2021 XtReleaseGC ((Widget) mw, mw->menu.highlight_shadow_top_gc);
2022 XtReleaseGC ((Widget) mw, mw->menu.highlight_shadow_bottom_gc);
2023 }
2024
2025 #if defined USE_CAIRO || defined HAVE_XFT
2026 static XftFont *
2027 getDefaultXftFont (XlwMenuWidget mw)
2028 {
2029 int screen = XScreenNumberOfScreen (mw->core.screen);
2030 return XftFontOpenName (XtDisplay (mw), screen, DEFAULT_FONTNAME);
2031 }
2032
2033 static int
2034 openXftFont (XlwMenuWidget mw)
2035 {
2036 char *fname = mw->menu.fontName;
2037
2038 mw->menu.xft_font = 0;
2039 mw->menu.default_face = fname && strcmp (fname, DEFAULT_FONTNAME) == 0;
2040
2041 if (fname && strcmp (fname, "none") != 0)
2042 {
2043 int screen = XScreenNumberOfScreen (mw->core.screen);
2044 int len = strlen (fname), i = len - 1;
2045
2046 while (i > 0 && '0' <= fname[i] && fname[i] <= '9')
2047 --i;
2048 if (fname[i] == ' ')
2049 {
2050 fname = xstrdup (mw->menu.fontName);
2051 fname[i] = '-';
2052 }
2053
2054 mw->menu.xft_font = XftFontOpenName (XtDisplay (mw), screen, fname);
2055 if (!mw->menu.xft_font)
2056 mw->menu.xft_font = getDefaultXftFont (mw);
2057 }
2058
2059 if (fname != mw->menu.fontName) xfree (fname);
2060
2061 return mw->menu.xft_font != 0;
2062 }
2063
2064 static void
2065 update_xft_colors (Widget w)
2066 {
2067 XlwMenuWidget mw;
2068 XColor colors[4];
2069
2070 mw = (XlwMenuWidget) w;
2071
2072 colors[0].pixel = mw->menu.xft_fg.pixel
2073 = mw->menu.foreground;
2074 colors[1].pixel = mw->menu.xft_bg.pixel
2075 = mw->core.background_pixel;
2076 colors[2].pixel = mw->menu.xft_disabled_fg.pixel
2077 = mw->menu.disabled_foreground;
2078 colors[3].pixel = mw->menu.xft_highlight_fg.pixel
2079 = (mw->menu.highlight_foreground != -1
2080 ? mw->menu.highlight_foreground
2081 : mw->menu.foreground);
2082
2083 XQueryColors (XtDisplay (mw), mw->core.colormap,
2084 colors, 4);
2085
2086 mw->menu.xft_fg.color.alpha = 0xFFFF;
2087 mw->menu.xft_fg.color.red = colors[0].red;
2088 mw->menu.xft_fg.color.green = colors[0].green;
2089 mw->menu.xft_fg.color.blue = colors[0].blue;
2090 mw->menu.xft_bg.color.alpha = 0xFFFF;
2091 mw->menu.xft_bg.color.red = colors[1].red;
2092 mw->menu.xft_bg.color.green = colors[1].green;
2093 mw->menu.xft_bg.color.blue = colors[1].blue;
2094 mw->menu.xft_disabled_fg.color.alpha = 0xFFFF;
2095 mw->menu.xft_disabled_fg.color.red = colors[2].red;
2096 mw->menu.xft_disabled_fg.color.green = colors[2].green;
2097 mw->menu.xft_disabled_fg.color.blue = colors[2].blue;
2098 mw->menu.xft_highlight_fg.color.alpha = 0xFFFF;
2099 mw->menu.xft_highlight_fg.color.red = colors[3].red;
2100 mw->menu.xft_highlight_fg.color.green = colors[3].green;
2101 mw->menu.xft_highlight_fg.color.blue = colors[3].blue;
2102 }
2103 #endif
2104
2105 static void
2106 XlwMenuInitialize (Widget request, Widget w, ArgList args, Cardinal *num_args)
2107 {
2108
2109 XlwMenuWidget mw = (XlwMenuWidget) w;
2110 Window window = RootWindowOfScreen (DefaultScreenOfDisplay (XtDisplay (mw)));
2111 Display* display = XtDisplay (mw);
2112
2113
2114 mw->menu.cursor = mw->menu.cursor_shape;
2115
2116 mw->menu.gray_pixmap
2117 = XCreatePixmapFromBitmapData (display, window, gray_bits,
2118 gray_width, gray_height,
2119 (unsigned long)1, (unsigned long)0, 1);
2120
2121 #if defined USE_CAIRO || defined HAVE_XFT
2122 if (openXftFont (mw))
2123 ;
2124 else
2125 #endif
2126 {
2127 mw->menu.font = XLoadQueryFont (display, mw->menu.fontName);
2128 if (!mw->menu.font)
2129 {
2130 mw->menu.font = XLoadQueryFont (display, "fixed");
2131 if (!mw->menu.font)
2132 {
2133 fprintf (stderr, "Menu font fixed not found, can't continue.\n");
2134 emacs_abort ();
2135 }
2136 }
2137 }
2138
2139 #ifdef HAVE_X_I18N
2140 if (mw->menu.fontSet)
2141 mw->menu.font_extents = XExtentsOfFontSet (mw->menu.fontSet);
2142 #endif
2143
2144 mw->menu.top_highlight_shadow_color = -1;
2145 mw->menu.bottom_highlight_shadow_color = -1;
2146 mw->menu.top_highlight_shadow_pixmap = None;
2147 mw->menu.bottom_highlight_shadow_pixmap = None;
2148
2149 make_drawing_gcs (mw);
2150 make_shadow_gcs (mw);
2151
2152 mw->menu.popped_up = False;
2153
2154 mw->menu.old_depth = 1;
2155 mw->menu.old_stack = (widget_value**)XtMalloc (sizeof (widget_value*));
2156 mw->menu.old_stack_length = 1;
2157 mw->menu.old_stack [0] = mw->menu.contents;
2158
2159 mw->menu.new_depth = 0;
2160 mw->menu.new_stack = 0;
2161 mw->menu.new_stack_length = 0;
2162 push_new_stack (mw, mw->menu.contents);
2163
2164 mw->menu.windows = (window_state*)XtMalloc (sizeof (window_state));
2165 mw->menu.windows_length = 1;
2166 mw->menu.windows [0].x = 0;
2167 mw->menu.windows [0].y = 0;
2168 mw->menu.windows [0].width = 0;
2169 mw->menu.windows [0].height = 0;
2170 mw->menu.windows [0].max_rest_width = 0;
2171 mw->menu.windows [0].pixmap = None;
2172 #if defined USE_CAIRO || defined HAVE_XFT
2173 mw->menu.windows [0].xft_draw = 0;
2174 #endif
2175 size_menu (mw, 0);
2176
2177 mw->core.width = mw->menu.windows [0].width;
2178 mw->core.height = mw->menu.windows [0].height;
2179 }
2180
2181 static void
2182 XlwMenuClassInitialize (void)
2183 {
2184 }
2185
2186 static void
2187 XlwMenuRealize (Widget w, Mask *valueMask, XSetWindowAttributes *attributes)
2188 {
2189 XlwMenuWidget mw = (XlwMenuWidget)w;
2190 XSetWindowAttributes xswa;
2191 int mask;
2192
2193 (*xlwMenuWidgetClass->core_class.superclass->core_class.realize)
2194 (w, valueMask, attributes);
2195
2196 xswa.save_under = True;
2197 xswa.cursor = mw->menu.cursor_shape;
2198 mask = CWSaveUnder | CWCursor;
2199
2200
2201
2202 #ifdef emacs
2203 x_catch_errors (XtDisplay (w));
2204 #endif
2205 XChangeWindowAttributes (XtDisplay (w), XtWindow (w), mask, &xswa);
2206 #ifdef emacs
2207 x_uncatch_errors ();
2208 #endif
2209
2210 mw->menu.windows [0].w = w;
2211 mw->menu.windows [0].window = XtWindow (w);
2212 mw->menu.windows [0].x = w->core.x;
2213 mw->menu.windows [0].y = w->core.y;
2214 mw->menu.windows [0].width = w->core.width;
2215 mw->menu.windows [0].height = w->core.height;
2216
2217 set_window_type (mw->menu.windows [0].w, mw);
2218 create_pixmap_for_menu (&mw->menu.windows [0], mw);
2219
2220 #if defined USE_CAIRO || defined HAVE_XFT
2221 if (mw->menu.xft_font)
2222 update_xft_colors (w);
2223 #endif
2224 }
2225
2226
2227
2228
2229 static void
2230 XlwMenuRedisplay (Widget w, XEvent *ev, Region region)
2231 {
2232 XlwMenuWidget mw = (XlwMenuWidget)w;
2233
2234
2235
2236 if (submenu_destroyed)
2237 {
2238 mw->menu.old_depth = 1;
2239 submenu_destroyed = 0;
2240 }
2241
2242 display_menu (mw, 0, False, NULL, NULL, NULL);
2243 }
2244
2245
2246
2247
2248
2249 void
2250 xlwmenu_redisplay (Widget w)
2251 {
2252 XlwMenuRedisplay (w, NULL, None);
2253 }
2254
2255 static void
2256 XlwMenuDestroy (Widget w)
2257 {
2258 int i;
2259 XlwMenuWidget mw = (XlwMenuWidget) w;
2260
2261 if (pointer_grabbed)
2262 ungrab_all ((Widget)w, CurrentTime);
2263 pointer_grabbed = 0;
2264 keyboard_grabbed = 0;
2265
2266 if (!XtIsShell (XtParent (w)))
2267 submenu_destroyed = 1;
2268
2269 release_drawing_gcs (mw);
2270 release_shadow_gcs (mw);
2271
2272
2273
2274 XFreePixmap (XtDisplay (mw), mw->menu.gray_pixmap);
2275 mw->menu.gray_pixmap = (Pixmap) -1;
2276
2277
2278
2279
2280 if (mw->menu.old_stack) XtFree ((char *) mw->menu.old_stack);
2281 if (mw->menu.new_stack) XtFree ((char *) mw->menu.new_stack);
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297 if (mw->menu.font)
2298 XFreeFont (XtDisplay (mw), mw->menu.font);
2299
2300 #if defined USE_CAIRO || defined HAVE_XFT
2301 if (mw->menu.windows [0].xft_draw)
2302 XftDrawDestroy (mw->menu.windows [0].xft_draw);
2303 if (mw->menu.xft_font)
2304 XftFontClose (XtDisplay (mw), mw->menu.xft_font);
2305 #endif
2306
2307 if (mw->menu.windows [0].pixmap != None)
2308 XFreePixmap (XtDisplay (mw), mw->menu.windows [0].pixmap);
2309
2310 for (i = 1; i < mw->menu.windows_length; i++)
2311 {
2312 if (mw->menu.windows [i].pixmap != None)
2313 XFreePixmap (XtDisplay (mw), mw->menu.windows [i].pixmap);
2314 #if defined USE_CAIRO || defined HAVE_XFT
2315 if (mw->menu.windows [i].xft_draw)
2316 XftDrawDestroy (mw->menu.windows [i].xft_draw);
2317 #endif
2318 }
2319
2320 if (mw->menu.windows)
2321 XtFree ((char *) mw->menu.windows);
2322 }
2323
2324 #if defined USE_CAIRO || defined HAVE_XFT
2325 static int
2326 fontname_changed (XlwMenuWidget newmw,
2327 XlwMenuWidget oldmw)
2328 {
2329
2330
2331
2332 return newmw->menu.fontName != oldmw->menu.fontName;
2333 }
2334 #endif
2335
2336 static Boolean
2337 XlwMenuSetValues (Widget current, Widget request, Widget new,
2338 ArgList args, Cardinal *num_args)
2339 {
2340 XlwMenuWidget oldmw = (XlwMenuWidget)current;
2341 XlwMenuWidget newmw = (XlwMenuWidget)new;
2342 Boolean do_redisplay = False;
2343
2344 if (newmw->menu.contents
2345 && newmw->menu.contents->contents
2346 && newmw->menu.contents->contents->change >= VISIBLE_CHANGE)
2347 do_redisplay = True;
2348
2349 if (newmw->menu.contents
2350 && newmw->menu.contents->contents == 0
2351 && newmw->menu.contents->change >= VISIBLE_CHANGE)
2352 do_redisplay = True;
2353
2354 if (newmw->core.background_pixel != oldmw->core.background_pixel
2355 || newmw->menu.foreground != oldmw->menu.foreground
2356 #if defined USE_CAIRO || defined HAVE_XFT
2357 || fontname_changed (newmw, oldmw)
2358 #endif
2359 #ifdef HAVE_X_I18N
2360 || newmw->menu.fontSet != oldmw->menu.fontSet
2361 || (newmw->menu.fontSet == NULL && newmw->menu.font != oldmw->menu.font)
2362 #else
2363 || newmw->menu.font != oldmw->menu.font
2364 #endif
2365 )
2366 {
2367 int i;
2368 release_drawing_gcs (newmw);
2369 make_drawing_gcs (newmw);
2370
2371 release_shadow_gcs (newmw);
2372
2373 newmw->menu.top_shadow_color = -1;
2374 newmw->menu.bottom_shadow_color = -1;
2375 make_shadow_gcs (newmw);
2376
2377 do_redisplay = True;
2378
2379 if (XtIsRealized (current))
2380
2381 for (i = 0; i < oldmw->menu.windows_length; i++)
2382 {
2383 XSetWindowBackground (XtDisplay (oldmw),
2384 oldmw->menu.windows [i].window,
2385 newmw->core.background_pixel);
2386
2387 XClearArea (XtDisplay (oldmw), oldmw->menu.windows[i].window,
2388 0, 0, 0, 0, True);
2389 }
2390
2391
2392 #if defined USE_CAIRO || defined HAVE_XFT
2393 if (oldmw->menu.xft_font)
2394 update_xft_colors (new);
2395 #endif
2396 }
2397
2398 #if defined USE_CAIRO || defined HAVE_XFT
2399 if (fontname_changed (newmw, oldmw))
2400 {
2401 int i;
2402 int screen = XScreenNumberOfScreen (newmw->core.screen);
2403 if (newmw->menu.xft_font)
2404 XftFontClose (XtDisplay (newmw), newmw->menu.xft_font);
2405 openXftFont (newmw);
2406 for (i = 0; i < newmw->menu.windows_length; i++)
2407 {
2408 if (newmw->menu.windows [i].xft_draw)
2409 XftDrawDestroy (newmw->menu.windows [i].xft_draw);
2410 newmw->menu.windows [i].xft_draw = 0;
2411 }
2412 if (newmw->menu.xft_font)
2413 for (i = 0; i < newmw->menu.windows_length; i++)
2414 newmw->menu.windows [i].xft_draw
2415 = XftDrawCreate (XtDisplay (newmw),
2416 newmw->menu.windows [i].window,
2417 DefaultVisual (XtDisplay (newmw), screen),
2418 newmw->core.colormap);
2419 }
2420 #endif
2421 #ifdef HAVE_X_I18N
2422 if (newmw->menu.fontSet != oldmw->menu.fontSet && newmw->menu.fontSet != NULL)
2423 {
2424 do_redisplay = True;
2425 newmw->menu.font_extents = XExtentsOfFontSet (newmw->menu.fontSet);
2426 }
2427 #endif
2428
2429 return do_redisplay;
2430 }
2431
2432 static void
2433 XlwMenuResize (Widget w)
2434 {
2435 XlwMenuWidget mw = (XlwMenuWidget)w;
2436
2437 if (mw->menu.popped_up)
2438 {
2439
2440 mw->core.width = mw->menu.windows [0].width;
2441 mw->core.height = mw->menu.windows [0].height;
2442 mw->core.parent->core.width = mw->core.width;
2443 mw->core.parent->core.height = mw->core.height;
2444 }
2445 else
2446 {
2447 mw->menu.windows [0].width = mw->core.width;
2448 mw->menu.windows [0].height = mw->core.height;
2449 create_pixmap_for_menu (&mw->menu.windows [0], mw);
2450 }
2451 }
2452
2453
2454 static void
2455 handle_single_motion_event (XlwMenuWidget mw, XMotionEvent *ev)
2456 {
2457 widget_value* val;
2458 int level;
2459
2460 if (!map_event_to_widget_value (mw, ev, &val, &level))
2461 pop_new_stack_if_no_contents (mw);
2462 else
2463 set_new_state (mw, val, level);
2464 remap_menubar (mw);
2465
2466
2467 XSync (XtDisplay (mw), False);
2468 }
2469
2470 static void
2471 handle_motion_event (XlwMenuWidget mw, XMotionEvent *ev)
2472 {
2473 int x = ev->x_root;
2474 int y = ev->y_root;
2475 int state = ev->state;
2476 XMotionEvent oldev = *ev;
2477
2478
2479 if (ev->is_hint
2480 && XQueryPointer (XtDisplay (mw), ev->window,
2481 &ev->root, &ev->subwindow,
2482 &ev->x_root, &ev->y_root,
2483 &ev->x, &ev->y,
2484 &ev->state)
2485 && ev->state == state
2486 && (ev->x_root != x || ev->y_root != y))
2487 handle_single_motion_event (mw, ev);
2488 else
2489 handle_single_motion_event (mw, &oldev);
2490 }
2491
2492 static void
2493 Start (Widget w, XEvent *ev, String *params, Cardinal *num_params)
2494 {
2495 XlwMenuWidget mw = (XlwMenuWidget)w;
2496
2497 if (!mw->menu.popped_up)
2498 {
2499 menu_post_event = *ev;
2500
2501
2502
2503 if (menu_post_event.xbutton.time == CurrentTime)
2504 menu_post_event.xbutton.time
2505 = XtLastTimestampProcessed (XtDisplay (w));
2506
2507 pop_up_menu (mw, (XButtonPressedEvent*) ev);
2508 }
2509 else
2510 {
2511
2512
2513 next_release_must_exit = 1;
2514
2515
2516 mw->menu.windows [0].x = ev->xmotion.x_root - ev->xmotion.x;
2517 mw->menu.windows [0].y = ev->xmotion.y_root - ev->xmotion.y;
2518
2519
2520 ev->xmotion.is_hint = 0;
2521 handle_motion_event (mw, &ev->xmotion);
2522 }
2523 }
2524
2525 static void
2526 Drag (Widget w, XEvent *ev, String *params, Cardinal *num_params)
2527 {
2528 XlwMenuWidget mw = (XlwMenuWidget)w;
2529 if (mw->menu.popped_up)
2530 handle_motion_event (mw, &ev->xmotion);
2531 }
2532
2533
2534
2535 static void
2536 Nothing (Widget w, XEvent *ev, String *params, Cardinal *num_params)
2537 {
2538 }
2539
2540 static widget_value *
2541 find_first_selectable (XlwMenuWidget mw, widget_value *item, int skip_titles)
2542 {
2543 widget_value *current = item;
2544 enum menu_separator separator;
2545
2546 while (lw_separator_p (current->name, &separator, 0) || !current->enabled
2547 || (skip_titles && !current->call_data && !current->contents))
2548 if (current->next)
2549 current = current->next;
2550 else
2551 return NULL;
2552
2553 return current;
2554 }
2555
2556 static widget_value *
2557 find_next_selectable (XlwMenuWidget mw, widget_value *item, int skip_titles)
2558 {
2559 widget_value *current = item;
2560 enum menu_separator separator;
2561
2562 while (current->next && (current = current->next)
2563 && (lw_separator_p (current->name, &separator, 0) || !current->enabled
2564 || (skip_titles && !current->call_data && !current->contents)))
2565 ;
2566
2567 if (current == item)
2568 {
2569 if (mw->menu.old_depth < 2)
2570 return current;
2571 current = mw->menu.old_stack [mw->menu.old_depth - 2]->contents;
2572
2573 while (lw_separator_p (current->name, &separator, 0)
2574 || !current->enabled
2575 || (skip_titles && !current->call_data
2576 && !current->contents))
2577 {
2578 if (current->next)
2579 current = current->next;
2580
2581 if (current == item)
2582 break;
2583 }
2584
2585 }
2586
2587 return current;
2588 }
2589
2590 static widget_value *
2591 find_prev_selectable (XlwMenuWidget mw, widget_value *item, int skip_titles)
2592 {
2593 widget_value *current = item;
2594 widget_value *prev = item;
2595
2596 while ((current = find_next_selectable (mw, current, skip_titles))
2597 != item)
2598 {
2599 if (prev == current)
2600 break;
2601 prev = current;
2602 }
2603
2604 return prev;
2605 }
2606
2607 static void
2608 Down (Widget w, XEvent *ev, String *params, Cardinal *num_params)
2609 {
2610 XlwMenuWidget mw = (XlwMenuWidget) w;
2611 widget_value* selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];
2612 int popup_menu_p = mw->menu.top_depth == 1;
2613
2614
2615 if (mw->menu.old_depth == mw->menu.top_depth)
2616
2617
2618
2619 set_new_state (mw,
2620 find_first_selectable (mw,
2621 selected_item->contents,
2622 popup_menu_p),
2623 mw->menu.old_depth);
2624 else
2625
2626 set_new_state (mw, find_next_selectable (mw, selected_item, popup_menu_p),
2627 mw->menu.old_depth - 1);
2628
2629 remap_menubar (mw);
2630 }
2631
2632 static void
2633 Up (Widget w, XEvent *ev, String *params, Cardinal *num_params)
2634 {
2635 XlwMenuWidget mw = (XlwMenuWidget) w;
2636 widget_value* selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];
2637 int popup_menu_p = mw->menu.top_depth == 1;
2638
2639
2640 if (mw->menu.old_depth == mw->menu.top_depth)
2641 {
2642
2643
2644
2645
2646
2647 set_new_state (mw,
2648 find_first_selectable (mw,
2649 selected_item->contents,
2650 popup_menu_p),
2651 mw->menu.old_depth);
2652 remap_menubar (mw);
2653 selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];
2654 set_new_state (mw,
2655 find_prev_selectable (mw,
2656 selected_item,
2657 popup_menu_p),
2658 mw->menu.old_depth - 1);
2659 }
2660 else
2661
2662 set_new_state (mw, find_prev_selectable (mw, selected_item, popup_menu_p),
2663 mw->menu.old_depth - 1);
2664
2665 remap_menubar (mw);
2666 }
2667
2668 void
2669 Left (Widget w, XEvent *ev, String *params, Cardinal *num_params)
2670 {
2671 XlwMenuWidget mw = (XlwMenuWidget) w;
2672 widget_value* selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];
2673
2674
2675 if (mw->menu.old_depth == mw->menu.top_depth)
2676
2677
2678
2679 set_new_state (mw, find_prev_selectable (mw, selected_item, 0),
2680 mw->menu.old_depth - 1);
2681 else if (mw->menu.old_depth == 1
2682 && selected_item->contents)
2683 {
2684 set_new_state (mw, selected_item->contents, mw->menu.old_depth);
2685 remap_menubar (mw);
2686 selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];
2687 if (!selected_item->enabled && find_first_selectable (mw,
2688 selected_item,
2689 0))
2690 set_new_state (mw, find_first_selectable (mw, selected_item, 0),
2691 mw->menu.old_depth - 1);
2692 }
2693
2694 else
2695 {
2696 pop_new_stack_if_no_contents (mw);
2697 set_new_state (mw, mw->menu.old_stack [mw->menu.old_depth - 2],
2698 mw->menu.old_depth - 2);
2699 }
2700
2701 remap_menubar (mw);
2702 }
2703
2704 void
2705 Right (Widget w, XEvent *ev, String *params, Cardinal *num_params)
2706 {
2707 XlwMenuWidget mw = (XlwMenuWidget) w;
2708 widget_value* selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];
2709
2710
2711 if (mw->menu.old_depth == mw->menu.top_depth)
2712
2713
2714
2715 set_new_state (mw, find_next_selectable (mw, selected_item, 0),
2716 mw->menu.old_depth - 1);
2717 else if (selected_item->contents)
2718 {
2719 set_new_state (mw, selected_item->contents, mw->menu.old_depth);
2720 remap_menubar (mw);
2721 selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];
2722 if (!selected_item->enabled && find_first_selectable (mw,
2723 selected_item,
2724 0))
2725 set_new_state (mw, find_first_selectable (mw, selected_item, 0),
2726 mw->menu.old_depth - 1);
2727 }
2728 else
2729 {
2730 pop_new_stack_if_no_contents (mw);
2731 set_new_state (mw, mw->menu.old_stack [mw->menu.old_depth - 2],
2732 mw->menu.old_depth - 2);
2733 }
2734
2735 remap_menubar (mw);
2736 }
2737
2738
2739
2740 static void
2741 Key (Widget w, XEvent *ev, String *params, Cardinal *num_params)
2742 {
2743 XlwMenuWidget mw = (XlwMenuWidget)w;
2744
2745
2746 mw->menu.new_depth = 1;
2747 remap_menubar (mw);
2748
2749 if (mw->menu.popped_up)
2750 {
2751 mw->menu.popped_up = False;
2752 ungrab_all ((Widget)mw, ev->xmotion.time);
2753 if (XtIsShell (XtParent ((Widget) mw)))
2754 XtPopdown (XtParent ((Widget) mw));
2755 else
2756 {
2757 XtRemoveGrab ((Widget) mw);
2758 display_menu (mw, 0, False, NULL, NULL, NULL);
2759 }
2760 }
2761
2762
2763 XtCallCallbackList ((Widget)mw, mw->menu.select, (XtPointer)0);
2764 }
2765
2766 static void
2767 Select (Widget w, XEvent *ev, String *params, Cardinal *num_params)
2768 {
2769 XlwMenuWidget mw = (XlwMenuWidget)w;
2770 widget_value* selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];
2771
2772
2773
2774
2775 if ((selected_item == 0
2776 || ((widget_value *) selected_item)->call_data == 0)
2777 && !next_release_must_exit
2778 && (ev->xbutton.time - menu_post_event.xbutton.time
2779 < XtGetMultiClickTime (XtDisplay (w))))
2780 return;
2781
2782
2783 mw->menu.new_depth = 1;
2784 remap_menubar (mw);
2785
2786 if (mw->menu.popped_up)
2787 {
2788 mw->menu.popped_up = False;
2789 ungrab_all ((Widget)mw, ev->xmotion.time);
2790 if (XtIsShell (XtParent ((Widget) mw)))
2791 XtPopdown (XtParent ((Widget) mw));
2792 else
2793 {
2794 XtRemoveGrab ((Widget) mw);
2795 display_menu (mw, 0, False, NULL, NULL, NULL);
2796 }
2797 }
2798
2799
2800 XtCallCallbackList ((Widget)mw, mw->menu.select, (XtPointer)selected_item);
2801 }
2802
2803
2804
2805 static void
2806 pop_up_menu (XlwMenuWidget mw, XButtonPressedEvent *event)
2807 {
2808 int x = event->x_root;
2809 int y = event->y_root;
2810 int w;
2811 int h;
2812 int borderwidth = mw->menu.shadow_thickness;
2813 Screen* screen = XtScreen (mw);
2814 Display *display = XtDisplay (mw);
2815 int screen_x;
2816 int screen_y;
2817 int screen_w;
2818 int screen_h;
2819
2820 #ifdef emacs
2821 xlw_monitor_dimensions_at_pos (display, screen, x, y,
2822 &screen_x, &screen_y,
2823 &screen_w, &screen_h);
2824 #else
2825 screen_x = 0;
2826 screen_y = 0;
2827 screen_w = WidthOfScreen (screen);
2828 screen_h = HeightOfScreen (screen);
2829 #endif
2830 next_release_must_exit = 0;
2831
2832 mw->menu.inside_entry = NULL;
2833 XtCallCallbackList ((Widget)mw, mw->menu.open, NULL);
2834
2835 if (XtIsShell (XtParent ((Widget)mw)))
2836 size_menu (mw, 0);
2837
2838 w = mw->menu.windows [0].width;
2839 h = mw->menu.windows [0].height;
2840
2841 x -= borderwidth;
2842 y -= borderwidth;
2843 if (x < screen_x + borderwidth)
2844 x = screen_x + borderwidth;
2845 if (x + w + 2 * borderwidth > screen_x + screen_w)
2846 x = (screen_x + screen_w) - w - 2 * borderwidth;
2847 if (y < screen_y + borderwidth)
2848 y = screen_y + borderwidth;
2849 if (y + h + 2 * borderwidth > screen_y + screen_h)
2850 y = (screen_y + screen_h) - h - 2 * borderwidth;
2851
2852 mw->menu.popped_up = True;
2853 if (XtIsShell (XtParent ((Widget)mw)))
2854 {
2855
2856 XtConfigureWidget (XtParent ((Widget)mw), x, y, w, h,
2857 XtParent ((Widget)mw)->core.border_width);
2858 XtPopup (XtParent ((Widget)mw), XtGrabExclusive);
2859 display_menu (mw, 0, False, NULL, NULL, NULL);
2860 mw->menu.windows [0].x = x + borderwidth;
2861 mw->menu.windows [0].y = y + borderwidth;
2862 mw->menu.top_depth = 1;
2863 }
2864 else
2865 {
2866 XEvent *ev = (XEvent *) event;
2867
2868 XtAddGrab ((Widget) mw, True, True);
2869
2870
2871 mw->menu.windows [0].x = ev->xmotion.x_root - ev->xmotion.x;
2872 mw->menu.windows [0].y = ev->xmotion.y_root - ev->xmotion.y;
2873 mw->menu.top_depth = 2;
2874 }
2875
2876 #ifdef emacs
2877 x_catch_errors (display);
2878 #endif
2879 if (XtGrabPointer ((Widget)mw, False,
2880 (PointerMotionMask
2881 | PointerMotionHintMask
2882 | ButtonReleaseMask
2883 | ButtonPressMask),
2884 GrabModeAsync, GrabModeAsync, None,
2885 mw->menu.cursor_shape,
2886 event->time) == Success)
2887 {
2888 if (true
2889 #ifdef emacs
2890 && lucid__menu_grab_keyboard
2891 #endif
2892 && XtGrabKeyboard ((Widget) mw, False, GrabModeAsync,
2893 GrabModeAsync, event->time) == Success)
2894 {
2895 XtSetKeyboardFocus ((Widget) mw, None);
2896 pointer_grabbed = 1;
2897 keyboard_grabbed = 1;
2898 }
2899 else
2900 {
2901 XtUngrabPointer ((Widget) mw, event->time);
2902 keyboard_grabbed = 0;
2903 }
2904 }
2905
2906 #ifdef emacs
2907 if (x_had_errors_p (display))
2908 {
2909 pointer_grabbed = 0;
2910 XtUngrabPointer ((Widget)mw, event->time);
2911 }
2912 x_uncatch_errors ();
2913 #endif
2914
2915 ((XMotionEvent*)event)->is_hint = 0;
2916 handle_motion_event (mw, (XMotionEvent*)event);
2917
2918 XlwMenuRedisplay ((Widget) mw, NULL, None);
2919 }