gtkmenuitem.c 31.8 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

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


#define BORDER_SPACING  3
42
#define SELECT_TIMEOUT  75
Elliot Lee's avatar
Elliot Lee committed
43 44 45 46 47 48

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


enum {
  ACTIVATE,
49
  ACTIVATE_ITEM,
50 51
  TOGGLE_SIZE_REQUEST,
  TOGGLE_SIZE_ALLOCATE,
Elliot Lee's avatar
Elliot Lee committed
52 53 54 55 56 57 58
  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);
59
static void gtk_menu_item_finalize       (GObject          *object);
Elliot Lee's avatar
Elliot Lee committed
60 61 62 63
static void gtk_menu_item_size_request   (GtkWidget        *widget,
					  GtkRequisition   *requisition);
static void gtk_menu_item_size_allocate  (GtkWidget        *widget,
					  GtkAllocation    *allocation);
64 65 66 67
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
68 69 70 71
static void gtk_menu_item_paint          (GtkWidget        *widget,
					  GdkRectangle     *area);
static gint gtk_menu_item_expose         (GtkWidget        *widget,
					  GdkEventExpose   *event);
72 73
static void gtk_menu_item_parent_set     (GtkWidget        *widget,
					  GtkWidget        *previous_parent);
74

75

76 77 78 79
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,
80
						     gint        *requisition);
81
static void gtk_real_menu_item_toggle_size_allocate (GtkMenuItem *menu_item,
82
						     gint         allocation);
83
static gboolean gtk_menu_item_mnemonic_activate     (GtkWidget   *widget,
84
						     gboolean     group_cycling);
85

Elliot Lee's avatar
Elliot Lee committed
86
static gint gtk_menu_item_select_timeout (gpointer          data);
87
static void gtk_menu_item_popup_submenu  (gpointer     data);
Elliot Lee's avatar
Elliot Lee committed
88 89 90
static void gtk_menu_item_position_menu  (GtkMenu          *menu,
					  gint             *x,
					  gint             *y,
91
					  gboolean         *push_in,
Elliot Lee's avatar
Elliot Lee committed
92
					  gpointer          user_data);
93 94
static void gtk_menu_item_show_all       (GtkWidget        *widget);
static void gtk_menu_item_hide_all       (GtkWidget        *widget);
95 96 97 98
static void gtk_menu_item_forall         (GtkContainer    *container,
					  gboolean         include_internals,
					  GtkCallback      callback,
					  gpointer         callback_data);
Elliot Lee's avatar
Elliot Lee committed
99

100

Elliot Lee's avatar
Elliot Lee committed
101
static GtkItemClass *parent_class;
102
static guint menu_item_signals[LAST_SIGNAL] = { 0 };
103 104
static guint32	last_submenu_deselect_time = 0;

Elliot Lee's avatar
Elliot Lee committed
105 106


Tim Janik's avatar
Tim Janik committed
107
GtkType
108
gtk_menu_item_get_type (void)
Elliot Lee's avatar
Elliot Lee committed
109
{
Tim Janik's avatar
Tim Janik committed
110
  static GtkType menu_item_type = 0;
Elliot Lee's avatar
Elliot Lee committed
111 112 113

  if (!menu_item_type)
    {
114
      static const GTypeInfo menu_item_info =
Elliot Lee's avatar
Elliot Lee committed
115 116
      {
	sizeof (GtkMenuItemClass),
117 118 119 120 121 122 123 124
	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
125 126
      };

127
      menu_item_type = g_type_register_static (GTK_TYPE_ITEM, "GtkMenuItem", &menu_item_info, 0);
Elliot Lee's avatar
Elliot Lee committed
128 129 130 131 132 133 134 135
    }

  return menu_item_type;
}

