1 /* GTK - The GIMP Toolkit
2 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
21 * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
22 * file for a list of people on the GTK+ Team. See the ChangeLog
23 * files for a list of changes. These files are distributed with
24 * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
27 #include <string.h> /* memset */
28 #include "gdk/gdkkeysyms.h"
29 #include "gtkaccelmap.h"
30 #include "gtkbindings.h"
34 #include "gtkmenuitem.h"
35 #include "gtksignal.h"
36 #include "gtkwindow.h"
38 #include "gtkvscrollbar.h"
39 #include "gtksettings.h"
43 #define MENU_ITEM_CLASS(w) GTK_MENU_ITEM_GET_CLASS (w)
44 #define MENU_NEEDS_RESIZE(m) GTK_MENU_SHELL (m)->menu_flag
46 #define SUBMENU_NAV_REGION_PADDING 2
47 #define SUBMENU_NAV_HYSTERESIS_TIMEOUT 333
49 #define MENU_SCROLL_STEP 10
50 #define MENU_SCROLL_ARROW_HEIGHT 16
51 #define MENU_SCROLL_FAST_ZONE 4
52 #define MENU_SCROLL_TIMEOUT1 150
53 #define MENU_SCROLL_TIMEOUT2 50
55 typedef struct _GtkMenuAttachData GtkMenuAttachData;
56 typedef struct _GtkMenuPrivate GtkMenuPrivate;
58 struct _GtkMenuAttachData
60 GtkWidget *attach_widget;
61 GtkMenuDetachFunc detacher;
64 struct _GtkMenuPrivate
66 gboolean have_position;
76 static void gtk_menu_class_init (GtkMenuClass *klass);
77 static void gtk_menu_init (GtkMenu *menu);
78 static void gtk_menu_set_property (GObject *object,
82 static void gtk_menu_get_property (GObject *object,
86 static void gtk_menu_destroy (GtkObject *object);
87 static void gtk_menu_finalize (GObject *object);
88 static void gtk_menu_realize (GtkWidget *widget);
89 static void gtk_menu_unrealize (GtkWidget *widget);
90 static void gtk_menu_size_request (GtkWidget *widget,
91 GtkRequisition *requisition);
92 static void gtk_menu_size_allocate (GtkWidget *widget,
93 GtkAllocation *allocation);
94 static void gtk_menu_paint (GtkWidget *widget,
95 GdkEventExpose *expose);
96 static void gtk_menu_show (GtkWidget *widget);
97 static gboolean gtk_menu_expose (GtkWidget *widget,
98 GdkEventExpose *event);
99 static gboolean gtk_menu_key_press (GtkWidget *widget,
101 static gboolean gtk_menu_motion_notify (GtkWidget *widget,
102 GdkEventMotion *event);
103 static gboolean gtk_menu_enter_notify (GtkWidget *widget,
104 GdkEventCrossing *event);
105 static gboolean gtk_menu_leave_notify (GtkWidget *widget,
106 GdkEventCrossing *event);
107 static void gtk_menu_scroll_to (GtkMenu *menu,
109 static void gtk_menu_stop_scrolling (GtkMenu *menu);
110 static gboolean gtk_menu_scroll_timeout (gpointer data);
111 static void gtk_menu_scroll_item_visible (GtkMenuShell *menu_shell,
112 GtkWidget *menu_item);
113 static void gtk_menu_select_item (GtkMenuShell *menu_shell,
114 GtkWidget *menu_item);
115 static void gtk_menu_real_insert (GtkMenuShell *menu_shell,
118 static void gtk_menu_scrollbar_changed (GtkAdjustment *adjustment,
120 static void gtk_menu_handle_scrolling (GtkMenu *menu,
122 static void gtk_menu_set_tearoff_hints (GtkMenu *menu,
124 static void gtk_menu_style_set (GtkWidget *widget,
125 GtkStyle *previous_style);
126 static gboolean gtk_menu_focus (GtkWidget *widget,
127 GtkDirectionType direction);
130 static void gtk_menu_stop_navigating_submenu (GtkMenu *menu);
131 static gboolean gtk_menu_stop_navigating_submenu_cb (gpointer user_data);
132 static gboolean gtk_menu_navigating_submenu (GtkMenu *menu,
135 static void gtk_menu_set_submenu_navigation_region (GtkMenu *menu,
136 GtkMenuItem *menu_item,
137 GdkEventCrossing *event);
139 static void gtk_menu_deactivate (GtkMenuShell *menu_shell);
140 static void gtk_menu_show_all (GtkWidget *widget);
141 static void gtk_menu_hide_all (GtkWidget *widget);
142 static void gtk_menu_position (GtkMenu *menu);
143 static void gtk_menu_reparent (GtkMenu *menu,
144 GtkWidget *new_parent,
146 static void gtk_menu_remove (GtkContainer *menu,
149 static void gtk_menu_update_title (GtkMenu *menu);
151 static void menu_grab_transfer_window_destroy (GtkMenu *menu);
152 static GdkWindow *menu_grab_transfer_window_get (GtkMenu *menu);
154 static void _gtk_menu_refresh_accel_paths (GtkMenu *menu,
155 gboolean group_changed);
157 static GtkMenuShellClass *parent_class = NULL;
158 static const gchar *attach_data_key = "gtk-menu-attach-data";
161 gtk_menu_get_private (GtkMenu *menu)
163 GtkMenuPrivate *private;
164 static GQuark private_quark = 0;
167 private_quark = g_quark_from_static_string ("gtk-menu-private");
169 private = g_object_get_qdata (G_OBJECT (menu), private_quark);
173 private = g_new0 (GtkMenuPrivate, 1);
174 private->have_position = FALSE;
176 g_object_set_qdata_full (G_OBJECT (menu), private_quark,
177 private, (GDestroyNotify) g_free);
184 gtk_menu_get_type (void)
186 static GtkType menu_type = 0;
190 static const GtkTypeInfo menu_info =
194 sizeof (GtkMenuClass),
195 (GtkClassInitFunc) gtk_menu_class_init,
196 (GtkObjectInitFunc) gtk_menu_init,
197 /* reserved_1 */ NULL,
198 /* reserved_2 */ NULL,
199 (GtkClassInitFunc) NULL,
202 menu_type = gtk_type_unique (gtk_menu_shell_get_type (), &menu_info);
209 gtk_menu_class_init (GtkMenuClass *class)
211 GObjectClass *gobject_class = G_OBJECT_CLASS (class);
212 GtkObjectClass *object_class = GTK_OBJECT_CLASS (class);
213 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
214 GtkContainerClass *container_class = GTK_CONTAINER_CLASS (class);
215 GtkMenuShellClass *menu_shell_class = GTK_MENU_SHELL_CLASS (class);
216 GtkBindingSet *binding_set;
218 parent_class = g_type_class_peek_parent (class);
220 gobject_class->finalize = gtk_menu_finalize;
221 gobject_class->set_property = gtk_menu_set_property;
222 gobject_class->get_property = gtk_menu_get_property;
224 g_object_class_install_property (gobject_class,
226 g_param_spec_string ("tearoff-title",
228 _("A title that may be displayed by the window manager when this menu is torn-off"),
230 G_PARAM_READABLE | G_PARAM_WRITABLE));
231 object_class->destroy = gtk_menu_destroy;
233 widget_class->realize = gtk_menu_realize;
234 widget_class->unrealize = gtk_menu_unrealize;
235 widget_class->size_request = gtk_menu_size_request;
236 widget_class->size_allocate = gtk_menu_size_allocate;
237 widget_class->show = gtk_menu_show;
238 widget_class->expose_event = gtk_menu_expose;
239 widget_class->key_press_event = gtk_menu_key_press;
240 widget_class->motion_notify_event = gtk_menu_motion_notify;
241 widget_class->show_all = gtk_menu_show_all;
242 widget_class->hide_all = gtk_menu_hide_all;
243 widget_class->enter_notify_event = gtk_menu_enter_notify;
244 widget_class->leave_notify_event = gtk_menu_leave_notify;
245 widget_class->style_set = gtk_menu_style_set;
246 widget_class->focus = gtk_menu_focus;
248 container_class->remove = gtk_menu_remove;
250 menu_shell_class->submenu_placement = GTK_LEFT_RIGHT;
251 menu_shell_class->deactivate = gtk_menu_deactivate;
252 menu_shell_class->select_item = gtk_menu_select_item;
253 menu_shell_class->insert = gtk_menu_real_insert;
255 binding_set = gtk_binding_set_by_class (class);
256 gtk_binding_entry_add_signal (binding_set,
259 GTK_TYPE_MENU_DIRECTION_TYPE,
261 gtk_binding_entry_add_signal (binding_set,
264 GTK_TYPE_MENU_DIRECTION_TYPE,
266 gtk_binding_entry_add_signal (binding_set,
269 GTK_TYPE_MENU_DIRECTION_TYPE,
271 gtk_binding_entry_add_signal (binding_set,
274 GTK_TYPE_MENU_DIRECTION_TYPE,
276 gtk_binding_entry_add_signal (binding_set,
279 GTK_TYPE_MENU_DIRECTION_TYPE,
280 GTK_MENU_DIR_PARENT);
281 gtk_binding_entry_add_signal (binding_set,
284 GTK_TYPE_MENU_DIRECTION_TYPE,
285 GTK_MENU_DIR_PARENT);
286 gtk_binding_entry_add_signal (binding_set,
289 GTK_TYPE_MENU_DIRECTION_TYPE,
291 gtk_binding_entry_add_signal (binding_set,
294 GTK_TYPE_MENU_DIRECTION_TYPE,
297 gtk_settings_install_property (g_param_spec_boolean ("gtk-can-change-accels",
298 _("Can change accelerators"),
299 _("Whether menu accelerators can be changed by pressing a key over the menu item"),
306 gtk_menu_set_property (GObject *object,
313 menu = GTK_MENU (object);
317 case PROP_TEAROFF_TITLE:
318 gtk_menu_set_title (menu, g_value_get_string (value));
321 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
327 gtk_menu_get_property (GObject *object,
334 menu = GTK_MENU (object);
338 case PROP_TEAROFF_TITLE:
339 g_value_set_string (value, gtk_menu_get_title (menu));
342 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
348 gtk_menu_window_event (GtkWidget *window,
352 gboolean handled = FALSE;
354 gtk_widget_ref (window);
355 gtk_widget_ref (menu);
360 case GDK_KEY_RELEASE:
361 handled = gtk_widget_event (menu, event);
367 gtk_widget_unref (window);
368 gtk_widget_unref (menu);
374 gtk_menu_window_size_request (GtkWidget *window,
375 GtkRequisition *requisition,
378 GtkMenuPrivate *private = gtk_menu_get_private (menu);
380 if (private->have_position)
382 gint screen_height = gdk_screen_height ();
384 if (private->y + requisition->height > screen_height)
385 requisition->height = screen_height - private->y;
390 gtk_menu_init (GtkMenu *menu)
392 menu->parent_menu_item = NULL;
393 menu->old_active_menu_item = NULL;
394 menu->accel_group = NULL;
395 menu->position_func = NULL;
396 menu->position_func_data = NULL;
397 menu->toggle_size = 0;
399 menu->toplevel = g_object_connect (gtk_widget_new (GTK_TYPE_WINDOW,
400 "type", GTK_WINDOW_POPUP,
403 "signal::event", gtk_menu_window_event, menu,
404 "signal::size_request", gtk_menu_window_size_request, menu,
405 "signal::destroy", gtk_widget_destroyed, &menu->toplevel,
407 gtk_window_set_policy (GTK_WINDOW (menu->toplevel),
409 gtk_window_set_mnemonic_modifier (GTK_WINDOW (menu->toplevel), 0);
411 /* Refloat the menu, so that reference counting for the menu isn't
412 * affected by it being a child of the toplevel
414 GTK_WIDGET_SET_FLAGS (menu, GTK_FLOATING);
415 menu->needs_destruction_ref_count = TRUE;
417 menu->view_window = NULL;
418 menu->bin_window = NULL;
420 menu->scroll_offset = 0;
421 menu->scroll_step = 0;
422 menu->timeout_id = 0;
423 menu->scroll_fast = FALSE;
425 menu->tearoff_window = NULL;
426 menu->tearoff_hbox = NULL;
427 menu->torn_off = FALSE;
428 menu->tearoff_active = FALSE;
429 menu->tearoff_adjustment = NULL;
430 menu->tearoff_scrollbar = NULL;
432 menu->upper_arrow_visible = FALSE;
433 menu->lower_arrow_visible = FALSE;
434 menu->upper_arrow_prelight = FALSE;
435 menu->lower_arrow_prelight = FALSE;
437 MENU_NEEDS_RESIZE (menu) = TRUE;
441 gtk_menu_destroy (GtkObject *object)
444 GtkMenuAttachData *data;
446 g_return_if_fail (GTK_IS_MENU (object));
448 menu = GTK_MENU (object);
450 gtk_menu_stop_scrolling (menu);
452 data = gtk_object_get_data (object, attach_data_key);
454 gtk_menu_detach (menu);
456 gtk_menu_stop_navigating_submenu (menu);
458 if (menu->old_active_menu_item)
460 gtk_widget_unref (menu->old_active_menu_item);
461 menu->old_active_menu_item = NULL;
464 /* Add back the reference count for being a child */
465 if (menu->needs_destruction_ref_count)
467 menu->needs_destruction_ref_count = FALSE;
468 gtk_object_ref (object);
471 if (menu->accel_group)
473 g_object_unref (menu->accel_group);
474 menu->accel_group = NULL;
478 gtk_widget_destroy (menu->toplevel);
479 if (menu->tearoff_window)
480 gtk_widget_destroy (menu->tearoff_window);
482 GTK_OBJECT_CLASS (parent_class)->destroy (object);
486 gtk_menu_finalize (GObject *object)
488 GtkMenu *menu = GTK_MENU (object);
490 g_free (menu->accel_path);
492 G_OBJECT_CLASS (parent_class)->finalize (object);
496 gtk_menu_attach_to_widget (GtkMenu *menu,
497 GtkWidget *attach_widget,
498 GtkMenuDetachFunc detacher)
500 GtkMenuAttachData *data;
502 g_return_if_fail (GTK_IS_MENU (menu));
503 g_return_if_fail (GTK_IS_WIDGET (attach_widget));
504 g_return_if_fail (detacher != NULL);
506 /* keep this function in sync with gtk_widget_set_parent()
509 data = gtk_object_get_data (GTK_OBJECT (menu), attach_data_key);
512 g_warning ("gtk_menu_attach_to_widget(): menu already attached to %s",
513 gtk_type_name (GTK_OBJECT_TYPE (data->attach_widget)));
517 gtk_object_ref (GTK_OBJECT (menu));
518 gtk_object_sink (GTK_OBJECT (menu));
520 data = g_new (GtkMenuAttachData, 1);
521 data->attach_widget = attach_widget;
522 data->detacher = detacher;
523 gtk_object_set_data (GTK_OBJECT (menu), attach_data_key, data);
525 if (GTK_WIDGET_STATE (menu) != GTK_STATE_NORMAL)
526 gtk_widget_set_state (GTK_WIDGET (menu), GTK_STATE_NORMAL);
528 /* we don't need to set the style here, since
529 * we are a toplevel widget.
532 /* Fallback title for menu comes from attach widget */
533 gtk_menu_update_title (menu);
537 gtk_menu_get_attach_widget (GtkMenu *menu)
539 GtkMenuAttachData *data;
541 g_return_val_if_fail (GTK_IS_MENU (menu), NULL);
543 data = gtk_object_get_data (GTK_OBJECT (menu), attach_data_key);
545 return data->attach_widget;
550 gtk_menu_detach (GtkMenu *menu)
552 GtkMenuAttachData *data;
554 g_return_if_fail (GTK_IS_MENU (menu));
556 /* keep this function in sync with gtk_widget_unparent()
558 data = gtk_object_get_data (GTK_OBJECT (menu), attach_data_key);
561 g_warning ("gtk_menu_detach(): menu is not attached");
564 gtk_object_remove_data (GTK_OBJECT (menu), attach_data_key);
566 data->detacher (data->attach_widget, menu);
568 if (GTK_WIDGET_REALIZED (menu))
569 gtk_widget_unrealize (GTK_WIDGET (menu));
573 /* Fallback title for menu comes from attach widget */
574 gtk_menu_update_title (menu);
576 gtk_widget_unref (GTK_WIDGET (menu));
580 gtk_menu_remove (GtkContainer *container,
584 g_return_if_fail (GTK_IS_MENU (container));
585 g_return_if_fail (GTK_IS_MENU_ITEM (widget));
587 menu = GTK_MENU (container);
589 /* Clear out old_active_menu_item if it matches the item we are removing
591 if (menu->old_active_menu_item == widget)
593 gtk_widget_unref (menu->old_active_menu_item);
594 menu->old_active_menu_item = NULL;
597 GTK_CONTAINER_CLASS (parent_class)->remove (container, widget);
604 return GTK_WIDGET (gtk_type_new (gtk_menu_get_type ()));
608 gtk_menu_real_insert (GtkMenuShell *menu_shell,
612 if (GTK_WIDGET_REALIZED (menu_shell))
613 gtk_widget_set_parent_window (child, GTK_MENU (menu_shell)->bin_window);
615 GTK_MENU_SHELL_CLASS (parent_class)->insert (menu_shell, child, position);
619 gtk_menu_tearoff_bg_copy (GtkMenu *menu)
624 widget = GTK_WIDGET (menu);
630 GdkGCValues gc_values;
632 menu->tearoff_active = FALSE;
633 menu->saved_scroll_offset = menu->scroll_offset;
635 gc_values.subwindow_mode = GDK_INCLUDE_INFERIORS;
636 gc = gdk_gc_new_with_values (widget->window,
637 &gc_values, GDK_GC_SUBWINDOW);
639 gdk_window_get_size (menu->tearoff_window->window, &width, &height);
641 pixmap = gdk_pixmap_new (menu->tearoff_window->window,
646 gdk_draw_pixmap (pixmap, gc,
647 menu->tearoff_window->window,
651 gtk_widget_set_usize (menu->tearoff_window,
655 gdk_window_set_back_pixmap (menu->tearoff_window->window, pixmap, FALSE);
656 gdk_pixmap_unref (pixmap);
661 popup_grab_on_window (GdkWindow *window,
662 guint32 activate_time)
664 if ((gdk_pointer_grab (window, TRUE,
665 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
666 GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
667 GDK_POINTER_MOTION_MASK,
668 NULL, NULL, activate_time) == 0))
670 if (gdk_keyboard_grab (window, TRUE,
675 gdk_display_pointer_ungrab (gdk_drawable_get_display (window),
685 gtk_menu_popup (GtkMenu *menu,
686 GtkWidget *parent_menu_shell,
687 GtkWidget *parent_menu_item,
688 GtkMenuPositionFunc func,
691 guint32 activate_time)
694 GtkWidget *xgrab_shell;
696 GdkEvent *current_event;
697 GtkMenuShell *menu_shell;
698 GtkMenuAttachData *attach_data;
700 g_return_if_fail (GTK_IS_MENU (menu));
702 widget = GTK_WIDGET (menu);
703 menu_shell = GTK_MENU_SHELL (menu);
705 menu_shell->parent_menu_shell = parent_menu_shell;
707 if (!g_object_get_data (G_OBJECT (menu), "gtk-menu-explicit-screen"))
709 /* The screen was not set explicitly, if the menu is
710 * attached to a widget, try to get screen from its
711 * toplevel window else go with the default
713 attach_data = gtk_object_get_data (GTK_OBJECT (menu), attach_data_key);
716 if (!GTK_WIDGET_REALIZED (menu))
717 gtk_window_set_screen (GTK_WINDOW (menu->toplevel),
718 gtk_widget_get_screen (attach_data->attach_widget));
722 /* Find the last viewable ancestor, and make an X grab on it
724 parent = GTK_WIDGET (menu);
728 gboolean viewable = TRUE;
729 GtkWidget *tmp = parent;
733 if (!GTK_WIDGET_MAPPED (tmp))
742 xgrab_shell = parent;
744 parent = GTK_MENU_SHELL (parent)->parent_menu_shell;
747 /* We want to receive events generated when we map the menu; unfortunately,
748 * since there is probably already an implicit grab in place from the
749 * button that the user used to pop up the menu, we won't receive then --
750 * in particular, the EnterNotify when the menu pops up under the pointer.
752 * If we are grabbing on a parent menu shell, no problem; just grab on
753 * that menu shell first before popping up the window with owner_events = TRUE.
755 * When grabbing on the menu itself, things get more convuluted - we
756 * we do an explicit grab on a specially created window with
757 * owner_events = TRUE, which we override further down with a grab
758 * on the menu. (We can't grab on the menu until it is mapped; we
759 * probably could just leave the grab on the other window, with a
760 * little reorganization of the code in gtkmenu*).
762 if (xgrab_shell && xgrab_shell != widget)
764 if (popup_grab_on_window (xgrab_shell->window, activate_time))
765 GTK_MENU_SHELL (xgrab_shell)->have_xgrab = TRUE;
769 GdkWindow *transfer_window;
771 xgrab_shell = widget;
772 transfer_window = menu_grab_transfer_window_get (menu);
773 if (popup_grab_on_window (transfer_window, activate_time))
774 GTK_MENU_SHELL (xgrab_shell)->have_xgrab = TRUE;
777 if (!GTK_MENU_SHELL (xgrab_shell)->have_xgrab)
779 /* We failed to make our pointer/keyboard grab. Rather than leaving the user
780 * with a stuck up window, we just abort here. Presumably the user will
783 menu_shell->parent_menu_shell = NULL;
784 menu_grab_transfer_window_destroy (menu);
788 menu_shell->active = TRUE;
789 menu_shell->button = button;
791 /* If we are popping up the menu from something other than, a button
792 * press then, as a heuristic, we ignore enter events for the menu
793 * until we get a MOTION_NOTIFY.
796 current_event = gtk_get_current_event ();
799 if ((current_event->type != GDK_BUTTON_PRESS) &&
800 (current_event->type != GDK_ENTER_NOTIFY))
801 menu_shell->ignore_enter = TRUE;
803 gdk_event_free (current_event);
808 gtk_menu_tearoff_bg_copy (menu);
810 gtk_menu_reparent (menu, menu->toplevel, FALSE);
813 menu->parent_menu_item = parent_menu_item;
814 menu->position_func = func;
815 menu->position_func_data = data;
816 menu_shell->activate_time = activate_time;
818 /* We need to show the menu here rather in the init function because
819 * code expects to be able to tell if the menu is onscreen by
820 * looking at the GTK_WIDGET_VISIBLE (menu)
822 gtk_widget_show (GTK_WIDGET (menu));
824 /* Position the menu, possibly changing the size request
826 gtk_menu_position (menu);
828 /* Compute the size of the toplevel and realize it so we
829 * can scroll correctly.
832 GtkRequisition tmp_request;
833 GtkAllocation tmp_allocation = { 0, };
835 gtk_widget_size_request (menu->toplevel, &tmp_request);
837 tmp_allocation.width = tmp_request.width;
838 tmp_allocation.height = tmp_request.height;
840 gtk_widget_size_allocate (menu->toplevel, &tmp_allocation);
842 gtk_widget_realize (GTK_WIDGET (menu));
845 gtk_menu_scroll_to (menu, menu->scroll_offset);
847 /* Once everything is set up correctly, map the toplevel window on
850 gtk_widget_show (menu->toplevel);
852 if (xgrab_shell == widget)
853 popup_grab_on_window (widget->window, activate_time); /* Should always succeed */
855 gtk_grab_add (GTK_WIDGET (menu));
859 gtk_menu_popdown (GtkMenu *menu)
861 GtkMenuPrivate *private;
862 GtkMenuShell *menu_shell;
864 g_return_if_fail (GTK_IS_MENU (menu));
866 menu_shell = GTK_MENU_SHELL (menu);
867 private = gtk_menu_get_private (menu);
869 menu_shell->parent_menu_shell = NULL;
870 menu_shell->active = FALSE;
871 menu_shell->ignore_enter = FALSE;
873 private->have_position = FALSE;
875 gtk_menu_stop_scrolling (menu);
877 gtk_menu_stop_navigating_submenu (menu);
879 if (menu_shell->active_menu_item)
881 if (menu->old_active_menu_item)
882 gtk_widget_unref (menu->old_active_menu_item);
883 menu->old_active_menu_item = menu_shell->active_menu_item;
884 gtk_widget_ref (menu->old_active_menu_item);
887 gtk_menu_shell_deselect (menu_shell);
889 /* The X Grab, if present, will automatically be removed when we hide
891 gtk_widget_hide (menu->toplevel);
895 gtk_widget_set_usize (menu->tearoff_window, -1, -1);
897 if (GTK_BIN (menu->toplevel)->child)
899 gtk_menu_reparent (menu, menu->tearoff_hbox, TRUE);
903 /* We popped up the menu from the tearoff, so we need to
904 * release the grab - we aren't actually hiding the menu.
906 if (menu_shell->have_xgrab)
908 GdkDisplay *display = gtk_widget_get_display (GTK_WIDGET (menu));
910 gdk_display_pointer_ungrab (display, GDK_CURRENT_TIME);
911 gdk_display_keyboard_ungrab (display, GDK_CURRENT_TIME);
915 /* gtk_menu_popdown is called each time a menu item is selected from
916 * a torn off menu. Only scroll back to the saved position if the
917 * non-tearoff menu was popped down.
919 if (!menu->tearoff_active)
920 gtk_menu_scroll_to (menu, menu->saved_scroll_offset);
921 menu->tearoff_active = TRUE;
924 gtk_widget_hide (GTK_WIDGET (menu));
926 menu_shell->have_xgrab = FALSE;
927 gtk_grab_remove (GTK_WIDGET (menu));
929 menu_grab_transfer_window_destroy (menu);
933 gtk_menu_get_active (GtkMenu *menu)
938 g_return_val_if_fail (GTK_IS_MENU (menu), NULL);
940 if (!menu->old_active_menu_item)
943 children = GTK_MENU_SHELL (menu)->children;
947 child = children->data;
948 children = children->next;
950 if (GTK_BIN (child)->child)
955 menu->old_active_menu_item = child;
956 if (menu->old_active_menu_item)
957 gtk_widget_ref (menu->old_active_menu_item);
960 return menu->old_active_menu_item;
964 gtk_menu_set_active (GtkMenu *menu,
970 g_return_if_fail (GTK_IS_MENU (menu));
972 tmp_list = g_list_nth (GTK_MENU_SHELL (menu)->children, index);
975 child = tmp_list->data;
976 if (GTK_BIN (child)->child)
978 if (menu->old_active_menu_item)
979 gtk_widget_unref (menu->old_active_menu_item);
980 menu->old_active_menu_item = child;
981 gtk_widget_ref (menu->old_active_menu_item);
987 gtk_menu_set_accel_group (GtkMenu *menu,
988 GtkAccelGroup *accel_group)
990 g_return_if_fail (GTK_IS_MENU (menu));
992 if (menu->accel_group != accel_group)
994 if (menu->accel_group)
995 gtk_accel_group_unref (menu->accel_group);
996 menu->accel_group = accel_group;
997 if (menu->accel_group)
998 gtk_accel_group_ref (menu->accel_group);
999 _gtk_menu_refresh_accel_paths (menu, TRUE);
1004 gtk_menu_get_accel_group (GtkMenu *menu)
1006 g_return_val_if_fail (GTK_IS_MENU (menu), NULL);
1008 return menu->accel_group;
1012 * gtk_menu_set_accel_path
1013 * @menu: a valid #GtkMenu
1014 * @accel_path: a valid accelerator path
1016 * Sets an accelerator path for this menu from which accelerator paths
1017 * for its immediate children, its menu items, can be constructed.
1018 * The main purpose of this function is to spare the programmer the
1019 * inconvenience of having to call gtk_menu_item_set_accel_path() on
1020 * each menu item that should support runtime user changable accelerators.
1021 * Instead, by just calling gtk_menu_set_accel_path() on their parent,
1022 * each menu item of this menu, that contains a label describing its purpose,
1023 * automatically gets an accel path assigned. For example, a menu containing
1024 * menu items "New" and "Exit", will, after
1025 * <literal>gtk_menu_set_accel_path (menu, "<Gnumeric-Sheet>/File");</literal>
1026 * has been called, assign its items the accel paths:
1027 * <literal>"<Gnumeric-Sheet>/File/New"</literal> and <literal>"<Gnumeric-Sheet>/File/Exit"</literal>.
1028 * Assigning accel paths to menu items then enables the user to change
1029 * their accelerators at runtime. More details about accelerator paths
1030 * and their default setups can be found at gtk_accel_map_add_entry().
1033 gtk_menu_set_accel_path (GtkMenu *menu,
1034 const gchar *accel_path)
1036 g_return_if_fail (GTK_IS_MENU (menu));
1038 g_return_if_fail (accel_path[0] == '<' && strchr (accel_path, '/')); /* simplistic check */
1040 g_free (menu->accel_path);
1041 menu->accel_path = g_strdup (accel_path);
1042 if (menu->accel_path)
1043 _gtk_menu_refresh_accel_paths (menu, FALSE);
1048 gboolean group_changed;
1052 refresh_accel_paths_foreach (GtkWidget *widget,
1055 AccelPropagation *prop = data;
1057 if (GTK_IS_MENU_ITEM (widget)) /* should always be true */
1058 _gtk_menu_item_refresh_accel_path (GTK_MENU_ITEM (widget),
1059 prop->menu->accel_path,
1060 prop->menu->accel_group,
1061 prop->group_changed);
1065 _gtk_menu_refresh_accel_paths (GtkMenu *menu,
1066 gboolean group_changed)
1068 g_return_if_fail (GTK_IS_MENU (menu));
1070 if (menu->accel_path && menu->accel_group)
1072 AccelPropagation prop;
1075 prop.group_changed = group_changed;
1076 gtk_container_foreach (GTK_CONTAINER (menu),
1077 refresh_accel_paths_foreach,
1083 gtk_menu_reposition (GtkMenu *menu)
1085 g_return_if_fail (GTK_IS_MENU (menu));
1087 if (GTK_WIDGET_DRAWABLE (menu) && !menu->torn_off)
1088 gtk_menu_position (menu);
1092 gtk_menu_scrollbar_changed (GtkAdjustment *adjustment,
1095 g_return_if_fail (GTK_IS_MENU (menu));
1097 if (adjustment->value != menu->scroll_offset)
1098 gtk_menu_scroll_to (menu, adjustment->value);
1102 gtk_menu_set_tearoff_hints (GtkMenu *menu,
1105 GdkGeometry geometry_hints;
1107 if (!menu->tearoff_window)
1110 if (GTK_WIDGET_VISIBLE (menu->tearoff_scrollbar))
1112 gtk_widget_size_request (menu->tearoff_scrollbar, NULL);
1113 width += menu->tearoff_scrollbar->requisition.width;
1116 geometry_hints.min_width = width;
1117 geometry_hints.max_width = width;
1119 geometry_hints.min_height = 0;
1120 geometry_hints.max_height = GTK_WIDGET (menu)->requisition.height;
1122 gtk_window_set_geometry_hints (GTK_WINDOW (menu->tearoff_window),
1125 GDK_HINT_MAX_SIZE|GDK_HINT_MIN_SIZE);
1129 gtk_menu_update_title (GtkMenu *menu)
1131 if (menu->tearoff_window)
1134 GtkWidget *attach_widget;
1136 title = gtk_menu_get_title (menu);
1139 attach_widget = gtk_menu_get_attach_widget (menu);
1140 if (GTK_IS_MENU_ITEM (attach_widget))
1142 GtkWidget *child = GTK_BIN (attach_widget)->child;
1143 if (GTK_IS_LABEL (child))
1144 title = gtk_label_get_text (GTK_LABEL (child));
1149 gtk_window_set_title (GTK_WINDOW (menu->tearoff_window), title);
1154 gtk_menu_set_tearoff_state (GtkMenu *menu,
1159 g_return_if_fail (GTK_IS_MENU (menu));
1161 if (menu->torn_off != torn_off)
1163 menu->torn_off = torn_off;
1164 menu->tearoff_active = torn_off;
1168 if (GTK_WIDGET_VISIBLE (menu))
1169 gtk_menu_popdown (menu);
1171 if (!menu->tearoff_window)
1173 menu->tearoff_window = gtk_widget_new (GTK_TYPE_WINDOW,
1174 "type", GTK_WINDOW_TOPLEVEL,
1175 "screen", gtk_widget_get_screen (menu->toplevel),
1176 "app_paintable", TRUE,
1179 gtk_window_set_type_hint (GTK_WINDOW (menu->tearoff_window),
1180 GDK_WINDOW_TYPE_HINT_MENU);
1181 gtk_window_set_mnemonic_modifier (GTK_WINDOW (menu->tearoff_window), 0);
1182 g_signal_connect (menu->tearoff_window, "destroy",
1183 G_CALLBACK (gtk_widget_destroyed), &menu->tearoff_window);
1184 g_signal_connect (menu->tearoff_window, "event",
1185 G_CALLBACK (gtk_menu_window_event), menu);
1187 gtk_menu_update_title (menu);
1189 gtk_widget_realize (menu->tearoff_window);
1191 menu->tearoff_hbox = gtk_hbox_new (FALSE, FALSE);
1192 gtk_container_add (GTK_CONTAINER (menu->tearoff_window), menu->tearoff_hbox);
1194 gdk_window_get_size (GTK_WIDGET (menu)->window, &width, &height);
1195 menu->tearoff_adjustment =
1196 GTK_ADJUSTMENT (gtk_adjustment_new (0,
1198 GTK_WIDGET (menu)->requisition.height,
1202 g_object_connect (GTK_OBJECT (menu->tearoff_adjustment),
1203 "signal::value_changed", gtk_menu_scrollbar_changed, menu,
1205 menu->tearoff_scrollbar = gtk_vscrollbar_new (menu->tearoff_adjustment);
1207 gtk_box_pack_end (GTK_BOX (menu->tearoff_hbox),
1208 menu->tearoff_scrollbar,
1211 if (menu->tearoff_adjustment->upper > height)
1212 gtk_widget_show (menu->tearoff_scrollbar);
1214 gtk_widget_show (menu->tearoff_hbox);
1217 gtk_menu_reparent (menu, menu->tearoff_hbox, FALSE);
1219 gdk_window_get_size (GTK_WIDGET (menu)->window, &width, NULL);
1221 /* Update menu->requisition
1223 gtk_widget_size_request (GTK_WIDGET (menu), NULL);
1225 gtk_menu_set_tearoff_hints (menu, width);
1227 gtk_widget_realize (menu->tearoff_window);
1228 gtk_menu_position (menu);
1230 gtk_widget_show (GTK_WIDGET (menu));
1231 gtk_widget_show (menu->tearoff_window);
1233 gtk_menu_scroll_to (menu, 0);
1238 gtk_widget_hide (menu->tearoff_window);
1239 gtk_menu_reparent (menu, menu->toplevel, FALSE);
1245 * gtk_menu_get_tearoff_state:
1248 * Returns whether the menu is torn off. See
1249 * gtk_menu_set_tearoff_state ().
1251 * Return value: %TRUE if the menu is currently torn off.
1254 gtk_menu_get_tearoff_state (GtkMenu *menu)
1256 g_return_val_if_fail (GTK_IS_MENU (menu), FALSE);
1258 return menu->torn_off;
1262 * gtk_menu_set_title:
1264 * @title: a string containing the title for the menu.
1266 * Sets the title string for the menu. The title is displayed when the menu
1267 * is shown as a tearoff menu.
1270 gtk_menu_set_title (GtkMenu *menu,
1273 g_return_if_fail (GTK_IS_MENU (menu));
1276 g_object_set_data_full (G_OBJECT (menu), "gtk-menu-title",
1277 g_strdup (title), (GtkDestroyNotify) g_free);
1279 g_object_set_data (G_OBJECT (menu), "gtk-menu-title", NULL);
1281 gtk_menu_update_title (menu);
1282 g_object_notify (G_OBJECT (menu), "tearoff_title");
1286 * gtk_menu_get_title:
1289 * Returns the title of the menu. See gtk_menu_set_title().
1291 * Return value: the title of the menu, or %NULL if the menu has no
1292 * title set on it. This string is owned by the widget and should
1293 * not be modified or freed.
1295 G_CONST_RETURN gchar *
1296 gtk_menu_get_title (GtkMenu *menu)
1298 g_return_val_if_fail (GTK_IS_MENU (menu), NULL);
1300 return gtk_object_get_data (GTK_OBJECT (menu), "gtk-menu-title");
1304 gtk_menu_reorder_child (GtkMenu *menu,
1308 GtkMenuShell *menu_shell;
1309 g_return_if_fail (GTK_IS_MENU (menu));
1310 g_return_if_fail (GTK_IS_MENU_ITEM (child));
1311 menu_shell = GTK_MENU_SHELL (menu);
1312 if (g_list_find (menu_shell->children, child))
1314 menu_shell->children = g_list_remove (menu_shell->children, child);
1315 menu_shell->children = g_list_insert (menu_shell->children, child, position);
1316 if (GTK_WIDGET_VISIBLE (menu_shell))
1317 gtk_widget_queue_resize (GTK_WIDGET (menu_shell));
1322 gtk_menu_style_set (GtkWidget *widget,
1323 GtkStyle *previous_style)
1325 if (GTK_WIDGET_REALIZED (widget))
1327 GtkMenu *menu = GTK_MENU (widget);
1329 gtk_style_set_background (widget->style, menu->bin_window, GTK_STATE_NORMAL);
1330 gtk_style_set_background (widget->style, menu->view_window, GTK_STATE_NORMAL);
1331 gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
1336 gtk_menu_realize (GtkWidget *widget)
1338 GdkWindowAttr attributes;
1339 gint attributes_mask;
1345 g_return_if_fail (GTK_IS_MENU (widget));
1347 menu = GTK_MENU (widget);
1349 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
1351 attributes.window_type = GDK_WINDOW_CHILD;
1352 attributes.x = widget->allocation.x;
1353 attributes.y = widget->allocation.y;
1354 attributes.width = widget->allocation.width;
1355 attributes.height = widget->allocation.height;
1356 attributes.wclass = GDK_INPUT_OUTPUT;
1357 attributes.visual = gtk_widget_get_visual (widget);
1358 attributes.colormap = gtk_widget_get_colormap (widget);
1360 attributes.event_mask = gtk_widget_get_events (widget);
1361 attributes.event_mask |= (GDK_EXPOSURE_MASK | GDK_KEY_PRESS_MASK |
1362 GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK );
1364 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1365 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
1366 gdk_window_set_user_data (widget->window, widget);
1368 border_width = GTK_CONTAINER (widget)->border_width;
1370 attributes.x = border_width + widget->style->xthickness;
1371 attributes.y = border_width + widget->style->ythickness;
1372 attributes.width = MAX (1, widget->allocation.width - attributes.x * 2);
1373 attributes.height = MAX (1, widget->allocation.height - attributes.y * 2);
1375 if (menu->upper_arrow_visible)
1377 attributes.y += MENU_SCROLL_ARROW_HEIGHT;
1378 attributes.height -= MENU_SCROLL_ARROW_HEIGHT;
1380 if (menu->lower_arrow_visible)
1381 attributes.height -= MENU_SCROLL_ARROW_HEIGHT;
1383 menu->view_window = gdk_window_new (widget->window, &attributes, attributes_mask);
1384 gdk_window_set_user_data (menu->view_window, menu);
1388 attributes.height = MAX (1, widget->requisition.height - (border_width + widget->style->ythickness) * 2);
1390 menu->bin_window = gdk_window_new (menu->view_window, &attributes, attributes_mask);
1391 gdk_window_set_user_data (menu->bin_window, menu);
1393 children = GTK_MENU_SHELL (menu)->children;
1396 child = children->data;
1397 children = children->next;
1399 gtk_widget_set_parent_window (child, menu->bin_window);
1402 widget->style = gtk_style_attach (widget->style, widget->window);
1403 gtk_style_set_background (widget->style, menu->bin_window, GTK_STATE_NORMAL);
1404 gtk_style_set_background (widget->style, menu->view_window, GTK_STATE_NORMAL);
1405 gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
1407 if (GTK_MENU_SHELL (widget)->active_menu_item)
1408 gtk_menu_scroll_item_visible (GTK_MENU_SHELL (widget),
1409 GTK_MENU_SHELL (widget)->active_menu_item);
1411 gdk_window_show (menu->bin_window);
1412 gdk_window_show (menu->view_window);
1416 gtk_menu_focus (GtkWidget *widget,
1417 GtkDirectionType direction)
1420 * A menu or its menu items cannot have focus
1425 /* See notes in gtk_menu_popup() for information about the "grab transfer window"
1428 menu_grab_transfer_window_get (GtkMenu *menu)
1430 GdkWindow *window = g_object_get_data (G_OBJECT (menu), "gtk-menu-transfer-window");
1433 GdkWindowAttr attributes;
1434 gint attributes_mask;
1436 attributes.x = -100;
1437 attributes.y = -100;
1438 attributes.width = 10;
1439 attributes.height = 10;
1440 attributes.window_type = GDK_WINDOW_TEMP;
1441 attributes.wclass = GDK_INPUT_ONLY;
1442 attributes.override_redirect = TRUE;
1443 attributes.event_mask = 0;
1445 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_NOREDIR;
1447 window = gdk_window_new (gtk_widget_get_root_window (GTK_WIDGET (menu)),
1448 &attributes, attributes_mask);
1449 gdk_window_set_user_data (window, menu);
1451 gdk_window_show (window);
1453 g_object_set_data (G_OBJECT (menu), "gtk-menu-transfer-window", window);
1460 menu_grab_transfer_window_destroy (GtkMenu *menu)
1462 GdkWindow *window = g_object_get_data (G_OBJECT (menu), "gtk-menu-transfer-window");
1465 gdk_window_set_user_data (window, NULL);
1466 gdk_window_destroy (window);
1467 g_object_set_data (G_OBJECT (menu), "gtk-menu-transfer-window", NULL);
1472 gtk_menu_unrealize (GtkWidget *widget)
1476 g_return_if_fail (GTK_IS_MENU (widget));
1478 menu = GTK_MENU (widget);
1480 menu_grab_transfer_window_destroy (menu);
1482 gdk_window_set_user_data (menu->view_window, NULL);
1483 gdk_window_destroy (menu->view_window);
1484 menu->view_window = NULL;
1486 gdk_window_set_user_data (menu->bin_window, NULL);
1487 gdk_window_destroy (menu->bin_window);
1488 menu->bin_window = NULL;
1490 (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
1494 gtk_menu_size_request (GtkWidget *widget,
1495 GtkRequisition *requisition)
1498 GtkMenuShell *menu_shell;
1501 guint max_toggle_size;
1502 guint max_accel_width;
1503 GtkRequisition child_requisition;
1505 g_return_if_fail (GTK_IS_MENU (widget));
1506 g_return_if_fail (requisition != NULL);
1508 menu = GTK_MENU (widget);
1509 menu_shell = GTK_MENU_SHELL (widget);
1511 requisition->width = 0;
1512 requisition->height = 0;
1514 max_toggle_size = 0;
1515 max_accel_width = 0;
1517 children = menu_shell->children;
1520 child = children->data;
1521 children = children->next;
1523 if (GTK_WIDGET_VISIBLE (child))
1527 /* It's important to size_request the child
1528 * before doing the toggle size request, in
1529 * case the toggle size request depends on the size
1530 * request of a child of the child (e.g. for ImageMenuItem)
1533 GTK_MENU_ITEM (child)->show_submenu_indicator = TRUE;
1534 gtk_widget_size_request (child, &child_requisition);
1536 requisition->width = MAX (requisition->width, child_requisition.width);
1537 requisition->height += child_requisition.height;
1539 gtk_menu_item_toggle_size_request (GTK_MENU_ITEM (child), &toggle_size);
1540 max_toggle_size = MAX (max_toggle_size, toggle_size);
1541 max_accel_width = MAX (max_accel_width, GTK_MENU_ITEM (child)->accelerator_width);
1545 requisition->width += max_toggle_size + max_accel_width;
1546 requisition->width += (GTK_CONTAINER (menu)->border_width +
1547 widget->style->xthickness) * 2;
1548 requisition->height += (GTK_CONTAINER (menu)->border_width +
1549 widget->style->ythickness) * 2;
1551 menu->toggle_size = max_toggle_size;
1553 /* Don't resize the tearoff if it is not active, because it won't redraw (it is only a background pixmap).
1555 if (menu->tearoff_active)
1556 gtk_menu_set_tearoff_hints (menu, requisition->width);
1560 gtk_menu_size_allocate (GtkWidget *widget,
1561 GtkAllocation *allocation)
1564 GtkMenuShell *menu_shell;
1566 GtkAllocation child_allocation;
1571 g_return_if_fail (GTK_IS_MENU (widget));
1572 g_return_if_fail (allocation != NULL);
1574 menu = GTK_MENU (widget);
1575 menu_shell = GTK_MENU_SHELL (widget);
1577 widget->allocation = *allocation;
1579 x = GTK_CONTAINER (menu)->border_width + widget->style->xthickness;
1580 y = GTK_CONTAINER (menu)->border_width + widget->style->ythickness;
1582 width = MAX (1, allocation->width - x * 2);
1583 height = MAX (1, allocation->height - y * 2);
1585 if (menu_shell->active)
1586 gtk_menu_scroll_to (menu, menu->scroll_offset);
1588 if (menu->upper_arrow_visible && !menu->tearoff_active)
1590 y += MENU_SCROLL_ARROW_HEIGHT;
1591 height -= MENU_SCROLL_ARROW_HEIGHT;
1594 if (menu->lower_arrow_visible && !menu->tearoff_active)
1595 height -= MENU_SCROLL_ARROW_HEIGHT;
1597 if (GTK_WIDGET_REALIZED (widget))
1599 gdk_window_move_resize (widget->window,
1600 allocation->x, allocation->y,
1601 allocation->width, allocation->height);
1603 gdk_window_move_resize (menu->view_window,
1610 if (menu_shell->children)
1612 child_allocation.x = 0;
1613 child_allocation.y = 0;
1614 child_allocation.width = width;
1616 children = menu_shell->children;
1619 child = children->data;
1620 children = children->next;
1622 if (GTK_WIDGET_VISIBLE (child))
1624 GtkRequisition child_requisition;
1625 gtk_widget_get_child_requisition (child, &child_requisition);
1627 child_allocation.height = child_requisition.height;
1629 gtk_menu_item_toggle_size_allocate (GTK_MENU_ITEM (child),
1631 gtk_widget_size_allocate (child, &child_allocation);
1632 gtk_widget_queue_draw (child);
1634 child_allocation.y += child_allocation.height;
1638 /* Resize the item window */
1639 if (GTK_WIDGET_REALIZED (widget))
1641 gdk_window_resize (menu->bin_window,
1642 child_allocation.width,
1643 child_allocation.y);
1647 if (menu->tearoff_active)
1649 if (allocation->height >= widget->requisition.height)
1651 if (GTK_WIDGET_VISIBLE (menu->tearoff_scrollbar))
1653 gtk_widget_hide (menu->tearoff_scrollbar);
1654 gtk_menu_set_tearoff_hints (menu, allocation->width);
1656 gtk_menu_scroll_to (menu, 0);
1661 menu->tearoff_adjustment->upper = widget->requisition.height;
1662 menu->tearoff_adjustment->page_size = allocation->height;
1664 if (menu->tearoff_adjustment->value + menu->tearoff_adjustment->page_size >
1665 menu->tearoff_adjustment->upper)
1668 value = menu->tearoff_adjustment->upper - menu->tearoff_adjustment->page_size;
1671 gtk_menu_scroll_to (menu, value);
1674 gtk_adjustment_changed (menu->tearoff_adjustment);
1676 if (!GTK_WIDGET_VISIBLE (menu->tearoff_scrollbar))
1678 gtk_widget_show (menu->tearoff_scrollbar);
1679 gtk_menu_set_tearoff_hints (menu, allocation->width);
1687 gtk_menu_paint (GtkWidget *widget,
1688 GdkEventExpose *event)
1692 gint border_x, border_y;
1694 g_return_if_fail (GTK_IS_MENU (widget));
1696 menu = GTK_MENU (widget);
1698 border_x = GTK_CONTAINER (widget)->border_width + widget->style->xthickness;
1699 border_y = GTK_CONTAINER (widget)->border_width + widget->style->ythickness;
1700 gdk_window_get_size (widget->window, &width, &height);
1702 if (event->window == widget->window)
1704 gtk_paint_box (widget->style,
1708 NULL, widget, "menu",
1710 if (menu->upper_arrow_visible && !menu->tearoff_active)
1712 gtk_paint_box (widget->style,
1714 menu->upper_arrow_prelight ?
1715 GTK_STATE_PRELIGHT : GTK_STATE_NORMAL,
1717 NULL, widget, "menu",
1721 MENU_SCROLL_ARROW_HEIGHT);
1723 gtk_paint_arrow (widget->style,
1725 menu->upper_arrow_prelight ?
1726 GTK_STATE_PRELIGHT : GTK_STATE_NORMAL,
1728 NULL, widget, "menu",
1731 width / 2 - MENU_SCROLL_ARROW_HEIGHT / 2 + 1,
1733 MENU_SCROLL_ARROW_HEIGHT - 2 * border_y - 2,
1734 MENU_SCROLL_ARROW_HEIGHT - 2 * border_y - 2);
1737 if (menu->lower_arrow_visible && !menu->tearoff_active)
1739 gtk_paint_box (widget->style,
1741 menu->lower_arrow_prelight ?
1742 GTK_STATE_PRELIGHT : GTK_STATE_NORMAL,
1744 NULL, widget, "menu",
1746 height - border_y - MENU_SCROLL_ARROW_HEIGHT + 1,
1748 MENU_SCROLL_ARROW_HEIGHT);
1750 gtk_paint_arrow (widget->style,
1752 menu->lower_arrow_prelight ?
1753 GTK_STATE_PRELIGHT : GTK_STATE_NORMAL,
1755 NULL, widget, "menu",
1758 width / 2 - MENU_SCROLL_ARROW_HEIGHT / 2 + 1,
1759 height - MENU_SCROLL_ARROW_HEIGHT + 1,
1760 MENU_SCROLL_ARROW_HEIGHT - 2 * border_y - 2,
1761 MENU_SCROLL_ARROW_HEIGHT - 2 * border_y - 2);
1767 gtk_menu_expose (GtkWidget *widget,
1768 GdkEventExpose *event)
1770 g_return_val_if_fail (GTK_IS_MENU (widget), FALSE);
1771 g_return_val_if_fail (event != NULL, FALSE);
1773 if (GTK_WIDGET_DRAWABLE (widget))
1775 gtk_menu_paint (widget, event);
1777 (* GTK_WIDGET_CLASS (parent_class)->expose_event) (widget, event);
1784 gtk_menu_show (GtkWidget *widget)
1786 GtkMenu *menu = GTK_MENU (widget);
1788 _gtk_menu_refresh_accel_paths (menu, FALSE);
1790 GTK_WIDGET_CLASS (parent_class)->show (widget);
1794 gtk_menu_key_press (GtkWidget *widget,
1797 GtkMenuShell *menu_shell;
1799 gboolean delete = FALSE;
1800 gboolean can_change_accels;
1801 gchar *accel = NULL;
1802 guint accel_key, accel_mods;
1803 GdkModifierType consumed_modifiers;
1804 GdkDisplay *display;
1806 g_return_val_if_fail (GTK_IS_MENU (widget), FALSE);
1807 g_return_val_if_fail (event != NULL, FALSE);
1809 menu_shell = GTK_MENU_SHELL (widget);
1810 menu = GTK_MENU (widget);
1812 gtk_menu_stop_navigating_submenu (menu);
1814 if (GTK_WIDGET_CLASS (parent_class)->key_press_event (widget, event))
1817 display = gtk_widget_get_display (widget);
1819 g_object_get (G_OBJECT (gtk_widget_get_settings (widget)),
1820 "gtk-menu-bar-accel", &accel,
1821 "gtk-can-change-accels", &can_change_accels,
1827 GdkModifierType mods = 0;
1828 gboolean handled = FALSE;
1830 gtk_accelerator_parse (accel, &keyval, &mods);
1833 g_warning ("Failed to parse menu bar accelerator '%s'\n", accel);
1835 /* FIXME this is wrong, needs to be in the global accel resolution
1836 * thing, to properly consider i18n etc., but that probably requires
1837 * AccelGroup changes etc.
1839 if (event->keyval == keyval &&
1840 (mods & event->state) == mods)
1842 gtk_signal_emit_by_name (GTK_OBJECT (menu), "cancel");
1851 switch (event->keyval)
1862 /* Figure out what modifiers went into determining the key symbol */
1863 gdk_keymap_translate_keyboard_state (gdk_keymap_get_for_display (display),
1864 event->hardware_keycode, event->state, event->group,
1865 NULL, NULL, NULL, &consumed_modifiers);
1867 accel_key = gdk_keyval_to_lower (event->keyval);
1868 accel_mods = event->state & gtk_accelerator_get_default_mod_mask () & ~consumed_modifiers;
1870 /* If lowercasing affects the keysym, then we need to include SHIFT in the modifiers,
1871 * We re-upper case when we match against the keyval, but display and save in caseless form.
1873 if (accel_key != event->keyval)
1874 accel_mods |= GDK_SHIFT_MASK;
1876 /* Modify the accelerators */
1877 if (can_change_accels &&
1878 menu_shell->active_menu_item &&
1879 GTK_BIN (menu_shell->active_menu_item)->child && /* no seperators */
1880 GTK_MENU_ITEM (menu_shell->active_menu_item)->submenu == NULL && /* no submenus */
1881 (delete || gtk_accelerator_valid (accel_key, accel_mods)))
1883 GtkWidget *menu_item = menu_shell->active_menu_item;
1884 gboolean locked, replace_accels = TRUE;
1887 path = _gtk_widget_get_accel_path (menu_item, &locked);
1888 if (!path || locked)
1890 /* can't change accelerators on menu_items without paths
1891 * (basically, those items are accelerator-locked).
1893 /* g_print("item has no path or is locked, menu prefix: %s\n", menu->accel_path); */
1894 gdk_display_beep (display);
1900 /* For the keys that act to delete the current setting, we delete
1901 * the current setting if there is one, otherwise, we set the
1902 * key as the accelerator.
1908 if (gtk_accel_map_lookup_entry (path, &key) &&
1909 (key.accel_key || key.accel_mods))
1915 changed = gtk_accel_map_change_entry (path, accel_key, accel_mods, replace_accels);
1919 /* we failed, probably because this key is in use and
1922 /* g_print("failed to change\n"); */
1923 gdk_display_beep (display);
1932 gtk_menu_motion_notify (GtkWidget *widget,
1933 GdkEventMotion *event)
1935 GtkWidget *menu_item;
1937 GtkMenuShell *menu_shell;
1939 gboolean need_enter;
1941 if (GTK_IS_MENU (widget))
1942 gtk_menu_handle_scrolling (GTK_MENU (widget), TRUE);
1944 /* We received the event for one of two reasons:
1946 * a) We are the active menu, and did gtk_grab_add()
1947 * b) The widget is a child of ours, and the event was propagated
1949 * Since for computation of navigation regions, we want the menu which
1950 * is the parent of the menu item, for a), we need to find that menu,
1951 * which may be different from 'widget'.
1953 menu_item = gtk_get_event_widget ((GdkEvent*) event);
1954 if (!menu_item || !GTK_IS_MENU_ITEM (menu_item) ||
1955 !_gtk_menu_item_is_selectable (menu_item) ||
1956 !GTK_IS_MENU (menu_item->parent))
1959 menu_shell = GTK_MENU_SHELL (menu_item->parent);
1960 menu = GTK_MENU (menu_shell);
1962 need_enter = (menu->navigation_region != NULL || menu_shell->ignore_enter);
1964 /* Check to see if we are within an active submenu's navigation region
1966 if (gtk_menu_navigating_submenu (menu, event->x_root, event->y_root))
1971 /* The menu is now sensitive to enter events on its items, but
1972 * was previously sensitive. So we fake an enter event.
1976 menu_shell->ignore_enter = FALSE;
1978 gdk_window_get_size (event->window, &width, &height);
1979 if (event->x >= 0 && event->x < width &&
1980 event->y >= 0 && event->y < height)
1982 GdkEvent *send_event = gdk_event_new (GDK_ENTER_NOTIFY);
1985 send_event->crossing.type = GDK_ENTER_NOTIFY;
1986 send_event->crossing.window = g_object_ref (event->window);
1987 send_event->crossing.time = event->time;
1988 send_event->crossing.send_event = TRUE;
1989 send_event->crossing.x_root = event->x_root;
1990 send_event->crossing.y_root = event->y_root;
1991 send_event->crossing.x = event->x;
1992 send_event->crossing.y = event->y;
1994 /* We send the event to 'widget', the currently active menu,
1995 * instead of 'menu', the menu that the pointer is in. This
1996 * will ensure that the event will be ignored unless the
1997 * menuitem is a child of the active menu or some parent
1998 * menu of the active menu.
2000 result = gtk_widget_event (widget, &send_event);
2001 gdk_event_free (send_event);
2011 gtk_menu_scroll_timeout (gpointer data)
2016 gint view_width, view_height;
2018 GDK_THREADS_ENTER ();
2020 menu = GTK_MENU (data);
2021 widget = GTK_WIDGET (menu);
2023 offset = menu->scroll_offset + menu->scroll_step;
2025 /* If we scroll upward and the non-visible top part
2026 * is smaller than the scroll arrow it would be
2027 * pretty stupid to show the arrow and taking more
2028 * screen space than just scrolling to the top.
2030 if ((menu->scroll_step < 0) && (offset < MENU_SCROLL_ARROW_HEIGHT))
2033 /* Don't scroll over the top if we weren't before: */
2034 if ((menu->scroll_offset >= 0) && (offset < 0))
2037 gdk_window_get_size (widget->window, &view_width, &view_height);
2039 /* Don't scroll past the bottom if we weren't before: */
2040 if (menu->scroll_offset > 0)
2041 view_height -= MENU_SCROLL_ARROW_HEIGHT;
2043 if ((menu->scroll_offset + view_height <= widget->requisition.height) &&
2044 (offset + view_height > widget->requisition.height))
2045 offset = widget->requisition.height - view_height;
2047 gtk_menu_scroll_to (menu, offset);
2049 GDK_THREADS_LEAVE ();
2055 gtk_menu_handle_scrolling (GtkMenu *menu, gboolean enter)
2057 GtkMenuShell *menu_shell;
2063 gboolean scroll_fast = FALSE;
2065 menu_shell = GTK_MENU_SHELL (menu);
2067 gdk_window_get_pointer (GTK_WIDGET (menu)->window, &x, &y, NULL);
2068 gdk_window_get_size (GTK_WIDGET (menu)->window, &width, &height);
2070 border = GTK_CONTAINER (menu)->border_width + GTK_WIDGET (menu)->style->ythickness;
2072 if (menu->upper_arrow_visible && !menu->tearoff_active)
2077 rect.height = MENU_SCROLL_ARROW_HEIGHT + border;
2080 if ((x >= rect.x) && (x < rect.x + rect.width) &&
2081 (y >= rect.y) && (y < rect.y + rect.height))
2084 scroll_fast = (y < rect.y + MENU_SCROLL_FAST_ZONE);
2087 if (enter && in_arrow &&
2088 (!menu->upper_arrow_prelight || menu->scroll_fast != scroll_fast))
2090 menu->upper_arrow_prelight = TRUE;
2091 menu->scroll_fast = scroll_fast;
2092 gdk_window_invalidate_rect (GTK_WIDGET (menu)->window, &rect, FALSE);
2094 /* Deselect the active item so that any submenus are poped down */
2095 gtk_menu_shell_deselect (menu_shell);
2097 gtk_menu_stop_scrolling (menu);
2098 menu->scroll_step = -MENU_SCROLL_STEP;
2099 menu->timeout_id = g_timeout_add ((scroll_fast) ? MENU_SCROLL_TIMEOUT2 : MENU_SCROLL_TIMEOUT1,
2100 gtk_menu_scroll_timeout,
2103 else if (!enter && !in_arrow && menu->upper_arrow_prelight)
2105 menu->upper_arrow_prelight = FALSE;
2106 gdk_window_invalidate_rect (GTK_WIDGET (menu)->window, &rect, FALSE);
2108 gtk_menu_stop_scrolling (menu);
2112 if (menu->lower_arrow_visible && !menu->tearoff_active)
2115 rect.y = height - border - MENU_SCROLL_ARROW_HEIGHT;
2117 rect.height = MENU_SCROLL_ARROW_HEIGHT + border;
2120 if ((x >= rect.x) && (x < rect.x + rect.width) &&
2121 (y >= rect.y) && (y < rect.y + rect.height))
2124 scroll_fast = (y > rect.y + rect.height - MENU_SCROLL_FAST_ZONE);
2127 if (enter && in_arrow &&
2128 (!menu->lower_arrow_prelight || menu->scroll_fast != scroll_fast))
2130 menu->lower_arrow_prelight = TRUE;
2131 menu->scroll_fast = scroll_fast;
2132 gdk_window_invalidate_rect (GTK_WIDGET (menu)->window, &rect, FALSE);
2134 /* Deselect the active item so that any submenus are poped down */
2135 gtk_menu_shell_deselect (menu_shell);
2137 gtk_menu_stop_scrolling (menu);
2138 menu->scroll_step = MENU_SCROLL_STEP;
2139 menu->timeout_id = g_timeout_add ((scroll_fast) ? MENU_SCROLL_TIMEOUT2 : MENU_SCROLL_TIMEOUT1,
2140 gtk_menu_scroll_timeout,
2143 else if (!enter && !in_arrow && menu->lower_arrow_prelight)
2145 menu->lower_arrow_prelight = FALSE;
2146 gdk_window_invalidate_rect (GTK_WIDGET (menu)->window, &rect, FALSE);
2148 gtk_menu_stop_scrolling (menu);
2154 gtk_menu_enter_notify (GtkWidget *widget,
2155 GdkEventCrossing *event)
2157 GtkWidget *menu_item;
2159 if (widget && GTK_IS_MENU (widget))
2160 gtk_menu_handle_scrolling (GTK_MENU (widget), TRUE);
2162 /* If this is a faked enter (see gtk_menu_motion_notify), 'widget'
2163 * will not correspond to the event widget's parent. Check to see
2164 * if we are in the parent's navigation region.
2166 menu_item = gtk_get_event_widget ((GdkEvent*) event);
2167 if (menu_item && GTK_IS_MENU_ITEM (menu_item) && GTK_IS_MENU (menu_item->parent) &&
2168 gtk_menu_navigating_submenu (GTK_MENU (menu_item->parent), event->x_root, event->y_root))
2171 return GTK_WIDGET_CLASS (parent_class)->enter_notify_event (widget, event);
2175 gtk_menu_leave_notify (GtkWidget *widget,
2176 GdkEventCrossing *event)
2178 GtkMenuShell *menu_shell;
2180 GtkMenuItem *menu_item;
2181 GtkWidget *event_widget;
2183 menu = GTK_MENU (widget);
2184 menu_shell = GTK_MENU_SHELL (widget);
2186 if (gtk_menu_navigating_submenu (menu, event->x_root, event->y_root))
2189 gtk_menu_handle_scrolling (menu, FALSE);
2191 event_widget = gtk_get_event_widget ((GdkEvent*) event);
2193 if (!event_widget || !GTK_IS_MENU_ITEM (event_widget))
2196 menu_item = GTK_MENU_ITEM (event_widget);
2198 /* Here we check to see if we're leaving an active menu item with a submenu,
2199 * in which case we enter submenu navigation mode.
2201 if (menu_shell->active_menu_item != NULL
2202 && menu_item->submenu != NULL
2203 && menu_item->submenu_placement == GTK_LEFT_RIGHT)
2205 if (GTK_MENU_SHELL (menu_item->submenu)->active)
2207 gtk_menu_set_submenu_navigation_region (menu, menu_item, event);
2212 return GTK_WIDGET_CLASS (parent_class)->leave_notify_event (widget, event);
2216 gtk_menu_stop_navigating_submenu (GtkMenu *menu)
2218 if (menu->navigation_region)
2220 gdk_region_destroy (menu->navigation_region);
2221 menu->navigation_region = NULL;
2224 if (menu->navigation_timeout)
2226 gtk_timeout_remove (menu->navigation_timeout);
2227 menu->navigation_timeout = 0;
2231 /* When the timeout is elapsed, the navigation region is destroyed
2232 * and the menuitem under the pointer (if any) is selected.
2235 gtk_menu_stop_navigating_submenu_cb (gpointer user_data)
2237 GtkMenu *menu = user_data;
2238 GdkWindow *child_window;
2240 GDK_THREADS_ENTER ();
2242 gtk_menu_stop_navigating_submenu (menu);
2244 if (GTK_WIDGET_REALIZED (menu))
2246 child_window = gdk_window_get_pointer (menu->bin_window, NULL, NULL, NULL);
2250 GdkEvent *send_event = gdk_event_new (GDK_ENTER_NOTIFY);
2252 send_event->crossing.window = g_object_ref (child_window);
2253 send_event->crossing.time = GDK_CURRENT_TIME; /* Bogus */
2254 send_event->crossing.send_event = TRUE;
2256 GTK_WIDGET_CLASS (parent_class)->enter_notify_event (GTK_WIDGET (menu), (GdkEventCrossing *)send_event);
2258 gdk_event_free (send_event);
2262 GDK_THREADS_LEAVE ();
2268 gtk_menu_navigating_submenu (GtkMenu *menu,
2272 if (menu->navigation_region)
2274 if (gdk_region_point_in (menu->navigation_region, event_x, event_y))
2278 gtk_menu_stop_navigating_submenu (menu);
2286 gtk_menu_set_submenu_navigation_region (GtkMenu *menu,
2287 GtkMenuItem *menu_item,
2288 GdkEventCrossing *event)
2290 gint submenu_left = 0;
2291 gint submenu_right = 0;
2292 gint submenu_top = 0;
2293 gint submenu_bottom = 0;
2297 GtkWidget *event_widget;
2299 g_return_if_fail (menu_item->submenu != NULL);
2300 g_return_if_fail (event != NULL);
2302 event_widget = gtk_get_event_widget ((GdkEvent*) event);
2304 gdk_window_get_origin (menu_item->submenu->window, &submenu_left, &submenu_top);
2305 gdk_window_get_size (menu_item->submenu->window, &width, &height);
2306 submenu_right = submenu_left + width;
2307 submenu_bottom = submenu_top + height;
2309 gdk_window_get_size (event_widget->window, &width, &height);
2311 if (event->x >= 0 && event->x < width)
2313 /* Set navigation region */
2314 /* We fudge/give a little padding in case the user
2315 * ``misses the vertex'' of the triangle/is off by a pixel or two.
2317 if (menu_item->submenu_direction == GTK_DIRECTION_RIGHT)
2318 point[0].x = event->x_root - SUBMENU_NAV_REGION_PADDING;
2320 point[0].x = event->x_root + SUBMENU_NAV_REGION_PADDING;
2322 /* Exiting the top or bottom? */
2325 point[0].y = event->y_root + 1;
2326 point[1].y = submenu_top;
2328 if (point[0].y <= point[1].y)
2333 point[0].y = event->y_root;
2334 point[1].y = submenu_bottom;
2336 if (point[0].y >= point[1].y)
2340 /* Submenu is to the left or right? */
2341 if (menu_item->submenu_direction == GTK_DIRECTION_RIGHT)
2342 point[1].x = submenu_left; /* right */
2344 point[1].x = submenu_right; /* left */
2346 point[2].x = point[1].x;
2347 point[2].y = point[0].y;
2349 gtk_menu_stop_navigating_submenu (menu);
2351 menu->navigation_region = gdk_region_polygon (point, 3, GDK_WINDING_RULE);
2353 menu->navigation_timeout = gtk_timeout_add (SUBMENU_NAV_HYSTERESIS_TIMEOUT,
2354 gtk_menu_stop_navigating_submenu_cb, menu);
2359 gtk_menu_deactivate (GtkMenuShell *menu_shell)
2363 g_return_if_fail (GTK_IS_MENU (menu_shell));
2365 parent = menu_shell->parent_menu_shell;
2367 menu_shell->activate_time = 0;
2368 gtk_menu_popdown (GTK_MENU (menu_shell));
2371 gtk_menu_shell_deactivate (GTK_MENU_SHELL (parent));
2375 gtk_menu_position (GtkMenu *menu)
2378 GtkRequisition requisition;
2379 GtkMenuPrivate *private;
2385 GdkRectangle monitor;
2388 g_return_if_fail (GTK_IS_MENU (menu));
2390 widget = GTK_WIDGET (menu);
2392 gdk_window_get_pointer (gtk_widget_get_root_window (widget),
2395 screen = gtk_widget_get_screen (widget);
2396 monitor_num = gdk_screen_get_monitor_at_point (screen, x, y);
2397 gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
2399 /* We need the requisition to figure out the right place to
2400 * popup the menu. In fact, we always need to ask here, since
2401 * if a size_request was queued while we weren't popped up,
2402 * the requisition won't have been recomputed yet.
2404 gtk_widget_size_request (widget, &requisition);
2408 if (menu->position_func)
2409 (* menu->position_func) (menu, &x, &y, &push_in, menu->position_func_data);
2412 x = CLAMP (x - 2, monitor.x, MAX (monitor.x, monitor.x + monitor.width - requisition.width));
2413 y = CLAMP (y - 2, monitor.y, MAX (monitor.y, monitor.y + monitor.height - requisition.height));
2420 menu_height = GTK_WIDGET (menu)->requisition.height;
2422 if (y + menu_height > monitor.y + monitor.height)
2424 scroll_offset -= y + menu_height - (monitor.y + monitor.height);
2425 y = (monitor.y + monitor.height) - menu_height;
2435 /* FIXME: should this be done in the various position_funcs ? */
2436 x = CLAMP (x, monitor.x, MAX (monitor.x, monitor.x + monitor.width - requisition.width));
2438 if (y + requisition.height > monitor.y + monitor.height)
2439 requisition.height = (monitor.y + monitor.height) - y;
2444 requisition.height -= -y;
2448 if (scroll_offset > 0)
2449 scroll_offset += MENU_SCROLL_ARROW_HEIGHT;
2451 gtk_window_move (GTK_WINDOW (GTK_MENU_SHELL (menu)->active ? menu->toplevel : menu->tearoff_window),
2454 if (GTK_MENU_SHELL (menu)->active)
2456 private = gtk_menu_get_private (menu);
2457 private->have_position = TRUE;
2461 gtk_widget_queue_resize (menu->toplevel);
2465 gtk_window_resize (GTK_WINDOW (menu->tearoff_window),
2466 requisition.width, requisition.height);
2469 menu->scroll_offset = scroll_offset;
2473 gtk_menu_stop_scrolling (GtkMenu *menu)
2475 if (menu->timeout_id)
2477 g_source_remove (menu->timeout_id);
2478 menu->timeout_id = 0;
2484 gtk_menu_scroll_to (GtkMenu *menu,
2489 gint view_width, view_height;
2491 gboolean last_visible;
2494 widget = GTK_WIDGET (menu);
2496 if (menu->tearoff_active &&
2497 menu->tearoff_adjustment &&
2498 (menu->tearoff_adjustment->value != offset))
2500 menu->tearoff_adjustment->value = offset;
2501 gtk_adjustment_value_changed (menu->tearoff_adjustment);
2504 /* Move/resize the viewport according to arrows: */
2505 view_width = widget->allocation.width;
2506 view_height = widget->allocation.height;
2508 border_width = GTK_CONTAINER (menu)->border_width;
2509 view_width -= (border_width + widget->style->xthickness) * 2;
2510 view_height -= (border_width + widget->style->ythickness) * 2;
2511 menu_height = widget->requisition.height - (border_width + widget->style->ythickness) * 2;
2513 offset = CLAMP (offset, 0, menu_height - view_height);
2515 /* Scroll the menu: */
2516 if (GTK_WIDGET_REALIZED (menu))
2517 gdk_window_move (menu->bin_window, 0, -offset);
2519 x = border_width + widget->style->xthickness;
2520 y = border_width + widget->style->ythickness;
2522 if (!menu->tearoff_active)
2524 last_visible = menu->upper_arrow_visible;
2525 menu->upper_arrow_visible = (offset > 0);
2527 if (menu->upper_arrow_visible)
2528 view_height -= MENU_SCROLL_ARROW_HEIGHT;
2530 if ( (last_visible != menu->upper_arrow_visible) &&
2531 !menu->upper_arrow_visible)
2533 menu->upper_arrow_prelight = FALSE;
2535 /* If we hid the upper arrow, possibly remove timeout */
2536 if (menu->scroll_step < 0)
2537 gtk_menu_stop_scrolling (menu);
2540 last_visible = menu->lower_arrow_visible;
2541 menu->lower_arrow_visible = (view_height + offset < menu_height);
2543 if (menu->lower_arrow_visible)
2544 view_height -= MENU_SCROLL_ARROW_HEIGHT;
2546 if ( (last_visible != menu->lower_arrow_visible) &&
2547 !menu->lower_arrow_visible)
2549 menu->lower_arrow_prelight = FALSE;
2551 /* If we hid the lower arrow, possibly remove timeout */
2552 if (menu->scroll_step > 0)
2553 gtk_menu_stop_scrolling (menu);
2556 if (menu->upper_arrow_visible)
2557 y += MENU_SCROLL_ARROW_HEIGHT;
2560 if (GTK_WIDGET_REALIZED (menu))
2561 gdk_window_move_resize (menu->view_window,
2567 menu->scroll_offset = offset;
2571 gtk_menu_scroll_item_visible (GtkMenuShell *menu_shell,
2572 GtkWidget *menu_item)
2577 GtkRequisition child_requisition;
2578 gint child_offset, child_height;
2582 gboolean last_child = 0;
2584 menu = GTK_MENU (menu_shell);
2586 /* We need to check if the selected item fully visible.
2587 * If not we need to scroll the menu so that it becomes fully
2594 children = menu_shell->children;
2597 child = children->data;
2598 children = children->next;
2600 if (GTK_WIDGET_VISIBLE (child))
2602 gtk_widget_size_request (child, &child_requisition);
2603 child_offset += child_height;
2604 child_height = child_requisition.height;
2607 if (child == menu_item)
2609 last_child = (children == NULL);
2614 if (child == menu_item)
2616 y = menu->scroll_offset;
2617 gdk_window_get_size (GTK_WIDGET (menu)->window, &width, &height);
2619 height -= 2*GTK_CONTAINER (menu)->border_width + 2*GTK_WIDGET (menu)->style->ythickness;
2621 if (child_offset + child_height <= y)
2623 /* Ignore the enter event we might get if the pointer is on the menu
2625 menu_shell->ignore_enter = TRUE;
2626 gtk_menu_scroll_to (menu, child_offset);
2631 if (menu->upper_arrow_visible && !menu->tearoff_active)
2632 arrow_height += MENU_SCROLL_ARROW_HEIGHT;
2633 if (menu->lower_arrow_visible && !menu->tearoff_active)
2634 arrow_height += MENU_SCROLL_ARROW_HEIGHT;
2636 if (child_offset >= y + height - arrow_height)
2639 if (!last_child && !menu->tearoff_active)
2640 arrow_height += MENU_SCROLL_ARROW_HEIGHT;
2642 y = child_offset + child_height - height + arrow_height;
2643 if ((y > 0) && !menu->tearoff_active)
2645 /* Need upper arrow */
2646 arrow_height += MENU_SCROLL_ARROW_HEIGHT;
2647 y = child_offset + child_height - height + arrow_height;
2649 /* Ignore the enter event we might get if the pointer is on the menu
2651 menu_shell->ignore_enter = TRUE;
2652 gtk_menu_scroll_to (menu, y);
2660 gtk_menu_select_item (GtkMenuShell *menu_shell,
2661 GtkWidget *menu_item)
2663 GtkMenu *menu = GTK_MENU (menu_shell);
2665 if (GTK_WIDGET_REALIZED (GTK_WIDGET (menu)))
2666 gtk_menu_scroll_item_visible (menu_shell, menu_item);
2668 GTK_MENU_SHELL_CLASS (parent_class)->select_item (menu_shell, menu_item);
2672 /* Reparent the menu, taking care of the refcounting
2674 * If unrealize is true we force a unrealize while reparenting the parent.
2675 * This can help eliminate flicker in some cases.
2677 * What happens is that when the menu is unrealized and then re-realized,
2678 * the allocations are as follows:
2680 * parent - 1x1 at (0,0)
2681 * child1 - 100x20 at (0,0)
2682 * child2 - 100x20 at (0,20)
2683 * child3 - 100x20 at (0,40)
2685 * That is, the parent is small but the children are full sized. Then,
2686 * when the queued_resize gets processed, the parent gets resized to
2689 * But in order to eliminate flicker when scrolling, gdkgeometry-x11.c
2690 * contains the following logic:
2692 * - if a move or resize operation on a window would change the clip
2693 * region on the children, then before the window is resized
2694 * the background for children is temporarily set to None, the
2695 * move/resize done, and the background for the children restored.
2697 * So, at the point where the parent is resized to final size, the
2698 * background for the children is temporarily None, and thus they
2699 * are not cleared to the background color and the previous background
2700 * (the image of the menu) is left in place.
2703 gtk_menu_reparent (GtkMenu *menu,
2704 GtkWidget *new_parent,
2707 GtkObject *object = GTK_OBJECT (menu);
2708 GtkWidget *widget = GTK_WIDGET (menu);
2709 gboolean was_floating = GTK_OBJECT_FLOATING (object);
2711 gtk_object_ref (object);
2712 gtk_object_sink (object);
2716 gtk_object_ref (object);
2717 gtk_container_remove (GTK_CONTAINER (widget->parent), widget);
2718 gtk_container_add (GTK_CONTAINER (new_parent), widget);
2719 gtk_object_unref (object);
2722 gtk_widget_reparent (GTK_WIDGET (menu), new_parent);
2725 GTK_OBJECT_SET_FLAGS (object, GTK_FLOATING);
2727 gtk_object_unref (object);
2731 gtk_menu_show_all (GtkWidget *widget)
2733 g_return_if_fail (GTK_IS_MENU (widget));
2735 /* Show children, but not self. */
2736 gtk_container_foreach (GTK_CONTAINER (widget), (GtkCallback) gtk_widget_show_all, NULL);
2741 gtk_menu_hide_all (GtkWidget *widget)
2743 g_return_if_fail (GTK_IS_MENU (widget));
2745 /* Hide children, but not self. */
2746 gtk_container_foreach (GTK_CONTAINER (widget), (GtkCallback) gtk_widget_hide_all, NULL);
2750 * gtk_menu_set_screen:
2751 * @menu: a #GtkMenu.
2752 * @screen: a #GtkScreen.
2754 * Sets the #GtkScreen on which the GtkMenu will be displayed.
2755 * This function can only be called before @menu is realized.
2758 gtk_menu_set_screen (GtkMenu *menu,
2761 g_return_if_fail (GTK_IS_MENU (menu));
2762 gtk_window_set_screen (GTK_WINDOW (menu->toplevel),
2764 g_object_set_data (G_OBJECT (menu), "gtk-menu-explicit-screen", screen);