root/trunk/lwlib/xlwmenu.c

Revision 4220, 70.5 kB (checked in by miyoshi, 6 months ago)

Sync up with Emacs22.2.

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