diff --git a/gtk/Makefile.am b/gtk/Makefile.am index cf258048df..e4eefd67f3 100644 --- a/gtk/Makefile.am +++ b/gtk/Makefile.am @@ -390,6 +390,9 @@ gtk_private_type_h_sources = \ # GTK+ header files that don't get installed gtk_private_h_sources = \ + gactionmuxer.h \ + gactionobserver.h \ + gactionobservable.h \ gtkaccelgroupprivate.h \ gtkanimationdescription.h \ gtkappchooserprivate.h \ @@ -503,6 +506,9 @@ deprecated_c_sources = \ gtk_base_c_sources = \ $(deprecated_c_sources) \ + gactionmuxer.c \ + gactionobserver.c \ + gactionobservable.c \ gtkquery.c \ gtksearchengine.c \ gtksearchenginesimple.c \ diff --git a/gtk/gactionmuxer.c b/gtk/gactionmuxer.c new file mode 100644 index 0000000000..bb3325ecf4 --- /dev/null +++ b/gtk/gactionmuxer.c @@ -0,0 +1,505 @@ +/* + * Copyright © 2011 Canonical Limited + * + * This library 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. + * + * Author: Ryan Lortie + */ + +#include "config.h" + +#include "gactionmuxer.h" + +#include "gactionobservable.h" +#include "gactionobserver.h" + +#include + +/** + * SECTION:gactionmuxer + * @short_description: Aggregate and monitor several action groups + * + * #GActionMuxer is a #GActionGroup and #GActionObservable that is + * capable of containing other #GActionGroup instances. + * + * The typical use is aggrgating all of the actions applicable to a + * particular context into a single action group, with namespacing. + * + * Consider the case of two action groups -- one containing actions + * applicable to an entire application (such as 'quit') and one + * containing actions applicable to a particular window in the + * application (such as 'fullscreen'). + * + * In this case, each of these action groups could be added to a + * #GActionMuxer with the prefixes "app" and "win", respectively. This + * would expose the actions as "app.quit" and "win.fullscreen" on the + * #GActionGroup interface presented by the #GActionMuxer. + * + * Activations and state change requests on the #GActionMuxer are wired + * through to the underlying action group in the expected way. + * + * This class is typically only used at the site of "consumption" of + * actions (eg: when displaying a menu that contains many actions on + * different objects). + * + * Since: 2.32 + **/ + +static void g_action_muxer_group_iface_init (GActionGroupInterface *iface); +static void g_action_muxer_observable_iface_init (GActionObservableInterface *iface); + +typedef GObjectClass GActionMuxerClass; + +struct _GActionMuxer +{ + GObject parent_instance; + + GHashTable *actions; + GHashTable *groups; +}; + +G_DEFINE_TYPE_WITH_CODE (GActionMuxer, g_action_muxer, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (G_TYPE_ACTION_GROUP, g_action_muxer_group_iface_init) + G_IMPLEMENT_INTERFACE (G_TYPE_ACTION_OBSERVABLE, g_action_muxer_observable_iface_init)) + +typedef struct +{ + GActionMuxer *muxer; + GSList *watchers; + gchar *fullname; +} Action; + +typedef struct +{ + GActionMuxer *muxer; + GActionGroup *group; + gchar *prefix; + gulong handler_ids[4]; +} Group; + +static gchar ** +g_action_muxer_list_actions (GActionGroup *action_group) +{ + GActionMuxer *muxer = G_ACTION_MUXER (action_group); + + return (gchar **) muxer->groups; +} + +static Group * +g_action_muxer_find_group (GActionMuxer *muxer, + const gchar **name) +{ + const gchar *dot; + gchar *prefix; + Group *group; + + dot = strchr (*name, '.'); + + if (!dot) + return NULL; + + prefix = g_strndup (*name, dot - *name); + group = g_hash_table_lookup (muxer->groups, prefix); + g_free (prefix); + + *name = dot + 1; + + return group; +} + +static Action * +g_action_muxer_lookup_action (GActionMuxer *muxer, + const gchar *prefix, + const gchar *action_name, + gchar **fullname) +{ + Action *action; + + *fullname = g_strconcat (prefix, ".", action_name, NULL); + action = g_hash_table_lookup (muxer->actions, *fullname); + + return action; +} + +static void +g_action_muxer_action_enabled_changed (GActionGroup *action_group, + const gchar *action_name, + gboolean enabled, + gpointer user_data) +{ + Group *group = user_data; + gchar *fullname; + Action *action; + GSList *node; + + g_print ("feeling changes\n"); + + action = g_action_muxer_lookup_action (group->muxer, group->prefix, action_name, &fullname); + for (node = action ? action->watchers : NULL; node; node = node->next) + g_action_observer_action_enabled_changed (node->data, G_ACTION_OBSERVABLE (group->muxer), fullname, enabled); + g_action_group_action_enabled_changed (G_ACTION_GROUP (group->muxer), fullname, enabled); + g_free (fullname); +} + +static void +g_action_muxer_action_state_changed (GActionGroup *action_group, + const gchar *action_name, + GVariant *state, + gpointer user_data) +{ + Group *group = user_data; + gchar *fullname; + Action *action; + GSList *node; + + action = g_action_muxer_lookup_action (group->muxer, group->prefix, action_name, &fullname); + for (node = action ? action->watchers : NULL; node; node = node->next) + g_action_observer_action_state_changed (node->data, G_ACTION_OBSERVABLE (group->muxer), fullname, state); + g_action_group_action_state_changed (G_ACTION_GROUP (group->muxer), fullname, state); + g_free (fullname); +} + +static void +g_action_muxer_action_added (GActionGroup *action_group, + const gchar *action_name, + gpointer user_data) +{ + const GVariantType *parameter_type; + Group *group = user_data; + gboolean enabled; + GVariant *state; + + if (g_action_group_query_action (group->group, action_name, &enabled, ¶meter_type, NULL, NULL, &state)) + { + gchar *fullname; + Action *action; + GSList *node; + + action = g_action_muxer_lookup_action (group->muxer, group->prefix, action_name, &fullname); + + for (node = action ? action->watchers : NULL; node; node = node->next) + g_action_observer_action_added (node->data, + G_ACTION_OBSERVABLE (group->muxer), + fullname, parameter_type, enabled, state); + + g_action_group_action_added (G_ACTION_GROUP (group->muxer), fullname); + + if (state) + g_variant_unref (state); + + g_free (fullname); + } +} + +static void +g_action_muxer_action_removed (GActionGroup *action_group, + const gchar *action_name, + gpointer user_data) +{ + Group *group = user_data; + gchar *fullname; + Action *action; + GSList *node; + + action = g_action_muxer_lookup_action (group->muxer, group->prefix, action_name, &fullname); + for (node = action ? action->watchers : NULL; node; node = node->next) + g_action_observer_action_removed (node->data, G_ACTION_OBSERVABLE (group->muxer), fullname); + g_action_group_action_removed (G_ACTION_GROUP (group->muxer), fullname); + g_free (fullname); +} + +static gboolean +g_action_muxer_query_action (GActionGroup *action_group, + const gchar *action_name, + gboolean *enabled, + const GVariantType **parameter_type, + const GVariantType **state_type, + GVariant **state_hint, + GVariant **state) +{ + GActionMuxer *muxer = G_ACTION_MUXER (action_group); + Group *group; + + group = g_action_muxer_find_group (muxer, &action_name); + + if (!group) + return FALSE; + + return g_action_group_query_action (group->group, action_name, enabled, + parameter_type, state_type, state_hint, state); +} + +static void +g_action_muxer_activate_action (GActionGroup *action_group, + const gchar *action_name, + GVariant *parameter) +{ + GActionMuxer *muxer = G_ACTION_MUXER (action_group); + Group *group; + + group = g_action_muxer_find_group (muxer, &action_name); + + if (group) + g_action_group_activate_action (group->group, action_name, parameter); +} + +static void +g_action_muxer_change_action_state (GActionGroup *action_group, + const gchar *action_name, + GVariant *state) +{ + GActionMuxer *muxer = G_ACTION_MUXER (action_group); + Group *group; + + group = g_action_muxer_find_group (muxer, &action_name); + + if (group) + g_action_group_change_action_state (group->group, action_name, state); +} + +static void +g_action_muxer_unregister_internal (Action *action, + gpointer observer) +{ + GActionMuxer *muxer = action->muxer; + GSList **ptr; + + for (ptr = &action->watchers; *ptr; ptr = &(*ptr)->next) + if ((*ptr)->data == observer) + { + *ptr = g_slist_remove (*ptr, observer); + + if (action->watchers == NULL) + { + g_hash_table_remove (muxer->actions, action->fullname); + g_free (action->fullname); + + g_slice_free (Action, action); + + g_object_unref (muxer); + } + + break; + } +} + +static void +g_action_muxer_weak_notify (gpointer data, + GObject *where_the_object_was) +{ + Action *action = data; + + g_action_muxer_unregister_internal (action, where_the_object_was); +} + +static void +g_action_muxer_register_observer (GActionObservable *observable, + const gchar *name, + GActionObserver *observer) +{ + GActionMuxer *muxer = G_ACTION_MUXER (observable); + Action *action; + + action = g_hash_table_lookup (muxer->actions, name); + + if (action == NULL) + { + action = g_slice_new (Action); + action->muxer = g_object_ref (muxer); + action->fullname = g_strdup (name); + action->watchers = NULL; + + g_hash_table_insert (muxer->actions, action->fullname, action); + } + + action->watchers = g_slist_prepend (action->watchers, observer); + g_object_weak_ref (G_OBJECT (observer), g_action_muxer_weak_notify, action); +} + +static void +g_action_muxer_unregister_observer (GActionObservable *observable, + const gchar *name, + GActionObserver *observer) +{ + GActionMuxer *muxer = G_ACTION_MUXER (observable); + Action *action; + + action = g_hash_table_lookup (muxer->actions, name); + g_object_weak_unref (G_OBJECT (observer), g_action_muxer_weak_notify, action); + g_action_muxer_unregister_internal (action, observer); +} + +static void +g_action_muxer_free_group (gpointer data) +{ + Group *group = data; + + g_object_unref (group->group); + g_free (group->prefix); + + g_slice_free (Group, group); +} + +static void +g_action_muxer_finalize (GObject *object) +{ + GActionMuxer *muxer = G_ACTION_MUXER (object); + + g_assert_cmpint (g_hash_table_size (muxer->actions), ==, 0); + g_hash_table_unref (muxer->actions); + g_hash_table_unref (muxer->groups); + + G_OBJECT_CLASS (g_action_muxer_parent_class) + ->finalize (object); +} + +static void +g_action_muxer_init (GActionMuxer *muxer) +{ + muxer->actions = g_hash_table_new (g_str_hash, g_str_equal); + muxer->groups = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_action_muxer_free_group); +} + +static void +g_action_muxer_observable_iface_init (GActionObservableInterface *iface) +{ + iface->register_observer = g_action_muxer_register_observer; + iface->unregister_observer = g_action_muxer_unregister_observer; +} + +static void +g_action_muxer_group_iface_init (GActionGroupInterface *iface) +{ + iface->list_actions = g_action_muxer_list_actions; + iface->query_action = g_action_muxer_query_action; + iface->activate_action = g_action_muxer_activate_action; + iface->change_action_state = g_action_muxer_change_action_state; +} + +static void +g_action_muxer_class_init (GObjectClass *class) +{ + class->finalize = g_action_muxer_finalize; +} + +/** + * g_action_muxer_insert: + * @muxer: a #GActionMuxer + * @prefix: the prefix string for the action group + * @action_group: a #GActionGroup + * + * Adds the actions in @action_group to the list of actions provided by + * @muxer. @prefix is prefixed to each action name, such that for each + * action x in @action_group, there is an equivalent + * action @prefix.x in @muxer. + * + * For example, if @prefix is "app" and @action_group + * contains an action called "quit", then @muxer will + * now contain an action called "app.quit". + * + * If any #GActionObservers are registered for actions in the group, + * "action_added" notifications will be emitted, as appropriate. + * + * @prefix must not contain a dot ('.'). + * + * Since: 2.32 + **/ +void +g_action_muxer_insert (GActionMuxer *muxer, + const gchar *prefix, + GActionGroup *action_group) +{ + gchar **actions; + Group *group; + gint i; + + /* TODO: diff instead of ripout and replace */ + g_action_muxer_remove (muxer, prefix); + + group = g_slice_new (Group); + group->muxer = muxer; + group->group = g_object_ref (action_group); + group->prefix = g_strdup (prefix); + + g_hash_table_insert (muxer->groups, group->prefix, group); + + actions = g_action_group_list_actions (group->group); + for (i = 0; actions[i]; i++) + g_action_muxer_action_added (group->group, actions[i], group); + g_strfreev (actions); + + group->handler_ids[0] = g_signal_connect (group->group, "action-added", + G_CALLBACK (g_action_muxer_action_added), group); + group->handler_ids[1] = g_signal_connect (group->group, "action-removed", + G_CALLBACK (g_action_muxer_action_removed), group); + group->handler_ids[2] = g_signal_connect (group->group, "action-enabled-changed", + G_CALLBACK (g_action_muxer_action_enabled_changed), group); + group->handler_ids[3] = g_signal_connect (group->group, "action-state-changed", + G_CALLBACK (g_action_muxer_action_state_changed), group); +} + +/** + * g_action_muxer_remove: + * @muxer: a #GActionMuxer + * @prefix: the prefix of the action group to remove + * + * Removes a #GActionGroup from the #GActionMuxer. + * + * If any #GActionObservers are registered for actions in the group, + * "action_removed" notifications will be emitted, as appropriate. + * + * Since: 2.32 + **/ +void +g_action_muxer_remove (GActionMuxer *muxer, + const gchar *prefix) +{ + Group *group; + + group = g_hash_table_lookup (muxer->groups, prefix); + + if (group != NULL) + { + gchar **actions; + gint i; + + g_hash_table_steal (muxer->groups, prefix); + + actions = g_action_group_list_actions (group->group); + for (i = 0; actions[i]; i++) + g_action_muxer_action_removed (group->group, actions[i], group); + g_strfreev (actions); + + /* 'for loop' or 'four loop'? */ + for (i = 0; i < 4; i++) + g_signal_handler_disconnect (group->group, group->handler_ids[i]); + + g_action_muxer_free_group (group); + } +} + +/** + * g_action_muxer_new: + * + * Creates a new #GActionMuxer. + * + * Since: 2.32 + **/ +GActionMuxer * +g_action_muxer_new (void) +{ + return g_object_new (G_TYPE_ACTION_MUXER, NULL); +} diff --git a/gtk/gactionmuxer.h b/gtk/gactionmuxer.h new file mode 100644 index 0000000000..adafd03b24 --- /dev/null +++ b/gtk/gactionmuxer.h @@ -0,0 +1,53 @@ +/* + * Copyright © 2011 Canonical Limited + * + * This library 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. + * + * Author: Ryan Lortie + */ + +#ifndef __G_ACTION_MUXER_H__ +#define __G_ACTION_MUXER_H__ + +#include + +G_BEGIN_DECLS + +#define G_TYPE_ACTION_MUXER (g_action_muxer_get_type ()) +#define G_ACTION_MUXER(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \ + G_TYPE_ACTION_MUXER, GActionMuxer)) +#define G_IS_ACTION_MUXER(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), \ + G_TYPE_ACTION_MUXER)) + +typedef struct _GActionMuxer GActionMuxer; + +G_GNUC_INTERNAL +GType g_action_muxer_get_type (void); +G_GNUC_INTERNAL +GActionMuxer * g_action_muxer_new (void); + +G_GNUC_INTERNAL +void g_action_muxer_insert (GActionMuxer *muxer, + const gchar *prefix, + GActionGroup *group); + +G_GNUC_INTERNAL +void g_action_muxer_remove (GActionMuxer *muxer, + const gchar *prefix); + +G_END_DECLS + +#endif /* __G_ACTION_MUXER_H__ */ diff --git a/gtk/gactionobservable.c b/gtk/gactionobservable.c new file mode 100644 index 0000000000..11ed63ba9b --- /dev/null +++ b/gtk/gactionobservable.c @@ -0,0 +1,86 @@ +/* + * Copyright © 2011 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 + */ + +#include "config.h" + +#include "gactionobservable.h" + +G_DEFINE_INTERFACE (GActionObservable, g_action_observable, G_TYPE_OBJECT) + +/** + * SECTION:gactionobserable + * @short_description: an interface implemented by objects that report + * changes to actions + * + * Since: 2.32 + */ + +void +g_action_observable_default_init (GActionObservableInterface *iface) +{ +} + +/** + * g_action_observable_register_observer: + * @observable: a #GActionObservable + * @action_name: the name of the action + * @observer: the #GActionObserver to which the events will be reported + * + * Registers @observer as being interested in changes to @action_name on + * @observable. + * + * Since: 2.32 + **/ +void +g_action_observable_register_observer (GActionObservable *observable, + const gchar *action_name, + GActionObserver *observer) +{ + g_return_if_fail (G_IS_ACTION_OBSERVABLE (observable)); + + G_ACTION_OBSERVABLE_GET_IFACE (observable) + ->register_observer (observable, action_name, observer); +} + +/** + * g_action_observable_unregister_observer: + * @observable: a #GActionObservable + * @action_name: the name of the action + * @observer: the #GActionObserver to which the events will be reported + * + * Removes the registration of @observer as being interested in changes + * to @action_name on @observable. + * + * If the observer was registered multiple times, it must be + * unregistered an equal number of times. + * + * Since: 2.32 + **/ +void +g_action_observable_unregister_observer (GActionObservable *observable, + const gchar *action_name, + GActionObserver *observer) +{ + g_return_if_fail (G_IS_ACTION_OBSERVABLE (observable)); + + G_ACTION_OBSERVABLE_GET_IFACE (observable) + ->unregister_observer (observable, action_name, observer); +} diff --git a/gtk/gactionobservable.h b/gtk/gactionobservable.h new file mode 100644 index 0000000000..ae6ac314cb --- /dev/null +++ b/gtk/gactionobservable.h @@ -0,0 +1,64 @@ +/* + * Copyright © 2011 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 + */ + +#ifndef __G_ACTION_OBSERVABLE_H__ +#define __G_ACTION_OBSERVABLE_H__ + +#include + +G_BEGIN_DECLS + +#define G_TYPE_ACTION_OBSERVABLE (g_action_observable_get_type ()) +#define G_ACTION_OBSERVABLE(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \ + G_TYPE_ACTION_OBSERVABLE, GActionObservable)) +#define G_IS_ACTION_OBSERVABLE(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), \ + G_TYPE_ACTION_OBSERVABLE)) +#define G_ACTION_OBSERVABLE_GET_IFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), \ + G_TYPE_ACTION_OBSERVABLE, GActionObservableInterface)) + +typedef struct _GActionObservableInterface GActionObservableInterface; + +struct _GActionObservableInterface +{ + GTypeInterface g_iface; + + void (* register_observer) (GActionObservable *observable, + const gchar *action_name, + GActionObserver *observer); + void (* unregister_observer) (GActionObservable *observable, + const gchar *action_name, + GActionObserver *observer); +}; + +G_GNUC_INTERNAL +GType g_action_observable_get_type (void); +G_GNUC_INTERNAL +void g_action_observable_register_observer (GActionObservable *observable, + const gchar *action_name, + GActionObserver *observer); +G_GNUC_INTERNAL +void g_action_observable_unregister_observer (GActionObservable *observable, + const gchar *action_name, + GActionObserver *observer); + +G_END_DECLS + +#endif /* __G_ACTION_OBSERVABLE_H__ */ diff --git a/gtk/gactionobserver.c b/gtk/gactionobserver.c new file mode 100644 index 0000000000..c9d4176c08 --- /dev/null +++ b/gtk/gactionobserver.c @@ -0,0 +1,171 @@ +/* + * Copyright © 2011 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 + */ + +#include "config.h" + +#include "gactionobserver.h" + +G_DEFINE_INTERFACE (GActionObserver, g_action_observer, G_TYPE_OBJECT) + +/** + * SECTION:gactionobserver + * @short_description: an interface implemented by objects that are + * interested in monitoring actions for changes + * + * GActionObserver is a simple interface allowing objects that wish to + * be notified of changes to actions to be notified of those changes. + * + * It is also possible to monitor changes to action groups using + * #GObject signals, but there are a number of reasons that this + * approach could become problematic: + * + * - there are four separate signals that must be manually connected + * and disconnected + * + * - when a large number of different observers wish to monitor a + * (usually disjoint) set of actions within the same action group, + * there is only one way to avoid having all notifications delivered + * to all observers: signal detail. In order to use signal detail, + * each action name must be quarked, which is not always practical. + * + * - even if quarking is acceptable, #GObject signal details are + * implemented by scanning a linked list, so there is no real + * decrease in complexity + * + * Since: 2.32 + */ + +void +g_action_observer_default_init (GActionObserverInterface *class) +{ +} + +/** + * g_action_observer_action_added: + * @observer: a #GActionObserver + * @observable: the source of the event + * @action_name: the name of the action + * @enabled: %TRUE if the action is now enabled + * @parameter_type: the parameter type for action invocations, or %NULL + * if no parameter is required + * @state: the current state of the action, or %NULL if the action is + * stateless + * + * This function is called when an action that the observer is + * registered to receive events for is added. + * + * This function should only be called by objects with which the + * observer has explicitly registered itself to receive events. + * + * Since: 2.32 + **/ +void +g_action_observer_action_added (GActionObserver *observer, + GActionObservable *observable, + const gchar *action_name, + const GVariantType *parameter_type, + gboolean enabled, + GVariant *state) +{ + g_return_if_fail (G_IS_ACTION_OBSERVER (observer)); + + G_ACTION_OBSERVER_GET_IFACE (observer) + ->action_added (observer, observable, action_name, parameter_type, enabled, state); +} + +/** + * g_action_observer_action_enabled_changed: + * @observer: a #GActionObserver + * @observable: the source of the event + * @action_name: the name of the action + * @enabled: %TRUE if the action is now enabled + * + * This function is called when an action that the observer is + * registered to receive events for becomes enabled or disabled. + * + * This function should only be called by objects with which the + * observer has explicitly registered itself to receive events. + * + * Since: 2.32 + **/ +void +g_action_observer_action_enabled_changed (GActionObserver *observer, + GActionObservable *observable, + const gchar *action_name, + gboolean enabled) +{ + g_return_if_fail (G_IS_ACTION_OBSERVER (observer)); + + G_ACTION_OBSERVER_GET_IFACE (observer) + ->action_enabled_changed (observer, observable, action_name, enabled); +} + +/** + * g_action_observer_action_state_changed: + * @observer: a #GActionObserver + * @observable: the source of the event + * @action_name: the name of the action + * @state: the new state of the action + * + * This function is called when an action that the observer is + * registered to receive events for changes its state. + * + * This function should only be called by objects with which the + * observer has explicitly registered itself to receive events. + * + * Since: 2.32 + **/ +void +g_action_observer_action_state_changed (GActionObserver *observer, + GActionObservable *observable, + const gchar *action_name, + GVariant *state) +{ + g_return_if_fail (G_IS_ACTION_OBSERVER (observer)); + + G_ACTION_OBSERVER_GET_IFACE (observer) + ->action_state_changed (observer, observable, action_name, state); +} + +/** + * g_action_observer_action_removed: + * @observer: a #GActionObserver + * @observable: the source of the event + * @action_name: the name of the action + * + * This function is called when an action that the observer is + * registered to receive events for is removed. + * + * This function should only be called by objects with which the + * observer has explicitly registered itself to receive events. + * + * Since: 2.32 + **/ +void +g_action_observer_action_removed (GActionObserver *observer, + GActionObservable *observable, + const gchar *action_name) +{ + g_return_if_fail (G_IS_ACTION_OBSERVER (observer)); + + G_ACTION_OBSERVER_GET_IFACE (observer) + ->action_removed (observer, observable, action_name); +} diff --git a/gtk/gactionobserver.h b/gtk/gactionobserver.h new file mode 100644 index 0000000000..eb15c3ab40 --- /dev/null +++ b/gtk/gactionobserver.h @@ -0,0 +1,90 @@ +/* + * Copyright © 2011 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 + */ + +#ifndef __G_ACTION_OBSERVER_H__ +#define __G_ACTION_OBSERVER_H__ + +#include + +G_BEGIN_DECLS + +#define G_TYPE_ACTION_OBSERVER (g_action_observer_get_type ()) +#define G_ACTION_OBSERVER(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \ + G_TYPE_ACTION_OBSERVER, GActionObserver)) +#define G_IS_ACTION_OBSERVER(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), \ + G_TYPE_ACTION_OBSERVER)) +#define G_ACTION_OBSERVER_GET_IFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), \ + G_TYPE_ACTION_OBSERVER, GActionObserverInterface)) + +typedef struct _GActionObserverInterface GActionObserverInterface; +typedef struct _GActionObservable GActionObservable; +typedef struct _GActionObserver GActionObserver; + +struct _GActionObserverInterface +{ + GTypeInterface g_iface; + + void (* action_added) (GActionObserver *observer, + GActionObservable *observable, + const gchar *action_name, + const GVariantType *parameter_type, + gboolean enabled, + GVariant *state); + void (* action_enabled_changed) (GActionObserver *observer, + GActionObservable *observable, + const gchar *action_name, + gboolean enabled); + void (* action_state_changed) (GActionObserver *observer, + GActionObservable *observable, + const gchar *action_name, + GVariant *state); + void (* action_removed) (GActionObserver *observer, + GActionObservable *observable, + const gchar *action_name); +}; + +G_GNUC_INTERNAL +GType g_action_observer_get_type (void); +G_GNUC_INTERNAL +void g_action_observer_action_added (GActionObserver *observer, + GActionObservable *observable, + const gchar *action_name, + const GVariantType *parameter_type, + gboolean enabled, + GVariant *state); +G_GNUC_INTERNAL +void g_action_observer_action_enabled_changed (GActionObserver *observer, + GActionObservable *observable, + const gchar *action_name, + gboolean enabled); +G_GNUC_INTERNAL +void g_action_observer_action_state_changed (GActionObserver *observer, + GActionObservable *observable, + const gchar *action_name, + GVariant *state); +G_GNUC_INTERNAL +void g_action_observer_action_removed (GActionObserver *observer, + GActionObservable *observable, + const gchar *action_name); + +G_END_DECLS + +#endif /* __G_ACTION_OBSERVER_H__ */