static void
gtk_menu_item_class_init (GtkMenuItemClass *klass)
{
136 137 138 139 140
  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
141

142
  parent_class = g_type_class_peek_parent (klass);
Elliot Lee's avatar
Elliot Lee committed
143

144
  gobject_class->finalize = gtk_menu_item_finalize;
145 146 147 148 149 150

  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;
151 152 153 154
  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;
155 156
  widget_class->show_all = gtk_menu_item_show_all;
  widget_class->hide_all = gtk_menu_item_hide_all;
157
  widget_class->mnemonic_activate = gtk_menu_item_mnemonic_activate;
158
  widget_class->parent_set = gtk_menu_item_parent_set;
159
  
160 161 162 163 164 165 166 167 168 169 170 171
  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
172 173
  menu_item_signals[ACTIVATE] =
    gtk_signal_new ("activate",
174
                    GTK_RUN_FIRST | GTK_RUN_ACTION,
175
                    GTK_CLASS_TYPE (object_class),
Elliot Lee's avatar
Elliot Lee committed
176
                    GTK_SIGNAL_OFFSET (GtkMenuItemClass, activate),
177
                    _gtk_marshal_VOID__VOID,
Elliot Lee's avatar
Elliot Lee committed
178
		    GTK_TYPE_NONE, 0);
179
  widget_class->activate_signal = menu_item_signals[ACTIVATE];
Elliot Lee's avatar
Elliot Lee committed
180

181 182 183
  menu_item_signals[ACTIVATE_ITEM] =
    gtk_signal_new ("activate_item",
                    GTK_RUN_FIRST,
184
                    GTK_CLASS_TYPE (object_class),
185 186 187 188
                    GTK_SIGNAL_OFFSET (GtkMenuItemClass, activate_item),
                    gtk_signal_default_marshaller,
		    GTK_TYPE_NONE, 0);

189 190 191 192 193
  menu_item_signals[TOGGLE_SIZE_REQUEST] =
    gtk_signal_new ("toggle_size_request",
                    GTK_RUN_FIRST,
                    GTK_CLASS_TYPE (object_class),
                    GTK_SIGNAL_OFFSET (GtkMenuItemClass, toggle_size_request),
194
                    _gtk_marshal_VOID__POINTER,
195
		    GTK_TYPE_NONE, 1,
196
		    GTK_TYPE_POINTER);
197 198 199 200 201 202

  menu_item_signals[TOGGLE_SIZE_ALLOCATE] =
    gtk_signal_new ("toggle_size_allocate",
                    GTK_RUN_FIRST,
                    GTK_CLASS_TYPE (object_class),
                    GTK_SIGNAL_OFFSET (GtkMenuItemClass, toggle_size_allocate),
203
                    _gtk_marshal_NONE__INT,
204
		    GTK_TYPE_NONE, 1,
205
		    GTK_TYPE_INT);
Elliot Lee's avatar
Elliot Lee committed
206 207 208 209 210
}

static void
gtk_menu_item_init (GtkMenuItem *menu_item)
{
211 212
  GTK_WIDGET_SET_FLAGS (menu_item, GTK_NO_WINDOW);
  
Elliot Lee's avatar
Elliot Lee committed
213 214
  menu_item->submenu = NULL;
  menu_item->toggle_size = 0;
215
  menu_item->accelerator_width = 0;
Elliot Lee's avatar
Elliot Lee committed
216 217 218 219 220 221 222 223 224
  menu_item->show_submenu_indicator = FALSE;
  menu_item->submenu_direction = GTK_DIRECTION_RIGHT;
  menu_item->submenu_placement = GTK_TOP_BOTTOM;
  menu_item->right_justify = FALSE;

  menu_item->timer = 0;
}

GtkWidget*
225
gtk_menu_item_new (void)
Elliot Lee's avatar
Elliot Lee committed
226 227 228 229 230 231 232 233
{
  return GTK_WIDGET (gtk_type_new (gtk_menu_item_get_type ()));
}

GtkWidget*
gtk_menu_item_new_with_label (const gchar *label)
{
  GtkWidget *menu_item;
Tim Janik's avatar
Tim Janik committed
234
  GtkWidget *accel_label;
Elliot Lee's avatar
Elliot Lee committed
235 236

  menu_item = gtk_menu_item_new ();
Tim Janik's avatar
Tim Janik committed
237 238
  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
239

Tim Janik's avatar
Tim Janik committed
240 241 242
  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
243 244 245 246

  return menu_item;
}

