Use GETLOADAVG_LIBS.
[emacs.git] / lwlib / xlwmenu.c
blob3a1a490710ba83e0a23610df741ff1787d5fe8b3
1 /* Implements a lightweight menubar widget.
2 Copyright (C) 1992 Lucid, Inc.
4 This file is part of the Lucid Widget Library.
6 The Lucid Widget Library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
11 The Lucid Widget Library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Emacs; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 Boston, MA 02111-1307, USA. */
21 /* Created by devin@lucid.com */
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
27 #include <stdio.h>
29 #include <sys/types.h>
30 #include <X11/Xos.h>
31 #include <X11/IntrinsicP.h>
32 #include <X11/ObjectP.h>
33 #include <X11/StringDefs.h>
34 #include <X11/cursorfont.h>
35 #include <X11/bitmaps/gray>
36 #include "xlwmenuP.h"
38 #ifdef emacs
39 /* Defined in xterm.c. */
40 extern int x_alloc_nearest_color_for_widget __P ((Widget, Colormap, XColor*));
41 extern int x_catch_errors __P ((Display*));
42 extern int x_uncatch_errors __P ((Display*, int));
43 extern int x_had_errors_p __P ((Display*));
44 extern int x_clear_errors __P ((Display*));
45 extern unsigned long x_copy_dpy_color __P ((Display *, Colormap,
46 unsigned long));
48 /* Defined in xfaces.c. */
49 extern void x_free_dpy_colors __P ((Display *, Screen *, Colormap,
50 unsigned long *pixels, int npixels));
51 #endif
53 static int pointer_grabbed;
54 static XEvent menu_post_event;
56 XFontStruct *xlwmenu_default_font;
58 static char
59 xlwMenuTranslations [] =
60 "<BtnDown>: start()\n\
61 <Motion>: drag()\n\
62 <BtnUp>: select()\n\
63 <Key>Shift_L: nothing()\n\
64 <Key>Shift_R: nothing()\n\
65 <Key>Meta_L: nothing()\n\
66 <Key>Meta_R: nothing()\n\
67 <Key>Control_L: nothing()\n\
68 <Key>Control_R: nothing()\n\
69 <Key>Hyper_L: nothing()\n\
70 <Key>Hyper_R: nothing()\n\
71 <Key>Super_L: nothing()\n\
72 <Key>Super_R: nothing()\n\
73 <Key>Alt_L: nothing()\n\
74 <Key>Alt_R: nothing()\n\
75 <Key>Caps_Lock: nothing()\n\
76 <Key>Shift_Lock: nothing()\n\
77 <KeyUp>Shift_L: nothing()\n\
78 <KeyUp>Shift_R: nothing()\n\
79 <KeyUp>Meta_L: nothing()\n\
80 <KeyUp>Meta_R: nothing()\n\
81 <KeyUp>Control_L: nothing()\n\
82 <KeyUp>Control_R: nothing()\n\
83 <KeyUp>Hyper_L: nothing()\n\
84 <KeyUp>Hyper_R: nothing()\n\
85 <KeyUp>Super_L: nothing()\n\
86 <KeyUp>Super_R: nothing()\n\
87 <KeyUp>Alt_L: nothing()\n\
88 <KeyUp>Alt_R: nothing()\n\
89 <KeyUp>Caps_Lock: nothing()\n\
90 <KeyUp>Shift_Lock:nothing()\n\
91 <Key>: key()\n\
92 <KeyUp>: key()\n\
95 #define offset(field) XtOffset(XlwMenuWidget, field)
96 static XtResource
97 xlwMenuResources[] =
99 {XtNfont, XtCFont, XtRFontStruct, sizeof(XFontStruct *),
100 offset(menu.font),XtRString, "XtDefaultFont"},
101 {XtNforeground, XtCForeground, XtRPixel, sizeof(Pixel),
102 offset(menu.foreground), XtRString, "XtDefaultForeground"},
103 {XtNbuttonForeground, XtCButtonForeground, XtRPixel, sizeof(Pixel),
104 offset(menu.button_foreground), XtRString, "XtDefaultForeground"},
105 {XtNmargin, XtCMargin, XtRDimension, sizeof(Dimension),
106 offset(menu.margin), XtRImmediate, (XtPointer) 4},
107 {XtNhorizontalSpacing, XtCMargin, XtRDimension, sizeof(Dimension),
108 offset(menu.horizontal_spacing), XtRImmediate, (XtPointer)3},
109 {XtNverticalSpacing, XtCMargin, XtRDimension, sizeof(Dimension),
110 offset(menu.vertical_spacing), XtRImmediate, (XtPointer)1},
111 {XtNarrowSpacing, XtCMargin, XtRDimension, sizeof(Dimension),
112 offset(menu.arrow_spacing), XtRImmediate, (XtPointer)10},
114 {XmNshadowThickness, XmCShadowThickness, XtRDimension,
115 sizeof (Dimension), offset (menu.shadow_thickness),
116 XtRImmediate, (XtPointer) 2},
117 {XmNtopShadowColor, XmCTopShadowColor, XtRPixel, sizeof (Pixel),
118 offset (menu.top_shadow_color), XtRImmediate, (XtPointer)-1},
119 {XmNbottomShadowColor, XmCBottomShadowColor, XtRPixel, sizeof (Pixel),
120 offset (menu.bottom_shadow_color), XtRImmediate, (XtPointer)-1},
121 {XmNtopShadowPixmap, XmCTopShadowPixmap, XtRPixmap, sizeof (Pixmap),
122 offset (menu.top_shadow_pixmap), XtRImmediate, (XtPointer)None},
123 {XmNbottomShadowPixmap, XmCBottomShadowPixmap, XtRPixmap, sizeof (Pixmap),
124 offset (menu.bottom_shadow_pixmap), XtRImmediate, (XtPointer)None},
126 {XtNopen, XtCCallback, XtRCallback, sizeof(XtPointer),
127 offset(menu.open), XtRCallback, (XtPointer)NULL},
128 {XtNselect, XtCCallback, XtRCallback, sizeof(XtPointer),
129 offset(menu.select), XtRCallback, (XtPointer)NULL},
130 {XtNhighlightCallback, XtCCallback, XtRCallback, sizeof(XtPointer),
131 offset(menu.highlight), XtRCallback, (XtPointer)NULL},
132 {XtNmenu, XtCMenu, XtRPointer, sizeof(XtPointer),
133 offset(menu.contents), XtRImmediate, (XtPointer)NULL},
134 {XtNcursor, XtCCursor, XtRCursor, sizeof(Cursor),
135 offset(menu.cursor_shape), XtRString, (XtPointer)"right_ptr"},
136 {XtNhorizontal, XtCHorizontal, XtRInt, sizeof(int),
137 offset(menu.horizontal), XtRImmediate, (XtPointer)True},
139 #undef offset
141 static Boolean XlwMenuSetValues();
142 static void XlwMenuRealize();
143 static void XlwMenuRedisplay();
144 static void XlwMenuResize();
145 static void XlwMenuInitialize();
146 static void XlwMenuRedisplay();
147 static void XlwMenuDestroy();
148 static void XlwMenuClassInitialize();
149 static void Start();
150 static void Drag();
151 static void Select();
152 static void Key();
153 static void Nothing();
154 static int separator_height ();
156 static XtActionsRec
157 xlwMenuActionsList [] =
159 {"start", Start},
160 {"drag", Drag},
161 {"select", Select},
162 {"key", Key},
163 {"nothing", Nothing},
166 #define SuperClass ((CoreWidgetClass)&coreClassRec)
168 XlwMenuClassRec xlwMenuClassRec =
170 { /* CoreClass fields initialization */
171 (WidgetClass) SuperClass, /* superclass */
172 "XlwMenu", /* class_name */
173 sizeof(XlwMenuRec), /* size */
174 XlwMenuClassInitialize, /* class_initialize */
175 NULL, /* class_part_initialize */
176 FALSE, /* class_inited */
177 XlwMenuInitialize, /* initialize */
178 NULL, /* initialize_hook */
179 XlwMenuRealize, /* realize */
180 xlwMenuActionsList, /* actions */
181 XtNumber(xlwMenuActionsList), /* num_actions */
182 xlwMenuResources, /* resources */
183 XtNumber(xlwMenuResources), /* resource_count */
184 NULLQUARK, /* xrm_class */
185 TRUE, /* compress_motion */
186 TRUE, /* compress_exposure */
187 TRUE, /* compress_enterleave */
188 FALSE, /* visible_interest */
189 XlwMenuDestroy, /* destroy */
190 XlwMenuResize, /* resize */
191 XlwMenuRedisplay, /* expose */
192 XlwMenuSetValues, /* set_values */
193 NULL, /* set_values_hook */
194 XtInheritSetValuesAlmost, /* set_values_almost */
195 NULL, /* get_values_hook */
196 NULL, /* accept_focus */
197 XtVersion, /* version */
198 NULL, /* callback_private */
199 xlwMenuTranslations, /* tm_table */
200 XtInheritQueryGeometry, /* query_geometry */
201 XtInheritDisplayAccelerator, /* display_accelerator */
202 NULL /* extension */
203 }, /* XlwMenuClass fields initialization */
205 0 /* dummy */
209 WidgetClass xlwMenuWidgetClass = (WidgetClass) &xlwMenuClassRec;
211 int submenu_destroyed;
213 static int next_release_must_exit;
215 \f/* Utilities */
218 /* Like abort, but remove grabs from widget W before. */
220 static void
221 abort_gracefully (w)
222 Widget w;
224 if (XtIsShell (XtParent (w)))
225 XtRemoveGrab (w);
226 XtUngrabPointer (w, CurrentTime);
227 abort ();
230 static void
231 push_new_stack (mw, val)
232 XlwMenuWidget mw;
233 widget_value* val;
235 if (!mw->menu.new_stack)
237 mw->menu.new_stack_length = 10;
238 mw->menu.new_stack =
239 (widget_value**)XtCalloc (mw->menu.new_stack_length,
240 sizeof (widget_value*));
242 else if (mw->menu.new_depth == mw->menu.new_stack_length)
244 mw->menu.new_stack_length *= 2;
245 mw->menu.new_stack =
246 (widget_value**)XtRealloc ((char*)mw->menu.new_stack,
247 mw->menu.new_stack_length * sizeof (widget_value*));
249 mw->menu.new_stack [mw->menu.new_depth++] = val;
252 static void
253 pop_new_stack_if_no_contents (mw)
254 XlwMenuWidget mw;
256 if (mw->menu.new_depth)
258 if (!mw->menu.new_stack [mw->menu.new_depth - 1]->contents)
259 mw->menu.new_depth -= 1;
263 static void
264 make_old_stack_space (mw, n)
265 XlwMenuWidget mw;
266 int n;
268 if (!mw->menu.old_stack)
270 mw->menu.old_stack_length = 10;
271 mw->menu.old_stack =
272 (widget_value**)XtCalloc (mw->menu.old_stack_length,
273 sizeof (widget_value*));
275 else if (mw->menu.old_stack_length < n)
277 mw->menu.old_stack_length *= 2;
278 mw->menu.old_stack =
279 (widget_value**)XtRealloc ((char*)mw->menu.old_stack,
280 mw->menu.old_stack_length * sizeof (widget_value*));
284 \f/* Size code */
286 string_width (mw, s)
287 XlwMenuWidget mw;
288 char *s;
290 XCharStruct xcs;
291 int drop;
293 XTextExtents (mw->menu.font, s, strlen (s), &drop, &drop, &drop, &xcs);
294 return xcs.width;
297 static int
298 arrow_width (mw)
299 XlwMenuWidget mw;
301 return (mw->menu.font->ascent * 3/4) | 1;
304 /* Return the width of toggle buttons of widget MW. */
306 static int
307 toggle_button_width (mw)
308 XlwMenuWidget mw;
310 return ((mw->menu.font->ascent + mw->menu.font->descent) * 2 / 3) | 1;
314 /* Return the width of radio buttons of widget MW. */
316 static int
317 radio_button_width (mw)
318 XlwMenuWidget mw;
320 return toggle_button_width (mw) * 1.41;
324 static XtResource
325 nameResource[] =
327 {"labelString", "LabelString", XtRString, sizeof(String),
328 0, XtRImmediate, 0},
331 static char*
332 resource_widget_value (mw, val)
333 XlwMenuWidget mw;
334 widget_value *val;
336 if (!val->toolkit_data)
338 char* resourced_name = NULL;
339 char* complete_name;
340 XtGetSubresources ((Widget) mw,
341 (XtPointer) &resourced_name,
342 val->name, val->name,
343 nameResource, 1, NULL, 0);
344 if (!resourced_name)
345 resourced_name = val->name;
346 if (!val->value)
348 complete_name = (char *) XtMalloc (strlen (resourced_name) + 1);
349 strcpy (complete_name, resourced_name);
351 else
353 int complete_length =
354 strlen (resourced_name) + strlen (val->value) + 2;
355 complete_name = XtMalloc (complete_length);
356 *complete_name = 0;
357 strcat (complete_name, resourced_name);
358 strcat (complete_name, " ");
359 strcat (complete_name, val->value);
362 val->toolkit_data = complete_name;
363 val->free_toolkit_data = True;
365 return (char*)val->toolkit_data;
368 /* Returns the sizes of an item */
369 static void
370 size_menu_item (mw, val, horizontal_p, label_width, rest_width, button_width,
371 height)
372 XlwMenuWidget mw;
373 widget_value* val;
374 int horizontal_p;
375 int* label_width;
376 int* rest_width;
377 int* button_width;
378 int* height;
380 enum menu_separator separator;
382 if (lw_separator_p (val->name, &separator, 0))
384 *height = separator_height (separator);
385 *label_width = 1;
386 *rest_width = 0;
387 *button_width = 0;
389 else
391 *height =
392 mw->menu.font->ascent + mw->menu.font->descent
393 + 2 * mw->menu.vertical_spacing + 2 * mw->menu.shadow_thickness;
395 *label_width =
396 string_width (mw, resource_widget_value (mw, val))
397 + mw->menu.horizontal_spacing + mw->menu.shadow_thickness;
399 *rest_width = mw->menu.horizontal_spacing + mw->menu.shadow_thickness;
400 if (!horizontal_p)
402 if (val->contents)
403 /* Add width of the arrow displayed for submenus. */
404 *rest_width += arrow_width (mw) + mw->menu.arrow_spacing;
405 else if (val->key)
406 /* Add width of key equivalent string. */
407 *rest_width += (string_width (mw, val->key)
408 + mw->menu.arrow_spacing);
410 if (val->button_type == BUTTON_TYPE_TOGGLE)
411 *button_width = (toggle_button_width (mw)
412 + mw->menu.horizontal_spacing);
413 else if (val->button_type == BUTTON_TYPE_RADIO)
414 *button_width = (radio_button_width (mw)
415 + mw->menu.horizontal_spacing);
420 static void
421 size_menu (mw, level)
422 XlwMenuWidget mw;
423 int level;
425 unsigned int label_width = 0;
426 int rest_width = 0;
427 int button_width = 0;
428 int max_rest_width = 0;
429 int max_button_width = 0;
430 unsigned int height = 0;
431 int horizontal_p = mw->menu.horizontal && (level == 0);
432 widget_value* val;
433 window_state* ws;
435 if (level >= mw->menu.old_depth)
436 abort_gracefully ((Widget) mw);
438 ws = &mw->menu.windows [level];
439 ws->width = 0;
440 ws->height = 0;
441 ws->label_width = 0;
442 ws->button_width = 0;
444 for (val = mw->menu.old_stack [level]->contents; val; val = val->next)
446 size_menu_item (mw, val, horizontal_p, &label_width, &rest_width,
447 &button_width, &height);
448 if (horizontal_p)
450 ws->width += label_width + rest_width;
451 if (height > ws->height)
452 ws->height = height;
454 else
456 if (label_width > ws->label_width)
457 ws->label_width = label_width;
458 if (rest_width > max_rest_width)
459 max_rest_width = rest_width;
460 if (button_width > max_button_width)
461 max_button_width = button_width;
462 ws->height += height;
466 if (horizontal_p)
467 ws->label_width = ws->button_width = 0;
468 else
470 ws->width = ws->label_width + max_rest_width + max_button_width;
471 ws->button_width = max_button_width;
474 ws->width += 2 * mw->menu.shadow_thickness;
475 ws->height += 2 * mw->menu.shadow_thickness;
477 if (horizontal_p)
479 ws->width += 2 * mw->menu.margin;
480 ws->height += 2 * mw->menu.margin;
485 \f/* Display code */
487 static void
488 draw_arrow (mw, window, gc, x, y, width, down_p)
489 XlwMenuWidget mw;
490 Window window;
491 GC gc;
492 int x;
493 int y;
494 int width;
495 int down_p;
497 Display *dpy = XtDisplay (mw);
498 GC top_gc = mw->menu.shadow_top_gc;
499 GC bottom_gc = mw->menu.shadow_bottom_gc;
500 int thickness = mw->menu.shadow_thickness;
501 int height = width;
502 XPoint pt[10];
503 /* alpha = atan (0.5)
504 factor = (1 + sin (alpha)) / cos (alpha) */
505 double factor = 1.62;
506 int thickness2 = thickness * factor;
508 y += (mw->menu.font->ascent + mw->menu.font->descent - height) / 2;
510 if (down_p)
512 GC temp;
513 temp = top_gc;
514 top_gc = bottom_gc;
515 bottom_gc = temp;
518 pt[0].x = x;
519 pt[0].y = y + height;
520 pt[1].x = x + thickness;
521 pt[1].y = y + height - thickness2;
522 pt[2].x = x + thickness2;
523 pt[2].y = y + thickness2;
524 pt[3].x = x;
525 pt[3].y = y;
526 XFillPolygon (dpy, window, top_gc, pt, 4, Convex, CoordModeOrigin);
528 pt[0].x = x;
529 pt[0].y = y;
530 pt[1].x = x + thickness;
531 pt[1].y = y + thickness2;
532 pt[2].x = x + width - thickness2;
533 pt[2].y = y + height / 2;
534 pt[3].x = x + width;
535 pt[3].y = y + height / 2;
536 XFillPolygon (dpy, window, top_gc, pt, 4, Convex, CoordModeOrigin);
538 pt[0].x = x;
539 pt[0].y = y + height;
540 pt[1].x = x + thickness;
541 pt[1].y = y + height - thickness2;
542 pt[2].x = x + width - thickness2;
543 pt[2].y = y + height / 2;
544 pt[3].x = x + width;
545 pt[3].y = y + height / 2;
546 XFillPolygon (dpy, window, bottom_gc, pt, 4, Convex, CoordModeOrigin);
551 static void
552 draw_shadow_rectangle (mw, window, x, y, width, height, erase_p, down_p)
553 XlwMenuWidget mw;
554 Window window;
555 int x;
556 int y;
557 int width;
558 int height;
559 int erase_p;
560 int down_p;
562 Display *dpy = XtDisplay (mw);
563 GC top_gc = !erase_p ? mw->menu.shadow_top_gc : mw->menu.background_gc;
564 GC bottom_gc = !erase_p ? mw->menu.shadow_bottom_gc : mw->menu.background_gc;
565 int thickness = mw->menu.shadow_thickness;
566 XPoint points [4];
568 if (!erase_p && down_p)
570 GC temp;
571 temp = top_gc;
572 top_gc = bottom_gc;
573 bottom_gc = temp;
576 points [0].x = x;
577 points [0].y = y;
578 points [1].x = x + width;
579 points [1].y = y;
580 points [2].x = x + width - thickness;
581 points [2].y = y + thickness;
582 points [3].x = x;
583 points [3].y = y + thickness;
584 XFillPolygon (dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
585 points [0].x = x;
586 points [0].y = y + thickness;
587 points [1].x = x;
588 points [1].y = y + height;
589 points [2].x = x + thickness;
590 points [2].y = y + height - thickness;
591 points [3].x = x + thickness;
592 points [3].y = y + thickness;
593 XFillPolygon (dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
594 points [0].x = x + width;
595 points [0].y = y;
596 points [1].x = x + width - thickness;
597 points [1].y = y + thickness;
598 points [2].x = x + width - thickness;
599 points [2].y = y + height - thickness;
600 points [3].x = x + width;
601 points [3].y = y + height - thickness;
602 XFillPolygon (dpy, window, bottom_gc, points, 4, Convex, CoordModeOrigin);
603 points [0].x = x;
604 points [0].y = y + height;
605 points [1].x = x + width;
606 points [1].y = y + height;
607 points [2].x = x + width;
608 points [2].y = y + height - thickness;
609 points [3].x = x + thickness;
610 points [3].y = y + height - thickness;
611 XFillPolygon (dpy, window, bottom_gc, points, 4, Convex, CoordModeOrigin);
615 static void
616 draw_shadow_rhombus (mw, window, x, y, width, height, erase_p, down_p)
617 XlwMenuWidget mw;
618 Window window;
619 int x;
620 int y;
621 int width;
622 int height;
623 int erase_p;
624 int down_p;
626 Display *dpy = XtDisplay (mw);
627 GC top_gc = !erase_p ? mw->menu.shadow_top_gc : mw->menu.background_gc;
628 GC bottom_gc = !erase_p ? mw->menu.shadow_bottom_gc : mw->menu.background_gc;
629 int thickness = mw->menu.shadow_thickness;
630 XPoint points [4];
632 if (!erase_p && down_p)
634 GC temp;
635 temp = top_gc;
636 top_gc = bottom_gc;
637 bottom_gc = temp;
640 points [0].x = x;
641 points [0].y = y + height / 2;
642 points [1].x = x + thickness;
643 points [1].y = y + height / 2;
644 points [2].x = x + width / 2;
645 points [2].y = y + thickness;
646 points [3].x = x + width / 2;
647 points [3].y = y;
648 XFillPolygon (dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
649 points [0].x = x + width / 2;
650 points [0].y = y;
651 points [1].x = x + width / 2;
652 points [1].y = y + thickness;
653 points [2].x = x + width - thickness;
654 points [2].y = y + height / 2;
655 points [3].x = x + width;
656 points [3].y = y + height / 2;
657 XFillPolygon (dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
658 points [0].x = x;
659 points [0].y = y + height / 2;
660 points [1].x = x + thickness;
661 points [1].y = y + height / 2;
662 points [2].x = x + width / 2;
663 points [2].y = y + height - thickness;
664 points [3].x = x + width / 2;
665 points [3].y = y + height;
666 XFillPolygon (dpy, window, bottom_gc, points, 4, Convex, CoordModeOrigin);
667 points [0].x = x + width / 2;
668 points [0].y = y + height;
669 points [1].x = x + width / 2;
670 points [1].y = y + height - thickness;
671 points [2].x = x + width - thickness;
672 points [2].y = y + height / 2;
673 points [3].x = x + width;
674 points [3].y = y + height / 2;
675 XFillPolygon (dpy, window, bottom_gc, points, 4, Convex, CoordModeOrigin);
679 /* Draw a toggle button on widget MW, X window WINDOW. X/Y is the
680 top-left corner of the menu item. SELECTED_P non-zero means the
681 toggle button is selected. */
683 static void
684 draw_toggle (mw, window, x, y, selected_p)
685 XlwMenuWidget mw;
686 Window window;
687 int x, y, selected_p;
689 int width, height;
691 width = toggle_button_width (mw);
692 height = width;
693 x += mw->menu.horizontal_spacing;
694 y += (mw->menu.font->ascent - height) / 2;
695 draw_shadow_rectangle (mw, window, x, y, width, height, False, selected_p);
699 /* Draw a radio button on widget MW, X window WINDOW. X/Y is the
700 top-left corner of the menu item. SELECTED_P non-zero means the
701 toggle button is selected. */
703 static void
704 draw_radio (mw, window, x, y, selected_p)
705 XlwMenuWidget mw;
706 Window window;
707 int x, y, selected_p;
709 int width, height;
711 width = radio_button_width (mw);
712 height = width;
713 x += mw->menu.horizontal_spacing;
714 y += (mw->menu.font->ascent - height) / 2;
715 draw_shadow_rhombus (mw, window, x, y, width, height, False, selected_p);
719 /* Draw a menu separator on widget MW, X window WINDOW. X/Y is the
720 top-left corner of the menu item. WIDTH is the width of the
721 separator to draw. TYPE is the separator type. */
723 static void
724 draw_separator (mw, window, x, y, width, type)
725 XlwMenuWidget mw;
726 Window window;
727 int x, y, width;
728 enum menu_separator type;
730 Display *dpy = XtDisplay (mw);
731 XGCValues xgcv;
733 switch (type)
735 case SEPARATOR_NO_LINE:
736 break;
738 case SEPARATOR_SINGLE_LINE:
739 XDrawLine (dpy, window, mw->menu.foreground_gc,
740 x, y, x + width, y);
741 break;
743 case SEPARATOR_DOUBLE_LINE:
744 draw_separator (mw, window, x, y, width, SEPARATOR_SINGLE_LINE);
745 draw_separator (mw, window, x, y + 2, width, SEPARATOR_SINGLE_LINE);
746 break;
748 case SEPARATOR_SINGLE_DASHED_LINE:
749 xgcv.line_style = LineOnOffDash;
750 XChangeGC (dpy, mw->menu.foreground_gc, GCLineStyle, &xgcv);
751 XDrawLine (dpy, window, mw->menu.foreground_gc,
752 x, y, x + width, y);
753 xgcv.line_style = LineSolid;
754 XChangeGC (dpy, mw->menu.foreground_gc, GCLineStyle, &xgcv);
755 break;
757 case SEPARATOR_DOUBLE_DASHED_LINE:
758 draw_separator (mw, window, x, y, width,
759 SEPARATOR_SINGLE_DASHED_LINE);
760 draw_separator (mw, window, x, y + 2, width,
761 SEPARATOR_SINGLE_DASHED_LINE);
762 break;
764 case SEPARATOR_SHADOW_ETCHED_IN:
765 XDrawLine (dpy, window, mw->menu.shadow_bottom_gc,
766 x, y, x + width, y);
767 XDrawLine (dpy, window, mw->menu.shadow_top_gc,
768 x, y + 1, x + width, y + 1);
769 break;
771 case SEPARATOR_SHADOW_ETCHED_OUT:
772 XDrawLine (dpy, window, mw->menu.shadow_top_gc,
773 x, y, x + width, y);
774 XDrawLine (dpy, window, mw->menu.shadow_bottom_gc,
775 x, y + 1, x + width, y + 1);
776 break;
778 case SEPARATOR_SHADOW_ETCHED_IN_DASH:
779 xgcv.line_style = LineOnOffDash;
780 XChangeGC (dpy, mw->menu.shadow_bottom_gc, GCLineStyle, &xgcv);
781 XChangeGC (dpy, mw->menu.shadow_top_gc, GCLineStyle, &xgcv);
782 draw_separator (mw, window, x, y, SEPARATOR_SHADOW_ETCHED_IN);
783 xgcv.line_style = LineSolid;
784 XChangeGC (dpy, mw->menu.shadow_bottom_gc, GCLineStyle, &xgcv);
785 XChangeGC (dpy, mw->menu.shadow_top_gc, GCLineStyle, &xgcv);
786 break;
788 case SEPARATOR_SHADOW_ETCHED_OUT_DASH:
789 xgcv.line_style = LineOnOffDash;
790 XChangeGC (dpy, mw->menu.shadow_bottom_gc, GCLineStyle, &xgcv);
791 XChangeGC (dpy, mw->menu.shadow_top_gc, GCLineStyle, &xgcv);
792 draw_separator (mw, window, x, y, SEPARATOR_SHADOW_ETCHED_OUT);
793 xgcv.line_style = LineSolid;
794 XChangeGC (dpy, mw->menu.shadow_bottom_gc, GCLineStyle, &xgcv);
795 XChangeGC (dpy, mw->menu.shadow_top_gc, GCLineStyle, &xgcv);
796 break;
798 case SEPARATOR_SHADOW_DOUBLE_ETCHED_IN:
799 draw_separator (mw, window, x, y, width, SEPARATOR_SHADOW_ETCHED_IN);
800 draw_separator (mw, window, x, y + 3, width, SEPARATOR_SHADOW_ETCHED_IN);
801 break;
803 case SEPARATOR_SHADOW_DOUBLE_ETCHED_OUT:
804 draw_separator (mw, window, x, y, width,
805 SEPARATOR_SHADOW_ETCHED_OUT);
806 draw_separator (mw, window, x, y + 3, width,
807 SEPARATOR_SHADOW_ETCHED_OUT);
808 break;
810 case SEPARATOR_SHADOW_DOUBLE_ETCHED_IN_DASH:
811 xgcv.line_style = LineOnOffDash;
812 XChangeGC (dpy, mw->menu.shadow_bottom_gc, GCLineStyle, &xgcv);
813 XChangeGC (dpy, mw->menu.shadow_top_gc, GCLineStyle, &xgcv);
814 draw_separator (mw, window, x, y, width,
815 SEPARATOR_SHADOW_DOUBLE_ETCHED_IN);
816 xgcv.line_style = LineSolid;
817 XChangeGC (dpy, mw->menu.shadow_bottom_gc, GCLineStyle, &xgcv);
818 XChangeGC (dpy, mw->menu.shadow_top_gc, GCLineStyle, &xgcv);
819 break;
821 case SEPARATOR_SHADOW_DOUBLE_ETCHED_OUT_DASH:
822 xgcv.line_style = LineOnOffDash;
823 XChangeGC (dpy, mw->menu.shadow_bottom_gc, GCLineStyle, &xgcv);
824 XChangeGC (dpy, mw->menu.shadow_top_gc, GCLineStyle, &xgcv);
825 draw_separator (mw, window, x, y, width,
826 SEPARATOR_SHADOW_DOUBLE_ETCHED_OUT);
827 xgcv.line_style = LineSolid;
828 XChangeGC (dpy, mw->menu.shadow_bottom_gc, GCLineStyle, &xgcv);
829 XChangeGC (dpy, mw->menu.shadow_top_gc, GCLineStyle, &xgcv);
830 break;
832 default:
833 abort ();
838 /* Return the pixel height of menu separator SEPARATOR. */
840 static int
841 separator_height (separator)
842 enum menu_separator separator;
844 switch (separator)
846 case SEPARATOR_NO_LINE:
847 return 2;
849 case SEPARATOR_SINGLE_LINE:
850 case SEPARATOR_SINGLE_DASHED_LINE:
851 return 1;
853 case SEPARATOR_DOUBLE_LINE:
854 case SEPARATOR_DOUBLE_DASHED_LINE:
855 return 3;
857 case SEPARATOR_SHADOW_ETCHED_IN:
858 case SEPARATOR_SHADOW_ETCHED_OUT:
859 case SEPARATOR_SHADOW_ETCHED_IN_DASH:
860 case SEPARATOR_SHADOW_ETCHED_OUT_DASH:
861 return 2;
863 case SEPARATOR_SHADOW_DOUBLE_ETCHED_IN:
864 case SEPARATOR_SHADOW_DOUBLE_ETCHED_OUT:
865 case SEPARATOR_SHADOW_DOUBLE_ETCHED_IN_DASH:
866 case SEPARATOR_SHADOW_DOUBLE_ETCHED_OUT_DASH:
867 return 5;
869 default:
870 abort ();
875 /* Display the menu item and increment where.x and where.y to show how large
876 the menu item was. */
878 static void
879 display_menu_item (mw, val, ws, where, highlighted_p, horizontal_p,
880 just_compute_p)
881 XlwMenuWidget mw;
882 widget_value* val;
883 window_state* ws;
884 XPoint* where;
885 Boolean highlighted_p;
886 Boolean horizontal_p;
887 Boolean just_compute_p;
889 GC deco_gc;
890 GC text_gc;
891 int font_ascent = mw->menu.font->ascent;
892 int font_descent = mw->menu.font->descent;
893 int shadow = mw->menu.shadow_thickness;
894 int margin = mw->menu.margin;
895 int h_spacing = mw->menu.horizontal_spacing;
896 int v_spacing = mw->menu.vertical_spacing;
897 int label_width;
898 int rest_width;
899 int button_width;
900 int height;
901 int width;
902 enum menu_separator separator;
903 int separator_p = lw_separator_p (val->name, &separator, 0);
905 /* compute the sizes of the item */
906 size_menu_item (mw, val, horizontal_p, &label_width, &rest_width,
907 &button_width, &height);
909 if (horizontal_p)
910 width = label_width + rest_width;
911 else
913 label_width = ws->label_width;
914 width = ws->width - 2 * shadow;
917 /* Only highlight an enabled item that has a callback. */
918 if (highlighted_p)
919 if (!val->enabled || !(val->call_data || val->contents))
920 highlighted_p = 0;
922 /* do the drawing. */
923 if (!just_compute_p)
925 /* Add the shadow border of the containing menu */
926 int x = where->x + shadow;
927 int y = where->y + shadow;
929 if (horizontal_p)
931 x += margin;
932 y += margin;
935 /* pick the foreground and background GC. */
936 if (val->enabled)
937 text_gc = mw->menu.foreground_gc;
938 else
939 text_gc = mw->menu.inactive_gc;
940 deco_gc = mw->menu.foreground_gc;
942 if (separator_p)
944 draw_separator (mw, ws->window, x, y, width, separator);
946 else
948 int x_offset = x + h_spacing + shadow;
949 char* display_string = resource_widget_value (mw, val);
950 draw_shadow_rectangle (mw, ws->window, x, y, width, height, True,
951 False);
953 /* Deal with centering a menu title. */
954 if (!horizontal_p && !val->contents && !val->call_data)
956 int l = string_width (mw, display_string);
958 if (width > l)
959 x_offset = (width - l) >> 1;
961 else if (!horizontal_p && ws->button_width)
962 x_offset += ws->button_width;
965 XDrawString (XtDisplay (mw), ws->window, text_gc, x_offset,
966 y + v_spacing + shadow + font_ascent,
967 display_string, strlen (display_string));
969 if (!horizontal_p)
971 if (val->button_type == BUTTON_TYPE_TOGGLE)
972 draw_toggle (mw, ws->window, x, y + v_spacing + shadow,
973 val->selected);
974 else if (val->button_type == BUTTON_TYPE_RADIO)
975 draw_radio (mw, ws->window, x, y + v_spacing + shadow,
976 val->selected);
978 if (val->contents)
980 int a_w = arrow_width (mw);
981 draw_arrow (mw, ws->window, deco_gc,
982 x + width - a_w
983 - mw->menu.horizontal_spacing
984 - mw->menu.shadow_thickness,
985 y + v_spacing + shadow, a_w,
986 highlighted_p);
988 else if (val->key)
990 XDrawString (XtDisplay (mw), ws->window, text_gc,
991 x + label_width + mw->menu.arrow_spacing,
992 y + v_spacing + shadow + font_ascent,
993 val->key, strlen (val->key));
996 else
998 XDrawRectangle (XtDisplay (mw), ws->window,
999 mw->menu.background_gc,
1000 x + shadow, y + shadow,
1001 label_width + h_spacing - 1,
1002 font_ascent + font_descent + 2 * v_spacing - 1);
1003 draw_shadow_rectangle (mw, ws->window, x, y, width, height,
1004 True, False);
1007 if (highlighted_p)
1008 draw_shadow_rectangle (mw, ws->window, x, y, width, height, False,
1009 False);
1013 where->x += width;
1014 where->y += height;
1017 static void
1018 display_menu (mw, level, just_compute_p, highlighted_pos, hit, hit_return,
1019 this, that)
1020 XlwMenuWidget mw;
1021 int level;
1022 Boolean just_compute_p;
1023 XPoint* highlighted_pos;
1024 XPoint* hit;
1025 widget_value** hit_return;
1026 widget_value* this;
1027 widget_value* that;
1029 widget_value* val;
1030 widget_value* following_item;
1031 window_state* ws;
1032 XPoint where;
1033 int horizontal_p = mw->menu.horizontal && (level == 0);
1034 int highlighted_p;
1035 int just_compute_this_one_p;
1036 /* This is set nonzero if the element containing HIGHLIGHTED_POS
1037 is disabled, so that we do not return any subsequent element either. */
1038 int no_return = 0;
1039 enum menu_separator separator;
1041 if (level >= mw->menu.old_depth)
1042 abort_gracefully ((Widget) mw);
1044 if (level < mw->menu.old_depth - 1)
1045 following_item = mw->menu.old_stack [level + 1];
1046 else
1047 following_item = NULL;
1049 if (hit)
1050 *hit_return = NULL;
1052 where.x = 0;
1053 where.y = 0;
1055 ws = &mw->menu.windows [level];
1056 for (val = mw->menu.old_stack [level]->contents; val; val = val->next)
1058 highlighted_p = val == following_item;
1059 if (highlighted_p && highlighted_pos)
1061 if (horizontal_p)
1062 highlighted_pos->x = where.x;
1063 else
1064 highlighted_pos->y = where.y;
1067 just_compute_this_one_p =
1068 just_compute_p || ((this || that) && val != this && val != that);
1070 display_menu_item (mw, val, ws, &where, highlighted_p, horizontal_p,
1071 just_compute_this_one_p);
1073 if (highlighted_p && highlighted_pos)
1075 if (horizontal_p)
1076 highlighted_pos->y = where.y;
1077 else
1078 highlighted_pos->x = where.x;
1081 if (hit
1082 && !*hit_return
1083 && (horizontal_p ? hit->x < where.x : hit->y < where.y)
1084 && !lw_separator_p (val->name, &separator, 0)
1085 && !no_return)
1087 if (val->enabled)
1088 *hit_return = val;
1089 else
1090 no_return = 1;
1093 if (horizontal_p)
1094 where.y = 0;
1095 else
1096 where.x = 0;
1099 if (!just_compute_p)
1100 draw_shadow_rectangle (mw, ws->window, 0, 0, ws->width, ws->height,
1101 False, False);
1104 \f/* Motion code */
1105 static void
1106 set_new_state (mw, val, level)
1107 XlwMenuWidget mw;
1108 widget_value* val;
1109 int level;
1111 int i;
1113 mw->menu.new_depth = 0;
1114 for (i = 0; i < level; i++)
1115 push_new_stack (mw, mw->menu.old_stack [i]);
1116 push_new_stack (mw, val);
1119 static void
1120 make_windows_if_needed (mw, n)
1121 XlwMenuWidget mw;
1122 int n;
1124 int i;
1125 int start_at;
1126 XSetWindowAttributes xswa;
1127 int mask;
1128 Window root = RootWindowOfScreen (DefaultScreenOfDisplay (XtDisplay (mw)));
1129 window_state* windows;
1131 if (mw->menu.windows_length >= n)
1132 return;
1134 xswa.save_under = True;
1135 xswa.override_redirect = True;
1136 xswa.background_pixel = mw->core.background_pixel;
1137 xswa.border_pixel = mw->core.border_pixel;
1138 xswa.event_mask =
1139 ExposureMask | PointerMotionMask | PointerMotionHintMask
1140 | ButtonReleaseMask | ButtonPressMask;
1141 xswa.cursor = mw->menu.cursor_shape;
1142 mask = CWSaveUnder | CWOverrideRedirect | CWBackPixel | CWBorderPixel
1143 | CWEventMask | CWCursor;
1145 if (!mw->menu.windows)
1147 mw->menu.windows =
1148 (window_state*)XtMalloc (n * sizeof (window_state));
1149 start_at = 0;
1151 else
1153 mw->menu.windows =
1154 (window_state*)XtRealloc ((char*)mw->menu.windows,
1155 n * sizeof (window_state));
1156 start_at = mw->menu.windows_length;
1158 mw->menu.windows_length = n;
1160 windows = mw->menu.windows;
1162 for (i = start_at; i < n; i++)
1164 windows [i].x = 0;
1165 windows [i].y = 0;
1166 windows [i].width = 1;
1167 windows [i].height = 1;
1168 windows [i].window =
1169 XCreateWindow (XtDisplay (mw), root, 0, 0, 1, 1,
1170 0, 0, CopyFromParent, CopyFromParent, mask, &xswa);
1174 /* Make the window fit in the screen */
1175 static void
1176 fit_to_screen (mw, ws, previous_ws, horizontal_p)
1177 XlwMenuWidget mw;
1178 window_state* ws;
1179 window_state* previous_ws;
1180 Boolean horizontal_p;
1182 unsigned int screen_width = WidthOfScreen (XtScreen (mw));
1183 unsigned int screen_height = HeightOfScreen (XtScreen (mw));
1184 /* 1 if we are unable to avoid an overlap between
1185 this menu and the parent menu in the X dimension. */
1186 int horizontal_overlap = 0;
1188 if (ws->x < 0)
1189 ws->x = 0;
1190 else if (ws->x + ws->width > screen_width)
1192 if (!horizontal_p)
1193 ws->x = previous_ws->x - ws->width;
1194 else
1195 ws->x = screen_width - ws->width;
1196 if (ws->x < 0)
1198 ws->x = 0;
1199 horizontal_overlap = 1;
1202 /* If we overlap in X, try to avoid overlap in Y. */
1203 if (horizontal_overlap
1204 && ws->y < previous_ws->y + previous_ws->height
1205 && previous_ws->y < ws->y + ws->height)
1207 /* Put this menu right below or right above PREVIOUS_WS
1208 if there's room. */
1209 if (previous_ws->y + previous_ws->height + ws->height < screen_height)
1210 ws->y = previous_ws->y + previous_ws->height;
1211 else if (previous_ws->y - ws->height > 0)
1212 ws->y = previous_ws->y - ws->height;
1215 if (ws->y < 0)
1216 ws->y = 0;
1217 else if (ws->y + ws->height > screen_height)
1219 if (horizontal_p)
1220 ws->y = previous_ws->y - ws->height;
1221 else
1222 ws->y = screen_height - ws->height;
1223 if (ws->y < 0)
1224 ws->y = 0;
1228 /* Updates old_stack from new_stack and redisplays. */
1229 static void
1230 remap_menubar (mw)
1231 XlwMenuWidget mw;
1233 int i;
1234 int last_same;
1235 XPoint selection_position;
1236 int old_depth = mw->menu.old_depth;
1237 int new_depth = mw->menu.new_depth;
1238 widget_value** old_stack;
1239 widget_value** new_stack;
1240 window_state* windows;
1241 widget_value* old_selection;
1242 widget_value* new_selection;
1244 /* Check that enough windows and old_stack are ready. */
1245 make_windows_if_needed (mw, new_depth);
1246 make_old_stack_space (mw, new_depth);
1247 windows = mw->menu.windows;
1248 old_stack = mw->menu.old_stack;
1249 new_stack = mw->menu.new_stack;
1251 /* compute the last identical different entry */
1252 for (i = 1; i < old_depth && i < new_depth; i++)
1253 if (old_stack [i] != new_stack [i])
1254 break;
1255 last_same = i - 1;
1257 /* Memorize the previously selected item to be able to refresh it */
1258 old_selection = last_same + 1 < old_depth ? old_stack [last_same + 1] : NULL;
1259 if (old_selection && !old_selection->enabled)
1260 old_selection = NULL;
1261 new_selection = last_same + 1 < new_depth ? new_stack [last_same + 1] : NULL;
1262 if (new_selection && !new_selection->enabled)
1263 new_selection = NULL;
1265 /* Call callback when the hightlighted item changes. */
1266 if (old_selection || new_selection)
1267 XtCallCallbackList ((Widget)mw, mw->menu.highlight,
1268 (XtPointer) new_selection);
1270 /* updates old_state from new_state. It has to be done now because
1271 display_menu (called below) uses the old_stack to know what to display. */
1272 for (i = last_same + 1; i < new_depth; i++)
1273 old_stack [i] = new_stack [i];
1274 mw->menu.old_depth = new_depth;
1276 /* refresh the last selection */
1277 selection_position.x = 0;
1278 selection_position.y = 0;
1279 display_menu (mw, last_same, new_selection == old_selection,
1280 &selection_position, NULL, NULL, old_selection, new_selection);
1282 /* Now place the new menus. */
1283 for (i = last_same + 1; i < new_depth && new_stack[i]->contents; i++)
1285 window_state *previous_ws = &windows[i - 1];
1286 window_state *ws = &windows[i];
1288 ws->x = (previous_ws->x + selection_position.x
1289 + mw->menu.shadow_thickness);
1290 if (i == 1)
1291 ws->x += mw->menu.margin;
1293 #if 0
1294 if (!mw->menu.horizontal || i > 1)
1295 ws->x += mw->menu.shadow_thickness;
1296 #endif
1298 ws->y = (previous_ws->y + selection_position.y
1299 + mw->menu.shadow_thickness);
1300 if (i == 1)
1301 ws->y += mw->menu.margin;
1303 size_menu (mw, i);
1305 fit_to_screen (mw, ws, previous_ws, mw->menu.horizontal && i == 1);
1307 XClearWindow (XtDisplay (mw), ws->window);
1308 XMoveResizeWindow (XtDisplay (mw), ws->window, ws->x, ws->y,
1309 ws->width, ws->height);
1310 XMapRaised (XtDisplay (mw), ws->window);
1311 display_menu (mw, i, False, &selection_position, NULL, NULL, NULL, NULL);
1314 /* unmap the menus that popped down */
1315 for (i = new_depth - 1; i < old_depth; i++)
1316 if (i >= new_depth || !new_stack[i]->contents)
1317 XUnmapWindow (XtDisplay (mw), windows[i].window);
1320 static Boolean
1321 motion_event_is_in_menu (mw, ev, level, relative_pos)
1322 XlwMenuWidget mw;
1323 XMotionEvent* ev;
1324 int level;
1325 XPoint* relative_pos;
1327 window_state* ws = &mw->menu.windows [level];
1328 int shadow = level == 0 ? 0 : mw->menu.shadow_thickness;
1329 int x = ws->x + shadow;
1330 int y = ws->y + shadow;
1331 relative_pos->x = ev->x_root - x;
1332 relative_pos->y = ev->y_root - y;
1333 return (x - shadow < ev->x_root && ev->x_root < x + ws->width
1334 && y - shadow < ev->y_root && ev->y_root < y + ws->height);
1337 static Boolean
1338 map_event_to_widget_value (mw, ev, val, level)
1339 XlwMenuWidget mw;
1340 XMotionEvent* ev;
1341 widget_value** val;
1342 int* level;
1344 int i;
1345 XPoint relative_pos;
1346 window_state* ws;
1348 *val = NULL;
1350 /* Find the window */
1351 for (i = mw->menu.old_depth - 1; i >= 0; i--)
1353 ws = &mw->menu.windows [i];
1354 if (ws && motion_event_is_in_menu (mw, ev, i, &relative_pos))
1356 display_menu (mw, i, True, NULL, &relative_pos, val, NULL, NULL);
1358 if (*val)
1360 *level = i + 1;
1361 return True;
1365 return False;
1368 \f/* Procedures */
1369 static void
1370 make_drawing_gcs (mw)
1371 XlwMenuWidget mw;
1373 XGCValues xgcv;
1375 xgcv.font = mw->menu.font->fid;
1376 xgcv.foreground = mw->menu.foreground;
1377 xgcv.background = mw->core.background_pixel;
1378 mw->menu.foreground_gc = XtGetGC ((Widget)mw,
1379 GCFont | GCForeground | GCBackground,
1380 &xgcv);
1382 xgcv.font = mw->menu.font->fid;
1383 xgcv.foreground = mw->menu.button_foreground;
1384 xgcv.background = mw->core.background_pixel;
1385 mw->menu.button_gc = XtGetGC ((Widget)mw,
1386 GCFont | GCForeground | GCBackground,
1387 &xgcv);
1389 xgcv.font = mw->menu.font->fid;
1390 xgcv.foreground = mw->menu.foreground;
1391 xgcv.background = mw->core.background_pixel;
1392 xgcv.fill_style = FillStippled;
1393 xgcv.stipple = mw->menu.gray_pixmap;
1394 mw->menu.inactive_gc = XtGetGC ((Widget)mw,
1395 (GCFont | GCForeground | GCBackground
1396 | GCFillStyle | GCStipple), &xgcv);
1398 xgcv.font = mw->menu.font->fid;
1399 xgcv.foreground = mw->menu.button_foreground;
1400 xgcv.background = mw->core.background_pixel;
1401 xgcv.fill_style = FillStippled;
1402 xgcv.stipple = mw->menu.gray_pixmap;
1403 mw->menu.inactive_button_gc = XtGetGC ((Widget)mw,
1404 (GCFont | GCForeground | GCBackground
1405 | GCFillStyle | GCStipple), &xgcv);
1407 xgcv.font = mw->menu.font->fid;
1408 xgcv.foreground = mw->core.background_pixel;
1409 xgcv.background = mw->menu.foreground;
1410 mw->menu.background_gc = XtGetGC ((Widget)mw,
1411 GCFont | GCForeground | GCBackground,
1412 &xgcv);
1415 static void
1416 release_drawing_gcs (mw)
1417 XlwMenuWidget mw;
1419 XtReleaseGC ((Widget) mw, mw->menu.foreground_gc);
1420 XtReleaseGC ((Widget) mw, mw->menu.button_gc);
1421 XtReleaseGC ((Widget) mw, mw->menu.inactive_gc);
1422 XtReleaseGC ((Widget) mw, mw->menu.inactive_button_gc);
1423 XtReleaseGC ((Widget) mw, mw->menu.background_gc);
1424 /* let's get some segvs if we try to use these... */
1425 mw->menu.foreground_gc = (GC) -1;
1426 mw->menu.button_gc = (GC) -1;
1427 mw->menu.inactive_gc = (GC) -1;
1428 mw->menu.inactive_button_gc = (GC) -1;
1429 mw->menu.background_gc = (GC) -1;
1432 #define MINL(x,y) ((((unsigned long) (x)) < ((unsigned long) (y))) \
1433 ? ((unsigned long) (x)) : ((unsigned long) (y)))
1435 static void
1436 make_shadow_gcs (mw)
1437 XlwMenuWidget mw;
1439 XGCValues xgcv;
1440 unsigned long pm = 0;
1441 Display *dpy = XtDisplay ((Widget) mw);
1442 Screen *screen = XtScreen ((Widget) mw);
1443 Colormap cmap = mw->core.colormap;
1444 XColor topc, botc;
1445 int top_frobbed = 0, bottom_frobbed = 0;
1447 mw->menu.free_top_shadow_color_p = 0;
1448 mw->menu.free_bottom_shadow_color_p = 0;
1450 if (mw->menu.top_shadow_color == -1)
1451 mw->menu.top_shadow_color = mw->core.background_pixel;
1452 else
1453 mw->menu.top_shadow_color = mw->menu.top_shadow_color;
1455 if (mw->menu.bottom_shadow_color == -1)
1456 mw->menu.bottom_shadow_color = mw->menu.foreground;
1457 else
1458 mw->menu.bottom_shadow_color = mw->menu.bottom_shadow_color;
1460 if (mw->menu.top_shadow_color == mw->core.background_pixel ||
1461 mw->menu.top_shadow_color == mw->menu.foreground)
1463 topc.pixel = mw->core.background_pixel;
1464 XQueryColor (dpy, cmap, &topc);
1465 /* don't overflow/wrap! */
1466 topc.red = MINL (65535, topc.red * 1.2);
1467 topc.green = MINL (65535, topc.green * 1.2);
1468 topc.blue = MINL (65535, topc.blue * 1.2);
1469 #ifdef emacs
1470 if (x_alloc_nearest_color_for_widget ((Widget) mw, cmap, &topc))
1471 #else
1472 if (XAllocColor (dpy, cmap, &topc))
1473 #endif
1475 mw->menu.top_shadow_color = topc.pixel;
1476 mw->menu.free_top_shadow_color_p = 1;
1477 top_frobbed = 1;
1480 if (mw->menu.bottom_shadow_color == mw->menu.foreground ||
1481 mw->menu.bottom_shadow_color == mw->core.background_pixel)
1483 botc.pixel = mw->core.background_pixel;
1484 XQueryColor (dpy, cmap, &botc);
1485 botc.red *= 0.6;
1486 botc.green *= 0.6;
1487 botc.blue *= 0.6;
1488 #ifdef emacs
1489 if (x_alloc_nearest_color_for_widget ((Widget) mw, cmap, &botc))
1490 #else
1491 if (XAllocColor (dpy, cmap, &botc))
1492 #endif
1494 mw->menu.bottom_shadow_color = botc.pixel;
1495 mw->menu.free_bottom_shadow_color_p = 1;
1496 bottom_frobbed = 1;
1500 if (top_frobbed && bottom_frobbed)
1502 int top_avg = ((topc.red / 3) + (topc.green / 3) + (topc.blue / 3));
1503 int bot_avg = ((botc.red / 3) + (botc.green / 3) + (botc.blue / 3));
1504 if (bot_avg > top_avg)
1506 Pixel tmp = mw->menu.top_shadow_color;
1507 mw->menu.top_shadow_color = mw->menu.bottom_shadow_color;
1508 mw->menu.bottom_shadow_color = tmp;
1510 else if (topc.pixel == botc.pixel)
1512 if (botc.pixel == mw->menu.foreground)
1514 if (mw->menu.free_top_shadow_color_p)
1516 x_free_dpy_colors (dpy, screen, cmap,
1517 &mw->menu.top_shadow_color, 1);
1518 mw->menu.free_top_shadow_color_p = 0;
1520 mw->menu.top_shadow_color = mw->core.background_pixel;
1522 else
1524 if (mw->menu.free_bottom_shadow_color_p)
1526 x_free_dpy_colors (dpy, screen, cmap,
1527 &mw->menu.bottom_shadow_color, 1);
1528 mw->menu.free_bottom_shadow_color_p = 0;
1530 mw->menu.bottom_shadow_color = mw->menu.foreground;
1535 if (!mw->menu.top_shadow_pixmap &&
1536 mw->menu.top_shadow_color == mw->core.background_pixel)
1538 mw->menu.top_shadow_pixmap = mw->menu.gray_pixmap;
1539 if (mw->menu.free_top_shadow_color_p)
1541 x_free_dpy_colors (dpy, screen, cmap, &mw->menu.top_shadow_color, 1);
1542 mw->menu.free_top_shadow_color_p = 0;
1544 mw->menu.top_shadow_color = mw->menu.foreground;
1546 if (!mw->menu.bottom_shadow_pixmap &&
1547 mw->menu.bottom_shadow_color == mw->core.background_pixel)
1549 mw->menu.bottom_shadow_pixmap = mw->menu.gray_pixmap;
1550 if (mw->menu.free_bottom_shadow_color_p)
1552 x_free_dpy_colors (dpy, screen, cmap,
1553 &mw->menu.bottom_shadow_color, 1);
1554 mw->menu.free_bottom_shadow_color_p = 0;
1556 mw->menu.bottom_shadow_color = mw->menu.foreground;
1559 xgcv.fill_style = FillStippled;
1560 xgcv.foreground = mw->menu.top_shadow_color;
1561 xgcv.stipple = mw->menu.top_shadow_pixmap;
1562 pm = (xgcv.stipple ? GCStipple|GCFillStyle : 0);
1563 mw->menu.shadow_top_gc = XtGetGC ((Widget)mw, GCForeground | pm, &xgcv);
1565 xgcv.foreground = mw->menu.bottom_shadow_color;
1566 xgcv.stipple = mw->menu.bottom_shadow_pixmap;
1567 pm = (xgcv.stipple ? GCStipple|GCFillStyle : 0);
1568 mw->menu.shadow_bottom_gc = XtGetGC ((Widget)mw, GCForeground | pm, &xgcv);
1572 static void
1573 release_shadow_gcs (mw)
1574 XlwMenuWidget mw;
1576 Display *dpy = XtDisplay ((Widget) mw);
1577 Screen *screen = XtScreen ((Widget) mw);
1578 Colormap cmap = mw->core.colormap;
1579 Pixel px[2];
1580 int i = 0;
1582 if (mw->menu.free_top_shadow_color_p)
1583 px[i++] = mw->menu.top_shadow_color;
1584 if (mw->menu.free_bottom_shadow_color_p)
1585 px[i++] = mw->menu.bottom_shadow_color;
1586 if (i > 0)
1587 x_free_dpy_colors (dpy, screen, cmap, px, i);
1589 XtReleaseGC ((Widget) mw, mw->menu.shadow_top_gc);
1590 XtReleaseGC ((Widget) mw, mw->menu.shadow_bottom_gc);
1593 static void
1594 XlwMenuInitialize (request, mw, args, num_args)
1595 Widget request;
1596 XlwMenuWidget mw;
1597 ArgList args;
1598 Cardinal *num_args;
1600 /* Get the GCs and the widget size */
1601 XSetWindowAttributes xswa;
1602 int mask;
1604 Window window = RootWindowOfScreen (DefaultScreenOfDisplay (XtDisplay (mw)));
1605 Display* display = XtDisplay (mw);
1607 #if 0
1608 widget_value *tem = (widget_value *) XtMalloc (sizeof (widget_value));
1610 /* _XtCreate is freeing the object that was passed to us,
1611 so make a copy that we will actually keep. */
1612 lwlib_bcopy (mw->menu.contents, tem, sizeof (widget_value));
1613 mw->menu.contents = tem;
1614 #endif
1616 /* mw->menu.cursor = XCreateFontCursor (display, mw->menu.cursor_shape); */
1617 mw->menu.cursor = mw->menu.cursor_shape;
1619 mw->menu.gray_pixmap
1620 = XCreatePixmapFromBitmapData (display, window, gray_bits,
1621 gray_width, gray_height,
1622 (unsigned long)1, (unsigned long)0, 1);
1624 /* I don't understand why this ends up 0 sometimes,
1625 but it does. This kludge works around it.
1626 Can anyone find a real fix? -- rms. */
1627 if (mw->menu.font == 0)
1628 mw->menu.font = xlwmenu_default_font;
1630 make_drawing_gcs (mw);
1631 make_shadow_gcs (mw);
1633 xswa.background_pixel = mw->core.background_pixel;
1634 xswa.border_pixel = mw->core.border_pixel;
1635 mask = CWBackPixel | CWBorderPixel;
1637 mw->menu.popped_up = False;
1639 mw->menu.old_depth = 1;
1640 mw->menu.old_stack = (widget_value**)XtMalloc (sizeof (widget_value*));
1641 mw->menu.old_stack_length = 1;
1642 mw->menu.old_stack [0] = mw->menu.contents;
1644 mw->menu.new_depth = 0;
1645 mw->menu.new_stack = 0;
1646 mw->menu.new_stack_length = 0;
1647 push_new_stack (mw, mw->menu.contents);
1649 mw->menu.windows = (window_state*)XtMalloc (sizeof (window_state));
1650 mw->menu.windows_length = 1;
1651 mw->menu.windows [0].x = 0;
1652 mw->menu.windows [0].y = 0;
1653 mw->menu.windows [0].width = 0;
1654 mw->menu.windows [0].height = 0;
1655 size_menu (mw, 0);
1657 mw->core.width = mw->menu.windows [0].width;
1658 mw->core.height = mw->menu.windows [0].height;
1661 static void
1662 XlwMenuClassInitialize ()
1666 static void
1667 XlwMenuRealize (w, valueMask, attributes)
1668 Widget w;
1669 Mask *valueMask;
1670 XSetWindowAttributes *attributes;
1672 XlwMenuWidget mw = (XlwMenuWidget)w;
1673 XSetWindowAttributes xswa;
1674 int mask;
1676 (*xlwMenuWidgetClass->core_class.superclass->core_class.realize)
1677 (w, valueMask, attributes);
1679 xswa.save_under = True;
1680 xswa.cursor = mw->menu.cursor_shape;
1681 mask = CWSaveUnder | CWCursor;
1682 XChangeWindowAttributes (XtDisplay (w), XtWindow (w), mask, &xswa);
1684 mw->menu.windows [0].window = XtWindow (w);
1685 mw->menu.windows [0].x = w->core.x;
1686 mw->menu.windows [0].y = w->core.y;
1687 mw->menu.windows [0].width = w->core.width;
1688 mw->menu.windows [0].height = w->core.height;
1691 /* Only the toplevel menubar/popup is a widget so it's the only one that
1692 receives expose events through Xt. So we repaint all the other panes
1693 when receiving an Expose event. */
1694 static void
1695 XlwMenuRedisplay (w, ev, region)
1696 Widget w;
1697 XEvent* ev;
1698 Region region;
1700 XlwMenuWidget mw = (XlwMenuWidget)w;
1701 int i;
1703 /* If we have a depth beyond 1, it's because a submenu was displayed.
1704 If the submenu has been destroyed, set the depth back to 1. */
1705 if (submenu_destroyed)
1707 mw->menu.old_depth = 1;
1708 submenu_destroyed = 0;
1711 for (i = 0; i < mw->menu.old_depth; i++)
1712 display_menu (mw, i, False, NULL, NULL, NULL, NULL, NULL);
1715 static void
1716 XlwMenuDestroy (w)
1717 Widget w;
1719 int i;
1720 XlwMenuWidget mw = (XlwMenuWidget) w;
1722 if (pointer_grabbed)
1723 XtUngrabPointer ((Widget)w, CurrentTime);
1724 pointer_grabbed = 0;
1726 submenu_destroyed = 1;
1728 release_drawing_gcs (mw);
1729 release_shadow_gcs (mw);
1731 /* this doesn't come from the resource db but is created explicitly
1732 so we must free it ourselves. */
1733 XFreePixmap (XtDisplay (mw), mw->menu.gray_pixmap);
1734 mw->menu.gray_pixmap = (Pixmap) -1;
1736 #if 0
1737 /* Do free mw->menu.contents because nowadays we copy it
1738 during initialization. */
1739 XtFree (mw->menu.contents);
1740 #endif
1742 /* Don't free mw->menu.contents because that comes from our creator.
1743 The `*_stack' elements are just pointers into `contents' so leave
1744 that alone too. But free the stacks themselves. */
1745 if (mw->menu.old_stack) XtFree ((char *) mw->menu.old_stack);
1746 if (mw->menu.new_stack) XtFree ((char *) mw->menu.new_stack);
1748 /* Remember, you can't free anything that came from the resource
1749 database. This includes:
1750 mw->menu.cursor
1751 mw->menu.top_shadow_pixmap
1752 mw->menu.bottom_shadow_pixmap
1753 mw->menu.font
1754 Also the color cells of top_shadow_color, bottom_shadow_color,
1755 foreground, and button_foreground will never be freed until this
1756 client exits. Nice, eh?
1759 /* start from 1 because the one in slot 0 is w->core.window */
1760 for (i = 1; i < mw->menu.windows_length; i++)
1761 XDestroyWindow (XtDisplay (mw), mw->menu.windows [i].window);
1762 if (mw->menu.windows)
1763 XtFree ((char *) mw->menu.windows);
1766 static Boolean
1767 XlwMenuSetValues (current, request, new)
1768 Widget current;
1769 Widget request;
1770 Widget new;
1772 XlwMenuWidget oldmw = (XlwMenuWidget)current;
1773 XlwMenuWidget newmw = (XlwMenuWidget)new;
1774 Boolean redisplay = False;
1775 int i;
1777 if (newmw->menu.contents
1778 && newmw->menu.contents->contents
1779 && newmw->menu.contents->contents->change >= VISIBLE_CHANGE)
1780 redisplay = True;
1781 /* Do redisplay if the contents are entirely eliminated. */
1782 if (newmw->menu.contents
1783 && newmw->menu.contents->contents == 0
1784 && newmw->menu.contents->change >= VISIBLE_CHANGE)
1785 redisplay = True;
1787 if (newmw->core.background_pixel != oldmw->core.background_pixel
1788 || newmw->menu.foreground != oldmw->menu.foreground
1789 || newmw->menu.font != oldmw->menu.font)
1791 release_drawing_gcs (newmw);
1792 make_drawing_gcs (newmw);
1793 redisplay = True;
1795 for (i = 0; i < oldmw->menu.windows_length; i++)
1797 XSetWindowBackground (XtDisplay (oldmw),
1798 oldmw->menu.windows [i].window,
1799 newmw->core.background_pixel);
1800 /* clear windows and generate expose events */
1801 XClearArea (XtDisplay (oldmw), oldmw->menu.windows[i].window,
1802 0, 0, 0, 0, True);
1806 return redisplay;
1809 static void
1810 XlwMenuResize (w)
1811 Widget w;
1813 XlwMenuWidget mw = (XlwMenuWidget)w;
1815 if (mw->menu.popped_up)
1817 /* Don't allow the popup menu to resize itself. */
1818 mw->core.width = mw->menu.windows [0].width;
1819 mw->core.height = mw->menu.windows [0].height;
1820 mw->core.parent->core.width = mw->core.width ;
1821 mw->core.parent->core.height = mw->core.height ;
1823 else
1825 mw->menu.windows [0].width = mw->core.width;
1826 mw->menu.windows [0].height = mw->core.height;
1830 \f/* Action procedures */
1831 static void
1832 handle_single_motion_event (mw, ev)
1833 XlwMenuWidget mw;
1834 XMotionEvent* ev;
1836 widget_value* val;
1837 int level;
1839 if (!map_event_to_widget_value (mw, ev, &val, &level))
1840 pop_new_stack_if_no_contents (mw);
1841 else
1842 set_new_state (mw, val, level);
1843 remap_menubar (mw);
1845 /* Sync with the display. Makes it feel better on X terms. */
1846 XSync (XtDisplay (mw), False);
1849 static void
1850 handle_motion_event (mw, ev)
1851 XlwMenuWidget mw;
1852 XMotionEvent* ev;
1854 int x = ev->x_root;
1855 int y = ev->y_root;
1856 int state = ev->state;
1858 handle_single_motion_event (mw, ev);
1860 /* allow motion events to be generated again */
1861 if (ev->is_hint
1862 && XQueryPointer (XtDisplay (mw), ev->window,
1863 &ev->root, &ev->subwindow,
1864 &ev->x_root, &ev->y_root,
1865 &ev->x, &ev->y,
1866 &ev->state)
1867 && ev->state == state
1868 && (ev->x_root != x || ev->y_root != y))
1869 handle_single_motion_event (mw, ev);
1872 static void
1873 Start (w, ev, params, num_params)
1874 Widget w;
1875 XEvent *ev;
1876 String *params;
1877 Cardinal *num_params;
1879 XlwMenuWidget mw = (XlwMenuWidget)w;
1881 if (!mw->menu.popped_up)
1883 menu_post_event = *ev;
1884 pop_up_menu (mw, (XButtonPressedEvent*) ev);
1886 else
1888 /* If we push a button while the menu is posted semipermanently,
1889 releasing the button should always pop the menu down. */
1890 next_release_must_exit = 1;
1892 /* notes the absolute position of the menubar window */
1893 mw->menu.windows [0].x = ev->xmotion.x_root - ev->xmotion.x;
1894 mw->menu.windows [0].y = ev->xmotion.y_root - ev->xmotion.y;
1896 /* handles the down like a move, slots are compatible */
1897 handle_motion_event (mw, &ev->xmotion);
1901 static void
1902 Drag (w, ev, params, num_params)
1903 Widget w;
1904 XEvent *ev;
1905 String *params;
1906 Cardinal *num_params;
1908 XlwMenuWidget mw = (XlwMenuWidget)w;
1909 if (mw->menu.popped_up)
1910 handle_motion_event (mw, &ev->xmotion);
1913 /* Do nothing.
1914 This is how we handle presses and releases of modifier keys. */
1915 static void
1916 Nothing (w, ev, params, num_params)
1917 Widget w;
1918 XEvent *ev;
1919 String *params;
1920 Cardinal *num_params;
1924 /* Handle key press and release events while menu is popped up.
1925 Our action is to get rid of the menu. */
1926 static void
1927 Key (w, ev, params, num_params)
1928 Widget w;
1929 XEvent *ev;
1930 String *params;
1931 Cardinal *num_params;
1933 XlwMenuWidget mw = (XlwMenuWidget)w;
1935 /* Pop down everything. */
1936 mw->menu.new_depth = 1;
1937 remap_menubar (mw);
1939 if (mw->menu.popped_up)
1941 mw->menu.popped_up = False;
1942 XtUngrabPointer ((Widget)mw, ev->xmotion.time);
1943 if (XtIsShell (XtParent ((Widget) mw)))
1944 XtPopdown (XtParent ((Widget) mw));
1945 else
1947 XtRemoveGrab ((Widget) mw);
1948 display_menu (mw, 0, False, NULL, NULL, NULL, NULL, NULL);
1952 /* callback */
1953 XtCallCallbackList ((Widget)mw, mw->menu.select, (XtPointer)0);
1956 static void
1957 Select (w, ev, params, num_params)
1958 Widget w;
1959 XEvent *ev;
1960 String *params;
1961 Cardinal *num_params;
1963 XlwMenuWidget mw = (XlwMenuWidget)w;
1964 widget_value* selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];
1966 /* If user releases the button quickly, without selecting anything,
1967 after the initial down-click that brought the menu up,
1968 do nothing. */
1969 if ((selected_item == 0
1970 || ((widget_value *) selected_item)->call_data == 0)
1971 && !next_release_must_exit
1972 && (ev->xbutton.time - menu_post_event.xbutton.time
1973 < XtGetMultiClickTime (XtDisplay (w))))
1974 return;
1976 /* pop down everything. */
1977 mw->menu.new_depth = 1;
1978 remap_menubar (mw);
1980 if (mw->menu.popped_up)
1982 mw->menu.popped_up = False;
1983 XtUngrabPointer ((Widget)mw, ev->xmotion.time);
1984 if (XtIsShell (XtParent ((Widget) mw)))
1985 XtPopdown (XtParent ((Widget) mw));
1986 else
1988 XtRemoveGrab ((Widget) mw);
1989 display_menu (mw, 0, False, NULL, NULL, NULL, NULL, NULL);
1993 /* callback */
1994 XtCallCallbackList ((Widget)mw, mw->menu.select, (XtPointer)selected_item);
1998 \f/* Special code to pop-up a menu */
1999 void
2000 pop_up_menu (mw, event)
2001 XlwMenuWidget mw;
2002 XButtonPressedEvent* event;
2004 int x = event->x_root;
2005 int y = event->y_root;
2006 int w;
2007 int h;
2008 int borderwidth = mw->menu.shadow_thickness;
2009 Screen* screen = XtScreen (mw);
2010 Display *display = XtDisplay (mw);
2011 int count;
2013 next_release_must_exit = 0;
2015 XtCallCallbackList ((Widget)mw, mw->menu.open, NULL);
2017 if (XtIsShell (XtParent ((Widget)mw)))
2018 size_menu (mw, 0);
2020 w = mw->menu.windows [0].width;
2021 h = mw->menu.windows [0].height;
2023 x -= borderwidth;
2024 y -= borderwidth;
2025 if (x < borderwidth)
2026 x = borderwidth;
2027 if (x + w + 2 * borderwidth > WidthOfScreen (screen))
2028 x = WidthOfScreen (screen) - w - 2 * borderwidth;
2029 if (y < borderwidth)
2030 y = borderwidth;
2031 if (y + h + 2 * borderwidth> HeightOfScreen (screen))
2032 y = HeightOfScreen (screen) - h - 2 * borderwidth;
2034 mw->menu.popped_up = True;
2035 if (XtIsShell (XtParent ((Widget)mw)))
2037 XtConfigureWidget (XtParent ((Widget)mw), x, y, w, h,
2038 XtParent ((Widget)mw)->core.border_width);
2039 XtPopup (XtParent ((Widget)mw), XtGrabExclusive);
2040 display_menu (mw, 0, False, NULL, NULL, NULL, NULL, NULL);
2041 mw->menu.windows [0].x = x + borderwidth;
2042 mw->menu.windows [0].y = y + borderwidth;
2044 else
2046 XEvent *ev = (XEvent *) event;
2048 XtAddGrab ((Widget) mw, True, True);
2050 /* notes the absolute position of the menubar window */
2051 mw->menu.windows [0].x = ev->xmotion.x_root - ev->xmotion.x;
2052 mw->menu.windows [0].y = ev->xmotion.y_root - ev->xmotion.y;
2055 #ifdef emacs
2056 count = x_catch_errors (display);
2057 #endif
2058 XtGrabPointer ((Widget)mw, False,
2059 (PointerMotionMask
2060 | PointerMotionHintMask
2061 | ButtonReleaseMask
2062 | ButtonPressMask),
2063 GrabModeAsync, GrabModeAsync, None,
2064 mw->menu.cursor_shape,
2065 event->time);
2066 pointer_grabbed = 1;
2067 #ifdef emacs
2068 if (x_had_errors_p (display))
2070 pointer_grabbed = 0;
2071 XtUngrabPointer ((Widget)mw, event->time);
2073 x_uncatch_errors (display, count);
2074 #endif
2076 handle_motion_event (mw, (XMotionEvent*)event);