gtkmenuitem.c 38.2 KB
Newer Older
Elliot Lee's avatar
Elliot Lee committed
1 2 3 4
/* GTK - The GIMP Toolkit
 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
 *
 * This library is free software; you can redistribute it and/or
5
 * modify it under the terms of the GNU Lesser General Public
Elliot Lee's avatar
Elliot Lee committed
6 7 8 9 10 11
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12
 * Lesser General Public License for more details.
Elliot Lee's avatar
Elliot Lee committed
13
 *
14
 * You should have received a copy of the GNU Lesser General Public
15 16 17
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
Elliot Lee's avatar
Elliot Lee committed
18
 */
19 20

/*
21
 * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
22 23 24 25 26
 * file for a list of people on the GTK+ Team.  See the ChangeLog
 * files for a list of changes.  These files are distributed with
 * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
 */

27 28
#define GTK_MENU_INTERNALS

29
#include <config.h>
Elliot Lee's avatar
Elliot Lee committed
30
#include <string.h>
Tim Janik's avatar
Tim Janik committed
31
#include "gtkaccellabel.h"
Elliot Lee's avatar
Elliot Lee committed
32
#include "gtkmain.h"
33
#include "gtkmarshalers.h"
Elliot Lee's avatar
Elliot Lee committed
34
#include "gtkmenu.h"
35
#include "gtkmenubar.h"
Elliot Lee's avatar
Elliot Lee committed
36
#include "gtkmenuitem.h"
37
#include "gtkseparatormenuitem.h"
Elliot Lee's avatar
Elliot Lee committed
38 39 40 41 42

#define MENU_ITEM_CLASS(w)  GTK_MENU_ITEM_CLASS (GTK_OBJECT (w)->klass)

enum {
  ACTIVATE,
43
  ACTIVATE_ITEM,
44 45
  TOGGLE_SIZE_REQUEST,
  TOGGLE_SIZE_ALLOCATE,
Elliot Lee's avatar
Elliot Lee committed
46 47 48 49 50 51 52
  LAST_SIGNAL
};


static void gtk_menu_item_class_init     (GtkMenuItemClass *klass);
static void gtk_menu_item_init           (GtkMenuItem      *menu_item);
static void gtk_menu_item_destroy        (GtkObject        *object);
53
static void gtk_menu_item_finalize       (GObject          *object);
Elliot Lee's avatar
Elliot Lee committed
54 55 56 57
static void gtk_menu_item_size_request   (GtkWidget        *widget,
					  GtkRequisition   *requisition);
static void gtk_menu_item_size_allocate  (GtkWidget        *widget,
					  GtkAllocation    *allocation);
58 59 60 61
static void gtk_menu_item_realize        (GtkWidget        *widget);
static void gtk_menu_item_unrealize      (GtkWidget        *widget);
static void gtk_menu_item_map            (GtkWidget        *widget);
static void gtk_menu_item_unmap          (GtkWidget        *widget);
Elliot Lee's avatar
Elliot Lee committed
62 63 64 65
static void gtk_menu_item_paint          (GtkWidget        *widget,
					  GdkRectangle     *area);
static gint gtk_menu_item_expose         (GtkWidget        *widget,
					  GdkEventExpose   *event);
66 67
static void gtk_menu_item_parent_set     (GtkWidget        *widget,
					  GtkWidget        *previous_parent);
68

69

70 71 72 73
static void gtk_real_menu_item_select               (GtkItem     *item);
static void gtk_real_menu_item_deselect             (GtkItem     *item);
static void gtk_real_menu_item_activate_item        (GtkMenuItem *item);
static void gtk_real_menu_item_toggle_size_request  (GtkMenuItem *menu_item,
74
						     gint        *requisition);
75
static void gtk_real_menu_item_toggle_size_allocate (GtkMenuItem *menu_item,
76
						     gint         allocation);
77
static gboolean gtk_menu_item_mnemonic_activate     (GtkWidget   *widget,
78
						     gboolean     group_cycling);
79

Elliot Lee's avatar
Elliot Lee committed
80
static gint gtk_menu_item_select_timeout (gpointer          data);
81
static void gtk_menu_item_popup_submenu  (gpointer     data);
Elliot Lee's avatar
Elliot Lee committed
82 83 84
static void gtk_menu_item_position_menu  (GtkMenu          *menu,
					  gint             *x,
					  gint             *y,
85
					  gboolean         *push_in,
Elliot Lee's avatar
Elliot Lee committed
86
					  gpointer          user_data);
87 88
static void gtk_menu_item_show_all       (GtkWidget        *widget);
static void gtk_menu_item_hide_all       (GtkWidget        *widget);
89 90 91 92
static void gtk_menu_item_forall         (GtkContainer    *container,
					  gboolean         include_internals,
					  GtkCallback      callback,
					  gpointer         callback_data);
Owen Taylor's avatar
Owen Taylor committed
93 94
static gboolean gtk_menu_item_can_activate_accel (GtkWidget *widget,
						  guint      signal_id);
Elliot Lee's avatar
Elliot Lee committed
95

96

Elliot Lee's avatar
Elliot Lee committed
97
static GtkItemClass *parent_class;
98
static guint menu_item_signals[LAST_SIGNAL] = { 0 };
Elliot Lee's avatar
Elliot Lee committed
99

Manish Singh's avatar
Manish Singh committed
100
GType
101
gtk_menu_item_get_type (void)
Elliot Lee's avatar
Elliot Lee committed
102
{
Manish Singh's avatar
Manish Singh committed
103
  static GType menu_item_type = 0;
Elliot Lee's avatar
Elliot Lee committed
104 105 106

  if (!menu_item_type)
    {
107
      static const GTypeInfo menu_item_info =
Elliot Lee's avatar
Elliot Lee committed
108 109
      {
	sizeof (GtkMenuItemClass),
110 111 112 113 114 115 116 117
	NULL,           /* base_init */
	NULL,           /* base_finalize */
	(GClassInitFunc) gtk_menu_item_class_init,
	NULL,           /* class_finalize */
	NULL,           /* class_data */
	sizeof (GtkMenuItem),
	16,             /* n_preallocs */
	(GInstanceInitFunc) gtk_menu_item_init,
Elliot Lee's avatar
Elliot Lee committed
118 119
      };

Manish Singh's avatar
Manish Singh committed
120 121
      menu_item_type = g_type_register_static (GTK_TYPE_ITEM, "GtkMenuItem",
					       &menu_item_info, 0);
Elliot Lee's avatar
Elliot Lee committed
122 123 124 125 126 127 128 129
    }

  return menu_item_type;
}

