Add new GtkActionable interface

This is the interface for GtkWidgets that can be associated with an
action on a GtkAppicationWindow or associated GtkApplication.

It essentially features 'action-name' and 'action-target' properties
with some associated convenience API.

This interface is implemented by GtkButton and GtkToolButton.

https://bugzilla.gnome.org/show_bug.cgi?id=667394
This commit is contained in:
Ryan Lortie 2012-01-05 22:22:06 -05:00
parent a3629592d3
commit 88ec007b98
11 changed files with 791 additions and 11 deletions

View File

@ -172,6 +172,7 @@ gtk_public_h_sources = \
gtkaccellabel.h \
gtkaccelmap.h \
gtkaccessible.h \
gtkactionable.h \
gtkaction.h \
gtkactiongroup.h \
gtkactivatable.h \
@ -392,6 +393,7 @@ gtk_private_type_h_sources = \
# GTK+ header files that don't get installed
gtk_private_h_sources = \
gactionmuxer.h \
gsimpleactionobserver.h \
gactionobserver.h \
gactionobservable.h \
gtkapplicationprivate.h \
@ -512,8 +514,10 @@ deprecated_c_sources = \
gtk_base_c_sources = \
$(deprecated_c_sources) \
gactionmuxer.c \
gsimpleactionobserver.c \
gactionobserver.c \
gactionobservable.c \
gtkactionable.c \
gtkquery.c \
gtksearchengine.c \
gtksearchenginesimple.c \

288
gtk/gsimpleactionobserver.c Normal file
View File