247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275

/**
 * 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 ();
  accel_label = gtk_type_new (GTK_TYPE_ACCEL_LABEL);
  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
276 277 278 279 280 281 282 283 284 285
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)
286
    gtk_widget_destroy (menu_item->submenu);
Elliot Lee's avatar
Elliot Lee committed
287

288 289 290 291 292 293 294 295 296 297 298
  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
299 300
}

301 302 303 304 305 306 307 308 309 310 311 312 313 314
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
315 316 317 318 319
void
gtk_menu_item_set_submenu (GtkMenuItem *menu_item,
			   GtkWidget   *submenu)
{
  g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
320
  
321
  if (menu_item->submenu != submenu)
Elliot Lee's avatar
Elliot Lee committed
322
    {
323 324
      gtk_menu_item_remove_submenu (menu_item);
      
325
      menu_item->submenu = submenu;
326 327 328 329
      gtk_menu_attach_to_widget (GTK_MENU (submenu),
				 GTK_WIDGET (menu_item),
				 gtk_menu_item_detacher);
      
330 331
      if (GTK_WIDGET (menu_item)->parent)
	gtk_widget_queue_resize (GTK_WIDGET (menu_item));
Elliot Lee's avatar
Elliot Lee committed
332 333 334
    }
}

335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351
/**
 * 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;
}

352 353 354 355 356 357 358 359 360
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));
}

361 362 363
void _gtk_menu_item_set_placement (GtkMenuItem         *menu_item,
				   GtkSubmenuPlacement  placement);

Elliot Lee's avatar
Elliot Lee committed
364
void
Owen Taylor's avatar
Owen Taylor committed
365
_gtk_menu_item_set_placement (GtkMenuItem         *menu_item,
Elliot Lee's avatar
Elliot Lee committed
366 367 368 369 370 371 372 373 374 375
			     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)
{
376 377
  g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
  
Elliot Lee's avatar
Elliot Lee committed
378 379 380 381 382 383
  gtk_item_select (GTK_ITEM (menu_item));
}

void
gtk_menu_item_deselect (GtkMenuItem *menu_item)
{
384 385
  g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
  
Elliot Lee's avatar
Elliot Lee committed
386 387 388 389 390 391
  gtk_item_deselect (GTK_ITEM (menu_item));
}

void
gtk_menu_item_activate (GtkMenuItem *menu_item)
{
392 393
  g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
  
Elliot Lee's avatar
Elliot Lee committed
394 395 396
  gtk_signal_emit (GTK_OBJECT (menu_item), menu_item_signals[ACTIVATE]);
}

397 398
void
gtk_menu_item_toggle_size_request (GtkMenuItem *menu_item,
399
				   gint        *requisition)
400 401 402 403 404 405 406 407
{
  g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));

  gtk_signal_emit (GTK_OBJECT (menu_item), menu_item_signals[TOGGLE_SIZE_REQUEST], requisition);
}

void
gtk_menu_item_toggle_size_allocate (GtkMenuItem *menu_item,
408
				    gint         allocation)
409 410 411 412 413 414
{
  g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));

  gtk_signal_emit (GTK_OBJECT (menu_item), menu_item_signals[TOGGLE_SIZE_ALLOCATE], allocation);
}

415 416 417 418 419 420 421 422 423 424
static void
gtk_menu_item_accel_width_foreach (GtkWidget *widget,
				   gpointer data)
{
  guint *width = data;

  if (GTK_IS_ACCEL_LABEL (widget))
    {
      guint w;

425
      w = gtk_accel_label_get_accel_width (GTK_ACCEL_LABEL (widget));
426 427 428 429 430 431 432
      *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
433 434 435 436 437

static void
gtk_menu_item_size_request (GtkWidget      *widget,
			    GtkRequisition *requisition)
{
Tim Janik's avatar
Tim Janik committed
438
  GtkMenuItem *menu_item;
Elliot Lee's avatar
Elliot Lee committed
439
  GtkBin *bin;
440
  guint accel_width;
Elliot Lee's avatar
Elliot Lee committed
441 442 443 444 445

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

  bin = GTK_BIN (widget);
Tim Janik's avatar
Tim Janik committed
446
  menu_item = GTK_MENU_ITEM (widget);
Elliot Lee's avatar
Elliot Lee committed
447 448

  requisition->width = (GTK_CONTAINER (widget)->border_width +
449
			widget->style->xthickness +
Elliot Lee's avatar
Elliot Lee committed
450 451
			BORDER_SPACING) * 2;
  requisition->height = (GTK_CONTAINER (widget)->border_width +
452
			 widget->style->ythickness) * 2;
Elliot Lee's avatar
Elliot Lee committed
453 454 455

  if (bin->child && GTK_WIDGET_VISIBLE (bin->child))
    {
456 457 458
      GtkRequisition child_requisition;
      
      gtk_widget_size_request (bin->child, &child_requisition);
Elliot Lee's avatar
Elliot Lee committed
459

460 461
      requisition->width += child_requisition.width;
      requisition->height += child_requisition.height;
Elliot Lee's avatar
Elliot Lee committed
462
    }
463

Tim Janik's avatar
Tim Janik committed
464 465
  if (menu_item->submenu && menu_item->show_submenu_indicator)
    requisition->width += 21;
466 467 468 469 470 471

  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
472 473 474 475 476 477
}

static void
gtk_menu_item_size_allocate (GtkWidget     *widget,
			     GtkAllocation *allocation)
{
Tim Janik's avatar
Tim Janik committed
478
  GtkMenuItem *menu_item;
Elliot Lee's avatar
Elliot Lee committed
479 480 481 482 483 484
  GtkBin *bin;
  GtkAllocation child_allocation;

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

Tim Janik's avatar
Tim Janik committed
485
  menu_item = GTK_MENU_ITEM (widget);
Elliot Lee's avatar
Elliot Lee committed
486
  bin = GTK_BIN (widget);
Tim Janik's avatar
Tim Janik committed
487 488
  
  widget->allocation = *allocation;
Elliot Lee's avatar
Elliot Lee committed
489 490 491 492

  if (bin->child)
    {
      child_allocation.x = (GTK_CONTAINER (widget)->border_width +
493
                            widget->style->xthickness +
Elliot Lee's avatar
Elliot Lee committed
494
			    BORDER_SPACING);
Owen Taylor's avatar
Owen Taylor committed
495
      child_allocation.y = (GTK_CONTAINER (widget)->border_width +
496
			    widget->style->ythickness);
497 498
      child_allocation.width = MAX (1, (gint)allocation->width - child_allocation.x * 2);
      child_allocation.height = MAX (1, (gint)allocation->height - child_allocation.y * 2);
Elliot Lee's avatar
Elliot Lee committed
499
      child_allocation.x += GTK_MENU_ITEM (widget)->toggle_size;
Tim Janik's avatar
Tim Janik committed
500
      child_allocation.width -= GTK_MENU_ITEM (widget)->toggle_size;
501 502 503
      child_allocation.x += widget->allocation.x;
      child_allocation.y += widget->allocation.y;
      
Tim Janik's avatar
Tim Janik committed
504 505 506
      if (menu_item->submenu && menu_item->show_submenu_indicator)
	child_allocation.width -= 21;
      
Elliot Lee's avatar
Elliot Lee committed
507 508 509
      gtk_widget_size_allocate (bin->child, &child_allocation);
    }

Tim Janik's avatar
Tim Janik committed
510
  if (GTK_WIDGET_REALIZED (widget))
511
    gdk_window_move_resize (menu_item->event_window,
Tim Janik's avatar
Tim Janik committed
512 513
                            allocation->x, allocation->y,
                            allocation->width, allocation->height);
Elliot Lee's avatar
Elliot Lee committed
514

Tim Janik's avatar
Tim Janik committed
515 516
  if (menu_item->submenu)
    gtk_menu_reposition (GTK_MENU (menu_item->submenu));
Elliot Lee's avatar
Elliot Lee committed
517 518
}

519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570
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);
  gdk_window_ref (widget->window);
  
  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_EXPOSURE_MASK |
			   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);
571 572

  gdk_window_show (menu_item->event_window);
573 574 575 576 577 578 579 580 581 582 583 584
}

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
585 586 587 588 589 590 591 592 593
static void
gtk_menu_item_paint (GtkWidget    *widget,
		     GdkRectangle *area)
{
  GtkMenuItem *menu_item;
  GtkStateType state_type;
  GtkShadowType shadow_type;
  gint width, height;
  gint x, y;
594
  gint border_width = GTK_CONTAINER (widget)->border_width;
Elliot Lee's avatar
Elliot Lee committed
595 596 597 598 599 600 601

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

      state_type = widget->state;

602 603 604 605
      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;
606
      
Elliot Lee's avatar
Elliot Lee committed
607 608
      if ((state_type == GTK_STATE_PRELIGHT) &&
	  (GTK_BIN (menu_item)->child))
609 610 611 612 613 614
	gtk_paint_box (widget->style,
		       widget->window,
		       GTK_STATE_PRELIGHT,
		       GTK_SHADOW_OUT,
		       area, widget, "menuitem",
		       x, y, width, height);
Elliot Lee's avatar
Elliot Lee committed
615

Tim Janik's avatar
Tim Janik committed
616
      if (menu_item->submenu && menu_item->show_submenu_indicator)
Elliot Lee's avatar
Elliot Lee committed
617 618 619 620 621
	{
	  shadow_type = GTK_SHADOW_OUT;
	  if (state_type == GTK_STATE_PRELIGHT)
	    shadow_type = GTK_SHADOW_IN;

622 623 624 625 626
	  gtk_paint_arrow (widget->style, widget->window,
			   state_type, shadow_type, 
			   area, widget, "menuitem", 
			   GTK_ARROW_RIGHT, TRUE,
			   x + width - 15, y + height / 2 - 5, 10, 10);
Elliot Lee's avatar
Elliot Lee committed
627 628 629
	}
      else if (!GTK_BIN (menu_item)->child)
	{
630 631
	   gtk_paint_hline (widget->style, widget->window, GTK_STATE_NORMAL,
			    area, widget, "menuitem",
632 633
			    widget->allocation.x, widget->allocation.width,
			    widget->allocation.y);
Elliot Lee's avatar
Elliot Lee committed
634 635 636 637 638 639 640 641 642 643 644 645 646 647 648
	}
    }
}

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);

649
      (* GTK_WIDGET_CLASS (parent_class)->expose_event) (widget, event);
Elliot Lee's avatar
Elliot Lee committed
650 651 652 653 654 655 656 657 658 659 660 661 662 663
    }

  return FALSE;
}

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);

664 665 666
  /*  if (menu_item->submenu && !GTK_WIDGET_VISIBLE (menu_item->submenu))*/
  if (menu_item->submenu)
    {
667 668 669 670 671 672 673 674 675
      guint32 etime;
      GdkEvent *event = gtk_get_current_event ();

      etime = event ? gdk_event_get_time (event) : GDK_CURRENT_TIME;
      if (etime >= last_submenu_deselect_time &&
	  last_submenu_deselect_time + SELECT_TIMEOUT > etime)
	menu_item->timer = gtk_timeout_add (SELECT_TIMEOUT - (etime - last_submenu_deselect_time),
					    gtk_menu_item_select_timeout,
					    menu_item);
676
      else
677
	gtk_menu_item_popup_submenu (menu_item);
678 679
      if (event)
	gdk_event_free(event);
680
    }
