(_XlwMenu_part): Add free_top_shadow_color_p and
[bpt/emacs.git] / lwlib / xlwmenu.c
CommitLineData
07bf635f
RS
1/* Implements a lightweight menubar widget.
2 Copyright (C) 1992 Lucid, Inc.
3
4This file is part of the Lucid Widget Library.
5
6The Lucid Widget Library is free software; you can redistribute it and/or
7modify it under the terms of the GNU General Public License as published by
8the Free Software Foundation; either version 2, or (at your option)
9any later version.
10
11The Lucid Widget Library is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
15
16You should have received a copy of the GNU General Public License
1fb87c77
KH
17along with GNU Emacs; see the file COPYING. If not, write to the
18Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19Boston, MA 02111-1307, USA. */
07bf635f
RS
20
21/* Created by devin@lucid.com */
22
0f0912e6
PE
23#ifdef HAVE_CONFIG_H
24#include <config.h>
25#endif
26
07bf635f
RS
27#include <stdio.h>
28
29#include <sys/types.h>
30#include <X11/Xos.h>
31#include <X11/IntrinsicP.h>
2289f3f4 32#include <X11/ObjectP.h>
07bf635f
RS
33#include <X11/StringDefs.h>
34#include <X11/cursorfont.h>
35#include <X11/bitmaps/gray>
36#include "xlwmenuP.h"
fc8fefed 37
a57a1605
DL
38#ifdef emacs
39/* Defined in xterm.c. */
40extern int x_alloc_nearest_color_for_widget __P ((Widget, Colormap, XColor*));
41extern int x_catch_errors __P ((Display*));
42extern int x_uncatch_errors __P ((Display*, int));
43extern int x_had_errors_p __P ((Display*));
44extern int x_clear_errors __P ((Display*));
fc8fefed
GM
45extern unsigned long x_copy_dpy_color __P ((Display *, Colormap,
46 unsigned long));
47
48/* Defined in xfaces.c. */
49extern void x_free_dpy_colors __P ((Display *, Screen *, Colormap,
50 unsigned long *pixels, int npixels));
a57a1605 51#endif
d398028f
PR
52
53static int pointer_grabbed;
54static XEvent menu_post_event;
07bf635f 55
47d52240
RS
56XFontStruct *xlwmenu_default_font;
57
07bf635f
RS
58static char
59xlwMenuTranslations [] =
ba624d0f
RS
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\
07bf635f
RS
93";
94
95#define offset(field) XtOffset(XlwMenuWidget, field)
96static XtResource
97xlwMenuResources[] =
98{
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),
be06a3df 106 offset(menu.margin), XtRImmediate, (XtPointer) 4},
07bf635f
RS
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},
113
d398028f 114 {XmNshadowThickness, XmCShadowThickness, XtRDimension,
07bf635f
RS
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},
125
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},
d6fd6371
GM
130 {XtNhighlightCallback, XtCCallback, XtRCallback, sizeof(XtPointer),
131 offset(menu.highlight), XtRCallback, (XtPointer)NULL},
07bf635f
RS
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},
138};
139#undef offset
140
141static Boolean XlwMenuSetValues();
142static void XlwMenuRealize();
143static void XlwMenuRedisplay();
144static void XlwMenuResize();
145static void XlwMenuInitialize();
146static void XlwMenuRedisplay();
147static void XlwMenuDestroy();
148static void XlwMenuClassInitialize();
149static void Start();
150static void Drag();
151static void Select();
ba624d0f
RS
152static void Key();
153static void Nothing();
be06a3df 154static int separator_height ();
07bf635f
RS
155
156static XtActionsRec
157xlwMenuActionsList [] =
158{
159 {"start", Start},
160 {"drag", Drag},
161 {"select", Select},
ba624d0f
RS
162 {"key", Key},
163 {"nothing", Nothing},
07bf635f
RS
164};
165
166#define SuperClass ((CoreWidgetClass)&coreClassRec)
167
168XlwMenuClassRec xlwMenuClassRec =
169{
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 */
204 {
205 0 /* dummy */
206 },
207};
208
209WidgetClass xlwMenuWidgetClass = (WidgetClass) &xlwMenuClassRec;
210
d398028f
PR
211int submenu_destroyed;
212
213static int next_release_must_exit;
214
07bf635f 215\f/* Utilities */
be06a3df
GM
216
217
218/* Like abort, but remove grabs from widget W before. */
219
220static void
221abort_gracefully (w)
222 Widget w;
223{
224 if (XtIsShell (XtParent (w)))
225 XtRemoveGrab (w);
226 XtUngrabPointer (w, CurrentTime);
227 abort ();
228}
229
07bf635f 230static void
d398028f
PR
231push_new_stack (mw, val)
232 XlwMenuWidget mw;
233 widget_value* val;
07bf635f
RS
234{
235 if (!mw->menu.new_stack)
236 {
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*));
241 }
242 else if (mw->menu.new_depth == mw->menu.new_stack_length)
243 {
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*));
248 }
249 mw->menu.new_stack [mw->menu.new_depth++] = val;
250}
251
252static void
d398028f
PR
253pop_new_stack_if_no_contents (mw)
254 XlwMenuWidget mw;
07bf635f
RS
255{
256 if (mw->menu.new_depth)
257 {
258 if (!mw->menu.new_stack [mw->menu.new_depth - 1]->contents)
259 mw->menu.new_depth -= 1;
260 }
261}
262
263static void
d398028f
PR
264make_old_stack_space (mw, n)
265 XlwMenuWidget mw;
266 int n;
07bf635f
RS
267{
268 if (!mw->menu.old_stack)
269 {
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*));
274 }
275 else if (mw->menu.old_stack_length < n)
276 {
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*));
281 }
282}
283
284\f/* Size code */
d398028f
PR
285int
286string_width (mw, s)
287 XlwMenuWidget mw;
288 char *s;
07bf635f
RS
289{
290 XCharStruct xcs;
291 int drop;
292
293 XTextExtents (mw->menu.font, s, strlen (s), &drop, &drop, &drop, &xcs);
294 return xcs.width;
295}
296
297static int
d398028f
PR
298arrow_width (mw)
299 XlwMenuWidget mw;
07bf635f 300{
be06a3df 301 return (mw->menu.font->ascent * 3/4) | 1;
07bf635f
RS
302}
303
be06a3df
GM
304/* Return the width of toggle buttons of widget MW. */
305
306static int
307toggle_button_width (mw)
308 XlwMenuWidget mw;
309{
310 return ((mw->menu.font->ascent + mw->menu.font->descent) * 2 / 3) | 1;
311}
312
313
314/* Return the width of radio buttons of widget MW. */
315
316static int
317radio_button_width (mw)
318 XlwMenuWidget mw;
319{
320 return toggle_button_width (mw) * 1.41;
321}
322
323
07bf635f
RS
324static XtResource
325nameResource[] =
326{
327 {"labelString", "LabelString", XtRString, sizeof(String),
328 0, XtRImmediate, 0},
329};
330
331static char*
d398028f
PR
332resource_widget_value (mw, val)
333 XlwMenuWidget mw;
334 widget_value *val;
07bf635f
RS
335{
336 if (!val->toolkit_data)
337 {
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)
d97342f3
RS
347 {
348 complete_name = (char *) XtMalloc (strlen (resourced_name) + 1);
349 strcpy (complete_name, resourced_name);
350 }
07bf635f
RS
351 else
352 {
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);
360 }
361
362 val->toolkit_data = complete_name;
363 val->free_toolkit_data = True;
364 }
365 return (char*)val->toolkit_data;
366}
367
368/* Returns the sizes of an item */
369static void
be06a3df
GM
370size_menu_item (mw, val, horizontal_p, label_width, rest_width, button_width,
371 height)
d398028f
PR
372 XlwMenuWidget mw;
373 widget_value* val;
374 int horizontal_p;
375 int* label_width;
376 int* rest_width;
be06a3df 377 int* button_width;
d398028f 378 int* height;
07bf635f 379{
be06a3df
GM
380 enum menu_separator separator;
381
382 if (lw_separator_p (val->name, &separator, 0))
07bf635f 383 {
be06a3df 384 *height = separator_height (separator);
07bf635f
RS
385 *label_width = 1;
386 *rest_width = 0;
be06a3df 387 *button_width = 0;
07bf635f
RS
388 }
389 else
390 {
391 *height =
392 mw->menu.font->ascent + mw->menu.font->descent
393 + 2 * mw->menu.vertical_spacing + 2 * mw->menu.shadow_thickness;
394
395 *label_width =
396 string_width (mw, resource_widget_value (mw, val))
397 + mw->menu.horizontal_spacing + mw->menu.shadow_thickness;
398
399 *rest_width = mw->menu.horizontal_spacing + mw->menu.shadow_thickness;
400 if (!horizontal_p)
401 {
402 if (val->contents)
be06a3df 403 /* Add width of the arrow displayed for submenus. */
07bf635f
RS
404 *rest_width += arrow_width (mw) + mw->menu.arrow_spacing;
405 else if (val->key)
be06a3df
GM
406 /* Add width of key equivalent string. */
407 *rest_width += (string_width (mw, val->key)
408 + mw->menu.arrow_spacing);
409
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);
07bf635f
RS
416 }
417 }
418}
419
420static void
d398028f
PR
421size_menu (mw, level)
422 XlwMenuWidget mw;
423 int level;
07bf635f 424{
ccf589a6 425 unsigned int label_width = 0;
07bf635f 426 int rest_width = 0;
be06a3df 427 int button_width = 0;
07bf635f 428 int max_rest_width = 0;
be06a3df 429 int max_button_width = 0;
ccf589a6 430 unsigned int height = 0;
07bf635f
RS
431 int horizontal_p = mw->menu.horizontal && (level == 0);
432 widget_value* val;
433 window_state* ws;
434
435 if (level >= mw->menu.old_depth)
be06a3df 436 abort_gracefully ((Widget) mw);
07bf635f
RS
437
438 ws = &mw->menu.windows [level];
439 ws->width = 0;
440 ws->height = 0;
441 ws->label_width = 0;
be06a3df 442 ws->button_width = 0;
07bf635f
RS
443
444 for (val = mw->menu.old_stack [level]->contents; val; val = val->next)
445 {
446 size_menu_item (mw, val, horizontal_p, &label_width, &rest_width,
be06a3df 447 &button_width, &height);
07bf635f
RS
448 if (horizontal_p)
449 {
450 ws->width += label_width + rest_width;
451 if (height > ws->height)
452 ws->height = height;
453 }
454 else
455 {
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;
be06a3df
GM
460 if (button_width > max_button_width)
461 max_button_width = button_width;
07bf635f
RS
462 ws->height += height;
463 }
464 }
465
466 if (horizontal_p)
be06a3df 467 ws->label_width = ws->button_width = 0;
07bf635f 468 else
be06a3df
GM
469 {
470 ws->width = ws->label_width + max_rest_width + max_button_width;
471 ws->button_width = max_button_width;
472 }
07bf635f
RS
473
474 ws->width += 2 * mw->menu.shadow_thickness;
475 ws->height += 2 * mw->menu.shadow_thickness;
be06a3df
GM
476
477 if (horizontal_p)
478 {
479 ws->width += 2 * mw->menu.margin;
480 ws->height += 2 * mw->menu.margin;
481 }
07bf635f
RS
482}
483
484
485\f/* Display code */
be06a3df 486
07bf635f 487static void
be06a3df 488draw_arrow (mw, window, gc, x, y, width, down_p)
d398028f
PR
489 XlwMenuWidget mw;
490 Window window;
491 GC gc;
492 int x;
493 int y;
494 int width;
be06a3df 495 int down_p;
07bf635f 496{
be06a3df
GM
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;
507
508 y += (mw->menu.font->ascent + mw->menu.font->descent - height) / 2;
07bf635f 509
be06a3df
GM
510 if (down_p)
511 {
512 GC temp;
513 temp = top_gc;
514 top_gc = bottom_gc;
515 bottom_gc = temp;
516 }
517
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);
527
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);
537
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);
07bf635f
RS
547}
548
be06a3df
GM
549
550
07bf635f 551static void
be06a3df 552draw_shadow_rectangle (mw, window, x, y, width, height, erase_p, down_p)
d398028f
PR
553 XlwMenuWidget mw;
554 Window window;
555 int x;
556 int y;
557 int width;
558 int height;
559 int erase_p;
be06a3df 560 int down_p;
07bf635f
RS
561{
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];
be06a3df
GM
567
568 if (!erase_p && down_p)
569 {
570 GC temp;
571 temp = top_gc;
572 top_gc = bottom_gc;
573 bottom_gc = temp;
574 }
575
07bf635f
RS
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);
612}
613
614
be06a3df
GM
615static void
616draw_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;
625{
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];
be06a3df
GM
631
632 if (!erase_p && down_p)
633 {
634 GC temp;
635 temp = top_gc;
636 top_gc = bottom_gc;
637 bottom_gc = temp;
638 }
639
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);
676}
677
678
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. */
682
683static void
684draw_toggle (mw, window, x, y, selected_p)
685 XlwMenuWidget mw;
686 Window window;
687 int x, y, selected_p;
688{
689 int width, height;
690
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);
696}
697
698
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. */
702
703static void
704draw_radio (mw, window, x, y, selected_p)
705 XlwMenuWidget mw;
706 Window window;
707 int x, y, selected_p;
708{
709 int width, height;
710
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);
716}
717
718
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. */
722
723static void
724draw_separator (mw, window, x, y, width, type)
725 XlwMenuWidget mw;
726 Window window;
727 int x, y, width;
728 enum menu_separator type;
729{
730 Display *dpy = XtDisplay (mw);
731 XGCValues xgcv;
732
733 switch (type)
734 {
735 case SEPARATOR_NO_LINE:
736 break;
737
738 case SEPARATOR_SINGLE_LINE:
739 XDrawLine (dpy, window, mw->menu.foreground_gc,
740 x, y, x + width, y);
741 break;
742
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;
747
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;
756
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;
763
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;
770
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;
777
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;
787
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;
797
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;
802
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;
809
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;
820
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;
831
832 default:
833 abort ();
834 }
835}
836
837
838/* Return the pixel height of menu separator SEPARATOR. */
839
840static int
841separator_height (separator)
842 enum menu_separator separator;
843{
844 switch (separator)
845 {
846 case SEPARATOR_NO_LINE:
847 return 2;
848
849 case SEPARATOR_SINGLE_LINE:
850 case SEPARATOR_SINGLE_DASHED_LINE:
851 return 1;
852
853 case SEPARATOR_DOUBLE_LINE:
854 case SEPARATOR_DOUBLE_DASHED_LINE:
855 return 3;
856
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;
862
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;
868
869 default:
870 abort ();
871 }
872}
873
874
07bf635f 875/* Display the menu item and increment where.x and where.y to show how large
be06a3df
GM
876 the menu item was. */
877
07bf635f 878static void
be06a3df
GM
879display_menu_item (mw, val, ws, where, highlighted_p, horizontal_p,
880 just_compute_p)
d398028f
PR
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;
07bf635f
RS
888{
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;
be06a3df 894 int margin = mw->menu.margin;
07bf635f
RS
895 int h_spacing = mw->menu.horizontal_spacing;
896 int v_spacing = mw->menu.vertical_spacing;
897 int label_width;
898 int rest_width;
be06a3df 899 int button_width;
07bf635f
RS
900 int height;
901 int width;
be06a3df
GM
902 enum menu_separator separator;
903 int separator_p = lw_separator_p (val->name, &separator, 0);
07bf635f
RS
904
905 /* compute the sizes of the item */
be06a3df
GM
906 size_menu_item (mw, val, horizontal_p, &label_width, &rest_width,
907 &button_width, &height);
07bf635f
RS
908
909 if (horizontal_p)
910 width = label_width + rest_width;
911 else
912 {
913 label_width = ws->label_width;
914 width = ws->width - 2 * shadow;
915 }
916
07bf635f
RS
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;
921
922 /* do the drawing. */
923 if (!just_compute_p)
924 {
925 /* Add the shadow border of the containing menu */
926 int x = where->x + shadow;
927 int y = where->y + shadow;
928
be06a3df
GM
929 if (horizontal_p)
930 {
931 x += margin;
932 y += margin;
933 }
934
07bf635f
RS
935 /* pick the foreground and background GC. */
936 if (val->enabled)
be06a3df 937 text_gc = mw->menu.foreground_gc;
07bf635f 938 else
be06a3df 939 text_gc = mw->menu.inactive_gc;
07bf635f
RS
940 deco_gc = mw->menu.foreground_gc;
941
942 if (separator_p)
943 {
be06a3df 944 draw_separator (mw, ws->window, x, y, width, separator);
07bf635f
RS
945 }
946 else
947 {
84593cae 948 int x_offset = x + h_spacing + shadow;
07bf635f 949 char* display_string = resource_widget_value (mw, val);
be06a3df
GM
950 draw_shadow_rectangle (mw, ws->window, x, y, width, height, True,
951 False);
84593cae
PR
952
953 /* Deal with centering a menu title. */
954 if (!horizontal_p && !val->contents && !val->call_data)
955 {
956 int l = string_width (mw, display_string);
957
958 if (width > l)
959 x_offset = (width - l) >> 1;
960 }
be06a3df
GM
961 else if (!horizontal_p && ws->button_width)
962 x_offset += ws->button_width;
963
964
84593cae 965 XDrawString (XtDisplay (mw), ws->window, text_gc, x_offset,
07bf635f
RS
966 y + v_spacing + shadow + font_ascent,
967 display_string, strlen (display_string));
968
969 if (!horizontal_p)
970 {
be06a3df
GM
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);
977
07bf635f
RS
978 if (val->contents)
979 {
980 int a_w = arrow_width (mw);
981 draw_arrow (mw, ws->window, deco_gc,
be06a3df 982 x + width - a_w
d398028f
PR
983 - mw->menu.horizontal_spacing
984 - mw->menu.shadow_thickness,
be06a3df
GM
985 y + v_spacing + shadow, a_w,
986 highlighted_p);
07bf635f
RS
987 }
988 else if (val->key)
989 {
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));
994 }
995 }
d398028f
PR
996 else
997 {
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,
be06a3df 1004 True, False);
d398028f 1005 }
07bf635f
RS
1006
1007 if (highlighted_p)
be06a3df
GM
1008 draw_shadow_rectangle (mw, ws->window, x, y, width, height, False,
1009 False);
07bf635f
RS
1010 }
1011 }
1012
1013 where->x += width;
1014 where->y += height;
1015}
1016
1017static void
d398028f
PR
1018display_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;
07bf635f
RS
1028{
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;
ccc1cac0
RS
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;
be06a3df 1039 enum menu_separator separator;
07bf635f
RS
1040
1041 if (level >= mw->menu.old_depth)
be06a3df 1042 abort_gracefully ((Widget) mw);
07bf635f
RS
1043
1044 if (level < mw->menu.old_depth - 1)
1045 following_item = mw->menu.old_stack [level + 1];
1046 else
1047 following_item = NULL;
1048
1049 if (hit)
1050 *hit_return = NULL;
1051
1052 where.x = 0;
1053 where.y = 0;
1054
1055 ws = &mw->menu.windows [level];
1056 for (val = mw->menu.old_stack [level]->contents; val; val = val->next)
1057 {
1058 highlighted_p = val == following_item;
1059 if (highlighted_p && highlighted_pos)
1060 {
1061 if (horizontal_p)
1062 highlighted_pos->x = where.x;
1063 else
1064 highlighted_pos->y = where.y;
1065 }
1066
1067 just_compute_this_one_p =
1068 just_compute_p || ((this || that) && val != this && val != that);
1069
1070 display_menu_item (mw, val, ws, &where, highlighted_p, horizontal_p,
1071 just_compute_this_one_p);
1072
1073 if (highlighted_p && highlighted_pos)
1074 {
1075 if (horizontal_p)
1076 highlighted_pos->y = where.y;
1077 else
1078 highlighted_pos->x = where.x;
1079 }
1080
1081 if (hit
1082 && !*hit_return
1083 && (horizontal_p ? hit->x < where.x : hit->y < where.y)
be06a3df 1084 && !lw_separator_p (val->name, &separator, 0)
ccc1cac0
RS
1085 && !no_return)
1086 {
1087 if (val->enabled)
1088 *hit_return = val;
1089 else
1090 no_return = 1;
1091 }
07bf635f
RS
1092
1093 if (horizontal_p)
1094 where.y = 0;
1095 else
1096 where.x = 0;
1097 }
1098
1099 if (!just_compute_p)
be06a3df
GM
1100 draw_shadow_rectangle (mw, ws->window, 0, 0, ws->width, ws->height,
1101 False, False);
07bf635f
RS
1102}
1103
1104\f/* Motion code */
1105static void
d398028f
PR
1106set_new_state (mw, val, level)
1107 XlwMenuWidget mw;
1108 widget_value* val;
1109 int level;
07bf635f
RS
1110{
1111 int i;
1112
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);
1117}
1118
1119static void
d398028f
PR
1120make_windows_if_needed (mw, n)
1121 XlwMenuWidget mw;
1122 int n;
07bf635f
RS
1123{
1124 int i;
1125 int start_at;
1126 XSetWindowAttributes xswa;
1127 int mask;
1128 Window root = RootWindowOfScreen (DefaultScreenOfDisplay (XtDisplay (mw)));
1129 window_state* windows;
1130
1131 if (mw->menu.windows_length >= n)
1132 return;
1133
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 =
d398028f 1139 ExposureMask | PointerMotionMask | PointerMotionHintMask
07bf635f
RS
1140 | ButtonReleaseMask | ButtonPressMask;
1141 xswa.cursor = mw->menu.cursor_shape;
1142 mask = CWSaveUnder | CWOverrideRedirect | CWBackPixel | CWBorderPixel
1143 | CWEventMask | CWCursor;
1144
1145 if (!mw->menu.windows)
1146 {
1147 mw->menu.windows =
1148 (window_state*)XtMalloc (n * sizeof (window_state));
1149 start_at = 0;
1150 }
1151 else
1152 {
1153 mw->menu.windows =
1154 (window_state*)XtRealloc ((char*)mw->menu.windows,
1155 n * sizeof (window_state));
1156 start_at = mw->menu.windows_length;
1157 }
1158 mw->menu.windows_length = n;
1159
1160 windows = mw->menu.windows;
1161
1162 for (i = start_at; i < n; i++)
1163 {
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);
1171 }
1172}
1173
1174/* Make the window fit in the screen */
1175static void
d398028f
PR
1176fit_to_screen (mw, ws, previous_ws, horizontal_p)
1177 XlwMenuWidget mw;
1178 window_state* ws;
1179 window_state* previous_ws;
1180 Boolean horizontal_p;
07bf635f 1181{
ccf589a6
RS
1182 unsigned int screen_width = WidthOfScreen (XtScreen (mw));
1183 unsigned int screen_height = HeightOfScreen (XtScreen (mw));
1ba46f7d
RS
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;
07bf635f
RS
1187
1188 if (ws->x < 0)
1189 ws->x = 0;
1190 else if (ws->x + ws->width > screen_width)
1191 {
1192 if (!horizontal_p)
1193 ws->x = previous_ws->x - ws->width;
1194 else
1195 ws->x = screen_width - ws->width;
525b8232 1196 if (ws->x < 0)
1ba46f7d
RS
1197 {
1198 ws->x = 0;
1199 horizontal_overlap = 1;
1200 }
1201 }
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)
1206 {
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;
07bf635f 1213 }
1ba46f7d 1214
07bf635f
RS
1215 if (ws->y < 0)
1216 ws->y = 0;
1217 else if (ws->y + ws->height > screen_height)
1218 {
1219 if (horizontal_p)
1220 ws->y = previous_ws->y - ws->height;
1221 else
1222 ws->y = screen_height - ws->height;
525b8232
RS
1223 if (ws->y < 0)
1224 ws->y = 0;
07bf635f
RS
1225 }
1226}
1227
1228/* Updates old_stack from new_stack and redisplays. */
1229static void
d398028f
PR
1230remap_menubar (mw)
1231 XlwMenuWidget mw;
07bf635f
RS
1232{
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;
1243
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;
1250
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;
1256
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;
1264
d6fd6371
GM
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);
1269
07bf635f
RS
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;
1275
908ff139 1276 /* refresh the last selection */
07bf635f
RS
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);
1281
1ba46f7d
RS
1282 /* Now place the new menus. */
1283 for (i = last_same + 1; i < new_depth && new_stack[i]->contents; i++)
07bf635f 1284 {
1ba46f7d
RS
1285 window_state *previous_ws = &windows[i - 1];
1286 window_state *ws = &windows[i];
07bf635f 1287
be06a3df
GM
1288 ws->x = (previous_ws->x + selection_position.x
1289 + mw->menu.shadow_thickness);
1290 if (i == 1)
1291 ws->x += mw->menu.margin;
1292
1293#if 0
07bf635f
RS
1294 if (!mw->menu.horizontal || i > 1)
1295 ws->x += mw->menu.shadow_thickness;
be06a3df
GM
1296#endif
1297
1298 ws->y = (previous_ws->y + selection_position.y
1299 + mw->menu.shadow_thickness);
1300 if (i == 1)
1301 ws->y += mw->menu.margin;
07bf635f
RS
1302
1303 size_menu (mw, i);
1304
1305 fit_to_screen (mw, ws, previous_ws, mw->menu.horizontal && i == 1);
1306
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);
1312 }
1313
1314 /* unmap the menus that popped down */
1315 for (i = new_depth - 1; i < old_depth; i++)
1ba46f7d
RS
1316 if (i >= new_depth || !new_stack[i]->contents)
1317 XUnmapWindow (XtDisplay (mw), windows[i].window);
07bf635f
RS
1318}
1319
1320static Boolean
d398028f
PR
1321motion_event_is_in_menu (mw, ev, level, relative_pos)
1322 XlwMenuWidget mw;
1323 XMotionEvent* ev;
1324 int level;
1325 XPoint* relative_pos;
07bf635f
RS
1326{
1327 window_state* ws = &mw->menu.windows [level];
4f08464c
RS
1328 int shadow = level == 0 ? 0 : mw->menu.shadow_thickness;
1329 int x = ws->x + shadow;
1330 int y = ws->y + shadow;
07bf635f
RS
1331 relative_pos->x = ev->x_root - x;
1332 relative_pos->y = ev->y_root - y;
4f08464c
RS
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);
07bf635f
RS
1335}
1336
1337static Boolean
d398028f
PR
1338map_event_to_widget_value (mw, ev, val, level)
1339 XlwMenuWidget mw;
1340 XMotionEvent* ev;
1341 widget_value** val;
1342 int* level;
07bf635f
RS
1343{
1344 int i;
1345 XPoint relative_pos;
1346 window_state* ws;
1347
1348 *val = NULL;
1349
1350 /* Find the window */
1351 for (i = mw->menu.old_depth - 1; i >= 0; i--)
1352 {
1353 ws = &mw->menu.windows [i];
1354 if (ws && motion_event_is_in_menu (mw, ev, i, &relative_pos))
1355 {
1356 display_menu (mw, i, True, NULL, &relative_pos, val, NULL, NULL);
1357
1358 if (*val)
1359 {
1360 *level = i + 1;
1361 return True;
1362 }
1363 }
1364 }
1365 return False;
1366}
1367
1368\f/* Procedures */
1369static void
d398028f
PR
1370make_drawing_gcs (mw)
1371 XlwMenuWidget mw;
07bf635f
RS
1372{
1373 XGCValues xgcv;
1374
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);
1381
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);
1388
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);
1397
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);
1406
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);
1413}
1414
1415static void
d398028f
PR
1416release_drawing_gcs (mw)
1417 XlwMenuWidget mw;
07bf635f
RS
1418{
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;
1430}
1431
1432#define MINL(x,y) ((((unsigned long) (x)) < ((unsigned long) (y))) \
1433 ? ((unsigned long) (x)) : ((unsigned long) (y)))
1434
1435static void
d398028f
PR
1436make_shadow_gcs (mw)
1437 XlwMenuWidget mw;
07bf635f
RS
1438{
1439 XGCValues xgcv;
1440 unsigned long pm = 0;
1441 Display *dpy = XtDisplay ((Widget) mw);
be06a3df 1442 Screen *screen = XtScreen ((Widget) mw);
b0404f9f 1443 Colormap cmap = mw->core.colormap;
07bf635f
RS
1444 XColor topc, botc;
1445 int top_frobbed = 0, bottom_frobbed = 0;
1446
1447 if (mw->menu.top_shadow_color == -1)
1448 mw->menu.top_shadow_color = mw->core.background_pixel;
1449 if (mw->menu.bottom_shadow_color == -1)
1450 mw->menu.bottom_shadow_color = mw->menu.foreground;
1451
1452 if (mw->menu.top_shadow_color == mw->core.background_pixel ||
1453 mw->menu.top_shadow_color == mw->menu.foreground)
1454 {
1455 topc.pixel = mw->core.background_pixel;
1456 XQueryColor (dpy, cmap, &topc);
1457 /* don't overflow/wrap! */
1458 topc.red = MINL (65535, topc.red * 1.2);
1459 topc.green = MINL (65535, topc.green * 1.2);
1460 topc.blue = MINL (65535, topc.blue * 1.2);
be06a3df 1461#ifdef emacs
a57a1605 1462 if (x_alloc_nearest_color_for_widget ((Widget) mw, cmap, &topc))
be06a3df 1463#else
07bf635f 1464 if (XAllocColor (dpy, cmap, &topc))
be06a3df 1465#endif
07bf635f
RS
1466 {
1467 mw->menu.top_shadow_color = topc.pixel;
1468 top_frobbed = 1;
1469 }
1470 }
1471 if (mw->menu.bottom_shadow_color == mw->menu.foreground ||
1472 mw->menu.bottom_shadow_color == mw->core.background_pixel)
1473 {
1474 botc.pixel = mw->core.background_pixel;
1475 XQueryColor (dpy, cmap, &botc);
1476 botc.red *= 0.6;
1477 botc.green *= 0.6;
1478 botc.blue *= 0.6;
be06a3df 1479#ifdef emacs
a57a1605 1480 if (x_alloc_nearest_color_for_widget ((Widget) mw, cmap, &botc))
be06a3df 1481#else
07bf635f 1482 if (XAllocColor (dpy, cmap, &botc))
be06a3df 1483#endif
07bf635f
RS
1484 {
1485 mw->menu.bottom_shadow_color = botc.pixel;
1486 bottom_frobbed = 1;
1487 }
1488 }
1489
1490 if (top_frobbed && bottom_frobbed)
1491 {
1492 int top_avg = ((topc.red / 3) + (topc.green / 3) + (topc.blue / 3));
1493 int bot_avg = ((botc.red / 3) + (botc.green / 3) + (botc.blue / 3));
1494 if (bot_avg > top_avg)
1495 {
1496 Pixel tmp = mw->menu.top_shadow_color;
1497 mw->menu.top_shadow_color = mw->menu.bottom_shadow_color;
1498 mw->menu.bottom_shadow_color = tmp;
1499 }
1500 else if (topc.pixel == botc.pixel)
1501 {
1502 if (botc.pixel == mw->menu.foreground)
fc8fefed
GM
1503 {
1504 x_free_dpy_colors (dpy, screen, cmap,
1505 &mw->menu.top_shadow_color, 1);
1506 mw->menu.top_shadow_color
1507 = x_copy_dpy_color (dpy, cmap, mw->core.background_pixel);
1508 }
07bf635f 1509 else
fc8fefed
GM
1510 {
1511 x_free_dpy_colors (dpy, screen, cmap,
1512 &mw->menu.bottom_shadow_color, 1);
1513 mw->menu.bottom_shadow_color
1514 = x_copy_dpy_color (dpy, cmap, mw->menu.foreground);
1515 }
07bf635f
RS
1516 }
1517 }
1518
1519 if (!mw->menu.top_shadow_pixmap &&
1520 mw->menu.top_shadow_color == mw->core.background_pixel)
1521 {
1522 mw->menu.top_shadow_pixmap = mw->menu.gray_pixmap;
fc8fefed
GM
1523 if (top_frobbed)
1524 x_free_dpy_colors (dpy, screen, cmap, &mw->menu.top_shadow_color, 1);
1525 mw->menu.top_shadow_color = x_copy_dpy_color (dpy, cmap,
1526 mw->menu.foreground);
07bf635f
RS
1527 }
1528 if (!mw->menu.bottom_shadow_pixmap &&
1529 mw->menu.bottom_shadow_color == mw->core.background_pixel)
1530 {
1531 mw->menu.bottom_shadow_pixmap = mw->menu.gray_pixmap;
fc8fefed
GM
1532 if (bottom_frobbed)
1533 x_free_dpy_colors (dpy, screen, cmap,
1534 &mw->menu.bottom_shadow_color, 1);
1535 mw->menu.bottom_shadow_color = x_copy_dpy_color (dpy, cmap,
1536 mw->menu.foreground);
07bf635f
RS
1537 }
1538
1539 xgcv.fill_style = FillStippled;
1540 xgcv.foreground = mw->menu.top_shadow_color;
1541 xgcv.stipple = mw->menu.top_shadow_pixmap;
1542 pm = (xgcv.stipple ? GCStipple|GCFillStyle : 0);
1543 mw->menu.shadow_top_gc = XtGetGC ((Widget)mw, GCForeground | pm, &xgcv);
1544
1545 xgcv.foreground = mw->menu.bottom_shadow_color;
1546 xgcv.stipple = mw->menu.bottom_shadow_pixmap;
1547 pm = (xgcv.stipple ? GCStipple|GCFillStyle : 0);
1548 mw->menu.shadow_bottom_gc = XtGetGC ((Widget)mw, GCForeground | pm, &xgcv);
1549}
1550
1551
1552static void
d398028f
PR
1553release_shadow_gcs (mw)
1554 XlwMenuWidget mw;
07bf635f 1555{
fc8fefed
GM
1556 Display *dpy = XtDisplay ((Widget) mw);
1557 Screen *screen = XtScreen ((Widget) mw);
1558 Colormap cmap = mw->core.colormap;
1559 Pixel px[2];
1560
1561 px[0] = mw->menu.top_shadow_color;
1562 px[1] = mw->menu.bottom_shadow_color;
1563 x_free_dpy_colors (dpy, screen, cmap, px, 2);
07bf635f
RS
1564 XtReleaseGC ((Widget) mw, mw->menu.shadow_top_gc);
1565 XtReleaseGC ((Widget) mw, mw->menu.shadow_bottom_gc);
1566}
1567
1568static void
47d52240 1569XlwMenuInitialize (request, mw, args, num_args)
d398028f 1570 Widget request;
47d52240 1571 XlwMenuWidget mw;
d398028f
PR
1572 ArgList args;
1573 Cardinal *num_args;
07bf635f
RS
1574{
1575 /* Get the GCs and the widget size */
07bf635f
RS
1576 XSetWindowAttributes xswa;
1577 int mask;
1578
1579 Window window = RootWindowOfScreen (DefaultScreenOfDisplay (XtDisplay (mw)));
1580 Display* display = XtDisplay (mw);
1581
d398028f
PR
1582#if 0
1583 widget_value *tem = (widget_value *) XtMalloc (sizeof (widget_value));
1584
1585 /* _XtCreate is freeing the object that was passed to us,
1586 so make a copy that we will actually keep. */
1587 lwlib_bcopy (mw->menu.contents, tem, sizeof (widget_value));
1588 mw->menu.contents = tem;
1589#endif
1590
07bf635f
RS
1591/* mw->menu.cursor = XCreateFontCursor (display, mw->menu.cursor_shape); */
1592 mw->menu.cursor = mw->menu.cursor_shape;
1593
a504c8fa
KH
1594 mw->menu.gray_pixmap
1595 = XCreatePixmapFromBitmapData (display, window, gray_bits,
1596 gray_width, gray_height,
1597 (unsigned long)1, (unsigned long)0, 1);
07bf635f 1598
47d52240
RS
1599 /* I don't understand why this ends up 0 sometimes,
1600 but it does. This kludge works around it.
1601 Can anyone find a real fix? -- rms. */
1602 if (mw->menu.font == 0)
1603 mw->menu.font = xlwmenu_default_font;
1604
07bf635f
RS
1605 make_drawing_gcs (mw);
1606 make_shadow_gcs (mw);
1607
1608 xswa.background_pixel = mw->core.background_pixel;
1609 xswa.border_pixel = mw->core.border_pixel;
1610 mask = CWBackPixel | CWBorderPixel;
1611
1612 mw->menu.popped_up = False;
1613
1614 mw->menu.old_depth = 1;
1615 mw->menu.old_stack = (widget_value**)XtMalloc (sizeof (widget_value*));
1616 mw->menu.old_stack_length = 1;
1617 mw->menu.old_stack [0] = mw->menu.contents;
1618
1619 mw->menu.new_depth = 0;
1620 mw->menu.new_stack = 0;
1621 mw->menu.new_stack_length = 0;
1622 push_new_stack (mw, mw->menu.contents);
1623
1624 mw->menu.windows = (window_state*)XtMalloc (sizeof (window_state));
1625 mw->menu.windows_length = 1;
1626 mw->menu.windows [0].x = 0;
1627 mw->menu.windows [0].y = 0;
1628 mw->menu.windows [0].width = 0;
1629 mw->menu.windows [0].height = 0;
1630 size_menu (mw, 0);
1631
1632 mw->core.width = mw->menu.windows [0].width;
1633 mw->core.height = mw->menu.windows [0].height;
1634}
1635
1636static void
1637XlwMenuClassInitialize ()
1638{
1639}
1640
1641static void
d398028f
PR
1642XlwMenuRealize (w, valueMask, attributes)
1643 Widget w;
1644 Mask *valueMask;
1645 XSetWindowAttributes *attributes;
07bf635f
RS
1646{
1647 XlwMenuWidget mw = (XlwMenuWidget)w;
1648 XSetWindowAttributes xswa;
1649 int mask;
1650
1651 (*xlwMenuWidgetClass->core_class.superclass->core_class.realize)
1652 (w, valueMask, attributes);
1653
1654 xswa.save_under = True;
1655 xswa.cursor = mw->menu.cursor_shape;
1656 mask = CWSaveUnder | CWCursor;
1657 XChangeWindowAttributes (XtDisplay (w), XtWindow (w), mask, &xswa);
1658
1659 mw->menu.windows [0].window = XtWindow (w);
1660 mw->menu.windows [0].x = w->core.x;
1661 mw->menu.windows [0].y = w->core.y;
1662 mw->menu.windows [0].width = w->core.width;
1663 mw->menu.windows [0].height = w->core.height;
1664}
1665
1666/* Only the toplevel menubar/popup is a widget so it's the only one that
1667 receives expose events through Xt. So we repaint all the other panes
1668 when receiving an Expose event. */
1669static void
d398028f
PR
1670XlwMenuRedisplay (w, ev, region)
1671 Widget w;
1672 XEvent* ev;
1673 Region region;
07bf635f
RS
1674{
1675 XlwMenuWidget mw = (XlwMenuWidget)w;
1676 int i;
1677
d398028f
PR
1678 /* If we have a depth beyond 1, it's because a submenu was displayed.
1679 If the submenu has been destroyed, set the depth back to 1. */
1680 if (submenu_destroyed)
1681 {
1682 mw->menu.old_depth = 1;
1683 submenu_destroyed = 0;
1684 }
1685
07bf635f
RS
1686 for (i = 0; i < mw->menu.old_depth; i++)
1687 display_menu (mw, i, False, NULL, NULL, NULL, NULL, NULL);
1688}
1689
1690static void
d398028f
PR
1691XlwMenuDestroy (w)
1692 Widget w;
07bf635f
RS
1693{
1694 int i;
1695 XlwMenuWidget mw = (XlwMenuWidget) w;
1696
d398028f
PR
1697 if (pointer_grabbed)
1698 XtUngrabPointer ((Widget)w, CurrentTime);
1699 pointer_grabbed = 0;
1700
1701 submenu_destroyed = 1;
1702
07bf635f
RS
1703 release_drawing_gcs (mw);
1704 release_shadow_gcs (mw);
1705
1706 /* this doesn't come from the resource db but is created explicitly
1707 so we must free it ourselves. */
1708 XFreePixmap (XtDisplay (mw), mw->menu.gray_pixmap);
1709 mw->menu.gray_pixmap = (Pixmap) -1;
1710
d398028f
PR
1711#if 0
1712 /* Do free mw->menu.contents because nowadays we copy it
1713 during initialization. */
1714 XtFree (mw->menu.contents);
1715#endif
1716
07bf635f
RS
1717 /* Don't free mw->menu.contents because that comes from our creator.
1718 The `*_stack' elements are just pointers into `contents' so leave
1719 that alone too. But free the stacks themselves. */
1720 if (mw->menu.old_stack) XtFree ((char *) mw->menu.old_stack);
1721 if (mw->menu.new_stack) XtFree ((char *) mw->menu.new_stack);
1722
1723 /* Remember, you can't free anything that came from the resource
1724 database. This includes:
1725 mw->menu.cursor
1726 mw->menu.top_shadow_pixmap
1727 mw->menu.bottom_shadow_pixmap
1728 mw->menu.font
1729 Also the color cells of top_shadow_color, bottom_shadow_color,
1730 foreground, and button_foreground will never be freed until this
1731 client exits. Nice, eh?
1732 */
1733
1734 /* start from 1 because the one in slot 0 is w->core.window */
1735 for (i = 1; i < mw->menu.windows_length; i++)
1736 XDestroyWindow (XtDisplay (mw), mw->menu.windows [i].window);
1737 if (mw->menu.windows)
1738 XtFree ((char *) mw->menu.windows);
1739}
1740
1741static Boolean
d398028f
PR
1742XlwMenuSetValues (current, request, new)
1743 Widget current;
1744 Widget request;
1745 Widget new;
07bf635f
RS
1746{
1747 XlwMenuWidget oldmw = (XlwMenuWidget)current;
1748 XlwMenuWidget newmw = (XlwMenuWidget)new;
1749 Boolean redisplay = False;
1750 int i;
1751
1752 if (newmw->menu.contents
1753 && newmw->menu.contents->contents
1754 && newmw->menu.contents->contents->change >= VISIBLE_CHANGE)
1755 redisplay = True;
ba624d0f
RS
1756 /* Do redisplay if the contents are entirely eliminated. */
1757 if (newmw->menu.contents
1758 && newmw->menu.contents->contents == 0
1759 && newmw->menu.contents->change >= VISIBLE_CHANGE)
1760 redisplay = True;
07bf635f
RS
1761
1762 if (newmw->core.background_pixel != oldmw->core.background_pixel
d398028f
PR
1763 || newmw->menu.foreground != oldmw->menu.foreground
1764 || newmw->menu.font != oldmw->menu.font)
07bf635f
RS
1765 {
1766 release_drawing_gcs (newmw);
1767 make_drawing_gcs (newmw);
1768 redisplay = True;
1769
1770 for (i = 0; i < oldmw->menu.windows_length; i++)
1771 {
1772 XSetWindowBackground (XtDisplay (oldmw),
1773 oldmw->menu.windows [i].window,
1774 newmw->core.background_pixel);
1775 /* clear windows and generate expose events */
1776 XClearArea (XtDisplay (oldmw), oldmw->menu.windows[i].window,
1777 0, 0, 0, 0, True);
1778 }
1779 }
1780
1781 return redisplay;
1782}
1783
1784static void
d398028f
PR
1785XlwMenuResize (w)
1786 Widget w;
07bf635f
RS
1787{
1788 XlwMenuWidget mw = (XlwMenuWidget)w;
1789
d398028f
PR
1790 if (mw->menu.popped_up)
1791 {
1792 /* Don't allow the popup menu to resize itself. */
1793 mw->core.width = mw->menu.windows [0].width;
1794 mw->core.height = mw->menu.windows [0].height;
1795 mw->core.parent->core.width = mw->core.width ;
1796 mw->core.parent->core.height = mw->core.height ;
1797 }
1798 else
1799 {
1800 mw->menu.windows [0].width = mw->core.width;
1801 mw->menu.windows [0].height = mw->core.height;
1802 }
07bf635f
RS
1803}
1804
1805\f/* Action procedures */
1806static void
d398028f
PR
1807handle_single_motion_event (mw, ev)
1808 XlwMenuWidget mw;
1809 XMotionEvent* ev;
07bf635f
RS
1810{
1811 widget_value* val;
1812 int level;
1813
1814 if (!map_event_to_widget_value (mw, ev, &val, &level))
1815 pop_new_stack_if_no_contents (mw);
1816 else
1817 set_new_state (mw, val, level);
1818 remap_menubar (mw);
1819
1820 /* Sync with the display. Makes it feel better on X terms. */
1821 XSync (XtDisplay (mw), False);
1822}
1823
1824static void
d398028f
PR
1825handle_motion_event (mw, ev)
1826 XlwMenuWidget mw;
1827 XMotionEvent* ev;
07bf635f
RS
1828{
1829 int x = ev->x_root;
1830 int y = ev->y_root;
1831 int state = ev->state;
1832
1833 handle_single_motion_event (mw, ev);
1834
1835 /* allow motion events to be generated again */
1836 if (ev->is_hint
1837 && XQueryPointer (XtDisplay (mw), ev->window,
1838 &ev->root, &ev->subwindow,
1839 &ev->x_root, &ev->y_root,
1840 &ev->x, &ev->y,
1841 &ev->state)
1842 && ev->state == state
1843 && (ev->x_root != x || ev->y_root != y))
1844 handle_single_motion_event (mw, ev);
1845}
1846
1847static void
d398028f
PR
1848Start (w, ev, params, num_params)
1849 Widget w;
1850 XEvent *ev;
1851 String *params;
1852 Cardinal *num_params;
07bf635f
RS
1853{
1854 XlwMenuWidget mw = (XlwMenuWidget)w;
1855
d398028f
PR
1856 if (!mw->menu.popped_up)
1857 {
1858 menu_post_event = *ev;
a57a1605 1859 pop_up_menu (mw, (XButtonPressedEvent*) ev);
d398028f
PR
1860 }
1861 else
4cc76151
PR
1862 {
1863 /* If we push a button while the menu is posted semipermanently,
1864 releasing the button should always pop the menu down. */
1865 next_release_must_exit = 1;
d398028f 1866
4cc76151
PR
1867 /* notes the absolute position of the menubar window */
1868 mw->menu.windows [0].x = ev->xmotion.x_root - ev->xmotion.x;
1869 mw->menu.windows [0].y = ev->xmotion.y_root - ev->xmotion.y;
07bf635f 1870
4cc76151
PR
1871 /* handles the down like a move, slots are compatible */
1872 handle_motion_event (mw, &ev->xmotion);
1873 }
07bf635f
RS
1874}
1875
1876static void
d398028f
PR
1877Drag (w, ev, params, num_params)
1878 Widget w;
1879 XEvent *ev;
1880 String *params;
1881 Cardinal *num_params;
07bf635f
RS
1882{
1883 XlwMenuWidget mw = (XlwMenuWidget)w;
3c9ce1c4
KH
1884 if (mw->menu.popped_up)
1885 handle_motion_event (mw, &ev->xmotion);
07bf635f
RS
1886}
1887
ba624d0f
RS
1888/* Do nothing.
1889 This is how we handle presses and releases of modifier keys. */
1890static void
1891Nothing (w, ev, params, num_params)
1892 Widget w;
1893 XEvent *ev;
1894 String *params;
1895 Cardinal *num_params;
1896{
1897}
1898
1899/* Handle key press and release events while menu is popped up.
1900 Our action is to get rid of the menu. */
1901static void
1902Key (w, ev, params, num_params)
1903 Widget w;
1904 XEvent *ev;
1905 String *params;
1906 Cardinal *num_params;
1907{
1908 XlwMenuWidget mw = (XlwMenuWidget)w;
1909
1910 /* Pop down everything. */
1911 mw->menu.new_depth = 1;
1912 remap_menubar (mw);
1913
1914 if (mw->menu.popped_up)
1915 {
1916 mw->menu.popped_up = False;
1917 XtUngrabPointer ((Widget)mw, ev->xmotion.time);
1918 if (XtIsShell (XtParent ((Widget) mw)))
1919 XtPopdown (XtParent ((Widget) mw));
1920 else
1921 {
1922 XtRemoveGrab ((Widget) mw);
1923 display_menu (mw, 0, False, NULL, NULL, NULL, NULL, NULL);
1924 }
1925 }
1926
1927 /* callback */
1928 XtCallCallbackList ((Widget)mw, mw->menu.select, (XtPointer)0);
1929}
1930
1931static void
d398028f
PR
1932Select (w, ev, params, num_params)
1933 Widget w;
1934 XEvent *ev;
1935 String *params;
1936 Cardinal *num_params;
07bf635f
RS
1937{
1938 XlwMenuWidget mw = (XlwMenuWidget)w;
1939 widget_value* selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];
1940
d398028f
PR
1941 /* If user releases the button quickly, without selecting anything,
1942 after the initial down-click that brought the menu up,
1943 do nothing. */
1944 if ((selected_item == 0
1945 || ((widget_value *) selected_item)->call_data == 0)
1946 && !next_release_must_exit
1947 && (ev->xbutton.time - menu_post_event.xbutton.time
1948 < XtGetMultiClickTime (XtDisplay (w))))
1949 return;
1950
1951 /* pop down everything. */
07bf635f
RS
1952 mw->menu.new_depth = 1;
1953 remap_menubar (mw);
1954
1955 if (mw->menu.popped_up)
1956 {
1957 mw->menu.popped_up = False;
1958 XtUngrabPointer ((Widget)mw, ev->xmotion.time);
2289f3f4
PR
1959 if (XtIsShell (XtParent ((Widget) mw)))
1960 XtPopdown (XtParent ((Widget) mw));
4cc76151
PR
1961 else
1962 {
1963 XtRemoveGrab ((Widget) mw);
1964 display_menu (mw, 0, False, NULL, NULL, NULL, NULL, NULL);
1965 }
07bf635f
RS
1966 }
1967
1968 /* callback */
1969 XtCallCallbackList ((Widget)mw, mw->menu.select, (XtPointer)selected_item);
07bf635f
RS
1970}
1971
1972
1973\f/* Special code to pop-up a menu */
1974void
d398028f
PR
1975pop_up_menu (mw, event)
1976 XlwMenuWidget mw;
1977 XButtonPressedEvent* event;
07bf635f
RS
1978{
1979 int x = event->x_root;
1980 int y = event->y_root;
1981 int w;
1982 int h;
1983 int borderwidth = mw->menu.shadow_thickness;
1984 Screen* screen = XtScreen (mw);
8400b9ed 1985 Display *display = XtDisplay (mw);
5efb61c7 1986 int count;
07bf635f 1987
d398028f
PR
1988 next_release_must_exit = 0;
1989
07bf635f
RS
1990 XtCallCallbackList ((Widget)mw, mw->menu.open, NULL);
1991
2289f3f4 1992 if (XtIsShell (XtParent ((Widget)mw)))
4cc76151 1993 size_menu (mw, 0);
07bf635f
RS
1994
1995 w = mw->menu.windows [0].width;
1996 h = mw->menu.windows [0].height;
1997
1998 x -= borderwidth;
1999 y -= borderwidth;
2000 if (x < borderwidth)
2001 x = borderwidth;
2002 if (x + w + 2 * borderwidth > WidthOfScreen (screen))
2003 x = WidthOfScreen (screen) - w - 2 * borderwidth;
2004 if (y < borderwidth)
2005 y = borderwidth;
2006 if (y + h + 2 * borderwidth> HeightOfScreen (screen))
2007 y = HeightOfScreen (screen) - h - 2 * borderwidth;
2008
2009 mw->menu.popped_up = True;
2289f3f4 2010 if (XtIsShell (XtParent ((Widget)mw)))
4cc76151 2011 {
2289f3f4
PR
2012 XtConfigureWidget (XtParent ((Widget)mw), x, y, w, h,
2013 XtParent ((Widget)mw)->core.border_width);
2014 XtPopup (XtParent ((Widget)mw), XtGrabExclusive);
4cc76151
PR
2015 display_menu (mw, 0, False, NULL, NULL, NULL, NULL, NULL);
2016 mw->menu.windows [0].x = x + borderwidth;
2017 mw->menu.windows [0].y = y + borderwidth;
2018 }
2019 else
2020 {
2021 XEvent *ev = (XEvent *) event;
2022
87a559bf 2023 XtAddGrab ((Widget) mw, True, True);
4cc76151
PR
2024
2025 /* notes the absolute position of the menubar window */
2026 mw->menu.windows [0].x = ev->xmotion.x_root - ev->xmotion.x;
2027 mw->menu.windows [0].y = ev->xmotion.y_root - ev->xmotion.y;
2028 }
2029
d398028f 2030#ifdef emacs
5efb61c7 2031 count = x_catch_errors (display);
d398028f 2032#endif
87a559bf
PR
2033 XtGrabPointer ((Widget)mw, False,
2034 (PointerMotionMask
2035 | PointerMotionHintMask
2036 | ButtonReleaseMask
2037 | ButtonPressMask),
2038 GrabModeAsync, GrabModeAsync, None,
2039 mw->menu.cursor_shape,
2040 event->time);
2041 pointer_grabbed = 1;
d398028f 2042#ifdef emacs
8400b9ed 2043 if (x_had_errors_p (display))
d398028f
PR
2044 {
2045 pointer_grabbed = 0;
2046 XtUngrabPointer ((Widget)mw, event->time);
2047 }
5efb61c7 2048 x_uncatch_errors (display, count);
d398028f 2049#endif
07bf635f 2050
07bf635f
RS
2051 handle_motion_event (mw, (XMotionEvent*)event);
2052}