summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gtk/Makefile.am4
-rw-r--r--gtk/gsimpleactionobserver.c288
-rw-r--r--gtk/gsimpleactionobserver.h53
-rw-r--r--gtk/gtk.h1
-rw-r--r--gtk/gtkactionable.c117
-rw-r--r--gtk/gtkactionable.h71
-rw-r--r--gtk/gtkapplicationprivate.h6
-rw-r--r--gtk/gtkapplicationwindow.c24
-rw-r--r--gtk/gtkbutton.c161
-rw-r--r--gtk/gtkbuttonprivate.h5
-rw-r--r--gtk/gtktoolbutton.c72
11 files changed, 791 insertions, 11 deletions
diff --git a/gtk/Makefile.am b/gtk/Makefile.am
index ab46644c24..97719ace6f 100644
--- a/gtk/Makefile.am
+++ b/gtk/Makefile.am
@@ -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 \
diff --git a/gtk/gsimpleactionobserver.c b/gtk/gsimpleactionobserver.c
new file mode 100644
index 0000000000..fdbe4bffd1
--- /dev/null
+++ b/gtk/gsimpleactionobserver.c
@@ -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;
+}
diff --git a/gtk/gsimpleactionobserver.h b/gtk/gsimpleactionobserver.h
new file mode 100644
index 0000000000..c3340f71b2
--- /dev/null
+++ b/gtk/gsimpleactionobserver.h
@@ -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__ */
diff --git a/gtk/gtk.h b/gtk/gtk.h
index 0668636fc0..88823e2381 100644
--- a/gtk/gtk.h
+++ b/gtk/gtk.h
@@ -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>
diff --git a/gtk/gtkactionable.c b/gtk/gtkactionable.c
new file mode 100644
index 0000000000..f78474e8f0
--- /dev/null
+++ b/gtk/gtkactionable.c
@@ -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);
+}
diff --git a/gtk/gtkactionable.h b/gtk/gtkactionable.h
new file mode 100644
index 0000000000..bef9bde3a9
--- /dev/null
+++ b/gtk/gtkactionable.h
@@ -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__ */
diff --git a/gtk/gtkapplicationprivate.h b/gtk/gtkapplicationprivate.h
index 9693f78d86..4ff3b2b27e 100644
--- a/gtk/gtkapplicationprivate.h
+++ b/gtk/gtkapplicationprivate.h
@@ -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__ */
diff --git a/gtk/gtkapplicationwindow.c b/gtk/gtkapplicationwindow.c
index 5f1d57c83d..341e57709a 100644
--- a/gtk/gtkapplicationwindow.c
+++ b/gtk/gtkapplicationwindow.c
@@ -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);
+}
diff --git a/gtk/gtkbutton.c b/gtk/gtkbutton.c
index 3b3b5b28d6..f7a908443b 100644
--- a/gtk/gtkbutton.c
+++ b/gtk/gtkbutton.c
@@ -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);
@@ -675,6 +694,83 @@ gtk_button_dispose (GObject *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,
const GValue *value,
@@ -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
diff --git a/gtk/gtkbuttonprivate.h b/gtk/gtkbuttonprivate.h
index 5db1bc1171..232973c1fc 100644
--- a/gtk/gtkbuttonprivate.h
+++ b/gtk/gtkbuttonprivate.h
@@ -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;
diff --git a/gtk/gtktoolbutton.c b/gtk/gtktoolbutton.c
index 934ca61321..ea67c34c6a 100644
--- a/gtk/gtktoolbutton.c
+++ b/gtk/gtktoolbutton.c
@@ -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)
{