root/branches/2.1/lwlib/xlwmenu.c

Revision 3104, 58.3 kB (checked in by himi, 6 years ago)

set svn:eol-style

  • Property svn:eol-style set to native
Line 
1 /* Implements a lightweight menubar widget.
2    Copyright (C) 1992 Lucid, Inc.
3
4 This file is part of the Lucid Widget Library.
5
6 The Lucid Widget Library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10
11 The Lucid Widget Library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with GNU Emacs; see the file COPYING.  If not, write to the
18 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 Boston, MA 02111-1307, USA.  */
20
21 /* Created by devin@lucid.com */
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <stdio.h>
28
29 #include <sys/types.h>
30 #if (defined __sun) && !(defined SUNOS41)
31 #define SUNOS41
32 #include <X11/Xos.h>
33 #undef SUNOS41
34 #else
35 #include <X11/Xos.h>
36 #endif
37 #include <X11/IntrinsicP.h>
38 #include <X11/ObjectP.h>
39 #include <X11/StringDefs.h>
40 #include <X11/cursorfont.h>
41 #include "xlwmenuP.h"
42
43 #ifdef emacs
44
45 /* Defined in xfns.c.  When config.h defines `static' as empty, we get
46    redefinition errors when gray_bitmap is included more than once, so
47    we're referring to the one include in xfns.c here.  */
48
49 extern int gray_bitmap_width;
50 extern int gray_bitmap_height;
51 extern char *gray_bitmap_bits;
52
53 /* Defined in xterm.c.  */
54 extern int x_alloc_nearest_color_for_widget __P ((Widget, Colormap, XColor*));
55 extern int x_alloc_lighter_color_for_widget __P ((Widget, Display*, Colormap,
56                                                   unsigned long *,
57                                                   double, int));
58 extern int x_catch_errors __P ((Display*));
59 extern int x_uncatch_errors __P ((Display*, int));
60 extern int x_had_errors_p __P ((Display*));
61 extern int x_clear_errors __P ((Display*));
62 extern unsigned long x_copy_dpy_color __P ((Display *, Colormap,
63                                             unsigned long));
64
65 /* Defined in xfaces.c.  */
66 extern void x_free_dpy_colors __P ((Display *, Screen *, Colormap,
67                                     unsigned long *pixels, int npixels));
68 #else /* not emacs */
69
70 #include <X11/bitmaps/gray>
71 #define gray_bitmap_width       gray_width
72 #define gray_bitmap_height      gray_height
73 #define gray_bitmap_bits        gray_bits
74
75 #endif /* not emacs */
76
77 static int pointer_grabbed;
78 static XEvent menu_post_event;
79
80 XFontStruct *xlwmenu_default_font;
81
82 static char
83 xlwMenuTranslations [] =
84 "<BtnDown>:       start()\n\
85 <Motion>:         drag()\n\
86 <BtnUp>:          select()\n\
87 <Key>Shift_L:     nothing()\n\
88 <Key>Shift_R:     nothing()\n\
89 <Key>Meta_L:      nothing()\n\
90 <Key>Meta_R:      nothing()\n\
91 <Key>Control_L:   nothing()\n\
92 <Key>Control_R:   nothing()\n\
93 <Key>Hyper_L:     nothing()\n\
94 <Key>Hyper_R:     nothing()\n\
95 <Key>Super_L:     nothing()\n\
96 <Key>Super_R:     nothing()\n\
97 <Key>Alt_L:       nothing()\n\
98 <Key>Alt_R:       nothing()\n\
99 <Key>Caps_Lock:   nothing()\n\
100 <Key>Shift_Lock:  nothing()\n\
101 <KeyUp>Shift_L:   nothing()\n\
102 <KeyUp>Shift_R:   nothing()\n\
103 <KeyUp>Meta_L:    nothing()\n\
104 <KeyUp>Meta_R:    nothing()\n\
105 <KeyUp>Control_L: nothing()\n\
106 <KeyUp>Control_R: nothing()\n\
107 <KeyUp>Hyper_L:   nothing()\n\
108 <KeyUp>Hyper_R:   nothing()\n\
109 <KeyUp>Super_L:   nothing()\n\
110 <KeyUp>Super_R:   nothing()\n\
111 <KeyUp>Alt_L:     nothing()\n\
112 <KeyUp>Alt_R:     nothing()\n\
113 <KeyUp>Caps_Lock: nothing()\n\
114 <KeyUp>Shift_Lock:nothing()\n\
115 <Key>:            key()\n\
116 <KeyUp>:          key()\n\
117 ";
118
119 #define offset(field) XtOffset(XlwMenuWidget, field)
120 static XtResource
121 xlwMenuResources[] =
122 {
123   {XtNfont,  XtCFont, XtRFontStruct, sizeof(XFontStruct *),
124      offset(menu.font),XtRString, "XtDefaultFont"},
125   {XtNforeground, XtCForeground, XtRPixel, sizeof(Pixel),
126      offset(menu.foreground), XtRString, "XtDefaultForeground"},
127   {XtNbuttonForeground, XtCButtonForeground, XtRPixel, sizeof(Pixel),
128      offset(menu.button_foreground), XtRString, "XtDefaultForeground"},
129   {XtNmargin, XtCMargin, XtRDimension,  sizeof(Dimension),
130      offset(menu.margin), XtRImmediate, (XtPointer) 4},
131   {XtNhorizontalSpacing, XtCMargin, XtRDimension,  sizeof(Dimension),
132      offset(menu.horizontal_spacing), XtRImmediate, (XtPointer)3},
133   {XtNverticalSpacing, XtCMargin, XtRDimension,  sizeof(Dimension),
134      offset(menu.vertical_spacing), XtRImmediate, (XtPointer)1},
135   {XtNarrowSpacing, XtCMargin, XtRDimension,  sizeof(Dimension),
136      offset(menu.arrow_spacing), XtRImmediate, (XtPointer)10},
137
138   {XmNshadowThickness, XmCShadowThickness, XtRDimension,
139      sizeof (Dimension), offset (menu.shadow_thickness),
140      XtRImmediate, (XtPointer) 2},
141   {XmNtopShadowColor, XmCTopShadowColor, XtRPixel, sizeof (Pixel),
142      offset (menu.top_shadow_color), XtRImmediate, (XtPointer)-1},
143   {XmNbottomShadowColor, XmCBottomShadowColor, XtRPixel, sizeof (Pixel),
144      offset (menu.bottom_shadow_color), XtRImmediate, (XtPointer)-1},
145   {XmNtopShadowPixmap, XmCTopShadowPixmap, XtRPixmap, sizeof (Pixmap),
146      offset (menu.top_shadow_pixmap), XtRImmediate, (XtPointer)None},
147   {XmNbottomShadowPixmap, XmCBottomShadowPixmap, XtRPixmap, sizeof (Pixmap),
148      offset (menu.bottom_shadow_pixmap), XtRImmediate, (XtPointer)None},
149
150   {XtNopen, XtCCallback, XtRCallback, sizeof(XtPointer),
151      offset(menu.open), XtRCallback, (XtPointer)NULL},
152   {XtNselect, XtCCallback, XtRCallback, sizeof(XtPointer),
153      offset(menu.select), XtRCallback, (XtPointer)NULL},
154   {XtNhighlightCallback, XtCCallback, XtRCallback, sizeof(XtPointer),
155      offset(menu.highlight), XtRCallback, (XtPointer)NULL},
156   {XtNmenu, XtCMenu, XtRPointer, sizeof(XtPointer),
157      offset(menu.contents), XtRImmediate, (XtPointer)NULL},
158   {XtNcursor, XtCCursor, XtRCursor, sizeof(Cursor),
159      offset(menu.cursor_shape), XtRString, (XtPointer)"right_ptr"},
160   {XtNhorizontal, XtCHorizontal, XtRInt, sizeof(int),
161      offset(menu.horizontal), XtRImmediate, (XtPointer)True},
162 };
163 #undef offset
164
165 static Boolean XlwMenuSetValues();
166 static void XlwMenuRealize();
167 static void XlwMenuRedisplay();
168 static void XlwMenuResize();
169 static void XlwMenuInitialize();
170 static void XlwMenuRedisplay();
171 static void XlwMenuDestroy();
172 static void XlwMenuClassInitialize();
173 static void Start();
174 static void Drag();
175 static void Select();
176 static void Key();
177 static void Nothing();
178 static int separator_height ();
179
180 static XtActionsRec
181 xlwMenuActionsList [] =
182 {
183   {"start",             Start},
184   {"drag",              Drag},
185   {"select",            Select},
186   {"key",               Key},
187   {"nothing",           Nothing},
188 };
189
190 #define SuperClass ((CoreWidgetClass)&coreClassRec)
191
192 XlwMenuClassRec xlwMenuClassRec =
193 {
194   {  /* CoreClass fields initialization */
195     (WidgetClass) SuperClass,           /* superclass             */
196     "XlwMenu",                          /* class_name             */
197     sizeof(XlwMenuRec),                 /* size                   */
198     XlwMenuClassInitialize,             /* class_initialize       */
199     NULL,                               /* class_part_initialize  */
200     FALSE,                              /* class_inited           */
201     XlwMenuInitialize,                  /* initialize             */
202     NULL,                               /* initialize_hook        */
203     XlwMenuRealize,                     /* realize                */
204     xlwMenuActionsList,                 /* actions                */
205     XtNumber(xlwMenuActionsList),       /* num_actions            */
206     xlwMenuResources,                   /* resources              */
207     XtNumber(xlwMenuResources),         /* resource_count         */
208     NULLQUARK,                          /* xrm_class              */
209     TRUE,                               /* compress_motion        */
210     TRUE,                               /* compress_exposure      */
211     TRUE,                               /* compress_enterleave    */
212     FALSE,                              /* visible_interest       */
213     XlwMenuDestroy,                     /* destroy                */
214     XlwMenuResize,                      /* resize                 */
215     XlwMenuRedisplay,                   /* expose                 */
216     XlwMenuSetValues,                   /* set_values             */
217     NULL,                               /* set_values_hook        */
218     XtInheritSetValuesAlmost,           /* set_values_almost      */
219     NULL,                               /* get_values_hook        */
220     NULL,                               /* accept_focus           */
221     XtVersion,                          /* version                */
222     NULL,                               /* callback_private       */
223     xlwMenuTranslations,                /* tm_table               */
224     XtInheritQueryGeometry,             /* query_geometry         */
225     XtInheritDisplayAccelerator,        /* display_accelerator    */
226     NULL                                /* extension              */
227   },  /* XlwMenuClass fields initialization */
228   {
229     0                                   /* dummy */
230   },
231 };
232
233 WidgetClass xlwMenuWidgetClass = (WidgetClass) &xlwMenuClassRec;
234
235 int submenu_destroyed;
236
237 static int next_release_must_exit;
238
239 /* Utilities */
240
241
242 /* Like abort, but remove grabs from widget W before.  */
243
244 static void
245 abort_gracefully (w)
246      Widget w;
247 {
248   if (XtIsShell (XtParent (w)))
249     XtRemoveGrab (w);
250   XtUngrabPointer (w, CurrentTime);
251   abort ();
252 }
253
254 static void
255 push_new_stack (mw, val)
256      XlwMenuWidget mw;
257      widget_value* val;
258 {
259   if (!mw->menu.new_stack)
260     {
261       mw->menu.new_stack_length = 10;
262       mw->menu.new_stack =
263         (widget_value**)XtCalloc (mw->menu.new_stack_length,
264                                   sizeof (widget_value*));
265     }
266   else if (mw->menu.new_depth == mw->menu.new_stack_length)
267     {
268       mw->menu.new_stack_length *= 2;
269       mw->menu.new_stack =
270         (widget_value**)XtRealloc ((char*)mw->menu.new_stack,
271                                    mw->menu.new_stack_length * sizeof (widget_value*));
272     }
273   mw->menu.new_stack [mw->menu.new_depth++] = val;
274 }
275
276 static void
277 pop_new_stack_if_no_contents (mw)
278      XlwMenuWidget mw;
279 {
280   if (mw->menu.new_depth)
281     {
282       if (!mw->menu.new_stack [mw->menu.new_depth - 1]->contents)
283         mw->menu.new_depth -= 1;
284     }
285 }
286
287 static void
288 make_old_stack_space (mw, n)
289      XlwMenuWidget mw;
290      int n;
291 {
292   if (!mw->menu.old_stack)
293     {
294       mw->menu.old_stack_length = 10;
295       mw->menu.old_stack =
296         (widget_value**)XtCalloc (mw->menu.old_stack_length,
297                                   sizeof (widget_value*));
298     }
299   else if (mw->menu.old_stack_length < n)
300     {
301       mw->menu.old_stack_length *= 2;
302       mw->menu.old_stack =
303         (widget_value**)XtRealloc ((char*)mw->menu.old_stack,
304                                    mw->menu.old_stack_length * sizeof (widget_value*));
305     }
306 }
307
308 /* Size code */
309 int
310 string_width (mw, s)
311      XlwMenuWidget mw;
312      char *s;
313 {
314   XCharStruct xcs;
315   int drop;
316
317   XTextExtents (mw->menu.font, s, strlen (s), &drop, &drop, &drop, &xcs);
318   return xcs.width;
319 }
320
321 static int
322 arrow_width (mw)
323      XlwMenuWidget mw;
324 {
325   return (mw->menu.font->ascent * 3/4) | 1;
326 }
327
328 /* Return the width of toggle buttons of widget MW.  */
329
330 static int
331 toggle_button_width (mw)
332      XlwMenuWidget mw;
333 {
334   return ((mw->menu.font->ascent + mw->menu.font->descent) * 2 / 3) | 1;
335 }
336
337
338 /* Return the width of radio buttons of widget MW.  */
339
340 static int
341 radio_button_width (mw)
342      XlwMenuWidget mw;
343 {
344   return toggle_button_width (mw) * 1.41;
345 }
346
347
348 static XtResource
349 nameResource[] =
350 {
351   {"labelString",  "LabelString", XtRString, sizeof(String),
352      0, XtRImmediate, 0},
353 };
354
355 static char*
356 resource_widget_value (mw, val)
357      XlwMenuWidget mw;
358      widget_value *val;
359 {
360   if (!val->toolkit_data)
361     {
362       char* resourced_name = NULL;
363       char* complete_name;
364       XtGetSubresources ((Widget) mw,
365                          (XtPointer) &resourced_name,
366                          val->name, val->name,
367                          nameResource, 1, NULL, 0);
368       if (!resourced_name)
369         resourced_name = val->name;
370       if (!val->value)
371         {
372           complete_name = (char *) XtMalloc (strlen (resourced_name) + 1);
373           strcpy (complete_name, resourced_name);
374         }
375       else
376         {
377           int complete_length =
378             strlen (resourced_name) + strlen (val->value) + 2;
379           complete_name = XtMalloc (complete_length);
380           *complete_name = 0;
381           strcat (complete_name, resourced_name);
382           strcat (complete_name, " ");
383           strcat (complete_name, val->value);
384         }
385
386       val->toolkit_data = complete_name;
387       val->free_toolkit_data = True;
388     }
389   return (char*)val->toolkit_data;
390 }
391
392 /* Returns the sizes of an item */
393 static void
394 size_menu_item (mw, val, horizontal_p, label_width, rest_width, button_width,
395                 height)
396      XlwMenuWidget mw;
397      widget_value* val;
398      int horizontal_p;
399      int* label_width;
400      int* rest_width;
401      int* button_width;
402      int* height;
403 {
404   enum menu_separator separator;
405
406   if (lw_separator_p (val->name, &separator, 0))
407     {
408       *height = separator_height (separator);
409       *label_width = 1;
410       *rest_width = 0;
411       *button_width = 0;
412     }
413   else
414     {
415       *height =
416         mw->menu.font->ascent + mw->menu.font->descent
417           + 2 * mw->menu.vertical_spacing + 2 * mw->menu.shadow_thickness;
418
419       *label_width =
420         string_width (mw, resource_widget_value (mw, val))
421           + mw->menu.horizontal_spacing + mw->menu.shadow_thickness;
422
423       *rest_width =  mw->menu.horizontal_spacing + mw->menu.shadow_thickness;
424       if (!horizontal_p)
425         {
426           if (val->contents)
427             /* Add width of the arrow displayed for submenus.  */
428             *rest_width += arrow_width (mw) + mw->menu.arrow_spacing;
429           else if (val->key)
430             /* Add width of key equivalent string.  */
431             *rest_width += (string_width (mw, val->key)
432                             + mw->menu.arrow_spacing);
433
434           if (val->button_type == BUTTON_TYPE_TOGGLE)
435             *button_width = (toggle_button_width (mw)
436                              + mw->menu.horizontal_spacing);
437           else if (val->button_type == BUTTON_TYPE_RADIO)
438             *button_width = (radio_button_width (mw)
439                              + mw->menu.horizontal_spacing);
440         }
441     }
442 }
443
444 static void
445 size_menu (mw, level)
446      XlwMenuWidget mw;
447      int level;
448 {
449   unsigned int  label_width = 0;
450   int           rest_width = 0;
451   int           button_width = 0;
452   int           max_rest_width = 0;
453   int           max_button_width = 0;
454   unsigned int  height = 0;
455   int           horizontal_p = mw->menu.horizontal && (level == 0);
456   widget_value* val;
457   window_state* ws;
458
459   if (level >= mw->menu.old_depth)
460     abort_gracefully ((Widget) mw);
461
462   ws = &mw->menu.windows [level];
463   ws->width = 0;
464   ws->height = 0;
465   ws->label_width = 0;
466   ws->button_width = 0;
467
468   for (val = mw->menu.old_stack [level]->contents; val; val = val->next)
469     {
470       size_menu_item (mw, val, horizontal_p, &label_width, &rest_width,
471                       &button_width, &height);
472       if (horizontal_p)
473         {
474           ws->width += label_width + rest_width;
475           if (height > ws->height)
476             ws->height = height;
477         }
478       else
479         {
480           if (label_width > ws->label_width)
481             ws->label_width = label_width;
482           if (rest_width > max_rest_width)
483             max_rest_width = rest_width;
484           if (button_width > max_button_width)
485             max_button_width = button_width;
486           ws->height += height;
487         }
488     }
489
490   if (horizontal_p)
491     ws->label_width = ws->button_width = 0;
492   else
493     {
494       ws->width = ws->label_width + max_rest_width + max_button_width;
495       ws->button_width = max_button_width;
496     }
497
498   ws->width += 2 * mw->menu.shadow_thickness;
499   ws->height += 2 * mw->menu.shadow_thickness;
500
501   if (horizontal_p)
502     {
503       ws->width += 2 * mw->menu.margin;
504       ws->height += 2 * mw->menu.margin;
505     }
506 }
507
508
509 /* Display code */
510
511 static void