comparison lwlib/xlwmenu.c @ 0:376386a54a3c r19-14

Import from CVS: tag r19-14
author cvs
date Mon, 13 Aug 2007 08:45:50 +0200
parents
children ac2d302a0011
comparison
equal deleted inserted replaced
-1:000000000000 0:376386a54a3c
1 /* Implements a lightweight menubar widget.
2 Copyright (C) 1992, 1993, 1994 Lucid, Inc.
3 Copyright (C) 1995 Tinker Systems and INS Engineering Corp.
4
5 This file is part of the Lucid Widget Library.
6
7 The Lucid Widget Library is free software; you can redistribute it and/or
8 modify it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2, or (at your option)
10 any later version.
11
12 The Lucid Widget Library is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with GNU Emacs; see the file COPYING. If not, write to
19 the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
20
21 /* Created by devin@lucid.com */
22
23 #include <stdlib.h>
24 #include <unistd.h>
25 #include <string.h>
26 #include <ctype.h>
27 #include <stdio.h>
28
29 #include <sys/types.h>
30 #include <X11/Xos.h>
31 #include <X11/IntrinsicP.h>
32 #include <X11/StringDefs.h>
33 #include <X11/cursorfont.h>
34 #include <X11/bitmaps/gray>
35
36 #ifdef NEED_MOTIF
37 #include <Xm/Xm.h>
38 #endif
39 #include "xlwmenuP.h"
40
41 static char
42 xlwMenuTranslations [] =
43 "<BtnDown>: start()\n\
44 <BtnMotion>: drag()\n\
45 <BtnUp>: select()\n\
46 ";
47
48 #define offset(field) XtOffset(XlwMenuWidget, field)
49 static XtResource
50 xlwMenuResources[] =
51 {
52 #ifdef NEED_MOTIF
53 /* There are three font list resources, so that we can accept either of
54 the resources *fontList: or *font:, and so that we can tell the
55 difference between them being specified, and being defaulted to a
56 font from the XtRString specified here.
57 */
58 {XmNfontList, XmCFontList, XmRFontList, sizeof(XmFontList),
59 offset(menu.font_list), XtRImmediate, (XtPointer)0},
60 {XtNfont, XtCFont, XmRFontList, sizeof(XmFontList),
61 offset(menu.font_list_2),XtRImmediate, (XtPointer)0},
62 {XmNfontList, XmCFontList, XmRFontList, sizeof(XmFontList),
63 offset(menu.fallback_font_list),
64 /* We must use an iso8859-1 font here, or people without $LANG set lose.
65 It's fair to assume that those who do have $LANG set also have the
66 *fontList resource set, or at least know how to deal with this.
67 */
68 XtRString, "-*-helvetica-bold-r-*-*-*-120-*-*-*-*-iso8859-1"},
69 #else
70 {XtNfont, XtCFont, XtRFontStruct, sizeof(XFontStruct *),
71 offset(menu.font),XtRString, "XtDefaultFont"},
72 #endif
73 {XtNforeground, XtCForeground, XtRPixel, sizeof(Pixel),
74 offset(menu.foreground), XtRString, "XtDefaultForeground"},
75 {XtNbuttonForeground, XtCButtonForeground, XtRPixel, sizeof(Pixel),
76 offset(menu.button_foreground), XtRString, "XtDefaultForeground"},
77 {XtNmargin, XtCMargin, XtRDimension, sizeof(Dimension),
78 offset(menu.margin), XtRImmediate, (XtPointer)2},
79 {XmNmarginWidth, XmCMarginWidth, XmRHorizontalDimension, sizeof(Dimension),
80 offset(menu.horizontal_margin), XtRImmediate, (XtPointer)2},
81 {XmNmarginHeight, XmCMarginHeight, XmRVerticalDimension, sizeof(Dimension),
82 offset(menu.vertical_margin), XtRImmediate, (XtPointer)1},
83 {XmNspacing, XmCSpacing, XmRHorizontalDimension, sizeof(Dimension),
84 offset(menu.column_spacing), XtRImmediate, (XtPointer)4},
85 {XmNindicatorSize, XmCIndicatorSize, XtRDimension, sizeof(Dimension),
86 offset(menu.indicator_size), XtRImmediate, (XtPointer)0},
87 {XmNshadowThickness, XmCShadowThickness, XmRHorizontalDimension,
88 sizeof (Dimension), offset (menu.shadow_thickness),
89 XtRImmediate, (XtPointer) 2},
90 {XmNselectColor, XmCSelectColor, XtRPixel, sizeof (Pixel),
91 offset (menu.select_color), XtRImmediate, (XtPointer)-1},
92 {XmNtopShadowColor, XmCTopShadowColor, XtRPixel, sizeof (Pixel),
93 offset (menu.top_shadow_color), XtRImmediate, (XtPointer)-1},
94 {XmNbottomShadowColor, XmCBottomShadowColor, XtRPixel, sizeof (Pixel),
95 offset (menu.bottom_shadow_color), XtRImmediate, (XtPointer)-1},
96 {XmNtopShadowPixmap, XmCTopShadowPixmap, XtRPixmap, sizeof (Pixmap),
97 offset (menu.top_shadow_pixmap), XtRImmediate, (XtPointer)None},
98 {XmNbottomShadowPixmap, XmCBottomShadowPixmap, XtRPixmap, sizeof (Pixmap),
99 offset (menu.bottom_shadow_pixmap), XtRImmediate, (XtPointer)None},
100
101 {XtNopen, XtCCallback, XtRCallback, sizeof(XtPointer),
102 offset(menu.open), XtRCallback, (XtPointer)NULL},
103 {XtNselect, XtCCallback, XtRCallback, sizeof(XtPointer),
104 offset(menu.select), XtRCallback, (XtPointer)NULL},
105 {XtNmenu, XtCMenu, XtRPointer, sizeof(XtPointer),
106 offset(menu.contents), XtRImmediate, (XtPointer)NULL},
107 {XtNcursor, XtCCursor, XtRCursor, sizeof(Cursor),
108 offset(menu.cursor_shape), XtRString, (XtPointer)"right_ptr"},
109 {XtNhorizontal, XtCHorizontal, XtRInt, sizeof(int),
110 offset(menu.horizontal), XtRImmediate, (XtPointer)True},
111 {XtNuseBackingStore, XtCUseBackingStore, XtRBoolean, sizeof (Boolean),
112 offset (menu.use_backing_store), XtRImmediate, (XtPointer)False},
113 {XtNbounceDown, XtCBounceDown, XtRBoolean, sizeof (Boolean),
114 offset (menu.bounce_down), XtRImmediate, (XtPointer)True},
115 {XtNresourceLabels, XtCResourceLabels, XtRBoolean, sizeof (Boolean),
116 offset (menu.lookup_labels), XtRImmediate, (XtPointer)False},
117 };
118 #undef offset
119
120 static Boolean XlwMenuSetValues (Widget current, Widget request, Widget new,
121 ArgList args, Cardinal *num_args);
122 static void XlwMenuRealize (Widget w, Mask *valueMask,
123 XSetWindowAttributes *attributes);
124 static void XlwMenuRedisplay (Widget w, XEvent *ev, Region region);
125 static void XlwMenuResize (Widget w);
126 static void XlwMenuInitialize (Widget request, Widget new, ArgList args,
127 Cardinal *num_args);
128 static void XlwMenuDestroy (Widget w);
129 static void XlwMenuClassInitialize (void);
130 static void Start (Widget w, XEvent *ev, String *params, Cardinal *num_params);
131 static void Drag (Widget w, XEvent *ev, String *params, Cardinal *num_params);
132 static void Select (Widget w, XEvent *ev, String *params,
133 Cardinal *num_params);
134
135 #ifdef NEED_MOTIF
136 static XFontStruct *default_font_of_font_list (XmFontList);
137 #endif
138
139 static XtActionsRec
140 xlwMenuActionsList [] =
141 {
142 {"start", Start},
143 {"drag", Drag},
144 {"select", Select},
145 };
146
147 #define SuperClass ((CoreWidgetClass)&coreClassRec)
148
149 XlwMenuClassRec xlwMenuClassRec =
150 {
151 { /* CoreClass fields initialization */
152 (WidgetClass) SuperClass, /* superclass */
153 "XlwMenu", /* class_name */
154 sizeof(XlwMenuRec), /* size */
155 XlwMenuClassInitialize, /* class_initialize */
156 NULL, /* class_part_initialize */
157 FALSE, /* class_inited */
158 XlwMenuInitialize, /* initialize */
159 NULL, /* initialize_hook */
160 XlwMenuRealize, /* realize */
161 xlwMenuActionsList, /* actions */
162 XtNumber(xlwMenuActionsList), /* num_actions */
163 xlwMenuResources, /* resources */
164 XtNumber(xlwMenuResources), /* resource_count */
165 NULLQUARK, /* xrm_class */
166 TRUE, /* compress_motion */
167 TRUE, /* compress_exposure */
168 TRUE, /* compress_enterleave */
169 FALSE, /* visible_interest */
170 XlwMenuDestroy, /* destroy */
171 XlwMenuResize, /* resize */
172 XlwMenuRedisplay, /* expose */
173 XlwMenuSetValues, /* set_values */
174 NULL, /* set_values_hook */
175 XtInheritSetValuesAlmost, /* set_values_almost */
176 NULL, /* get_values_hook */
177 NULL, /* #### - should this be set for grabs? accept_focus */
178 XtVersion, /* version */
179 NULL, /* callback_private */
180 xlwMenuTranslations, /* tm_table */
181 XtInheritQueryGeometry, /* query_geometry */
182 XtInheritDisplayAccelerator, /* display_accelerator */
183 NULL /* extension */
184 }, /* XlwMenuClass fields initialization */
185 {
186 0 /* dummy */
187 },
188 };
189
190 WidgetClass xlwMenuWidgetClass = (WidgetClass) &xlwMenuClassRec;
191
192 /* Utilities */
193 #if 0 /* Apparently not used anywhere */
194
195 static char *
196 safe_strdup (char *s)
197 {
198 char *result;
199 if (! s) return 0;
200 result = (char *) malloc (strlen (s) + 1);
201 if (! result)
202 return 0;
203 strcpy (result, s);
204 return result;
205 }
206
207 #endif /* 0 */
208
209 /* Replacement for XAllocColor() that tries to return the nearest
210 available color if the colormap is full. From FSF Emacs. */
211
212 static int
213 allocate_nearest_color (Display *display, Colormap screen_colormap,
214 XColor *color_def)
215 {
216 int status;
217
218 status = XAllocColor (display, screen_colormap, color_def);
219 if (!status)
220 {
221 /* If we got to this point, the colormap is full, so we're
222 going to try and get the next closest color.
223 The algorithm used is a least-squares matching, which is
224 what X uses for closest color matching with StaticColor visuals. */
225
226 XColor *cells;
227 int no_cells;
228 int nearest;
229 long nearest_delta, trial_delta;
230 int x;
231
232 no_cells = XDisplayCells (display, XDefaultScreen (display));
233 /* Don't use alloca here because lwlib doesn't have the
234 necessary configuration information that src does. */
235 cells = (XColor *) malloc (sizeof (XColor) * no_cells);
236
237 for (x = 0; x < no_cells; x++)
238 cells[x].pixel = x;
239
240 XQueryColors (display, screen_colormap, cells, no_cells);
241 nearest = 0;
242 /* I'm assuming CSE so I'm not going to condense this. */
243 nearest_delta = ((((color_def->red >> 8) - (cells[0].red >> 8))
244 * ((color_def->red >> 8) - (cells[0].red >> 8)))
245 +
246 (((color_def->green >> 8) - (cells[0].green >> 8))
247 * ((color_def->green >> 8) - (cells[0].green >> 8)))
248 +
249 (((color_def->blue >> 8) - (cells[0].blue >> 8))
250 * ((color_def->blue >> 8) - (cells[0].blue >> 8))));
251 for (x = 1; x < no_cells; x++)
252 {
253 trial_delta = ((((color_def->red >> 8) - (cells[x].red >> 8))
254 * ((color_def->red >> 8) - (cells[x].red >> 8)))
255 +
256 (((color_def->green >> 8) - (cells[x].green >> 8))
257 * ((color_def->green >> 8) - (cells[x].green >> 8)))
258 +
259 (((color_def->blue >> 8) - (cells[x].blue >> 8))
260 * ((color_def->blue >> 8) - (cells[x].blue >> 8))));
261 if (trial_delta < nearest_delta)
262 {
263 nearest = x;
264 nearest_delta = trial_delta;
265 }
266 }
267 color_def->red = cells[nearest].red;
268 color_def->green = cells[nearest].green;
269 color_def->blue = cells[nearest].blue;
270 status = XAllocColor (display, screen_colormap, color_def);
271
272 free (cells);
273 }
274
275 return status;
276 }
277
278 static void
279 push_new_stack (XlwMenuWidget mw, widget_value *val)
280 {
281 if (!mw->menu.new_stack)
282 {
283 mw->menu.new_stack_length = 10;
284 mw->menu.new_stack =
285 (widget_value**)XtCalloc (mw->menu.new_stack_length,
286 sizeof (widget_value*));
287 }
288 else if (mw->menu.new_depth == mw->menu.new_stack_length)
289 {
290 mw->menu.new_stack_length *= 2;
291 mw->menu.new_stack =
292 (widget_value**)XtRealloc ((char*)mw->menu.new_stack,
293 mw->menu.new_stack_length *
294 sizeof (widget_value*));
295 }
296 mw->menu.new_stack [mw->menu.new_depth++] = val;
297 }
298
299 static void
300 pop_new_stack_if_no_contents (XlwMenuWidget mw)
301 {
302 if (mw->menu.new_depth)
303 {
304 if (!mw->menu.new_stack [mw->menu.new_depth - 1]->contents)
305 {
306 mw->menu.new_depth -= 1;
307 }
308 }
309 }
310
311 static void
312 make_old_stack_space (XlwMenuWidget mw, int n)
313 {
314 if (!mw->menu.old_stack)
315 {
316 mw->menu.old_stack_length = 10;
317 mw->menu.old_stack =
318 (widget_value**)XtCalloc (mw->menu.old_stack_length,
319 sizeof (widget_value*));
320 }
321 else if (mw->menu.old_stack_length < n)
322 {
323 mw->menu.old_stack_length *= 2;
324 mw->menu.old_stack =
325 (widget_value**)XtRealloc ((char*)mw->menu.old_stack,
326 mw->menu.old_stack_length *
327 sizeof (widget_value*));
328 }
329 }
330
331 static Boolean
332 close_to_reference_time(Widget w, Time reference_time, XEvent *ev)
333 {
334 return (reference_time &&
335 (ev->xbutton.time - reference_time
336 < XtGetMultiClickTime (XtDisplay (w))));
337 }
338
339 /* Size code */
340 static int
341 string_width (XlwMenuWidget mw,
342 #ifdef NEED_MOTIF
343 XmString s
344 #else
345 char *s
346 #endif
347 )
348 {
349 #ifdef NEED_MOTIF
350 Dimension width, height;
351 XmStringExtent (mw->menu.font_list, s, &width, &height);
352 return width;
353 #else
354 XCharStruct xcs;
355 int drop;
356 XTextExtents (mw->menu.font, s, strlen (s), &drop, &drop, &drop, &xcs);
357 return xcs.width;
358 #endif
359 }
360
361 static void
362 massage_resource_name (CONST char *in, char *out)
363 {
364 /* Turn a random string into something suitable for using as a resource.
365 For example:
366
367 "Kill Buffer" -> "killBuffer"
368 "Find File..." -> "findFile"
369 "Search and Replace..." -> "searchAndReplace"
370 */
371
372 # define GOOD_CHAR(c) (((c) >= 'a' && (c) <= 'z') || \
373 ((c) >= 'A' && (c) <= 'Z') || \
374 ((c) >= '0' && (c) <= '9') || \
375 ((c) == '_') || \
376 ((c) > 0240))
377 int firstp = 1;
378 while (*in)
379 {
380 if (GOOD_CHAR ((unsigned char) *in))
381 {
382 if (firstp)
383 *out = tolower (*in);
384 else
385 *out = toupper (*in);
386 firstp = 0;
387 in++;
388 out++;
389 while (GOOD_CHAR ((unsigned char) *in))
390 {
391 *out = *in;
392 in++;
393 out++;
394 }
395 }
396 else
397 {
398 /* A bogus char between words; skip it. */
399 in++;
400 }
401 }
402 *out = 0;
403 #undef GOOD_CHAR
404 }
405
406 static XtResource
407 nameResource[] =
408 {
409 { "labelString", "LabelString", XtRString, sizeof(String),
410 0, XtRImmediate, 0 }
411 };
412
413 /*
414 * This function looks through string searching for parameter
415 * inserts of the form:
416 * %[padding]1
417 * padding is space (' ') or dash ('-') characters meaning
418 * padding to the left or right of the inserted parameter.
419 * In essence all %1 strings are replaced by value in the return
420 * value (which the caller is expected to free).
421 * %% means insert one % (like printf).
422 * %1 means insert value.
423 * %-1 means insert value followed by one space. The latter is
424 * not inserted if value is a zero length string.
425 */
426 static char*
427 parameterize_string (CONST char *string, CONST char *value)
428 {
429 char *percent;
430 char *result;
431 unsigned done = 0;
432 unsigned ntimes;
433
434 if (!string)
435 {
436 result = XtMalloc(1);
437 result[0] = '\0';
438 return (result);
439 }
440
441 if (!value)
442 value = "";
443
444 for (ntimes = 1, result = (char *) string; (percent = strchr(result, '%'));
445 ntimes++)
446 result = &percent[1];
447
448 result = XtMalloc((ntimes * strlen(value)) + strlen(string) + 4);
449 result[0] = '\0';
450
451 while ((percent = strchr(string, '%')))
452 {
453 unsigned left_pad;
454 unsigned right_pad;
455 char *p;
456
457 if (percent[1] == '%')
458 { /* it's a real % */
459 strncat(result, string, 1 + percent - string); /* incl % */
460 string = &percent[2]; /* after the second '%' */
461 continue; /* with the while() loop */
462 }
463
464 left_pad = 0;
465 right_pad = 0;
466
467 for (p = &percent[1]; /* test *p inside the loop */ ; p++)
468 {
469 if (*p == ' ')
470 { /* left pad */
471 left_pad++;
472 }
473 else if (*p == '-')
474 { /* right pad */
475 right_pad++;
476 }
477 else if (*p == '1')
478 { /* param and terminator */
479 strncat(result, string, percent - string);
480 if (value[0] != '\0')
481 {
482 unsigned i;
483 for (i = 0; i < left_pad; i++)
484 strcat(result, " ");
485 strcat(result, value);
486 for (i = 0; i < right_pad; i++)
487 strcat(result, " ");
488 }
489 string = &p[1]; /* after the '1' */
490 done++; /* no need to do old way */
491 break; /* out of for() loop */
492 }
493 else
494 { /* bogus, copy the format as is */
495 /* out of for() loop */
496 strncat(result, string, 1 + p - string);
497 string= (*p ? &p[1] : p);
498 break;
499 }
500 }
501 }
502
503 /*
504 * Copy the tail of the string
505 */
506 strcat(result, string);
507
508 /*
509 * If we have not processed a % string, and we have a value, tail it.
510 */
511 if (!done && value[0] != '\0')
512 {
513 strcat(result, " ");
514 strcat(result, value);
515 }
516
517 return result;
518 }
519
520 #ifdef NEED_MOTIF
521
522 static XmString
523 resource_widget_value (XlwMenuWidget mw, widget_value *val)
524 {
525 if (!val->toolkit_data)
526 {
527 char *resourced_name = NULL;
528 char *converted_name, *str;
529 XmString complete_name;
530 char massaged_name [1024];
531
532 if (mw->menu.lookup_labels)
533 {
534 /* Convert value style name into resource style name.
535 eg: "Free Willy" becomes "freeWilly" */
536 massage_resource_name (val->name, massaged_name);
537
538 /* If we have a value (parameter) see if we can find a "Named"
539 resource. */
540 if (val->value)
541 {
542 char named_name[1024];
543 sprintf(named_name, "%sNamed", massaged_name);
544 XtGetSubresources ((Widget) mw,
545 (XtPointer) &resourced_name,
546 named_name, named_name,
547 nameResource, 1, NULL, 0);
548 }
549
550 /* If nothing yet, try to load from the massaged name. */
551 if (!resourced_name)
552 {
553 XtGetSubresources ((Widget) mw,
554 (XtPointer) &resourced_name,
555 massaged_name, massaged_name,
556 nameResource, 1, NULL, 0);
557 }
558 } /* if (mw->menu.lookup_labels) */
559
560 /* Still nothing yet, use the name as the value. */
561 if (!resourced_name)
562 resourced_name = val->name;
563
564 /* Parameterize the string. */
565 converted_name = parameterize_string(resourced_name, val->value);
566
567 /* nuke newline characters to prevent menubar screwups */
568 for ( str = converted_name ; *str ; str++ )
569 {
570 if (str[0] == '\n') str[0] = ' ';
571 }
572
573 /* Improve OSF's bottom line. */
574 #if (XmVersion >= 1002)
575 complete_name = XmStringCreateLocalized (converted_name);
576 #else
577 complete_name = XmStringCreateLtoR (converted_name,
578 XmSTRING_DEFAULT_CHARSET);
579 #endif
580 XtFree (converted_name);
581
582 val->toolkit_data = complete_name;
583 val->free_toolkit_data = True;
584 }
585 return ((XmString) val->toolkit_data);
586 }
587
588 /* Unused */
589 #if 0
590 /*
591 * These two routines should be a seperate file..djw
592 */
593 static char *
594 xlw_create_localized_string (Widget w,
595 char *name,
596 char **args,
597 unsigned nargs)
598 {
599 char *string = NULL;
600 char *arg = NULL;
601
602 if (nargs > 0)
603 arg = args[0];
604
605 XtGetSubresources (w,
606 (XtPointer)&string,
607 name,
608 name,
609 nameResource, 1,
610 NULL, 0
611 );
612
613 if (!string)
614 string = name;
615
616 return parameterize_string (string, arg);
617 }
618
619 static XmString
620 xlw_create_localized_xmstring (Widget w,
621 char *name,
622 char **args,
623 unsigned nargs)
624 {
625 char * string = xlw_create_localized_string (w, name, args, nargs);
626 XmString xm_string = XmStringCreateLtoR (string, XmSTRING_DEFAULT_CHARSET);
627 XtFree(string);
628 return xm_string;
629 }
630 #endif /* 0 */
631
632 #else /* !Motif */
633
634 static char*
635 resource_widget_value (XlwMenuWidget mw, widget_value *val)
636 {
637 if (!val->toolkit_data)
638 {
639 char *resourced_name = NULL;
640 char *complete_name;
641 char massaged_name [1024];
642
643 if (mw->menu.lookup_labels)
644 {
645 massage_resource_name (val->name, massaged_name);
646
647 XtGetSubresources ((Widget) mw,
648 (XtPointer) &resourced_name,
649 massaged_name, massaged_name,
650 nameResource, 1, NULL, 0);
651 }
652 if (!resourced_name)
653 resourced_name = val->name;
654
655 complete_name = parameterize_string(resourced_name, val->value);
656
657 val->toolkit_data = complete_name;
658 /* nuke newline characters to prevent menubar screwups */
659 for ( ; *complete_name ; complete_name++ )
660 {
661 if (complete_name[0] == '\n')
662 complete_name[0] = ' ';
663 }
664 val->free_toolkit_data = True;
665 }
666 return (char*)val->toolkit_data;
667 }
668
669 #endif /* !Motif */
670
671 /*
672 * Code for drawing strings.
673 */
674 static void
675 string_draw(
676 XlwMenuWidget mw,
677 Window window,
678 int x, int y,
679 GC gc,
680 #ifdef NEED_MOTIF
681 XmString string
682 #else
683 char *string
684 #endif
685 ) {
686 #ifdef NEED_MOTIF
687 XmStringDraw (XtDisplay (mw), window,
688 mw->menu.font_list,
689 string, gc,
690 x, y,
691 1000, /* ???? width */
692 XmALIGNMENT_BEGINNING,
693 0, /* ???? layout_direction */
694 0);
695 #else
696 XDrawString (XtDisplay (mw), window, gc,
697 x, y + mw->menu.font_ascent, string, strlen (string));
698
699 #endif
700 }
701
702 static void
703 binding_draw (XlwMenuWidget mw, Window w, int x, int y, GC gc, char *value)
704 {
705 #ifdef NEED_MOTIF
706 XmString xm_value = XmStringCreateLtoR(value, XmSTRING_DEFAULT_CHARSET);
707 string_draw (mw, w, x, y, gc, xm_value);
708 XmStringFree (xm_value);
709 #else
710 string_draw (mw, w, x, y, gc, value);
711 #endif
712 }
713
714 /*
715 * Low level code for drawing 3-D edges.
716 */
717 static void
718 shadow_rectangle_draw (
719 Display *dpy,
720 Window window,
721 GC top_gc,
722 GC bottom_gc,
723 int x, int y, unsigned width, unsigned height,
724 unsigned thickness
725 )
726 {
727 XPoint points [4];
728
729 if (!thickness)
730 return;
731
732 points [0].x = x;
733 points [0].y = y;
734 points [1].x = x + width;
735 points [1].y = y;
736 points [2].x = x + width - thickness;
737 points [2].y = y + thickness;
738 points [3].x = x;
739 points [3].y = y + thickness;
740 XFillPolygon (dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
741 points [0].x = x;
742 points [0].y = y + thickness;
743 points [1].x = x;
744 points [1].y = y + height;
745 points [2].x = x + thickness;
746 points [2].y = y + height - thickness;
747 points [3].x = x + thickness;
748 points [3].y = y + thickness;
749 XFillPolygon (dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
750 points [0].x = x + width;
751 points [0].y = y;
752 points [1].x = x + width - thickness;
753 points [1].y = y + thickness;
754 points [2].x = x + width - thickness;
755 points [2].y = y + height - thickness;
756 points [3].x = x + width;
757 points [3].y = y + height - thickness;
758 XFillPolygon (dpy, window, bottom_gc, points, 4, Convex, CoordModeOrigin);
759 points [0].x = x;
760 points [0].y = y + height;
761 points [1].x = x + width;
762 points [1].y = y + height;
763 points [2].x = x + width;
764 points [2].y = y + height - thickness;
765 points [3].x = x + thickness;
766 points [3].y = y + height - thickness;
767 XFillPolygon (dpy, window, bottom_gc, points, 4, Convex, CoordModeOrigin);
768 }
769
770 typedef enum e_shadow_type
771 {
772 /* these are Motif compliant */
773 SHADOW_BACKGROUND,
774 SHADOW_OUT,
775 SHADOW_IN,
776 SHADOW_ETCHED_OUT,
777 SHADOW_ETCHED_IN,
778 SHADOW_ETCHED_OUT_DASH,
779 SHADOW_ETCHED_IN_DASH,
780 SHADOW_SINGLE_LINE,
781 SHADOW_DOUBLE_LINE,
782 SHADOW_SINGLE_DASHED_LINE,
783 SHADOW_DOUBLE_DASHED_LINE,
784 SHADOW_NO_LINE,
785 /* these are all non-Motif */
786 SHADOW_DOUBLE_ETCHED_OUT,
787 SHADOW_DOUBLE_ETCHED_IN,
788 SHADOW_DOUBLE_ETCHED_OUT_DASH,
789 SHADOW_DOUBLE_ETCHED_IN_DASH
790 } shadow_type;
791
792 static void
793 shadow_draw (XlwMenuWidget mw,
794 Window window,
795 int x, int y, unsigned width, unsigned height,
796 shadow_type type
797 )
798 {
799 Display *dpy = XtDisplay (mw);
800 GC top_gc;
801 GC bottom_gc;
802 int thickness = mw->menu.shadow_thickness;
803 #if 0
804 XPoint points [4];
805 #endif /* 0 */
806 Boolean etched = False;
807
808 switch (type)
809 {
810 case SHADOW_BACKGROUND:
811 top_gc = bottom_gc = mw->menu.background_gc;
812 break;
813 case SHADOW_ETCHED_IN:
814 top_gc = mw->menu.shadow_bottom_gc;
815 bottom_gc = mw->menu.shadow_top_gc;
816 etched = True;
817 break;
818 case SHADOW_ETCHED_OUT:
819 top_gc = mw->menu.shadow_top_gc;
820 bottom_gc = mw->menu.shadow_bottom_gc;
821 etched = True;
822 break;
823 case SHADOW_IN:
824 top_gc = mw->menu.shadow_bottom_gc;
825 bottom_gc = mw->menu.shadow_top_gc;
826 break;
827 case SHADOW_OUT:
828 default:
829 top_gc = mw->menu.shadow_top_gc;
830 bottom_gc = mw->menu.shadow_bottom_gc;
831 break;
832 }
833
834 if (etched)
835 {
836 unsigned half = thickness/2;
837 shadow_rectangle_draw (
838 dpy,
839 window,
840 top_gc,
841 top_gc,
842 x, y,
843 width - half, height - half,
844 thickness - half
845 );
846 shadow_rectangle_draw (
847 dpy,
848 window,
849 bottom_gc,
850 bottom_gc,
851 x + half, y + half,
852 width - half , height - half,
853 half
854 );
855 }
856 else
857 {
858 shadow_rectangle_draw (
859 dpy,
860 window,
861 top_gc,
862 bottom_gc,
863 x, y,
864 width, height,
865 thickness
866 );
867 }
868 }
869
870 static void
871 arrow_decoration_draw (
872 XlwMenuWidget mw,
873 Window window,
874 int x, int y,
875 unsigned width,
876 Boolean raised
877 )
878 {
879 Display *dpy = XtDisplay (mw);
880 GC top_gc;
881 GC bottom_gc;
882 GC select_gc;
883 int thickness = mw->menu.shadow_thickness;
884 XPoint points [4];
885 int half_width;
886 int length = (int)((double)width * 0.87);
887 int thick_med = (int)((double)thickness * 1.73);
888
889 if (width & 0x1)
890 half_width = width/2 + 1;
891 else
892 half_width = width/2;
893
894 select_gc = mw->menu.background_gc;
895
896 if (raised)
897 {
898 top_gc = mw->menu.shadow_bottom_gc;
899 bottom_gc = mw->menu.shadow_top_gc;
900 }
901 else
902 {
903 top_gc = mw->menu.shadow_top_gc;
904 bottom_gc = mw->menu.shadow_bottom_gc;
905 }
906
907 /*
908 * Fill internal area, we do this first so that the borders
909 * have a nice sharp edge.
910 */
911 points [0].x = x + thickness;
912 points [0].y = y + thickness;
913 points [1].x = x + length - thickness;
914 points [1].y = y + half_width;
915 points [2].x = x + length - thickness;
916 points [2].y = y + half_width + thickness;
917 points [3].x = x + thickness;
918 points [3].y = y + width - thickness;
919
920 XFillPolygon (
921 dpy,
922 window,
923 select_gc,
924 points,
925 4,
926 Convex,
927 CoordModeOrigin
928 );
929
930 /* left border */
931 points [0].x = x;
932 points [0].y = y;
933 points [1].x = x + thickness;
934 points [1].y = y + thick_med;
935 points [2].x = x + thickness;
936 points [2].y = y + width - thick_med;
937 points [3].x = x;
938 points [3].y = y + width;
939
940 XFillPolygon (dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
941
942 /* top border */
943 points [0].x = x;
944 points [0].y = y + width;
945 points [1].x = x + length;
946 points [1].y = y + half_width;
947 points [2].x = x + length - (thickness + thickness);
948 points [2].y = y + half_width;
949 points [3].x = x + thickness;
950 points [3].y = y + width - thick_med;
951
952 XFillPolygon (dpy, window, bottom_gc, points, 4, Convex, CoordModeOrigin);
953
954 /* bottom shadow */
955 points [0].x = x;
956 points [0].y = y;
957 points [1].x = x + length;
958 points [1].y = y + half_width;
959 points [2].x = x + length - (thickness + thickness);
960 points [2].y = y + half_width;
961 points [3].x = x + thickness;
962 points [3].y = y + thick_med;
963
964 XFillPolygon (dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
965 }
966
967 static void
968 toggle_decoration_draw (
969 XlwMenuWidget mw,
970 Window window,
971 int x, int y,
972 unsigned width,
973 Boolean set
974 )
975 {
976 Display *dpy = XtDisplay (mw);
977 int thickness = mw->menu.shadow_thickness;
978 shadow_type type;
979 GC select_gc = mw->menu.select_gc;
980
981 if (set)
982 type = SHADOW_IN;
983 else
984 type = SHADOW_OUT;
985
986 /*
987 * Fill internal area.
988 */
989 if (set)
990 XFillRectangle (
991 dpy,
992 window,
993 select_gc,
994 x + thickness,
995 y + thickness,
996 width - (2*thickness),
997 width - (2*thickness)
998 );
999
1000 shadow_draw(mw, window, x, y, width, width, type);
1001 }
1002
1003 static void
1004 radio_decoration_draw (
1005 XlwMenuWidget mw,
1006 Window window,
1007 int x, int y,
1008 unsigned width,
1009 Boolean enabled
1010 )
1011 {
1012 Display *dpy = XtDisplay (mw);
1013 GC top_gc;
1014 GC bottom_gc;
1015 GC select_gc = mw->menu.select_gc;
1016 int thickness = mw->menu.shadow_thickness;
1017 XPoint points[6];
1018 int half_width;
1019 #if 0
1020 int npoints;
1021 #endif /* 0 */
1022
1023 if (width & 0x1)
1024 width++;
1025
1026 half_width = width/2;
1027
1028 if (enabled)
1029 {
1030 top_gc = mw->menu.shadow_bottom_gc;
1031 bottom_gc = mw->menu.shadow_top_gc;
1032 }
1033 else
1034 {
1035 top_gc = mw->menu.shadow_top_gc;
1036 bottom_gc = mw->menu.shadow_bottom_gc;
1037 }
1038
1039 #if 1
1040 /*
1041 * Draw the bottom first, just incase the regions overlap.
1042 * The top should cast the longer shadow.
1043 */
1044 points [0].x = x; /* left corner */
1045 points [0].y = y + half_width;
1046 points [1].x = x + half_width; /* bottom corner */
1047 points [1].y = y + width;
1048 points [2].x = x + half_width; /* bottom inside corner */
1049 points [2].y = y + width - thickness;
1050 points [3].x = x + thickness; /* left inside corner */
1051 points [3].y = y + half_width;
1052
1053 XFillPolygon (dpy, window, bottom_gc, points, 4, Convex, CoordModeOrigin);
1054
1055 points [0].x = x + half_width; /* bottom corner */
1056 points [0].y = y + width;
1057 points [1].x = x + width; /* right corner */
1058 points [1].y = y + half_width;
1059 points [2].x = x + width - thickness; /* right inside corner */
1060 points [2].y = y + half_width;
1061 points [3].x = x + half_width; /* bottom inside corner */
1062 points [3].y = y + width - thickness;
1063
1064 XFillPolygon (dpy, window, bottom_gc, points, 4, Convex, CoordModeOrigin);
1065
1066 points [0].x = x; /* left corner */
1067 points [0].y = y + half_width;
1068 points [1].x = x + half_width; /* top corner */
1069 points [1].y = y;
1070 points [2].x = x + half_width; /* top inside corner */
1071 points [2].y = y + thickness;
1072 points [3].x = x + thickness; /* left inside corner */
1073 points [3].y = y + half_width;
1074
1075 XFillPolygon (dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
1076
1077 points [0].x = x + half_width; /* top corner */
1078 points [0].y = y;
1079 points [1].x = x + width; /* right corner */
1080 points [1].y = y + half_width;
1081 points [2].x = x + width - thickness; /* right inside corner */
1082 points [2].y = y + half_width;
1083 points [3].x = x + half_width; /* top inside corner */
1084 points [3].y = y + thickness;
1085
1086 XFillPolygon (dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
1087 #else
1088 /*
1089 * Draw the bottom first, just incase the regions overlap.
1090 * The top should cast the longer shadow.
1091 */
1092 npoints = 0;
1093 points [npoints].x = x; /* left corner */
1094 points [npoints++].y = y + half_width;
1095 points [npoints].x = x + half_width; /* bottom corner */
1096 points [npoints++].y = y + width;
1097 points [npoints].x = x + width; /* right corner */
1098 points [npoints++].y = y + half_width;
1099 points [npoints].x = x + width - thickness; /* right inside corner */
1100 points [npoints++].y = y + half_width;
1101 points [npoints].x = x + half_width; /* bottom inside corner */
1102 points [npoints++].y = y + width - thickness;
1103 points [npoints].x = x + thickness; /* left inside corner */
1104 points [npoints++].y = y + half_width;
1105
1106 XFillPolygon (dpy, window, bottom_gc,
1107 points, npoints, Nonconvex, CoordModeOrigin);
1108
1109 npoints = 0;
1110
1111 points [npoints].x = x; /* left corner */
1112 points [npoints++].y = y + half_width;
1113 points [npoints].x = x + half_width; /* top corner */
1114 points [npoints++].y = y;
1115 points [npoints].x = x + width; /* right corner */
1116 points [npoints++].y = y + half_width;
1117 points [npoints].x = x + width - thickness; /* right inside corner */
1118 points [npoints++].y = y + half_width;
1119 points [npoints].x = x + half_width; /* top inside corner */
1120 points [npoints++].y = y + thickness;
1121 points [npoints].x = x + thickness; /* left inside corner */
1122 points [npoints++].y = y + half_width;
1123
1124 XFillPolygon (dpy, window, top_gc, points, npoints, Nonconvex,
1125 CoordModeOrigin);
1126 #endif
1127
1128
1129 /*
1130 * Fill internal area.
1131 */
1132 if (enabled)
1133 {
1134 points [0].x = x + thickness;
1135 points [0].y = y + half_width;
1136 points [1].x = x + half_width;
1137 points [1].y = y + thickness;
1138 points [2].x = x + width - thickness;
1139 points [2].y = y + half_width;
1140 points [3].x = x + half_width;
1141 points [3].y = y + width - thickness;
1142 XFillPolygon (dpy,
1143 window,
1144 select_gc,
1145 points,
1146 4,
1147 Convex,
1148 CoordModeOrigin
1149 );
1150 }
1151 }
1152
1153 static void
1154 separator_decoration_draw (
1155 XlwMenuWidget mw,
1156 Window window,
1157 int x, int y,
1158 unsigned width,
1159 Boolean vertical,
1160 shadow_type type
1161 )
1162 {
1163 Display *dpy = XtDisplay (mw);
1164 GC top_gc;
1165 GC bottom_gc;
1166 unsigned offset = 0;
1167 unsigned num_separators = 1;
1168 unsigned top_line_thickness = 0;
1169 unsigned bottom_line_thickness = 0;
1170 Boolean dashed = False;
1171 int i;
1172
1173 switch (type)
1174 {
1175 case SHADOW_NO_LINE: /* nothing to do */
1176 return;
1177 case SHADOW_DOUBLE_LINE:
1178 num_separators = 2;
1179 case SHADOW_SINGLE_LINE:
1180 top_gc = bottom_gc = mw->menu.foreground_gc;
1181 top_line_thickness = 1;
1182 break;
1183 case SHADOW_DOUBLE_DASHED_LINE:
1184 num_separators = 2;
1185 case SHADOW_SINGLE_DASHED_LINE:
1186 top_gc = bottom_gc = mw->menu.foreground_gc;
1187 top_line_thickness = 1;
1188 dashed = True;
1189 break;
1190 case SHADOW_DOUBLE_ETCHED_OUT_DASH:
1191 num_separators = 2;
1192 case SHADOW_ETCHED_OUT_DASH:
1193 top_gc = mw->menu.shadow_top_gc;
1194 bottom_gc = mw->menu.shadow_bottom_gc;
1195 top_line_thickness = mw->menu.shadow_thickness/2;
1196 bottom_line_thickness = mw->menu.shadow_thickness - top_line_thickness;
1197 dashed = True;
1198 break;
1199 case SHADOW_DOUBLE_ETCHED_IN_DASH:
1200 num_separators = 2;
1201 case SHADOW_ETCHED_IN_DASH:
1202 top_gc = mw->menu.shadow_bottom_gc;
1203 bottom_gc = mw->menu.shadow_top_gc;
1204 top_line_thickness = mw->menu.shadow_thickness/2;
1205 bottom_line_thickness = mw->menu.shadow_thickness - top_line_thickness;
1206 dashed = True;
1207 break;
1208 case SHADOW_DOUBLE_ETCHED_OUT:
1209 num_separators = 2;
1210 case SHADOW_ETCHED_OUT:
1211 top_gc = mw->menu.shadow_top_gc;
1212 bottom_gc = mw->menu.shadow_bottom_gc;
1213 top_line_thickness = mw->menu.shadow_thickness/2;
1214 bottom_line_thickness = mw->menu.shadow_thickness - top_line_thickness;
1215 break;
1216 case SHADOW_DOUBLE_ETCHED_IN:
1217 num_separators = 2;
1218 case SHADOW_ETCHED_IN:
1219 default:
1220 top_gc = mw->menu.shadow_bottom_gc;
1221 bottom_gc = mw->menu.shadow_top_gc;
1222 top_line_thickness = mw->menu.shadow_thickness/2;
1223 bottom_line_thickness = mw->menu.shadow_thickness - top_line_thickness;
1224 break;
1225 }
1226
1227 if (dashed)
1228 {
1229 XGCValues values;
1230 values.line_style = LineOnOffDash;
1231 if (top_line_thickness > 0)
1232 XChangeGC (dpy, top_gc, GCLineStyle, &values);
1233 if (bottom_line_thickness > 0 && bottom_gc != top_gc)
1234 XChangeGC (dpy, bottom_gc, GCLineStyle, &values);
1235 }
1236
1237 while (num_separators--)
1238 {
1239 for (i = 0; i < top_line_thickness; i++)
1240 XDrawLine (dpy, window, top_gc, x, y + i, x + width, y + i);
1241
1242 for (i = 0; i < bottom_line_thickness; i++)
1243 XDrawLine (
1244 dpy, window, bottom_gc,
1245 x, y + top_line_thickness + offset + i,
1246 x + width, y + top_line_thickness + offset + i
1247 );
1248 y += (top_line_thickness + offset + bottom_line_thickness + 1);
1249 }
1250
1251 if (dashed)
1252 {
1253 XGCValues values;
1254 values.line_style = LineSolid;
1255 if (top_line_thickness > 0)
1256 XChangeGC (dpy, top_gc, GCLineStyle, &values);
1257 if (bottom_line_thickness > 0 && bottom_gc != top_gc)
1258 XChangeGC (dpy, bottom_gc, GCLineStyle, &values);
1259 }
1260 }
1261
1262 #define SLOPPY_TYPES 0 /* 0=off, 1=error check, 2=easy to please */
1263 #if SLOPPY_TYPES
1264 #if SLOPPY_TYPES < 2
1265
1266 static char *wv_types[] =
1267 {
1268 "UNSPECIFIED",
1269 "BUTTON",
1270 "TOGGLE",
1271 "RADIO",
1272 "TEXT",
1273 "SEPARATOR",
1274 "CASCADE",
1275 "PUSHRIGHT",
1276 "INCREMENTAL"
1277 };
1278
1279 static void
1280 print_widget_value (widget_value *wv, int just_one, int depth)
1281 {
1282 char d [200];
1283 int i;
1284 for (i = 0; i < depth; i++) d[i] = ' ';
1285 d[depth]=0;
1286 if (!wv)
1287 {
1288 printf ("%s(null widget value pointer)\n", d);
1289 return;
1290 }
1291 printf ("%stype: %s\n", d, wv_types [wv->type]);
1292 #if 0
1293 printf ("%sname: %s\n", d, (wv->name ? wv->name : "(null)"));
1294 #else
1295 if (wv->name) printf ("%sname: %s\n", d, wv->name);
1296 #endif
1297 if (wv->value) printf ("%svalue: %s\n", d, wv->value);
1298 if (wv->key) printf ("%skey: %s\n", d, wv->key);
1299 printf ("%senabled: %d\n", d, wv->enabled);
1300 if (wv->contents)
1301 {
1302 printf ("\n%scontents: \n", d);
1303 print_widget_value (wv->contents, 0, depth + 5);
1304 }
1305 if (!just_one && wv->next)
1306 {
1307 printf ("\n");
1308 print_widget_value (wv->next, 0, depth);
1309 }
1310 }
1311 #endif
1312
1313 static Boolean
1314 all_dashes_p (char *s)
1315 {
1316 char *p;
1317 if (!s || s[0] == '\0')
1318 return False;
1319 for (p = s; *p == '-'; p++);
1320
1321 if (*p == '!' || *p == '\0')
1322 return True;
1323 return False;
1324 }
1325 #endif
1326
1327 static widget_value_type
1328 menu_item_type (widget_value *val)
1329 {
1330 if (val->type != UNSPECIFIED_TYPE)
1331 return val->type;
1332 else
1333 {
1334 #if SLOPPY_TYPES
1335 if (all_dashes_p(val->name))
1336 return SEPARATOR_TYPE;
1337 else if (val->name && val->name[0] == '\0') /* push right */
1338 return PUSHRIGHT_TYPE;
1339 else if (val->contents) /* cascade */
1340 return CASCADE_TYPE;
1341 else if (val->call_data) /* push button */
1342 return BUTTON_TYPE;
1343 else
1344 return TEXT_TYPE;
1345 #else
1346 abort();
1347 #endif
1348 }
1349 }
1350
1351 static void
1352 label_button_size (
1353 XlwMenuWidget mw,
1354 widget_value *val,
1355 Boolean in_menubar,
1356 unsigned *toggle_width,
1357 unsigned *label_width,
1358 unsigned *bindings_width,
1359 unsigned *height
1360 )
1361 {
1362 *height = (mw->menu.font_ascent + mw->menu.font_descent +
1363 2 * mw->menu.vertical_margin +
1364 2 * mw->menu.shadow_thickness);
1365 /* no left column decoration */
1366 *toggle_width = mw->menu.horizontal_margin + mw->menu.shadow_thickness;;
1367
1368 *label_width = string_width (mw, resource_widget_value (mw, val));
1369 *bindings_width = mw->menu.horizontal_margin + mw->menu.shadow_thickness;
1370 }
1371
1372 static void
1373 label_button_draw (
1374 XlwMenuWidget mw,
1375 widget_value *val,
1376 Boolean in_menubar,
1377 Boolean highlighted,
1378 Window window,
1379 int x, int y,
1380 unsigned width,
1381 unsigned height,
1382 unsigned label_offset,
1383 unsigned binding_tab
1384 )
1385 {
1386 int y_offset = mw->menu.shadow_thickness + mw->menu.vertical_margin;
1387
1388 if (!label_offset)
1389 label_offset = mw->menu.shadow_thickness + mw->menu.horizontal_margin;
1390
1391 /*
1392 * Draw the label string.
1393 */
1394 string_draw (
1395 mw,
1396 window,
1397 x + label_offset, y + y_offset,
1398 mw->menu.foreground_gc,
1399 resource_widget_value (mw, val)
1400 );
1401 }
1402
1403 static void
1404 push_button_size (
1405 XlwMenuWidget mw,
1406 widget_value *val,
1407 Boolean in_menubar,
1408 unsigned *toggle_width,
1409 unsigned *label_width,
1410 unsigned *bindings_width,
1411 unsigned *height
1412 )
1413 {
1414 /* inherit */
1415 label_button_size (
1416 mw, val, in_menubar,
1417 toggle_width, label_width, bindings_width,
1418 height
1419 );
1420
1421 /* key bindings to display? */
1422 if (!in_menubar && val->key)
1423 {
1424 int w;
1425 #ifdef NEED_MOTIF
1426 XmString key = XmStringCreateLtoR (val->key, XmSTRING_DEFAULT_CHARSET);
1427 w = string_width(mw, key);
1428 XmStringFree (key);
1429 #else
1430 char *key = val->key;
1431 w = string_width (mw, key);
1432 #endif
1433 *bindings_width += w + mw->menu.column_spacing;
1434 }
1435 }
1436
1437 static void
1438 push_button_draw (
1439 XlwMenuWidget mw,
1440 widget_value *val,
1441 Boolean in_menubar,
1442 Boolean highlighted,
1443 Window window,
1444 int x, int y,
1445 unsigned width, unsigned height,
1446 unsigned label_offset,
1447 unsigned binding_offset
1448 )
1449 {
1450 int y_offset = mw->menu.shadow_thickness + mw->menu.vertical_margin;
1451 GC gc;
1452 shadow_type type;
1453 Boolean menu_pb = in_menubar && (menu_item_type (val) == BUTTON_TYPE);
1454
1455 /*
1456 * Draw the label string.
1457 */
1458 if (!label_offset)
1459 label_offset = mw->menu.shadow_thickness + mw->menu.horizontal_margin;
1460
1461 if (menu_pb)
1462 {
1463 if (val->enabled)
1464 gc = mw->menu.button_gc;
1465 else
1466 gc = mw->menu.inactive_button_gc;
1467 }
1468 else
1469 {
1470 if (val->enabled)
1471 gc = mw->menu.foreground_gc;
1472 else
1473 gc = mw->menu.inactive_gc;
1474 }
1475
1476 string_draw (
1477 mw,
1478 window,
1479 x + label_offset, y + y_offset,
1480 gc,
1481 resource_widget_value(mw, val)
1482 );
1483
1484 /*
1485 * Draw the keybindings
1486 */
1487 if (val->key)
1488 {
1489 if (!binding_offset)
1490 {
1491 unsigned s_width = string_width (mw, resource_widget_value(mw, val));
1492 binding_offset = label_offset + s_width + mw->menu.shadow_thickness;
1493 }
1494 binding_draw (mw, window,
1495 x + binding_offset + mw->menu.column_spacing,
1496 y + y_offset, gc, val->key);
1497 }
1498
1499 /*
1500 * Draw the shadow
1501 */
1502 if (menu_pb)
1503 {
1504 if (highlighted)
1505 type = SHADOW_OUT;
1506 else
1507 type = (val->selected ? SHADOW_ETCHED_OUT : SHADOW_ETCHED_IN);
1508 }
1509 else
1510 {
1511 if (highlighted)
1512 type = SHADOW_OUT;
1513 else
1514 type = SHADOW_BACKGROUND;
1515 }
1516
1517 shadow_draw (mw, window, x, y, width, height, type);
1518 }
1519
1520 static unsigned int
1521 arrow_decoration_height (XlwMenuWidget mw)
1522 {
1523 unsigned int result =
1524 (mw->menu.font_ascent + mw->menu.font_descent) / (unsigned int)2;
1525
1526 result += 2 * mw->menu.shadow_thickness;
1527
1528 if (result > (mw->menu.font_ascent + mw->menu.font_descent))
1529 result = mw->menu.font_ascent + mw->menu.font_descent;
1530
1531 return result;
1532 }
1533
1534 static void
1535 cascade_button_size (
1536 XlwMenuWidget mw,
1537 widget_value *val,
1538 Boolean in_menubar,
1539 unsigned *toggle_width,
1540 unsigned *label_width,
1541 unsigned *arrow_width,
1542 unsigned *height
1543 )
1544 {
1545 /* inherit */
1546 label_button_size (
1547 mw, val, in_menubar,
1548 toggle_width, label_width, arrow_width,
1549 height
1550 );
1551 /* we have a pull aside arrow */
1552 if (!in_menubar)
1553 {
1554 *arrow_width += arrow_decoration_height(mw) + mw->menu.column_spacing;
1555 }
1556 }
1557
1558 static void
1559 cascade_button_draw (
1560 XlwMenuWidget mw,
1561 widget_value *val,
1562 Boolean in_menubar,
1563 Boolean highlighted,
1564 Window window,
1565 int x, int y,
1566 unsigned width, unsigned height,
1567 unsigned label_offset,
1568 unsigned binding_offset
1569 )
1570 {
1571 shadow_type type;
1572
1573 /*
1574 * Draw the label string.
1575 */
1576 label_button_draw (mw, val, in_menubar, highlighted,
1577 window, x, y, width, height, label_offset,
1578 binding_offset);
1579
1580 /*
1581 * Draw the pull aside arrow
1582 */
1583 if (!in_menubar && val->contents)
1584 {
1585 int y_offset;
1586 unsigned arrow_height = arrow_decoration_height (mw);
1587
1588 y_offset = mw->menu.shadow_thickness + mw->menu.vertical_margin +
1589 (mw->menu.font_ascent+mw->menu.font_descent - arrow_height)/2;
1590
1591 if (!binding_offset)
1592 {
1593 unsigned s_width = string_width(mw, resource_widget_value (mw, val));
1594
1595 if (!label_offset)
1596 label_offset = mw->menu.shadow_thickness +
1597 mw->menu.horizontal_margin;
1598
1599 binding_offset = label_offset + s_width + mw->menu.shadow_thickness;
1600 }
1601
1602 arrow_decoration_draw (
1603 mw,
1604 window,
1605 x + binding_offset + mw->menu.column_spacing,
1606 y + y_offset,
1607 arrow_height,
1608 highlighted
1609 );
1610 }
1611
1612 /*
1613 * Draw the shadow
1614 */
1615 if (highlighted)
1616 type = SHADOW_OUT;
1617 else
1618 type = SHADOW_BACKGROUND;
1619
1620 shadow_draw(mw, window, x, y, width, height, type);
1621 }
1622
1623 static unsigned
1624 toggle_decoration_height(XlwMenuWidget mw)
1625 {
1626 unsigned rv;
1627 if (mw->menu.indicator_size > 0)
1628 rv = mw->menu.indicator_size;
1629 else
1630 rv = mw->menu.font_ascent;
1631
1632 if (rv > (mw->menu.font_ascent+mw->menu.font_descent))
1633 rv = mw->menu.font_ascent+mw->menu.font_descent;
1634
1635 return rv;
1636 }
1637
1638 static void
1639 toggle_button_size (
1640 XlwMenuWidget mw,
1641 widget_value *val,
1642 Boolean in_menubar,
1643 unsigned *toggle_width,
1644 unsigned *label_width,
1645 unsigned *bindings_width,
1646 unsigned *height
1647 )
1648 {
1649 /* inherit */
1650 push_button_size (
1651 mw, val, in_menubar,
1652 toggle_width, label_width, bindings_width,
1653 height
1654 );
1655 /* we have a toggle */
1656 *toggle_width += toggle_decoration_height(mw) + mw->menu.column_spacing;
1657 }
1658
1659 static void
1660 toggle_button_draw (
1661 XlwMenuWidget mw,
1662 widget_value *val,
1663 Boolean in_menubar,
1664 Boolean highlighted,
1665 Window window,
1666 int x, int y,
1667 unsigned width, unsigned height,
1668 unsigned label_tab,
1669 unsigned binding_tab
1670 )
1671 {
1672 int x_offset;
1673 int y_offset;
1674 unsigned t_height = toggle_decoration_height(mw);
1675
1676 /*
1677 * Draw a toggle.
1678 */
1679 x_offset = mw->menu.shadow_thickness + mw->menu.horizontal_margin;
1680 y_offset = mw->menu.shadow_thickness + mw->menu.vertical_margin;
1681 y_offset += (mw->menu.font_ascent + mw->menu.font_descent - t_height)/2;
1682
1683 toggle_decoration_draw (mw, window, x + x_offset, y + y_offset,
1684 t_height, val->selected);
1685
1686 /*
1687 * Draw the pushbutton parts.
1688 */
1689 push_button_draw (mw, val, in_menubar, highlighted, window, x, y, width,
1690 height, label_tab, binding_tab);
1691 }
1692
1693 static unsigned
1694 radio_decoration_height(XlwMenuWidget mw)
1695 {
1696 return toggle_decoration_height(mw);
1697 }
1698
1699 static void
1700 radio_button_draw (
1701 XlwMenuWidget mw,
1702 widget_value *val,
1703 Boolean in_menubar,
1704 Boolean highlighted,
1705 Window window,
1706 int x, int y,
1707 unsigned width, unsigned height,
1708 unsigned label_tab,
1709 unsigned binding_tab
1710 )
1711 {
1712 int x_offset;
1713 int y_offset;
1714 unsigned r_height = radio_decoration_height(mw);
1715
1716 /*
1717 * Draw a toggle.
1718 */
1719 x_offset = mw->menu.shadow_thickness + mw->menu.horizontal_margin;
1720 y_offset = mw->menu.shadow_thickness + mw->menu.vertical_margin;
1721 y_offset += (mw->menu.font_ascent + mw->menu.font_descent - r_height)/2;
1722
1723 radio_decoration_draw (mw, window, x + x_offset, y + y_offset, r_height,
1724 val->selected);
1725
1726 /*
1727 * Draw the pushbutton parts.
1728 */
1729 push_button_draw (mw, val, in_menubar, highlighted, window, x, y, width,
1730 height, label_tab, binding_tab);
1731 }
1732
1733 static struct _shadow_names
1734 {
1735 CONST char * name;
1736 shadow_type type;
1737 } shadow_names[] =
1738 {
1739 /* Motif */
1740 { "singleLine", SHADOW_SINGLE_LINE },
1741 { "doubleLine", SHADOW_DOUBLE_LINE },
1742 { "singleDashedLine", SHADOW_SINGLE_DASHED_LINE },
1743 { "doubleDashedLine", SHADOW_DOUBLE_DASHED_LINE },
1744 { "noLine", SHADOW_NO_LINE },
1745 { "shadowEtchedIn", SHADOW_ETCHED_IN },
1746 { "shadowEtchedOut", SHADOW_ETCHED_OUT },
1747 { "shadowEtchedInDash", SHADOW_ETCHED_IN_DASH },
1748 { "shadowEtchedOutDash", SHADOW_ETCHED_OUT_DASH },
1749 /* non-Motif */
1750 { "shadowDoubleEtchedIn", SHADOW_DOUBLE_ETCHED_IN },
1751 { "shadowDoubleEtchedOut", SHADOW_DOUBLE_ETCHED_OUT },
1752 { "shadowDoubleEtchedInDash", SHADOW_DOUBLE_ETCHED_IN_DASH },
1753 { "shadowDoubleEtchedOutDash", SHADOW_DOUBLE_ETCHED_OUT_DASH }
1754 };
1755
1756 static shadow_type
1757 separator_type (char *name)
1758 {
1759 int i;
1760
1761 if (name)
1762 {
1763 for (i = 0; i < XtNumber(shadow_names); i++ )
1764 {
1765 if (strcmp (name, shadow_names[i].name) == 0)
1766 return shadow_names[i].type;
1767 }
1768 }
1769 return SHADOW_BACKGROUND;
1770 }
1771
1772 static unsigned
1773 separator_decoration_height (XlwMenuWidget mw, widget_value *val)
1774 {
1775
1776 switch (separator_type(val->value))
1777 {
1778 case SHADOW_NO_LINE:
1779 case SHADOW_SINGLE_LINE:
1780 case SHADOW_SINGLE_DASHED_LINE:
1781 return 1;
1782 case SHADOW_DOUBLE_LINE:
1783 case SHADOW_DOUBLE_DASHED_LINE:
1784 return 3;
1785 case SHADOW_DOUBLE_ETCHED_OUT:
1786 case SHADOW_DOUBLE_ETCHED_IN:
1787 case SHADOW_DOUBLE_ETCHED_OUT_DASH:
1788 case SHADOW_DOUBLE_ETCHED_IN_DASH:
1789 return (1 + 2 * mw->menu.shadow_thickness);
1790 case SHADOW_ETCHED_OUT:
1791 case SHADOW_ETCHED_IN:
1792 default:
1793 return mw->menu.shadow_thickness;
1794 }
1795 }
1796
1797 static void
1798 separator_size (XlwMenuWidget mw,
1799 widget_value *val,
1800 Boolean in_menubar,
1801 unsigned *toggle_width,
1802 unsigned *label_width,
1803 unsigned *rest_width,
1804 unsigned *height
1805 )
1806 {
1807 *height = separator_decoration_height (mw, val);
1808 *label_width = 1;
1809 *toggle_width = *rest_width = 0;
1810 }
1811
1812 static void
1813 separator_draw (XlwMenuWidget mw,
1814 widget_value *val,
1815 Boolean in_menubar,
1816 Boolean highlighted,
1817 Window window,
1818 int x, int y,
1819 unsigned width, unsigned height,
1820 unsigned label_tab,
1821 unsigned binding_tab
1822 )
1823 {
1824 unsigned sep_width;
1825
1826 if (in_menubar)
1827 sep_width = height;
1828 else
1829 sep_width = width;
1830
1831 separator_decoration_draw (mw,
1832 window,
1833 x,
1834 y,
1835 sep_width,
1836 in_menubar,
1837 separator_type(val->value)
1838 );
1839 }
1840
1841 static void
1842 pushright_size (XlwMenuWidget mw,
1843 widget_value *val,
1844 Boolean in_menubar,
1845 unsigned *toggle_width,
1846 unsigned *label_width,
1847 unsigned *rest_width,
1848 unsigned *height
1849 )
1850 {
1851 *height = *label_width = *toggle_width = *rest_width = 0;
1852 }
1853
1854 static void
1855 size_menu_item (XlwMenuWidget mw,
1856 widget_value *val,
1857 int horizontal,
1858 unsigned *toggle_width,
1859 unsigned *label_width,
1860 unsigned *rest_width,
1861 unsigned *height
1862 )
1863 {
1864
1865 void (*function_ptr) (
1866 XlwMenuWidget _mw,
1867 widget_value *_val,
1868 Boolean _in_menubar,
1869 unsigned *_toggle_width,
1870 unsigned *_label_width,
1871 unsigned *_rest_width,
1872 unsigned *_height
1873 );
1874 switch (menu_item_type (val))
1875 {
1876 case TOGGLE_TYPE:
1877 case RADIO_TYPE:
1878 function_ptr = toggle_button_size;
1879 break;
1880 case SEPARATOR_TYPE:
1881 function_ptr = separator_size;
1882 break;
1883 case INCREMENTAL_TYPE:
1884 case CASCADE_TYPE:
1885 function_ptr = cascade_button_size;
1886 break;
1887 case BUTTON_TYPE:
1888 function_ptr = push_button_size;
1889 break;
1890 case PUSHRIGHT_TYPE:
1891 function_ptr = pushright_size;
1892 break;
1893 case TEXT_TYPE:
1894 default:
1895 function_ptr = label_button_size;
1896 break;
1897 }
1898
1899 (*function_ptr) (
1900 mw,
1901 val,
1902 horizontal,
1903 toggle_width,
1904 label_width,
1905 rest_width,
1906 height
1907 );
1908 }
1909
1910 static void
1911 display_menu_item (
1912 XlwMenuWidget mw,
1913 widget_value *val,
1914 window_state *ws,
1915 XPoint *where,
1916 Boolean highlighted,
1917 Boolean horizontal,
1918 Boolean just_compute
1919 )
1920 {
1921
1922 int x = where->x /* + mw->menu.shadow_thickness */ ;
1923 int y = where->y /* + mw->menu.shadow_thickness */ ;
1924 unsigned toggle_width;
1925 unsigned label_width;
1926 unsigned binding_width;
1927 unsigned width;
1928 unsigned height;
1929 unsigned label_tab;
1930 unsigned binding_tab;
1931 void (*function_ptr) (
1932 XlwMenuWidget _mw,
1933 widget_value *_val,
1934 Boolean _in_menubar,
1935 Boolean _highlighted,
1936 Window _window,
1937 int _x, int _y,
1938 unsigned _width, unsigned _height,
1939 unsigned _label_tab,
1940 unsigned _binding_tab
1941 );
1942
1943 size_menu_item (
1944 mw, val, horizontal,
1945 &toggle_width, &label_width, &binding_width, &height
1946 );
1947
1948 if (horizontal)
1949 {
1950 width = toggle_width + label_width + binding_width;
1951 height = ws->height - 2 * mw->menu.shadow_thickness;
1952 }
1953 else
1954 {
1955 width = ws->width - 2 * mw->menu.shadow_thickness;
1956 toggle_width = ws->toggle_width;
1957 label_width = ws->label_width;
1958 }
1959
1960 where->x += width;
1961 where->y += height;
1962
1963 if (just_compute)
1964 return;
1965
1966 label_tab = toggle_width;
1967 binding_tab = toggle_width + label_width;
1968
1969 switch (menu_item_type (val))
1970 {
1971 case TOGGLE_TYPE:
1972 function_ptr = toggle_button_draw;
1973 break;
1974 case RADIO_TYPE:
1975 function_ptr = radio_button_draw;
1976 break;
1977 case SEPARATOR_TYPE:
1978 function_ptr = separator_draw;
1979 break;
1980 case INCREMENTAL_TYPE:
1981 case CASCADE_TYPE:
1982 function_ptr = cascade_button_draw;
1983 break;
1984 case BUTTON_TYPE:
1985 function_ptr = push_button_draw;
1986 break;
1987 case TEXT_TYPE:
1988 function_ptr = label_button_draw;
1989 break;
1990 default: /* do no drawing */
1991 return;
1992 }
1993
1994 (*function_ptr) (
1995 mw,
1996 val,
1997 horizontal,
1998 highlighted,
1999 ws->window,
2000 x, y,
2001 width, height,
2002 label_tab,
2003 binding_tab
2004 );
2005 }
2006
2007 static void
2008 size_menu (XlwMenuWidget mw, int level)
2009 {
2010 unsigned toggle_width;
2011 unsigned label_width;
2012 unsigned rest_width;
2013 unsigned height;
2014 unsigned max_toggle_width = 0;
2015 unsigned max_label_width = 0;
2016 unsigned max_rest_width = 0;
2017 unsigned max_height = 0;
2018 int horizontal_p = mw->menu.horizontal && (level == 0);
2019 widget_value* val;
2020 window_state* ws;
2021
2022 if (level >= mw->menu.old_depth)
2023 abort ();
2024
2025 ws = &mw->menu.windows [level];
2026
2027 for (val = mw->menu.old_stack [level]->contents; val; val = val->next)
2028 {
2029 size_menu_item (
2030 mw,
2031 val,
2032 horizontal_p,
2033 &toggle_width,
2034 &label_width,
2035 &rest_width,
2036 &height
2037 );
2038 if (horizontal_p)
2039 {
2040 max_label_width += toggle_width + label_width + rest_width;
2041 if (height > max_height)
2042 max_height = height;
2043 }
2044 else
2045 {
2046 if (toggle_width > max_toggle_width)
2047 max_toggle_width = toggle_width;
2048 if (label_width > max_label_width)
2049 max_label_width = label_width;
2050 if (rest_width > max_rest_width)
2051 max_rest_width = rest_width;
2052 max_height += height;
2053 }
2054 }
2055
2056 ws->height = max_height;
2057 ws->width = max_label_width + max_rest_width + max_toggle_width;
2058 ws->toggle_width = max_toggle_width;
2059 ws->label_width = max_label_width;
2060
2061 ws->width += 2 * mw->menu.shadow_thickness;
2062 ws->height += 2 * mw->menu.shadow_thickness;
2063 }
2064
2065 static void
2066 display_menu (XlwMenuWidget mw, int level, Boolean just_compute_p,
2067 XPoint *highlighted_pos, XPoint *hit, widget_value **hit_return,
2068 widget_value *this, widget_value *that)
2069 {
2070 widget_value *val;
2071 widget_value *following_item;
2072 window_state *ws;
2073 XPoint where;
2074 int horizontal_p = mw->menu.horizontal && (level == 0);
2075 int highlighted_p;
2076 int just_compute_this_one_p;
2077
2078 if (level >= mw->menu.old_depth)
2079 abort ();
2080
2081 if (level < mw->menu.old_depth - 1)
2082 following_item = mw->menu.old_stack [level + 1];
2083 else
2084 following_item = NULL;
2085
2086 #if SLOPPY_TYPES == 1
2087 puts("===================================================================");
2088 print_widget_value (following_item, 1, 0);
2089 #endif
2090 if (following_item
2091 && following_item->type == CASCADE_TYPE
2092 && following_item->contents
2093 && following_item->contents->type == INCREMENTAL_TYPE)
2094 {
2095 /* okay, we're now doing a lisp callback to incrementally generate
2096 more of the menu. */
2097 XtCallCallbackList ((Widget)mw,
2098 mw->menu.open,
2099 (XtPointer)following_item->contents);
2100 #if SLOPPY_TYPES == 1
2101 puts("==== NEW ==== NEW ==== NEW ==== NEW ==== NEW ==== NEW ==== NEW ====");
2102 print_widget_value(following_item, 1, 0);
2103 #endif
2104 }
2105
2106 if (hit)
2107 *hit_return = NULL;
2108
2109 where.x = mw->menu.shadow_thickness;
2110 where.y = mw->menu.shadow_thickness;
2111
2112 ws = &mw->menu.windows [level];
2113 for (val = mw->menu.old_stack [level]->contents; val; val = val->next)
2114 {
2115 XPoint start;
2116
2117 highlighted_p = (val == following_item);
2118 /* If this is the partition (the dummy item which says that menus
2119 after this should be flushright) then figure out how big the
2120 following items are. This means we walk down the tail of the
2121 list twice, but that's no big deal - it's short.
2122 */
2123 if (horizontal_p && (menu_item_type (val) == PUSHRIGHT_TYPE))
2124 {
2125 widget_value *rest;
2126 XPoint flushright_size;
2127 int new_x;
2128 flushright_size.x = 0;
2129 flushright_size.y = 0;
2130 for (rest = val; rest; rest = rest->next)
2131 display_menu_item (mw, rest, ws, &flushright_size,
2132 highlighted_p, horizontal_p, True);
2133 new_x = ws->width - (flushright_size.x + mw->menu.shadow_thickness);
2134 if (new_x > where.x)
2135 where.x = new_x;
2136 /* We know what we need; don't draw this item. */
2137 continue;
2138 }
2139
2140 if (highlighted_p && highlighted_pos)
2141 {
2142 if (horizontal_p)
2143 highlighted_pos->x = where.x;
2144 else
2145 highlighted_pos->y = where.y;
2146 }
2147
2148 just_compute_this_one_p =
2149 just_compute_p || ((this || that) && val != this && val != that);
2150
2151 start.x = where.x;
2152 start.y = where.y;
2153 display_menu_item (mw, val, ws, &where, highlighted_p, horizontal_p,
2154 just_compute_this_one_p);
2155
2156 if (highlighted_p && highlighted_pos)
2157 {
2158 if (horizontal_p)
2159 highlighted_pos->y = ws->height;
2160 else
2161 highlighted_pos->x = ws->width;
2162 }
2163
2164 if (hit && !*hit_return && (val->type != SEPARATOR_TYPE))
2165 {
2166 if (horizontal_p && hit->x > start.x && hit->x < where.x)
2167 *hit_return = val;
2168 else if (!horizontal_p && hit->y > start.y && hit->y < where.y)
2169 *hit_return = val;
2170 }
2171
2172 if (horizontal_p)
2173 where.y = mw->menu.shadow_thickness;
2174 else
2175 where.x = mw->menu.shadow_thickness;
2176 }
2177
2178 /* Draw slab edges around menu */
2179 if (!just_compute_p)
2180 shadow_draw(mw, ws->window, 0, 0, ws->width, ws->height, SHADOW_OUT);
2181 }
2182
2183 /* Motion code */
2184 static void
2185 set_new_state (XlwMenuWidget mw, widget_value *val, int level)
2186 {
2187 int i;
2188
2189 mw->menu.new_depth = 0;
2190 for (i = 0; i < level; i++)
2191 push_new_stack (mw, mw->menu.old_stack [i]);
2192 if (val)
2193 push_new_stack (mw, val);
2194 }
2195
2196 static void
2197 make_windows_if_needed (XlwMenuWidget mw, int n)
2198 {
2199 int i;
2200 int start_at;
2201 XSetWindowAttributes xswa;
2202 int mask;
2203 #define ROOT_PARENT
2204 #ifdef ROOT_PARENT
2205 Window root = RootWindowOfScreen (DefaultScreenOfDisplay (XtDisplay (mw)));
2206 #endif
2207 window_state *windows;
2208
2209 if (mw->menu.windows_length >= n)
2210 return;
2211
2212 xswa.save_under = True;
2213 xswa.override_redirect = True;
2214 xswa.background_pixel = mw->core.background_pixel;
2215 xswa.border_pixel = mw->core.border_pixel;
2216 xswa.event_mask = (ExposureMask | ButtonMotionMask
2217 | ButtonReleaseMask | ButtonPressMask);
2218 xswa.cursor = mw->menu.cursor_shape;
2219 mask = CWSaveUnder | CWOverrideRedirect | CWBackPixel | CWBorderPixel
2220 | CWEventMask | CWCursor;
2221
2222 if (mw->menu.use_backing_store)
2223 {
2224 xswa.backing_store = Always;
2225 mask |= CWBackingStore;
2226 }
2227
2228 if (!mw->menu.windows)
2229 {
2230 mw->menu.windows =
2231 (window_state *) XtMalloc (n * sizeof (window_state));
2232 start_at = 0;
2233 }
2234 else
2235 {
2236 mw->menu.windows =
2237 (window_state *) XtRealloc ((char*)mw->menu.windows,
2238 n * sizeof (window_state));
2239 start_at = mw->menu.windows_length;
2240 }
2241 mw->menu.windows_length = n;
2242
2243 windows = mw->menu.windows;
2244
2245 for (i = start_at; i < n; i++)
2246 {
2247 windows [i].x = 0;
2248 windows [i].y = 0;
2249 windows [i].width = 1;
2250 windows [i].height = 1;
2251 windows [i].window =
2252 XCreateWindow (XtDisplay (mw),
2253 #ifdef ROOT_PARENT
2254 root,
2255 #else
2256 ((i > 0)
2257 ? windows[0].window
2258 : XtWindow (XtParent (mw))),
2259 #endif
2260 0, 0, 1, 1,
2261 0, 0, CopyFromParent, CopyFromParent, mask, &xswa);
2262 }
2263 }
2264
2265 /* Make the window fit in the screen */
2266 static void
2267 fit_to_screen (XlwMenuWidget mw, window_state *ws, window_state *previous_ws,
2268 Boolean horizontal_p)
2269 {
2270 int screen_width = WidthOfScreen (XtScreen (mw));
2271 int screen_height = HeightOfScreen (XtScreen (mw));
2272
2273 if (ws->x < 0)
2274 ws->x = 0;
2275 else if ((int) (ws->x + ws->width) > screen_width)
2276 {
2277 if (!horizontal_p)
2278 ws->x = previous_ws->x - ws->width;
2279 else
2280 {
2281 ws->x = screen_width - ws->width;
2282
2283 /* This check is to make sure we cut off the right side
2284 instead of the left side if the menu is wider than the
2285 screen. */
2286 if (ws->x < 0)
2287 ws->x = 0;
2288 }
2289 }
2290 if (ws->y < 0)
2291 ws->y = 0;
2292 else if ((int) (ws->y + ws->height) > screen_height)
2293 {
2294 if (horizontal_p)
2295 {
2296 /* A pulldown must either be entirely above or below the menubar.
2297 If we're here, the pulldown doesn't fit below the menubar, so
2298 let's determine if it will fit above the menubar.
2299 Only put it above if there is more room above than below.
2300 Note shadow_thickness offset to allow for slab surround.
2301 */
2302 if (ws->y > (screen_height / 2))
2303 ws->y = previous_ws->y - ws->height + mw->menu.shadow_thickness;
2304 }
2305 else
2306 {
2307 ws->y = screen_height - ws->height;
2308 /* if it's taller than the screen, display the topmost part
2309 that will fit, beginning at the top of the screen. */
2310 if (ws->y < 0)
2311 ws->y = 0;
2312 }
2313 }
2314 }
2315
2316 /* Updates old_stack from new_stack and redisplays. */
2317 static void
2318 remap_menubar (XlwMenuWidget mw)
2319 {
2320 int i;
2321 int last_same;
2322 XPoint selection_position;
2323 int old_depth = mw->menu.old_depth;
2324 int new_depth = mw->menu.new_depth;
2325 widget_value **old_stack;
2326 widget_value **new_stack;
2327 window_state *windows;
2328 widget_value *old_selection;
2329 widget_value *new_selection;
2330
2331 /* Check that enough windows and old_stack are ready. */
2332 make_windows_if_needed (mw, new_depth);
2333 make_old_stack_space (mw, new_depth);
2334 windows = mw->menu.windows;
2335 old_stack = mw->menu.old_stack;
2336 new_stack = mw->menu.new_stack;
2337
2338 /* compute the last identical different entry */
2339 for (i = 1; i < old_depth && i < new_depth; i++)
2340 if (old_stack [i] != new_stack [i])
2341 break;
2342 last_same = i - 1;
2343
2344 /* Memorize the previously selected item to be able to refresh it */
2345 old_selection = last_same + 1 < old_depth ? old_stack [last_same + 1] : NULL;
2346 if (old_selection && !old_selection->enabled)
2347 old_selection = NULL;
2348 new_selection = last_same + 1 < new_depth ? new_stack [last_same + 1] : NULL;
2349 if (new_selection && !new_selection->enabled)
2350 new_selection = NULL;
2351
2352 /* updates old_state from new_state. It has to be done now because
2353 display_menu (called below) uses the old_stack to know what to display. */
2354 for (i = last_same + 1; i < new_depth; i++)
2355 old_stack [i] = new_stack [i];
2356 mw->menu.old_depth = new_depth;
2357
2358 /* refresh the last seletion */
2359 selection_position.x = 0;
2360 selection_position.y = 0;
2361 display_menu (mw, last_same, new_selection == old_selection,
2362 &selection_position, NULL, NULL, old_selection, new_selection);
2363
2364 /* Now popup the new menus */
2365 for (i = last_same + 1; i < new_depth && new_stack [i]->contents; i++)
2366 {
2367 window_state *previous_ws = &windows [i - 1];
2368 window_state *ws = &windows [i];
2369
2370 ws->x = previous_ws->x + selection_position.x;
2371 ws->y = previous_ws->y + selection_position.y;
2372
2373 /* take into account the slab around the new menu */
2374 ws->y -= mw->menu.shadow_thickness;
2375
2376 size_menu (mw, i);
2377
2378 fit_to_screen (mw, ws, previous_ws, mw->menu.horizontal && i == 1);
2379
2380 XClearWindow (XtDisplay (mw), ws->window);
2381 XMoveResizeWindow (XtDisplay (mw), ws->window, ws->x, ws->y,
2382 ws->width, ws->height);
2383 XMapRaised (XtDisplay (mw), ws->window);
2384 display_menu (mw, i, False, &selection_position, NULL, NULL, NULL, NULL);
2385 }
2386
2387 /* unmap the menus that popped down */
2388 for (i = new_depth - 1; i < old_depth; i++)
2389 if (i >= new_depth || !new_stack [i]->contents)
2390 XUnmapWindow (XtDisplay (mw), windows [i].window);
2391 }
2392
2393 static Boolean
2394 motion_event_is_in_menu (XlwMenuWidget mw, XMotionEvent *ev, int level,
2395 XPoint *relative_pos)
2396 {
2397 window_state *ws = &mw->menu.windows [level];
2398 int x = level == 0 ? ws->x : ws->x + mw->menu.shadow_thickness;
2399 int y = level == 0 ? ws->y : ws->y + mw->menu.shadow_thickness;
2400 relative_pos->x = ev->x_root - x;
2401 relative_pos->y = ev->y_root - y;
2402 return (x < ev->x_root && ev->x_root < (int) (x + ws->width)
2403 && y < ev->y_root && ev->y_root < (int) (y + ws->height));
2404 }
2405
2406 static Boolean
2407 map_event_to_widget_value (XlwMenuWidget mw, XMotionEvent *ev,
2408 widget_value **val_ptr, int *level,
2409 Boolean *inside_menu)
2410 {
2411 int i;
2412 XPoint relative_pos;
2413 window_state* ws;
2414
2415 *val_ptr = NULL;
2416 *inside_menu = False;
2417
2418 /* Find the window */
2419 for (i = mw->menu.old_depth - 1; i >= 0; i--)
2420 {
2421 ws = &mw->menu.windows [i];
2422 if (ws && motion_event_is_in_menu (mw, ev, i, &relative_pos))
2423 {
2424 *inside_menu = True; /* special logic for menubar below... */
2425 if ((ev->type == ButtonPress) ||
2426 (ev->state != 0))
2427 {
2428 display_menu (mw, i, True, NULL, &relative_pos,
2429 val_ptr, NULL, NULL);
2430 if (*val_ptr)
2431 {
2432 *level = i + 1;
2433 *inside_menu = True;
2434 return True;
2435 }
2436 else if (mw->menu.horizontal || i == 0)
2437 {
2438 /* if we're clicking on empty part of the menubar, then
2439 unpost the stay-up menu */
2440 *inside_menu = False;
2441 }
2442 }
2443 }
2444 }
2445 return False;
2446 }
2447
2448 /* Procedures */
2449 static void
2450 make_drawing_gcs (XlwMenuWidget mw)
2451 {
2452 XGCValues xgcv;
2453 unsigned long flags = (GCFont | GCForeground | GCBackground);
2454
2455 #ifdef NEED_MOTIF
2456 xgcv.font = default_font_of_font_list (mw->menu.font_list)->fid;
2457 #else
2458 xgcv.font = mw->menu.font->fid;
2459 #endif
2460
2461 xgcv.foreground = mw->core.background_pixel;
2462 xgcv.background = mw->menu.foreground;
2463 mw->menu.background_gc = XtGetGC ((Widget)mw, flags, &xgcv);
2464
2465 xgcv.foreground = mw->menu.foreground;
2466 xgcv.background = mw->core.background_pixel;
2467 mw->menu.foreground_gc = XtGetGC ((Widget)mw, flags, &xgcv);
2468
2469 if (mw->menu.select_color != (Pixel)-1)
2470 {
2471 xgcv.foreground = mw->menu.select_color;
2472 }
2473 else
2474 {
2475 Display *dpy = XtDisplay(mw);
2476 if (CellsOfScreen(DefaultScreenOfDisplay(dpy)) <= 2)
2477 { /* mono */
2478 xgcv.foreground = mw->menu.foreground;
2479 }
2480 else
2481 { /* color */
2482 XColor xcolor;
2483 Colormap cmap = DefaultColormapOfScreen (XtScreen ((Widget) mw));
2484 xcolor.pixel = mw->core.background_pixel;
2485 XQueryColor (dpy, cmap, &xcolor);
2486 xcolor.red *= 0.85;
2487 xcolor.green *= 0.85;
2488 xcolor.blue *= 0.85;
2489 if (allocate_nearest_color (dpy, cmap, &xcolor))
2490 xgcv.foreground = xcolor.pixel;
2491 }
2492 }
2493 xgcv.background = mw->core.background_pixel;
2494 mw->menu.select_gc = XtGetGC ((Widget)mw, flags, &xgcv);
2495
2496 xgcv.foreground = mw->menu.foreground;
2497 xgcv.background = mw->core.background_pixel;
2498 xgcv.fill_style = FillStippled;
2499 xgcv.stipple = mw->menu.gray_pixmap;
2500 mw->menu.inactive_gc = XtGetGC ((Widget)mw,
2501 (flags | GCFillStyle | GCStipple),
2502 &xgcv);
2503
2504 xgcv.foreground = mw->menu.button_foreground;
2505 xgcv.background = mw->core.background_pixel;
2506 mw->menu.button_gc = XtGetGC ((Widget)mw, flags, &xgcv);
2507
2508 xgcv.fill_style = FillStippled;
2509 xgcv.stipple = mw->menu.gray_pixmap;
2510 mw->menu.inactive_button_gc = XtGetGC ((Widget)mw,
2511 (flags | GCFillStyle | GCStipple),
2512 &xgcv);
2513 }
2514
2515 static void
2516 release_drawing_gcs (XlwMenuWidget mw)
2517 {
2518 XtReleaseGC ((Widget) mw, mw->menu.foreground_gc);
2519 XtReleaseGC ((Widget) mw, mw->menu.button_gc);
2520 XtReleaseGC ((Widget) mw, mw->menu.inactive_gc);
2521 XtReleaseGC ((Widget) mw, mw->menu.inactive_button_gc);
2522 XtReleaseGC ((Widget) mw, mw->menu.background_gc);
2523 XtReleaseGC ((Widget) mw, mw->menu.select_gc);
2524 /* let's get some segvs if we try to use these... */
2525 mw->menu.foreground_gc = (GC) -1;
2526 mw->menu.button_gc = (GC) -1;
2527 mw->menu.inactive_gc = (GC) -1;
2528 mw->menu.inactive_button_gc = (GC) -1;
2529 mw->menu.background_gc = (GC) -1;
2530 mw->menu.select_gc = (GC) -1;
2531 }
2532
2533 #define MINL(x,y) ((((unsigned long) (x)) < ((unsigned long) (y))) \
2534 ? ((unsigned long) (x)) : ((unsigned long) (y)))
2535
2536 static void
2537 make_shadow_gcs (XlwMenuWidget mw)
2538 {
2539 XGCValues xgcv;
2540 unsigned long pm = 0;
2541 Display *dpy = XtDisplay ((Widget) mw);
2542 Colormap cmap = DefaultColormapOfScreen (XtScreen ((Widget) mw));
2543 XColor topc, botc;
2544 int top_frobbed = 0, bottom_frobbed = 0;
2545
2546 if (mw->menu.top_shadow_color == -1)
2547 mw->menu.top_shadow_color = mw->core.background_pixel;
2548 if (mw->menu.bottom_shadow_color == -1)
2549 mw->menu.bottom_shadow_color = mw->menu.foreground;
2550
2551 if (mw->menu.top_shadow_color == mw->core.background_pixel ||
2552 mw->menu.top_shadow_color == mw->menu.foreground)
2553 {
2554 topc.pixel = mw->core.background_pixel;
2555 XQueryColor (dpy, cmap, &topc);
2556 /* don't overflow/wrap! */
2557 topc.red = MINL (65535, topc.red * 1.2);
2558 topc.green = MINL (65535, topc.green * 1.2);
2559 topc.blue = MINL (65535, topc.blue * 1.2);
2560 if (allocate_nearest_color (dpy, cmap, &topc))
2561 {
2562 if (topc.pixel == mw->core.background_pixel)
2563 {
2564 XFreeColors( dpy, cmap, &topc.pixel, 1, 0);
2565 topc.red = MINL (65535, topc.red + 0x8000);
2566 topc.green = MINL (65535, topc.green + 0x8000);
2567 topc.blue = MINL (65535, topc.blue + 0x8000);
2568 if (allocate_nearest_color (dpy, cmap, &topc))
2569 {
2570 mw->menu.top_shadow_color = topc.pixel;
2571 }
2572 }
2573 else
2574 {
2575 mw->menu.top_shadow_color = topc.pixel;
2576 }
2577
2578 top_frobbed = 1;
2579 }
2580 }
2581 if (mw->menu.bottom_shadow_color == mw->menu.foreground ||
2582 mw->menu.bottom_shadow_color == mw->core.background_pixel)
2583 {
2584 botc.pixel = mw->core.background_pixel;
2585 XQueryColor (dpy, cmap, &botc);
2586 botc.red *= 0.6;
2587 botc.green *= 0.6;
2588 botc.blue *= 0.6;
2589 if (allocate_nearest_color (dpy, cmap, &botc))
2590 {
2591 if (botc.pixel == mw->core.background_pixel)
2592 {
2593 XFreeColors (dpy, cmap, &botc.pixel, 1, 0);
2594 botc.red = MINL (65535, botc.red + 0x4000);
2595 botc.green = MINL (65535, botc.green + 0x4000);
2596 botc.blue = MINL (65535, botc.blue + 0x4000);
2597 if (allocate_nearest_color (dpy, cmap, &botc))
2598 {
2599 mw->menu.bottom_shadow_color = botc.pixel;
2600 }
2601 }
2602 else
2603 {
2604 mw->menu.bottom_shadow_color = botc.pixel;
2605 }
2606
2607 bottom_frobbed = 1;
2608 }
2609 }
2610
2611 if (top_frobbed && bottom_frobbed)
2612 {
2613 int top_avg = ((topc.red / 3) + (topc.green / 3) + (topc.blue / 3));
2614 int bot_avg = ((botc.red / 3) + (botc.green / 3) + (botc.blue / 3));
2615 if (bot_avg > top_avg)
2616 {
2617 Pixel tmp = mw->menu.top_shadow_color;
2618 mw->menu.top_shadow_color = mw->menu.bottom_shadow_color;
2619 mw->menu.bottom_shadow_color = tmp;
2620 }
2621 else if (topc.pixel == botc.pixel)
2622 {
2623 if (botc.pixel == mw->menu.foreground)
2624 mw->menu.top_shadow_color = mw->core.background_pixel;
2625 else
2626 mw->menu.bottom_shadow_color = mw->menu.foreground;
2627 }
2628 }
2629
2630 if (!mw->menu.top_shadow_pixmap &&
2631 mw->menu.top_shadow_color == mw->core.background_pixel)
2632 {
2633 mw->menu.top_shadow_pixmap = mw->menu.gray_pixmap;
2634 mw->menu.top_shadow_color = mw->menu.foreground;
2635 }
2636 if (!mw->menu.bottom_shadow_pixmap &&
2637 mw->menu.bottom_shadow_color == mw->core.background_pixel)
2638 {
2639 mw->menu.bottom_shadow_pixmap = mw->menu.gray_pixmap;
2640 mw->menu.bottom_shadow_color = mw->menu.foreground;
2641 }
2642
2643 xgcv.fill_style = FillOpaqueStippled;
2644 xgcv.foreground = mw->menu.top_shadow_color;
2645 xgcv.background = mw->core.background_pixel;
2646 xgcv.stipple = mw->menu.top_shadow_pixmap;
2647 pm = (xgcv.stipple ? GCStipple|GCFillStyle : 0);
2648 mw->menu.shadow_top_gc =
2649 XtGetGC((Widget)mw, GCForeground|GCBackground|pm, &xgcv);
2650
2651 xgcv.foreground = mw->menu.bottom_shadow_color;
2652 xgcv.stipple = mw->menu.bottom_shadow_pixmap;
2653 pm = (xgcv.stipple ? GCStipple|GCFillStyle : 0);
2654 mw->menu.shadow_bottom_gc =
2655 XtGetGC ((Widget)mw, GCForeground|GCBackground|pm, &xgcv);
2656 }
2657
2658
2659 static void
2660 release_shadow_gcs (XlwMenuWidget mw)
2661 {
2662 XtReleaseGC ((Widget) mw, mw->menu.shadow_top_gc);
2663 XtReleaseGC ((Widget) mw, mw->menu.shadow_bottom_gc);
2664 }
2665
2666
2667 static void
2668 extract_font_extents (XlwMenuWidget mw)
2669 {
2670 #ifdef NEED_MOTIF
2671 /* Find the maximal ascent/descent of the fonts in the font list
2672 so that all menu items can be the same height... */
2673 mw->menu.font_ascent = 0;
2674 mw->menu.font_descent = 0;
2675
2676 {
2677 XmFontContext context;
2678 #if (XmVersion >= 1002)
2679 XmFontListEntry fontentry;
2680 #else
2681 XmStringCharSet charset;
2682 #endif
2683 XFontStruct *font;
2684
2685 if (! XmFontListInitFontContext (&context, mw->menu.font_list))
2686 abort ();
2687 #if (XmVersion >= 1002)
2688 /* There is a BUG in the 1.2 version of XmFontListGetNextFont() (or more
2689 specifically, in _XmGetFirstFont()) that can cause a null pointer to be
2690 passed to XFontsOfFontSet. Use XmFontListNextEntry(), which is the
2691 newer equivalent, instead. Also, it supports font sets, and the
2692 older function doesn't. */
2693 while ((fontentry = XmFontListNextEntry (context)))
2694 {
2695 char *one_of_them;
2696 XmFontType rettype;
2697
2698 one_of_them = XmFontListEntryGetFont (fontentry, &rettype);
2699 if (rettype == XmFONT_IS_FONTSET)
2700 {
2701 XFontSet fontset = (XFontSet) one_of_them;
2702 XFontStruct **fontstruct_list;
2703 char **fontname_list;
2704 int fontcount = XFontsOfFontSet (fontset, &fontstruct_list,
2705 &fontname_list);
2706 while (--fontcount >= 0)
2707 {
2708 font = fontstruct_list[fontcount];
2709 if (font->ascent > (int) mw->menu.font_ascent)
2710 mw->menu.font_ascent = font->ascent;
2711 if (font->descent > (int) mw->menu.font_descent)
2712 mw->menu.font_descent = font->descent;
2713 }
2714 }
2715 else /* XmFONT_IS_FONT */
2716 {
2717 font = (XFontStruct *) one_of_them;
2718 if (font->ascent > (int) mw->menu.font_ascent)
2719 mw->menu.font_ascent = font->ascent;
2720 if (font->descent > (int) mw->menu.font_descent)
2721 mw->menu.font_descent = font->descent;
2722 }
2723 }
2724 #else /* motif 1.1 */
2725 while (XmFontListGetNextFont (context, &charset, &font))
2726 {
2727 if (font->ascent > (int) mw->menu.font_ascent)
2728 mw->menu.font_ascent = font->ascent;
2729 if (font->descent > (int) mw->menu.font_descent)
2730 mw->menu.font_descent = font->descent;
2731 XtFree (charset);
2732 }
2733 #endif /* Motif version */
2734 XmFontListFreeFontContext (context);
2735 }
2736 #else /* Not Motif */
2737 mw->menu.font_ascent = mw->menu.font->ascent;
2738 mw->menu.font_descent = mw->menu.font->descent;
2739 #endif /* NEED_MOTIF */
2740 }
2741
2742 #ifdef NEED_MOTIF
2743 static XFontStruct *
2744 default_font_of_font_list (XmFontList font_list)
2745 {
2746 XFontStruct *font = 0;
2747 # if 0
2748 /* Xm/Label.c does this: */
2749 _XmFontListGetDefaultFont (font_list, &font);
2750 # else /* !0 */
2751 {
2752 XmFontContext context;
2753 #if (XmVersion >= 1002)
2754 XmFontListEntry fontentry;
2755 XmFontType rettype;
2756 char *one_of_them;
2757 #else
2758 XmStringCharSet charset;
2759 #endif
2760
2761 if (! XmFontListInitFontContext (&context, font_list))
2762 abort ();
2763 #if (XmVersion >= 1002)
2764 /* There is a BUG in the 1.2 version of XmFontListGetNextFont() (or more
2765 specifically, in _XmGetFirstFont()) that can cause a null pointer to be
2766 passed to XFontsOfFontSet. Use XmFontListNextEntry(), which is the
2767 newer equivalent, instead. */
2768 fontentry = XmFontListNextEntry (context);
2769 one_of_them = XmFontListEntryGetFont (fontentry, &rettype);
2770 if (rettype == XmFONT_IS_FONTSET)
2771 {
2772 XFontSet fontset = (XFontSet) one_of_them;
2773 XFontStruct **fontstruct_list;
2774 char **fontname_list;
2775 (void) XFontsOfFontSet (fontset, &fontstruct_list, &fontname_list);
2776 font = fontstruct_list[0];
2777 }
2778 else /* XmFONT_IS_FONT */
2779 {
2780 font = (XFontStruct *) one_of_them;
2781 }
2782 #else
2783 if (! XmFontListGetNextFont (context, &charset, &font))
2784 abort ();
2785 XtFree (charset);
2786 #endif
2787 XmFontListFreeFontContext (context);
2788 }
2789 # endif /* !0 */
2790
2791 if (! font) abort ();
2792 return font;
2793 }
2794 #endif /* NEED_MOTIF */
2795
2796 static void
2797 XlwMenuInitialize (Widget request, Widget new, ArgList args,
2798 Cardinal *num_args)
2799 {
2800 /* Get the GCs and the widget size */
2801 XlwMenuWidget mw = (XlwMenuWidget)new;
2802
2803 XSetWindowAttributes xswa;
2804 int mask;
2805
2806 Window window = RootWindowOfScreen (DefaultScreenOfDisplay (XtDisplay (mw)));
2807 Display *display = XtDisplay (mw);
2808
2809 /* mw->menu.cursor = XCreateFontCursor (display, mw->menu.cursor_shape); */
2810 mw->menu.cursor = mw->menu.cursor_shape;
2811
2812 mw->menu.gray_pixmap = XCreatePixmapFromBitmapData (display, window,
2813 (char *) gray_bits,
2814 gray_width,
2815 gray_height, 1, 0, 1);
2816
2817 #ifdef NEED_MOTIF
2818 /* The menu.font_list slot came from the *fontList resource (Motif standard.)
2819 The menu.font_list_2 slot came from the *font resource, for backward
2820 compatibility with older versions of this code, and consistency with the
2821 rest of emacs. If both font and fontList are specified, we use font.
2822 If only one is specified, we use that. If neither are specified, we
2823 use the "fallback" value. What a kludge!!!
2824
2825 Note that this has the bug that a more general wildcard like "*fontList:"
2826 will override a more specific resource like "Emacs*menubar.font:". But
2827 I can't think of a way around that.
2828 */
2829 if (mw->menu.font_list) /* if *fontList is specified, use that */
2830 ;
2831 else if (mw->menu.font_list_2) /* else if *font is specified, use that */
2832 mw->menu.font_list = mw->menu.font_list_2;
2833 else /* otherwise use default */
2834 mw->menu.font_list = mw->menu.fallback_font_list;
2835 #endif
2836
2837 make_drawing_gcs (mw);
2838 make_shadow_gcs (mw);
2839 extract_font_extents (mw);
2840
2841 xswa.background_pixel = mw->core.background_pixel;
2842 xswa.border_pixel = mw->core.border_pixel;
2843 mask = CWBackPixel | CWBorderPixel;
2844
2845 mw->menu.popped_up = False;
2846 mw->menu.pointer_grabbed = False;
2847 mw->menu.next_release_must_exit = False;
2848
2849 mw->menu.old_depth = 1;
2850 mw->menu.old_stack = (widget_value**)XtMalloc (sizeof (widget_value*));
2851 mw->menu.old_stack_length = 1;
2852 mw->menu.old_stack [0] = mw->menu.contents;
2853
2854 mw->menu.new_depth = 0;
2855 mw->menu.new_stack = 0;
2856 mw->menu.new_stack_length = 0;
2857 push_new_stack (mw, mw->menu.contents);
2858
2859 mw->menu.windows = (window_state*)XtMalloc (sizeof (window_state));
2860 mw->menu.windows_length = 1;
2861 mw->menu.windows [0].x = 0;
2862 mw->menu.windows [0].y = 0;
2863 mw->menu.windows [0].width = 0;
2864 mw->menu.windows [0].height = 0;
2865 size_menu (mw, 0);
2866
2867 mw->core.width = mw->menu.windows [0].width;
2868 mw->core.height = mw->menu.windows [0].height;
2869 }
2870
2871 static void
2872 XlwMenuClassInitialize (void)
2873 {
2874 }
2875
2876 static void
2877 XlwMenuRealize (Widget w, Mask *valueMask, XSetWindowAttributes *attributes)
2878 {
2879 XlwMenuWidget mw = (XlwMenuWidget)w;
2880 XSetWindowAttributes xswa;
2881 int mask;
2882
2883 (*xlwMenuWidgetClass->core_class.superclass->core_class.realize)
2884 (w, valueMask, attributes);
2885
2886 xswa.save_under = True;
2887 xswa.cursor = mw->menu.cursor_shape;
2888 mask = CWSaveUnder | CWCursor;
2889 if (mw->menu.use_backing_store)
2890 {
2891 xswa.backing_store = Always;
2892 mask |= CWBackingStore;
2893 }
2894 XChangeWindowAttributes (XtDisplay (w), XtWindow (w), mask, &xswa);
2895
2896 mw->menu.windows [0].window = XtWindow (w);
2897 mw->menu.windows [0].x = w->core.x;
2898 mw->menu.windows [0].y = w->core.y;
2899 mw->menu.windows [0].width = w->core.width;
2900 mw->menu.windows [0].height = w->core.height;
2901 }
2902
2903 /* Only the toplevel menubar/popup is a widget so it's the only one that
2904 receives expose events through Xt. So we repaint all the other panes
2905 when receiving an Expose event. */
2906 static void
2907 XlwMenuRedisplay (Widget w, XEvent *ev, Region region)
2908 {
2909 XlwMenuWidget mw = (XlwMenuWidget)w;
2910 int i;
2911
2912 if (mw->core.being_destroyed) return;
2913
2914 for (i = 0; i < mw->menu.old_depth; i++)
2915 display_menu (mw, i, False, NULL, NULL, NULL, NULL, NULL);
2916 set_new_state (mw, NULL, mw->menu.old_depth); /* #### - ??? */
2917 remap_menubar (mw); /* #### - do these two lines do anything? */
2918 }
2919
2920 static void
2921 XlwMenuDestroy (Widget w)
2922 {
2923 int i;
2924 XlwMenuWidget mw = (XlwMenuWidget) w;
2925
2926 if (mw->menu.pointer_grabbed)
2927 {
2928 XtUngrabPointer (w, CurrentTime);
2929 mw->menu.pointer_grabbed = False;
2930 }
2931
2932 release_drawing_gcs (mw);
2933 release_shadow_gcs (mw);
2934
2935 /* this doesn't come from the resource db but is created explicitly
2936 so we must free it ourselves. */
2937 XFreePixmap (XtDisplay (mw), mw->menu.gray_pixmap);
2938 mw->menu.gray_pixmap = (Pixmap) -1;
2939
2940 /* Don't free mw->menu.contents because that comes from our creator.
2941 The `*_stack' elements are just pointers into `contents' so leave
2942 that alone too. But free the stacks themselves. */
2943 if (mw->menu.old_stack) XtFree ((char *) mw->menu.old_stack);
2944 if (mw->menu.new_stack) XtFree ((char *) mw->menu.new_stack);
2945
2946 /* Remember, you can't free anything that came from the resource
2947 database. This includes:
2948 mw->menu.cursor
2949 mw->menu.top_shadow_pixmap
2950 mw->menu.bottom_shadow_pixmap
2951 mw->menu.font
2952 mw->menu.font_set
2953 Also the color cells of top_shadow_color, bottom_shadow_color,
2954 foreground, and button_foreground will never be freed until this
2955 client exits. Nice, eh?
2956 */
2957
2958 /* start from 1 because the one in slot 0 is w->core.window */
2959 for (i = 1; i < mw->menu.windows_length; i++)
2960 XDestroyWindow (XtDisplay (mw), mw->menu.windows [i].window);
2961 if (mw->menu.windows)
2962 XtFree ((char *) mw->menu.windows);
2963 }
2964
2965 static Boolean
2966 XlwMenuSetValues (Widget current, Widget request, Widget new, ArgList args,
2967 Cardinal *num_args)
2968 {
2969 XlwMenuWidget oldmw = (XlwMenuWidget)current;
2970 XlwMenuWidget newmw = (XlwMenuWidget)new;
2971 Boolean redisplay = False;
2972 int i;
2973
2974 if (newmw->menu.contents
2975 && newmw->menu.contents->contents
2976 && newmw->menu.contents->contents->change >= VISIBLE_CHANGE)
2977 redisplay = True;
2978
2979 if (newmw->core.background_pixel != oldmw->core.background_pixel
2980 || newmw->menu.foreground != oldmw->menu.foreground
2981 /* For the XEditResource protocol, which may want to change the font. */
2982 #ifdef NEED_MOTIF
2983 || newmw->menu.font_list != oldmw->menu.font_list
2984 || newmw->menu.font_list_2 != oldmw->menu.font_list_2
2985 || newmw->menu.fallback_font_list != oldmw->menu.fallback_font_list
2986 #else
2987 || newmw->menu.font != oldmw->menu.font
2988 #endif
2989 )
2990 {
2991 release_drawing_gcs (newmw);
2992 make_drawing_gcs (newmw);
2993 redisplay = True;
2994
2995 for (i = 0; i < oldmw->menu.windows_length; i++)
2996 {
2997 XSetWindowBackground (XtDisplay (oldmw),
2998 oldmw->menu.windows [i].window,
2999 newmw->core.background_pixel);
3000 /* clear windows and generate expose events */
3001 XClearArea (XtDisplay (oldmw), oldmw->menu.windows[i].window,
3002 0, 0, 0, 0, True);
3003 }
3004 }
3005
3006 return redisplay;
3007 }
3008
3009 static void
3010 XlwMenuResize (Widget w)
3011 {
3012 XlwMenuWidget mw = (XlwMenuWidget)w;
3013
3014 mw->menu.windows [0].width = mw->core.width;
3015 mw->menu.windows [0].height = mw->core.height;
3016 }
3017
3018 /* Action procedures */
3019 static void
3020 handle_single_motion_event (XlwMenuWidget mw, XMotionEvent *ev,
3021 Boolean select_p)
3022 {
3023 widget_value *val;
3024 Boolean stay_up;
3025 int level;
3026
3027 if (!map_event_to_widget_value (mw, ev, &val, &level, &stay_up))
3028 {
3029 /* we wind up here when: (a) the event is in the menubar, (b) the
3030 event isn't in the menubar or any of the panes, (c) the event is on
3031 a disabled menu item */
3032 pop_new_stack_if_no_contents (mw);
3033 if (select_p && !stay_up) {
3034 /* pop down all menus and exit */
3035 mw->menu.next_release_must_exit = True;
3036 set_new_state(mw, (val = NULL), 1);
3037 }
3038 }
3039 else
3040 {
3041 /* we wind up here when: (a) the event pops up a pull_right menu,
3042 (b) a menu item that is not disabled is highlighted */
3043 if (select_p && mw->menu.bounce_down
3044 && close_to_reference_time((Widget)mw,
3045 mw->menu.menu_bounce_time,
3046 (XEvent *)ev))
3047 {
3048 /* motion can cause more than one event. Don't bounce right back
3049 up if we've just bounced down. */
3050 val = NULL;
3051 }
3052 else if (select_p && mw->menu.bounce_down &&
3053 mw->menu.last_selected_val &&
3054 (mw->menu.last_selected_val == val))
3055 {
3056 val = NULL; /* assigned to mw->last_selected_val below */
3057 mw->menu.menu_bounce_time = ev->time;
3058 /* popdown last menu if we're selecting the same menu item as we did
3059 last time and the XlwMenu.bounceDown resource is set, if the
3060 item is on the menubar itself, then exit. */
3061 if (level == (mw->menu.popped_up ? 0 : 1))
3062 mw->menu.next_release_must_exit = True;
3063 }
3064 else
3065 mw->menu.menu_bounce_time = 0;
3066 set_new_state (mw, val, level);
3067 }
3068 mw->menu.last_selected_val = val;
3069 remap_menubar (mw);
3070
3071 /* Sync with the display. Makes it feel better on X terms. */
3072 XFlush (XtDisplay (mw));
3073 }
3074
3075 static void
3076 handle_motion_event (XlwMenuWidget mw, XMotionEvent *ev,
3077 Boolean select_p)
3078 {
3079 int x = ev->x_root;
3080 int y = ev->y_root;
3081 int state = ev->state;
3082 XMotionEvent *event= ev, dummy;
3083
3084 /* allow motion events to be generated again */
3085 dummy.window = ev->window;
3086 if (ev->is_hint
3087 && XQueryPointer (XtDisplay (mw), dummy.window,
3088 &dummy.root, &dummy.subwindow,
3089 &dummy.x_root, &dummy.y_root,
3090 &dummy.x, &dummy.y,
3091 &dummy.state)
3092 && dummy.state == state
3093 && (dummy.x_root != x || dummy.y_root != y))
3094 {
3095 /* don't handle the event twice or that breaks bounce_down. --Stig */
3096 dummy.type = ev->type;
3097 event = &dummy;
3098 }
3099
3100 handle_single_motion_event (mw, event, select_p);
3101 }
3102
3103 static void
3104 Start (Widget w, XEvent *ev, String *params, Cardinal *num_params)
3105 {
3106 XlwMenuWidget mw = (XlwMenuWidget)w;
3107
3108 if (!mw->menu.pointer_grabbed)
3109 {
3110 mw->menu.menu_post_time = ev->xbutton.time;
3111 mw->menu.menu_bounce_time = 0;
3112 mw->menu.next_release_must_exit = True;
3113 mw->menu.last_selected_val = NULL;
3114
3115 XtCallCallbackList ((Widget)mw, mw->menu.open, NULL);
3116
3117 /* notes the absolute position of the menubar window */
3118 mw->menu.windows [0].x = ev->xmotion.x_root - ev->xmotion.x;
3119 mw->menu.windows [0].y = ev->xmotion.y_root - ev->xmotion.y;
3120
3121 XtGrabPointer ((Widget)mw, False,
3122 (ButtonMotionMask | ButtonReleaseMask | ButtonPressMask),
3123 GrabModeAsync, GrabModeAsync,
3124 None, mw->menu.cursor_shape,
3125 ((XButtonPressedEvent*)ev)->time);
3126 mw->menu.pointer_grabbed = True;
3127 }
3128
3129 /* handles the down like a move, slots are mostly compatible */
3130 handle_motion_event (mw, &ev->xmotion, True);
3131 }
3132
3133 static void
3134 Drag (Widget w, XEvent *ev, String *params, Cardinal *num_params)
3135 {
3136 XlwMenuWidget mw = (XlwMenuWidget)w;
3137 handle_motion_event (mw, &ev->xmotion, False);
3138 }
3139
3140 static void
3141 Select (Widget w, XEvent *ev, String *params, Cardinal *num_params)
3142 {
3143 XlwMenuWidget mw = (XlwMenuWidget)w;
3144 widget_value *selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];
3145
3146 /* If user releases the button quickly, without selecting anything,
3147 after the initial down-click that brought the menu up,
3148 do nothing. */
3149 if ((selected_item == 0 || selected_item->call_data == 0)
3150 && (!mw->menu.next_release_must_exit
3151 || close_to_reference_time(w, mw->menu.menu_post_time, ev)))
3152 {
3153 mw->menu.next_release_must_exit = False;
3154 return;
3155 }
3156
3157 /* pop down everything */
3158 mw->menu.new_depth = 1;
3159 remap_menubar (mw);
3160
3161 /* Destroy() only gets called for popup menus. Menubar widgets aren't
3162 destroyed when their menu panes get nuked. */
3163 if (mw->menu.pointer_grabbed)
3164 {
3165 XtUngrabPointer ((Widget)w, ev->xmotion.time);
3166 mw->menu.pointer_grabbed = False;
3167 }
3168
3169 if (mw->menu.popped_up)
3170 {
3171 mw->menu.popped_up = False;
3172 XtPopdown (XtParent (mw));
3173 }
3174
3175 /* callback */
3176 XtCallCallbackList ((Widget)mw, mw->menu.select, (XtPointer)selected_item);
3177 }
3178
3179
3180 /* Special code to pop-up a menu */
3181 void
3182 xlw_pop_up_menu (XlwMenuWidget mw, XButtonPressedEvent *event)
3183 {
3184 int x = event->x_root;
3185 int y = event->y_root;
3186 int w;
3187 int h;
3188 int borderwidth = mw->menu.shadow_thickness;
3189 Screen* screen = XtScreen (mw);
3190
3191 mw->menu.menu_post_time = event->time;
3192 mw->menu.menu_bounce_time = 0;
3193 mw->menu.next_release_must_exit = True;
3194 mw->menu.last_selected_val = NULL;
3195
3196 XtCallCallbackList ((Widget)mw, mw->menu.open, NULL);
3197
3198 size_menu (mw, 0);
3199
3200 w = mw->menu.windows [0].width;
3201 h = mw->menu.windows [0].height;
3202
3203 x -= borderwidth;
3204 y -= borderwidth;
3205 if (x < borderwidth)
3206 x = borderwidth;
3207 if (x + w + 2 * borderwidth > WidthOfScreen (screen))
3208 x = WidthOfScreen (screen) - w - 2 * borderwidth;
3209 if (y < borderwidth)
3210 y = borderwidth;
3211 if (y + h + 2 * borderwidth> HeightOfScreen (screen))
3212 y = HeightOfScreen (screen) - h - 2 * borderwidth;
3213
3214 mw->menu.popped_up = True;
3215 XtConfigureWidget (XtParent (mw), x, y, w, h,
3216 XtParent (mw)->core.border_width);
3217 XtPopup (XtParent (mw), XtGrabExclusive);
3218 display_menu (mw, 0, False, NULL, NULL, NULL, NULL, NULL);
3219 if (!mw->menu.pointer_grabbed)
3220 {
3221 XtGrabPointer ((Widget)mw, False,
3222 (ButtonMotionMask | ButtonReleaseMask | ButtonPressMask),
3223 GrabModeAsync, GrabModeAsync,
3224 None, mw->menu.cursor_shape, event->time);
3225 mw->menu.pointer_grabbed = True;
3226 }
3227
3228 mw->menu.windows [0].x = x + borderwidth;
3229 mw->menu.windows [0].y = y + borderwidth;
3230
3231 handle_motion_event (mw, (XMotionEvent *) event, True);
3232 }
3233
3234 /* #### unused */
3235 #if 0
3236 /*
3237 * This is a horrible function which should not be needed.
3238 * use it to put the resize method back the way the XlwMenu
3239 * class initializer put it. Motif screws with this when
3240 * the XlwMenu class gets instantiated.
3241 */
3242 void
3243 xlw_unmunge_class_resize (Widget w)
3244 {
3245 if (w->core.widget_class->core_class.resize != XlwMenuResize)
3246 w->core.widget_class->core_class.resize = XlwMenuResize;
3247 }
3248 #endif /* 0 */
3249