comparison lwlib/xlwmenu.c @ 428:3ecd8885ac67 r21-2-22

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