gtkmenuitem.c 30.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/. 
 */

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


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

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


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

71

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

Elliot Lee's avatar
Elliot Lee committed
82
static gint gtk_menu_item_select_timeout (gpointer          data);
83
static void gtk_menu_item_popup_submenu  (gpointer     data);
Elliot Lee's avatar
Elliot Lee committed
84 85 86
static void gtk_menu_item_position_menu  (GtkMenu          *menu,
					  gint             *x,
					  gint             *y,
87
					  gboolean         *push_in,
Elliot Lee's avatar
Elliot Lee committed
88
					  gpointer          user_data);
89 90
static void gtk_menu_item_show_all       (GtkWidget        *widget);
static void gtk_menu_item_hide_all       (GtkWidget        *widget);
91 92 93 94
static void gtk_menu_item_forall         (GtkContainer    *container,
					  gboolean         include_internals,
					  GtkCallback      callback,
					  gpointer         callback_data);
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 };
99 100
static guint32	last_submenu_deselect_time = 0;

Elliot Lee's avatar
Elliot Lee committed
101 102


Tim Janik's avatar
Tim Janik committed
103
GtkType
104
gtk_menu_item_get_type (void)
Elliot Lee's avatar
Elliot Lee committed
105
{
Tim Janik's avatar
Tim Janik committed
106
  static GtkType menu_item_type = 0;
Elliot Lee's avatar
Elliot Lee committed
107 108 109

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

123
      menu_item_type = g_type_register_static (GTK_TYPE_ITEM, "GtkMenuItem", &menu_item_info, 0);
Elliot Lee's avatar
Elliot Lee committed
124 125 126 127 128 129 130 131
    }

  return menu_item_type;
}

static void
gtk_menu_item_class_init (GtkMenuItemClass *klass)
{
132 133 134 135 136
  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
137

138
  parent_class = g_type_class_peek_parent (klass);
Elliot Lee's avatar
Elliot Lee committed
139

140
  gobject_class->finalize = gtk_menu_item_finalize;
141 142 143 144 145 146

  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;
147 148 149 150
  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;
151 152
  widget_class->show_all = gtk_menu_item_show_all;
  widget_class->hide_all = gtk_menu_item_hide_all;
153
  widget_class->mnemonic_activate = gtk_menu_item_mnemonic_activate;
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 168
  menu_item_signals[ACTIVATE] =
    gtk_signal_new ("activate",
169
                    GTK_RUN_FIRST | GTK_RUN_ACTION,
170
                    GTK_CLASS_TYPE (object_class),
Elliot Lee's avatar
Elliot Lee committed
171
                    GTK_SIGNAL_OFFSET (GtkMenuItemClass, activate),
172
                    _gtk_marshal_VOID__VOID,
Elliot Lee's avatar
Elliot Lee committed
173
		    GTK_TYPE_NONE, 0);
174
  widget_class->activate_signal = menu_item_signals[ACTIVATE];
Elliot Lee's avatar
Elliot Lee committed
175

176 177 178
  menu_item_signals[ACTIVATE_ITEM] =
    gtk_signal_new ("activate_item",
                    GTK_RUN_FIRST,
179
                    GTK_CLASS_TYPE (object_class),
180 181 182 183
                    GTK_SIGNAL_OFFSET (GtkMenuItemClass, activate_item),
                    gtk_signal_default_marshaller,
		    GTK_TYPE_NONE, 0);

184 185 186 187 188
  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),
189
                    _gtk_marshal_VOID__POINTER,
190
		    GTK_TYPE_NONE, 1,
191
		    GTK_TYPE_POINTER);
192 193 194 195 196 197

  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),
198
                    _gtk_marshal_NONE__INT,
199
		    GTK_TYPE_NONE, 1,
200
		    GTK_TYPE_INT);
Elliot Lee's avatar
Elliot Lee committed
201 202 203 204 205
}