@ -0,0 +1,288 @@
/*
* Copyright © 2012 Canonical Limited
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2 of the
* licence 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
* USA.
*
* Authors: Ryan Lortie <desrt@desrt.ca>
*/
#include "config.h"
#include "gsimpleactionobserver.h"
#include "gactionobservable.h"
typedef GObjectClass GSimpleActionObserverClass;
struct _GSimpleActionObserver
{
GObject parent_instance;
GActionGroup *action_group;
gchar *action_name;
GVariant *target;
gboolean can_activate;
gboolean active;
gboolean enabled;
gint reporting;
};
static void g_simple_action_observer_init_iface (GActionObserverInterface *iface);
G_DEFINE_TYPE_WITH_CODE (GSimpleActionObserver, g_simple_action_observer, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (G_TYPE_ACTION_OBSERVER, g_simple_action_observer_init_iface));
enum
{
PROP_0,
PROP_ACTIVE,
PROP_ENABLED,
N_PROPS
};
static GParamSpec *g_simple_action_observer_pspecs[N_PROPS];
static void
g_simple_action_observer_action_added (GActionObserver *g_observer,
GActionObservable *observable,
const gchar *action_name,
const GVariantType *parameter_type,
gboolean enabled,
GVariant *state)
{
GSimpleActionObserver *observer = G_SIMPLE_ACTION_OBSERVER (g_observer);
gboolean active;
/* we can only activate if we have the correct type of parameter */
observer->can_activate = (observer->target == NULL && parameter_type == NULL) ||
(observer->target != NULL && parameter_type != NULL &&
g_variant_is_of_type (observer->target, parameter_type));
if (observer->can_activate)
{
if (observer->target != NULL && state != NULL)
active = g_variant_equal (state, observer->target);
else if (state != NULL && g_variant_is_of_type (state, G_VARIANT_TYPE_BOOLEAN))
active = g_variant_get_boolean (state);
else
active = FALSE;
if (active != observer->active)
{
observer->active = active;
observer->reporting++;
g_object_notify_by_pspec (G_OBJECT (observer), g_simple_action_observer_pspecs[PROP_ACTIVE]);
observer->reporting--;
}
if (enabled != observer->enabled)
{
observer->enabled = enabled;
g_object_notify_by_pspec (G_OBJECT (observer), g_simple_action_observer_pspecs[PROP_ENABLED]);
}
}
}
static void
g_simple_action_observer_action_enabled_changed (GActionObserver *g_observer,
GActionObservable *observable,
const gchar *action_name,
gboolean enabled)
{
GSimpleActionObserver *observer = G_SIMPLE_ACTION_OBSERVER (g_observer);
if (!observer->can_activate)
return;
if (enabled != observer->enabled)
{
observer->enabled = enabled;
g_object_notify_by_pspec (G_OBJECT (observer), g_simple_action_observer_pspecs[PROP_ENABLED]);
}
}
static void
g_simple_action_observer_action_state_changed (GActionObserver *g_observer,
GActionObservable *observable,
const gchar *action_name,
GVariant *state)
{
GSimpleActionObserver *observer = G_SIMPLE_ACTION_OBSERVER (g_observer);
gboolean active = FALSE;
if (!observer->can_activate)
return;
if (observer->target)
active = g_variant_equal (state, observer->target);
else if (g_variant_is_of_type (state, G_VARIANT_TYPE_BOOLEAN))
active = g_variant_get_boolean (state);
if (active != observer->active)
{
observer->active = active;
observer->reporting++;
g_object_notify_by_pspec (G_OBJECT (observer), g_simple_action_observer_pspecs[PROP_ACTIVE]);
observer->reporting--;
}
}
static void
g_simple_action_observer_action_removed (GActionObserver *g_observer,
GActionObservable *observable,
const gchar *action_name)
{
GSimpleActionObserver *observer = G_SIMPLE_ACTION_OBSERVER (g_observer);
if (!observer->can_activate)
return;
observer->can_activate = FALSE;
if (observer->active)
{
observer->active = FALSE;
observer->reporting++;
g_object_notify_by_pspec (G_OBJECT (observer), g_simple_action_observer_pspecs[PROP_ACTIVE]);
observer->reporting--;
}
if (observer->enabled)
{
observer->enabled = FALSE;
g_object_notify_by_pspec (G_OBJECT (observer), g_simple_action_observer_pspecs[PROP_ENABLED]);
}
}
static void
g_simple_action_observer_get_property (GObject *object, guint prop_id,
GValue *value, GParamSpec *pspec)
{
GSimpleActionObserver *observer = G_SIMPLE_ACTION_OBSERVER (object);
switch (prop_id)
{
case PROP_ACTIVE:
g_value_set_boolean (value, observer->active);
break;
case PROP_ENABLED:
g_value_set_boolean (value, observer->enabled);
break;
default:
g_assert_not_reached ();
}
}
static void
g_simple_action_observer_finalize (GObject *object)
{
GSimpleActionObserver *observer = G_SIMPLE_ACTION_OBSERVER (object);
g_object_unref (observer->action_group);
g_free (observer->action_name);
if (observer->target)
g_variant_unref (observer->target);
G_OBJECT_CLASS (g_simple_action_observer_parent_class)
->finalize (object);
}
static void
g_simple_action_observer_init (GSimpleActionObserver *observer)
{
}
static void
g_simple_action_observer_init_iface (GActionObserverInterface *iface)
{
iface->action_added = g_simple_action_observer_action_added;
iface->action_enabled_changed = g_simple_action_observer_action_enabled_changed;
iface->action_state_changed = g_simple_action_observer_action_state_changed;
iface->action_removed = g_simple_action_observer_action_removed;
}
static void
g_simple_action_observer_class_init (GObjectClass *class)
{
class->get_property = g_simple_action_observer_get_property;
class->finalize = g_simple_action_observer_finalize;
g_simple_action_observer_pspecs[PROP_ACTIVE] = g_param_spec_boolean ("active", "active", "active", FALSE,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
g_simple_action_observer_pspecs[PROP_ENABLED] = g_param_spec_boolean ("enabled", "enabled", "enabled", FALSE,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (class, N_PROPS, g_simple_action_observer_pspecs);
}
GSimpleActionObserver *
g_simple_action_observer_new (GActionObservable *observable,
const gchar *action_name,
GVariant *target)
{
GSimpleActionObserver *observer;
const GVariantType *type;
gboolean enabled;
GVariant *state;
observer = g_object_new (G_TYPE_SIMPLE_ACTION_OBSERVER, NULL);
observer->action_group = g_object_ref (observable);
observer->action_name = g_strdup (action_name);
if (target)
observer->target = g_variant_ref_sink (target);
g_action_observable_register_observer (observable, action_name, G_ACTION_OBSERVER (observer));
if (g_action_group_query_action (observer->action_group, action_name, &enabled, &type, NULL, NULL, &state))
{
g_simple_action_observer_action_added (G_ACTION_OBSERVER (observer), observable,
action_name, type, enabled, state);
if (state)
g_variant_unref (state);
}
return observer;
}
void
g_simple_action_observer_activate (GSimpleActionObserver *observer)
{
g_return_if_fail (G_IS_SIMPLE_ACTION_OBSERVER (observer));
if (observer->can_activate && !observer->reporting)
g_action_group_activate_action (G_ACTION_GROUP (observer->action_group),
observer->action_name, observer->target);
}
gboolean
g_simple_action_observer_get_active (GSimpleActionObserver *observer)
{
g_return_val_if_fail (G_IS_SIMPLE_ACTION_OBSERVER (observer), FALSE);
return observer->active;
}
gboolean
g_simple_action_observer_get_enabled (GSimpleActionObserver *observer)
{
g_return_val_if_fail (G_IS_SIMPLE_ACTION_OBSERVER (observer), FALSE);
return observer->enabled;
}

View File

@ -0,0 +1,53 @@
/*
* Copyright © 2012 Canonical Limited
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2 of the
* licence 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
* USA.
*
* Authors: Ryan Lortie <desrt@desrt.ca>
*/
#ifndef __G_SIMPLE_ACTION_OBSERVER_H__
#define __G_SIMPLE_ACTION_OBSERVER_H__
#include "gactionobserver.h"
G_BEGIN_DECLS
#define G_TYPE_SIMPLE_ACTION_OBSERVER (g_simple_action_observer_get_type ())
#define G_SIMPLE_ACTION_OBSERVER(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \
G_TYPE_SIMPLE_ACTION_OBSERVER, \
GSimpleActionObserver))
#define G_IS_SIMPLE_ACTION_OBSERVER(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), \
G_TYPE_SIMPLE_ACTION_OBSERVER))
typedef struct _GSimpleActionObserver GSimpleActionObserver;
G_GNUC_INTERNAL
GType g_simple_action_observer_get_type (void);
G_GNUC_INTERNAL
GSimpleActionObserver * g_simple_action_observer_new (GActionObservable *observable,
const gchar *action_name,
GVariant *target);
G_GNUC_INTERNAL
void g_simple_action_observer_activate (GSimpleActionObserver *observer);
G_GNUC_INTERNAL
gboolean g_simple_action_observer_get_active (GSimpleActionObserver *observer);
G_GNUC_INTERNAL
gboolean g_simple_action_observer_get_enabled (GSimpleActionObserver *observer);
G_END_DECLS
#endif /* __G_SIMPLE_ACTION_OBSERVER_H__ */