681
  
Elliot Lee's avatar
Elliot Lee committed
682 683 684 685 686 687 688 689 690 691 692 693 694 695 696
  gtk_widget_set_state (GTK_WIDGET (menu_item), GTK_STATE_PRELIGHT);
  gtk_widget_draw (GTK_WIDGET (menu_item), NULL);
}

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)
    {
697 698 699
      guint32 etime;
      GdkEvent *event = gtk_get_current_event ();

Elliot Lee's avatar
Elliot Lee committed
700
      if (menu_item->timer)
701 702 703 704
	{
	  gtk_timeout_remove (menu_item->timer);
	  menu_item->timer = 0;
	}
Elliot Lee's avatar
Elliot Lee committed
705 706
      else
	gtk_menu_popdown (GTK_MENU (menu_item->submenu));
707 708 709 710

      etime = event ? gdk_event_get_time (event) : GDK_CURRENT_TIME;
      if (etime > last_submenu_deselect_time)
	last_submenu_deselect_time = etime;
711 712
      if (event)
	gdk_event_free(event);
Elliot Lee's avatar
Elliot Lee committed
713 714 715 716 717 718
    }

  gtk_widget_set_state (GTK_WIDGET (menu_item), GTK_STATE_NORMAL);
  gtk_widget_draw (GTK_WIDGET (menu_item), NULL);
}