static void
gtk_menu_item_class_init (GtkMenuItemClass *klass)
{
130 131 132 133 134
  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
  GtkObjectClass *object_class = GTK_OBJECT_CLASS (klass);
  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
  GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
  GtkItemClass *item_class = GTK_ITEM_CLASS (klass);
Elliot Lee's avatar
Elliot Lee committed
135

136
  parent_class = g_type_class_peek_parent (klass);
Elliot Lee's avatar
Elliot Lee committed
137

138
  gobject_class->finalize = gtk_menu_item_finalize;
139 140 141 142 143 144

  object_class->destroy = gtk_menu_item_destroy;

  widget_class->size_request = gtk_menu_item_size_request;
  widget_class->size_allocate = gtk_menu_item_size_allocate;
  widget_class->expose_event = gtk_menu_item_expose;
145 146 147 148
  widget_class->realize = gtk_menu_item_realize;
  widget_class->unrealize = gtk_menu_item_unrealize;
  widget_class->map = gtk_menu_item_map;
  widget_class->unmap = gtk_menu_item_unmap;
149 150
  widget_class->show_all = gtk_menu_item_show_all;
  widget_class->hide_all = gtk_menu_item_hide_all;
151
  widget_class->mnemonic_activate = gtk_menu_item_mnemonic_activate;
152
  widget_class->parent_set = gtk_menu_item_parent_set;
Owen Taylor's avatar
Owen Taylor committed
153
  widget_class->can_activate_accel = gtk_menu_item_can_activate_accel;
154
  
155 156 157 158 159 160 161 162 163 164 165 166
  container_class->forall = gtk_menu_item_forall;

  item_class->select = gtk_real_menu_item_select;
  item_class->deselect = gtk_real_menu_item_deselect;

  klass->activate = NULL;
  klass->activate_item = gtk_real_menu_item_activate_item;
  klass->toggle_size_request = gtk_real_menu_item_toggle_size_request;
  klass->toggle_size_allocate = gtk_real_menu_item_toggle_size_allocate;

  klass->hide_on_activate = TRUE;

Elliot Lee's avatar
Elliot Lee committed
167
  menu_item_signals[ACTIVATE] =
Manish Singh's avatar
Manish Singh committed
168 169 170 171 172 173 174
    g_signal_new ("activate",
		  G_OBJECT_CLASS_TYPE (gobject_class),
		  G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
		  G_STRUCT_OFFSET (GtkMenuItemClass, activate),
		  NULL, NULL,
		  _gtk_marshal_VOID__VOID,
		  G_TYPE_NONE, 0);
175
  widget_class->activate_signal = menu_item_signals[ACTIVATE];
Elliot Lee's avatar
Elliot Lee committed
176

177
  menu_item_signals[ACTIVATE_ITEM] =
Manish Singh's avatar
Manish Singh committed
178 179 180 181 182 183 184
    g_signal_new ("activate_item",
		  G_OBJECT_CLASS_TYPE (gobject_class),
		  G_SIGNAL_RUN_FIRST,
		  G_STRUCT_OFFSET (GtkMenuItemClass, activate_item),
		  NULL, NULL,
		  _gtk_marshal_VOID__VOID,
		  G_TYPE_NONE, 0);
185

186
  menu_item_signals[TOGGLE_SIZE_REQUEST] =
Manish Singh's avatar
Manish Singh committed
187 188 189 190 191 192 193 194
    g_signal_new ("toggle_size_request",
		  G_OBJECT_CLASS_TYPE (gobject_class),
		  G_SIGNAL_RUN_FIRST,
		  G_STRUCT_OFFSET (GtkMenuItemClass, toggle_size_request),
		  NULL, NULL,
		  _gtk_marshal_VOID__POINTER,
		  G_TYPE_NONE, 1,
		  G_TYPE_POINTER);
195 196

  menu_item_signals[TOGGLE_SIZE_ALLOCATE] =
Manish Singh's avatar
Manish Singh committed
197 198 199 200 201 202 203 204
    g_signal_new ("toggle_size_allocate",
		  G_OBJECT_CLASS_TYPE (gobject_class),
		  G_SIGNAL_RUN_FIRST,
		  G_STRUCT_OFFSET (GtkMenuItemClass, toggle_size_allocate),
		  NULL, NULL,
		  _gtk_marshal_NONE__INT,
		  G_TYPE_NONE, 1,
		  G_TYPE_INT);
205 206 207 208 209 210

  gtk_widget_class_install_style_property_parser (widget_class,
						  g_param_spec_enum ("selected_shadow_type",
								     "Selected Shadow Type",
								     "Shadow type when item is selected",
								     GTK_TYPE_SHADOW_TYPE,
211
								     GTK_SHADOW_NONE,
212 213
								     G_PARAM_READABLE),
						  gtk_rc_property_parse_enum);
214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240

  gtk_widget_class_install_style_property (widget_class,
					   g_param_spec_int ("horizontal_padding",
							     "Horizontal Padding",
							     "Padding to left and right of the menu item",
							     0,
							     G_MAXINT,
							     3,
							     G_PARAM_READABLE));

  gtk_widget_class_install_style_property (widget_class,
					   g_param_spec_int ("toggle_spacing",
							     "Icon Spacing",
							     "Space between icon and label",
							     0,
							     G_MAXINT,
							     5,
							     G_PARAM_READABLE));

  gtk_widget_class_install_style_property (widget_class,
					   g_param_spec_int ("arrow_spacing",
							     "Arrow Spacing",
							     "Space between label and arrow",
							     0,
							     G_MAXINT,
							     10,
							     G_PARAM_READABLE));
Elliot Lee's avatar
Elliot Lee committed
241 242 243 244 245
}

static void
gtk_menu_item_init (GtkMenuItem *menu_item)
{
246 247
  GTK_WIDGET_SET_FLAGS (menu_item, GTK_NO_WINDOW);
  
Elliot Lee's avatar
Elliot Lee committed
248 249
  menu_item->submenu = NULL;
  menu_item->toggle_size = 0;
250
  menu_item->accelerator_width = 0;
Elliot Lee's avatar
Elliot Lee committed
251
  menu_item->show_submenu_indicator = FALSE;
252 253 254 255
  if (gtk_widget_get_direction (GTK_WIDGET (menu_item)) == GTK_TEXT_DIR_RTL)
    menu_item->submenu_direction = GTK_DIRECTION_LEFT;
  else
    menu_item->submenu_direction = GTK_DIRECTION_RIGHT;
Elliot Lee's avatar
Elliot Lee committed
256 257 258 259 260 261 262
  menu_item->submenu_placement = GTK_TOP_BOTTOM;
  menu_item->right_justify = FALSE;

  menu_item->timer = 0;
}

GtkWidget*
263
gtk_menu_item_new (void)
Elliot Lee's avatar
Elliot Lee committed
264
{
Manish Singh's avatar
Manish Singh committed
265
  return g_object_new (GTK_TYPE_MENU_ITEM, NULL);
Elliot Lee's avatar
Elliot Lee committed
266 267 268 269 270 271
}

GtkWidget*
gtk_menu_item_new_with_label (const gchar *label)
{
  GtkWidget *menu_item;
Tim Janik's avatar
Tim Janik committed
272
  GtkWidget *accel_label;
Elliot Lee's avatar
Elliot Lee committed
273 274

  menu_item = gtk_menu_item_new ();
Tim Janik's avatar
Tim Janik committed
275 276
  accel_label = gtk_accel_label_new (label);
  gtk_misc_set_alignment (GTK_MISC (accel_label), 0.0, 0.5);
Elliot Lee's avatar
Elliot Lee committed
277

Tim Janik's avatar
Tim Janik committed
278 279 280
  gtk_container_add (GTK_CONTAINER (menu_item), accel_label);
  gtk_accel_label_set_accel_widget (GTK_ACCEL_LABEL (accel_label), menu_item);
  gtk_widget_show (accel_label);
Elliot Lee's avatar
Elliot Lee committed
281 282 283 284

  return menu_item;
}

285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302

