X-Git-Url: http://pileus.org/git/?a=blobdiff_plain;f=gtk%2Fgtkmenushell.c;h=ae36459d43f981feae370333b014a5f46b5df0f8;hb=17d3775555888151780fa404242e734a8e7f6b21;hp=d72670aea378fde97d5fd4ec299f1a3f4d197006;hpb=7650482e468ee6f01cc5f7f3b161406fabc56bae;p=~andy%2Fgtk diff --git a/gtk/gtkmenushell.c b/gtk/gtkmenushell.c index d72670aea..ae36459d4 100644 --- a/gtk/gtkmenushell.c +++ b/gtk/gtkmenushell.c @@ -12,9 +12,7 @@ * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * License along with this library. If not, see . */ /* @@ -24,22 +22,41 @@ * GTK+ at ftp://ftp.gtk.org/pub/gtk/. */ +/** + * SECTION:gtkmenushell + * @Title: GtkMenuShell + * @Short_description: A base class for menu objects + * + * A #GtkMenuShell is the abstract base class used to derive the + * #GtkMenu and #GtkMenuBar subclasses. + * + * A #GtkMenuShell is a container of #GtkMenuItem objects arranged + * in a list which can be navigated, selected, and activated by the + * user to perform application functions. A #GtkMenuItem can have a + * submenu associated with it, allowing for nested hierarchical menus. + */ #include "config.h" -#include "gdk/gdkkeysyms.h" + #include "gtkbindings.h" #include "gtkkeyhash.h" #include "gtklabel.h" #include "gtkmain.h" #include "gtkmarshalers.h" #include "gtkmenubar.h" -#include "gtkmenuitem.h" +#include "gtkmenuitemprivate.h" #include "gtkmenushellprivate.h" #include "gtkmenuprivate.h" #include "gtkmnemonichash.h" -#include "gtktearoffmenuitem.h" #include "gtkwindow.h" #include "gtkprivate.h" +#include "gtkmain.h" #include "gtkintl.h" +#include "gtktypebuiltins.h" + +#include "deprecated/gtktearoffmenuitem.h" + +#include "a11y/gtkmenushellaccessible.h" + #define MENU_SHELL_TIMEOUT 500 @@ -56,6 +73,7 @@ enum { CANCEL, CYCLE_FOCUS, MOVE_SELECTED, + INSERT, LAST_SIGNAL }; @@ -230,6 +248,12 @@ gtk_menu_shell_class_init (GtkMenuShellClass *klass) klass->insert = gtk_menu_shell_real_insert; klass->move_selected = gtk_menu_shell_real_move_selected; + /** + * GtkMenuShell::deactivate: + * @menushell: the object which received the signal + * + * This signal is emitted when a menu shell is deactivated. + */ menu_shell_signals[DEACTIVATE] = g_signal_new (I_("deactivate"), G_OBJECT_CLASS_TYPE (object_class), @@ -239,6 +263,13 @@ gtk_menu_shell_class_init (GtkMenuShellClass *klass) _gtk_marshal_VOID__VOID, G_TYPE_NONE, 0); + /** + * GtkMenuShell::selection-done: + * @menushell: the object which received the signal + * + * This signal is emitted when a selection has been + * completed within a menu shell. + */ menu_shell_signals[SELECTION_DONE] = g_signal_new (I_("selection-done"), G_OBJECT_CLASS_TYPE (object_class), @@ -248,6 +279,14 @@ gtk_menu_shell_class_init (GtkMenuShellClass *klass) _gtk_marshal_VOID__VOID, G_TYPE_NONE, 0); + /** + * GtkMenuShell::move-current: + * @menushell: the object which received the signal + * @direction: the direction to move + * + * An keybinding signal which moves the current menu item + * in the direction specified by @direction. + */ menu_shell_signals[MOVE_CURRENT] = g_signal_new (I_("move-current"), G_OBJECT_CLASS_TYPE (object_class), @@ -258,6 +297,14 @@ gtk_menu_shell_class_init (GtkMenuShellClass *klass) G_TYPE_NONE, 1, GTK_TYPE_MENU_DIRECTION_TYPE); + /** + * GtkMenuShell::activate-current: + * @menushell: the object which received the signal + * @force_hide: if %TRUE, hide the menu after activating the menu item + * + * An action signal that activates the current menu item within + * the menu shell. + */ menu_shell_signals[ACTIVATE_CURRENT] = g_signal_new (I_("activate-current"), G_OBJECT_CLASS_TYPE (object_class), @@ -268,6 +315,13 @@ gtk_menu_shell_class_init (GtkMenuShellClass *klass) G_TYPE_NONE, 1, G_TYPE_BOOLEAN); + /** + * GtkMenuShell::cancel: + * @menushell: the object which received the signal + * + * An action signal which cancels the selection within the menu shell. + * Causes the #GtkMenuShell::selection-done signal to be emitted. + */ menu_shell_signals[CANCEL] = g_signal_new (I_("cancel"), G_OBJECT_CLASS_TYPE (object_class), @@ -277,6 +331,14 @@ gtk_menu_shell_class_init (GtkMenuShellClass *klass) _gtk_marshal_VOID__VOID, G_TYPE_NONE, 0); + /** + * GtkMenuShell::cycle-focus: + * @menushell: the object which received the signal + * @direction: the direction to cycle in + * + * A keybinding signal which moves the focus in the + * given @direction. + */ menu_shell_signals[CYCLE_FOCUS] = g_signal_new_class_handler (I_("cycle-focus"), G_OBJECT_CLASS_TYPE (object_class), @@ -309,6 +371,31 @@ gtk_menu_shell_class_init (GtkMenuShellClass *klass) G_TYPE_BOOLEAN, 1, G_TYPE_INT); + /** + * GtkMenuShell::insert: + * @menu_shell: the object on which the signal is emitted + * @child: the #GtkMenuItem that is being inserted + * @position: the position at which the insert occurs + * + * The ::insert signal is emitted when a new #GtkMenuItem is added to + * a #GtkMenuShell. A separate signal is used instead of + * GtkContainer::add because of the need for an additional position + * parameter. + * + * The inverse of this signal is the GtkContainer::removed signal. + * + * Since: 3.2 + **/ + menu_shell_signals[INSERT] = + g_signal_new (I_("insert"), + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GtkMenuShellClass, insert), + NULL, NULL, + _gtk_marshal_VOID__OBJECT_INT, + G_TYPE_NONE, 2, GTK_TYPE_WIDGET, G_TYPE_INT); + + binding_set = gtk_binding_set_by_class (klass); gtk_binding_entry_add_signal (binding_set, GDK_KEY_Escape, 0, @@ -364,6 +451,8 @@ gtk_menu_shell_class_init (GtkMenuShellClass *klass) TRUE, GTK_PARAM_READWRITE)); + gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_MENU_SHELL_ACCESSIBLE); + g_type_class_add_private (object_class, sizeof (GtkMenuShellPrivate)); } @@ -446,6 +535,14 @@ gtk_menu_shell_dispose (GObject *object) G_OBJECT_CLASS (gtk_menu_shell_parent_class)->dispose (object); } +/** + * gtk_menu_shell_append: + * @menu_shell: a #GtkMenuShell + * @child: The #GtkMenuItem to add + * + * Adds a new #GtkMenuItem to the end of the menu shell's + * item list. + */ void gtk_menu_shell_append (GtkMenuShell *menu_shell, GtkWidget *child) @@ -453,6 +550,14 @@ gtk_menu_shell_append (GtkMenuShell *menu_shell, gtk_menu_shell_insert (menu_shell, child, -1); } +/** + * gtk_menu_shell_prepend: + * @menu_shell: a #GtkMenuShell + * @child: The #GtkMenuItem to add + * + * Adds a new #GtkMenuItem to the beginning of the menu shell's + * item list. + */ void gtk_menu_shell_prepend (GtkMenuShell *menu_shell, GtkWidget *child) @@ -460,20 +565,25 @@ gtk_menu_shell_prepend (GtkMenuShell *menu_shell, gtk_menu_shell_insert (menu_shell, child, 0); } +/** + * gtk_menu_shell_insert: + * @menu_shell: a #GtkMenuShell + * @child: The #GtkMenuItem to add + * @position: The position in the item list where @child + * is added. Positions are numbered from 0 to n-1 + * + * Adds a new #GtkMenuItem to the menu shell's item list + * at the position indicated by @position. + */ void gtk_menu_shell_insert (GtkMenuShell *menu_shell, GtkWidget *child, gint position) { - GtkMenuShellClass *class; - g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell)); g_return_if_fail (GTK_IS_MENU_ITEM (child)); - class = GTK_MENU_SHELL_GET_CLASS (menu_shell); - - if (class->insert) - class->insert (menu_shell, child, position); + g_signal_emit (menu_shell, menu_shell_signals[INSERT], 0, child, position); } static void @@ -488,12 +598,22 @@ gtk_menu_shell_real_insert (GtkMenuShell *menu_shell, gtk_widget_set_parent (child, GTK_WIDGET (menu_shell)); } +/** + * gtk_menu_shell_deactivate: + * @menu_shell: a #GtkMenuShell + * + * Deactivates the menu shell. + * + * Typically this results in the menu shell being erased + * from the screen. + */ void gtk_menu_shell_deactivate (GtkMenuShell *menu_shell) { g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell)); - g_signal_emit (menu_shell, menu_shell_signals[DEACTIVATE], 0); + if (menu_shell->priv->active) + g_signal_emit (menu_shell, menu_shell_signals[DEACTIVATE], 0); } static void @@ -503,6 +623,7 @@ gtk_menu_shell_realize (GtkWidget *widget) GdkWindow *window; GdkWindowAttr attributes; gint attributes_mask; + GtkStyleContext *context; gtk_widget_set_realized (widget, TRUE); @@ -528,14 +649,14 @@ gtk_menu_shell_realize (GtkWidget *widget) window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask); gtk_widget_set_window (widget, window); - gdk_window_set_user_data (window, widget); + gtk_widget_register_window (widget, window); - gtk_widget_style_attach (widget); - gtk_style_set_background (gtk_widget_get_style (widget), window, GTK_STATE_NORMAL); + context = gtk_widget_get_style_context (widget); + gtk_style_context_set_background (context, window); } -void -_gtk_menu_shell_activate (GtkMenuShell *menu_shell) +static void +gtk_menu_shell_activate (GtkMenuShell *menu_shell) { GtkMenuShellPrivate *priv = menu_shell->priv; @@ -603,7 +724,7 @@ gtk_menu_shell_button_press (GtkWidget *widget, gtk_widget_get_parent (menu_item) == widget && menu_item != priv->active_menu_item) { - _gtk_menu_shell_activate (menu_shell); + gtk_menu_shell_activate (menu_shell); priv->button = event->button; if (GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement == GTK_TOP_BOTTOM) @@ -645,9 +766,10 @@ gtk_menu_shell_button_press (GtkWidget *widget, } } - if (menu_item && _gtk_menu_item_is_selectable (menu_item) && - GTK_MENU_ITEM (menu_item)->submenu != NULL && - !gtk_widget_get_visible (GTK_MENU_ITEM (menu_item)->submenu)) + if (menu_item && + _gtk_menu_item_is_selectable (menu_item) && + GTK_MENU_ITEM (menu_item)->priv->submenu != NULL && + !gtk_widget_get_visible (GTK_MENU_ITEM (menu_item)->priv->submenu)) { _gtk_menu_item_popup_submenu (menu_item, FALSE); priv->activated_submenu = TRUE; @@ -701,7 +823,7 @@ gtk_menu_shell_button_release (GtkWidget *widget, if (menu_item && (priv->active_menu_item == menu_item) && _gtk_menu_item_is_selectable (menu_item)) { - GtkWidget *submenu = GTK_MENU_ITEM (menu_item)->submenu; + GtkWidget *submenu = GTK_MENU_ITEM (menu_item)->priv->submenu; if (submenu == NULL) { @@ -943,7 +1065,7 @@ gtk_menu_shell_enter_notify (GtkWidget *widget, if (event->detail != GDK_NOTIFY_INFERIOR) { - if (gtk_widget_get_state (menu_item) != GTK_STATE_PRELIGHT) + if ((gtk_widget_get_state_flags (menu_item) & GTK_STATE_FLAG_PRELIGHT) == 0) gtk_menu_shell_select_item (menu_shell, menu_item); /* If any mouse button is down, and there is a submenu @@ -955,19 +1077,17 @@ gtk_menu_shell_enter_notify (GtkWidget *widget, * its submenu. */ if ((event->state & (GDK_BUTTON1_MASK|GDK_BUTTON2_MASK|GDK_BUTTON3_MASK)) && - GTK_MENU_ITEM (menu_item)->submenu != NULL) + GTK_MENU_ITEM (menu_item)->priv->submenu != NULL) { GTK_MENU_SHELL (parent)->priv->activated_submenu = TRUE; - if (!gtk_widget_get_visible (GTK_MENU_ITEM (menu_item)->submenu)) + if (!gtk_widget_get_visible (GTK_MENU_ITEM (menu_item)->priv->submenu)) { - gboolean touchscreen_mode; + GdkDevice *source_device; - g_object_get (gtk_widget_get_settings (widget), - "gtk-touchscreen-mode", &touchscreen_mode, - NULL); + source_device = gdk_event_get_source_device ((GdkEvent *) event); - if (touchscreen_mode) + if (gdk_device_get_source (source_device) == GDK_SOURCE_TOUCHSCREEN) _gtk_menu_item_popup_submenu (menu_item, TRUE); } } @@ -987,7 +1107,7 @@ gtk_menu_shell_leave_notify (GtkWidget *widget, GdkEventCrossing *event) { if (event->mode == GDK_CROSSING_GTK_GRAB || - event->mode == GDK_CROSSING_GTK_GRAB || + event->mode == GDK_CROSSING_GTK_UNGRAB || event->mode == GDK_CROSSING_STATE_CHANGED) return TRUE; @@ -1010,10 +1130,10 @@ gtk_menu_shell_leave_notify (GtkWidget *widget, } if ((priv->active_menu_item == event_widget) && - (menu_item->submenu == NULL)) + (menu_item->priv->submenu == NULL)) { if ((event->detail != GDK_NOTIFY_INFERIOR) && - (gtk_widget_get_state (GTK_WIDGET (menu_item)) != GTK_STATE_NORMAL)) + (gtk_widget_get_state_flags (GTK_WIDGET (menu_item)) & GTK_STATE_FLAG_PRELIGHT) != 0) { gtk_menu_shell_deselect (menu_shell); } @@ -1095,7 +1215,6 @@ gtk_real_menu_shell_deactivate (GtkMenuShell *menu_shell) if (priv->active) { - priv->button = 0; priv->active = FALSE; priv->activate_time = 0; @@ -1170,6 +1289,13 @@ gtk_menu_shell_get_item (GtkMenuShell *menu_shell, /* Handlers for action signals */ +/** + * gtk_menu_shell_select_item: + * @menu_shell: a #GtkMenuShell + * @menu_item: The #GtkMenuItem to select + * + * Selects the menu item from the menu shell. + */ void gtk_menu_shell_select_item (GtkMenuShell *menu_shell, GtkWidget *menu_item) @@ -1211,6 +1337,8 @@ gtk_menu_shell_real_select_item (GtkMenuShell *menu_shell, return; } + gtk_menu_shell_activate (menu_shell); + priv->active_menu_item = menu_item; if (pack_dir == GTK_PACK_DIRECTION_TTB || pack_dir == GTK_PACK_DIRECTION_BTT) _gtk_menu_item_set_placement (GTK_MENU_ITEM (priv->active_menu_item), @@ -1225,10 +1353,17 @@ gtk_menu_shell_real_select_item (GtkMenuShell *menu_shell, /* This allows the bizarre radio buttons-with-submenus-display-history * behavior */ - if (GTK_MENU_ITEM (priv->active_menu_item)->submenu) + if (GTK_MENU_ITEM (priv->active_menu_item)->priv->submenu) gtk_widget_activate (priv->active_menu_item); } +/** + * gtk_menu_shell_deselect: + * @menu_shell: a #GtkMenuShell + * + * Deselects the currently selected item from the menu shell, + * if any. + */ void gtk_menu_shell_deselect (GtkMenuShell *menu_shell) { @@ -1244,6 +1379,15 @@ gtk_menu_shell_deselect (GtkMenuShell *menu_shell) } } +/** + * gtk_menu_shell_activate_item: + * @menu_shell: a #GtkMenuShell + * @menu_item: the #GtkMenuItem to activate + * @force_deactivate: if %TRUE, force the deactivation of the + * menu shell after the menu item is activated + * + * Activates the menu item within the menu shell. + */ void gtk_menu_shell_activate_item (GtkMenuShell *menu_shell, GtkWidget *menu_item, @@ -1447,11 +1591,11 @@ gtk_menu_shell_select_submenu_first (GtkMenuShell *menu_shell) menu_item = GTK_MENU_ITEM (priv->active_menu_item); - if (menu_item->submenu) + if (menu_item->priv->submenu) { _gtk_menu_item_popup_submenu (GTK_WIDGET (menu_item), FALSE); - gtk_menu_shell_select_first (GTK_MENU_SHELL (menu_item->submenu), TRUE); - if (GTK_MENU_SHELL (menu_item->submenu)->priv->active_menu_item) + gtk_menu_shell_select_first (GTK_MENU_SHELL (menu_item->priv->submenu), TRUE); + if (GTK_MENU_SHELL (menu_item->priv->submenu)->priv->active_menu_item) return TRUE; } @@ -1465,45 +1609,19 @@ gtk_real_menu_shell_move_current (GtkMenuShell *menu_shell, GtkMenuShellPrivate *priv = menu_shell->priv; GtkMenuShell *parent_menu_shell = NULL; gboolean had_selection; - gboolean touchscreen_mode; priv->in_unselectable_item = FALSE; had_selection = priv->active_menu_item != NULL; - g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu_shell)), - "gtk-touchscreen-mode", &touchscreen_mode, - NULL); - if (priv->parent_menu_shell) parent_menu_shell = GTK_MENU_SHELL (priv->parent_menu_shell); switch (direction) { case GTK_MENU_DIR_PARENT: - if (touchscreen_mode && - priv->active_menu_item && - GTK_MENU_ITEM (priv->active_menu_item)->submenu && - gtk_widget_get_visible (GTK_MENU_ITEM (priv->active_menu_item)->submenu)) - { - /* if we are on a menu item that has an open submenu but the - * focus is not in that submenu (e.g. because it's empty or - * has only insensitive items), close that submenu instead of - * running into the code below which would close *this* menu. - */ - _gtk_menu_item_popdown_submenu (priv->active_menu_item); - _gtk_menu_shell_update_mnemonics (menu_shell); - } - else if (parent_menu_shell) + if (parent_menu_shell) { - if (touchscreen_mode) - { - /* close menu when returning from submenu. */ - _gtk_menu_item_popdown_submenu (GTK_MENU (menu_shell)->priv->parent_menu_item); - _gtk_menu_shell_update_mnemonics (parent_menu_shell); - break; - } - if (GTK_MENU_SHELL_GET_CLASS (parent_menu_shell)->submenu_placement == GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement) gtk_menu_shell_deselect (menu_shell); @@ -1522,9 +1640,9 @@ gtk_real_menu_shell_move_current (GtkMenuShell *menu_shell, */ else if (priv->active_menu_item && _gtk_menu_item_is_selectable (priv->active_menu_item) && - GTK_MENU_ITEM (priv->active_menu_item)->submenu) + GTK_MENU_ITEM (priv->active_menu_item)->priv->submenu) { - GtkMenuShell *submenu = GTK_MENU_SHELL (GTK_MENU_ITEM (priv->active_menu_item)->submenu); + GtkMenuShell *submenu = GTK_MENU_SHELL (GTK_MENU_ITEM (priv->active_menu_item)->priv->submenu); if (GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement != GTK_MENU_SHELL_GET_CLASS (submenu)->submenu_placement) @@ -1535,7 +1653,7 @@ gtk_real_menu_shell_move_current (GtkMenuShell *menu_shell, case GTK_MENU_DIR_CHILD: if (priv->active_menu_item && _gtk_menu_item_is_selectable (priv->active_menu_item) && - GTK_MENU_ITEM (priv->active_menu_item)->submenu) + GTK_MENU_ITEM (priv->active_menu_item)->priv->submenu) { if (gtk_menu_shell_select_submenu_first (menu_shell)) break; @@ -1562,17 +1680,13 @@ gtk_real_menu_shell_move_current (GtkMenuShell *menu_shell, case GTK_MENU_DIR_PREV: gtk_menu_shell_move_selected (menu_shell, -1); - if (!had_selection && - !priv->active_menu_item && - priv->children) + if (!had_selection && !priv->active_menu_item && priv->children) _gtk_menu_shell_select_last (menu_shell, TRUE); break; case GTK_MENU_DIR_NEXT: gtk_menu_shell_move_selected (menu_shell, 1); - if (!had_selection && - !priv->active_menu_item && - priv->children) + if (!had_selection && !priv->active_menu_item && priv->children) gtk_menu_shell_select_first (menu_shell, TRUE); break; } @@ -1587,7 +1701,7 @@ gtk_real_menu_shell_activate_current (GtkMenuShell *menu_shell, if (priv->active_menu_item && _gtk_menu_item_is_selectable (priv->active_menu_item)) { - if (GTK_MENU_ITEM (priv->active_menu_item)->submenu == NULL) + if (GTK_MENU_ITEM (priv->active_menu_item)->priv->submenu == NULL) gtk_menu_shell_activate_item (menu_shell, priv->active_menu_item, force_hide); @@ -1609,12 +1723,10 @@ static void gtk_real_menu_shell_cycle_focus (GtkMenuShell *menu_shell, GtkDirectionType dir) { - GtkMenuShellPrivate *priv = menu_shell->priv; - while (menu_shell && !GTK_IS_MENU_BAR (menu_shell)) { - if (priv->parent_menu_shell) - menu_shell = GTK_MENU_SHELL (priv->parent_menu_shell); + if (menu_shell->priv->parent_menu_shell) + menu_shell = GTK_MENU_SHELL (menu_shell->priv->parent_menu_shell); else menu_shell = NULL; } @@ -1745,8 +1857,11 @@ gtk_menu_shell_activate_mnemonic (GtkMenuShell *menu_shell, event->group); if (entries) - result = _gtk_mnemonic_hash_activate (mnemonic_hash, - GPOINTER_TO_UINT (entries->data)); + { + result = _gtk_mnemonic_hash_activate (mnemonic_hash, + GPOINTER_TO_UINT (entries->data)); + g_slist_free (entries); + } return result; } @@ -1876,14 +1991,14 @@ gtk_menu_shell_set_take_focus (GtkMenuShell *menu_shell, * * Gets the currently selected item. * - * Returns: the currently selected item + * Returns: (transfer none): the currently selected item * * Since: 3.0 */ GtkWidget * gtk_menu_shell_get_selected_item (GtkMenuShell *menu_shell) { - g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell)); + g_return_val_if_fail (GTK_IS_MENU_SHELL (menu_shell), NULL); return menu_shell->priv->active_menu_item; } @@ -1897,14 +2012,14 @@ gtk_menu_shell_get_selected_item (GtkMenuShell *menu_shell) * The parent menu shell of a submenu is the #GtkMenu or #GtkMenuBar * from which it was opened up. * - * Returns: the parent #GtkMenuShell + * Returns: (transfer none): the parent #GtkMenuShell * * Since: 3.0 */ GtkWidget * gtk_menu_shell_get_parent_shell (GtkMenuShell *menu_shell) { - g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell)); + g_return_val_if_fail (GTK_IS_MENU_SHELL (menu_shell), NULL); return menu_shell->priv->parent_menu_shell; }