static void
gtk_menu_item_init (GtkMenuItem *menu_item)
{
206 207
  GTK_WIDGET_SET_FLAGS (menu_item, GTK_NO_WINDOW);
  
Elliot Lee's avatar
Elliot Lee committed
208 209
  menu_item->submenu = NULL;
  menu_item->toggle_size = 0;
210
  menu_item->accelerator_width = 0;
Elliot Lee's avatar
Elliot Lee committed
211 212 213 214 215 216 217 218 219
  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*
220
gtk_menu_item_new (void)
Elliot Lee's avatar
Elliot Lee committed
221 222 223 224 225 226 227 228
{
  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
229
  GtkWidget *accel_label;
Elliot Lee's avatar
Elliot Lee committed
230 231

  menu_item = gtk_menu_item_new ();
Tim Janik's avatar
Tim Janik committed
232 233
  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
234

Tim Janik's avatar
Tim Janik committed
235 236 237
  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
238 239 240 241

  return menu_item;
}

242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270

/**
 * 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
271 272 273 274 275 276 277 278 279 280
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)
281
    gtk_widget_destroy (menu_item->submenu);
Elliot Lee's avatar
Elliot Lee committed
282

283 284 285 286 287 288 289 290 291 292 293
  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
294 295
}

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

330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346
/**
 * 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;
}

347 348 349 350 351 352 353 354 355
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));
}

Elliot Lee's avatar
Elliot Lee committed
356
void
Owen Taylor's avatar
Owen Taylor committed
357
_gtk_menu_item_set_placement (GtkMenuItem         *menu_item,
Elliot Lee's avatar
Elliot Lee committed
358 359 360 361 362 363 364 365 366 367
			     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)
{
368 369
  g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
  
Elliot Lee's avatar
Elliot Lee committed
370 371 372 373 374 375
  gtk_item_select (GTK_ITEM (menu_item));
}

void
gtk_menu_item_deselect (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_deselect (GTK_ITEM (menu_item));
}

void
gtk_menu_item_activate (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
  gtk_signal_emit (GTK_OBJECT (menu_item), menu_item_signals[ACTIVATE]);
}

389 390
void
gtk_menu_item_toggle_size_request (GtkMenuItem *menu_item,
391
				   gint        *requisition)
392 393 394 395 396 397 398 399
{
  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,
400
				    gint         allocation)
401 402 403 404 405 406
{
  g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));

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

407 408 409 410 411 412 413 414 415 416
static void
gtk_menu_item_accel_width_foreach (GtkWidget *widget,
				   gpointer data)
{
  guint *width = data;

  if (GTK_IS_ACCEL_LABEL (widget))
    {
      guint w;

417
      w = gtk_accel_label_get_accel_width (GTK_ACCEL_LABEL (widget));
418 419 420 421 422 423 424
      *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
425 426 427 428 429

static void
gtk_menu_item_size_request (GtkWidget      *widget,
			    GtkRequisition *requisition)
{
Tim Janik's avatar
Tim Janik committed
430
  GtkMenuItem *menu_item;
Elliot Lee's avatar
Elliot Lee committed
431
  GtkBin *bin;
432
  guint accel_width;
Elliot Lee's avatar
Elliot Lee committed
433 434 435 436 437

  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
438
  menu_item = GTK_MENU_ITEM (widget);
Elliot Lee's avatar
Elliot Lee committed
439 440

  requisition->width = (GTK_CONTAINER (widget)->border_width +
441
			widget->style->xthickness +
Elliot Lee's avatar
Elliot Lee committed
442 443
			BORDER_SPACING) * 2;
  requisition->height = (GTK_CONTAINER (widget)->border_width +
444
			 widget->style->ythickness) * 2;
Elliot Lee's avatar
Elliot Lee committed
445 446 447

  if (bin->child && GTK_WIDGET_VISIBLE (bin->child))
    {
448 449 450
      GtkRequisition child_requisition;
      
      gtk_widget_size_request (bin->child, &child_requisition);
Elliot Lee's avatar
Elliot Lee committed
451

452 453
      requisition->width += child_requisition.width;
      requisition->height += child_requisition.height;
Elliot Lee's avatar
Elliot Lee committed
454
    }
455

Tim Janik's avatar
Tim Janik committed
456 457
  if (menu_item->submenu && menu_item->show_submenu_indicator)
    requisition->width += 21;
458 459 460 461 462 463

  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
464 465 466 467 468 469
}

static void
gtk_menu_item_size_allocate (GtkWidget     *widget,
			     GtkAllocation *allocation)
{
Tim Janik's avatar
Tim Janik committed
470
  GtkMenuItem *menu_item;
Elliot Lee's avatar
Elliot Lee committed
471 472 473 474 475 476
  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
477
  menu_item = GTK_MENU_ITEM (widget);
Elliot Lee's avatar
Elliot Lee committed
478
  bin = GTK_BIN (widget);
Tim Janik's avatar
Tim Janik committed
479 480
  
  widget->allocation = *allocation;
Elliot Lee's avatar
Elliot Lee committed
481 482 483 484

  if (bin->child)
    {
      child_allocation.x = (GTK_CONTAINER (widget)->border_width +
485
                            widget->style->xthickness +
Elliot Lee's avatar
Elliot Lee committed
486
			    BORDER_SPACING);
Owen Taylor's avatar
Owen Taylor committed
487
      child_allocation.y = (GTK_CONTAINER (widget)->border_width +
488
			    widget->style->ythickness);
489 490
      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
491
      child_allocation.x += GTK_MENU_ITEM (widget)->toggle_size;
Tim Janik's avatar
Tim Janik committed
492
      child_allocation.width -= GTK_MENU_ITEM (widget)->toggle_size;
493 494 495
      child_allocation.x += widget->allocation.x;
      child_allocation.y += widget->allocation.y;
      
Tim Janik's avatar
Tim Janik committed
496 497 498
      if (menu_item->submenu && menu_item->show_submenu_indicator)
	child_allocation.width -= 21;
      
Elliot Lee's avatar
Elliot Lee committed
499 500 501
      gtk_widget_size_allocate (bin->child, &child_allocation);
    }

Tim Janik's avatar
Tim Janik committed
502
  if (GTK_WIDGET_REALIZED (widget))
503
    gdk_window_move_resize (menu_item->event_window,
Tim Janik's avatar
Tim Janik committed
504 505
                            allocation->x, allocation->y,
                            allocation->width, allocation->height);
Elliot Lee's avatar
Elliot Lee committed
506

Tim Janik's avatar
Tim Janik committed
507 508
  if (menu_item->submenu)
    gtk_menu_reposition (GTK_MENU (menu_item->submenu));
Elliot Lee's avatar
Elliot Lee committed
509 510
}

511 512 513 514 515 516 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 571 572 573 574 575 576
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);
  
  gdk_window_show (menu_item->event_window);

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

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
577 578 579 580 581 582 583 584 585
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;
586
  gint border_width = GTK_CONTAINER (widget)->border_width;
Elliot Lee's avatar
Elliot Lee committed
587 588 589 590 591 592 593

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

      state_type = widget->state;

594 595 596 597
      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;
598
      
Elliot Lee's avatar
Elliot Lee committed
599 600
      if ((state_type == GTK_STATE_PRELIGHT) &&
	  (GTK_BIN (menu_item)->child))
601 602 603 604 605 606
	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
607

Tim Janik's avatar
Tim Janik committed
608
      if (menu_item->submenu && menu_item->show_submenu_indicator)
Elliot Lee's avatar
Elliot Lee committed
609 610 611 612 613
	{
	  shadow_type = GTK_SHADOW_OUT;
	  if (state_type == GTK_STATE_PRELIGHT)
	    shadow_type = GTK_SHADOW_IN;

614 615 616 617 618
	  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
619 620 621
	}
      else if (!GTK_BIN (menu_item)->child)
	{
622 623
	   gtk_paint_hline (widget->style, widget->window, GTK_STATE_NORMAL,
			    area, widget, "menuitem",
624 625
			    widget->allocation.x, widget->allocation.width,
			    widget->allocation.y);
Elliot Lee's avatar
Elliot Lee committed
626 627 628 629 630 631 632 633 634 635 636 637 638 639 640
	}
    }
}

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

641
      (* GTK_WIDGET_CLASS (parent_class)->expose_event) (widget, event);
Elliot Lee's avatar
Elliot Lee committed
642 643 644 645 646 647 648 649 650 651 652 653 654 655
    }

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

656 657 658
  /*  if (menu_item->submenu && !GTK_WIDGET_VISIBLE (menu_item->submenu))*/
  if (menu_item->submenu)
    {
659 660 661 662 663 664 665 666 667
      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);
668
      else
669
	gtk_menu_item_popup_submenu (menu_item);
670 671
      if (event)
	gdk_event_free(event);
672
    }