/**
 * gtk_menu_item_new_with_mnemonic:
 * @label: The text of the button, with an underscore in front of the
 *         mnemonic character
 * @returns: a new #GtkMenuItem
 *
 * Creates a new #GtkMenuItem containing a label. The label
 * will be created using gtk_label_new_with_mnemonic(), so underscores
 * in @label indicate the mnemonic for the menu item.
 **/
GtkWidget*
gtk_menu_item_new_with_mnemonic (const gchar *label)
{
  GtkWidget *menu_item;
  GtkWidget *accel_label;

  menu_item = gtk_menu_item_new ();
Manish Singh's avatar
Manish Singh committed
303
  accel_label = g_object_new (GTK_TYPE_ACCEL_LABEL, NULL);
304 305 306 307 308 309 310 311 312 313
  gtk_label_set_text_with_mnemonic (GTK_LABEL (accel_label), label);
  gtk_misc_set_alignment (GTK_MISC (accel_label), 0.0, 0.5);

  gtk_container_add (GTK_CONTAINER (menu_item), accel_label);
  gtk_accel_label_set_accel_widget (GTK_ACCEL_LABEL (accel_label), menu_item);
  gtk_widget_show (accel_label);

  return menu_item;
}

Elliot Lee's avatar
Elliot Lee committed
314 315 316 317 318 319 320 321 322 323
static void
gtk_menu_item_destroy (GtkObject *object)
{
  GtkMenuItem *menu_item;

  g_return_if_fail (GTK_IS_MENU_ITEM (object));

  menu_item = GTK_MENU_ITEM (object);

  if (menu_item->submenu)
324
    gtk_widget_destroy (menu_item->submenu);
Elliot Lee's avatar
Elliot Lee committed
325

326 327 328 329 330 331 332 333 334 335 336
  GTK_OBJECT_CLASS (parent_class)->destroy (object);
}

static void
gtk_menu_item_finalize (GObject *object)
{
  GtkMenuItem *menu_item = GTK_MENU_ITEM (object);

  g_free (menu_item->accel_path);

  G_OBJECT_CLASS (parent_class)->finalize (object);
Elliot Lee's avatar
Elliot Lee committed
337 338
}

339 340 341 342 343 344 345 346 347 348 349 350 351 352
static void
gtk_menu_item_detacher (GtkWidget     *widget,
			GtkMenu       *menu)
{
  GtkMenuItem *menu_item;

  g_return_if_fail (GTK_IS_MENU_ITEM (widget));

  menu_item = GTK_MENU_ITEM (widget);
  g_return_if_fail (menu_item->submenu == (GtkWidget*) menu);

  menu_item->submenu = NULL;
}

Elliot Lee's avatar
Elliot Lee committed
353 354 355 356 357
void
gtk_menu_item_set_submenu (GtkMenuItem *menu_item,
			   GtkWidget   *submenu)
{
  g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
358
  
359
  if (menu_item->submenu != submenu)
Elliot Lee's avatar
Elliot Lee committed
360
    {
361 362
      gtk_menu_item_remove_submenu (menu_item);
      
363
      menu_item->submenu = submenu;
364 365 366 367
      gtk_menu_attach_to_widget (GTK_MENU (submenu),
				 GTK_WIDGET (menu_item),
				 gtk_menu_item_detacher);
      
368 369
      if (GTK_WIDGET (menu_item)->parent)
	gtk_widget_queue_resize (GTK_WIDGET (menu_item));
Elliot Lee's avatar
Elliot Lee committed
370 371 372
    }
}

373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389
/**
 * gtk_menu_item_get_submenu:
 * @menu_item: a #GtkMenuItem
 *
 * Gets the submenu underneath this menu item, if any. See
 * gtk_menu_item_set_submenu().
 *
 * Return value: submenu for this menu item, or %NULL if none.
 **/
GtkWidget *
gtk_menu_item_get_submenu (GtkMenuItem *menu_item)
{
  g_return_val_if_fail (GTK_IS_MENU_ITEM (menu_item), NULL);

  return menu_item->submenu;
}

390 391 392 393 394 395 396 397 398
void
gtk_menu_item_remove_submenu (GtkMenuItem         *menu_item)
{
  g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
      
  if (menu_item->submenu)
    gtk_menu_detach (GTK_MENU (menu_item->submenu));
}

399 400 401
void _gtk_menu_item_set_placement (GtkMenuItem         *menu_item,
				   GtkSubmenuPlacement  placement);

Elliot Lee's avatar
Elliot Lee committed
402
void
Owen Taylor's avatar
Owen Taylor committed
403
_gtk_menu_item_set_placement (GtkMenuItem         *menu_item,
Elliot Lee's avatar
Elliot Lee committed
404 405 406 407 408 409 410 411 412 413
			     GtkSubmenuPlacement  placement)
{
  g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));

  menu_item->submenu_placement = placement;
}

void
gtk_menu_item_select (GtkMenuItem *menu_item)
{
414 415
  g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
  
Elliot Lee's avatar
Elliot Lee committed
416 417 418 419 420 421
  gtk_item_select (GTK_ITEM (menu_item));
}

void
gtk_menu_item_deselect (GtkMenuItem *menu_item)
{
422 423
  g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
  
Elliot Lee's avatar
Elliot Lee committed
424 425 426 427 428 429
  gtk_item_deselect (GTK_ITEM (menu_item));
}

void
gtk_menu_item_activate (GtkMenuItem *menu_item)
{
430 431
  g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
  
Manish Singh's avatar
Manish Singh committed
432
  g_signal_emit (menu_item, menu_item_signals[ACTIVATE], 0);
Elliot Lee's avatar
Elliot Lee committed
433 434
}

435 436
void
gtk_menu_item_toggle_size_request (GtkMenuItem *menu_item,
437
				   gint        *requisition)
438 439 440
{
  g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));

Manish Singh's avatar
Manish Singh committed
441
  g_signal_emit (menu_item, menu_item_signals[TOGGLE_SIZE_REQUEST], 0, requisition);
442 443 444 445
}

void
gtk_menu_item_toggle_size_allocate (GtkMenuItem *menu_item,
446
				    gint         allocation)
447 448 449
{
  g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));

Manish Singh's avatar
Manish Singh committed
450
  g_signal_emit (menu_item, menu_item_signals[TOGGLE_SIZE_ALLOCATE], 0, allocation);
451 452
}

453 454 455 456 457 458 459 460 461 462
static void
gtk_menu_item_accel_width_foreach (GtkWidget *widget,
				   gpointer data)
{
  guint *width = data;

  if (GTK_IS_ACCEL_LABEL (widget))
    {
      guint w;

463
      w = gtk_accel_label_get_accel_width (GTK_ACCEL_LABEL (widget));
464 465 466 467 468 469 470
      *width = MAX (*width, w);
    }
  else if (GTK_IS_CONTAINER (widget))
    gtk_container_foreach (GTK_CONTAINER (widget),
			   gtk_menu_item_accel_width_foreach,
			   data);
}
Elliot Lee's avatar
Elliot Lee committed
471

472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491
static gint
get_minimum_width (GtkWidget *widget)
{
  PangoContext *context;
  PangoFontMetrics *metrics;
  gint height;

  context = gtk_widget_get_pango_context (widget);
  metrics = pango_context_get_metrics (context,
				       widget->style->font_desc,
				       pango_context_get_language (context));

  height = pango_font_metrics_get_ascent (metrics) +
      pango_font_metrics_get_descent (metrics);
  
  pango_font_metrics_unref (metrics);

  return PANGO_PIXELS (7 * height);
}