719
static gboolean
720
gtk_menu_item_mnemonic_activate (GtkWidget *widget,
721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737
				 gboolean   group_cycling)
{
  if (group_cycling)
    {
      if (widget->parent &&
	  GTK_IS_MENU_SHELL (widget->parent))
	gtk_menu_shell_select_item (GTK_MENU_SHELL (widget->parent),
				    widget);

    }
  else
    gtk_signal_emit (GTK_OBJECT (widget), menu_item_signals[ACTIVATE_ITEM]);
  
  return TRUE;
}


738 739 740 741
static void
gtk_real_menu_item_activate_item (GtkMenuItem *menu_item)
{
  GtkWidget *widget;
742
  GtkMenuShell *submenu; 
743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764

  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);

	  if (!menu_shell->active)
	    {
	      gtk_grab_add (GTK_WIDGET (menu_shell));
	      menu_shell->have_grab = TRUE;
	      menu_shell->active = TRUE;
	    }

765 766 767 768 769
	  gtk_menu_shell_select_item (GTK_MENU_SHELL (widget->parent), widget); 
	  gtk_menu_item_popup_submenu (widget); 

	  submenu = GTK_MENU_SHELL (menu_item->submenu);
	  if (submenu->children)
770 771 772 773 774 775 776
	    {
	      if (submenu->children->next &&
		  GTK_IS_TEAROFF_MENU_ITEM (submenu->children->data))
		gtk_menu_shell_select_item (submenu, submenu->children->next->data);
	      else
		gtk_menu_shell_select_item (submenu, submenu->children->data);
	    }