673
  
Elliot Lee's avatar
Elliot Lee committed
674 675 676 677 678 679 680 681 682 683 684 685 686 687 688
  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)
    {
689 690 691
      guint32 etime;
      GdkEvent *event = gtk_get_current_event ();

Elliot Lee's avatar
Elliot Lee committed
692
      if (menu_item->timer)
693 694 695 696
	{
	  gtk_timeout_remove (menu_item->timer);
	  menu_item->timer = 0;
	}
Elliot Lee's avatar
Elliot Lee committed
697 698
      else
	gtk_menu_popdown (GTK_MENU (menu_item->submenu));
699 700 701 702

      etime = event ? gdk_event_get_time (event) : GDK_CURRENT_TIME;
      if (etime > last_submenu_deselect_time)
	last_submenu_deselect_time = etime;
703 704
      if (event)
	gdk_event_free(event);
Elliot Lee's avatar
Elliot Lee committed
705 706 707 708 709 710
    }

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

711
static gboolean
712
gtk_menu_item_mnemonic_activate (GtkWidget *widget,
713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729
				 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;
}


730 731 732 733
static void
gtk_real_menu_item_activate_item (GtkMenuItem *menu_item)
{
  GtkWidget *widget;
734
  GtkMenuShell *submenu; 
735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756

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

757 758 759 760 761
	  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)