Elliot Lee's avatar
Elliot Lee committed
492 493 494 495
static void
gtk_menu_item_size_request (GtkWidget      *widget,
			    GtkRequisition *requisition)
{
Tim Janik's avatar
Tim Janik committed
496
  GtkMenuItem *menu_item;
Elliot Lee's avatar
Elliot Lee committed
497
  GtkBin *bin;
498
  guint accel_width;
499
  guint horizontal_padding;
Elliot Lee's avatar
Elliot Lee committed
500 501 502 503

  g_return_if_fail (GTK_IS_MENU_ITEM (widget));
  g_return_if_fail (requisition != NULL);

504 505 506 507
  gtk_widget_style_get (widget,
 			"horizontal padding", &horizontal_padding,
			NULL);
  
Elliot Lee's avatar
Elliot Lee committed
508
  bin = GTK_BIN (widget);
Tim Janik's avatar
Tim Janik committed
509
  menu_item = GTK_MENU_ITEM (widget);
Elliot Lee's avatar
Elliot Lee committed
510 511

  requisition->width = (GTK_CONTAINER (widget)->border_width +
512
			widget->style->xthickness + horizontal_padding) * 2;
Elliot Lee's avatar
Elliot Lee committed
513
  requisition->height = (GTK_CONTAINER (widget)->border_width +
514
			 widget->style->ythickness) * 2;
Elliot Lee's avatar
Elliot Lee committed
515 516 517

  if (bin->child && GTK_WIDGET_VISIBLE (bin->child))
    {
518 519 520
      GtkRequisition child_requisition;
      
      gtk_widget_size_request (bin->child, &child_requisition);
Elliot Lee's avatar
Elliot Lee committed
521

522 523
      requisition->width += child_requisition.width;
      requisition->height += child_requisition.height;
524

525
      if (menu_item->submenu && menu_item->show_submenu_indicator)
526 527 528 529 530 531 532 533 534
	{
	  guint arrow_spacing;
	  
	  gtk_widget_style_get (widget,
				"arrow_spacing", &arrow_spacing,
				NULL);

	  requisition->width += child_requisition.height;
	  requisition->width += arrow_spacing;
535 536

	  requisition->width = MAX (requisition->width, get_minimum_width (widget));
537 538 539 540 541 542
	}
    }
  else
    {
      /* separator item */
      requisition->height += 4;
543
    }
544 545 546 547 548 549

  accel_width = 0;
  gtk_container_foreach (GTK_CONTAINER (menu_item),
			 gtk_menu_item_accel_width_foreach,
			 &accel_width);
  menu_item->accelerator_width = accel_width;
Elliot Lee's avatar
Elliot Lee committed
550 551 552 553 554 555
}

static void
gtk_menu_item_size_allocate (GtkWidget     *widget,
			     GtkAllocation *allocation)
{
Tim Janik's avatar
Tim Janik committed
556
  GtkMenuItem *menu_item;
Elliot Lee's avatar
Elliot Lee committed
557 558
  GtkBin *bin;
  GtkAllocation child_allocation;
559
  GtkTextDirection direction;
Elliot Lee's avatar
Elliot Lee committed
560 561 562 563

  g_return_if_fail (GTK_IS_MENU_ITEM (widget));
  g_return_if_fail (allocation != NULL);

Tim Janik's avatar
Tim Janik committed
564
  menu_item = GTK_MENU_ITEM (widget);
Elliot Lee's avatar
Elliot Lee committed
565
  bin = GTK_BIN (widget);
Tim Janik's avatar
Tim Janik committed
566
  
567 568
  direction = gtk_widget_get_direction (widget);

Tim Janik's avatar
Tim Janik committed
569
  widget->allocation = *allocation;
Elliot Lee's avatar
Elliot Lee committed
570 571 572

  if (bin->child)
    {
573
      GtkRequisition child_requisition;
574 575 576 577 578
      guint horizontal_padding;

      gtk_widget_style_get (widget,
			    "horizontal_padding", &horizontal_padding,
			    NULL);
579
      
Elliot Lee's avatar
Elliot Lee committed
580
      child_allocation.x = (GTK_CONTAINER (widget)->border_width +
581
                            widget->style->xthickness +
582
			    horizontal_padding);
Owen Taylor's avatar
Owen Taylor committed
583
      child_allocation.y = (GTK_CONTAINER (widget)->border_width +
584
			    widget->style->ythickness);
585 586
      child_allocation.width = MAX (1, (gint)allocation->width - child_allocation.x * 2);
      child_allocation.height = MAX (1, (gint)allocation->height - child_allocation.y * 2);
587 588
      if (direction == GTK_TEXT_DIR_LTR)
	child_allocation.x += GTK_MENU_ITEM (widget)->toggle_size;
Tim Janik's avatar
Tim Janik committed
589
      child_allocation.width -= GTK_MENU_ITEM (widget)->toggle_size;
590 591
      child_allocation.x += widget->allocation.x;
      child_allocation.y += widget->allocation.y;
592 593

      gtk_widget_get_child_requisition (bin->child, &child_requisition);
594 595 596 597 598 599 600
      if (menu_item->submenu && menu_item->show_submenu_indicator) 
	{
	  if (direction == GTK_TEXT_DIR_RTL)
	    child_allocation.x += child_requisition.height;
	  child_allocation.width -= child_requisition.height;
	}
      
601 602 603
      if (child_allocation.width < 1)
	child_allocation.width = 1;

Elliot Lee's avatar
Elliot Lee committed
604 605 606
      gtk_widget_size_allocate (bin->child, &child_allocation);
    }

Tim Janik's avatar
Tim Janik committed
607
  if (GTK_WIDGET_REALIZED (widget))
608
    gdk_window_move_resize (menu_item->event_window,
Tim Janik's avatar
Tim Janik committed
609 610
                            allocation->x, allocation->y,
                            allocation->width, allocation->height);
Elliot Lee's avatar
Elliot Lee committed
611

Tim Janik's avatar
Tim Janik committed
612 613
  if (menu_item->submenu)
    gtk_menu_reposition (GTK_MENU (menu_item->submenu));
Elliot Lee's avatar
Elliot Lee committed
614 615
}

616 617 618 619 620 621 622 623 624 625
static void
gtk_menu_item_realize (GtkWidget *widget)
{
  GtkMenuItem *menu_item = GTK_MENU_ITEM (widget);
  GdkWindowAttr attributes;
  gint attributes_mask;

  GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);

  widget->window = gtk_widget_get_parent_window (widget);
Manish Singh's avatar
Manish Singh committed
626
  g_object_ref (widget->window);
627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666
  
  attributes.x = widget->allocation.x;
  attributes.y = widget->allocation.y;
  attributes.width = widget->allocation.width;
  attributes.height = widget->allocation.height;
  attributes.window_type = GDK_WINDOW_CHILD;
  attributes.wclass = GDK_INPUT_ONLY;
  attributes.event_mask = (gtk_widget_get_events (widget) |
			   GDK_BUTTON_PRESS_MASK |
			   GDK_BUTTON_RELEASE_MASK |
			   GDK_ENTER_NOTIFY_MASK |
			   GDK_LEAVE_NOTIFY_MASK |
			   GDK_POINTER_MOTION_MASK);

  attributes_mask = GDK_WA_X | GDK_WA_Y;
  menu_item->event_window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
  gdk_window_set_user_data (menu_item->event_window, widget);

  widget->style = gtk_style_attach (widget->style, widget->window);
}

