xlwmenu.c 69.5 KB
Newer Older
1
/* Implements a lightweight menubar widget.
Richard M. Stallman's avatar
Richard M. Stallman committed
2
   Copyright (C) 1992 Lucid, Inc.
3
   Copyright (C) 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
Richard M. Stallman's avatar
Richard M. Stallman committed
4 5 6

This file is part of the Lucid Widget Library.

7
The Lucid Widget Library is free software; you can redistribute it and/or
Richard M. Stallman's avatar
Richard M. Stallman committed
8 9 10 11 12
modify it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.

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
Richard M. Stallman's avatar
Richard M. Stallman committed
14 15 16 17
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
Karl Heuer's avatar
Karl Heuer committed
18
along with GNU Emacs; see the file COPYING.  If not, write to the
Lute Kamstra's avatar
Lute Kamstra committed
19 20
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.  */
Richard M. Stallman's avatar
Richard M. Stallman committed
21 22 23

/* Created by devin@lucid.com */

24 25 26 27
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

Dave Love's avatar
Dave Love committed
28
#include "lisp.h"
Richard M. Stallman's avatar
Richard M. Stallman committed
29

Richard M. Stallman's avatar
Richard M. Stallman committed
30 31 32
#include <stdio.h>

#include <sys/types.h>
33 34
#if (defined __sun) && !(defined SUNOS41)
#define SUNOS41
Richard M. Stallman's avatar
Richard M. Stallman committed
35
#include <X11/Xos.h>
36 37 38 39
#undef SUNOS41
#else
#include <X11/Xos.h>
#endif
Richard M. Stallman's avatar
Richard M. Stallman committed
40
#include <X11/IntrinsicP.h>
Paul Reilly's avatar
Paul Reilly committed
41
#include <X11/ObjectP.h>
Richard M. Stallman's avatar
Richard M. Stallman committed
42 43 44
#include <X11/StringDefs.h>
#include <X11/cursorfont.h>
#include "xlwmenuP.h"
45

46
#ifdef emacs
47 48 49 50 51 52 53

/* Defined in xfns.c.  When config.h defines `static' as empty, we get
   redefinition errors when gray_bitmap is included more than once, so
   we're referring to the one include in xfns.c here.  */

extern int gray_bitmap_width;
extern int gray_bitmap_height;
54
extern char *gray_bitmap_bits;
55

56 57
#include "xterm.h"

58 59 60 61 62 63 64 65
#else /* not emacs */

#include <X11/bitmaps/gray>
#define gray_bitmap_width	gray_width
#define gray_bitmap_height	gray_height
#define gray_bitmap_bits	gray_bits

#endif /* not emacs */
Paul Reilly's avatar
Paul Reilly committed
66 67 68

static int pointer_grabbed;
static XEvent menu_post_event;
Richard M. Stallman's avatar
Richard M. Stallman committed
69

70 71
XFontStruct *xlwmenu_default_font;

72 73
static char
xlwMenuTranslations [] =
74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
"<BtnDown>:	  start()\n\
<Motion>:	  drag()\n\
<BtnUp>:	  select()\n\
<Key>Shift_L:     nothing()\n\
<Key>Shift_R:     nothing()\n\
<Key>Meta_L:      nothing()\n\
<Key>Meta_R:      nothing()\n\
<Key>Control_L:   nothing()\n\
<Key>Control_R:   nothing()\n\
<Key>Hyper_L:     nothing()\n\
<Key>Hyper_R:     nothing()\n\
<Key>Super_L:     nothing()\n\
<Key>Super_R:     nothing()\n\
<Key>Alt_L:       nothing()\n\
<Key>Alt_R:       nothing()\n\
<Key>Caps_Lock:   nothing()\n\
<Key>Shift_Lock:  nothing()\n\
<KeyUp>Shift_L:   nothing()\n\
<KeyUp>Shift_R:   nothing()\n\
<KeyUp>Meta_L:    nothing()\n\
<KeyUp>Meta_R:    nothing()\n\
<KeyUp>Control_L: nothing()\n\
<KeyUp>Control_R: nothing()\n\
<KeyUp>Hyper_L:   nothing()\n\
<KeyUp>Hyper_R:   nothing()\n\
<KeyUp>Super_L:   nothing()\n\
<KeyUp>Super_R:   nothing()\n\
<KeyUp>Alt_L:     nothing()\n\
<KeyUp>Alt_R:     nothing()\n\
<KeyUp>Caps_Lock: nothing()\n\
<KeyUp>Shift_Lock:nothing()\n\
105 106 107 108 109
<Key>Return:      select()\n\
<Key>Down:        down()\n\
<Key>Up:          up()\n\
<Key>Left:        left()\n\
<Key>Right:       right()\n\
110 111
<Key>:            key()\n\
<KeyUp>:          key()\n\
Richard M. Stallman's avatar
Richard M. Stallman committed
112 113
";

114 115 116 117 118
/* FIXME: Space should toggle toggleable menu item but not remove the menu
   so you can toggle the next one without entering the menu again.  */

/* FIXME: Should ESC close one level of menu structure or the complete menu?  */

Pavel Janík's avatar
Pavel Janík committed
119
/* FIXME: F10 should enter the menu, the first one in the menu-bar.  */
120