762 763 764 765 766 767 768
	    {
	      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);
	    }
769 770 771
	}
    }
}
772 773
static void
gtk_real_menu_item_toggle_size_request (GtkMenuItem *menu_item,
774
					gint        *requisition)
775 776 777 778 779 780 781 782
{
  g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));

  *requisition = 0;
}

static void
gtk_real_menu_item_toggle_size_allocate (GtkMenuItem *menu_item,
783
					 gint         allocation)
784 785 786 787 788
{
  g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));

  menu_item->toggle_size = allocation;
}
789

Elliot Lee's avatar
Elliot Lee committed
790 791 792
static gint
gtk_menu_item_select_timeout (gpointer data)
{
793
  GDK_THREADS_ENTER ();
794

795
  gtk_menu_item_popup_submenu (data);
796 797 798 799 800 801 802

  GDK_THREADS_LEAVE ();

  return FALSE;  
}

static void
803
gtk_menu_item_popup_submenu (gpointer data)
804 805 806
{
  GtkMenuItem *menu_item;

Elliot Lee's avatar
Elliot Lee committed
807 808 809
  menu_item = GTK_MENU_ITEM (data);
  menu_item->timer = 0;

810
  if (GTK_WIDGET_IS_SENSITIVE (menu_item->submenu))
811
    {
812 813 814 815 816 817 818
      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);
819
    }
Elliot Lee's avatar
Elliot Lee committed
820 821 822 823 824 825
}

static void
gtk_menu_item_position_menu (GtkMenu  *menu,
			     gint     *x,
			     gint     *y,
826
			     gboolean *push_in,
Elliot Lee's avatar
Elliot Lee committed
827 828 829
			     gpointer  user_data)
{
  GtkMenuItem *menu_item;
830
  GtkWidget *widget;
Tim Janik's avatar
Tim Janik committed
831
  GtkWidget *parent_menu_item;
Elliot Lee's avatar
Elliot Lee committed
832 833 834 835 836 837 838 839 840 841
  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);
842
  widget = GTK_WIDGET (user_data);
Elliot Lee's avatar
Elliot Lee committed
843 844 845 846 847 848 849

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

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

850
  if (!gdk_window_get_origin (widget->window, &tx, &ty))
Owen Taylor's avatar
Owen Taylor committed
851 852 853 854
    {
      g_warning ("Menu not on screen");
      return;
    }
Elliot Lee's avatar
Elliot Lee committed
855

856 857 858
  tx += widget->allocation.x;
  ty += widget->allocation.y;