static void
gtk_menu_item_unrealize (GtkWidget *widget)
{
  GtkMenuItem *menu_item = GTK_MENU_ITEM (widget);

  gdk_window_set_user_data (menu_item->event_window, NULL);
  gdk_window_destroy (menu_item->event_window);
  menu_item->event_window = NULL;
  
  if (GTK_WIDGET_CLASS (parent_class)->unrealize)
    (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
}

static void
gtk_menu_item_map (GtkWidget *widget)
{
  GtkMenuItem *menu_item = GTK_MENU_ITEM (widget);
  
  GTK_WIDGET_CLASS (parent_class)->map (widget);
667 668

  gdk_window_show (menu_item->event_window);
669 670 671 672 673 674 675 676 677 678 679 680
}

static void
gtk_menu_item_unmap (GtkWidget *widget)
{
  GtkMenuItem *menu_item = GTK_MENU_ITEM (widget);
    
  gdk_window_hide (menu_item->event_window);

  GTK_WIDGET_CLASS (parent_class)->unmap (widget);
}

Elliot Lee's avatar
Elliot Lee committed
681 682 683 684 685 686
static void
gtk_menu_item_paint (GtkWidget    *widget,
		     GdkRectangle *area)
{
  GtkMenuItem *menu_item;
  GtkStateType state_type;
687
  GtkShadowType shadow_type, selected_shadow_type;
Elliot Lee's avatar
Elliot Lee committed
688 689
  gint width, height;
  gint x, y;
690
  gint border_width = GTK_CONTAINER (widget)->border_width;
Elliot Lee's avatar
Elliot Lee committed
691 692 693 694 695 696

  if (GTK_WIDGET_DRAWABLE (widget))
    {
      menu_item = GTK_MENU_ITEM (widget);

      state_type = widget->state;
697
      
698 699 700 701
      x = widget->allocation.x + border_width;
      y = widget->allocation.y + border_width;
      width = widget->allocation.width - border_width * 2;
      height = widget->allocation.height - border_width * 2;
702
      
Elliot Lee's avatar
Elliot Lee committed
703 704
      if ((state_type == GTK_STATE_PRELIGHT) &&
	  (GTK_BIN (menu_item)->child))
705 706 707 708 709 710 711 712 713 714 715 716
	{
	  gtk_widget_style_get (widget,
				"selected_shadow_type", &selected_shadow_type,
				NULL);
	  gtk_paint_box (widget->style,
			 widget->window,
			 GTK_STATE_PRELIGHT,
			 selected_shadow_type,
			 area, widget, "menuitem",
			 x, y, width, height);
	}
  
Tim Janik's avatar
Tim Janik committed
717
      if (menu_item->submenu && menu_item->show_submenu_indicator)
Elliot Lee's avatar
Elliot Lee committed
718
	{
719
	  GtkRequisition child_requisition;
720
	  gint arrow_x, arrow_y;
721 722
	  gint arrow_size;
	  gint arrow_extent;
723
	  guint horizontal_padding;
724 725
	  GtkTextDirection direction;
	  GtkArrowType arrow_type;
726

727 728
	  direction = gtk_widget_get_direction (widget);
      
729 730 731 732
 	  gtk_widget_style_get (widget,
 				"horizontal_padding", &horizontal_padding,
 				NULL);
 	  
733 734 735 736
	  gtk_widget_get_child_requisition (GTK_BIN (menu_item)->child,
					    &child_requisition);

	  arrow_size = child_requisition.height - 2 * widget->style->ythickness;
737
	  arrow_extent = arrow_size * 0.8;
738
	  
Elliot Lee's avatar
Elliot Lee committed
739 740 741 742
	  shadow_type = GTK_SHADOW_OUT;
	  if (state_type == GTK_STATE_PRELIGHT)
	    shadow_type = GTK_SHADOW_IN;

743 744 745 746 747 748 749 750 751 752
	  if (direction == GTK_TEXT_DIR_LTR)
	    {
	      arrow_x = x + width - horizontal_padding - arrow_extent;
	      arrow_type = GTK_ARROW_RIGHT;
	    }
	  else
	    {
	      arrow_x = x + horizontal_padding;
	      arrow_type = GTK_ARROW_LEFT;
	    }
753

754 755
	  arrow_y = y + (height - arrow_extent) / 2;

756 757 758
	  gtk_paint_arrow (widget->style, widget->window,
			   state_type, shadow_type, 
			   area, widget, "menuitem", 
759
			   arrow_type, TRUE,
760 761
			   arrow_x, arrow_y,
			   arrow_extent, arrow_extent);
Elliot Lee's avatar
Elliot Lee committed
762 763 764
	}
      else if (!GTK_BIN (menu_item)->child)
	{
765 766 767 768 769 770
	  guint horizontal_padding;

	  gtk_widget_style_get (widget,
				"horizontal_padding", &horizontal_padding,
				NULL);
	  
771 772
	  gtk_paint_hline (widget->style, widget->window, GTK_STATE_NORMAL,
			   area, widget, "menuitem",
773 774 775 776
			   widget->allocation.x + horizontal_padding + widget->style->xthickness,
			   widget->allocation.x + widget->allocation.width - horizontal_padding - widget->style->xthickness - 1,
			   widget->allocation.y + (widget->allocation.height -
						   widget->style->ythickness) / 2);
Elliot Lee's avatar
Elliot Lee committed
777 778 779 780 781 782 783 784 785 786 787 788 789 790 791
	}
    }
}

static gint
gtk_menu_item_expose (GtkWidget      *widget,
		      GdkEventExpose *event)
{
  g_return_val_if_fail (GTK_IS_MENU_ITEM (widget), FALSE);
  g_return_val_if_fail (event != NULL, FALSE);

  if (GTK_WIDGET_DRAWABLE (widget))
    {
      gtk_menu_item_paint (widget, &event->area);

792
      (* GTK_WIDGET_CLASS (parent_class)->expose_event) (widget, event);
Elliot Lee's avatar
Elliot Lee committed
793 794 795 796 797
    }

  return FALSE;
}

798 799 800 801 802 803 804 805 806 807 808 809 810
static gint
get_popup_delay (GtkMenuItem *menu_item)
{
  GtkWidget *parent = GTK_WIDGET (menu_item)->parent;

  if (GTK_IS_MENU_SHELL (parent))
    {
      return _gtk_menu_shell_get_popup_delay (GTK_MENU_SHELL (parent));
    }
  else
    {
      gint popup_delay;
      
811
      g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu_item)),
812 813 814 815 816 817 818
		    "gtk-menu-popup-delay", &popup_delay,
		    NULL);

      return popup_delay;
    }
}