777 778 779
	}
    }
}
780 781
static void
gtk_real_menu_item_toggle_size_request (GtkMenuItem *menu_item,
782
					gint        *requisition)
783 784 785 786 787 788 789 790
{
  g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));

  *requisition = 0;
}

static void
gtk_real_menu_item_toggle_size_allocate (GtkMenuItem *menu_item,
791
					 gint         allocation)
792 793 794 795 796
{
  g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));

  menu_item->toggle_size = allocation;
}
797

Elliot Lee's avatar
Elliot Lee committed
798 799 800
static gint
gtk_menu_item_select_timeout (gpointer data)
{
801
  GDK_THREADS_ENTER ();
802

803
  gtk_menu_item_popup_submenu (data);
804 805 806 807 808 809 810

  GDK_THREADS_LEAVE ();

  return FALSE;  
}

static void
811
gtk_menu_item_popup_submenu (gpointer data)
812 813 814
{
  GtkMenuItem *menu_item;

Elliot Lee's avatar
Elliot Lee committed
815 816 817
  menu_item = GTK_MENU_ITEM (data);
  menu_item->timer = 0;

818
  if (GTK_WIDGET_IS_SENSITIVE (menu_item->submenu))
819
    {
820 821 822 823 824 825 826
      gtk_menu_popup (GTK_MENU (menu_item->submenu),
		      GTK_WIDGET (menu_item)->parent,
		      GTK_WIDGET (menu_item),
		      gtk_menu_item_position_menu,
		      menu_item,
		      GTK_MENU_SHELL (GTK_WIDGET (menu_item)->parent)->button,
		      0);
827
    }
Elliot Lee's avatar
Elliot Lee committed
828 829 830 831 832 833
}