Elliot Lee's avatar
Elliot Lee committed
859 860 861
  switch (menu_item->submenu_placement)
    {
    case GTK_TOP_BOTTOM:
862 863
      if ((ty + widget->allocation.height + theight) <= screen_height)
	ty += widget->allocation.height;
Elliot Lee's avatar
Elliot Lee committed
864 865
      else if ((ty - theight) >= 0)
	ty -= theight;
866 867
      else if (screen_height - (ty + widget->allocation.height) > ty)
	ty += widget->allocation.height;
868 869
      else
	ty -= theight;
Elliot Lee's avatar
Elliot Lee committed
870 871 872 873
      break;

    case GTK_LEFT_RIGHT:
      menu_item->submenu_direction = GTK_DIRECTION_RIGHT;
874
      parent_menu_item = GTK_MENU (widget->parent)->parent_menu_item;
Elliot Lee's avatar
Elliot Lee committed
875
      if (parent_menu_item)
Tim Janik's avatar
Tim Janik committed
876
	menu_item->submenu_direction = GTK_MENU_ITEM (parent_menu_item)->submenu_direction;
Elliot Lee's avatar
Elliot Lee committed
877 878 879 880 881 882 883 884 885

      switch (menu_item->submenu_direction)
	{
	case GTK_DIRECTION_LEFT:
	  if ((tx - twidth) >= 0)
	    tx -= twidth;
	  else
	    {
	      menu_item->submenu_direction = GTK_DIRECTION_RIGHT;
886
	      tx += widget->allocation.width - 5;
Elliot Lee's avatar
Elliot Lee committed
887 888 889 890
	    }
	  break;

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

901
      ty += widget->allocation.height / 4;
902

903 904
      /* 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
905 906 907
      break;
    }

908 909
  /* If we have negative, tx, here it is because we can't get
   * the menu all the way on screen. Favor the left portion.
910 911
   */
  *x = CLAMP (tx, 0, MAX (0, screen_width - twidth));
912
  *y = ty;
Elliot Lee's avatar
Elliot Lee committed
913 914
}

915 916 917 918 919 920 921 922 923 924 925 926
/**
 * 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
927
void
928 929
gtk_menu_item_set_right_justified (GtkMenuItem *menu_item,
				   gboolean     right_justified)
Elliot Lee's avatar
Elliot Lee committed
930
{
931 932 933
  g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));

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

935 936 937
  if (right_justified != menu_item->right_justify)
    {
      menu_item->right_justify = right_justified;
938
      gtk_widget_queue_resize (GTK_WIDGET (menu_item));
939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957
    }
}

/**
 * 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
958
}
959

960

961 962 963
static void
gtk_menu_item_show_all (GtkWidget *widget)
{
964
  GtkMenuItem *menu_item;
965 966

  g_return_if_fail (GTK_IS_MENU_ITEM (widget));
967

968 969
  menu_item = GTK_MENU_ITEM (widget);

970
  /* show children including submenu */
971 972
  if (menu_item->submenu)
    gtk_widget_show_all (menu_item->submenu);
973 974
  gtk_container_foreach (GTK_CONTAINER (widget), (GtkCallback) gtk_widget_show_all, NULL);

975 976 977 978 979 980
  gtk_widget_show (widget);
}

static void
gtk_menu_item_hide_all (GtkWidget *widget)
{
981
  GtkMenuItem *menu_item;
982 983 984 985

  g_return_if_fail (GTK_IS_MENU_ITEM (widget));

  gtk_widget_hide (widget);
986 987 988 989 990

  menu_item = GTK_MENU_ITEM (widget);

  /* hide children including submenu */
  gtk_container_foreach (GTK_CONTAINER (widget), (GtkCallback) gtk_widget_hide_all, NULL);
991 992
  if (menu_item->submenu)
    gtk_widget_hide_all (menu_item->submenu);
993 994
}

995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095
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);
    }
}

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));
  g_return_if_fail (GTK_IS_ACCEL_GROUP (accel_group));

  widget = GTK_WIDGET (menu_item);

  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)
	_gtk_widget_set_accel_path (widget, path, accel_group);
    }
  else if (group_changed)			/* reinstall accelerators */
    _gtk_widget_set_accel_path (widget, path, accel_group);
}

/**
 * gtk_menu_item_set_accel_path
 * @menu_item:  a valid #GtkMenuItem
 * @accel_path: accelerator path, corresponding to this menu item's funcitonality
 *
 * 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.
 */
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 */
  _gtk_widget_set_accel_path (widget, NULL, NULL);

  /* 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);
    }
}

1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111
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)
1112
    callback (bin->child, callback_data);
1113
}
1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126

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