Elliot Lee's avatar
Elliot Lee committed
819 820 821 822 823 824 825 826 827
static void
gtk_real_menu_item_select (GtkItem *item)
{
  GtkMenuItem *menu_item;

  g_return_if_fail (GTK_IS_MENU_ITEM (item));

  menu_item = GTK_MENU_ITEM (item);

828 829
  if (menu_item->submenu)
    {
830
      gint popup_delay;
831

832
      if (menu_item->timer)
833
	g_source_remove (menu_item->timer);
834 835 836 837

      popup_delay = get_popup_delay (menu_item);
      
      if (popup_delay > 0)
838
	{
839 840
	  GdkEvent *event = gtk_get_current_event ();
	  
841 842 843
	  menu_item->timer = g_timeout_add (popup_delay,
					    gtk_menu_item_select_timeout,
					    menu_item);
844 845 846 847 848 849
	  if (event &&
	      event->type != GDK_BUTTON_PRESS &&
	      event->type != GDK_ENTER_NOTIFY)
	    menu_item->timer_from_keypress = TRUE;
	  else
	    menu_item->timer_from_keypress = FALSE;
850 851

	  if (event)
852
	    gdk_event_free (event);
853
	}
854
      else
855
	gtk_menu_item_popup_submenu (menu_item);
856
    }
857
  
Elliot Lee's avatar
Elliot Lee committed
858
  gtk_widget_set_state (GTK_WIDGET (menu_item), GTK_STATE_PRELIGHT);
Soeren Sandmann's avatar
Soeren Sandmann committed
859
  gtk_widget_queue_draw (GTK_WIDGET (menu_item));
Elliot Lee's avatar
Elliot Lee committed
860 861 862 863 864 865 866 867 868 869 870 871 872 873
}

static void
gtk_real_menu_item_deselect (GtkItem *item)
{
  GtkMenuItem *menu_item;

  g_return_if_fail (GTK_IS_MENU_ITEM (item));

  menu_item = GTK_MENU_ITEM (item);

  if (menu_item->submenu)
    {
      if (menu_item->timer)
874
	{
875
	  g_source_remove (menu_item->timer);
876 877
	  menu_item->timer = 0;
	}
Elliot Lee's avatar
Elliot Lee committed
878 879 880 881 882
      else
	gtk_menu_popdown (GTK_MENU (menu_item->submenu));
    }

  gtk_widget_set_state (GTK_WIDGET (menu_item), GTK_STATE_NORMAL);
Soeren Sandmann's avatar
Soeren Sandmann committed
883
  gtk_widget_queue_draw (GTK_WIDGET (menu_item));
Elliot Lee's avatar
Elliot Lee committed
884 885
}

886
static gboolean
887
gtk_menu_item_mnemonic_activate (GtkWidget *widget,
888 889
				 gboolean   group_cycling)
{
890 891 892 893
  if (group_cycling &&
      widget->parent &&
      GTK_IS_MENU_SHELL (widget->parent) &&
      GTK_MENU_SHELL (widget->parent)->active)
894
    {
895 896
      gtk_menu_shell_select_item (GTK_MENU_SHELL (widget->parent),
				  widget);
897 898
    }
  else
Manish Singh's avatar
Manish Singh committed
899
    g_signal_emit (widget, menu_item_signals[ACTIVATE_ITEM], 0);
900 901 902 903 904
  
  return TRUE;
}


905 906 907 908
static void
gtk_real_menu_item_activate_item (GtkMenuItem *menu_item)
{
  GtkWidget *widget;
909
  GtkMenuShell *submenu; 
910 911 912 913 914 915 916 917 918 919 920 921 922 923 924

  g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));

  widget = GTK_WIDGET (menu_item);
  
  if (widget->parent &&
      GTK_IS_MENU_SHELL (widget->parent))
    {
      if (menu_item->submenu == NULL)
	gtk_menu_shell_activate_item (GTK_MENU_SHELL (widget->parent),
				      widget, TRUE);
      else
	{
	  GtkMenuShell *menu_shell = GTK_MENU_SHELL (widget->parent);

925
	  _gtk_menu_shell_activate (menu_shell);
926

927 928 929
	  gtk_menu_shell_select_item (GTK_MENU_SHELL (widget->parent), widget); 
	  gtk_menu_item_popup_submenu (widget); 

930
	  gtk_menu_shell_select_first (GTK_MENU_SHELL (menu_item->submenu), TRUE);
931
	  submenu = GTK_MENU_SHELL (menu_item->submenu);
932 933 934
	}
    }
}
935 936
static void
gtk_real_menu_item_toggle_size_request (GtkMenuItem *menu_item,
937
					gint        *requisition)
938 939 940 941 942 943 944 945
{
  g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));

  *requisition = 0;
}

static void
gtk_real_menu_item_toggle_size_allocate (GtkMenuItem *menu_item,
946
					 gint         allocation)
947 948 949 950 951
{
  g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));

  menu_item->toggle_size = allocation;
}
952

Elliot Lee's avatar
Elliot Lee committed
953 954 955
static gint
gtk_menu_item_select_timeout (gpointer data)
{
956
  GtkMenuItem *menu_item;
957
  GtkWidget *parent;
958
  
959
  GDK_THREADS_ENTER ();
960

961
  menu_item = GTK_MENU_ITEM (data);
962 963 964 965 966 967 968 969 970

  parent = GTK_WIDGET (menu_item)->parent;

  if (parent && GTK_IS_MENU_SHELL (parent) && GTK_MENU_SHELL (parent)->active)
    {
      gtk_menu_item_popup_submenu (data);
      if (menu_item->timer_from_keypress && menu_item->submenu)
	GTK_MENU_SHELL (menu_item->submenu)->ignore_enter = TRUE;
    }
971 972 973 974 975 976 977

  GDK_THREADS_LEAVE ();

  return FALSE;  
}

static void
978
gtk_menu_item_popup_submenu (gpointer data)
979
{
980
  GtkWidget *widget;
981 982
  GtkMenuItem *menu_item;

983 984
  widget = GTK_WIDGET (data);
  menu_item = GTK_MENU_ITEM (widget);
985 986

  if (menu_item->timer)
987
    g_source_remove (menu_item->timer);
Elliot Lee's avatar
Elliot Lee committed
988 989
  menu_item->timer = 0;

990
  if (GTK_WIDGET_IS_SENSITIVE (menu_item->submenu))
991 992 993 994 995 996 997
    gtk_menu_popup (GTK_MENU (menu_item->submenu),
		    widget->parent,
		    widget,
		    gtk_menu_item_position_menu,
		    menu_item,
		    GTK_MENU_SHELL (widget->parent)->button,
		    0);
Elliot Lee's avatar
Elliot Lee committed
998 999
}

1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016
static void
get_offsets (GtkMenu *menu,
	     gint    *horizontal_offset,
	     gint    *vertical_offset)
{
  gint vertical_padding;
  
  gtk_widget_style_get (GTK_WIDGET (menu),
			"horizontal_offset", horizontal_offset,
			"vertical_offset", vertical_offset,
			"vertical_padding", &vertical_padding,
			NULL);

  *vertical_offset -= GTK_WIDGET (menu)->style->ythickness;
  *vertical_offset -= vertical_padding;
}