View File

@ -36,6 +36,7 @@
#include <gtk/gtkaccelmap.h>
#include <gtk/gtkaccessible.h>
#include <gtk/gtkaction.h>
#include <gtk/gtkactionable.h>
#include <gtk/gtkactiongroup.h>
#include <gtk/gtkactivatable.h>
#include <gtk/gtkadjustment.h>

117
gtk/gtkactionable.c Normal file
View File

@ -0,0 +1,117 @@
/*
* Copyright © 2012 Canonical Limited
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2 of the
* licence 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
* USA.
*
* Authors: Ryan Lortie <desrt@desrt.ca>
*/
#include "config.h"
#include "gtkactionable.h"
#include "gtkwidget.h"
#include "gtkintl.h"
G_DEFINE_INTERFACE (GtkActionable, gtk_actionable, GTK_TYPE_WIDGET)
static void
gtk_actionable_default_init (GtkActionableInterface *iface)
{
g_object_interface_install_property (iface,
g_param_spec_string ("action-name", P_("action name"),
P_("The name of the associated action, like 'app.quit'"),
NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_interface_install_property (iface,
g_param_spec_variant ("action-target", P_("action target value"),
P_("The parameter for action invocations"),
G_VARIANT_TYPE_ANY, NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
}
const gchar *
gtk_actionable_get_action_name (GtkActionable *actionable)
{
g_return_val_if_fail (GTK_IS_ACTIONABLE (actionable), NULL);
return GTK_ACTIONABLE_GET_IFACE (actionable)
->get_action_name (actionable);
}
void
gtk_actionable_set_action_name (GtkActionable *actionable,
const gchar *action_name)
{
g_return_if_fail (GTK_IS_ACTIONABLE (actionable));
GTK_ACTIONABLE_GET_IFACE (actionable)
->set_action_name (actionable, action_name);
}
GVariant *
gtk_actionable_get_action_target_value (GtkActionable *actionable)
{
g_return_val_if_fail (GTK_IS_ACTIONABLE (actionable), NULL);
return GTK_ACTIONABLE_GET_IFACE (actionable)
->get_action_target_value (actionable);
}
void
gtk_actionable_set_action_target_value (GtkActionable *actionable,
GVariant *target_value)
{
g_return_if_fail (GTK_IS_ACTIONABLE (actionable));
GTK_ACTIONABLE_GET_IFACE (actionable)
->set_action_target_value (actionable, target_value);
}
void
gtk_actionable_set_action_target (GtkActionable *actionable,
const gchar *format_string,
...)
{
va_list ap;
va_start (ap, format_string);
gtk_actionable_set_action_target_value (actionable, g_variant_new_va (format_string, NULL, &ap));
va_end (ap);
}
void
gtk_actionable_set_detailed_action_name (GtkActionable *actionable,
const gchar *detailed_action_name)
{
gchar **parts;
g_return_if_fail (GTK_IS_ACTIONABLE (actionable));
if (detailed_action_name == NULL)
{
gtk_actionable_set_action_name (actionable, NULL);
gtk_actionable_set_action_target_value (actionable, NULL);
return;
}
parts = g_strsplit (detailed_action_name, "::", 2);
gtk_actionable_set_action_name (actionable, parts[0]);
if (parts[0] && parts[1])
gtk_actionable_set_action_target (actionable, "s", parts[1]);
else
gtk_actionable_set_action_target_value (actionable, NULL);
g_strfreev (parts);
}

71
gtk/gtkactionable.h Normal file
View File

@ -0,0 +1,71 @@
/*
* Copyright © 2012 Canonical Limited
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2 of the
* licence 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
* USA.
*
* Authors: Ryan Lortie <desrt@desrt.ca>
*/
#ifndef __GTK_ACTIONABLE_H__
#define __GTK_ACTIONABLE_H__
#include <glib-object.h>
G_BEGIN_DECLS
#define GTK_TYPE_ACTIONABLE (gtk_actionable_get_type ())
#define GTK_ACTIONABLE(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \
GTK_TYPE_ACTIONABLE, GtkActionable))
#define GTK_IS_ACTIONABLE(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), \
GTK_TYPE_ACTIONABLE))
#define GTK_ACTIONABLE_GET_IFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), \
GTK_TYPE_ACTIONABLE, GtkActionableInterface))
typedef struct _GtkActionableInterface GtkActionableInterface;
typedef struct _GtkActionable GtkActionable;
struct _GtkActionableInterface
{
GTypeInterface g_iface;
const gchar * (* get_action_name) (GtkActionable *actionable);
void (* set_action_name) (GtkActionable *actionable,
const gchar *action_name);
GVariant * (* get_action_target_value) (GtkActionable *actionable);
void (* set_action_target_value) (GtkActionable *actionable,
GVariant *action_target_value);
};
GType gtk_actionable_get_type (void) G_GNUC_CONST;
const gchar * gtk_actionable_get_action_name (GtkActionable *actionable);
void gtk_actionable_set_action_name (GtkActionable *actionable,
const gchar *action_name);
GVariant * gtk_actionable_get_action_target_value (GtkActionable *actionable);
void gtk_actionable_set_action_target_value (GtkActionable *actionable,
GVariant *target_value);
void gtk_actionable_set_action_target (GtkActionable *actionable,
const gchar *format_string,
...);
void gtk_actionable_set_detailed_action_name (GtkActionable *actionable,
const gchar *detailed_action_name);
G_END_DECLS
#endif /* __GTK_ACTIONABLE_H__ */