static void
gtk_menu_item_position_menu (GtkMenu  *menu,
			     gint     *x,
			     gint     *y,
834
			     gboolean *push_in,
Elliot Lee's avatar
Elliot Lee committed
835 836 837
			     gpointer  user_data)
{
  GtkMenuItem *menu_item;
838
  GtkWidget *widget;
Tim Janik's avatar
Tim Janik committed
839
  GtkWidget *parent_menu_item;
Elliot Lee's avatar
Elliot Lee committed
840 841 842 843 844 845 846 847 848 849
  gint screen_width;
  gint screen_height;
  gint twidth, theight;
  gint tx, ty;

  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);
850
  widget = GTK_WIDGET (user_data);
Elliot Lee's avatar
Elliot Lee committed
851 852 853 854 855 856 857

  twidth = GTK_WIDGET (menu)->requisition.width;
  theight = GTK_WIDGET (menu)->requisition.height;

  screen_width = gdk_screen_width ();
  screen_height = gdk_screen_height ();

858
  if (!gdk_window_get_origin (widget->window, &tx, &ty))
Owen Taylor's avatar
Owen Taylor committed
859 860 861 862
    {
      g_warning ("Menu not on screen");
      return;
    }
Elliot Lee's avatar
Elliot Lee committed
863

864 865 866
  tx += widget->allocation.x;
  ty += widget->allocation.y;

Elliot Lee's avatar
Elliot Lee committed
867 868 869
  switch (menu_item->submenu_placement)
    {
    case GTK_TOP_BOTTOM:
870 871
      if ((ty + widget->allocation.height + theight) <= screen_height)
	ty += widget->allocation.height;
Elliot Lee's avatar
Elliot Lee committed
872 873
      else if ((ty - theight) >= 0)
	ty -= theight;
874 875
      else if (screen_height - (ty + widget->allocation.height) > ty)
	ty += widget->allocation.height;
876 877
      else
	ty -= theight;
Elliot Lee's avatar
Elliot Lee committed
878 879 880 881
      break;

    case GTK_LEFT_RIGHT:
      menu_item->submenu_direction = GTK_DIRECTION_RIGHT;
882
      parent_menu_item = GTK_MENU (widget->parent)->parent_menu_item;
Elliot Lee's avatar
Elliot Lee committed
883
      if (parent_menu_item)
Tim Janik's avatar
Tim Janik committed
884
	menu_item->submenu_direction = GTK_MENU_ITEM (parent_menu_item)->submenu_direction;
Elliot Lee's avatar
Elliot Lee committed
885 886 887 888 889 890 891 892 893

      switch (menu_item->submenu_direction)
	{
	case GTK_DIRECTION_LEFT:
	  if ((tx - twidth) >= 0)
	    tx -= twidth;
	  else
	    {
	      menu_item->submenu_direction = GTK_DIRECTION_RIGHT;
894
	      tx += widget->allocation.width - 5;
Elliot Lee's avatar
Elliot Lee committed
895 896 897 898
	    }
	  break;

	case GTK_DIRECTION_RIGHT:
899 900
	  if ((tx + widget->allocation.width + twidth - 5) <= screen_width)
	    tx += widget->allocation.width - 5;
Elliot Lee's avatar
Elliot Lee committed
901 902 903 904 905 906 907 908
	  else
	    {
	      menu_item->submenu_direction = GTK_DIRECTION_LEFT;
	      tx -= twidth;
	    }
	  break;
	}

909
      ty += widget->allocation.height / 4;
910

911 912
      /* If the height of the menu doesn't fit we move it upward. */
      ty = CLAMP (ty, 0, MAX (0, screen_height - theight));
Elliot Lee's avatar
Elliot Lee committed
913 914 915
      break;
    }

916 917
  /* If we have negative, tx, here it is because we can't get
   * the menu all the way on screen. Favor the left portion.
918 919
   */
  *x = CLAMP (tx, 0, MAX (0, screen_width - twidth));
920
  *y = ty;
Elliot Lee's avatar
Elliot Lee committed
921 922
}