Elliot Lee's avatar
Elliot Lee committed
1017 1018 1019 1020
static void
gtk_menu_item_position_menu (GtkMenu  *menu,
			     gint     *x,
			     gint     *y,
1021
			     gboolean *push_in,
Elliot Lee's avatar
Elliot Lee committed
1022 1023 1024
			     gpointer  user_data)
{
  GtkMenuItem *menu_item;
1025
  GtkWidget *widget;
Tim Janik's avatar
Tim Janik committed
1026
  GtkWidget *parent_menu_item;
1027
  GdkScreen *screen;
Elliot Lee's avatar
Elliot Lee committed
1028 1029
  gint twidth, theight;
  gint tx, ty;
1030
  GtkTextDirection direction;
1031 1032
  GdkRectangle monitor;
  gint monitor_num;
1033 1034 1035
  gint horizontal_offset;
  gint vertical_offset;
  gint parent_xthickness;
Elliot Lee's avatar
Elliot Lee committed
1036 1037 1038 1039 1040 1041

  g_return_if_fail (menu != NULL);
  g_return_if_fail (x != NULL);
  g_return_if_fail (y != NULL);

  menu_item = GTK_MENU_ITEM (user_data);
1042
  widget = GTK_WIDGET (user_data);
Elliot Lee's avatar
Elliot Lee committed
1043

1044 1045 1046
  if (push_in)
    *push_in = FALSE;

1047 1048
  direction = gtk_widget_get_direction (widget);

Elliot Lee's avatar
Elliot Lee committed
1049 1050 1051
  twidth = GTK_WIDGET (menu)->requisition.width;
  theight = GTK_WIDGET (menu)->requisition.height;

1052
  screen = gtk_widget_get_screen (GTK_WIDGET (menu));
1053
  monitor_num = gdk_screen_get_monitor_at_window (screen, menu_item->event_window);
1054 1055 1056
  if (monitor_num < 0)
    monitor_num = 0;
  gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
Elliot Lee's avatar
Elliot Lee committed
1057

1058
  if (!gdk_window_get_origin (widget->window, &tx, &ty))
Owen Taylor's avatar
Owen Taylor committed
1059 1060 1061 1062
    {
      g_warning ("Menu not on screen");
      return;
    }
Elliot Lee's avatar
Elliot Lee committed
1063

1064 1065 1066
  tx += widget->allocation.x;
  ty += widget->allocation.y;

1067 1068
  get_offsets (menu, &horizontal_offset, &vertical_offset);
  
Elliot Lee's avatar
Elliot Lee committed
1069 1070 1071
  switch (menu_item->submenu_placement)
    {
    case GTK_TOP_BOTTOM:
1072 1073 1074 1075 1076 1077 1078 1079
      if (direction == GTK_TEXT_DIR_LTR)
	menu_item->submenu_direction = GTK_DIRECTION_RIGHT;
      else 
	{
	  menu_item->submenu_direction = GTK_DIRECTION_LEFT;
	  tx += widget->allocation.width - twidth;
	}

1080
      if ((ty + widget->allocation.height + theight) <= monitor.y + monitor.height)
1081
	ty += widget->allocation.height;
1082
      else if ((ty - theight) >= monitor.y)
Elliot Lee's avatar
Elliot Lee committed
1083
	ty -= theight;
1084
      else if (monitor.y + monitor.height - (ty + widget->allocation.height) > ty)
1085
	ty += widget->allocation.height;
1086 1087
      else
	ty -= theight;
Elliot Lee's avatar
Elliot Lee committed
1088 1089 1090
      break;

    case GTK_LEFT_RIGHT:
1091
      parent_menu_item = GTK_MENU (widget->parent)->parent_menu_item;
1092
      parent_xthickness = widget->parent->style->xthickness;
1093 1094 1095
      if (parent_menu_item && 
	  !GTK_MENU (widget->parent)->torn_off && 
	  !GTK_MENU_SHELL (menu)->active)
Tim Janik's avatar
Tim Janik committed
1096
	menu_item->submenu_direction = GTK_MENU_ITEM (parent_menu_item)->submenu_direction;
1097 1098 1099 1100
      else if (direction == GTK_TEXT_DIR_LTR)
	menu_item->submenu_direction = GTK_DIRECTION_RIGHT;
      else
	menu_item->submenu_direction = GTK_DIRECTION_LEFT;
Elliot Lee's avatar
Elliot Lee committed
1101 1102 1103 1104

      switch (menu_item->submenu_direction)
	{
	case GTK_DIRECTION_LEFT:
1105 1106
	  if ((tx - twidth - parent_xthickness - horizontal_offset) >= monitor.x)
	    tx -= twidth + parent_xthickness + horizontal_offset;
Elliot Lee's avatar
Elliot Lee committed
1107 1108 1109
	  else
	    {
	      menu_item->submenu_direction = GTK_DIRECTION_RIGHT;
1110
	      tx += widget->allocation.width + parent_xthickness + horizontal_offset;
Elliot Lee's avatar
Elliot Lee committed
1111 1112 1113 1114
	    }
	  break;

	case GTK_DIRECTION_RIGHT:
1115 1116
	  if ((tx + widget->allocation.width + parent_xthickness + horizontal_offset + twidth) <= monitor.x + monitor.width)
	    tx += widget->allocation.width + parent_xthickness + horizontal_offset;
Elliot Lee's avatar
Elliot Lee committed
1117 1118 1119
	  else
	    {
	      menu_item->submenu_direction = GTK_DIRECTION_LEFT;
1120
	      tx -= twidth + parent_xthickness + horizontal_offset;
Elliot Lee's avatar
Elliot Lee committed
1121 1122 1123 1124
	    }
	  break;
	}

1125 1126
      ty += vertical_offset;
      
1127
      /* If the height of the menu doesn't fit we move it upward. */
1128
      ty = CLAMP (ty, monitor.y, MAX (monitor.y, monitor.y + monitor.height - theight));
Elliot Lee's avatar
Elliot Lee committed
1129 1130 1131
      break;
    }

1132 1133
  /* If we have negative, tx, here it is because we can't get
   * the menu all the way on screen. Favor the left portion.
1134
   */
1135
  *x = CLAMP (tx, monitor.x, MAX (monitor.x, monitor.x + monitor.width - twidth));
1136
  *y = ty;
1137 1138

  gtk_menu_set_monitor (menu, monitor_num);
Elliot Lee's avatar
Elliot Lee committed
1139 1140
}

1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152
/**
 * gtk_menu_item_set_right_justified:
 * @menu_item: a #GtkMenuItem.
 * @right_justified: if %TRUE the menu item will appear at the 
 *   far right if added to a menu bar.
 * 
 * Sets whether the menu item appears justified at the right
 * side of a menu bar. This was traditionally done for "Help" menu
 * items, but is now considered a bad idea. (If the widget
 * layout is reversed for a right-to-left language like Hebrew
 * or Arabic, right-justified-menu-items appear at the left.)
 **/
Elliot Lee's avatar
Elliot Lee committed
1153
void
1154 1155
gtk_menu_item_set_right_justified (GtkMenuItem *menu_item,
				   gboolean     right_justified)
Elliot Lee's avatar
Elliot Lee committed
1156
{
1157 1158 1159
  g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));

  right_justified = right_justified != FALSE;
Elliot Lee's avatar
Elliot Lee committed
1160

1161 1162 1163
  if (right_justified != menu_item->right_justify)
    {
      menu_item->right_justify = right_justified;
1164
      gtk_widget_queue_resize (GTK_WIDGET (menu_item));
1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183
    }
}

/**
 * gtk_menu_item_get_right_justified:
 * @menu_item: a #GtkMenuItem
 * 
 * Gets whether the menu item appears justified at the right
 * side of the menu bar.
 * 
 * Return value: %TRUE if the menu item will appear at the
 *   far right if added to a menu bar.
 **/
gboolean
gtk_menu_item_get_right_justified (GtkMenuItem *menu_item)
{
  g_return_val_if_fail (GTK_IS_MENU_ITEM (menu_item), FALSE);
  
  return menu_item->right_justify;
Elliot Lee's avatar
Elliot Lee committed
1184
}
1185

1186