Richard M. Stallman's avatar
Richard M. Stallman committed
121
#define offset(field) XtOffset(XlwMenuWidget, field)
122
static XtResource
Richard M. Stallman's avatar
Richard M. Stallman committed
123
xlwMenuResources[] =
124
{
125
#ifdef HAVE_X_I18N
Jan Djärv's avatar
Jan Djärv committed
126 127 128
  {XtNfontSet,  XtCFontSet, XtRFontSet, sizeof(XFontSet),
     offset(menu.fontSet), XtRFontSet, NULL},
#endif
Richard M. Stallman's avatar
Richard M. Stallman committed
129
  {XtNfont,  XtCFont, XtRFontStruct, sizeof(XFontStruct *),
130
     offset(menu.font), XtRString, "XtDefaultFont"},
Richard M. Stallman's avatar
Richard M. Stallman committed
131 132
  {XtNforeground, XtCForeground, XtRPixel, sizeof(Pixel),
     offset(menu.foreground), XtRString, "XtDefaultForeground"},
133 134
  {XtNdisabledForeground, XtCDisabledForeground, XtRPixel, sizeof(Pixel),
   offset(menu.disabled_foreground), XtRString, (XtPointer)NULL},
Richard M. Stallman's avatar
Richard M. Stallman committed
135 136 137
  {XtNbuttonForeground, XtCButtonForeground, XtRPixel, sizeof(Pixel),
     offset(menu.button_foreground), XtRString, "XtDefaultForeground"},
  {XtNmargin, XtCMargin, XtRDimension,  sizeof(Dimension),
Pavel Janík's avatar
Pavel Janík committed
138
     offset(menu.margin), XtRImmediate, (XtPointer)1},
Richard M. Stallman's avatar
Richard M. Stallman committed
139 140 141
  {XtNhorizontalSpacing, XtCMargin, XtRDimension,  sizeof(Dimension),
     offset(menu.horizontal_spacing), XtRImmediate, (XtPointer)3},
  {XtNverticalSpacing, XtCMargin, XtRDimension,  sizeof(Dimension),
Pavel Janík's avatar
Pavel Janík committed
142
     offset(menu.vertical_spacing), XtRImmediate, (XtPointer)2},
Richard M. Stallman's avatar
Richard M. Stallman committed
143 144 145
  {XtNarrowSpacing, XtCMargin, XtRDimension,  sizeof(Dimension),
     offset(menu.arrow_spacing), XtRImmediate, (XtPointer)10},

Paul Reilly's avatar
Paul Reilly committed
146
  {XmNshadowThickness, XmCShadowThickness, XtRDimension,
Richard M. Stallman's avatar
Richard M. Stallman committed
147
     sizeof (Dimension), offset (menu.shadow_thickness),
Pavel Janík's avatar
Pavel Janík committed
148
     XtRImmediate, (XtPointer)1},
Richard M. Stallman's avatar
Richard M. Stallman committed
149 150 151 152 153 154 155 156 157
  {XmNtopShadowColor, XmCTopShadowColor, XtRPixel, sizeof (Pixel),
     offset (menu.top_shadow_color), XtRImmediate, (XtPointer)-1},
  {XmNbottomShadowColor, XmCBottomShadowColor, XtRPixel, sizeof (Pixel),
     offset (menu.bottom_shadow_color), XtRImmediate, (XtPointer)-1},
  {XmNtopShadowPixmap, XmCTopShadowPixmap, XtRPixmap, sizeof (Pixmap),
     offset (menu.top_shadow_pixmap), XtRImmediate, (XtPointer)None},
  {XmNbottomShadowPixmap, XmCBottomShadowPixmap, XtRPixmap, sizeof (Pixmap),
     offset (menu.bottom_shadow_pixmap), XtRImmediate, (XtPointer)None},

158
  {XtNopen, XtCCallback, XtRCallback, sizeof(XtPointer),
Richard M. Stallman's avatar
Richard M. Stallman committed
159
     offset(menu.open), XtRCallback, (XtPointer)NULL},
160
  {XtNselect, XtCCallback, XtRCallback, sizeof(XtPointer),
Richard M. Stallman's avatar
Richard M. Stallman committed
161
     offset(menu.select), XtRCallback, (XtPointer)NULL},
162
  {XtNhighlightCallback, XtCCallback, XtRCallback, sizeof(XtPointer),
163
     offset(menu.highlight), XtRCallback, (XtPointer)NULL},
Richard M. Stallman's avatar
Richard M. Stallman committed
164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182
  {XtNmenu, XtCMenu, XtRPointer, sizeof(XtPointer),
     offset(menu.contents), XtRImmediate, (XtPointer)NULL},
  {XtNcursor, XtCCursor, XtRCursor, sizeof(Cursor),
     offset(menu.cursor_shape), XtRString, (XtPointer)"right_ptr"},
  {XtNhorizontal, XtCHorizontal, XtRInt, sizeof(int),
     offset(menu.horizontal), XtRImmediate, (XtPointer)True},
};
#undef offset

static Boolean XlwMenuSetValues();
static void XlwMenuRealize();
static void XlwMenuRedisplay();
static void XlwMenuResize();
static void XlwMenuInitialize();
static void XlwMenuRedisplay();
static void XlwMenuDestroy();
static void XlwMenuClassInitialize();
static void Start();
static void Drag();
183 184 185 186
static void Down();
static void Up();
static void Left();
static void Right();
Richard M. Stallman's avatar
Richard M. Stallman committed
187
static void Select();
188 189
static void Key();
static void Nothing();
Dave Love's avatar
Dave Love committed
190
static int separator_height __P ((enum menu_separator));
191 192
static void pop_up_menu __P ((XlwMenuWidget, XButtonPressedEvent *));

Richard M. Stallman's avatar
Richard M. Stallman committed
193

194
static XtActionsRec
Richard M. Stallman's avatar
Richard M. Stallman committed
195 196 197 198
xlwMenuActionsList [] =
{
  {"start",		Start},
  {"drag",		Drag},
199 200 201 202
  {"down",		Down},
  {"up",		Up},
  {"left",		Left},
  {"right",		Right},
Richard M. Stallman's avatar
Richard M. Stallman committed
203
  {"select",		Select},
204
  {"key",		Key},
205
  {"MenuGadgetEscape",  Key},   /* Compatibility with Lesstif/Motif.  */
206
  {"nothing",		Nothing},
Richard M. Stallman's avatar
Richard M. Stallman committed
207 208 209 210 211 212 213
};

#define SuperClass ((CoreWidgetClass)&coreClassRec)

XlwMenuClassRec xlwMenuClassRec =
{
  {  /* CoreClass fields initialization */
214
    (WidgetClass) SuperClass,		/* superclass		  */
Richard M. Stallman's avatar
Richard M. Stallman committed
215 216 217 218 219 220 221 222 223 224 225 226 227 228
    "XlwMenu",				/* class_name		  */
    sizeof(XlwMenuRec),			/* size			  */
    XlwMenuClassInitialize,		/* class_initialize	  */
    NULL,				/* class_part_initialize  */
    FALSE,				/* class_inited		  */
    XlwMenuInitialize,			/* initialize		  */
    NULL,				/* initialize_hook	  */
    XlwMenuRealize,			/* realize		  */
    xlwMenuActionsList,			/* actions		  */
    XtNumber(xlwMenuActionsList),	/* num_actions		  */
    xlwMenuResources,			/* resources		  */
    XtNumber(xlwMenuResources),		/* resource_count	  */
    NULLQUARK,				/* xrm_class		  */
    TRUE,				/* compress_motion	  */
229
    XtExposeCompressMaximal,		/* compress_exposure	  */
Richard M. Stallman's avatar
Richard M. Stallman committed
230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253
    TRUE,				/* compress_enterleave    */
    FALSE,				/* visible_interest	  */
    XlwMenuDestroy,			/* destroy		  */
    XlwMenuResize,			/* resize		  */
    XlwMenuRedisplay,			/* expose		  */
    XlwMenuSetValues,			/* set_values		  */
    NULL,				/* set_values_hook	  */
    XtInheritSetValuesAlmost,		/* set_values_almost	  */
    NULL,				/* get_values_hook	  */
    NULL,				/* accept_focus		  */
    XtVersion,				/* version		  */
    NULL,				/* callback_private	  */
    xlwMenuTranslations,		/* tm_table		  */
    XtInheritQueryGeometry,		/* query_geometry	  */
    XtInheritDisplayAccelerator,	/* display_accelerator	  */
    NULL				/* extension		  */
  },  /* XlwMenuClass fields initialization */
  {
    0					/* dummy */
  },
};

WidgetClass xlwMenuWidgetClass = (WidgetClass) &xlwMenuClassRec;

Paul Reilly's avatar
Paul Reilly committed
254 255
int submenu_destroyed;

256 257 258 259
/* For debug, if installation-directory is non-nil this is not an installed
   Emacs.   In that case we do not grab the keyboard to make it easier to
   debug. */
#define GRAB_KEYBOARD  (EQ (Vinstallation_directory, Qnil))
260

Paul Reilly's avatar
Paul Reilly committed
261 262
static int next_release_must_exit;

Richard M. Stallman's avatar
Richard M. Stallman committed
263
/* Utilities */
264

265 266 267 268 269 270 271
/* Ungrab pointer and keyboard */
static void
ungrab_all (w, ungrabtime)
     Widget w;
     Time ungrabtime;
{
  XtUngrabPointer (w, ungrabtime);
272
  if (GRAB_KEYBOARD) XtUngrabKeyboard (w, ungrabtime);
273
}
274 275 276 277 278 279 280 281 282

