diff options
-rw-r--r-- | ChangeLog | 44 | ||||
-rw-r--r-- | docs/reference/ChangeLog | 8 | ||||
-rw-r--r-- | docs/reference/gtk/gtk-docs.sgml | 1 | ||||
-rw-r--r-- | docs/reference/gtk/gtk-sections.txt | 15 | ||||
-rw-r--r-- | docs/reference/gtk/gtk.types | 1 | ||||
-rw-r--r-- | gtk/Makefile.am | 2 | ||||
-rw-r--r-- | gtk/gtk.h | 1 | ||||
-rw-r--r-- | gtk/gtk.symbols | 56 | ||||
-rw-r--r-- | gtk/gtkaction.c | 712 | ||||
-rw-r--r-- | gtk/gtkaction.h | 25 | ||||
-rw-r--r-- | gtk/gtkactiongroup.c | 7 | ||||
-rw-r--r-- | gtk/gtkactivatable.c | 540 | ||||
-rw-r--r-- | gtk/gtkactivatable.h | 88 | ||||
-rw-r--r-- | gtk/gtkbutton.c | 228 | ||||
-rw-r--r-- | gtk/gtkcheckmenuitem.c | 100 | ||||
-rw-r--r-- | gtk/gtkimagemenuitem.c | 142 | ||||
-rw-r--r-- | gtk/gtkmenuitem.c | 238 | ||||
-rw-r--r-- | gtk/gtkradioaction.c | 3 | ||||
-rw-r--r-- | gtk/gtkradiomenuitem.c | 10 | ||||
-rw-r--r-- | gtk/gtkrecentaction.c | 84 | ||||
-rw-r--r-- | gtk/gtkrecentchooser.c | 151 | ||||
-rw-r--r-- | gtk/gtkrecentchooserdefault.c | 83 | ||||
-rw-r--r-- | gtk/gtkrecentchoosermenu.c | 63 | ||||
-rw-r--r-- | gtk/gtkrecentchooserprivate.h | 27 | ||||
-rw-r--r-- | gtk/gtktoggleaction.c | 75 | ||||
-rw-r--r-- | gtk/gtktogglebutton.c | 66 | ||||
-rw-r--r-- | gtk/gtktoggletoolbutton.c | 65 | ||||
-rw-r--r-- | gtk/gtktoolbutton.c | 170 | ||||
-rw-r--r-- | gtk/gtktoolitem.c | 176 |
29 files changed, 2368 insertions, 813 deletions
@@ -1,5 +1,49 @@ 2009-01-23 Matthias Clasen <mclasen@redhat.com> + Bug 560228 – Add "action-controller" property to GtkWidgetClass + + Rework the way actions and proxies interact, to make the + interaction less ad hoc, more extensible, and better suited + for support in GUI builders like glade. + + To be used as a proxy, a widget must now implement the + GtkActivatable interface, and GtkActivatable implementations + are responsible for syncing their appearance with the action + and for activating the action. + + All the widgets that are commonly used as proxies implement + GtkActivatable now. + + Patch by Tristan van Berkom. + + * gtk/gtkactivatable.[hc]: The GtkActivatable interface. + + * gtk/gtkbutton.c: + * gtk/gtktogglebutton.c: + * gtk/gtktoolitem.c: + * gtk/gtktoolbutton.c: + * gtk/gtktoggletoolbutton.c: + * gtk/gtkmenuitem.c: + * gtk/gtkcheckmenuitem.c: + * gtk/gtkimagemenuitem.c: + * gtk/gtkradiomenuitem.c: + * gtk/gtkrecentchooserprivate.h: + * gtk/gtkrecentchooser.c: + * gtk/gtkrecentchooserdefault.c: + * gtk/gtkrecentchoosermenu.c: Implement GtkActivatable. + * gtk/gtkaction.[hc]: Move appearance synchronization to + GtkActivatable implementations. + + * gtk/gtkradioaction.c: + * gtk/gtkrecentaction.c: + * gtk/gtktoggleaction.c: + * gtk/gtkactiongroup.c: Adapt. + + * gtk/gtk.h: Include gtkactivatable.h + * gtk/gtk.symbols: Add new functions + +2009-01-23 Matthias Clasen <mclasen@redhat.com> + Bug 567124 – proposal to delay doing something related to immodule until widgets realized diff --git a/docs/reference/ChangeLog b/docs/reference/ChangeLog index 4e9331027a..0df5972956 100644 --- a/docs/reference/ChangeLog +++ b/docs/reference/ChangeLog @@ -1,5 +1,13 @@ 2009-01-23 Matthias Clasen <mclasen@redhat.com> + * gtk/gtk-sections.txt: Add new GtkActivatable API + + * gtk/gtk-docs.sgml: Include GtkActivatable section + + * gtk.types: Add gtk_activatable_get_type + +2009-01-23 Matthias Clasen <mclasen@redhat.com> + * gtk/gtk-sections.txt: Additions 2009-01-23 Matthias Clasen <mclasen@redhat.com> diff --git a/docs/reference/gtk/gtk-docs.sgml b/docs/reference/gtk/gtk-docs.sgml index 51afa822d3..dd203f6653 100644 --- a/docs/reference/gtk/gtk-docs.sgml +++ b/docs/reference/gtk/gtk-docs.sgml @@ -247,6 +247,7 @@ that is, GUI components such as #GtkButton or #GtkTextView. <xi:include href="xml/gtktoggleaction.xml" /> <xi:include href="xml/gtkradioaction.xml" /> <xi:include href="xml/gtkrecentaction.xml" /> + <xi:include href="xml/gtkactivatable.xml" /> </chapter> <chapter id="SelectorWidgets"> diff --git a/docs/reference/gtk/gtk-sections.txt b/docs/reference/gtk/gtk-sections.txt index 10abe573a2..6f1191d07e 100644 --- a/docs/reference/gtk/gtk-sections.txt +++ b/docs/reference/gtk/gtk-sections.txt @@ -188,6 +188,8 @@ gtk_action_disconnect_proxy gtk_action_get_proxies gtk_action_connect_accelerator gtk_action_disconnect_accelerator +gtk_action_block_activate +gtk_action_unblock_activate gtk_action_block_activate_from gtk_action_unblock_activate_from gtk_action_get_accel_path @@ -227,6 +229,19 @@ GtkActionPrivate </SECTION> <SECTION> +<FILE>gtkactivatable</FILE> +<TITLE>GtkActivatable</TITLE> +gtk_activatable_do_set_related_action +gtk_activatable_get_related_action +gtk_activatable_get_use_action_appearance +gtk_activatable_reset +gtk_activatable_set_related_action +gtk_activatable_set_use_action_appearance +<SUBSECTION Private> +gtk_activatable_get_type +</SECTION> + +<SECTION> <FILE>gtkactiongroup</FILE> <TITLE>GtkActionGroup</TITLE> GtkActionGroup diff --git a/docs/reference/gtk/gtk.types b/docs/reference/gtk/gtk.types index 6481be9895..68e2e52bfe 100644 --- a/docs/reference/gtk/gtk.types +++ b/docs/reference/gtk/gtk.types @@ -8,6 +8,7 @@ gtk_accel_map_get_type gtk_accessible_get_type gtk_action_get_type gtk_action_group_get_type +gtk_activatable_get_type gtk_adjustment_get_type gtk_alignment_get_type gtk_arrow_get_type diff --git a/gtk/Makefile.am b/gtk/Makefile.am index a3e9eedc3a..2339a86543 100644 --- a/gtk/Makefile.am +++ b/gtk/Makefile.am @@ -148,6 +148,7 @@ gtk_public_h_sources = \ gtkaccessible.h \ gtkaction.h \ gtkactiongroup.h \ + gtkactivatable.h \ gtkadjustment.h \ gtkalignment.h \ gtkarrow.h \ @@ -397,6 +398,7 @@ gtk_base_c_sources = \ gtkaccessible.c \ gtkaction.c \ gtkactiongroup.c \ + gtkactivatable.c \ gtkadjustment.c \ gtkalignment.c \ gtkarrow.c \ @@ -37,6 +37,7 @@ #include <gtk/gtkaccessible.h> #include <gtk/gtkaction.h> #include <gtk/gtkactiongroup.h> +#include <gtk/gtkactivatable.h> #include <gtk/gtkadjustment.h> #include <gtk/gtkalignment.h> #include <gtk/gtkarrow.h> diff --git a/gtk/gtk.symbols b/gtk/gtk.symbols index 777cb0aac7..2ca4a6a7d5 100644 --- a/gtk/gtk.symbols +++ b/gtk/gtk.symbols @@ -144,49 +144,61 @@ gtk_accessible_get_type G_GNUC_CONST #if IN_HEADER(__GTK_ACTION_H__) #if IN_FILE(__GTK_ACTION_C__) gtk_action_activate +gtk_action_block_activate +#ifndef GTK_DISABLE_DEPRECATED gtk_action_block_activate_from +#endif gtk_action_connect_accelerator +#ifndef GTK_DISABLE_DEPRECATED gtk_action_connect_proxy +#endif gtk_action_create_icon gtk_action_create_menu_item gtk_action_create_tool_item gtk_action_create_menu gtk_action_disconnect_accelerator +#ifndef GTK_DISABLE_DEPRECATED gtk_action_disconnect_proxy +#endif gtk_action_get_accel_closure gtk_action_get_accel_path +gtk_action_get_gicon +gtk_action_get_label gtk_action_get_name gtk_action_get_proxies +#ifndef GTK_DISABLE_DEPRECATED gtk_widget_get_action +#endif +gtk_action_get_icon_name +gtk_action_get_is_important gtk_action_get_sensitive +gtk_action_get_short_label +gtk_action_get_stock_id +gtk_action_get_tooltip gtk_action_get_type G_GNUC_CONST gtk_action_get_visible +gtk_action_get_visible_horizontal +gtk_action_get_visible_vertical gtk_action_is_sensitive gtk_action_is_visible gtk_action_new gtk_action_set_accel_group gtk_action_set_accel_path -gtk_action_set_sensitive -gtk_action_set_visible -gtk_action_unblock_activate_from +gtk_action_set_gicon +gtk_action_set_icon_name +gtk_action_set_is_important gtk_action_set_label -gtk_action_get_label +gtk_action_set_sensitive gtk_action_set_short_label -gtk_action_get_short_label -gtk_action_set_tooltip -gtk_action_get_tooltip gtk_action_set_stock_id -gtk_action_get_stock_id -gtk_action_set_gicon -gtk_action_get_gicon -gtk_action_set_icon_name -gtk_action_get_icon_name +gtk_action_set_tooltip +gtk_action_set_visible gtk_action_set_visible_horizontal -gtk_action_get_visible_horizontal gtk_action_set_visible_vertical -gtk_action_get_visible_vertical -gtk_action_set_is_important -gtk_action_get_is_important +gtk_action_unblock_activate +#ifndef GTK_DISABLE_DEPRECATED +gtk_action_unblock_activate_from +#endif #endif #endif @@ -216,6 +228,18 @@ gtk_action_group_translate_string #endif #endif +#if IN_HEADER(__GTK_ACTIVATABLE_H__) +#if IN_FILE(__GTK_ACTIVATABLE_C__) +gtk_activatable_do_set_related_action +gtk_activatable_get_related_action +gtk_activatable_get_type G_GNUC_CONST +gtk_activatable_get_use_action_appearance +gtk_activatable_reset +gtk_activatable_set_related_action +gtk_activatable_set_use_action_appearance +#endif +#endif + #if IN_HEADER(__GTK_ADJUSTMENT_H__) #if IN_FILE(__GTK_ADJUSTMENT_C__) gtk_adjustment_changed diff --git a/gtk/gtkaction.c b/gtk/gtkaction.c index 59c52d3552..6d29ca4e34 100644 --- a/gtk/gtkaction.c +++ b/gtk/gtkaction.c @@ -47,6 +47,7 @@ #include "gtktoolbar.h" #include "gtkprivate.h" #include "gtkbuildable.h" +#include "gtkactivatable.h" #include "gtkalias.h" @@ -71,6 +72,8 @@ struct _GtkActionPrivate guint is_important : 1; guint hide_if_empty : 1; guint visible_overflown : 1; + guint recursion_guard : 1; + guint activate_blocked : 1; /* accelerator */ guint accel_count; @@ -120,10 +123,6 @@ G_DEFINE_TYPE_WITH_CODE (GtkAction, gtk_action, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, gtk_action_buildable_init)) - -static GQuark quark_gtk_action_proxy = 0; -static const gchar gtk_action_proxy_key[] = "gtk-action"; - static void gtk_action_finalize (GObject *object); static void gtk_action_set_property (GObject *object, guint prop_id, @@ -135,8 +134,6 @@ static void gtk_action_get_property (GObject *object, GParamSpec *pspec); static void gtk_action_set_action_group (GtkAction *action, GtkActionGroup *action_group); -static void gtk_action_sync_tooltip (GtkAction *action, - GtkWidget *proxy); static GtkWidget *create_menu_item (GtkAction *action); static GtkWidget *create_tool_item (GtkAction *action); @@ -144,7 +141,7 @@ static void connect_proxy (GtkAction *action, GtkWidget *proxy); static void disconnect_proxy (GtkAction *action, GtkWidget *proxy); - + static void closure_accel_activate (GClosure *closure, GValue *return_value, guint n_param_values, @@ -160,8 +157,6 @@ gtk_action_class_init (GtkActionClass *klass) { GObjectClass *gobject_class; - quark_gtk_action_proxy = g_quark_from_static_string (gtk_action_proxy_key); - gobject_class = G_OBJECT_CLASS (klass); gobject_class->finalize = gtk_action_finalize; @@ -170,14 +165,13 @@ gtk_action_class_init (GtkActionClass *klass) klass->activate = NULL; - klass->create_menu_item = create_menu_item; - klass->create_tool_item = create_tool_item; - klass->create_menu = NULL; - klass->connect_proxy = connect_proxy; - klass->disconnect_proxy = disconnect_proxy; - - klass->menu_item_type = GTK_TYPE_IMAGE_MENU_ITEM; + klass->create_menu_item = create_menu_item; + klass->create_tool_item = create_tool_item; + klass->create_menu = NULL; + klass->menu_item_type = GTK_TYPE_IMAGE_MENU_ITEM; klass->toolbar_item_type = GTK_TYPE_TOOL_BUTTON; + klass->connect_proxy = connect_proxy; + klass->disconnect_proxy = disconnect_proxy; g_object_class_install_property (gobject_class, PROP_NAME, @@ -194,6 +188,9 @@ gtk_action_class_init (GtkActionClass *klass) * The label used for menu items and buttons that activate * this action. If the label is %NULL, GTK+ uses the stock * label specified via the stock-id property. + * + * This is an appearance property and thus only applies if + * #GtkActivatable:use-action-appearance is %TRUE. */ g_object_class_install_property (gobject_class, PROP_LABEL, @@ -208,6 +205,9 @@ gtk_action_class_init (GtkActionClass *klass) * GtkAction:short-label: * * A shorter label that may be used on toolbar buttons. + * + * This is an appearance property and thus only applies if + * #GtkActivatable:use-action-appearance is %TRUE. */ g_object_class_install_property (gobject_class, PROP_SHORT_LABEL, @@ -230,6 +230,9 @@ gtk_action_class_init (GtkActionClass *klass) * GtkAction:stock-id: * * The stock icon displayed in widgets representing this action. + * + * This is an appearance property and thus only applies if + * #GtkActivatable:use-action-appearance is %TRUE. */ g_object_class_install_property (gobject_class, PROP_STOCK_ID, @@ -247,6 +250,9 @@ gtk_action_class_init (GtkActionClass *klass) * Note that the stock icon is preferred, if the #GtkAction:stock-id * property holds the id of an existing stock icon. * + * This is an appearance property and thus only applies if + * #GtkActivatable:use-action-appearance is %TRUE. + * * Since: 2.16 */ g_object_class_install_property (gobject_class, @@ -265,6 +271,9 @@ gtk_action_class_init (GtkActionClass *klass) * property holds the id of an existing stock icon, and the #GIcon is * preferred if the #GtkAction:gicon property is set. * + * This is an appearance property and thus only applies if + * #GtkActivatable:use-action-appearance is %TRUE. + * * Since: 2.10 */ g_object_class_install_property (gobject_class, @@ -381,6 +390,7 @@ gtk_action_init (GtkAction *action) action->private_data->visible_overflown = TRUE; action->private_data->is_important = FALSE; action->private_data->hide_if_empty = TRUE; + action->private_data->activate_blocked = FALSE; action->private_data->sensitive = TRUE; action->private_data->visible = TRUE; @@ -634,12 +644,30 @@ static void remove_proxy (GtkAction *action, GtkWidget *proxy) { - if (GTK_IS_MENU_ITEM (proxy)) - gtk_action_disconnect_accelerator (action); - action->private_data->proxies = g_slist_remove (action->private_data->proxies, proxy); } +static void +connect_proxy (GtkAction *action, + GtkWidget *proxy) +{ + action->private_data->proxies = g_slist_prepend (action->private_data->proxies, proxy); + + if (action->private_data->action_group) + _gtk_action_group_emit_connect_proxy (action->private_data->action_group, action, proxy); + +} + +static void +disconnect_proxy (GtkAction *action, + GtkWidget *proxy) +{ + remove_proxy (action, proxy); + + if (action->private_data->action_group) + _gtk_action_group_emit_disconnect_proxy (action->private_data->action_group, action, proxy); +} + /** * _gtk_action_sync_menu_visible: * @action: a #GtkAction, or %NULL to determine the action from @proxy @@ -670,7 +698,7 @@ _gtk_action_sync_menu_visible (GtkAction *action, g_return_if_fail (action == NULL || GTK_IS_ACTION (action)); if (action == NULL) - action = g_object_get_qdata (G_OBJECT (proxy), quark_gtk_action_proxy); + action = gtk_activatable_get_related_action (GTK_ACTIVATABLE (proxy)); if (action) { @@ -685,257 +713,6 @@ _gtk_action_sync_menu_visible (GtkAction *action, gtk_widget_hide (proxy); } -gboolean _gtk_menu_is_empty (GtkWidget *menu); - -static gboolean -gtk_action_create_menu_proxy (GtkToolItem *tool_item, - GtkAction *action) -{ - GtkWidget *menu_item; - - if (action->private_data->visible_overflown) - { - menu_item = gtk_action_create_menu_item (action); - - g_object_ref_sink (menu_item); - - gtk_tool_item_set_proxy_menu_item (tool_item, - "gtk-action-menu-item", menu_item); - g_object_unref (menu_item); - } - else - gtk_tool_item_set_proxy_menu_item (tool_item, - "gtk-action-menu-item", NULL); - - return TRUE; -} - -static void -connect_proxy (GtkAction *action, - GtkWidget *proxy) -{ - g_object_ref (action); - g_object_set_qdata_full (G_OBJECT (proxy), quark_gtk_action_proxy, action, - g_object_unref); - - /* add this widget to the list of proxies */ - action->private_data->proxies = g_slist_prepend (action->private_data->proxies, proxy); - g_object_weak_ref (G_OBJECT (proxy), (GWeakNotify)remove_proxy, action); - - gtk_widget_set_sensitive (proxy, gtk_action_is_sensitive (action)); - if (gtk_action_is_visible (action)) - gtk_widget_show (proxy); - else - gtk_widget_hide (proxy); - gtk_widget_set_no_show_all (proxy, TRUE); - - if (GTK_IS_MENU_ITEM (proxy)) - { - GtkWidget *label; - /* menu item specific synchronisers ... */ - - if (action->private_data->accel_quark) - { - gtk_action_connect_accelerator (action); - gtk_menu_item_set_accel_path (GTK_MENU_ITEM (proxy), - g_quark_to_string (action->private_data->accel_quark)); - } - - label = GTK_BIN (proxy)->child; - - /* make sure label is a label */ - if (label && !GTK_IS_LABEL (label)) - { - gtk_container_remove (GTK_CONTAINER (proxy), label); - label = NULL; - } - - if (!label) - label = g_object_new (GTK_TYPE_ACCEL_LABEL, - "use-underline", TRUE, - "xalign", 0.0, - "visible", TRUE, - "parent", proxy, - NULL); - - if (GTK_IS_ACCEL_LABEL (label) && action->private_data->accel_quark) - g_object_set (label, - "accel-closure", action->private_data->accel_closure, - NULL); - - gtk_label_set_label (GTK_LABEL (label), action->private_data->label); - - if (GTK_IS_IMAGE_MENU_ITEM (proxy)) - { - GtkWidget *image; - - image = gtk_image_menu_item_get_image (GTK_IMAGE_MENU_ITEM (proxy)); - if (image && !GTK_IS_IMAGE (image)) - { - gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (proxy), NULL); - image = NULL; - } - if (!image) - { - image = gtk_image_new (); - gtk_widget_show (image); - gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (proxy), - image); - } - - if (action->private_data->stock_id && - gtk_icon_factory_lookup_default (action->private_data->stock_id)) - gtk_image_set_from_stock (GTK_IMAGE (image), - action->private_data->stock_id, GTK_ICON_SIZE_MENU); - else if (action->private_data->gicon) - gtk_image_set_from_gicon (GTK_IMAGE (image), - action->private_data->gicon, GTK_ICON_SIZE_MENU); - else if (action->private_data->icon_name) - gtk_image_set_from_icon_name (GTK_IMAGE (image), - action->private_data->icon_name, GTK_ICON_SIZE_MENU); - else - gtk_image_clear (GTK_IMAGE (image)); - } - - if (gtk_menu_item_get_submenu (GTK_MENU_ITEM (proxy)) == NULL) - g_signal_connect_object (proxy, "activate", - G_CALLBACK (gtk_action_activate), action, - G_CONNECT_SWAPPED); - - } - else if (GTK_IS_TOOL_ITEM (proxy)) - { - /* toolbar button specific synchronisers ... */ - if (GTK_IS_TOOL_BUTTON (proxy)) - { - GtkWidget *image; - GtkIconSize icon_size; - - g_object_set (proxy, - "visible-horizontal", action->private_data->visible_horizontal, - "visible-vertical", action->private_data->visible_vertical, - "is-important", action->private_data->is_important, - "label", action->private_data->short_label, - "use-underline", TRUE, - "stock-id", action->private_data->stock_id, - "icon-name", action->private_data->icon_name, - NULL); - - if (action->private_data->stock_id && - gtk_icon_factory_lookup_default (action->private_data->stock_id)) - { - /* use the stock icon */ - gtk_tool_button_set_icon_widget (GTK_TOOL_BUTTON (proxy), NULL); - } - else if (action->private_data->gicon) - { - icon_size = gtk_tool_item_get_icon_size (GTK_TOOL_ITEM (proxy)); - image = gtk_tool_button_get_icon_widget (GTK_TOOL_BUTTON (proxy)); - if (!image) - { - image = gtk_image_new (); - gtk_widget_show (image); - gtk_tool_button_set_icon_widget (GTK_TOOL_BUTTON (proxy), - image); - } - - gtk_image_set_from_gicon (GTK_IMAGE (image), - action->private_data->gicon, - icon_size); - } - - g_signal_connect_object (proxy, "clicked", - G_CALLBACK (gtk_action_activate), action, - G_CONNECT_SWAPPED); - } - else - { - g_object_set (proxy, - "visible-horizontal", action->private_data->visible_horizontal, - "visible-vertical", action->private_data->visible_vertical, - "is-important", action->private_data->is_important, - NULL); - } - - gtk_action_sync_tooltip (action, proxy); - - g_signal_connect_object (proxy, "create-menu-proxy", - G_CALLBACK (gtk_action_create_menu_proxy), - action, 0); - - gtk_tool_item_rebuild_menu (GTK_TOOL_ITEM (proxy)); - } - else if (GTK_IS_BUTTON (proxy)) - { - /* button specific synchronisers ... */ - if (gtk_button_get_use_stock (GTK_BUTTON (proxy))) - { - /* synchronise stock-id */ - g_object_set (proxy, - "label", action->private_data->stock_id, - NULL); - } - else - { - GtkWidget *image; - - image = gtk_button_get_image (GTK_BUTTON (proxy)); - - if (GTK_IS_IMAGE (image) || - GTK_BIN (proxy)->child == NULL || - GTK_IS_LABEL (GTK_BIN (proxy)->child)) - { - /* synchronise the label */ - g_object_set (proxy, - "label", action->private_data->short_label, - "use-underline", TRUE, - NULL); - } - - if (GTK_IS_IMAGE (image) && - (gtk_image_get_storage_type (GTK_IMAGE (image)) == GTK_IMAGE_EMPTY || - gtk_image_get_storage_type (GTK_IMAGE (image)) == GTK_IMAGE_GICON)) - gtk_image_set_from_gicon (GTK_IMAGE (image), - action->private_data->gicon, GTK_ICON_SIZE_MENU); - else if (GTK_IS_IMAGE (image) && - (gtk_image_get_storage_type (GTK_IMAGE (image)) == GTK_IMAGE_EMPTY || - gtk_image_get_storage_type (GTK_IMAGE (image)) == GTK_IMAGE_ICON_NAME)) - gtk_image_set_from_icon_name (GTK_IMAGE (image), - action->private_data->icon_name, GTK_ICON_SIZE_MENU); - } - /* we leave the button alone if there is a custom child */ - g_signal_connect_object (proxy, "clicked", - G_CALLBACK (gtk_action_activate), action, - G_CONNECT_SWAPPED); - } - - if (action->private_data->action_group) - _gtk_action_group_emit_connect_proxy (action->private_data->action_group, action, proxy); -} - -static void -disconnect_proxy (GtkAction *action, - GtkWidget *proxy) -{ - g_object_set_qdata (G_OBJECT (proxy), quark_gtk_action_proxy, NULL); - - g_object_weak_unref (G_OBJECT (proxy), (GWeakNotify)remove_proxy, action); - remove_proxy (action, proxy); - - /* disconnect the activate handler */ - g_signal_handlers_disconnect_by_func (proxy, - G_CALLBACK (gtk_action_activate), - action); - - /* toolbar button specific synchronisers ... */ - g_signal_handlers_disconnect_by_func (proxy, - G_CALLBACK (gtk_action_create_menu_proxy), - action); - - if (action->private_data->action_group) - _gtk_action_group_emit_disconnect_proxy (action->private_data->action_group, action, proxy); -} - void _gtk_action_emit_activate (GtkAction *action) { @@ -973,11 +750,43 @@ gtk_action_activate (GtkAction *action) { g_return_if_fail (GTK_IS_ACTION (action)); - if (gtk_action_is_sensitive (action)) + if (action->private_data->activate_blocked == FALSE && + gtk_action_is_sensitive (action)) _gtk_action_emit_activate (action); } /** + * gtk_action_block_activate: + * + * Disable activation signals from the action + * + * This is needed when updating the state of your proxy + * #GtkActivatable widget could result in calling gtk_action_activate(), + * this is a convenience function to avoid recursing in those + * cases (updating toggle state for instance). + */ +void +gtk_action_block_activate (GtkAction *action) +{ + g_return_if_fail (GTK_IS_ACTION (action)); + + action->private_data->activate_blocked = TRUE; +} + +/** + * gtk_action_unblock_activate: + * + * Reenable activation signals from the action + */ +void +gtk_action_unblock_activate (GtkAction *action) +{ + g_return_if_fail (GTK_IS_ACTION (action)); + + action->private_data->activate_blocked = FALSE; +} + +/** * gtk_action_create_icon: * @action: the action object * @icon_size: the size of the icon that should be created. @@ -1024,7 +833,8 @@ gtk_action_create_menu_item (GtkAction *action) menu_item = GTK_ACTION_GET_CLASS (action)->create_menu_item (action); - GTK_ACTION_GET_CLASS (action)->connect_proxy (action, menu_item); + gtk_activatable_set_use_action_appearance (GTK_ACTIVATABLE (menu_item), TRUE); + gtk_activatable_set_related_action (GTK_ACTIVATABLE (menu_item), action); return menu_item; } @@ -1048,11 +858,32 @@ gtk_action_create_tool_item (GtkAction *action) button = GTK_ACTION_GET_CLASS (action)->create_tool_item (action); - GTK_ACTION_GET_CLASS (action)->connect_proxy (action, button); + gtk_activatable_set_use_action_appearance (GTK_ACTIVATABLE (button), TRUE); + gtk_activatable_set_related_action (GTK_ACTIVATABLE (button), action); return button; } +void +_gtk_action_add_to_proxy_list (GtkAction *action, + GtkWidget *proxy) +{ + g_return_if_fail (GTK_IS_ACTION (action)); + g_return_if_fail (GTK_IS_WIDGET (proxy)); + + GTK_ACTION_GET_CLASS (action)->connect_proxy (action, proxy); +} + +void +_gtk_action_remove_from_proxy_list (GtkAction *action, + GtkWidget *proxy) +{ + g_return_if_fail (GTK_IS_ACTION (action)); + g_return_if_fail (GTK_IS_WIDGET (proxy)); + + GTK_ACTION_GET_CLASS (action)->disconnect_proxy (action, proxy); +} + /** * gtk_action_connect_proxy: * @action: the action object @@ -1067,22 +898,20 @@ gtk_action_create_tool_item (GtkAction *action) * first. * * Since: 2.4 + * + * Deprecated 2.16: Use gtk_activatable_set_related_action() instead. */ void gtk_action_connect_proxy (GtkAction *action, GtkWidget *proxy) { - GtkAction *prev_action; - g_return_if_fail (GTK_IS_ACTION (action)); g_return_if_fail (GTK_IS_WIDGET (proxy)); + g_return_if_fail (GTK_IS_ACTIVATABLE (proxy)); - prev_action = g_object_get_qdata (G_OBJECT (proxy), quark_gtk_action_proxy); + gtk_activatable_set_use_action_appearance (GTK_ACTIVATABLE (proxy), TRUE); - if (prev_action) - GTK_ACTION_GET_CLASS (action)->disconnect_proxy (prev_action, proxy); - - GTK_ACTION_GET_CLASS (action)->connect_proxy (action, proxy); + gtk_activatable_set_related_action (GTK_ACTIVATABLE (proxy), action); } /** @@ -1094,6 +923,8 @@ gtk_action_connect_proxy (GtkAction *action, * Does <emphasis>not</emphasis> destroy the widget, however. * * Since: 2.4 + * + * Deprecated 2.16: Use gtk_activatable_set_related_action() instead. */ void gtk_action_disconnect_proxy (GtkAction *action, @@ -1102,9 +933,7 @@ gtk_action_disconnect_proxy (GtkAction *action, g_return_if_fail (GTK_IS_ACTION (action)); g_return_if_fail (GTK_IS_WIDGET (proxy)); - g_return_if_fail (g_object_get_qdata (G_OBJECT (proxy), quark_gtk_action_proxy) == action); - - GTK_ACTION_GET_CLASS (action)->disconnect_proxy (action, proxy); + gtk_activatable_set_related_action (GTK_ACTIVATABLE (proxy), NULL); } /** @@ -1139,15 +968,19 @@ gtk_action_get_proxies (GtkAction *action) * %NULL, if it is not attached to an action. * * Since: 2.10 + * + * Deprecated 2.16: Use gtk_activatable_get_related_action() instead. */ GtkAction* gtk_widget_get_action (GtkWidget *widget) { g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL); - - return g_object_get_qdata (G_OBJECT (widget), quark_gtk_action_proxy); -} + if (GTK_IS_ACTIVATABLE (widget)) + return gtk_activatable_get_related_action (GTK_ACTIVATABLE (widget)); + + return NULL; +} /** * gtk_action_get_name: @@ -1211,22 +1044,6 @@ gtk_action_get_sensitive (GtkAction *action) return action->private_data->sensitive; } -void -_gtk_action_sync_sensitive (GtkAction *action) -{ - GSList *p; - GtkWidget *proxy; - gboolean sensitive; - - sensitive = gtk_action_is_sensitive (action); - - for (p = action->private_data->proxies; p; p = p->next) - { - proxy = (GtkWidget *)p->data; - gtk_widget_set_sensitive (proxy, sensitive); - } -} - /** * gtk_action_set_sensitive: * @action: the action object @@ -1251,8 +1068,6 @@ gtk_action_set_sensitive (GtkAction *action, { action->private_data->sensitive = sensitive; - _gtk_action_sync_sensitive (action); - g_object_notify (G_OBJECT (action), "sensitive"); } } @@ -1300,36 +1115,6 @@ gtk_action_get_visible (GtkAction *action) return action->private_data->visible; } -void -_gtk_action_sync_visible (GtkAction *action) -{ - GSList *p; - GtkWidget *proxy; - GtkWidget *menu; - gboolean visible; - - visible = gtk_action_is_visible (action); - - for (p = action->private_data->proxies; p; p = p->next) - { - proxy = (GtkWidget *)p->data; - - if (GTK_IS_MENU_ITEM (proxy)) - { - menu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (proxy)); - - _gtk_action_sync_menu_visible (action, proxy, _gtk_menu_is_empty (menu)); - } - else - { - if (visible) - gtk_widget_show (proxy); - else - gtk_widget_hide (proxy); - } - } -} - /** * gtk_action_set_visible: * @action: the action object @@ -1354,8 +1139,6 @@ gtk_action_set_visible (GtkAction *action, { action->private_data->visible = visible; - _gtk_action_sync_visible (action); - g_object_notify (G_OBJECT (action), "visible"); } } @@ -1374,8 +1157,7 @@ void gtk_action_set_is_important (GtkAction *action, gboolean is_important) { - GSList *p; - GtkWidget *proxy; + g_return_if_fail (GTK_IS_ACTION (action)); g_return_if_fail (GTK_IS_ACTION (action)); @@ -1384,15 +1166,6 @@ gtk_action_set_is_important (GtkAction *action, if (action->private_data->is_important != is_important) { action->private_data->is_important = is_important; - - for (p = action->private_data->proxies; p; p = p->next) - { - proxy = (GtkWidget *)p->data; - - if (GTK_IS_TOOL_ITEM (proxy)) - gtk_tool_item_set_is_important (GTK_TOOL_ITEM (proxy), - is_important); - } g_object_notify (G_OBJECT (action), "is-important"); } @@ -1403,12 +1176,12 @@ gtk_action_set_is_important (GtkAction *action, * @action: a #GtkAction * * Checks whether @action is important or not - * + * * Returns: whether @action is important * * Since: 2.16 */ -gboolean +gboolean gtk_action_get_is_important (GtkAction *action) { g_return_val_if_fail (GTK_IS_ACTION (action), FALSE); @@ -1429,8 +1202,6 @@ void gtk_action_set_label (GtkAction *action, const gchar *label) { - GSList *p; - GtkWidget *proxy, *child; gchar *tmp; g_return_if_fail (GTK_IS_ACTION (action)); @@ -1447,21 +1218,7 @@ gtk_action_set_label (GtkAction *action, if (gtk_stock_lookup (action->private_data->stock_id, &stock_item)) action->private_data->label = g_strdup (stock_item.label); } - - for (p = action->private_data->proxies; p; p = p->next) - { - proxy = (GtkWidget *)p->data; - - if (GTK_IS_MENU_ITEM (proxy)) - { - child = GTK_BIN (proxy)->child; - - if (GTK_IS_LABEL (child)) - gtk_label_set_label (GTK_LABEL (child), - action->private_data->label); - } - } - + g_object_notify (G_OBJECT (action), "label"); /* if short_label is unset, set short_label=label */ @@ -1482,7 +1239,7 @@ gtk_action_set_label (GtkAction *action, * * Since: 2.16 */ -G_CONST_RETURN gchar * +G_CONST_RETURN gchar * gtk_action_get_label (GtkAction *action) { g_return_val_if_fail (GTK_IS_ACTION (action), NULL); @@ -1495,16 +1252,14 @@ gtk_action_get_label (GtkAction *action) * @action: a #GtkAction * @label: the label text to set * - * Sets a shorter label on @action. + * Sets a shorter label text on @action. * * Since: 2.16 */ void -gtk_action_set_short_label (GtkAction *action, +gtk_action_set_short_label (GtkAction *action, const gchar *label) { - GSList *p; - GtkWidget *proxy, *child; gchar *tmp; g_return_if_fail (GTK_IS_ACTION (action)); @@ -1517,29 +1272,6 @@ gtk_action_set_short_label (GtkAction *action, if (!action->private_data->short_label_set) action->private_data->short_label = g_strdup (action->private_data->label); - for (p = action->private_data->proxies; p; p = p->next) - { - proxy = (GtkWidget *)p->data; - - if (GTK_IS_TOOL_BUTTON (proxy)) - gtk_tool_button_set_label (GTK_TOOL_BUTTON (proxy), - action->private_data->short_label); - else if (GTK_IS_BUTTON (proxy) && - !gtk_button_get_use_stock (GTK_BUTTON (proxy))) - { - GtkWidget *image; - - child = GTK_BIN (proxy)->child; - - image = gtk_button_get_image (GTK_BUTTON (proxy)); - - if (GTK_IS_IMAGE (image) || - child == NULL || GTK_IS_LABEL (child)) - gtk_button_set_label (GTK_BUTTON (proxy), - action->private_data->short_label); - } - } - g_object_notify (G_OBJECT (action), "short-label"); } @@ -1548,16 +1280,17 @@ gtk_action_set_short_label (GtkAction *action, * @action: a #GtkAction * @label: the label text to set * - * Sets a shorter label on @action. + * Gets the short label text of @action. + * + * Returns: the short label text. * * Since: 2.16 */ -G_CONST_RETURN gchar * +G_CONST_RETURN gchar * gtk_action_get_short_label (GtkAction *action) { g_return_val_if_fail (GTK_IS_ACTION (action), NULL); - g_object_notify (G_OBJECT (action), "short-label"); return action->private_data->short_label; } @@ -1574,8 +1307,7 @@ void gtk_action_set_visible_horizontal (GtkAction *action, gboolean visible_horizontal) { - GSList *p; - GtkWidget *proxy; + g_return_if_fail (GTK_IS_ACTION (action)); g_return_if_fail (GTK_IS_ACTION (action)); @@ -1584,15 +1316,6 @@ gtk_action_set_visible_horizontal (GtkAction *action, if (action->private_data->visible_horizontal != visible_horizontal) { action->private_data->visible_horizontal = visible_horizontal; - - for (p = action->private_data->proxies; p; p = p->next) - { - proxy = (GtkWidget *)p->data; - - if (GTK_IS_TOOL_ITEM (proxy)) - gtk_tool_item_set_visible_horizontal (GTK_TOOL_ITEM (proxy), - visible_horizontal); - } g_object_notify (G_OBJECT (action), "visible-horizontal"); } @@ -1603,12 +1326,12 @@ gtk_action_set_visible_horizontal (GtkAction *action, * @action: a #GtkAction * * Checks whether @action is visible when horizontal - * + * * Returns: whether @action is visible when horizontal * * Since: 2.16 */ -gboolean +gboolean gtk_action_get_visible_horizontal (GtkAction *action) { g_return_val_if_fail (GTK_IS_ACTION (action), FALSE); @@ -1621,7 +1344,7 @@ gtk_action_get_visible_horizontal (GtkAction *action) * @action: a #GtkAction * @visible_vertical: whether the action is visible vertically * - * Sets whether @action is visible when vertical + * Sets whether @action is visible when vertical * * Since: 2.16 */ @@ -1629,8 +1352,7 @@ void gtk_action_set_visible_vertical (GtkAction *action, gboolean visible_vertical) { - GSList *p; - GtkWidget *proxy; + g_return_if_fail (GTK_IS_ACTION (action)); g_return_if_fail (GTK_IS_ACTION (action)); @@ -1639,15 +1361,6 @@ gtk_action_set_visible_vertical (GtkAction *action, if (action->private_data->visible_vertical != visible_vertical) { action->private_data->visible_vertical = visible_vertical; - - for (p = action->private_data->proxies; p; p = p->next) - { - proxy = (GtkWidget *)p->data; - - if (GTK_IS_TOOL_ITEM (proxy)) - gtk_tool_item_set_visible_vertical (GTK_TOOL_ITEM (proxy), - visible_vertical); - } g_object_notify (G_OBJECT (action), "visible-vertical"); } @@ -1658,12 +1371,12 @@ gtk_action_set_visible_vertical (GtkAction *action, * @action: a #GtkAction * * Checks whether @action is visible when horizontal - * + * * Returns: whether @action is visible when horizontal * * Since: 2.16 */ -gboolean +gboolean gtk_action_get_visible_vertical (GtkAction *action) { g_return_val_if_fail (GTK_IS_ACTION (action), FALSE); @@ -1671,14 +1384,6 @@ gtk_action_get_visible_vertical (GtkAction *action) return action->private_data->visible_vertical; } -static void -gtk_action_sync_tooltip (GtkAction *action, - GtkWidget *proxy) -{ - gtk_tool_item_set_tooltip_text (GTK_TOOL_ITEM (proxy), - action->private_data->tooltip); -} - /** * gtk_action_set_tooltip: * @action: a #GtkAction @@ -1692,8 +1397,6 @@ void gtk_action_set_tooltip (GtkAction *action, const gchar *tooltip) { - GSList *p; - GtkWidget *proxy; gchar *tmp; g_return_if_fail (GTK_IS_ACTION (action)); @@ -1702,14 +1405,6 @@ gtk_action_set_tooltip (GtkAction *action, action->private_data->tooltip = g_strdup (tooltip); g_free (tmp); - for (p = action->private_data->proxies; p; p = p->next) - { - proxy = (GtkWidget *)p->data; - - if (GTK_IS_TOOL_ITEM (proxy)) - gtk_action_sync_tooltip (action, proxy); - } - g_object_notify (G_OBJECT (action), "tooltip"); } @@ -1719,11 +1414,11 @@ gtk_action_set_tooltip (GtkAction *action, * * Gets the tooltip text of @action. * -* Returns: the tooltip text + * Returns: the tooltip text * * Since: 2.16 */ -G_CONST_RETURN gchar * +G_CONST_RETURN gchar * gtk_action_get_tooltip (GtkAction *action) { g_return_val_if_fail (GTK_IS_ACTION (action), NULL); @@ -1744,42 +1439,16 @@ void gtk_action_set_stock_id (GtkAction *action, const gchar *stock_id) { - GSList *p; - GtkWidget *proxy, *image; gchar *tmp; - + + g_return_if_fail (GTK_IS_ACTION (action)); + g_return_if_fail (GTK_IS_ACTION (action)); tmp = action->private_data->stock_id; action->private_data->stock_id = g_strdup (stock_id); g_free (tmp); - for (p = action->private_data->proxies; p; p = p->next) - { - proxy = (GtkWidget *)p->data; - - if (GTK_IS_IMAGE_MENU_ITEM (proxy)) - { - image = gtk_image_menu_item_get_image (GTK_IMAGE_MENU_ITEM (proxy)); - - if (GTK_IS_IMAGE (image)) - gtk_image_set_from_stock (GTK_IMAGE (image), - action->private_data->stock_id, GTK_ICON_SIZE_MENU); - } - else if (GTK_IS_TOOL_BUTTON (proxy)) - { - gtk_tool_button_set_icon_widget (GTK_TOOL_BUTTON (proxy), NULL); - gtk_tool_button_set_stock_id (GTK_TOOL_BUTTON (proxy), - action->private_data->stock_id); - } - else if (GTK_IS_BUTTON (proxy) && - gtk_button_get_use_stock (GTK_BUTTON (proxy))) - { - gtk_button_set_label (GTK_BUTTON (proxy), - action->private_data->stock_id); - } - } - g_object_notify (G_OBJECT (action), "stock-id"); /* update label and short_label if appropriate */ @@ -1807,7 +1476,7 @@ gtk_action_set_stock_id (GtkAction *action, * * Since: 2.16 */ -G_CONST_RETURN gchar * +G_CONST_RETURN gchar * gtk_action_get_stock_id (GtkAction *action) { g_return_val_if_fail (GTK_IS_ACTION (action), NULL); @@ -1828,48 +1497,16 @@ void gtk_action_set_icon_name (GtkAction *action, const gchar *icon_name) { - GSList *p; - GtkWidget *proxy, *image; gchar *tmp; - + + g_return_if_fail (GTK_IS_ACTION (action)); + g_return_if_fail (GTK_IS_ACTION (action)); tmp = action->private_data->icon_name; action->private_data->icon_name = g_strdup (icon_name); g_free (tmp); - for (p = action->private_data->proxies; p; p = p->next) - { - proxy = (GtkWidget *)p->data; - - if (GTK_IS_IMAGE_MENU_ITEM (proxy)) - { - image = gtk_image_menu_item_get_image (GTK_IMAGE_MENU_ITEM (proxy)); - - if (GTK_IS_IMAGE (image) && - (gtk_image_get_storage_type (GTK_IMAGE (image)) == GTK_IMAGE_EMPTY || - gtk_image_get_storage_type (GTK_IMAGE (image)) == GTK_IMAGE_ICON_NAME)) - gtk_image_set_from_icon_name (GTK_IMAGE (image), - action->private_data->icon_name, GTK_ICON_SIZE_MENU); - } - else if (GTK_IS_TOOL_BUTTON (proxy)) - { - gtk_tool_button_set_icon_name (GTK_TOOL_BUTTON (proxy), - action->private_data->icon_name); - } - else if (GTK_IS_BUTTON (proxy) && - !gtk_button_get_use_stock (GTK_BUTTON (proxy))) - { - image = gtk_button_get_image (GTK_BUTTON (proxy)); - - if (GTK_IS_IMAGE (image) && - (gtk_image_get_storage_type (GTK_IMAGE (image)) == GTK_IMAGE_EMPTY || - gtk_image_get_storage_type (GTK_IMAGE (image)) == GTK_IMAGE_ICON_NAME)) - gtk_image_set_from_icon_name (GTK_IMAGE (image), - action->private_data->icon_name, GTK_ICON_SIZE_MENU); - } - } - g_object_notify (G_OBJECT (action), "icon-name"); } @@ -1883,7 +1520,7 @@ gtk_action_set_icon_name (GtkAction *action, * * Since: 2.16 */ -G_CONST_RETURN gchar * +G_CONST_RETURN gchar * gtk_action_get_icon_name (GtkAction *action) { g_return_val_if_fail (GTK_IS_ACTION (action), NULL); @@ -1904,11 +1541,6 @@ void gtk_action_set_gicon (GtkAction *action, GIcon *icon) { - GSList *p; - GtkWidget *proxy, *image; - GtkIconSize icon_size; - gboolean has_stock_icon; - g_return_if_fail (GTK_IS_ACTION (action)); if (action->private_data->gicon) @@ -1919,48 +1551,6 @@ gtk_action_set_gicon (GtkAction *action, if (action->private_data->gicon) g_object_ref (action->private_data->gicon); - if (action->private_data->stock_id && - gtk_icon_factory_lookup_default (action->private_data->stock_id)) - has_stock_icon = TRUE; - else - has_stock_icon = FALSE; - - for (p = action->private_data->proxies; p; p = p->next) - { - proxy = (GtkWidget *)p->data; - - if (GTK_IS_IMAGE_MENU_ITEM (proxy) && !has_stock_icon) - { - image = gtk_image_menu_item_get_image (GTK_IMAGE_MENU_ITEM (proxy)); - gtk_image_set_from_gicon (GTK_IMAGE (image), icon, GTK_ICON_SIZE_MENU); - } - else if (GTK_IS_TOOL_BUTTON (proxy)) - { - if (has_stock_icon || !icon) - image = NULL; - else - { - image = gtk_tool_button_get_icon_widget (GTK_TOOL_BUTTON (proxy)); - icon_size = gtk_tool_item_get_icon_size (GTK_TOOL_ITEM (proxy)); - - if (!image) - image = gtk_image_new (); - } - - gtk_tool_button_set_icon_widget (GTK_TOOL_BUTTON (proxy), image); - gtk_image_set_from_gicon (GTK_IMAGE (image), icon, icon_size); - } - else if (GTK_IS_BUTTON (proxy) && - !gtk_button_get_use_stock (GTK_BUTTON (proxy))) - { - image = gtk_button_get_image (GTK_BUTTON (proxy)); - if (GTK_IS_IMAGE (image) && - (gtk_image_get_storage_type (GTK_IMAGE (image)) == GTK_IMAGE_EMPTY || - gtk_image_get_storage_type (GTK_IMAGE (image)) == GTK_IMAGE_GICON)) - gtk_image_set_from_gicon (GTK_IMAGE (image), icon, GTK_ICON_SIZE_BUTTON); - } - } - g_object_notify (G_OBJECT (action), "gicon"); } @@ -1994,6 +1584,9 @@ gtk_action_get_gicon (GtkAction *action) * This function is intended for use by action implementations. * * Since: 2.4 + * + * Deprecated 2.16: activatables are now responsible for activating the + * action directly so this doesnt apply anymore. */ void gtk_action_block_activate_from (GtkAction *action, @@ -2017,6 +1610,9 @@ gtk_action_block_activate_from (GtkAction *action, * This function is intended for use by action implementations. * * Since: 2.4 + * + * Deprecated 2.16: activatables are now responsible for activating the + * action directly so this doesnt apply anymore. */ void gtk_action_unblock_activate_from (GtkAction *action, diff --git a/gtk/gtkaction.h b/gtk/gtkaction.h index ff20c7dd66..a3636b478a 100644 --- a/gtk/gtkaction.h +++ b/gtk/gtkaction.h @@ -105,22 +105,33 @@ GtkWidget * gtk_action_create_icon (GtkAction *action, GtkWidget * gtk_action_create_menu_item (GtkAction *action); GtkWidget * gtk_action_create_tool_item (GtkAction *action); GtkWidget * gtk_action_create_menu (GtkAction *action); -void gtk_action_connect_proxy (GtkAction *action, - GtkWidget *proxy); -void gtk_action_disconnect_proxy (GtkAction *action, - GtkWidget *proxy); GSList * gtk_action_get_proxies (GtkAction *action); -GtkAction * gtk_widget_get_action (GtkWidget *widget); void gtk_action_connect_accelerator (GtkAction *action); void gtk_action_disconnect_accelerator (GtkAction *action); G_CONST_RETURN gchar *gtk_action_get_accel_path (GtkAction *action); GClosure *gtk_action_get_accel_closure (GtkAction *action); -/* protected ... for use by child actions */ +#ifndef GTK_DISABLE_DEPRECATED +GtkAction *gtk_widget_get_action (GtkWidget *widget); +void gtk_action_connect_proxy (GtkAction *action, + GtkWidget *proxy); +void gtk_action_disconnect_proxy (GtkAction *action, + GtkWidget *proxy); void gtk_action_block_activate_from (GtkAction *action, GtkWidget *proxy); void gtk_action_unblock_activate_from (GtkAction *action, GtkWidget *proxy); +#endif /* GTK_DISABLE_DEPRECATED */ +void gtk_action_block_activate (GtkAction *action); +void gtk_action_unblock_activate (GtkAction *action); + + +void _gtk_action_add_to_proxy_list (GtkAction *action, + GtkWidget *proxy); +void _gtk_action_remove_from_proxy_list(GtkAction *action, + GtkWidget *proxy); + +/* protected ... for use by child actions */ void _gtk_action_emit_activate (GtkAction *action); /* protected ... for use by action groups */ @@ -128,8 +139,6 @@ void gtk_action_set_accel_path (GtkAction *action, const gchar *accel_path); void gtk_action_set_accel_group (GtkAction *action, GtkAccelGroup *accel_group); -void _gtk_action_sync_sensitive (GtkAction *action); -void _gtk_action_sync_visible (GtkAction *action); void _gtk_action_sync_menu_visible (GtkAction *action, GtkWidget *proxy, gboolean empty); diff --git a/gtk/gtkactiongroup.c b/gtk/gtkactiongroup.c index a4c5feb624..2794c58008 100644 --- a/gtk/gtkactiongroup.c +++ b/gtk/gtkactiongroup.c @@ -626,8 +626,8 @@ cb_set_action_sensitivity (const gchar *name, { /* Minor optimization, the action_groups state only affects actions * that are themselves sensitive */ - if (gtk_action_get_sensitive (action)) - _gtk_action_sync_sensitive (action); + g_object_notify (G_OBJECT (action), "sensitive"); + } /** @@ -691,8 +691,7 @@ cb_set_action_visiblity (const gchar *name, { /* Minor optimization, the action_groups state only affects actions * that are themselves visible */ - if (gtk_action_get_visible (action)) - _gtk_action_sync_visible (action); + g_object_notify (G_OBJECT (action), "visible"); } /** diff --git a/gtk/gtkactivatable.c b/gtk/gtkactivatable.c new file mode 100644 index 0000000000..5f7252d2b5 --- /dev/null +++ b/gtk/gtkactivatable.c @@ -0,0 +1,540 @@ +/* gtkactivatable.c + * Copyright (C) 2008 Tristan Van Berkom <tristan.van.berkom@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library 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. + */ + +/** + * SECTION:gtk-activatable + * @Short_Description: An interface for activatable widgets. + * + * Activatable widgets can be connected to a #GtkAction and reflects + * the state of its action - a #GtkActivatable can also provide feedback + * through its action, as they are responsible for activating their + * related actions. + * + * <refsect2> + * <title>Implementing GtkActivatable</title> + * <para> + * When extending a class that is already #GtkActivatable; it is only + * necessary to implement the #GtkActivatable->reset() and #GtkActivatable->update() + * methods and chain up to the parent implementation, however when introducing + * a new #GtkActivatable class; the #GtkActivatable:related-action and + * #GtkActivatable:use-action-appearance properties need to be handled by + * the implementor. Handling these properties is mostly a matter of installing + * the action pointer and boolean flag on your instance, and calling + * gtk_activatable_do_set_related_action() and gtk_activatable_reset() at the + * appropriate times. + * </para> + * <example> + * <title>A class fragment implementing #GtkActivatable</title> + * <programlisting><![CDATA[ + * + * enum { + * ... + * + * PROP_ACTIVATABLE_RELATED_ACTION, + * PROP_ACTIVATABLE_USE_ACTION_APPEARANCE + * } + * + * struct _FooBarPrivate + * { + * + * ... + * + * GtkAction *action; + * gboolean use_action_appearance; + * }; + * + * ... + * + * static void foo_bar_activatable_interface_init (GtkActivatableIface *iface); + * static void foo_bar_activatable_update (GtkActivatable *activatable, + * GtkAction *action, + * const gchar *property_name); + * static void foo_bar_activatable_reset (GtkActivatable *activatable, + * GtkAction *action); + * ... + * + * + * static void + * foo_bar_class_init (FooBarClass *klass) + * { + * + * ... + * + * g_object_class_override_property (gobject_class, PROP_ACTIVATABLE_RELATED_ACTION, "related-action"); + * g_object_class_override_property (gobject_class, PROP_ACTIVATABLE_USE_ACTION_APPEARANCE, "use-action-appearance"); + * + * ... + * } + * + * + * static void + * foo_bar_activatable_interface_init (GtkActivatableIface *iface) + * { + * iface->update = foo_bar_activatable_update; + * iface->reset = foo_bar_activatable_reset; + * } + * + * ... Break the reference using gtk_activatable_do_set_related_action()... + * + * static void + * foo_bar_dispose (GObject *object) + * { + * FooBar *bar = FOO_BAR (object); + * FooBarPrivate *priv = FOO_BAR_GET_PRIVATE (bar); + * + * ... + * + * if (priv->action) + * { + * gtk_activatable_do_set_related_action (GTK_ACTIVATABLE (bar), NULL); + * priv->action = NULL; + * } + * G_OBJECT_CLASS (foo_bar_parent_class)->dispose (object); + * } + * + * ... Handle the "related-action" and "use-action-appearance" properties ... + * + * static void + * foo_bar_set_property (GObject *object, + * guint prop_id, + * const GValue *value, + * GParamSpec *pspec) + * { + * FooBar *bar = FOO_BAR (object); + * FooBarPrivate *priv = FOO_BAR_GET_PRIVATE (bar); + * + * switch (prop_id) + * { + * + * ... + * + * case PROP_ACTIVATABLE_RELATED_ACTION: + * foo_bar_set_related_action (bar, g_value_get_object (value)); + * break; + * case PROP_ACTIVATABLE_USE_ACTION_APPEARANCE: + * foo_bar_set_use_action_appearance (bar, g_value_get_boolean (value)); + * break; + * default: + * G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + * break; + * } + * } + * + * static void + * foo_bar_get_property (GObject *object, + * guint prop_id, + * GValue *value, + * GParamSpec *pspec) + * { + * FooBar *bar = FOO_BAR (object); + * FooBarPrivate *priv = FOO_BAR_GET_PRIVATE (bar); + * + * switch (prop_id) + * { + * + * ... + * + * case PROP_ACTIVATABLE_RELATED_ACTION: + * g_value_set_object (value, priv->action); + * break; + * case PROP_ACTIVATABLE_USE_ACTION_APPEARANCE: + * g_value_set_boolean (value, priv->use_action_appearance); + * break; + * default: + * G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + * break; + * } + * } + * + * + * static void + * foo_bar_set_use_action_appearance (FooBar *bar, + * gboolean use_appearance) + * { + * FooBarPrivate *priv = FOO_BAR_GET_PRIVATE (bar); + * + * if (priv->use_action_appearance != use_appearance) + * { + * priv->use_action_appearance = use_appearance; + * + * gtk_activatable_reset (GTK_ACTIVATABLE (bar), priv->action); + * } + * } + * + * ... call gtk_activatable_do_set_related_action() and then assign the action pointer, + * no need to reference the action here since gtk_activatable_do_set_related_action() already + * holds a reference here for you... + * static void + * foo_bar_set_related_action (FooBar *bar, + * GtkAction *action) + * { + * FooBarPrivate *priv = FOO_BAR_GET_PRIVATE (bar); + * + * if (priv->action == action) + * return; + * + * gtk_activatable_do_set_related_action (GTK_ACTIVATABLE (bar), action); + * + * priv->action = action; + * } + * + * ... Selectively reset and update activatable depending on the use-action-appearance property ... + * static void + * gtk_button_activatable_reset (GtkActivatable *activatable, + * GtkAction *action) + * { + * GtkButtonPrivate *priv = GTK_BUTTON_GET_PRIVATE (activatable); + * + * if (!action) + * return; + * + * if (gtk_action_is_visible (action)) + * gtk_widget_show (GTK_WIDGET (activatable)); + * else + * gtk_widget_hide (GTK_WIDGET (activatable)); + * + * gtk_widget_set_sensitive (GTK_WIDGET (activatable), gtk_action_is_sensitive (action)); + * + * ... + * + * if (priv->use_action_appearance) + * { + * if (gtk_action_get_stock_id (action)) + * foo_bar_set_stock (button, gtk_action_get_stock_id (action)); + * else if (gtk_action_get_label (action)) + * foo_bar_set_label (button, gtk_action_get_label (action)); + * + * ... + * + * } + * } + * + * static void + * foo_bar_activatable_update (GtkActivatable *activatable, + * GtkAction *action, + * const gchar *property_name) + * { + * FooBarPrivate *priv = FOO_BAR_GET_PRIVATE (activatable); + * + * if (strcmp (property_name, "visible") == 0) + * { + * if (gtk_action_is_visible (action)) + * gtk_widget_show (GTK_WIDGET (activatable)); + * else + * gtk_widget_hide (GTK_WIDGET (activatable)); + * } + * else if (strcmp (property_name, "sensitive") == 0) + * gtk_widget_set_sensitive (GTK_WIDGET (activatable), gtk_action_is_sensitive (action)); + * + * ... + * + * if (!priv->use_action_appearance) + * return; + * + * if (strcmp (property_name, "stock-id") == 0) + * foo_bar_set_stock (button, gtk_action_get_stock_id (action)); + * else if (strcmp (property_name, "label") == 0) + * foo_bar_set_label (button, gtk_action_get_label (action)); + * + * ... + * }]]></programlisting> + * </example> + * + */ + +#include "config.h" +#include "gtkactivatable.h" +#include "gtkactiongroup.h" +#include "gtktypeutils.h" +#include "gtkprivate.h" +#include "gtkintl.h" +#include "gtkalias.h" + + +static void gtk_activatable_class_init (gpointer g_iface); + +GType +gtk_activatable_get_type (void) +{ + static GType activatable_type = 0; + + if (!activatable_type) + activatable_type = + g_type_register_static_simple (G_TYPE_INTERFACE, I_("GtkActivatable"), + sizeof (GtkActivatableIface), + (GClassInitFunc) gtk_activatable_class_init, + 0, NULL, 0); + + return activatable_type; +} + +static void +gtk_activatable_class_init (gpointer g_iface) +{ + /** + * GtkActivatable:related-action: + * + * The action that this activatable will activate and receive + * updates from for various states and possibly appearance. + * + * <note><para>#GtkActivatable implementors need to handle the this property and + * call gtk_activatable_do_set_related_action() when it changes.</para></note> + * + * Since: 2.16 + */ + g_object_interface_install_property (g_iface, + g_param_spec_object ("related-action", + P_("Related Action"), + P_("The action this activatable will activate and receive updates from"), + GTK_TYPE_ACTION, + GTK_PARAM_READWRITE)); + + /** + * GtkActivatable:use-action-appearance: + * + * Whether this activatable should reset its layout + * and appearance when setting the related action or when + * the action changes appearance. + * + * See the #GtkAction documentation directly to find which properties + * should be ignored by the #GtkActivatable when this property is %FALSE. + * + * <note><para>#GtkActivatable implementors need to handle this property + * and call gtk_activatable_reset() on the activatable widget when it changes.</para></note> + * + * Since: 2.16 + */ + g_object_interface_install_property (g_iface, + g_param_spec_boolean ("use-action-appearance", + P_("Use Action Appearance"), + P_("Whether to use the related actions appearance properties"), + TRUE, + GTK_PARAM_READWRITE)); + + +} + +static void +gtk_activatable_update (GtkActivatable *activatable, + GtkAction *action, + const gchar *property_name) +{ + GtkActivatableIface *iface; + + g_return_if_fail (GTK_IS_ACTIVATABLE (activatable)); + + iface = GTK_ACTIVATABLE_GET_IFACE (activatable); + if (iface->update) + iface->update (activatable, action, property_name); + else + g_critical ("GtkActivatable->update() unimplemented for type %s", + g_type_name (G_OBJECT_TYPE (activatable))); +} + +/** + * gtk_activatable_reset: + * @activatable: a #GtkActivatable + * @action: the related #GtkAction or %NULL + * + * This is called to update the activatable completely, this is called internally when + * the #GtkActivatable::related-action property is set or unset and by the implementing + * class when #GtkActivatable::use-action-appearance changes. + * + * Since: 2.16 + **/ +void +gtk_activatable_reset (GtkActivatable *activatable, + GtkAction *action) +{ + GtkActivatableIface *iface; + + g_return_if_fail (GTK_IS_ACTIVATABLE (activatable)); + + iface = GTK_ACTIVATABLE_GET_IFACE (activatable); + if (iface->reset) + iface->reset (activatable, action); + else + g_critical ("GtkActivatable->reset() unimplemented for type %s", + g_type_name (G_OBJECT_TYPE (activatable))); +} + + +/** + * gtk_activatable_set_related_action: + * @activatable: a #GtkActivatable + * @action: the #GtkAction to set + * + * Sets the related action on the @activatable object. + * + * <note><para>#GtkActivatable implementors need to handle the #GtkActivatable:related-action + * property and call gtk_activatable_do_set_related_action() when it changes.</para></note> + * + * Since: 2.16 + **/ +void +gtk_activatable_set_related_action (GtkActivatable *activatable, + GtkAction *action) +{ + g_return_if_fail (GTK_IS_ACTIVATABLE (activatable)); + g_return_if_fail (action == NULL || GTK_IS_ACTION (action)); + + g_object_set (activatable, "related-action", action, NULL); +} + +static void +gtk_activatable_action_notify (GtkAction *action, + GParamSpec *pspec, + GtkActivatable *activatable) +{ + gtk_activatable_update (activatable, action, pspec->name); +} + +/** + * gtk_activatable_do_set_related_action: + * @activatable: a #GtkActivatable + * @action: the #GtkAction to set + * + * This is a utility function for #GtkActivatable implementors. + * + * When implementing #GtkActivatable you must call this when + * handling changes of the #GtkActivatable:related-action, and + * you must also use this to break references in #GObject->dispose(). + * + * This function adds a reference to the currently set related + * action for you, it also makes sure the #GtkActivatable->update() + * method is called when the related #GtkAction properties change + * and registers to the action's proxy list. + * + * <note><para>Be careful to call this before setting the local + * copy of the #GtkAction property, since this function uses + * gtk_activatable_get_action() to retrieve the previous action</para></note> + */ +void +gtk_activatable_do_set_related_action (GtkActivatable *activatable, + GtkAction *action) +{ + GtkAction *prev_action; + + prev_action = gtk_activatable_get_related_action (activatable); + + if (prev_action != action) + { + if (prev_action) + { + g_signal_handlers_disconnect_by_func (prev_action, gtk_activatable_action_notify, activatable); + + _gtk_action_remove_from_proxy_list (prev_action, GTK_WIDGET (activatable)); + + g_object_unref (prev_action); + + /* Some apps are using the object data directly... + * so continue to set it for a bit longer + */ + g_object_set_data (activatable, "gtk-action", NULL); + } + + /* Some applications rely on their proxy UI to be set up + * before they receive the ::connect-proxy signal, so we + * need to call reset() before add_to_proxy_list(). + */ + gtk_activatable_reset (activatable, action); + + if (action) + { + g_object_ref (action); + + g_signal_connect (G_OBJECT (action), "notify", G_CALLBACK (gtk_activatable_action_notify), activatable); + + _gtk_action_add_to_proxy_list (action, GTK_WIDGET (activatable)); + + g_object_set_data (activatable, "gtk-action", action); + } + } +} + +/** + * gtk_activatable_get_related_action: + * @activatable: a #GtkActivatable + * + * Gets the related #GtkAction for @activatable. + * + * Returns: the related #GtkAction if one is set. + * + * Since: 2.16 + **/ +GtkAction * +gtk_activatable_get_related_action (GtkActivatable *activatable) +{ + GtkAction *action; + + g_return_val_if_fail (GTK_IS_ACTIVATABLE (activatable), NULL); + + g_object_get (activatable, "related-action", &action, NULL); + + /* g_object_get() gives us a ref... */ + if (action) + g_object_unref (action); + + return action; +} + +/** + * gtk_activatable_set_use_action_appearance: + * @activatable: a #GtkActivatable + * @use_appearance: whether to use the actions appearance + * + * Sets whether this activatable should reset its layout and appearance + * when setting the related action or when the action changes appearance + * + * <note><para>#GtkActivatable implementors need to handle the #GtkActivatable:use-action-appearance + * property and call gtk_activatable_reset() to update @activatable if needed.</para></note> + * + * Since: 2.16 + **/ +void +gtk_activatable_set_use_action_appearance (GtkActivatable *activatable, + gboolean use_appearance) +{ + g_object_set (activatable, "use-action-appearance", use_appearance, NULL); +} + +/** + * gtk_activatable_get_use_action_appearance: + * @activatable: a #GtkActivatable + * + * Gets whether this activatable should reset its layout + * and appearance when setting the related action or when + * the action changes appearance. + * + * Returns: whether @activatable uses its actions appearance. + * + * Since: 2.16 + **/ +gboolean +gtk_activatable_get_use_action_appearance (GtkActivatable *activatable) +{ + gboolean use_appearance; + + g_object_get (activatable, "use-action-appearance", &use_appearance, NULL); + + return use_appearance; +} + +#define __GTK_ACTIVATABLE_C__ +#include "gtkaliasdef.c" diff --git a/gtk/gtkactivatable.h b/gtk/gtkactivatable.h new file mode 100644 index 0000000000..75478899ed --- /dev/null +++ b/gtk/gtkactivatable.h @@ -0,0 +1,88 @@ +/* GTK - The GIMP Toolkit + * Copyright (C) 2008 Tristan Van Berkom <tristan.van.berkom@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library 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. + */ + +#if defined(GTK_DISABLE_SINGLE_INCLUDES) && !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) +#error "Only <gtk/gtk.h> can be included directly." +#endif + +#ifndef __GTK_ACTIVATABLE_H__ +#define __GTK_ACTIVATABLE_H__ + +#include <gtk/gtkaction.h> +#include <gtk/gtktypeutils.h> + +G_BEGIN_DECLS + +#define GTK_TYPE_ACTIVATABLE (gtk_activatable_get_type ()) +#define GTK_ACTIVATABLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_ACTIVATABLE, GtkActivatable)) +#define GTK_ACTIVATABLE_CLASS(obj) (G_TYPE_CHECK_CLASS_CAST ((obj), GTK_TYPE_ACTIVATABLE, GtkActivatableIface)) +#define GTK_IS_ACTIVATABLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_ACTIVATABLE)) +#define GTK_ACTIVATABLE_GET_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), GTK_TYPE_ACTIVATABLE, GtkActivatableIface)) + + +typedef struct _GtkActivatable GtkActivatable; /* Dummy typedef */ +typedef struct _GtkActivatableIface GtkActivatableIface; + + +/** + * GtkActivatableIface: + * @update: Called to update the activatable when its related action's properties change. + * You must check the #GtkActivatable:use-action-appearance property only apply action + * properties that are meant to effect the appearance accordingly. + * @reset: Called to update the activatable completely, this is called internally when + * #GtkActivatable::related-action property is set or unset and by the implementor when + * #GtkActivatable::use-action-appearance changes.<note><para>This method can be called + * with a %NULL action at times</para></note> + * + * Since: 2.16 + */ + +struct _GtkActivatableIface +{ + GTypeInterface g_iface; + + /* virtual table */ + void (* update) (GtkActivatable *activatable, + GtkAction *action, + const gchar *property_name); + void (* reset) (GtkActivatable *activatable, + GtkAction *action); +}; + + +GType gtk_activatable_get_type (void) G_GNUC_CONST; + +void gtk_activatable_reset (GtkActivatable *activatable, + GtkAction *action); + +void gtk_activatable_set_related_action (GtkActivatable *activatable, + GtkAction *action); +GtkAction *gtk_activatable_get_related_action (GtkActivatable *activatable); + +void gtk_activatable_set_use_action_appearance (GtkActivatable *activatable, + gboolean use_appearance); +gboolean gtk_activatable_get_use_action_appearance (GtkActivatable *activatable); + +/* For use in activatable implementations */ +void gtk_activatable_do_set_related_action (GtkActivatable *activatable, + GtkAction *action); + +G_END_DECLS + +#endif /* __GTK_ACTIVATABLE_H__ */ diff --git a/gtk/gtkbutton.c b/gtk/gtkbutton.c index 234f0367ec..d882ca130b 100644 --- a/gtk/gtkbutton.c +++ b/gtk/gtkbutton.c @@ -36,6 +36,7 @@ #include "gtkvbox.h" #include "gtkstock.h" #include "gtkiconfactory.h" +#include "gtkactivatable.h" #include "gtkprivate.h" #include "gtkintl.h" #include "gtkalias.h" @@ -69,7 +70,11 @@ enum { PROP_FOCUS_ON_CLICK, PROP_XALIGN, PROP_YALIGN, - PROP_IMAGE_POSITION + PROP_IMAGE_POSITION, + + /* activatable properties */ + PROP_ACTIVATABLE_RELATED_ACTION, + PROP_ACTIVATABLE_USE_ACTION_APPEARANCE }; #define GTK_BUTTON_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GTK_TYPE_BUTTON, GtkButtonPrivate)) @@ -80,14 +85,17 @@ struct _GtkButtonPrivate gfloat xalign; gfloat yalign; GtkWidget *image; - guint align_set : 1; - guint image_is_stock : 1; - guint has_grab : 1; + guint align_set : 1; + guint image_is_stock : 1; + guint has_grab : 1; + guint use_action_appearance : 1; guint32 grab_time; GtkPositionType image_position; + GtkAction *action; }; static void gtk_button_destroy (GtkObject *object); +static void gtk_button_dispose (GObject *object); static void gtk_button_set_property (GObject *object, guint prop_id, const GValue *value, @@ -124,6 +132,7 @@ static gint gtk_button_leave_notify (GtkWidget *widget, GdkEventCrossing *event); static void gtk_real_button_pressed (GtkButton *button); static void gtk_real_button_released (GtkButton *button); +static void gtk_real_button_clicked (GtkButton *button); static void gtk_real_button_activate (GtkButton *button); static void gtk_button_update_state (GtkButton *button); static void gtk_button_add (GtkContainer *container, @@ -142,9 +151,22 @@ static void gtk_button_grab_notify (GtkWidget *widget, gboolean was_grabbed); +static void gtk_button_activatable_interface_init (GtkActivatableIface *iface); +static void gtk_button_activatable_update (GtkActivatable *activatable, + GtkAction *action, + const gchar *property_name); +static void gtk_button_activatable_reset (GtkActivatable *activatable, + GtkAction *action); +static void gtk_button_set_related_action (GtkButton *button, + GtkAction *action); +static void gtk_button_set_use_action_appearance (GtkButton *button, + gboolean use_appearance); + static guint button_signals[LAST_SIGNAL] = { 0 }; -G_DEFINE_TYPE (GtkButton, gtk_button, GTK_TYPE_BIN) +G_DEFINE_TYPE_WITH_CODE (GtkButton, gtk_button, GTK_TYPE_BIN, + G_IMPLEMENT_INTERFACE (GTK_TYPE_ACTIVATABLE, + gtk_button_activatable_interface_init)) static void gtk_button_class_init (GtkButtonClass *klass) @@ -159,7 +181,8 @@ gtk_button_class_init (GtkButtonClass *klass) widget_class = (GtkWidgetClass*) klass; container_class = (GtkContainerClass*) klass; - gobject_class->constructor = gtk_button_constructor; + gobject_class->constructor = gtk_button_constructor; + gobject_class->dispose = gtk_button_dispose; gobject_class->set_property = gtk_button_set_property; gobject_class->get_property = gtk_button_get_property; @@ -188,7 +211,7 @@ gtk_button_class_init (GtkButtonClass *klass) klass->pressed = gtk_real_button_pressed; klass->released = gtk_real_button_released; - klass->clicked = NULL; + klass->clicked = gtk_real_button_clicked; klass->enter = gtk_button_update_state; klass->leave = gtk_button_update_state; klass->activate = gtk_real_button_activate; @@ -303,6 +326,9 @@ gtk_button_class_init (GtkButtonClass *klass) GTK_POS_LEFT, GTK_PARAM_READWRITE)); + g_object_class_override_property (gobject_class, PROP_ACTIVATABLE_RELATED_ACTION, "related-action"); + g_object_class_override_property (gobject_class, PROP_ACTIVATABLE_USE_ACTION_APPEARANCE, "use-action-appearance"); + /** * GtkButton::pressed: * @button: the object that received the signal @@ -521,6 +547,7 @@ gtk_button_init (GtkButton *button) priv->align_set = 0; priv->image_is_stock = TRUE; priv->image_position = GTK_POS_LEFT; + priv->use_action_appearance = TRUE; } static void @@ -600,6 +627,20 @@ gtk_button_add (GtkContainer *container, GTK_CONTAINER_CLASS (gtk_button_parent_class)->add (container, widget); } +static void +gtk_button_dispose (GObject *object) +{ + GtkButton *button = GTK_BUTTON (object); + GtkButtonPrivate *priv = GTK_BUTTON_GET_PRIVATE (button); + + if (priv->action) + { + gtk_activatable_do_set_related_action (GTK_ACTIVATABLE (button), NULL); + priv->action = NULL; + } + G_OBJECT_CLASS (gtk_button_parent_class)->dispose (object); +} + static void gtk_button_set_property (GObject *object, guint prop_id, @@ -638,6 +679,12 @@ gtk_button_set_property (GObject *object, case PROP_IMAGE_POSITION: gtk_button_set_image_position (button, g_value_get_enum (value)); break; + case PROP_ACTIVATABLE_RELATED_ACTION: + gtk_button_set_related_action (button, g_value_get_object (value)); + break; + case PROP_ACTIVATABLE_USE_ACTION_APPEARANCE: + gtk_button_set_use_action_appearance (button, g_value_get_boolean (value)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -682,12 +729,170 @@ gtk_button_get_property (GObject *object, case PROP_IMAGE_POSITION: g_value_set_enum (value, priv->image_position); break; + case PROP_ACTIVATABLE_RELATED_ACTION: + g_value_set_object (value, priv->action); + break; + case PROP_ACTIVATABLE_USE_ACTION_APPEARANCE: + g_value_set_boolean (value, priv->use_action_appearance); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } +static void +gtk_button_activatable_interface_init (GtkActivatableIface *iface) +{ + iface->update = gtk_button_activatable_update; + iface->reset = gtk_button_activatable_reset; +} + +static void +activatable_update_stock_id (GtkButton *button, + GtkAction *action) +{ + if (!gtk_button_get_use_stock (button)) + return; + + gtk_button_set_label (button, gtk_action_get_stock_id (action)); +} + +static void +activatable_update_short_label (GtkButton *button, + GtkAction *action) +{ + GtkWidget *image; + + if (gtk_button_get_use_stock (button)) + return; + + image = gtk_button_get_image (button); + + /* Dont touch custom child... */ + if (GTK_IS_IMAGE (image) || + GTK_BIN (button)->child == NULL || + GTK_IS_LABEL (GTK_BIN (button)->child)) + { + gtk_button_set_label (button, gtk_action_get_short_label (action)); + gtk_button_set_use_underline (button, TRUE); + } +} + +static void +activatable_update_icon_name (GtkButton *button, + GtkAction *action) +{ + GtkWidget *image; + + if (gtk_button_get_use_stock (button)) + return; + + image = gtk_button_get_image (button); + + if (GTK_IS_IMAGE (image) && + (gtk_image_get_storage_type (GTK_IMAGE (image)) == GTK_IMAGE_EMPTY || + gtk_image_get_storage_type (GTK_IMAGE (image)) == GTK_IMAGE_ICON_NAME)) + gtk_image_set_from_icon_name (GTK_IMAGE (image), + gtk_action_get_icon_name (action), GTK_ICON_SIZE_MENU); +} + +static void +activatable_update_gicon (GtkButton *button, + GtkAction *action) +{ + GtkWidget *image = gtk_button_get_image (button); + GIcon *icon = gtk_action_get_gicon (action); + + if (GTK_IS_IMAGE (image) && + (gtk_image_get_storage_type (GTK_IMAGE (image)) == GTK_IMAGE_EMPTY || + gtk_image_get_storage_type (GTK_IMAGE (image)) == GTK_IMAGE_GICON)) + gtk_image_set_from_gicon (GTK_IMAGE (image), icon, GTK_ICON_SIZE_BUTTON); +} + +static void +gtk_button_activatable_update (GtkActivatable *activatable, + GtkAction *action, + const gchar *property_name) +{ + GtkButtonPrivate *priv = GTK_BUTTON_GET_PRIVATE (activatable); + + if (strcmp (property_name, "visible") == 0) + { + if (gtk_action_is_visible (action)) + gtk_widget_show (GTK_WIDGET (activatable)); + else + gtk_widget_hide (GTK_WIDGET (activatable)); + } + else if (strcmp (property_name, "sensitive") == 0) + gtk_widget_set_sensitive (GTK_WIDGET (activatable), gtk_action_is_sensitive (action)); + + if (!priv->use_action_appearance) + return; + + if (strcmp (property_name, "stock-id") == 0) + activatable_update_stock_id (GTK_BUTTON (activatable), action); + else if (strcmp (property_name, "gicon") == 0) + activatable_update_gicon (GTK_BUTTON (activatable), action); + else if (strcmp (property_name, "short-label") == 0) + activatable_update_short_label (GTK_BUTTON (activatable), action); + else if (strcmp (property_name, "icon-name") == 0) + activatable_update_icon_name (GTK_BUTTON (activatable), action); +} + +static void +gtk_button_activatable_reset (GtkActivatable *activatable, + GtkAction *action) +{ + GtkButtonPrivate *priv = GTK_BUTTON_GET_PRIVATE (activatable); + + if (!action) + return; + + if (gtk_action_is_visible (action)) + gtk_widget_show (GTK_WIDGET (activatable)); + else + gtk_widget_hide (GTK_WIDGET (activatable)); + + gtk_widget_set_sensitive (GTK_WIDGET (activatable), gtk_action_is_sensitive (action)); + + if (priv->use_action_appearance) + { + activatable_update_stock_id (GTK_BUTTON (activatable), action); + activatable_update_short_label (GTK_BUTTON (activatable), action); + activatable_update_gicon (GTK_BUTTON (activatable), action); + activatable_update_icon_name (GTK_BUTTON (activatable), action); + } +} + +static void +gtk_button_set_related_action (GtkButton *button, + GtkAction *action) +{ + GtkButtonPrivate *priv = GTK_BUTTON_GET_PRIVATE (button); + + if (priv->action == action) + return; + + gtk_activatable_do_set_related_action (GTK_ACTIVATABLE (button), action); + + priv->action = action; +} + +static void +gtk_button_set_use_action_appearance (GtkButton *button, + gboolean use_appearance) +{ + GtkButtonPrivate *priv = GTK_BUTTON_GET_PRIVATE (button); + + if (priv->use_action_appearance != use_appearance) + { + priv->use_action_appearance = use_appearance; + + gtk_activatable_reset (GTK_ACTIVATABLE (button), priv->action); + } +} + GtkWidget* gtk_button_new (void) { @@ -1494,6 +1699,15 @@ gtk_real_button_released (GtkButton *button) } } +static void +gtk_real_button_clicked (GtkButton *button) +{ + GtkButtonPrivate *priv = GTK_BUTTON_GET_PRIVATE (button); + + if (priv->action) + gtk_action_activate (priv->action); +} + static gboolean button_activate_timeout (gpointer data) { diff --git a/gtk/gtkcheckmenuitem.c b/gtk/gtkcheckmenuitem.c index a179ab24e0..e65370b0f1 100644 --- a/gtk/gtkcheckmenuitem.c +++ b/gtk/gtkcheckmenuitem.c @@ -27,6 +27,8 @@ #include "config.h" #include "gtkcheckmenuitem.h" #include "gtkaccellabel.h" +#include "gtkactivatable.h" +#include "gtktoggleaction.h" #include "gtkmarshalers.h" #include "gtkprivate.h" #include "gtkintl.h" @@ -53,19 +55,28 @@ static void gtk_check_menu_item_draw_indicator (GtkCheckMenuItem *che GdkRectangle *area); static void gtk_real_check_menu_item_draw_indicator (GtkCheckMenuItem *check_menu_item, GdkRectangle *area); -static void gtk_check_menu_item_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec); -static void gtk_check_menu_item_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec); - - -static guint check_menu_item_signals[LAST_SIGNAL] = { 0 }; - -G_DEFINE_TYPE (GtkCheckMenuItem, gtk_check_menu_item, GTK_TYPE_MENU_ITEM) +static void gtk_check_menu_item_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec); +static void gtk_check_menu_item_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec); + +static void gtk_check_menu_item_activatable_interface_init (GtkActivatableIface *iface); +static void gtk_check_menu_item_activatable_update (GtkActivatable *activatable, + GtkAction *action, + const gchar *property_name); +static void gtk_check_menu_item_activatable_reset (GtkActivatable *activatable, + GtkAction *action); + +static GtkActivatableIface *parent_activatable_iface; +static guint check_menu_item_signals[LAST_SIGNAL] = { 0 }; + +G_DEFINE_TYPE_WITH_CODE (GtkCheckMenuItem, gtk_check_menu_item, GTK_TYPE_MENU_ITEM, + G_IMPLEMENT_INTERFACE (GTK_TYPE_ACTIVATABLE, + gtk_check_menu_item_activatable_interface_init)) static void gtk_check_menu_item_class_init (GtkCheckMenuItemClass *klass) @@ -107,8 +118,7 @@ gtk_check_menu_item_class_init (GtkCheckMenuItemClass *klass) gtk_widget_class_install_style_property (widget_class, g_param_spec_int ("indicator-size", - P_("Indicator Size") -, + P_("Indicator Size"), P_("Size of check or radio indicator"), 0, G_MAXINT, @@ -134,6 +144,64 @@ gtk_check_menu_item_class_init (GtkCheckMenuItemClass *klass) G_TYPE_NONE, 0); } +static void +gtk_check_menu_item_activatable_interface_init (GtkActivatableIface *iface) +{ + parent_activatable_iface = g_type_interface_peek_parent (iface); + iface->update = gtk_check_menu_item_activatable_update; + iface->reset = gtk_check_menu_item_activatable_reset; +} + +static void +gtk_check_menu_item_activatable_update (GtkActivatable *activatable, + GtkAction *action, + const gchar *property_name) +{ + GtkCheckMenuItem *check_menu_item; + + check_menu_item = GTK_CHECK_MENU_ITEM (activatable); + + parent_activatable_iface->update (activatable, action, property_name); + + if (strcmp (property_name, "active") == 0) + { + gtk_action_block_activate (action); + gtk_check_menu_item_set_active (check_menu_item, gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))); + gtk_action_unblock_activate (action); + } + + if (!gtk_activatable_get_use_action_appearance (activatable)) + return; + + if (strcmp (property_name, "draw-as-radio") == 0) + gtk_check_menu_item_set_draw_as_radio (check_menu_item, + gtk_toggle_action_get_draw_as_radio (GTK_TOGGLE_ACTION (action))); +} + +static void +gtk_check_menu_item_activatable_reset (GtkActivatable *activatable, + GtkAction *action) +{ + GtkCheckMenuItem *check_menu_item; + + check_menu_item = GTK_CHECK_MENU_ITEM (activatable); + + parent_activatable_iface->reset (activatable, action); + + if (!action) + return; + + gtk_action_block_activate (action); + gtk_check_menu_item_set_active (check_menu_item, gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))); + gtk_action_unblock_activate (action); + + if (!gtk_activatable_get_use_action_appearance (activatable)) + return; + + gtk_check_menu_item_set_draw_as_radio (check_menu_item, + gtk_toggle_action_get_draw_as_radio (GTK_TOGGLE_ACTION (action))); +} + GtkWidget* gtk_check_menu_item_new (void) { @@ -351,6 +419,8 @@ gtk_check_menu_item_activate (GtkMenuItem *menu_item) gtk_check_menu_item_toggled (check_menu_item); gtk_widget_queue_draw (GTK_WIDGET (check_menu_item)); + GTK_MENU_ITEM_CLASS (gtk_check_menu_item_parent_class)->activate (menu_item); + g_object_notify (G_OBJECT (check_menu_item), "active"); } diff --git a/gtk/gtkimagemenuitem.c b/gtk/gtkimagemenuitem.c index c4a1c8facb..27b641f60d 100644 --- a/gtk/gtkimagemenuitem.c +++ b/gtk/gtkimagemenuitem.c @@ -34,6 +34,7 @@ #include "gtkmenubar.h" #include "gtkcontainer.h" #include "gtkwindow.h" +#include "gtkactivatable.h" #include "gtkprivate.h" #include "gtkalias.h" @@ -70,6 +71,12 @@ static void gtk_image_menu_item_screen_changed (GtkWidget *widget, static void gtk_image_menu_item_recalculate (GtkImageMenuItem *image_menu_item); +static void gtk_image_menu_item_activatable_interface_init (GtkActivatableIface *iface); +static void gtk_image_menu_item_activatable_update (GtkActivatable *activatable, + GtkAction *action, + const gchar *property_name); +static void gtk_image_menu_item_activatable_reset (GtkActivatable *activatable, + GtkAction *action); typedef struct { gchar *label; @@ -83,7 +90,12 @@ enum { PROP_ACCEL_GROUP }; -G_DEFINE_TYPE (GtkImageMenuItem, gtk_image_menu_item, GTK_TYPE_MENU_ITEM) +static GtkActivatableIface *parent_activatable_iface; + + +G_DEFINE_TYPE_WITH_CODE (GtkImageMenuItem, gtk_image_menu_item, GTK_TYPE_MENU_ITEM, + G_IMPLEMENT_INTERFACE (GTK_TYPE_ACTIVATABLE, + gtk_image_menu_item_activatable_interface_init)) #define GET_PRIVATE(object) \ (G_TYPE_INSTANCE_GET_PRIVATE ((object), GTK_TYPE_IMAGE_MENU_ITEM, GtkImageMenuItemPrivate)) @@ -504,6 +516,134 @@ gtk_image_menu_item_forall (GtkContainer *container, (* callback) (image_menu_item->image, callback_data); } + +static void +gtk_image_menu_item_activatable_interface_init (GtkActivatableIface *iface) +{ + parent_activatable_iface = g_type_interface_peek_parent (iface); + iface->update = gtk_image_menu_item_activatable_update; + iface->reset = gtk_image_menu_item_activatable_reset; +} + +static gboolean +activatable_update_stock_id (GtkImageMenuItem *image_menu_item, GtkAction *action) +{ + GtkWidget *image; + const gchar *stock_id = gtk_action_get_stock_id (action); + + image = gtk_image_menu_item_get_image (image_menu_item); + + if (GTK_IS_IMAGE (image) && + stock_id && gtk_icon_factory_lookup_default (stock_id)) + { + gtk_image_set_from_stock (GTK_IMAGE (image), stock_id, GTK_ICON_SIZE_MENU); + return TRUE; + } + + return FALSE; +} + +static gboolean +activatable_update_gicon (GtkImageMenuItem *image_menu_item, GtkAction *action) +{ + GtkWidget *image; + GIcon *icon = gtk_action_get_gicon (action); + const gchar *stock_id = gtk_action_get_stock_id (action); + + image = gtk_image_menu_item_get_image (image_menu_item); + + if (icon && GTK_IS_IMAGE (image) && + !(stock_id && gtk_icon_factory_lookup_default (stock_id))) + { + gtk_image_set_from_gicon (GTK_IMAGE (image), icon, GTK_ICON_SIZE_MENU); + return TRUE; + } + + return FALSE; +} + +static void +activatable_update_icon_name (GtkImageMenuItem *image_menu_item, GtkAction *action) +{ + GtkWidget *image; + const gchar *icon_name = gtk_action_get_icon_name (action); + + image = gtk_image_menu_item_get_image (image_menu_item); + + if (GTK_IS_IMAGE (image) && icon_name && + (gtk_image_get_storage_type (GTK_IMAGE (image)) == GTK_IMAGE_EMPTY || + gtk_image_get_storage_type (GTK_IMAGE (image)) == GTK_IMAGE_ICON_NAME)) + { + gtk_image_set_from_icon_name (GTK_IMAGE (image), icon_name, GTK_ICON_SIZE_MENU); + } +} + +static void +gtk_image_menu_item_activatable_update (GtkActivatable *activatable, + GtkAction *action, + const gchar *property_name) +{ + GtkImageMenuItem *image_menu_item; + GtkWidget *image; + gboolean use_appearance; + + image_menu_item = GTK_IMAGE_MENU_ITEM (activatable); + + parent_activatable_iface->update (activatable, action, property_name); + + use_appearance = gtk_activatable_get_use_action_appearance (activatable); + if (!use_appearance) + return; + + if (strcmp (property_name, "stock-id") == 0) + activatable_update_stock_id (image_menu_item, action); + else if (strcmp (property_name, "gicon") == 0) + activatable_update_gicon (image_menu_item, action); + else if (strcmp (property_name, "icon-name") == 0) + activatable_update_icon_name (image_menu_item, action); +} + +static void +gtk_image_menu_item_activatable_reset (GtkActivatable *activatable, + GtkAction *action) +{ + GtkImageMenuItem *image_menu_item; + GtkWidget *image; + gboolean use_appearance; + + image_menu_item = GTK_IMAGE_MENU_ITEM (activatable); + + parent_activatable_iface->reset (activatable, action); + + if (!action) + return; + + use_appearance = gtk_activatable_get_use_action_appearance (activatable); + if (!use_appearance) + return; + + image = gtk_image_menu_item_get_image (image_menu_item); + if (image && !GTK_IS_IMAGE (image)) + { + gtk_image_menu_item_set_image (image_menu_item, NULL); + image = NULL; + } + + if (!image) + { + image = gtk_image_new (); + gtk_widget_show (image); + gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (activatable), + image); + } + + if (!activatable_update_stock_id (image_menu_item, action) && + !activatable_update_gicon (image_menu_item, action)) + activatable_update_icon_name (image_menu_item, action); + +} + + /** * gtk_image_menu_item_new: * @returns: a new #GtkImageMenuItem. diff --git a/gtk/gtkmenuitem.c b/gtk/gtkmenuitem.c index 9d8b33523f..03eca0bf59 100644 --- a/gtk/gtkmenuitem.c +++ b/gtk/gtkmenuitem.c @@ -37,9 +37,16 @@ #include "gtkseparatormenuitem.h" #include "gtkprivate.h" #include "gtkbuildable.h" +#include "gtkactivatable.h" #include "gtkintl.h" #include "gtkalias.h" + +typedef struct { + GtkAction *action; + gboolean use_action_appearance; +} GtkMenuItemPrivate; + enum { ACTIVATE, ACTIVATE_ITEM, @@ -54,10 +61,15 @@ enum { PROP_SUBMENU, PROP_ACCEL_PATH, PROP_LABEL, - PROP_USE_UNDERLINE + PROP_USE_UNDERLINE, + + /* activatable properties */ + PROP_ACTIVATABLE_RELATED_ACTION, + PROP_ACTIVATABLE_USE_ACTION_APPEARANCE }; +static void gtk_menu_item_dispose (GObject *object); static void gtk_menu_item_set_property (GObject *object, guint prop_id, const GValue *value, @@ -85,6 +97,7 @@ static void gtk_menu_item_parent_set (GtkWidget *widget, 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 (GtkMenuItem *item); static void gtk_real_menu_item_activate_item (GtkMenuItem *item); static void gtk_real_menu_item_toggle_size_request (GtkMenuItem *menu_item, gint *requisition); @@ -120,13 +133,30 @@ static void gtk_menu_item_buildable_add_child (GtkBuildable *buildab GObject *child, const gchar *type); +static void gtk_menu_item_activatable_interface_init (GtkActivatableIface *iface); +static void gtk_menu_item_activatable_update (GtkActivatable *activatable, + GtkAction *action, + const gchar *property_name); +static void gtk_menu_item_activatable_reset (GtkActivatable *activatable, + GtkAction *action); +static void gtk_menu_item_set_related_action (GtkMenuItem *menu_item, + GtkAction *action); +static void gtk_menu_item_set_use_action_appearance (GtkMenuItem *menu_item, + gboolean use_appearance); + + static guint menu_item_signals[LAST_SIGNAL] = { 0 }; static GtkBuildableIface *parent_buildable_iface; G_DEFINE_TYPE_WITH_CODE (GtkMenuItem, gtk_menu_item, GTK_TYPE_ITEM, G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, - gtk_menu_item_buildable_interface_init)) + gtk_menu_item_buildable_interface_init) + G_IMPLEMENT_INTERFACE (GTK_TYPE_ACTIVATABLE, + gtk_menu_item_activatable_interface_init)) + +#define GET_PRIVATE(object) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((object), GTK_TYPE_MENU_ITEM, GtkMenuItemPrivate)) static void gtk_menu_item_class_init (GtkMenuItemClass *klass) @@ -137,6 +167,7 @@ gtk_menu_item_class_init (GtkMenuItemClass *klass) GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass); GtkItemClass *item_class = GTK_ITEM_CLASS (klass); + gobject_class->dispose = gtk_menu_item_dispose; gobject_class->set_property = gtk_menu_item_set_property; gobject_class->get_property = gtk_menu_item_get_property; @@ -157,15 +188,15 @@ gtk_menu_item_class_init (GtkMenuItemClass *klass) container_class->forall = gtk_menu_item_forall; - item_class->select = gtk_real_menu_item_select; - item_class->deselect = gtk_real_menu_item_deselect; + 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->activate = gtk_real_menu_item_activate; + 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->set_label = gtk_real_menu_item_set_label; - klass->get_label = gtk_real_menu_item_get_label; + klass->set_label = gtk_real_menu_item_set_label; + klass->get_label = gtk_real_menu_item_get_label; klass->hide_on_activate = TRUE; @@ -288,6 +319,9 @@ gtk_menu_item_class_init (GtkMenuItemClass *klass) FALSE, GTK_PARAM_READWRITE)); + g_object_class_override_property (gobject_class, PROP_ACTIVATABLE_RELATED_ACTION, "related-action"); + g_object_class_override_property (gobject_class, PROP_ACTIVATABLE_USE_ACTION_APPEARANCE, "use-action-appearance"); + gtk_widget_class_install_style_property_parser (widget_class, g_param_spec_enum ("selected-shadow-type", "Selected Shadow Type", @@ -344,12 +378,19 @@ gtk_menu_item_class_init (GtkMenuItemClass *klass) P_("The minimum desired width of the menu item in characters"), 0, G_MAXINT, 12, GTK_PARAM_READABLE)); + + g_type_class_add_private (object_class, sizeof (GtkMenuItemPrivate)); } static void gtk_menu_item_init (GtkMenuItem *menu_item) { + GtkMenuItemPrivate *priv = GET_PRIVATE (menu_item); + GTK_WIDGET_SET_FLAGS (menu_item, GTK_NO_WINDOW); + + priv->action = NULL; + priv->use_action_appearance = TRUE; menu_item->submenu = NULL; menu_item->toggle_size = 0; @@ -399,6 +440,22 @@ gtk_menu_item_new_with_mnemonic (const gchar *label) NULL); } +static void +gtk_menu_item_dispose (GObject *object) +{ + GtkMenuItem *menu_item = GTK_MENU_ITEM (object); + GtkMenuItemPrivate *priv = GET_PRIVATE (menu_item); + + if (priv->action) + { + gtk_action_disconnect_accelerator (priv->action); + gtk_activatable_do_set_related_action (GTK_ACTIVATABLE (menu_item), NULL); + + priv->action = NULL; + } + G_OBJECT_CLASS (gtk_menu_item_parent_class)->dispose (object); +} + static void gtk_menu_item_set_property (GObject *object, guint prop_id, @@ -424,7 +481,12 @@ gtk_menu_item_set_property (GObject *object, case PROP_USE_UNDERLINE: gtk_menu_item_set_use_underline (menu_item, g_value_get_boolean (value)); break; - + case PROP_ACTIVATABLE_RELATED_ACTION: + gtk_menu_item_set_related_action (menu_item, g_value_get_object (value)); + break; + case PROP_ACTIVATABLE_USE_ACTION_APPEARANCE: + gtk_menu_item_set_use_action_appearance (menu_item, g_value_get_boolean (value)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -438,6 +500,7 @@ gtk_menu_item_get_property (GObject *object, GParamSpec *pspec) { GtkMenuItem *menu_item = GTK_MENU_ITEM (object); + GtkMenuItemPrivate *priv = GET_PRIVATE (menu_item); switch (prop_id) { @@ -456,7 +519,12 @@ gtk_menu_item_get_property (GObject *object, case PROP_USE_UNDERLINE: g_value_set_boolean (value, gtk_menu_item_get_use_underline (menu_item)); break; - + case PROP_ACTIVATABLE_RELATED_ACTION: + g_value_set_object (value, priv->action); + break; + case PROP_ACTIVATABLE_USE_ACTION_APPEARANCE: + g_value_set_boolean (value, priv->use_action_appearance); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -505,6 +573,138 @@ gtk_menu_item_buildable_add_child (GtkBuildable *buildable, parent_buildable_iface->add_child (buildable, builder, child, type); } +static void +gtk_menu_item_activatable_interface_init (GtkActivatableIface *iface) +{ + iface->update = gtk_menu_item_activatable_update; + iface->reset = gtk_menu_item_activatable_reset; +} + +static void +activatable_update_label (GtkMenuItem *menu_item, GtkAction *action) +{ + GtkWidget *child = GTK_BIN (menu_item)->child; + + if (GTK_IS_LABEL (child)) + { + const gchar *label; + + label = gtk_action_get_label (action); + gtk_label_set_label (GTK_LABEL (child), label ? label : ""); + } +} + +gboolean _gtk_menu_is_empty (GtkWidget *menu); + +static void +gtk_menu_item_activatable_update (GtkActivatable *activatable, + GtkAction *action, + const gchar *property_name) +{ + GtkMenuItem *menu_item = GTK_MENU_ITEM (activatable); + GtkMenuItemPrivate *priv = GET_PRIVATE (menu_item); + + if (strcmp (property_name, "visible") == 0) + _gtk_action_sync_menu_visible (action, GTK_WIDGET (menu_item), + _gtk_menu_is_empty (gtk_menu_item_get_submenu (menu_item))); + else if (strcmp (property_name, "sensitive") == 0) + gtk_widget_set_sensitive (GTK_WIDGET (menu_item), gtk_action_is_sensitive (action)); + else if (priv->use_action_appearance) + { + if (strcmp (property_name, "label") == 0) + activatable_update_label (menu_item, action); + } +} + +static void +gtk_menu_item_activatable_reset (GtkActivatable *activatable, + GtkAction *action) +{ + GtkMenuItem *menu_item = GTK_MENU_ITEM (activatable); + GtkMenuItemPrivate *priv = GET_PRIVATE (menu_item); + + if (!action) + return; + + _gtk_action_sync_menu_visible (action, GTK_WIDGET (menu_item), + _gtk_menu_is_empty (gtk_menu_item_get_submenu (menu_item))); + + gtk_widget_set_sensitive (GTK_WIDGET (menu_item), gtk_action_is_sensitive (action)); + + if (priv->use_action_appearance) + { + GtkWidget *label = GTK_BIN (menu_item)->child; + + /* make sure label is a label */ + if (label && !GTK_IS_LABEL (label)) + { + gtk_container_remove (GTK_CONTAINER (menu_item), label); + label = NULL; + } + + if (!label) + label = g_object_new (GTK_TYPE_ACCEL_LABEL, + "use-underline", TRUE, + "xalign", 0.0, + "visible", TRUE, + "parent", menu_item, + NULL); + + if (GTK_IS_ACCEL_LABEL (label) && gtk_action_get_accel_path (action)) + g_object_set (label, + "accel-closure", gtk_action_get_accel_closure (action), + NULL); + + activatable_update_label (menu_item, action); + } +} + +static void +gtk_menu_item_set_related_action (GtkMenuItem *menu_item, + GtkAction *action) +{ + GtkMenuItemPrivate *priv = GET_PRIVATE (menu_item); + + if (priv->action == action) + return; + + if (priv->action) + { + gtk_action_disconnect_accelerator (priv->action); + } + + if (action) + { + const gchar *accel_path; + + accel_path = gtk_action_get_accel_path (action); + if (accel_path) + { + gtk_action_connect_accelerator (action); + gtk_menu_item_set_accel_path (menu_item, accel_path); + } + } + + gtk_activatable_do_set_related_action (GTK_ACTIVATABLE (menu_item), action); + + priv->action = action; +} + +static void +gtk_menu_item_set_use_action_appearance (GtkMenuItem *menu_item, + gboolean use_appearance) +{ + GtkMenuItemPrivate *priv = GET_PRIVATE (menu_item); + + if (priv->use_action_appearance != use_appearance) + { + priv->use_action_appearance = use_appearance; + + gtk_activatable_reset (GTK_ACTIVATABLE (menu_item), priv->action); + } +} + + /** * gtk_menu_item_set_submenu: * @menu_item: a #GtkMenuItem @@ -629,7 +829,7 @@ void gtk_menu_item_activate (GtkMenuItem *menu_item) { g_return_if_fail (GTK_IS_MENU_ITEM (menu_item)); - + g_signal_emit (menu_item, menu_item_signals[ACTIVATE], 0); } @@ -1142,13 +1342,27 @@ gtk_menu_item_mnemonic_activate (GtkWidget *widget, return TRUE; } +static void +gtk_real_menu_item_activate (GtkMenuItem *menu_item) +{ + GtkMenuItemPrivate *priv; + + priv = GET_PRIVATE (menu_item); + + if (priv->action) + gtk_action_activate (priv->action); +} + + static void gtk_real_menu_item_activate_item (GtkMenuItem *menu_item) { + GtkMenuItemPrivate *priv; GtkWidget *widget; g_return_if_fail (GTK_IS_MENU_ITEM (menu_item)); + priv = GET_PRIVATE (menu_item); widget = GTK_WIDGET (menu_item); if (widget->parent && diff --git a/gtk/gtkradioaction.c b/gtk/gtkradioaction.c index 2b113e17cd..1be634d7f8 100644 --- a/gtk/gtkradioaction.c +++ b/gtk/gtkradioaction.c @@ -324,6 +324,8 @@ gtk_radio_action_activate (GtkAction *action) if (tmp_action->private_data->active && (tmp_action != toggle_action)) { toggle_action->private_data->active = !toggle_action->private_data->active; + + g_object_notify (G_OBJECT (action), "active"); break; } } @@ -331,6 +333,7 @@ gtk_radio_action_activate (GtkAction *action) else { toggle_action->private_data->active = !toggle_action->private_data->active; + g_object_notify (G_OBJECT (action), "active"); tmp_list = radio_action->private_data->group; while (tmp_list) diff --git a/gtk/gtkradiomenuitem.c b/gtk/gtkradiomenuitem.c index 0b33dc6c93..f17b5ed2b7 100644 --- a/gtk/gtkradiomenuitem.c +++ b/gtk/gtkradiomenuitem.c @@ -28,6 +28,7 @@ #include "gtkaccellabel.h" #include "gtkmarshalers.h" #include "gtkradiomenuitem.h" +#include "gtkactivatable.h" #include "gtkprivate.h" #include "gtkintl.h" #include "gtkalias.h" @@ -420,9 +421,14 @@ gtk_radio_menu_item_activate (GtkMenuItem *menu_item) GtkRadioMenuItem *radio_menu_item = GTK_RADIO_MENU_ITEM (menu_item); GtkCheckMenuItem *check_menu_item = GTK_CHECK_MENU_ITEM (menu_item); GtkCheckMenuItem *tmp_menu_item; + GtkAction *action; GSList *tmp_list; gint toggled; + action = gtk_activatable_get_related_action (GTK_ACTIVATABLE (menu_item)); + if (action && gtk_menu_item_get_submenu (menu_item) == NULL) + gtk_action_activate (action); + toggled = FALSE; if (check_menu_item->active) @@ -467,7 +473,9 @@ gtk_radio_menu_item_activate (GtkMenuItem *menu_item) } if (toggled) - gtk_check_menu_item_toggled (check_menu_item); + { + gtk_check_menu_item_toggled (check_menu_item); + } gtk_widget_queue_draw (GTK_WIDGET (radio_menu_item)); } diff --git a/gtk/gtkrecentaction.c b/gtk/gtkrecentaction.c index 449eaad3c7..bf5c6d081e 100644 --- a/gtk/gtkrecentaction.c +++ b/gtk/gtkrecentaction.c @@ -42,7 +42,6 @@ struct _GtkRecentActionPrivate { GtkRecentManager *manager; - guint manager_changed_id; guint show_numbers : 1; @@ -57,8 +56,8 @@ struct _GtkRecentActionPrivate GtkRecentSortType sort_type; GtkRecentSortFunc sort_func; - gpointer sort_data; - GDestroyNotify data_destroy; + gpointer sort_data; + GDestroyNotify data_destroy; GtkRecentFilter *current_filter; @@ -213,7 +212,7 @@ gtk_recent_action_set_sort_func (GtkRecentChooser *chooser, for (l = priv->choosers; l; l = l->next) { GtkRecentChooser *chooser_menu = l->data; - + gtk_recent_chooser_set_sort_func (chooser_menu, priv->sort_func, priv->sort_data, priv->data_destroy); @@ -225,7 +224,6 @@ set_current_filter (GtkRecentAction *action, GtkRecentFilter *filter) { GtkRecentActionPrivate *priv = action->priv; - GSList *l; g_object_ref (action); @@ -237,13 +235,6 @@ set_current_filter (GtkRecentAction *action, if (priv->current_filter) g_object_ref_sink (priv->current_filter); - for (l = priv->choosers; l; l = l->next) - { - GtkRecentChooser *chooser = l->data; - - gtk_recent_chooser_set_filter (chooser, priv->current_filter); - } - g_object_notify (G_OBJECT (action), "filter"); g_object_unref (action); @@ -338,21 +329,10 @@ gtk_recent_action_connect_proxy (GtkAction *action, GtkRecentAction *recent_action = GTK_RECENT_ACTION (action); GtkRecentActionPrivate *priv = recent_action->priv; + /* it can only be a recent chooser implementor anyway... */ if (GTK_IS_RECENT_CHOOSER (widget) && !g_slist_find (priv->choosers, widget)) { - g_object_set (G_OBJECT (widget), - "show-private", priv->show_private, - "show-not-found", priv->show_not_found, - "show-tips", priv->show_tips, - "show-icons", priv->show_icons, - "show-numbers", priv->show_numbers, - "limit", priv->limit, - "sort-type", priv->sort_type, - "filter", priv->current_filter, - "local-only", priv->local_only, - NULL); - if (priv->sort_func) { gtk_recent_chooser_set_sort_func (GTK_RECENT_CHOOSER (widget), @@ -369,7 +349,8 @@ gtk_recent_action_connect_proxy (GtkAction *action, action); } - GTK_ACTION_CLASS (gtk_recent_action_parent_class)->connect_proxy (action, widget); + if (GTK_ACTION_CLASS (gtk_recent_action_parent_class)->connect_proxy) + GTK_ACTION_CLASS (gtk_recent_action_parent_class)->connect_proxy (action, widget); } static void @@ -385,7 +366,8 @@ gtk_recent_action_disconnect_proxy (GtkAction *action, if (g_slist_find (priv->choosers, widget)) priv->choosers = g_slist_remove (priv->choosers, widget); - GTK_ACTION_CLASS (gtk_recent_action_parent_class)->disconnect_proxy (action, widget); + if (GTK_ACTION_CLASS (gtk_recent_action_parent_class)->disconnect_proxy) + GTK_ACTION_CLASS (gtk_recent_action_parent_class)->disconnect_proxy (action, widget); } static GtkWidget * @@ -458,38 +440,15 @@ gtk_recent_action_create_tool_item (GtkAction *action) } static void -manager_changed_cb (GtkRecentManager *manager, - gpointer user_data) -{ - /* do we need to propagate the signal to all the proxies? guess not */ -} - -static void set_recent_manager (GtkRecentAction *action, GtkRecentManager *manager) { GtkRecentActionPrivate *priv = action->priv; - if (priv->manager) - { - if (priv->manager_changed_id) - { - g_signal_handler_disconnect (priv->manager, priv->manager_changed_id); - priv->manager_changed_id = 0; - } - - priv->manager = NULL; - } - if (manager) priv->manager = NULL; else priv->manager = gtk_recent_manager_get_default (); - - if (priv->manager) - priv->manager_changed_id = g_signal_connect (priv->manager, "changed", - G_CALLBACK (manager_changed_cb), - action); } static void @@ -520,14 +479,6 @@ gtk_recent_action_dispose (GObject *gobject) GtkRecentAction *action = GTK_RECENT_ACTION (gobject); GtkRecentActionPrivate *priv = action->priv; - if (priv->manager_changed_id) - { - if (priv->manager) - g_signal_handler_disconnect (priv->manager, priv->manager_changed_id); - - priv->manager_changed_id = 0; - } - if (priv->current_filter) { g_object_unref (priv->current_filter); @@ -545,7 +496,6 @@ gtk_recent_action_set_property (GObject *gobject, { GtkRecentAction *action = GTK_RECENT_ACTION (gobject); GtkRecentActionPrivate *priv = action->priv; - GSList *l; switch (prop_id) { @@ -574,33 +524,20 @@ gtk_recent_action_set_property (GObject *gobject, priv->sort_type = g_value_get_enum (value); break; case GTK_RECENT_CHOOSER_PROP_FILTER: - /* this already iterates over the choosers list */ set_current_filter (action, g_value_get_object (value)); - return; + break; case GTK_RECENT_CHOOSER_PROP_SELECT_MULTIPLE: g_warning ("%s: Choosers of type `%s' do not support selecting multiple items.", G_STRFUNC, G_OBJECT_TYPE_NAME (gobject)); return; case GTK_RECENT_CHOOSER_PROP_RECENT_MANAGER: - /* this is a construct-only property; we set the recent-manager - * of the choosers with this value when we create them, so there's - * no need to iterate later. - */ set_recent_manager (action, g_value_get_object (value)); - return; + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); return; } - - /* propagate the properties to the proxies we have created */ - for (l = priv->choosers; l != NULL; l = l->next) - { - GObject *proxy = l->data; - - g_object_set_property (proxy, pspec->name, value); - } } static void @@ -707,7 +644,6 @@ gtk_recent_action_init (GtkRecentAction *action) priv->current_filter = NULL; priv->manager = NULL; - priv->manager_changed_id = 0; } /** diff --git a/gtk/gtkrecentchooser.c b/gtk/gtkrecentchooser.c index 31157b98d7..298a125300 100644 --- a/gtk/gtkrecentchooser.c +++ b/gtk/gtkrecentchooser.c @@ -24,6 +24,8 @@ #include "gtkrecentchooser.h" #include "gtkrecentchooserprivate.h" #include "gtkrecentmanager.h" +#include "gtkrecentaction.h" +#include "gtkactivatable.h" #include "gtkintl.h" #include "gtktypebuiltins.h" #include "gtkprivate.h" @@ -39,7 +41,14 @@ enum LAST_SIGNAL }; -static void gtk_recent_chooser_class_init (gpointer g_iface); +static void gtk_recent_chooser_class_init (gpointer g_iface); +static gboolean recent_chooser_has_show_numbers (GtkRecentChooser *chooser); + +static GQuark quark_gtk_related_action = 0; +static GQuark quark_gtk_use_action_appearance = 0; +static const gchar gtk_related_action_key[] = "gtk-related-action"; +static const gchar gtk_use_action_appearance_key[] = "gtk-use-action-appearance"; + static guint chooser_signals[LAST_SIGNAL] = { 0, }; @@ -66,6 +75,9 @@ static void gtk_recent_chooser_class_init (gpointer g_iface) { GType iface_type = G_TYPE_FROM_INTERFACE (g_iface); + + quark_gtk_related_action = g_quark_from_static_string (gtk_related_action_key); + quark_gtk_use_action_appearance = g_quark_from_static_string (gtk_use_action_appearance_key); /** * GtkRecentChooser::selection-changed @@ -575,6 +587,25 @@ gtk_recent_chooser_get_show_tips (GtkRecentChooser *chooser) return show_tips; } +static gboolean +recent_chooser_has_show_numbers (GtkRecentChooser *chooser) +{ + GParamSpec *pspec; + + /* This is the result of a minor screw up: the "show-numbers" property + * was removed from the GtkRecentChooser interface, but the accessors + * remained in the interface API; now we need to check whether the + * implementation of the RecentChooser interface has a "show-numbers" + * boolean property installed before accessing it, and avoid an + * assertion failure using a more graceful warning. This should really + * go away as soon as we can break API and remove these accessors. + */ + pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (chooser), + "show-numbers"); + + return (pspec && pspec->value_type == G_TYPE_BOOLEAN); +} + /** * gtk_recent_chooser_set_show_numbers: * @chooser: a #GtkRecentChooser @@ -590,25 +621,13 @@ void gtk_recent_chooser_set_show_numbers (GtkRecentChooser *chooser, gboolean show_numbers) { - GParamSpec *pspec; - g_return_if_fail (GTK_IS_RECENT_CHOOSER (chooser)); - /* This is the result of a minor screw up: the "show-numbers" property - * was removed from the GtkRecentChooser interface, but the accessors - * remained in the interface API; now we need to check whether the - * implementation of the RecentChooser interface has a "show-numbers" - * boolean property installed before accessing it, and avoid an - * assertion failure using a more graceful warning. This should really - * go away as soon as we can break API and remove these accessors. - */ - pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (chooser), - "show-numbers"); - if (!pspec || pspec->value_type != G_TYPE_BOOLEAN) + if (!recent_chooser_has_show_numbers (chooser)) { g_warning ("Choosers of type `%s' do not support showing numbers", G_OBJECT_TYPE_NAME (chooser)); - + return; } @@ -1074,5 +1093,107 @@ _gtk_recent_chooser_selection_changed (GtkRecentChooser *chooser) g_signal_emit (chooser, chooser_signals[SELECTION_CHANGED], 0); } +void +_gtk_recent_chooser_activatable_update (GtkActivatable *activatable, + GtkAction *action, + const gchar *property_name) +{ + GtkRecentChooser *recent_chooser = GTK_RECENT_CHOOSER (activatable); + GtkRecentChooser *action_chooser = GTK_RECENT_CHOOSER (action); + GtkRecentAction *recent_action = GTK_RECENT_ACTION (action); + + if (strcmp (property_name, "show-numbers") == 0 && recent_chooser_has_show_numbers (recent_chooser)) + gtk_recent_chooser_set_show_numbers (recent_chooser, + gtk_recent_action_get_show_numbers (recent_action)); + else if (strcmp (property_name, "show-private") == 0) + gtk_recent_chooser_set_show_private (recent_chooser, gtk_recent_chooser_get_show_private (action_chooser)); + else if (strcmp (property_name, "show-not-found") == 0) + gtk_recent_chooser_set_show_not_found (recent_chooser, gtk_recent_chooser_get_show_not_found (action_chooser)); + else if (strcmp (property_name, "show-tips") == 0) + gtk_recent_chooser_set_show_tips (recent_chooser, gtk_recent_chooser_get_show_tips (action_chooser)); + else if (strcmp (property_name, "show-icons") == 0) + gtk_recent_chooser_set_show_icons (recent_chooser, gtk_recent_chooser_get_show_icons (action_chooser)); + else if (strcmp (property_name, "limit") == 0) + gtk_recent_chooser_set_limit (recent_chooser, gtk_recent_chooser_get_limit (action_chooser)); + else if (strcmp (property_name, "local-only") == 0) + gtk_recent_chooser_set_local_only (recent_chooser, gtk_recent_chooser_get_local_only (action_chooser)); + else if (strcmp (property_name, "sort-type") == 0) + gtk_recent_chooser_set_sort_type (recent_chooser, gtk_recent_chooser_get_sort_type (action_chooser)); + else if (strcmp (property_name, "filter") == 0) + gtk_recent_chooser_set_filter (recent_chooser, gtk_recent_chooser_get_filter (action_chooser)); +} + +void +_gtk_recent_chooser_activatable_reset (GtkActivatable *activatable, + GtkAction *action) +{ + GtkRecentChooser *recent_chooser = GTK_RECENT_CHOOSER (activatable); + GtkRecentChooser *action_chooser = GTK_RECENT_CHOOSER (action); + + if (!action) + return; + + if (recent_chooser_has_show_numbers (recent_chooser)) + gtk_recent_chooser_set_show_numbers (recent_chooser, + gtk_recent_action_get_show_numbers (GTK_RECENT_ACTION (action))); + gtk_recent_chooser_set_show_private (recent_chooser, gtk_recent_chooser_get_show_private (action_chooser)); + gtk_recent_chooser_set_show_not_found (recent_chooser, gtk_recent_chooser_get_show_not_found (action_chooser)); + gtk_recent_chooser_set_show_tips (recent_chooser, gtk_recent_chooser_get_show_tips (action_chooser)); + gtk_recent_chooser_set_show_icons (recent_chooser, gtk_recent_chooser_get_show_icons (action_chooser)); + gtk_recent_chooser_set_limit (recent_chooser, gtk_recent_chooser_get_limit (action_chooser)); + gtk_recent_chooser_set_local_only (recent_chooser, gtk_recent_chooser_get_local_only (action_chooser)); + gtk_recent_chooser_set_sort_type (recent_chooser, gtk_recent_chooser_get_sort_type (action_chooser)); + gtk_recent_chooser_set_filter (recent_chooser, gtk_recent_chooser_get_filter (action_chooser)); +} + +void +_gtk_recent_chooser_set_related_action (GtkRecentChooser *recent_chooser, + GtkAction *action) +{ + GtkAction *prev_action; + + prev_action = g_object_get_qdata (G_OBJECT (recent_chooser), quark_gtk_related_action); + + if (prev_action == action) + return; + + gtk_activatable_do_set_related_action (GTK_ACTIVATABLE (recent_chooser), action); + g_object_set_qdata (G_OBJECT (recent_chooser), quark_gtk_related_action, action); +} + +GtkAction * +_gtk_recent_chooser_get_related_action (GtkRecentChooser *recent_chooser) +{ + return g_object_get_qdata (G_OBJECT (recent_chooser), quark_gtk_related_action); +} + +/* The default for use-action-appearance is TRUE, so we try to set the + * qdata backwards for this case. + */ +void +_gtk_recent_chooser_set_use_action_appearance (GtkRecentChooser *recent_chooser, + gboolean use_appearance) +{ + GtkAction *action; + gboolean use_action_appearance; + + action = g_object_get_qdata (G_OBJECT (recent_chooser), quark_gtk_related_action); + use_action_appearance = !GPOINTER_TO_INT (g_object_get_qdata (G_OBJECT (recent_chooser), quark_gtk_use_action_appearance)); + + if (use_action_appearance != use_appearance) + { + + g_object_set_qdata (G_OBJECT (recent_chooser), quark_gtk_use_action_appearance, !GINT_TO_POINTER (use_appearance)); + + gtk_activatable_reset (GTK_ACTIVATABLE (recent_chooser), action); + } +} + +gboolean +_gtk_recent_chooser_get_use_action_appearance (GtkRecentChooser *recent_chooser) +{ + return !GPOINTER_TO_INT (g_object_get_qdata (G_OBJECT (recent_chooser), quark_gtk_use_action_appearance)); +} + #define __GTK_RECENT_CHOOSER_C__ #include "gtkaliasdef.c" diff --git a/gtk/gtkrecentchooserdefault.c b/gtk/gtkrecentchooserdefault.c index 7e1b35ffd7..d8fecb2fbc 100644 --- a/gtk/gtkrecentchooserdefault.c +++ b/gtk/gtkrecentchooserdefault.c @@ -65,6 +65,7 @@ #include "gtktooltip.h" #include "gtktypebuiltins.h" #include "gtkvbox.h" +#include "gtkactivatable.h" #include "gtkrecentmanager.h" #include "gtkrecentfilter.h" @@ -76,7 +77,15 @@ #include "gtkprivate.h" #include "gtkalias.h" - +enum +{ + PROP_0, + + /* activatable properties */ + PROP_ACTIVATABLE_RELATED_ACTION, + PROP_ACTIVATABLE_USE_ACTION_APPEARANCE +}; + struct _GtkRecentChooserDefault { @@ -274,13 +283,20 @@ static gboolean recent_view_query_tooltip_cb (GtkWidget *widget, GtkTooltip *tooltip, gpointer user_data); - +static void gtk_recent_chooser_activatable_iface_init (GtkActivatableIface *iface); +static void gtk_recent_chooser_activatable_update (GtkActivatable *activatable, + GtkAction *action, + const gchar *property_name); +static void gtk_recent_chooser_activatable_reset (GtkActivatable *activatable, + GtkAction *action); G_DEFINE_TYPE_WITH_CODE (GtkRecentChooserDefault, _gtk_recent_chooser_default, GTK_TYPE_VBOX, G_IMPLEMENT_INTERFACE (GTK_TYPE_RECENT_CHOOSER, - gtk_recent_chooser_iface_init)) + gtk_recent_chooser_iface_init) + G_IMPLEMENT_INTERFACE (GTK_TYPE_ACTIVATABLE, + gtk_recent_chooser_activatable_iface_init)) @@ -302,6 +318,14 @@ gtk_recent_chooser_iface_init (GtkRecentChooserIface *iface) iface->list_filters = gtk_recent_chooser_default_list_filters; } +static void +gtk_recent_chooser_activatable_iface_init (GtkActivatableIface *iface) + +{ + iface->update = gtk_recent_chooser_activatable_update; + iface->reset = gtk_recent_chooser_activatable_reset; +} + static void _gtk_recent_chooser_default_class_init (GtkRecentChooserDefaultClass *klass) { @@ -318,6 +342,9 @@ _gtk_recent_chooser_default_class_init (GtkRecentChooserDefaultClass *klass) widget_class->show_all = gtk_recent_chooser_default_show_all; _gtk_recent_chooser_install_properties (gobject_class); + + g_object_class_override_property (gobject_class, PROP_ACTIVATABLE_RELATED_ACTION, "related-action"); + g_object_class_override_property (gobject_class, PROP_ACTIVATABLE_USE_ACTION_APPEARANCE, "use-action-appearance"); } static void @@ -528,6 +555,12 @@ gtk_recent_chooser_default_set_property (GObject *object, case GTK_RECENT_CHOOSER_PROP_FILTER: set_current_filter (impl, g_value_get_object (value)); break; + case PROP_ACTIVATABLE_RELATED_ACTION: + _gtk_recent_chooser_set_related_action (GTK_RECENT_CHOOSER (impl), g_value_get_object (value)); + break; + case PROP_ACTIVATABLE_USE_ACTION_APPEARANCE: + _gtk_recent_chooser_set_use_action_appearance (GTK_RECENT_CHOOSER (impl), g_value_get_boolean (value)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -571,6 +604,12 @@ gtk_recent_chooser_default_get_property (GObject *object, case GTK_RECENT_CHOOSER_PROP_FILTER: g_value_set_object (value, impl->current_filter); break; + case PROP_ACTIVATABLE_RELATED_ACTION: + g_value_set_object (value, _gtk_recent_chooser_get_related_action (GTK_RECENT_CHOOSER (impl))); + break; + case PROP_ACTIVATABLE_USE_ACTION_APPEARANCE: + g_value_set_boolean (value, _gtk_recent_chooser_get_use_action_appearance (GTK_RECENT_CHOOSER (impl))); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -1909,6 +1948,44 @@ set_recent_manager (GtkRecentChooserDefault *impl, } } +static void +gtk_recent_chooser_activatable_update (GtkActivatable *activatable, + GtkAction *action, + const gchar *property_name) +{ + if (strcmp (property_name, "visible") == 0) + { + if (gtk_action_is_visible (action)) + gtk_widget_show (GTK_WIDGET (activatable)); + else + gtk_widget_hide (GTK_WIDGET (activatable)); + } + + if (strcmp (property_name, "sensitive") == 0) + gtk_widget_set_sensitive (GTK_WIDGET (activatable), gtk_action_is_sensitive (action)); + + _gtk_recent_chooser_activatable_update (activatable, action, property_name); +} + + +static void +gtk_recent_chooser_activatable_reset (GtkActivatable *activatable, + GtkAction *action) +{ + if (action) + { + if (gtk_action_is_visible (action)) + gtk_widget_show (GTK_WIDGET (activatable)); + else + gtk_widget_hide (GTK_WIDGET (activatable)); + + gtk_widget_set_sensitive (GTK_WIDGET (activatable), gtk_action_is_sensitive (action)); + } + + _gtk_recent_chooser_activatable_reset (activatable, action); +} + + GtkWidget * _gtk_recent_chooser_default_new (GtkRecentManager *manager) { diff --git a/gtk/gtkrecentchoosermenu.c b/gtk/gtkrecentchoosermenu.c index 54ea3c7897..bc02aa6dfd 100644 --- a/gtk/gtkrecentchoosermenu.c +++ b/gtk/gtkrecentchoosermenu.c @@ -42,6 +42,7 @@ #include "gtkimage.h" #include "gtklabel.h" #include "gtktooltip.h" +#include "gtkactivatable.h" #include "gtktypebuiltins.h" #include "gtkprivate.h" #include "gtkalias.h" @@ -86,10 +87,14 @@ struct _GtkRecentChooserMenuPrivate enum { PROP_0, + PROP_SHOW_NUMBERS, - PROP_SHOW_NUMBERS + /* activatable properties */ + PROP_ACTIVATABLE_RELATED_ACTION, + PROP_ACTIVATABLE_USE_ACTION_APPEARANCE }; + #define FALLBACK_ICON_SIZE 32 #define FALLBACK_ITEM_LIMIT 10 #define DEFAULT_LABEL_WIDTH 30 @@ -155,11 +160,20 @@ static void item_activate_cb (GtkWidget *widget, static void manager_changed_cb (GtkRecentManager *manager, gpointer user_data); +static void gtk_recent_chooser_activatable_iface_init (GtkActivatableIface *iface); +static void gtk_recent_chooser_activatable_update (GtkActivatable *activatable, + GtkAction *action, + const gchar *property_name); +static void gtk_recent_chooser_activatable_reset (GtkActivatable *activatable, + GtkAction *action); + G_DEFINE_TYPE_WITH_CODE (GtkRecentChooserMenu, gtk_recent_chooser_menu, GTK_TYPE_MENU, G_IMPLEMENT_INTERFACE (GTK_TYPE_RECENT_CHOOSER, - gtk_recent_chooser_iface_init)) + gtk_recent_chooser_iface_init) + G_IMPLEMENT_INTERFACE (GTK_TYPE_ACTIVATABLE, + gtk_recent_chooser_activatable_iface_init)) static void @@ -179,6 +193,14 @@ gtk_recent_chooser_iface_init (GtkRecentChooserIface *iface) iface->list_filters = gtk_recent_chooser_menu_list_filters; } +static void +gtk_recent_chooser_activatable_iface_init (GtkActivatableIface *iface) + +{ + iface->update = gtk_recent_chooser_activatable_update; + iface->reset = gtk_recent_chooser_activatable_reset; +} + static void gtk_recent_chooser_menu_class_init (GtkRecentChooserMenuClass *klass) { @@ -208,6 +230,10 @@ gtk_recent_chooser_menu_class_init (GtkRecentChooserMenuClass *klass) FALSE, GTK_PARAM_READWRITE)); + + g_object_class_override_property (gobject_class, PROP_ACTIVATABLE_RELATED_ACTION, "related-action"); + g_object_class_override_property (gobject_class, PROP_ACTIVATABLE_USE_ACTION_APPEARANCE, "use-action-appearance"); + g_type_class_add_private (klass, sizeof (GtkRecentChooserMenuPrivate)); } @@ -376,6 +402,12 @@ gtk_recent_chooser_menu_set_property (GObject *object, case GTK_RECENT_CHOOSER_PROP_FILTER: gtk_recent_chooser_menu_set_current_filter (menu, g_value_get_object (value)); break; + case PROP_ACTIVATABLE_RELATED_ACTION: + _gtk_recent_chooser_set_related_action (GTK_RECENT_CHOOSER (menu), g_value_get_object (value)); + break; + case PROP_ACTIVATABLE_USE_ACTION_APPEARANCE: + _gtk_recent_chooser_set_use_action_appearance (GTK_RECENT_CHOOSER (menu), g_value_get_boolean (value)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -423,6 +455,12 @@ gtk_recent_chooser_menu_get_property (GObject *object, case GTK_RECENT_CHOOSER_PROP_FILTER: g_value_set_object (value, priv->current_filter); break; + case PROP_ACTIVATABLE_RELATED_ACTION: + g_value_set_object (value, _gtk_recent_chooser_get_related_action (GTK_RECENT_CHOOSER (menu))); + break; + case PROP_ACTIVATABLE_USE_ACTION_APPEARANCE: + g_value_set_boolean (value, _gtk_recent_chooser_get_use_action_appearance (GTK_RECENT_CHOOSER (menu))); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -1131,6 +1169,27 @@ gtk_recent_chooser_menu_set_show_tips (GtkRecentChooserMenu *menu, gtk_container_foreach (GTK_CONTAINER (menu), foreach_set_shot_tips, menu); } +static void +gtk_recent_chooser_activatable_update (GtkActivatable *activatable, + GtkAction *action, + const gchar *property_name) +{ + if (strcmp (property_name, "sensitive") == 0) + gtk_widget_set_sensitive (GTK_WIDGET (activatable), gtk_action_is_sensitive (action)); + + _gtk_recent_chooser_activatable_update (activatable, action, property_name); +} + +static void +gtk_recent_chooser_activatable_reset (GtkActivatable *activatable, + GtkAction *action) +{ + gtk_widget_set_sensitive (GTK_WIDGET (activatable), gtk_action_is_sensitive (action)); + + _gtk_recent_chooser_activatable_reset (activatable, action); +} + + /* * Public API */ diff --git a/gtk/gtkrecentchooserprivate.h b/gtk/gtkrecentchooserprivate.h index 1057d05190..23ea2b497c 100644 --- a/gtk/gtkrecentchooserprivate.h +++ b/gtk/gtkrecentchooserprivate.h @@ -25,17 +25,30 @@ #include "gtkrecentmanager.h" #include "gtkrecentchooser.h" +#include "gtkactivatable.h" G_BEGIN_DECLS -GtkRecentManager *_gtk_recent_chooser_get_recent_manager (GtkRecentChooser *chooser); -GList * _gtk_recent_chooser_get_items (GtkRecentChooser *chooser, - GtkRecentFilter *filter, - GtkRecentSortFunc func, - gpointer data); +GtkRecentManager *_gtk_recent_chooser_get_recent_manager (GtkRecentChooser *chooser); +GList * _gtk_recent_chooser_get_items (GtkRecentChooser *chooser, + GtkRecentFilter *filter, + GtkRecentSortFunc func, + gpointer data); -void _gtk_recent_chooser_item_activated (GtkRecentChooser *chooser); -void _gtk_recent_chooser_selection_changed (GtkRecentChooser *chooser); +void _gtk_recent_chooser_item_activated (GtkRecentChooser *chooser); +void _gtk_recent_chooser_selection_changed (GtkRecentChooser *chooser); + +void _gtk_recent_chooser_activatable_update (GtkActivatable *activatable, + GtkAction *action, + const gchar *property_name); +void _gtk_recent_chooser_activatable_reset (GtkActivatable *activatable, + GtkAction *action); +void _gtk_recent_chooser_set_related_action (GtkRecentChooser *recent_chooser, + GtkAction *action); +GtkAction *_gtk_recent_chooser_get_related_action (GtkRecentChooser *recent_chooser); +void _gtk_recent_chooser_set_use_action_appearance (GtkRecentChooser *recent_chooser, + gboolean use_appearance); +gboolean _gtk_recent_chooser_get_use_action_appearance (GtkRecentChooser *recent_chooser); G_END_DECLS diff --git a/gtk/gtktoggleaction.c b/gtk/gtktoggleaction.c index 33f045a0c4..19930ecefd 100644 --- a/gtk/gtktoggleaction.c +++ b/gtk/gtktoggleaction.c @@ -54,11 +54,6 @@ enum { G_DEFINE_TYPE (GtkToggleAction, gtk_toggle_action, GTK_TYPE_ACTION) static void gtk_toggle_action_activate (GtkAction *action); -static void gtk_toggle_action_real_toggled (GtkToggleAction *action); -static void connect_proxy (GtkAction *action, - GtkWidget *proxy); -static void disconnect_proxy (GtkAction *action, - GtkWidget *proxy); static void set_property (GObject *object, guint prop_id, const GValue *value, @@ -87,16 +82,22 @@ gtk_toggle_action_class_init (GtkToggleActionClass *klass) gobject_class->get_property = get_property; action_class->activate = gtk_toggle_action_activate; - action_class->connect_proxy = connect_proxy; - action_class->disconnect_proxy = disconnect_proxy; action_class->menu_item_type = GTK_TYPE_CHECK_MENU_ITEM; action_class->toolbar_item_type = GTK_TYPE_TOGGLE_TOOL_BUTTON; action_class->create_menu_item = create_menu_item; - klass->toggled = gtk_toggle_action_real_toggled; + klass->toggled = NULL; + /** + * GtkToggleAction:draw-as-radio: + * + * Whether the proxies for this action look like radio action proxies. + * + * This is an appearance property and thus only applies if + * #GtkActivatable:use-action-appearance is %TRUE. + */ g_object_class_install_property (gobject_class, PROP_DRAW_AS_RADIO, g_param_spec_boolean ("draw-as-radio", @@ -232,64 +233,6 @@ gtk_toggle_action_activate (GtkAction *action) gtk_toggle_action_toggled (toggle_action); } -static void -gtk_toggle_action_real_toggled (GtkToggleAction *action) -{ - GSList *slist; - - g_return_if_fail (GTK_IS_TOGGLE_ACTION (action)); - - for (slist = gtk_action_get_proxies (GTK_ACTION (action)); slist; slist = slist->next) - { - GtkWidget *proxy = slist->data; - - gtk_action_block_activate_from (GTK_ACTION (action), proxy); - if (GTK_IS_CHECK_MENU_ITEM (proxy)) - gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (proxy), - action->private_data->active); - else if (GTK_IS_TOGGLE_TOOL_BUTTON (proxy)) - gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON (proxy), - action->private_data->active); - else if (GTK_IS_TOGGLE_BUTTON (proxy)) - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (proxy), - action->private_data->active); - else { - g_warning ("Don't know how to toggle `%s' widgets", - G_OBJECT_TYPE_NAME (proxy)); - } - gtk_action_unblock_activate_from (GTK_ACTION (action), proxy); - } -} - -static void -connect_proxy (GtkAction *action, - GtkWidget *proxy) -{ - GtkToggleAction *toggle_action; - - toggle_action = GTK_TOGGLE_ACTION (action); - - /* do this before hand, so that we don't call the "activate" handler */ - if (GTK_IS_CHECK_MENU_ITEM (proxy)) - gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (proxy), - toggle_action->private_data->active); - else if (GTK_IS_TOGGLE_TOOL_BUTTON (proxy)) - gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON (proxy), - toggle_action->private_data->active); - else if (GTK_IS_TOGGLE_BUTTON (proxy)) - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (proxy), - toggle_action->private_data->active); - - GTK_ACTION_CLASS (parent_class)->connect_proxy (action, proxy); -} - -static void -disconnect_proxy (GtkAction *action, - GtkWidget *proxy) -{ - GTK_ACTION_CLASS (parent_class)->disconnect_proxy (action, proxy); -} - /** * gtk_toggle_action_toggled: * @action: the action object diff --git a/gtk/gtktogglebutton.c b/gtk/gtktogglebutton.c index 33505a9254..88f22fbf0c 100644 --- a/gtk/gtktogglebutton.c +++ b/gtk/gtktogglebutton.c @@ -29,6 +29,8 @@ #include "gtkmain.h" #include "gtkmarshalers.h" #include "gtktogglebutton.h" +#include "gtktoggleaction.h" +#include "gtkactivatable.h" #include "gtkprivate.h" #include "gtkintl.h" #include "gtkalias.h" @@ -67,9 +69,20 @@ static void gtk_toggle_button_get_property (GObject *object, GParamSpec *pspec); static void gtk_toggle_button_update_state (GtkButton *button); -static guint toggle_button_signals[LAST_SIGNAL] = { 0 }; -G_DEFINE_TYPE (GtkToggleButton, gtk_toggle_button, GTK_TYPE_BUTTON) +static void gtk_toggle_button_activatable_interface_init (GtkActivatableIface *iface); +static void gtk_toggle_button_activatable_update (GtkActivatable *activatable, + GtkAction *action, + const gchar *property_name); +static void gtk_toggle_button_activatable_reset (GtkActivatable *activatable, + GtkAction *action); + +static GtkActivatableIface *parent_activatable_iface; +static guint toggle_button_signals[LAST_SIGNAL] = { 0 }; + +G_DEFINE_TYPE_WITH_CODE (GtkToggleButton, gtk_toggle_button, GTK_TYPE_BUTTON, + G_IMPLEMENT_INTERFACE (GTK_TYPE_ACTIVATABLE, + gtk_toggle_button_activatable_interface_init)) static void gtk_toggle_button_class_init (GtkToggleButtonClass *class) @@ -139,6 +152,53 @@ gtk_toggle_button_init (GtkToggleButton *toggle_button) } +static void +gtk_toggle_button_activatable_interface_init (GtkActivatableIface *iface) +{ + parent_activatable_iface = g_type_interface_peek_parent (iface); + iface->update = gtk_toggle_button_activatable_update; + iface->reset = gtk_toggle_button_activatable_reset; +} + +static void +gtk_toggle_button_activatable_update (GtkActivatable *activatable, + GtkAction *action, + const gchar *property_name) +{ + GtkToggleButton *button; + + parent_activatable_iface->update (activatable, action, property_name); + + button = GTK_TOGGLE_BUTTON (activatable); + + if (strcmp (property_name, "active") == 0) + { + gtk_action_block_activate (action); + gtk_toggle_button_set_active (button, gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))); + gtk_action_unblock_activate (action); + } + +} + +static void +gtk_toggle_button_activatable_reset (GtkActivatable *activatable, + GtkAction *action) +{ + GtkToggleButton *button; + + parent_activatable_iface->reset (activatable, action); + + if (!action) + return; + + button = GTK_TOGGLE_BUTTON (activatable); + + gtk_action_block_activate (action); + gtk_toggle_button_set_active (button, gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))); + gtk_action_unblock_activate (action); +} + + GtkWidget* gtk_toggle_button_new (void) { @@ -441,6 +501,8 @@ gtk_toggle_button_clicked (GtkButton *button) gtk_toggle_button_update_state (button); g_object_notify (G_OBJECT (toggle_button), "active"); + + GTK_BUTTON_CLASS (gtk_toggle_button_parent_class)->clicked (button); } static void diff --git a/gtk/gtktoggletoolbutton.c b/gtk/gtktoggletoolbutton.c index 1076458aae..c527c136b2 100644 --- a/gtk/gtktoggletoolbutton.c +++ b/gtk/gtktoggletoolbutton.c @@ -1,4 +1,4 @@ -/* gtktoggletoolbutton.c + /* gtktoggletoolbutton.c * * Copyright (C) 2002 Anders Carlsson <andersca@gnome.org> * Copyright (C) 2002 James Henstridge <james@daa.com.au> @@ -27,6 +27,8 @@ #include "gtkstock.h" #include "gtkintl.h" #include "gtkradiotoolbutton.h" +#include "gtktoggleaction.h" +#include "gtkactivatable.h" #include "gtkprivate.h" #include "gtkalias.h" @@ -67,9 +69,20 @@ static void button_toggled (GtkWidget *widget, static void menu_item_activated (GtkWidget *widget, GtkToggleToolButton *button); -static guint toggle_signals[LAST_SIGNAL] = { 0 }; -G_DEFINE_TYPE (GtkToggleToolButton, gtk_toggle_tool_button, GTK_TYPE_TOOL_BUTTON) +static void gtk_toggle_tool_button_activatable_interface_init (GtkActivatableIface *iface); +static void gtk_toggle_tool_button_activatable_update (GtkActivatable *activatable, + GtkAction *action, + const gchar *property_name); +static void gtk_toggle_tool_button_activatable_reset (GtkActivatable *activatable, + GtkAction *action); + +static GtkActivatableIface *parent_activatable_iface; +static guint toggle_signals[LAST_SIGNAL] = { 0 }; + +G_DEFINE_TYPE_WITH_CODE (GtkToggleToolButton, gtk_toggle_tool_button, GTK_TYPE_TOOL_BUTTON, + G_IMPLEMENT_INTERFACE (GTK_TYPE_ACTIVATABLE, + gtk_toggle_tool_button_activatable_interface_init)) static void gtk_toggle_tool_button_class_init (GtkToggleToolButtonClass *klass) @@ -292,6 +305,52 @@ button_toggled (GtkWidget *widget, } } +static void +gtk_toggle_tool_button_activatable_interface_init (GtkActivatableIface *iface) +{ + parent_activatable_iface = g_type_interface_peek_parent (iface); + iface->update = gtk_toggle_tool_button_activatable_update; + iface->reset = gtk_toggle_tool_button_activatable_reset; +} + +static void +gtk_toggle_tool_button_activatable_update (GtkActivatable *activatable, + GtkAction *action, + const gchar *property_name) +{ + GtkToggleToolButton *button; + + parent_activatable_iface->update (activatable, action, property_name); + + button = GTK_TOGGLE_TOOL_BUTTON (activatable); + + if (strcmp (property_name, "active") == 0) + { + gtk_action_block_activate (action); + gtk_toggle_tool_button_set_active (button, gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))); + gtk_action_unblock_activate (action); + } +} + +static void +gtk_toggle_tool_button_activatable_reset (GtkActivatable *activatable, + GtkAction *action) +{ + GtkToggleToolButton *button; + + parent_activatable_iface->reset (activatable, action); + + if (!action) + return; + + button = GTK_TOGGLE_TOOL_BUTTON (activatable); + + gtk_action_block_activate (action); + gtk_toggle_tool_button_set_active (button, gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))); + gtk_action_unblock_activate (action); +} + + /** * gtk_toggle_tool_button_new: * diff --git a/gtk/gtktoolbutton.c b/gtk/gtktoolbutton.c index 65b3c1aaa9..be2bf5af48 100644 --- a/gtk/gtktoolbutton.c +++ b/gtk/gtktoolbutton.c @@ -32,6 +32,7 @@ #include "gtkvbox.h" #include "gtkintl.h" #include "gtktoolbar.h" +#include "gtkactivatable.h" #include "gtkprivate.h" #include "gtkalias.h" @@ -77,11 +78,14 @@ static void gtk_tool_button_style_set (GtkWidget *widget, GtkStyle *prev_style); static void gtk_tool_button_construct_contents (GtkToolItem *tool_item); - -static GObjectClass *parent_class = NULL; -static guint toolbutton_signals[LAST_SIGNAL] = { 0 }; -#define GTK_TOOL_BUTTON_GET_PRIVATE(obj)(G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_TOOL_BUTTON, GtkToolButtonPrivate)) +static void gtk_tool_button_activatable_interface_init (GtkActivatableIface *iface); +static void gtk_tool_button_activatable_update (GtkActivatable *activatable, + GtkAction *action, + const gchar *property_name); +static void gtk_tool_button_activatable_reset (GtkActivatable *activatable, + GtkAction *action); + struct _GtkToolButtonPrivate { @@ -97,19 +101,37 @@ struct _GtkToolButtonPrivate guint contents_invalid : 1; }; +static GObjectClass *parent_class = NULL; +static GtkActivatableIface *parent_activatable_iface; +static guint toolbutton_signals[LAST_SIGNAL] = { 0 }; + +#define GTK_TOOL_BUTTON_GET_PRIVATE(obj)(G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_TOOL_BUTTON, GtkToolButtonPrivate)) + GType gtk_tool_button_get_type (void) { static GType type = 0; - + if (!type) - type = g_type_register_static_simple (GTK_TYPE_TOOL_ITEM, - I_("GtkToolButton"), - sizeof (GtkToolButtonClass), - (GClassInitFunc) gtk_tool_button_class_init, - sizeof (GtkToolButton), - (GInstanceInitFunc) gtk_tool_button_init, - 0); + { + static const GInterfaceInfo activatable_info = + { + (GInterfaceInitFunc) gtk_tool_button_activatable_interface_init, + (GInterfaceFinalizeFunc) NULL, + NULL + }; + + type = g_type_register_static_simple (GTK_TYPE_TOOL_ITEM, + I_("GtkToolButton"), + sizeof (GtkToolButtonClass), + (GClassInitFunc) gtk_tool_button_class_init, + sizeof (GtkToolButton), + (GInstanceInitFunc) gtk_tool_button_init, + 0); + + g_type_add_interface_static (type, GTK_TYPE_ACTIVATABLE, + &activatable_info); + } return type; } @@ -672,6 +694,13 @@ static void button_clicked (GtkWidget *widget, GtkToolButton *button) { + GtkAction *action; + + action = gtk_activatable_get_related_action (GTK_ACTIVATABLE (button)); + + if (action) + gtk_action_activate (action); + g_signal_emit_by_name (button, "clicked"); } @@ -704,6 +733,123 @@ gtk_tool_button_style_set (GtkWidget *widget, gtk_tool_button_update_icon_spacing (GTK_TOOL_BUTTON (widget)); } +static void +gtk_tool_button_activatable_interface_init (GtkActivatableIface *iface) +{ + parent_activatable_iface = g_type_interface_peek_parent (iface); + iface->update = gtk_tool_button_activatable_update; + iface->reset = gtk_tool_button_activatable_reset; +} + +static void +gtk_tool_button_activatable_update (GtkActivatable *activatable, + GtkAction *action, + const gchar *property_name) +{ + GtkToolButton *button; + GtkWidget *image; + + parent_activatable_iface->update (activatable, action, property_name); + + if (!gtk_activatable_get_use_action_appearance (activatable)) + return; + + button = GTK_TOOL_BUTTON (activatable); + + if (strcmp (property_name, "short-label") == 0) + { + if (!gtk_action_get_stock_id (action) && + !gtk_action_get_icon_name (action)) + { + gtk_tool_button_set_use_underline (button, TRUE); + gtk_tool_button_set_label (button, gtk_action_get_short_label (action)); + } + } + else if (strcmp (property_name, "stock-id") == 0) + { + if (gtk_action_get_stock_id (action)) + { + gtk_tool_button_set_label (button, NULL); + gtk_tool_button_set_icon_name (button, NULL); + } + gtk_tool_button_set_stock_id (button, gtk_action_get_stock_id (action)); + } + else if (strcmp (property_name, "gicon") == 0) + { + const gchar *stock_id = gtk_action_get_stock_id (action); + GIcon *icon = gtk_action_get_gicon (action); + GtkIconSize icon_size = GTK_ICON_SIZE_BUTTON; + + if ((stock_id && gtk_icon_factory_lookup_default (stock_id)) || !icon) + image = NULL; + else + { + image = gtk_tool_button_get_icon_widget (button); + icon_size = gtk_tool_item_get_icon_size (GTK_TOOL_ITEM (button)); + + if (!image) + image = gtk_image_new (); + } + + gtk_tool_button_set_icon_widget (button, image); + gtk_image_set_from_gicon (GTK_IMAGE (image), icon, icon_size); + + } + else if (strcmp (property_name, "icon-name") == 0) + { + if (gtk_action_get_icon_name (action)) + { + gtk_tool_button_set_label (button, NULL); + gtk_tool_button_set_stock_id (button, NULL); + } + gtk_tool_button_set_icon_name (button, gtk_action_get_icon_name (action)); + } +} + +static void +gtk_tool_button_activatable_reset (GtkActivatable *activatable, + GtkAction *action) +{ + GtkToolButton *button; + GIcon *icon; + + parent_activatable_iface->reset (activatable, action); + + if (!action) + return; + + if (!gtk_activatable_get_use_action_appearance (activatable)) + return; + + button = GTK_TOOL_BUTTON (activatable); + + gtk_tool_button_set_label (button, NULL); + gtk_tool_button_set_stock_id (button, NULL); + gtk_tool_button_set_icon_name (button, NULL); + gtk_tool_button_set_use_underline (button, TRUE); + + if (gtk_action_get_stock_id (action)) + gtk_tool_button_set_stock_id (button, gtk_action_get_stock_id (action)); + else if ((icon = gtk_action_get_gicon (action)) != NULL) + { + GtkIconSize icon_size = gtk_tool_item_get_icon_size (GTK_TOOL_ITEM (button)); + GtkWidget *image = gtk_tool_button_get_icon_widget (button); + + if (!image) + { + image = gtk_image_new (); + gtk_widget_show (image); + gtk_tool_button_set_icon_widget (button, image); + } + + gtk_image_set_from_gicon (GTK_IMAGE (image), icon, icon_size); + } + else if (gtk_action_get_icon_name (action)) + gtk_tool_button_set_icon_name (button, gtk_action_get_icon_name (action)); + else + gtk_tool_button_set_label (button, gtk_action_get_short_label (action)); +} + /** * gtk_tool_button_new_from_stock: * @stock_id: the name of the stock item diff --git a/gtk/gtktoolitem.c b/gtk/gtktoolitem.c index 40a8d0cd0f..54b60e792f 100644 --- a/gtk/gtktoolitem.c +++ b/gtk/gtktoolitem.c @@ -30,6 +30,7 @@ #include "gtkmarshalers.h" #include "gtktoolshell.h" #include "gtkseparatormenuitem.h" +#include "gtkactivatable.h" #include "gtkintl.h" #include "gtkmain.h" #include "gtkprivate.h" @@ -69,7 +70,11 @@ enum { PROP_0, PROP_VISIBLE_HORIZONTAL, PROP_VISIBLE_VERTICAL, - PROP_IS_IMPORTANT + PROP_IS_IMPORTANT, + + /* activatable properties */ + PROP_ACTIVATABLE_RELATED_ACTION, + PROP_ACTIVATABLE_USE_ACTION_APPEARANCE }; #define GTK_TOOL_ITEM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GTK_TYPE_TOOL_ITEM, GtkToolItemPrivate)) @@ -90,11 +95,15 @@ struct _GtkToolItemPrivate gchar *menu_item_id; GtkWidget *menu_item; + + GtkAction *action; + gboolean use_action_appearance; }; -static void gtk_tool_item_finalize (GObject *object); -static void gtk_tool_item_parent_set (GtkWidget *toolitem, - GtkWidget *parent); +static void gtk_tool_item_finalize (GObject *object); +static void gtk_tool_item_dispose (GObject *object); +static void gtk_tool_item_parent_set (GtkWidget *toolitem, + GtkWidget *parent); static void gtk_tool_item_set_property (GObject *object, guint prop_id, const GValue *value, @@ -120,10 +129,22 @@ static gboolean gtk_tool_item_real_set_tooltip (GtkToolItem *tool_item, static gboolean gtk_tool_item_create_menu_proxy (GtkToolItem *item); +static void gtk_tool_item_activatable_interface_init (GtkActivatableIface *iface); +static void gtk_tool_item_activatable_update (GtkActivatable *activatable, + GtkAction *action, + const gchar *property_name); +static void gtk_tool_item_activatable_reset (GtkActivatable *activatable, + GtkAction *action); +static void gtk_tool_item_set_related_action (GtkToolItem *item, + GtkAction *action); +static void gtk_tool_item_set_use_action_appearance (GtkToolItem *item, + gboolean use_appearance); static guint toolitem_signals[LAST_SIGNAL] = { 0 }; -G_DEFINE_TYPE (GtkToolItem, gtk_tool_item, GTK_TYPE_BIN) +G_DEFINE_TYPE_WITH_CODE (GtkToolItem, gtk_tool_item, GTK_TYPE_BIN, + G_IMPLEMENT_INTERFACE (GTK_TYPE_ACTIVATABLE, + gtk_tool_item_activatable_interface_init)) static void gtk_tool_item_class_init (GtkToolItemClass *klass) @@ -136,8 +157,9 @@ gtk_tool_item_class_init (GtkToolItemClass *klass) object_class->set_property = gtk_tool_item_set_property; object_class->get_property = gtk_tool_item_get_property; - object_class->finalize = gtk_tool_item_finalize; - object_class->notify = gtk_tool_item_property_notify; + object_class->finalize = gtk_tool_item_finalize; + object_class->dispose = gtk_tool_item_dispose; + object_class->notify = gtk_tool_item_property_notify; widget_class->realize = gtk_tool_item_realize; widget_class->unrealize = gtk_tool_item_unrealize; @@ -172,6 +194,10 @@ gtk_tool_item_class_init (GtkToolItemClass *klass) FALSE, GTK_PARAM_READWRITE)); + g_object_class_override_property (object_class, PROP_ACTIVATABLE_RELATED_ACTION, "related-action"); + g_object_class_override_property (object_class, PROP_ACTIVATABLE_USE_ACTION_APPEARANCE, "use-action-appearance"); + + /** * GtkToolItem::create-menu-proxy: * @tool_item: the object the signal was emitted on @@ -276,6 +302,8 @@ gtk_tool_item_init (GtkToolItem *toolitem) toolitem->priv->visible_vertical = TRUE; toolitem->priv->homogeneous = FALSE; toolitem->priv->expand = FALSE; + + toolitem->priv->use_action_appearance = TRUE; } static void @@ -292,6 +320,20 @@ gtk_tool_item_finalize (GObject *object) } static void +gtk_tool_item_dispose (GObject *object) +{ + GtkToolItem *item = GTK_TOOL_ITEM (object); + + if (item->priv->action) + { + gtk_activatable_do_set_related_action (GTK_ACTIVATABLE (item), NULL); + item->priv->action = NULL; + } + G_OBJECT_CLASS (gtk_tool_item_parent_class)->dispose (object); +} + + +static void gtk_tool_item_parent_set (GtkWidget *toolitem, GtkWidget *prev_parent) { @@ -318,6 +360,12 @@ gtk_tool_item_set_property (GObject *object, case PROP_IS_IMPORTANT: gtk_tool_item_set_is_important (toolitem, g_value_get_boolean (value)); break; + case PROP_ACTIVATABLE_RELATED_ACTION: + gtk_tool_item_set_related_action (toolitem, g_value_get_object (value)); + break; + case PROP_ACTIVATABLE_USE_ACTION_APPEARANCE: + gtk_tool_item_set_use_action_appearance (toolitem, g_value_get_boolean (value)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -343,6 +391,12 @@ gtk_tool_item_get_property (GObject *object, case PROP_IS_IMPORTANT: g_value_set_boolean (value, toolitem->priv->is_important); break; + case PROP_ACTIVATABLE_RELATED_ACTION: + g_value_set_object (value, toolitem->priv->action); + break; + case PROP_ACTIVATABLE_USE_ACTION_APPEARANCE: + g_value_set_boolean (value, toolitem->priv->use_action_appearance); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -503,9 +557,117 @@ gtk_tool_item_size_allocate (GtkWidget *widget, static gboolean gtk_tool_item_create_menu_proxy (GtkToolItem *item) { + GtkWidget *menu_item; + gboolean visible_overflown; + + if (item->priv->action) + { + g_object_get (item->priv->action, "visible-overflown", &visible_overflown, NULL); + + if (visible_overflown) + { + menu_item = gtk_action_create_menu_item (item->priv->action); + + g_object_ref_sink (menu_item); + gtk_tool_item_set_proxy_menu_item (item, "gtk-action-menu-item", menu_item); + g_object_unref (menu_item); + } + else + gtk_tool_item_set_proxy_menu_item (item, "gtk-action-menu-item", NULL); + } + return FALSE; } +static void +gtk_tool_item_activatable_interface_init (GtkActivatableIface *iface) +{ + iface->update = gtk_tool_item_activatable_update; + iface->reset = gtk_tool_item_activatable_reset; +} + +static void +gtk_tool_item_activatable_update (GtkActivatable *activatable, + GtkAction *action, + const gchar *property_name) +{ + if (strcmp (property_name, "visible") == 0) + { + if (gtk_action_is_visible (action)) + gtk_widget_show (GTK_WIDGET (activatable)); + else + gtk_widget_hide (GTK_WIDGET (activatable)); + } + else if (strcmp (property_name, "sensitive") == 0) + gtk_widget_set_sensitive (GTK_WIDGET (activatable), gtk_action_is_sensitive (action)); + else if (strcmp (property_name, "tooltip") == 0) + gtk_tool_item_set_tooltip_text (GTK_TOOL_ITEM (activatable), + gtk_action_get_tooltip (action)); + else if (strcmp (property_name, "visible-horizontal") == 0) + gtk_tool_item_set_visible_horizontal (GTK_TOOL_ITEM (activatable), + gtk_action_get_visible_horizontal (action)); + else if (strcmp (property_name, "visible-vertical") == 0) + gtk_tool_item_set_visible_vertical (GTK_TOOL_ITEM (activatable), + gtk_action_get_visible_vertical (action)); + else if (strcmp (property_name, "is-important") == 0) + gtk_tool_item_set_is_important (GTK_TOOL_ITEM (activatable), + gtk_action_get_is_important (action)); +} + +static void +gtk_tool_item_activatable_reset (GtkActivatable *activatable, + GtkAction *action) +{ + if (!action) + return; + + if (gtk_action_is_visible (action)) + gtk_widget_show (GTK_WIDGET (activatable)); + else + gtk_widget_hide (GTK_WIDGET (activatable)); + + gtk_widget_set_sensitive (GTK_WIDGET (activatable), gtk_action_is_sensitive (action)); + + gtk_tool_item_set_tooltip_text (GTK_TOOL_ITEM (activatable), + gtk_action_get_tooltip (action)); + gtk_tool_item_set_visible_horizontal (GTK_TOOL_ITEM (activatable), + gtk_action_get_visible_horizontal (action)); + gtk_tool_item_set_visible_vertical (GTK_TOOL_ITEM (activatable), + gtk_action_get_visible_vertical (action)); + gtk_tool_item_set_is_important (GTK_TOOL_ITEM (activatable), + gtk_action_get_is_important (action)); +} + +static void +gtk_tool_item_set_related_action (GtkToolItem *item, + GtkAction *action) +{ + if (item->priv->action == action) + return; + + gtk_activatable_do_set_related_action (GTK_ACTIVATABLE (item), action); + + item->priv->action = action; + + if (action) + { + gtk_tool_item_rebuild_menu (item); + } +} + +static void +gtk_tool_item_set_use_action_appearance (GtkToolItem *item, + gboolean use_appearance) +{ + if (item->priv->use_action_appearance != use_appearance) + { + item->priv->use_action_appearance = use_appearance; + + gtk_activatable_reset (GTK_ACTIVATABLE (item), item->priv->action); + } +} + + /** * gtk_tool_item_new: * |