1187 1188 1189
static void
gtk_menu_item_show_all (GtkWidget *widget)
{
1190
  GtkMenuItem *menu_item;
1191 1192

  g_return_if_fail (GTK_IS_MENU_ITEM (widget));
1193

1194 1195
  menu_item = GTK_MENU_ITEM (widget);

1196
  /* show children including submenu */
1197 1198
  if (menu_item->submenu)
    gtk_widget_show_all (menu_item->submenu);
1199 1200
  gtk_container_foreach (GTK_CONTAINER (widget), (GtkCallback) gtk_widget_show_all, NULL);

1201 1202 1203 1204 1205 1206
  gtk_widget_show (widget);
}

static void
gtk_menu_item_hide_all (GtkWidget *widget)
{
1207
  GtkMenuItem *menu_item;
1208 1209 1210 1211

  g_return_if_fail (GTK_IS_MENU_ITEM (widget));

  gtk_widget_hide (widget);
1212 1213 1214 1215 1216

  menu_item = GTK_MENU_ITEM (widget);

  /* hide children including submenu */
  gtk_container_foreach (GTK_CONTAINER (widget), (GtkCallback) gtk_widget_hide_all, NULL);
1217 1218
  if (menu_item->submenu)
    gtk_widget_hide_all (menu_item->submenu);
1219 1220
}

1221
static gboolean
Owen Taylor's avatar
Owen Taylor committed
1222 1223
gtk_menu_item_can_activate_accel (GtkWidget *widget,
				  guint      signal_id)
1224
{
Owen Taylor's avatar
Owen Taylor committed
1225
  /* Chain to the parent GtkMenu for further checks */
1226 1227 1228 1229
  return (GTK_WIDGET_IS_SENSITIVE (widget) && GTK_WIDGET_VISIBLE (widget) &&
          widget->parent && gtk_widget_can_activate_accel (widget->parent, signal_id));
}

1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250
static void
gtk_menu_item_accel_name_foreach (GtkWidget *widget,
				  gpointer data)
{
  const gchar **path_p = data;

  if (!*path_p)
    {
      if (GTK_IS_LABEL (widget))
	{
	  *path_p = gtk_label_get_text (GTK_LABEL (widget));
	  if (*path_p && (*path_p)[0] == 0)
	    *path_p = NULL;
	}
      else if (GTK_IS_CONTAINER (widget))
	gtk_container_foreach (GTK_CONTAINER (widget),
			       gtk_menu_item_accel_name_foreach,
			       data);
    }
}

1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267
static void
gtk_menu_item_parent_set (GtkWidget *widget,
			  GtkWidget *previous_parent)
{
  GtkMenuItem *menu_item = GTK_MENU_ITEM (widget);
  GtkMenu *menu = GTK_IS_MENU (widget->parent) ? GTK_MENU (widget->parent) : NULL;

  if (menu)
    _gtk_menu_item_refresh_accel_path (menu_item,
				       menu->accel_path,
				       menu->accel_group,
				       TRUE);

  if (GTK_WIDGET_CLASS (parent_class)->parent_set)
    GTK_WIDGET_CLASS (parent_class)->parent_set (widget, previous_parent);
}

1268 1269 1270 1271 1272 1273 1274 1275 1276 1277
void
_gtk_menu_item_refresh_accel_path (GtkMenuItem   *menu_item,
				   const gchar   *prefix,
				   GtkAccelGroup *accel_group,
				   gboolean       group_changed)
{
  const gchar *path;
  GtkWidget *widget;

  g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
1278
  g_return_if_fail (!accel_group || GTK_IS_ACCEL_GROUP (accel_group));
1279 1280 1281

  widget = GTK_WIDGET (menu_item);

1282 1283
  if (!accel_group)
    {
1284
      gtk_widget_set_accel_path (widget, NULL, NULL);
1285 1286 1287
      return;
    }

1288
  path = _gtk_widget_get_accel_path (widget, NULL);
1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303
  if (!path)					/* no active accel_path yet */
    {
      path = menu_item->accel_path;
      if (!path && prefix)
	{
	  gchar *postfix = NULL;

	  /* try to construct one from label text */
	  gtk_container_foreach (GTK_CONTAINER (menu_item),
				 gtk_menu_item_accel_name_foreach,
				 &postfix);
	  menu_item->accel_path = postfix ? g_strconcat (prefix, "/", postfix, NULL) : NULL;
	  path = menu_item->accel_path;
	}
      if (path)
1304
	gtk_widget_set_accel_path (widget, path, accel_group);
1305 1306
    }
  else if (group_changed)			/* reinstall accelerators */
1307
    gtk_widget_set_accel_path (widget, path, accel_group);
1308 1309 1310 1311 1312
}

/**
 * gtk_menu_item_set_accel_path
 * @menu_item:  a valid #GtkMenuItem
1313 1314
 * @accel_path: accelerator path, corresponding to this menu item's
 *              functionality, or %NULL to unset the current path.
1315 1316 1317 1318 1319 1320 1321 1322
 *
 * Set the accelerator path on @menu_item, through which runtime changes of the
 * menu item's accelerator caused by the user can be identified and saved to
 * persistant storage (see gtk_accel_map_save() on this).
 * To setup a default accelerator for this menu item, call
 * gtk_accel_map_add_entry() with the same @accel_path.
 * See also gtk_accel_map_add_entry() on the specifics of accelerator paths,
 * and gtk_menu_set_accel_path() for a more convenient variant of this function.
1323 1324 1325 1326
 *
 * This function is basically a convenience wrapper that handles calling
 * gtk_widget_set_accel_path() with the appropriate accelerator group for
 * the menu item.
Matthias Clasen's avatar
Matthias Clasen committed
1327 1328 1329
 *
 * Note that you do need to set an accelerator on the parent menu with
 * gtk_menu_set_accel_group() for this to work.
1330 1331 1332 1333 1334 1335 1336 1337
 */
void
gtk_menu_item_set_accel_path (GtkMenuItem *menu_item,
			      const gchar *accel_path)
{
  GtkWidget *widget;

  g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
1338 1339
  g_return_if_fail (accel_path == NULL ||
		    (accel_path[0] == '<' && strchr (accel_path, '/')));
1340 1341 1342 1343 1344 1345 1346 1347

  widget = GTK_WIDGET (menu_item);

  /* store new path */
  g_free (menu_item->accel_path);
  menu_item->accel_path = g_strdup (accel_path);

  /* forget accelerators associated with old path */
1348
  gtk_widget_set_accel_path (widget, NULL, NULL);
1349 1350

  /* install accelerators associated with new path */
1351
  if (widget->parent && GTK_IS_MENU (widget->parent))
1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362
    {
      GtkMenu *menu = GTK_MENU (widget->parent);

      if (menu->accel_group)
	_gtk_menu_item_refresh_accel_path (GTK_MENU_ITEM (widget),
					   NULL,
					   menu->accel_group,
					   FALSE);
    }
}

1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378
static void
gtk_menu_item_forall (GtkContainer *container,
		      gboolean      include_internals,
		      GtkCallback   callback,
		      gpointer      callback_data)
{
  GtkBin *bin;
  GtkMenuItem *menu_item;

  g_return_if_fail (GTK_IS_MENU_ITEM (container));
  g_return_if_fail (callback != NULL);

  bin = GTK_BIN (container);
  menu_item = GTK_MENU_ITEM (container);

  if (bin->child)
1379
    callback (bin->child, callback_data);