/* Like abort, but remove grabs from widget W before.  */

static void
abort_gracefully (w)
     Widget w;
{
  if (XtIsShell (XtParent (w)))
    XtRemoveGrab (w);
283
  ungrab_all (w, CurrentTime);
284 285 286
  abort ();
}

Richard M. Stallman's avatar
Richard M. Stallman committed
287
static void
Paul Reilly's avatar
Paul Reilly committed
288 289 290
push_new_stack (mw, val)
     XlwMenuWidget mw;
     widget_value* val;
Richard M. Stallman's avatar
Richard M. Stallman committed
291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309
{
  if (!mw->menu.new_stack)
    {
      mw->menu.new_stack_length = 10;
      mw->menu.new_stack =
	(widget_value**)XtCalloc (mw->menu.new_stack_length,
				  sizeof (widget_value*));
    }
  else if (mw->menu.new_depth == mw->menu.new_stack_length)
    {
      mw->menu.new_stack_length *= 2;
      mw->menu.new_stack =
	(widget_value**)XtRealloc ((char*)mw->menu.new_stack,
				   mw->menu.new_stack_length * sizeof (widget_value*));
    }
  mw->menu.new_stack [mw->menu.new_depth++] = val;
}

static void
Paul Reilly's avatar
Paul Reilly committed
310 311
pop_new_stack_if_no_contents (mw)
     XlwMenuWidget mw;
Richard M. Stallman's avatar
Richard M. Stallman committed
312
{
313
  if (mw->menu.new_depth > 1)
Richard M. Stallman's avatar
Richard M. Stallman committed
314 315 316 317 318 319 320
    {
      if (!mw->menu.new_stack [mw->menu.new_depth - 1]->contents)
	mw->menu.new_depth -= 1;
    }
}

static void
Paul Reilly's avatar
Paul Reilly committed
321 322 323
make_old_stack_space (mw, n)
     XlwMenuWidget mw;
     int n;
Richard M. Stallman's avatar
Richard M. Stallman committed
324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341
{
  if (!mw->menu.old_stack)
    {
      mw->menu.old_stack_length = 10;
      mw->menu.old_stack =
	(widget_value**)XtCalloc (mw->menu.old_stack_length,
				  sizeof (widget_value*));
    }
  else if (mw->menu.old_stack_length < n)
    {
      mw->menu.old_stack_length *= 2;
      mw->menu.old_stack =
	(widget_value**)XtRealloc ((char*)mw->menu.old_stack,
				   mw->menu.old_stack_length * sizeof (widget_value*));
    }
}

/* Size code */
342
static int
Paul Reilly's avatar
Paul Reilly committed
343 344 345
string_width (mw, s)
     XlwMenuWidget mw;
     char *s;
Richard M. Stallman's avatar
Richard M. Stallman committed
346 347 348
{
  XCharStruct xcs;
  int drop;
Jan Djärv's avatar
Jan Djärv committed
349 350 351 352 353 354 355 356
#ifdef HAVE_X_I18N
  XRectangle ink, logical;
  if (mw->menu.fontSet)
    {
      XmbTextExtents (mw->menu.fontSet, s, strlen (s), &ink, &logical);
      return logical.width;
    }
#endif
357

Richard M. Stallman's avatar
Richard M. Stallman committed
358 359
  XTextExtents (mw->menu.font, s, strlen (s), &drop, &drop, &drop, &xcs);
  return xcs.width;
Jan Djärv's avatar
Jan Djärv committed
360

Richard M. Stallman's avatar
Richard M. Stallman committed
361 362
}

363 364
#ifdef HAVE_X_I18N
#define MENU_FONT_HEIGHT(mw) \
Jan Djärv's avatar
Jan Djärv committed
365 366 367
  ((mw)->menu.fontSet != NULL \
   ? (mw)->menu.font_extents->max_logical_extent.height   \
   : (mw)->menu.font->ascent + (mw)->menu.font->descent)
368
#define MENU_FONT_ASCENT(mw) \
Jan Djärv's avatar
Jan Djärv committed
369 370 371
  ((mw)->menu.fontSet != NULL \
   ? - (mw)->menu.font_extents->max_logical_extent.y \
   : (mw)->menu.font->ascent)
372 373 374 375 376 377
#else
#define MENU_FONT_HEIGHT(mw) \
  ((mw)->menu.font->ascent + (mw)->menu.font->descent)
#define MENU_FONT_ASCENT(mw) ((mw)->menu.font->ascent)
#endif

Richard M. Stallman's avatar
Richard M. Stallman committed
378
static int
Paul Reilly's avatar
Paul Reilly committed
379 380
arrow_width (mw)
     XlwMenuWidget mw;
Richard M. Stallman's avatar
Richard M. Stallman committed
381
{
382
  return (MENU_FONT_ASCENT (mw) * 3/4) | 1;
Richard M. Stallman's avatar
Richard M. Stallman committed
383 384
}

385 386 387 388 389 390
/* Return the width of toggle buttons of widget MW.  */

static int
toggle_button_width (mw)
     XlwMenuWidget mw;
{
391
  return (MENU_FONT_HEIGHT (mw) * 2 / 3) | 1;
392 393 394 395 396 397 398 399 400 401 402 403 404
}


/* Return the width of radio buttons of widget MW.  */

static int
radio_button_width (mw)
     XlwMenuWidget mw;
{
  return toggle_button_width (mw) * 1.41;
}


Richard M. Stallman's avatar
Richard M. Stallman committed
405 406
static XtResource
nameResource[] =
407
{
Richard M. Stallman's avatar
Richard M. Stallman committed
408 409 410 411 412
  {"labelString",  "LabelString", XtRString, sizeof(String),
     0, XtRImmediate, 0},
};

static char*
Paul Reilly's avatar
Paul Reilly committed
413 414 415
resource_widget_value (mw, val)
     XlwMenuWidget mw;
     widget_value *val;
Richard M. Stallman's avatar
Richard M. Stallman committed
416 417 418 419 420 421 422 423 424 425 426 427
{
  if (!val->toolkit_data)
    {
      char* resourced_name = NULL;
      char* complete_name;
      XtGetSubresources ((Widget) mw,
			 (XtPointer) &resourced_name,
			 val->name, val->name,
			 nameResource, 1, NULL, 0);
      if (!resourced_name)
	resourced_name = val->name;
      if (!val->value)
428 429 430 431
	{
	  complete_name = (char *) XtMalloc (strlen (resourced_name) + 1);
	  strcpy (complete_name, resourced_name);
	}
Richard M. Stallman's avatar
Richard M. Stallman committed
432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450
      else
	{
	  int complete_length =
	    strlen (resourced_name) + strlen (val->value) + 2;
	  complete_name = XtMalloc (complete_length);
	  *complete_name = 0;
	  strcat (complete_name, resourced_name);
	  strcat (complete_name, " ");
	  strcat (complete_name, val->value);
	}

      val->toolkit_data = complete_name;
      val->free_toolkit_data = True;
    }
  return (char*)val->toolkit_data;
}

/* Returns the sizes of an item */
static void
451 452
size_menu_item (mw, val, horizontal_p, label_width, rest_width, button_width,
		height)
Paul Reilly's avatar
Paul Reilly committed
453 454 455 456 457
     XlwMenuWidget mw;
     widget_value* val;
     int horizontal_p;
     int* label_width;
     int* rest_width;