View File

@ -23,6 +23,7 @@
#ifndef __GTK_APPLICATION_PRIVATE_H__
#define __GTK_APPLICATION_PRIVATE_H__
#include "gsimpleactionobserver.h"
#include "gtkapplicationwindow.h"
G_GNUC_INTERNAL
@ -33,4 +34,9 @@ gboolean gtk_application_window_publish (GtkAppl
G_GNUC_INTERNAL
void gtk_application_window_unpublish (GtkApplicationWindow *window);
G_GNUC_INTERNAL
GSimpleActionObserver * gtk_application_window_get_observer (GtkApplicationWindow *window,
const gchar *action_name,
GVariant *target);
#endif /* __GTK_APPLICATION_PRIVATE_H__ */

View File

@ -182,6 +182,7 @@ struct _GtkApplicationWindowPrivate
{
GSimpleActionGroup *actions;
GActionObservable *muxer;
gboolean muxer_initialised;
GtkWidget *menubar;
GtkAccelGroup *accels;
GSList *accel_closures;
@ -671,14 +672,11 @@ gtk_application_window_real_realize (GtkWidget *widget)
g_signal_connect (settings, "notify::gtk-shell-shows-menubar",
G_CALLBACK (gtk_application_window_shell_shows_menubar_changed), window);
if (window->priv->muxer == NULL)
if (!window->priv->muxer_initialised)
{
GActionMuxer *muxer;
muxer = g_action_muxer_new ();
g_action_muxer_insert (muxer, "app", G_ACTION_GROUP (application));
g_action_muxer_insert (muxer, "win", G_ACTION_GROUP (window));
window->priv->muxer = G_ACTION_OBSERVABLE (muxer);
g_action_muxer_insert (G_ACTION_MUXER (window->priv->muxer), "app", G_ACTION_GROUP (application));
g_action_muxer_insert (G_ACTION_MUXER (window->priv->muxer), "win", G_ACTION_GROUP (window));
window->priv->muxer_initialised = TRUE;
}
gtk_application_window_update_shell_shows_app_menu (window, settings);
@ -875,6 +873,8 @@ gtk_application_window_init (GtkApplicationWindow *window)
G_CALLBACK (g_action_group_action_state_changed), window);
g_signal_connect_swapped (window->priv->actions, "action-removed",
G_CALLBACK (g_action_group_action_removed), window);
window->priv->muxer = G_ACTION_OBSERVABLE (g_action_muxer_new ());
}
static void
@ -982,3 +982,13 @@ gtk_application_window_set_show_menubar (GtkApplicationWindow *window,
g_object_notify_by_pspec (G_OBJECT (window), gtk_application_window_properties[PROP_SHOW_MENUBAR]);
}
}
GSimpleActionObserver *
gtk_application_window_get_observer (GtkApplicationWindow *window,
const gchar *action_name,
GVariant *target)
{
g_return_val_if_fail (GTK_IS_APPLICATION_WINDOW (window), NULL);
return g_simple_action_observer_new (window->priv->muxer, action_name, target);
}