923 924 925 926 927 928 929 930 931 932 933 934
/**
 * 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
935
void
936 937
gtk_menu_item_set_right_justified (GtkMenuItem *menu_item,
				   gboolean     right_justified)
Elliot Lee's avatar
Elliot Lee committed
938
{
939 940 941
  g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));

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

943 944 945
  if (right_justified != menu_item->right_justify)
    {
      menu_item->right_justify = right_justified;
946
      gtk_widget_queue_resize (GTK_WIDGET (menu_item));
947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965
    }
}

/**
 * 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
966
}
967

968

969 970 971
static void
gtk_menu_item_show_all (GtkWidget *widget)
{
972
  GtkMenuItem *menu_item;
973 974

  g_return_if_fail (GTK_IS_MENU_ITEM (widget));
975

976 977
  menu_item = GTK_MENU_ITEM (widget);

978
  /* show children including submenu */
979 980
  if (menu_item->submenu)
    gtk_widget_show_all (menu_item->submenu);
981 982
  gtk_container_foreach (GTK_CONTAINER (widget), (GtkCallback) gtk_widget_show_all, NULL);

983 984 985 986 987 988
  gtk_widget_show (widget);
}

static void
gtk_menu_item_hide_all (GtkWidget *widget)
{
989
  GtkMenuItem *menu_item;
990 991 992 993

  g_return_if_fail (GTK_IS_MENU_ITEM (widget));

  gtk_widget_hide (widget);
994 995 996 997 998

  menu_item = GTK_MENU_ITEM (widget);

  /* hide children including submenu */
  gtk_container_foreach (GTK_CONTAINER (widget), (GtkCallback) gtk_widget_hide_all, NULL);
999 1000
  if (menu_item->submenu)
    gtk_widget_hide_all (menu_item->submenu);
1001 1002
}

1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023
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);
    }
}

1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040
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);
}

1041 1042 1043 1044 1045 1046 1047 1048 1049 1050
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));
1051
  g_return_if_fail (!accel_group || GTK_IS_ACCEL_GROUP (accel_group));
1052 1053 1054

  widget = GTK_WIDGET (menu_item);

1055 1056
  if (!accel_group)
    {
1057
      gtk_widget_set_accel_path (widget, NULL, NULL);
1058 1059 1060
      return;
    }

1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076
  path = _gtk_widget_get_accel_path (widget);
  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)
1077
	gtk_widget_set_accel_path (widget, path, accel_group);
1078 1079
    }
  else if (group_changed)			/* reinstall accelerators */
1080
    gtk_widget_set_accel_path (widget, path, accel_group);
1081 1082 1083 1084 1085
}

/**
 * gtk_menu_item_set_accel_path
 * @menu_item:  a valid #GtkMenuItem
Matthias Clasen's avatar
Matthias Clasen committed
1086
 * @accel_path: accelerator path, corresponding to this menu item's functionality
1087 1088 1089 1090 1091 1092 1093 1094
 *
 * 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.
1095 1096 1097 1098
 *
 * This function is basically a convenience wrapper that handles calling
 * gtk_widget_set_accel_path() with the appropriate accelerator group for
 * the menu item.
1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115
 */
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));
  g_return_if_fail (accel_path && accel_path[0] == '<' && strchr (accel_path, '/'));

  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 */
1116
  gtk_widget_set_accel_path (widget, NULL, NULL);
1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130

  /* install accelerators associated with new path */
  if (widget->parent)
    {
      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);
    }
}

1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146
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)
1147
    callback (bin->child, callback_data);
1148
}
1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161

gboolean
_gtk_menu_item_is_selectable (GtkWidget *menu_item)
{
  if ((!GTK_BIN (menu_item)->child &&
       G_OBJECT_TYPE (menu_item) == GTK_TYPE_MENU_ITEM) ||
      GTK_IS_SEPARATOR_MENU_ITEM (menu_item) ||
      !GTK_WIDGET_IS_SENSITIVE (menu_item) ||
      !GTK_WIDGET_VISIBLE (menu_item))
    return FALSE;

  return TRUE;
}