458
     int* button_width;
Paul Reilly's avatar
Paul Reilly committed
459
     int* height;
Richard M. Stallman's avatar
Richard M. Stallman committed
460
{
461
  enum menu_separator separator;
462

463
  if (lw_separator_p (val->name, &separator, 0))
Richard M. Stallman's avatar
Richard M. Stallman committed
464
    {
465
      *height = separator_height (separator);
Richard M. Stallman's avatar
Richard M. Stallman committed
466 467
      *label_width = 1;
      *rest_width = 0;
468
      *button_width = 0;
Richard M. Stallman's avatar
Richard M. Stallman committed
469 470 471
    }
  else
    {
472 473
      *height = MENU_FONT_HEIGHT (mw)
	+ 2 * mw->menu.vertical_spacing + 2 * mw->menu.shadow_thickness;
474

Richard M. Stallman's avatar
Richard M. Stallman committed
475 476 477
      *label_width =
	string_width (mw, resource_widget_value (mw, val))
	  + mw->menu.horizontal_spacing + mw->menu.shadow_thickness;
478

Richard M. Stallman's avatar
Richard M. Stallman committed
479 480 481 482
      *rest_width =  mw->menu.horizontal_spacing + mw->menu.shadow_thickness;
      if (!horizontal_p)
	{
	  if (val->contents)
483
	    /* Add width of the arrow displayed for submenus.  */
Richard M. Stallman's avatar
Richard M. Stallman committed
484 485
	    *rest_width += arrow_width (mw) + mw->menu.arrow_spacing;
	  else if (val->key)
486 487 488 489 490 491 492 493 494 495
	    /* Add width of key equivalent string.  */
	    *rest_width += (string_width (mw, val->key)
			    + mw->menu.arrow_spacing);

	  if (val->button_type == BUTTON_TYPE_TOGGLE)
	    *button_width = (toggle_button_width (mw)
			     + mw->menu.horizontal_spacing);
	  else if (val->button_type == BUTTON_TYPE_RADIO)
	    *button_width = (radio_button_width (mw)
			     + mw->menu.horizontal_spacing);
Richard M. Stallman's avatar
Richard M. Stallman committed
496 497 498 499 500
	}
    }
}

static void
Paul Reilly's avatar
Paul Reilly committed
501 502 503
size_menu (mw, level)
     XlwMenuWidget mw;
     int level;
Richard M. Stallman's avatar
Richard M. Stallman committed
504
{
505
  unsigned int  label_width = 0;
Richard M. Stallman's avatar
Richard M. Stallman committed
506
  int		rest_width = 0;
507
  int		button_width = 0;
Richard M. Stallman's avatar
Richard M. Stallman committed
508
  int		max_rest_width = 0;
509
  int		max_button_width = 0;
510
  unsigned int  height = 0;
Richard M. Stallman's avatar
Richard M. Stallman committed
511 512 513 514 515
  int		horizontal_p = mw->menu.horizontal && (level == 0);
  widget_value*	val;
  window_state*	ws;

  if (level >= mw->menu.old_depth)
516
    abort_gracefully ((Widget) mw);
Richard M. Stallman's avatar
Richard M. Stallman committed
517

518
  ws = &mw->menu.windows [level];
Richard M. Stallman's avatar
Richard M. Stallman committed
519 520 521
  ws->width = 0;
  ws->height = 0;
  ws->label_width = 0;
522
  ws->button_width = 0;
Richard M. Stallman's avatar
Richard M. Stallman committed
523 524 525 526

  for (val = mw->menu.old_stack [level]->contents; val; val = val->next)
    {
      size_menu_item (mw, val, horizontal_p, &label_width, &rest_width,
527
		      &button_width, &height);
Richard M. Stallman's avatar
Richard M. Stallman committed
528 529 530 531 532 533 534 535 536 537 538 539
      if (horizontal_p)
	{
	  ws->width += label_width + rest_width;
	  if (height > ws->height)
	    ws->height = height;
	}
      else
	{
	  if (label_width > ws->label_width)
	    ws->label_width = label_width;
	  if (rest_width > max_rest_width)
	    max_rest_width = rest_width;
540 541
	  if (button_width > max_button_width)
	    max_button_width = button_width;
Richard M. Stallman's avatar
Richard M. Stallman committed
542 543 544
	  ws->height += height;
	}
    }
545

Richard M. Stallman's avatar
Richard M. Stallman committed
546
  if (horizontal_p)
547
    ws->label_width = ws->button_width = 0;
Richard M. Stallman's avatar
Richard M. Stallman committed
548
  else
549 550 551 552
    {
      ws->width = ws->label_width + max_rest_width + max_button_width;
      ws->button_width = max_button_width;
    }
Richard M. Stallman's avatar
Richard M. Stallman committed
553 554 555

  ws->width += 2 * mw->menu.shadow_thickness;
  ws->height += 2 * mw->menu.shadow_thickness;
556 557 558 559 560 561

  if (horizontal_p)
    {
      ws->width += 2 * mw->menu.margin;
      ws->height += 2 * mw->menu.margin;
    }
Richard M. Stallman's avatar
Richard M. Stallman committed
562 563 564 565
}


/* Display code */
566

Richard M. Stallman's avatar
Richard M. Stallman committed
567
static void
568
draw_arrow (mw, window, gc, x, y, width, down_p)
Paul Reilly's avatar
Paul Reilly committed
569 570 571 572 573 574
     XlwMenuWidget mw;
     Window window;
     GC gc;
     int x;
     int y;
     int width;
575
     int down_p;
Richard M. Stallman's avatar
Richard M. Stallman committed
576
{
577 578 579 580 581 582 583 584 585 586
  Display *dpy = XtDisplay (mw);
  GC top_gc = mw->menu.shadow_top_gc;
  GC bottom_gc = mw->menu.shadow_bottom_gc;
  int thickness = mw->menu.shadow_thickness;
  int height = width;
  XPoint pt[10];
  /* alpha = atan (0.5)
     factor = (1 + sin (alpha)) / cos (alpha) */
  double factor = 1.62;
  int thickness2 = thickness * factor;
587

588
  y += (MENU_FONT_HEIGHT (mw) - height) / 2;
589

590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606
  if (down_p)
    {
      GC temp;
      temp = top_gc;
      top_gc = bottom_gc;
      bottom_gc = temp;
    }

  pt[0].x = x;
  pt[0].y = y + height;
  pt[1].x = x + thickness;
  pt[1].y = y + height - thickness2;
  pt[2].x = x + thickness2;
  pt[2].y = y + thickness2;
  pt[3].x = x;
  pt[3].y = y;
  XFillPolygon (dpy, window, top_gc, pt, 4, Convex, CoordModeOrigin);
607

608 609 610 611 612 613 614 615 616
  pt[0].x = x;
  pt[0].y = y;
  pt[1].x = x + thickness;
  pt[1].y = y + thickness2;
  pt[2].x = x + width - thickness2;
  pt[2].y = y + height / 2;
  pt[3].x = x + width;
  pt[3].y = y + height / 2;
  XFillPolygon (dpy, window, top_gc, pt, 4, Convex, CoordModeOrigin);
617

618 619 620 621 622 623 624 625 626
  pt[0].x = x;
  pt[0].y = y + height;
  pt[1].x = x + thickness;
  pt[1].y = y + height - thickness2;
  pt[2].x = x + width - thickness2;
  pt[2].y = y + height / 2;
  pt[3].x = x + width;
  pt[3].y = y + height / 2;
  XFillPolygon (dpy, window, bottom_gc, pt, 4, Convex, CoordModeOrigin);
Richard M. Stallman's avatar
Richard M. Stallman committed
627 628
}