View File

@ -58,7 +58,8 @@
#include "gtkprivate.h"
#include "gtkintl.h"
#include "a11y/gtkbuttonaccessible.h"
#include "gtkapplicationprivate.h"
#include "gtkactionable.h"
static const GtkBorder default_default_border = { 1, 1, 1, 1 };
static const GtkBorder default_default_outside_border = { 0, 0, 0, 0 };
@ -90,6 +91,8 @@ enum {
PROP_XALIGN,
PROP_YALIGN,
PROP_IMAGE_POSITION,
PROP_ACTION_NAME,
PROP_ACTION_TARGET,
/* activatable properties */
PROP_ACTIVATABLE_RELATED_ACTION,
@ -147,8 +150,11 @@ static void gtk_button_state_changed (GtkWidget *widget,
GtkStateType previous_state);
static void gtk_button_grab_notify (GtkWidget *widget,
gboolean was_grabbed);
static void gtk_button_hierarchy_changed (GtkWidget *widget,
GtkWidget *previous_toplevel);
static void gtk_button_actionable_iface_init (GtkActionableInterface *iface);
static void gtk_button_activatable_interface_init(GtkActivatableIface *iface);
static void gtk_button_update (GtkActivatable *activatable,
GtkAction *action,
@ -170,6 +176,7 @@ static void gtk_button_get_preferred_height (GtkWidget *widget,
static guint button_signals[LAST_SIGNAL] = { 0 };
G_DEFINE_TYPE_WITH_CODE (GtkButton, gtk_button, GTK_TYPE_BIN,
G_IMPLEMENT_INTERFACE (GTK_TYPE_ACTIONABLE, gtk_button_actionable_iface_init)
G_IMPLEMENT_INTERFACE (GTK_TYPE_ACTIVATABLE,
gtk_button_activatable_interface_init))
@ -208,6 +215,7 @@ gtk_button_class_init (GtkButtonClass *klass)
widget_class->leave_notify_event = gtk_button_leave_notify;
widget_class->state_changed = gtk_button_state_changed;
widget_class->grab_notify = gtk_button_grab_notify;
widget_class->hierarchy_changed = gtk_button_hierarchy_changed;
container_class->child_type = gtk_button_child_type;
container_class->add = gtk_button_add;
@ -330,6 +338,9 @@ gtk_button_class_init (GtkButtonClass *klass)
GTK_POS_LEFT,
GTK_PARAM_READWRITE));
g_object_class_override_property (gobject_class, PROP_ACTION_NAME, "action-name");
g_object_class_override_property (gobject_class, PROP_ACTION_TARGET, "action-target");
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");
@ -584,6 +595,12 @@ gtk_button_destroy (GtkWidget *widget)
priv->label_text = NULL;
}
if (priv->action_name)
{
g_free (priv->action_name);
priv->action_name = NULL;
}
GTK_WIDGET_CLASS (gtk_button_parent_class)->destroy (widget);
}
@ -666,6 +683,8 @@ gtk_button_dispose (GObject *object)
GtkButton *button = GTK_BUTTON (object);
GtkButtonPrivate *priv = button->priv;
g_clear_object (&priv->action_observer);
if (priv->action)
{
gtk_activatable_do_set_related_action (GTK_ACTIVATABLE (button), NULL);
@ -674,6 +693,83 @@ gtk_button_dispose (GObject *object)
G_OBJECT_CLASS (gtk_button_parent_class)->dispose (object);
}
static void
gtk_button_update_action_observer (GtkButton *button)
{
GtkWidget *window;
g_signal_handlers_disconnect_by_func (button, gtk_real_button_clicked, NULL);
/* we are the only owner so this will clear all the signals */
g_clear_object (&button->priv->action_observer);
window = gtk_widget_get_toplevel (GTK_WIDGET (button));
if (GTK_IS_APPLICATION_WINDOW (window) && button->priv->action_name)
{
GSimpleActionObserver *observer;
observer = gtk_application_window_get_observer (GTK_APPLICATION_WINDOW (window),
button->priv->action_name,
button->priv->action_target);
_gtk_button_set_depressed (button, g_simple_action_observer_get_active (observer));
if (g_object_class_find_property (G_OBJECT_GET_CLASS (button), "active"))
g_object_bind_property (observer, "active", button, "active", G_BINDING_SYNC_CREATE);
g_object_bind_property (observer, "enabled", button, "sensitive", G_BINDING_SYNC_CREATE);
button->priv->action_observer = observer;
g_signal_connect_after (button, "clicked", G_CALLBACK (gtk_real_button_clicked), NULL);
}
}
static void
gtk_button_set_action_name (GtkActionable *actionable,
const gchar *action_name)
{
GtkButton *button = GTK_BUTTON (actionable);
g_return_if_fail (GTK_IS_BUTTON (button));
if (g_strcmp0 (action_name, button->priv->action_name) != 0)
{
g_free (button->priv->action_name);
button->priv->action_name = g_strdup (action_name);
gtk_button_update_action_observer (button);
g_object_notify (G_OBJECT (button), "action-name");
}
}
static void
gtk_button_set_action_target_value (GtkActionable *actionable,
GVariant *action_target)
{
GtkButton *button = GTK_BUTTON (actionable);
g_return_if_fail (GTK_IS_BUTTON (button));
if (action_target != button->priv->action_target &&
(!action_target || !button->priv->action_target ||
!g_variant_equal (action_target, button->priv->action_target)))
{
if (button->priv->action_target)
g_variant_unref (button->priv->action_target);
button->priv->action_target = NULL;
if (action_target)
button->priv->action_target = g_variant_ref_sink (action_target);
gtk_button_update_action_observer (button);
g_object_notify (G_OBJECT (button), "action-target");
}
}
static void
gtk_button_set_property (GObject *object,
guint prop_id,
@ -718,6 +814,12 @@ gtk_button_set_property (GObject *object,
case PROP_ACTIVATABLE_USE_ACTION_APPEARANCE:
gtk_button_set_use_action_appearance (button, g_value_get_boolean (value));
break;
case PROP_ACTION_NAME:
gtk_button_set_action_name (GTK_ACTIONABLE (button), g_value_get_string (value));
break;
case PROP_ACTION_TARGET:
gtk_button_set_action_target_value (GTK_ACTIONABLE (button), g_value_get_variant (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@ -768,12 +870,43 @@ gtk_button_get_property (GObject *object,
case PROP_ACTIVATABLE_USE_ACTION_APPEARANCE:
g_value_set_boolean (value, priv->use_action_appearance);
break;
case PROP_ACTION_NAME:
g_value_set_string (value, priv->action_name);
break;
case PROP_ACTION_TARGET:
g_value_set_variant (value, priv->action_target);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static const gchar *
gtk_button_get_action_name (GtkActionable *actionable)
{
GtkButton *button = GTK_BUTTON (actionable);
return button->priv->action_name;
}
static GVariant *
gtk_button_get_action_target_value (GtkActionable *actionable)
{
GtkButton *button = GTK_BUTTON (actionable);
return button->priv->action_target;
}
static void
gtk_button_actionable_iface_init (GtkActionableInterface *iface)
{
iface->get_action_name = gtk_button_get_action_name;
iface->set_action_name = gtk_button_set_action_name;
iface->get_action_target_value = gtk_button_get_action_target_value;
iface->set_action_target_value = gtk_button_set_action_target_value;
}
static void
gtk_button_activatable_interface_init (GtkActivatableIface *iface)
{
@ -1822,6 +1955,9 @@ gtk_real_button_clicked (GtkButton *button)
{
GtkButtonPrivate *priv = button->priv;
if (priv->action_observer)
g_simple_action_observer_activate (priv->action_observer);
if (priv->action)
gtk_action_activate (priv->action);
}
@ -2416,6 +2552,28 @@ gtk_button_grab_notify (GtkWidget *widget,
}
}
static void
gtk_button_hierarchy_changed (GtkWidget *widget,
GtkWidget *previous_toplevel)
{
GtkButton *button = GTK_BUTTON (widget);
GtkWidgetClass *parent_class;
parent_class = GTK_WIDGET_CLASS (gtk_button_parent_class);
if (parent_class->hierarchy_changed)
parent_class->hierarchy_changed (widget, previous_toplevel);
if (button->priv->action_name)
{
GtkWidget *toplevel;
toplevel = gtk_widget_get_toplevel (widget);
if (toplevel != previous_toplevel)
gtk_button_update_action_observer (button);
}
}
/**
* gtk_button_set_image:
* @button: a #GtkButton
@ -2525,7 +2683,6 @@ gtk_button_get_image_position (GtkButton *button)
return button->priv->image_position;
}
/**
* gtk_button_get_event_window:
* @button: a #GtkButton

View File

@ -19,6 +19,7 @@
#ifndef __GTK_BUTTON_PRIVATE_H__
#define __GTK_BUTTON_PRIVATE_H__
#include "gsimpleactionobserver.h"
#include "gtkaction.h"
G_BEGIN_DECLS
@ -29,6 +30,10 @@ struct _GtkButtonPrivate
GtkAction *action;
GtkWidget *image;
gchar *action_name;
GVariant *action_target;
GSimpleActionObserver *action_observer;
GdkDevice *grab_keyboard;
GdkWindow *event_window;

View File

@ -32,6 +32,7 @@
#include "gtkintl.h"
#include "gtktoolbar.h"
#include "gtkactivatable.h"
#include "gtkactionable.h"
#include "gtkprivate.h"
#include <string.h>
@ -81,7 +82,9 @@ enum {
PROP_LABEL_WIDGET,
PROP_STOCK_ID,
PROP_ICON_NAME,
PROP_ICON_WIDGET
PROP_ICON_WIDGET,
PROP_ACTION_NAME,
PROP_ACTION_TARGET
};
static void gtk_tool_button_init (GtkToolButton *button,
@ -107,6 +110,7 @@ static void gtk_tool_button_style_updated (GtkWidget *widget);
static void gtk_tool_button_construct_contents (GtkToolItem *tool_item);
static void gtk_tool_button_actionable_iface_init (GtkActionableInterface *iface);
static void gtk_tool_button_activatable_interface_init (GtkActivatableIface *iface);
static void gtk_tool_button_update (GtkActivatable *activatable,
GtkAction *action,
@ -135,7 +139,6 @@ static GObjectClass *parent_class = NULL;
static GtkActivatableIface *parent_activatable_iface;
static guint toolbutton_signals[LAST_SIGNAL] = { 0 };
GType
gtk_tool_button_get_type (void)
{
@ -143,6 +146,12 @@ gtk_tool_button_get_type (void)
if (!type)
{
const GInterfaceInfo actionable_info =
{
(GInterfaceInitFunc) gtk_tool_button_actionable_iface_init,
(GInterfaceFinalizeFunc) NULL,
NULL
};
const GInterfaceInfo activatable_info =
{
(GInterfaceInitFunc) gtk_tool_button_activatable_interface_init,
@ -158,6 +167,7 @@ gtk_tool_button_get_type (void)
(GInstanceInitFunc) gtk_tool_button_init,
0);
g_type_add_interface_static (type, GTK_TYPE_ACTIONABLE, &actionable_info);
g_type_add_interface_static (type, GTK_TYPE_ACTIVATABLE,
&activatable_info);
}
@ -278,6 +288,9 @@ gtk_tool_button_class_init (GtkToolButtonClass *klass)
GTK_TYPE_WIDGET,
GTK_PARAM_READWRITE));
g_object_class_override_property (object_class, PROP_ACTION_NAME, "action-name");
g_object_class_override_property (object_class, PROP_ACTION_TARGET, "action-target");
/**
* GtkButton:icon-spacing:
*
@ -612,6 +625,12 @@ gtk_tool_button_set_property (GObject *object,
case PROP_ICON_WIDGET:
gtk_tool_button_set_icon_widget (button, g_value_get_object (value));
break;
case PROP_ACTION_NAME:
g_object_set_property (G_OBJECT (button->priv->button), "action-name", value);
break;
case PROP_ACTION_TARGET:
g_object_set_property (G_OBJECT (button->priv->button), "action-target", value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@ -660,12 +679,61 @@ gtk_tool_button_get_property (GObject *object,
case PROP_ICON_WIDGET:
g_value_set_object (value, button->priv->icon_widget);
break;
case PROP_ACTION_NAME:
g_object_get_property (G_OBJECT (button->priv->button), "action-name", value);
break;
case PROP_ACTION_TARGET:
g_object_get_property (G_OBJECT (button->priv->button), "action-target", value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static const gchar *
gtk_tool_button_get_action_name (GtkActionable *actionable)
{
GtkToolButton *button = GTK_TOOL_BUTTON (actionable);
return gtk_actionable_get_action_name (GTK_ACTIONABLE (button->priv->button));
}
static void
gtk_tool_button_set_action_name (GtkActionable *actionable,
const gchar *action_name)
{
GtkToolButton *button = GTK_TOOL_BUTTON (actionable);
gtk_actionable_set_action_name (GTK_ACTIONABLE (button->priv->button), action_name);
}
static GVariant *
gtk_tool_button_get_action_target_value (GtkActionable *actionable)
{
GtkToolButton *button = GTK_TOOL_BUTTON (actionable);
return gtk_actionable_get_action_target_value (GTK_ACTIONABLE (button->priv->button));
}
static void
gtk_tool_button_set_action_target_value (GtkActionable *actionable,
GVariant *action_target)
{
GtkToolButton *button = GTK_TOOL_BUTTON (actionable);
gtk_actionable_set_action_target_value (GTK_ACTIONABLE (button->priv->button), action_target);
}
static void
gtk_tool_button_actionable_iface_init (GtkActionableInterface *iface)
{
iface->get_action_name = gtk_tool_button_get_action_name;
iface->set_action_name = gtk_tool_button_set_action_name;
iface->get_action_target_value = gtk_tool_button_get_action_target_value;
iface->set_action_target_value = gtk_tool_button_set_action_target_value;
}
static void
gtk_tool_button_finalize (GObject *object)
{