629 630


Richard M. Stallman's avatar
Richard M. Stallman committed
631
static void
632
draw_shadow_rectangle (mw, window, x, y, width, height, erase_p, down_p)
Paul Reilly's avatar
Paul Reilly committed
633 634 635 636 637 638 639
     XlwMenuWidget mw;
     Window window;
     int x;
     int y;
     int width;
     int height;
     int erase_p;
640
     int down_p;
Richard M. Stallman's avatar
Richard M. Stallman committed
641 642 643 644 645 646
{
  Display *dpy = XtDisplay (mw);
  GC top_gc = !erase_p ? mw->menu.shadow_top_gc : mw->menu.background_gc;
  GC bottom_gc = !erase_p ? mw->menu.shadow_bottom_gc : mw->menu.background_gc;
  int thickness = mw->menu.shadow_thickness;
  XPoint points [4];
647 648 649 650 651 652 653 654

  if (!erase_p && down_p)
    {
      GC temp;
      temp = top_gc;
      top_gc = bottom_gc;
      bottom_gc = temp;
    }
655

Richard M. Stallman's avatar
Richard M. Stallman committed
656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694
  points [0].x = x;
  points [0].y = y;
  points [1].x = x + width;
  points [1].y = y;
  points [2].x = x + width - thickness;
  points [2].y = y + thickness;
  points [3].x = x;
  points [3].y = y + thickness;
  XFillPolygon (dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
  points [0].x = x;
  points [0].y = y + thickness;
  points [1].x = x;
  points [1].y = y + height;
  points [2].x = x + thickness;
  points [2].y = y + height - thickness;
  points [3].x = x + thickness;
  points [3].y = y + thickness;
  XFillPolygon (dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
  points [0].x = x + width;
  points [0].y = y;
  points [1].x = x + width - thickness;
  points [1].y = y + thickness;
  points [2].x = x + width - thickness;
  points [2].y = y + height - thickness;
  points [3].x = x + width;
  points [3].y = y + height - thickness;
  XFillPolygon (dpy, window, bottom_gc, points, 4, Convex, CoordModeOrigin);
  points [0].x = x;
  points [0].y = y + height;
  points [1].x = x + width;
  points [1].y = y + height;
  points [2].x = x + width;
  points [2].y = y + height - thickness;
  points [3].x = x + thickness;
  points [3].y = y + height - thickness;
  XFillPolygon (dpy, window, bottom_gc, points, 4, Convex, CoordModeOrigin);
}


695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773
static void
draw_shadow_rhombus (mw, window, x, y, width, height, erase_p, down_p)
     XlwMenuWidget mw;
     Window window;
     int x;
     int y;
     int width;
     int height;
     int erase_p;
     int down_p;
{
  Display *dpy = XtDisplay (mw);
  GC top_gc = !erase_p ? mw->menu.shadow_top_gc : mw->menu.background_gc;
  GC bottom_gc = !erase_p ? mw->menu.shadow_bottom_gc : mw->menu.background_gc;
  int thickness = mw->menu.shadow_thickness;
  XPoint points [4];

  if (!erase_p && down_p)
    {
      GC temp;
      temp = top_gc;
      top_gc = bottom_gc;
      bottom_gc = temp;
    }

  points [0].x = x;
  points [0].y = y + height / 2;
  points [1].x = x + thickness;
  points [1].y = y + height / 2;
  points [2].x = x + width / 2;
  points [2].y = y + thickness;
  points [3].x = x + width / 2;
  points [3].y = y;
  XFillPolygon (dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
  points [0].x = x + width / 2;
  points [0].y = y;
  points [1].x = x + width / 2;
  points [1].y = y + thickness;
  points [2].x = x + width - thickness;
  points [2].y = y + height / 2;
  points [3].x = x + width;
  points [3].y = y + height / 2;
  XFillPolygon (dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
  points [0].x = x;
  points [0].y = y + height / 2;
  points [1].x = x + thickness;
  points [1].y = y + height / 2;
  points [2].x = x + width / 2;
  points [2].y = y + height - thickness;
  points [3].x = x + width / 2;
  points [3].y = y + height;
  XFillPolygon (dpy, window, bottom_gc, points, 4, Convex, CoordModeOrigin);
  points [0].x = x + width / 2;
  points [0].y = y + height;
  points [1].x = x + width / 2;
  points [1].y = y + height - thickness;
  points [2].x = x + width - thickness;
  points [2].y = y + height / 2;
  points [3].x = x + width;
  points [3].y = y + height / 2;
  XFillPolygon (dpy, window, bottom_gc, points, 4, Convex, CoordModeOrigin);
}


/* Draw a toggle button on widget MW, X window WINDOW.  X/Y is the
   top-left corner of the menu item.  SELECTED_P non-zero means the
   toggle button is selected.  */

static void
draw_toggle (mw, window, x, y, selected_p)
     XlwMenuWidget mw;
     Window window;
     int x, y, selected_p;
{
  int width, height;

  width = toggle_button_width (mw);
  height = width;
  x += mw->menu.horizontal_spacing;
774
  y += (MENU_FONT_ASCENT (mw) - height) / 2;
775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793
  draw_shadow_rectangle (mw, window, x, y, width, height, False, selected_p);
}


/* Draw a radio button on widget MW, X window WINDOW.  X/Y is the
   top-left corner of the menu item.  SELECTED_P non-zero means the
   toggle button is selected.  */

static void
draw_radio (mw, window, x, y, selected_p)
     XlwMenuWidget mw;
     Window window;
     int x, y, selected_p;
{
  int width, height;

  width = radio_button_width (mw);
  height = width;
  x += mw->menu.horizontal_spacing;
794
  y += (MENU_FONT_ASCENT (mw) - height) / 2;
795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816
  draw_shadow_rhombus (mw, window, x, y, width, height, False, selected_p);
}


/* Draw a menu separator on widget MW, X window WINDOW.  X/Y is the
   top-left corner of the menu item.  WIDTH is the width of the
   separator to draw.  TYPE is the separator type.  */

static void
draw_separator (mw, window, x, y, width, type)
     XlwMenuWidget mw;
     Window window;
     int x, y, width;
     enum menu_separator type;
{
  Display *dpy = XtDisplay (mw);
  XGCValues xgcv;

  switch (type)
    {
    case SEPARATOR_NO_LINE:
      break;
817

818 819 820 821
    case SEPARATOR_SINGLE_LINE:
      XDrawLine (dpy, window, mw->menu.foreground_gc,
		 x, y, x + width, y);
      break;
822

823 824 825 826
    case SEPARATOR_DOUBLE_LINE:
      draw_separator (mw, window, x, y, width, SEPARATOR_SINGLE_LINE);
      draw_separator (mw, window, x, y + 2, width, SEPARATOR_SINGLE_LINE);
      break;
827

828 829 830 831 832 833 834 835
    case SEPARATOR_SINGLE_DASHED_LINE:
      xgcv.line_style = LineOnOffDash;
      XChangeGC (dpy, mw->menu.foreground_gc, GCLineStyle, &xgcv);
      XDrawLine (dpy, window, mw->menu.foreground_gc,
		 x, y, x + width, y);
      xgcv.line_style = LineSolid;
      XChangeGC (dpy, mw->menu.foreground_gc, GCLineStyle, &xgcv);
      break;
836

837 838 839 840 841 842
    case SEPARATOR_DOUBLE_DASHED_LINE:
      draw_separator (mw, window, x, y, width,
		      SEPARATOR_SINGLE_DASHED_LINE);
      draw_separator (mw, window, x, y + 2, width,
		      SEPARATOR_SINGLE_DASHED_LINE);
      break;
843

844 845 846 847 848 849 850 851 852 853 854 855 856
    case SEPARATOR_SHADOW_ETCHED_IN:
      XDrawLine (dpy, window, mw->menu.shadow_bottom_gc,
		 x, y, x + width, y);
      XDrawLine (dpy, window, mw->menu.shadow_top_gc,
		 x, y + 1, x + width, y + 1);
      break;

    case SEPARATOR_SHADOW_ETCHED_OUT:
      XDrawLine (dpy, window, mw->menu.shadow_top_gc,
		 x, y, x + width, y);
      XDrawLine (dpy, window, mw->menu.shadow_bottom_gc,
		 x, y + 1, x + width, y + 1);
      break;
857

858 859 860 861
    case SEPARATOR_SHADOW_ETCHED_IN_DASH:
      xgcv.line_style = LineOnOffDash;
      XChangeGC (dpy, mw->menu.shadow_bottom_gc, GCLineStyle, &xgcv);
      XChangeGC (dpy, mw->menu.shadow_top_gc, GCLineStyle, &xgcv);
862
      draw_separator (mw, window, x, y, width, SEPARATOR_SHADOW_ETCHED_IN);
863 864 865 866
      xgcv.line_style = LineSolid;
      XChangeGC (dpy, mw->menu.shadow_bottom_gc, GCLineStyle, &xgcv);
      XChangeGC (dpy, mw->menu.shadow_top_gc, GCLineStyle, &xgcv);
      break;
867

868 869 870 871
    case SEPARATOR_SHADOW_ETCHED_OUT_DASH:
      xgcv.line_style = LineOnOffDash;
      XChangeGC (dpy, mw->menu.shadow_bottom_gc, GCLineStyle, &xgcv);
      XChangeGC (dpy, mw->menu.shadow_top_gc, GCLineStyle, &xgcv);
872
      draw_separator (mw, window, x, y, width, SEPARATOR_SHADOW_ETCHED_OUT);
873 874 875 876 877 878 879 880 881
      xgcv.line_style = LineSolid;
      XChangeGC (dpy, mw->menu.shadow_bottom_gc, GCLineStyle, &xgcv);
      XChangeGC (dpy, mw->menu.shadow_top_gc, GCLineStyle, &xgcv);
      break;

    case SEPARATOR_SHADOW_DOUBLE_ETCHED_IN:
      draw_separator (mw, window, x, y, width, SEPARATOR_SHADOW_ETCHED_IN);
      draw_separator (mw, window, x, y + 3, width, SEPARATOR_SHADOW_ETCHED_IN);
      break;
882

883 884 885 886 887 888
    case SEPARATOR_SHADOW_DOUBLE_ETCHED_OUT:
      draw_separator (mw, window, x, y, width,
		      SEPARATOR_SHADOW_ETCHED_OUT);
      draw_separator (mw, window, x, y + 3, width,
		      SEPARATOR_SHADOW_ETCHED_OUT);
      break;
889

890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927
    case SEPARATOR_SHADOW_DOUBLE_ETCHED_IN_DASH:
      xgcv.line_style = LineOnOffDash;
      XChangeGC (dpy, mw->menu.shadow_bottom_gc, GCLineStyle, &xgcv);
      XChangeGC (dpy, mw->menu.shadow_top_gc, GCLineStyle, &xgcv);
      draw_separator (mw, window, x, y, width,
		      SEPARATOR_SHADOW_DOUBLE_ETCHED_IN);
      xgcv.line_style = LineSolid;
      XChangeGC (dpy, mw->menu.shadow_bottom_gc, GCLineStyle, &xgcv);
      XChangeGC (dpy, mw->menu.shadow_top_gc, GCLineStyle, &xgcv);
      break;

    case SEPARATOR_SHADOW_DOUBLE_ETCHED_OUT_DASH:
      xgcv.line_style = LineOnOffDash;
      XChangeGC (dpy, mw->menu.shadow_bottom_gc, GCLineStyle, &xgcv);
      XChangeGC (dpy, mw->menu.shadow_top_gc, GCLineStyle, &xgcv);
      draw_separator (mw, window, x, y, width,
		      SEPARATOR_SHADOW_DOUBLE_ETCHED_OUT);
      xgcv.line_style = LineSolid;
      XChangeGC (dpy, mw->menu.shadow_bottom_gc, GCLineStyle, &xgcv);
      XChangeGC (dpy, mw->menu.shadow_top_gc, GCLineStyle, &xgcv);
      break;

    default:
      abort ();
    }
}


/* Return the pixel height of menu separator SEPARATOR.  */

static int
separator_height (separator)
     enum menu_separator separator;
{
  switch (separator)
    {
    case SEPARATOR_NO_LINE:
      return 2;
928

929 930 931
    case SEPARATOR_SINGLE_LINE:
    case SEPARATOR_SINGLE_DASHED_LINE:
      return 1;
932

933 934 935
    case SEPARATOR_DOUBLE_LINE:
    case SEPARATOR_DOUBLE_DASHED_LINE:
      return 3;
936

937 938 939 940 941
    case SEPARATOR_SHADOW_ETCHED_IN:
    case SEPARATOR_SHADOW_ETCHED_OUT:
    case SEPARATOR_SHADOW_ETCHED_IN_DASH:
    case SEPARATOR_SHADOW_ETCHED_OUT_DASH:
      return 2;
942

943 944 945 946 947
    case SEPARATOR_SHADOW_DOUBLE_ETCHED_IN:
    case SEPARATOR_SHADOW_DOUBLE_ETCHED_OUT:
    case SEPARATOR_SHADOW_DOUBLE_ETCHED_IN_DASH:
    case SEPARATOR_SHADOW_DOUBLE_ETCHED_OUT_DASH:
      return 5;
948

949 950 951 952 953 954
    default:
      abort ();
    }
}


Richard M. Stallman's avatar
Richard M. Stallman committed
955
/* Display the menu item and increment where.x and where.y to show how large
956 957
   the menu item was.  */

Richard M. Stallman's avatar
Richard M. Stallman committed
958
static void
959 960
display_menu_item (mw, val, ws, where, highlighted_p, horizontal_p,
		   just_compute_p)
Paul Reilly's avatar
Paul Reilly committed
961 962 963 964 965 966 967
     XlwMenuWidget mw;
     widget_value* val;
     window_state* ws;
     XPoint* where;
     Boolean highlighted_p;
     Boolean horizontal_p;
     Boolean just_compute_p;
Richard M. Stallman's avatar
Richard M. Stallman committed
968 969 970
{
  GC deco_gc;
  GC text_gc;
971 972
  int font_height = MENU_FONT_HEIGHT (mw);
  int font_ascent = MENU_FONT_ASCENT (mw);
Richard M. Stallman's avatar
Richard M. Stallman committed
973
  int shadow = mw->menu.shadow_thickness;
974
  int margin = mw->menu.margin;
Richard M. Stallman's avatar
Richard M. Stallman committed
975 976 977 978
  int h_spacing = mw->menu.horizontal_spacing;
  int v_spacing = mw->menu.vertical_spacing;
  int label_width;
  int rest_width;
979
  int button_width;
Richard M. Stallman's avatar
Richard M. Stallman committed
980 981
  int height;
  int width;
982 983
  enum menu_separator separator;
  int separator_p = lw_separator_p (val->name, &separator, 0);
Richard M. Stallman's avatar
Richard M. Stallman committed
984 985

  /* compute the sizes of the item */
986 987
  size_menu_item (mw, val, horizontal_p, &label_width, &rest_width,
		  &button_width, &height);
Richard M. Stallman's avatar
Richard M. Stallman committed
988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008

  if (horizontal_p)
    width = label_width + rest_width;
  else
    {
      label_width = ws->label_width;
      width = ws->width - 2 * shadow;
    }

  /* Only highlight an enabled item that has a callback. */
  if (highlighted_p)
    if (!val->enabled || !(val->call_data || val->contents))
      highlighted_p = 0;

  /* do the drawing. */
  if (!just_compute_p)
    {
      /* Add the shadow border of the containing menu */
      int x = where->x + shadow;
      int y = where->y + shadow;

1009 1010 1011 1012 1013 1014
      if (horizontal_p)
	{
	  x += margin;
	  y += margin;
	}

Richard M. Stallman's avatar
Richard M. Stallman committed
1015 1016
      /* pick the foreground and background GC. */
      if (val->enabled)
1017
	text_gc = mw->menu.foreground_gc;
Richard M. Stallman's avatar
Richard M. Stallman committed
1018
      else
1019
	text_gc = mw->menu.disabled_gc;
Richard M. Stallman's avatar
Richard M. Stallman committed
1020 1021 1022 1023
      deco_gc = mw->menu.foreground_gc;

      if (separator_p)
	{
1024
	  draw_separator (mw, ws->window, x, y, width, separator);
Richard M. Stallman's avatar
Richard M. Stallman committed
1025
	}
1026
      else
Richard M. Stallman's avatar
Richard M. Stallman committed
1027
	{
1028
	  int x_offset = x + h_spacing + shadow;
Richard M. Stallman's avatar
Richard M. Stallman committed
1029
	  char* display_string = resource_widget_value (mw, val);
1030 1031
	  draw_shadow_rectangle (mw, ws->window, x, y, width, height, True,
				 False);
1032 1033 1034 1035 1036 1037 1038 1039 1040

	  /* Deal with centering a menu title. */
	  if (!horizontal_p && !val->contents && !val->call_data)
	    {
	      int l = string_width (mw, display_string);

	      if (width > l)
		x_offset = (width - l) >> 1;
	    }
1041 1042
	  else if (!horizontal_p && ws->button_width)
	    x_offset += ws->button_width;
1043 1044


1045
#ifdef HAVE_X_I18N
Jan Djärv's avatar
Jan Djärv committed
1046 1047 1048 1049 1050 1051
          if (mw->menu.fontSet)
            XmbDrawString (XtDisplay (mw), ws->window, mw->menu.fontSet,
                           text_gc, x_offset,
                           y + v_spacing + shadow + font_ascent,
                           display_string, strlen (display_string));
          else
1052
#endif
Jan Djärv's avatar
Jan Djärv committed
1053
          XDrawString (XtDisplay (mw), ws->window,
1054
		       text_gc, x_offset,
Richard M. Stallman's avatar
Richard M. Stallman committed
1055 1056
		       y + v_spacing + shadow + font_ascent,
		       display_string, strlen (display_string));
1057

Richard M. Stallman's avatar
Richard M. Stallman committed
1058 1059
	  if (!horizontal_p)
	    {
1060 1061 1062 1063 1064 1065
	      if (val->button_type == BUTTON_TYPE_TOGGLE)
		draw_toggle (mw, ws->window, x, y + v_spacing + shadow,
			     val->selected);
	      else if (val->button_type == BUTTON_TYPE_RADIO)
		draw_radio (mw, ws->window, x, y + v_spacing + shadow,
			    val->selected);
1066

Richard M. Stallman's avatar
Richard M. Stallman committed
1067 1068 1069 1070
	      if (val->contents)
		{
		  int a_w = arrow_width (mw);
		  draw_arrow (mw, ws->window, deco_gc,
1071
			      x + width - a_w
1072
			      - mw->menu.horizontal_spacing
Paul Reilly's avatar
Paul Reilly committed
1073
			      - mw->menu.shadow_thickness,
1074 1075
			      y + v_spacing + shadow, a_w,
			      highlighted_p);
Richard M. Stallman's avatar
Richard M. Stallman committed
1076 1077 1078
		}
	      else if (val->key)
		{
1079
#ifdef HAVE_X_I18N
Jan Djärv's avatar
Jan Djärv committed
1080 1081 1082 1083 1084 1085 1086 1087
                  if (mw->menu.fontSet)
                    XmbDrawString (XtDisplay (mw), ws->window,
                                   mw->menu.fontSet,
                                   text_gc,
                                   x + label_width + mw->menu.arrow_spacing,
                                   y + v_spacing + shadow + font_ascent,
                                   val->key, strlen (val->key));
                  else
1088
#endif
Jan Djärv's avatar
Jan Djärv committed
1089
		  XDrawString (XtDisplay (mw), ws->window,
1090
			       text_gc,
Richard M. Stallman's avatar
Richard M. Stallman committed
1091 1092 1093 1094 1095
			       x + label_width + mw->menu.arrow_spacing,
			       y + v_spacing + shadow + font_ascent,
			       val->key, strlen (val->key));
		}
	    }
Paul Reilly's avatar
Paul Reilly committed
1096 1097
	  else
	    {
1098
	      XDrawRectangle (XtDisplay (mw), ws->window,
Paul Reilly's avatar
Paul Reilly committed
1099 1100 1101
			      mw->menu.background_gc,
			      x + shadow, y + shadow,
			      label_width + h_spacing - 1,
1102
			      font_height + 2 * v_spacing - 1);
Paul Reilly's avatar
Paul Reilly committed
1103
	      draw_shadow_rectangle (mw, ws->window, x, y, width, height,
1104
				     True, False);
Paul Reilly's avatar
Paul Reilly committed
1105
	    }
Richard M. Stallman's avatar
Richard M. Stallman committed
1106 1107

	  if (highlighted_p)
1108 1109
	    draw_shadow_rectangle (mw, ws->window, x, y, width, height, False,
				   False);
Richard M. Stallman's avatar
Richard M. Stallman committed
1110 1111
	}
    }
1112

Richard M. Stallman's avatar
Richard M. Stallman committed
1113 1114 1115 1116 1117
  where->x += width;
  where->y += height;
}

static void
Paul Reilly's avatar
Paul Reilly committed
1118 1119 1120 1121 1122 1123 1124 1125 1126 1127
display_menu (mw, level, just_compute_p, highlighted_pos, hit, hit_return,
	      this, that)
     XlwMenuWidget mw;
     int level;
     Boolean just_compute_p;
     XPoint* highlighted_pos;
     XPoint* hit;
     widget_value** hit_return;
     widget_value* this;
     widget_value* that;
Richard M. Stallman's avatar
Richard M. Stallman committed
1128 1129 1130 1131 1132 1133 1134 1135
{
  widget_value*	val;
  widget_value* following_item;
  window_state* ws;
  XPoint	where;
  int horizontal_p = mw->menu.horizontal && (level == 0);
  int highlighted_p;
  int just_compute_this_one_p;
1136 1137 1138
  /* This is set nonzero if the element containing HIGHLIGHTED_POS
     is disabled, so that we do not return any subsequent element either.  */
  int no_return = 0;
1139
  enum menu_separator separator;
Richard M. Stallman's avatar
Richard M. Stallman committed
1140 1141

  if (level >= mw->menu.old_depth)
1142
    abort_gracefully ((Widget) mw);
Richard M. Stallman's avatar
Richard M. Stallman committed
1143 1144 1145

  if (level < mw->menu.old_depth - 1)
    following_item = mw->menu.old_stack [level + 1];
1146
  else
Richard M. Stallman's avatar
Richard M. Stallman committed
1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165
    following_item = NULL;

  if (hit)
    *hit_return = NULL;

  where.x = 0;
  where.y = 0;

  ws = &mw->menu.windows [level];
  for (val = mw->menu.old_stack [level]->contents; val; val = val->next)
    {
      highlighted_p = val == following_item;
      if (highlighted_p && highlighted_pos)
	{
	  if (horizontal_p)
	    highlighted_pos->x = where.x;
	  else
	    highlighted_pos->y = where.y;
	}
1166

Richard M. Stallman's avatar
Richard M. Stallman committed
1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183
      just_compute_this_one_p =
	just_compute_p || ((this || that) && val != this &&  val != that);

      display_menu_item (mw, val, ws, &where, highlighted_p, horizontal_p,
			 just_compute_this_one_p);

      if (highlighted_p && highlighted_pos)
	{
	  if (horizontal_p)
	    highlighted_pos->y = where.y;
	  else
	    highlighted_pos->x = where.x;
	}

      if (hit
	  && !*hit_return
	  && (horizontal_p ? hit->x < where.x : hit->y < where.y)
1184
	  && !lw_separator_p (val->name, &separator, 0)
1185 1186 1187 1188 1189 1190 1191
	  && !no_return)
	{
	  if (val->enabled)
	    *hit_return = val;
	  else
	    no_return = 1;
	}
Richard M. Stallman's avatar
Richard M. Stallman committed
1192 1193 1194 1195 1196 1197

      if (horizontal_p)
	where.y = 0;
      else
	where.x = 0;
    }
1198

Richard M. Stallman's avatar
Richard M. Stallman committed
1199
  if (!just_compute_p)
1200 1201
    draw_shadow_rectangle (mw, ws->window, 0, 0, ws->width, ws->height,
			   False, False);
Richard M. Stallman's avatar
Richard M. Stallman committed
1202 1203 1204 1205
}

/* Motion code */
static void
Paul Reilly's avatar
Paul Reilly committed
1206 1207 1208 1209
set_new_state (mw, val, level)
     XlwMenuWidget mw;
     widget_value* val;
     int level;
Richard M. Stallman's avatar
Richard M. Stallman committed
1210 1211
{
  int i;
1212

Richard M. Stallman's avatar
Richard M. Stallman committed
1213 1214 1215 1216 1217 1218 1219
  mw->menu.new_depth = 0;
  for (i = 0; i < level; i++)
    push_new_stack (mw, mw->menu.old_stack [i]);
  push_new_stack (mw, val);
}

static void
Paul Reilly's avatar
Paul Reilly committed
1220 1221 1222
make_windows_if_needed (mw, n)
     XlwMenuWidget mw;
     int n;
Richard M. Stallman's avatar
Richard M. Stallman committed
1223 1224 1225 1226 1227 1228 1229
{
  int i;
  int start_at;
  XSetWindowAttributes xswa;
  int mask;
  Window root = RootWindowOfScreen (DefaultScreenOfDisplay (XtDisplay (mw)));
  window_state* windows;
1230

Richard M. Stallman's avatar
Richard M. Stallman committed
1231 1232 1233 1234 1235 1236 1237 1238
  if (mw->menu.windows_length >= n)
    return;

  xswa.save_under = True;
  xswa.override_redirect = True;
  xswa.background_pixel = mw->core.background_pixel;
  xswa.border_pixel = mw->core.border_pixel;
  xswa.event_mask =
Paul Reilly's avatar
Paul Reilly committed
1239
    ExposureMask | PointerMotionMask | PointerMotionHintMask
Richard M. Stallman's avatar
Richard M. Stallman committed
1240 1241 1242 1243
      | ButtonReleaseMask | ButtonPressMask;
  xswa.cursor = mw->menu.cursor_shape;
  mask = CWSaveUnder | CWOverrideRedirect | CWBackPixel | CWBorderPixel
    | CWEventMask | CWCursor;
1244

Richard M. Stallman's avatar
Richard M. Stallman committed
1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273
  if (!mw->menu.windows)
    {
      mw->menu.windows =
	(window_state*)XtMalloc (n * sizeof (window_state));
      start_at = 0;
    }
  else
    {
      mw->menu.windows =
	(window_state*)XtRealloc ((char*)mw->menu.windows,
				  n * sizeof (window_state));
      start_at = mw->menu.windows_length;
    }
  mw->menu.windows_length = n;

  windows = mw->menu.windows;

  for (i = start_at; i < n; i++)
   {
     windows [i].x = 0;
     windows [i].y = 0;
     windows [i].width = 1;
     windows [i].height = 1;
     windows [i].window =
       XCreateWindow (XtDisplay (mw), root, 0, 0, 1, 1,
		      0, 0, CopyFromParent, CopyFromParent, mask, &xswa);
  }
}

1274 1275 1276 1277 1278 1279 1280 1281 1282
/* Value is non-zero if WINDOW is part of menu bar widget W.  */

int
xlwmenu_window_p (w, window)
     Widget w;
     Window window;
{
  XlwMenuWidget mw = (XlwMenuWidget) w;
  int i;
1283

1284 1285 1286 1287 1288 1289 1290
  for (i = 0; i < mw->menu.windows_length; ++i)
    if (window == mw->menu.windows[i].window)
      break;

  return i < mw->menu.windows_length;
}

Richard M. Stallman's avatar
Richard M. Stallman committed
1291 1292
/* Make the window fit in the screen */
static void
Paul Reilly's avatar
Paul Reilly committed
1293 1294 1295 1296 1297
fit_to_screen (mw, ws, previous_ws, horizontal_p)
     XlwMenuWidget mw;
     window_state* ws;
     window_state* previous_ws;
     Boolean horizontal_p;
Richard M. Stallman's avatar
Richard M. Stallman committed
1298
{
1299 1300
  unsigned int screen_width = WidthOfScreen (XtScreen (mw));
  unsigned int screen_height = HeightOfScreen (XtScreen (mw));
1301 1302 1303
  /* 1 if we are unable to avoid an overlap between
     this menu and the parent menu in the X dimension.  */
  int horizontal_overlap = 0;
Richard M. Stallman's avatar
Richard M. Stallman committed
1304 1305 1306 1307 1308 1309

  if (ws->x < 0)
    ws->x = 0;
  else if (ws->x + ws->width > screen_width)
    {
      if (!horizontal_p)
Miles Bader's avatar
Miles Bader committed