* 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.
+ * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* Authors: Ryan Lortie <desrt@desrt.ca>
*/
#include "config.h"
-#include "gsimpleasyncresult.h"
-#include "gdbusactiongroup.h"
+#include "gdbusactiongroup-private.h"
+
+#include "gremoteactiongroup.h"
#include "gdbusconnection.h"
-#include "gasyncinitable.h"
#include "gactiongroup.h"
-
/**
* SECTION:gdbusactiongroup
* @title: GDBusActionGroup
* @short_description: A D-Bus GActionGroup implementation
- * @see_also: <link linkend="gio-GActionGroup-exporter">GActionGroup exporter</link>
+ * @include: gio/gio.h
+ * @see_also: [GActionGroup exporter][gio-GActionGroup-exporter]
*
* #GDBusActionGroup is an implementation of the #GActionGroup
* interface that can be used as a proxy for an action group
g_slice_free (ActionInfo, info);
}
-ActionInfo *
+static ActionInfo *
action_info_new_from_iter (GVariantIter *iter)
{
const gchar *param_str;
GVariant *state;
gchar *name;
- if (!g_variant_iter_next (iter, "{s(bg@av)}", &name,
+ if (!g_variant_iter_next (iter, "{s(b&g@av)}", &name,
&enabled, ¶m_str, &state))
return NULL;
return info;
}
-static void g_dbus_action_group_iface_init (GActionGroupInterface *);
+static void g_dbus_action_group_remote_iface_init (GRemoteActionGroupInterface *iface);
+static void g_dbus_action_group_iface_init (GActionGroupInterface *iface);
G_DEFINE_TYPE_WITH_CODE (GDBusActionGroup, g_dbus_action_group, G_TYPE_OBJECT,
- G_IMPLEMENT_INTERFACE (G_TYPE_ACTION_GROUP, g_dbus_action_group_iface_init))
-
-static gchar **
-g_dbus_action_group_list_actions (GActionGroup *g_group)
-{
- GDBusActionGroup *group = G_DBUS_ACTION_GROUP (g_group);
- GHashTableIter iter;
- gint n, i = 0;
- gchar **keys;
- gpointer key;
-
- n = g_hash_table_size (group->actions);
- keys = g_new (gchar *, n + 1);
-
- g_hash_table_iter_init (&iter, group->actions);
- while (g_hash_table_iter_next (&iter, &key, NULL))
- keys[i++] = g_strdup (key);
- g_assert_cmpint (i, ==, n);
- keys[n] = NULL;
-
- group->strict = TRUE;
-
- return keys;
-}
-
-static gboolean
-g_dbus_action_group_query_action (GActionGroup *g_group,
- const gchar *action_name,
- gboolean *enabled,
- const GVariantType **parameter_type,
- const GVariantType **state_type,
- GVariant **state_hint,
- GVariant **state)
-{
- GDBusActionGroup *group = G_DBUS_ACTION_GROUP (g_group);
- ActionInfo *info;
-
- info = g_hash_table_lookup (group->actions, action_name);
-
- if (info == NULL)
- {
- group->strict = TRUE;
- return FALSE;
- }
-
- if (enabled)
- *enabled = info->enabled;
-
- if (parameter_type)
- *parameter_type = info->parameter_type;
-
- if (state_type)
- *state_type = info->state ? g_variant_get_type (info->state) : NULL;
-
- if (state_hint)
- *state_hint = NULL;
-
- if (state)
- *state = info->state ? g_variant_ref (info->state) : NULL;
-
- return TRUE;
-}
-
-static void
-g_dbus_action_group_change_state (GActionGroup *g_group,
- const gchar *action_name,
- GVariant *value)
-{
- GDBusActionGroup *group = G_DBUS_ACTION_GROUP (g_group);
-
- /* Don't bother with the checks. The other side will do it again. */
- g_dbus_connection_call (group->connection, group->bus_name, group->object_path, "org.gtk.Actions", "SetState",
- g_variant_new ("(sva{sv})", action_name, value, NULL),
- NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
-}
-
-static void
-g_dbus_action_group_activate (GActionGroup *g_group,
- const gchar *action_name,
- GVariant *parameter)
-{
- GDBusActionGroup *group = G_DBUS_ACTION_GROUP (g_group);
- GVariantBuilder builder;
-
- g_variant_builder_init (&builder, G_VARIANT_TYPE ("av"));
-
- if (parameter)
- g_variant_builder_add (&builder, "v", parameter);
-
- g_dbus_connection_call (group->connection, group->bus_name, group->object_path, "org.gtk.Actions", "Activate",
- g_variant_new ("(sava{sv})", action_name, &builder, NULL),
- NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
-}
-
-static void
-g_dbus_action_group_finalize (GObject *object)
-{
- GDBusActionGroup *group = G_DBUS_ACTION_GROUP (object);
-
- g_dbus_connection_signal_unsubscribe (group->connection, group->subscription_id);
- g_hash_table_unref (group->actions);
- g_object_unref (group->connection);
- g_free (group->object_path);
- g_free (group->bus_name);
-
- G_OBJECT_CLASS (g_dbus_action_group_parent_class)
- ->finalize (object);
-}
-
-static void
-g_dbus_action_group_init (GDBusActionGroup *group)
-{
- group->actions = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, action_info_free);
-}
-
-static void
-g_dbus_action_group_class_init (GDBusActionGroupClass *class)
-{
- GObjectClass *object_class = G_OBJECT_CLASS (class);
-
- object_class->finalize = g_dbus_action_group_finalize;
-}
-
-static void
-g_dbus_action_group_iface_init (GActionGroupInterface *iface)
-{
- iface->list_actions = g_dbus_action_group_list_actions;
- iface->query_action = g_dbus_action_group_query_action;
- iface->change_action_state = g_dbus_action_group_change_state;
- iface->activate_action = g_dbus_action_group_activate;
-}
+ G_IMPLEMENT_INTERFACE (G_TYPE_ACTION_GROUP, g_dbus_action_group_iface_init)
+ G_IMPLEMENT_INTERFACE (G_TYPE_REMOTE_ACTION_GROUP, g_dbus_action_group_remote_iface_init))
static void
g_dbus_action_group_changed (GDBusConnection *connection,
GDBusActionGroup *group = user_data;
GActionGroup *g_group = user_data;
+ /* make sure that we've been fully initialised */
+ if (group->actions == NULL)
+ return;
+
if (g_str_equal (signal_name, "Changed") &&
g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(asa{sb}a{sv}a{s(bgav)})")))
{
else
action_info_free (info);
}
+ g_variant_iter_free (iter);
}
}
}
+
static void
g_dbus_action_group_describe_all_done (GObject *source,
GAsyncResult *result,
gpointer user_data)
{
- GSimpleAsyncResult *my_result = user_data;
- GDBusActionGroup *group;
- GError *error = NULL;
+ GDBusActionGroup *group= user_data;
GVariant *reply;
- group = g_simple_async_result_get_op_res_gpointer (my_result);
- g_assert (G_IS_DBUS_ACTION_GROUP (group));
+ g_assert (group->actions == NULL);
+ group->actions = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, action_info_free);
+
g_assert (group->connection == (gpointer) source);
- reply = g_dbus_connection_call_finish (group->connection, result, &error);
+ reply = g_dbus_connection_call_finish (group->connection, result, NULL);
if (reply != NULL)
{
g_variant_get (reply, "(a{s(bgav)})", &iter);
while ((action = action_info_new_from_iter (iter)))
- g_hash_table_insert (group->actions, action->name, action);
+ {
+ g_hash_table_insert (group->actions, action->name, action);
+
+ if (group->strict)
+ g_action_group_action_added (G_ACTION_GROUP (group), action->name);
+ }
g_variant_iter_free (iter);
g_variant_unref (reply);
}
+
+ g_object_unref (group);
+}
+
+
+static void
+g_dbus_action_group_async_init (GDBusActionGroup *group)
+{
+ if (group->subscription_id != 0)
+ return;
+
+ group->subscription_id =
+ g_dbus_connection_signal_subscribe (group->connection, group->bus_name, "org.gtk.Actions", "Changed", group->object_path,
+ NULL, G_DBUS_SIGNAL_FLAGS_NONE, g_dbus_action_group_changed, group, NULL);
+
+ g_dbus_connection_call (group->connection, group->bus_name, group->object_path, "org.gtk.Actions", "DescribeAll", NULL,
+ G_VARIANT_TYPE ("(a{s(bgav)})"), G_DBUS_CALL_FLAGS_NONE, -1, NULL,
+ g_dbus_action_group_describe_all_done, g_object_ref (group));
+}
+
+static gchar **
+g_dbus_action_group_list_actions (GActionGroup *g_group)
+{
+ GDBusActionGroup *group = G_DBUS_ACTION_GROUP (g_group);
+ gchar **keys;
+
+ if (group->actions != NULL)
+ {
+ GHashTableIter iter;
+ gint n, i = 0;
+ gpointer key;
+
+ n = g_hash_table_size (group->actions);
+ keys = g_new (gchar *, n + 1);
+
+ g_hash_table_iter_init (&iter, group->actions);
+ while (g_hash_table_iter_next (&iter, &key, NULL))
+ keys[i++] = g_strdup (key);
+ g_assert_cmpint (i, ==, n);
+ keys[n] = NULL;
+ }
else
{
- g_simple_async_result_set_from_error (my_result, error);
- g_error_free (error);
+ g_dbus_action_group_async_init (group);
+ keys = g_new0 (gchar *, 1);
}
- g_simple_async_result_complete (my_result);
- g_object_unref (my_result);
+ group->strict = TRUE;
+
+ return keys;
}
-/**
- * g_dbus_action_group_new:
- * @connection: A #GDBusConnection
- * @bus_name: the bus name which exports the action group
- * @object_path: the object path at which the action group is exported
- * @cancellable: A #GCancellable or %NULL
- * @callback: Callback function to invoke when the object is ready
- * @user_data: User data to pass to @callback
- *
- * Creates a new, empty, #GDBusActionGroup.
- *
- * This is a failable asynchronous constructor - when the object
- * is ready, @callback will be invoked and you can use
- * g_dbus_action_group_new_finish() to get the result.
- *
- * See g_dbus_action_group_new_sync() and for a synchronous version
- * of this constructor.
- */
-void
-g_dbus_action_group_new (GDBusConnection *connection,
- const gchar *bus_name,
- const gchar *object_path,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data)
+static gboolean
+g_dbus_action_group_query_action (GActionGroup *g_group,
+ const gchar *action_name,
+ gboolean *enabled,
+ const GVariantType **parameter_type,
+ const GVariantType **state_type,
+ GVariant **state_hint,
+ GVariant **state)
{
- GSimpleAsyncResult *result;
- GDBusActionGroup *group;
+ GDBusActionGroup *group = G_DBUS_ACTION_GROUP (g_group);
+ ActionInfo *info;
- group = g_object_new (G_TYPE_DBUS_ACTION_GROUP, NULL);
- group->connection = g_object_ref (connection);
- group->bus_name = g_strdup (bus_name);
- group->object_path = g_strdup (object_path);
+ if (group->actions != NULL)
+ {
+ info = g_hash_table_lookup (group->actions, action_name);
- /* It's probably excessive to worry about watching the name ownership.
- * The person using this class will know for themselves when the name
- * disappears.
- */
+ if (info == NULL)
+ {
+ group->strict = TRUE;
+ return FALSE;
+ }
- result = g_simple_async_result_new (G_OBJECT (group), callback, user_data, g_dbus_action_group_new);
- g_simple_async_result_set_op_res_gpointer (result, group, g_object_unref);
+ if (enabled)
+ *enabled = info->enabled;
- group->subscription_id =
- g_dbus_connection_signal_subscribe (connection, bus_name, "org.gtk.Actions", "Changed", object_path, NULL,
- G_DBUS_SIGNAL_FLAGS_NONE, g_dbus_action_group_changed, group, NULL);
+ if (parameter_type)
+ *parameter_type = info->parameter_type;
+
+ if (state_type)
+ *state_type = info->state ? g_variant_get_type (info->state) : NULL;
+
+ if (state_hint)
+ *state_hint = NULL;
+
+ if (state)
+ *state = info->state ? g_variant_ref (info->state) : NULL;
- g_dbus_connection_call (connection, bus_name, object_path, "org.gtk.Actions", "DescribeAll", NULL,
- G_VARIANT_TYPE ("(a{s(bgav)})"), G_DBUS_CALL_FLAGS_NONE, -1, cancellable,
- g_dbus_action_group_describe_all_done, result);
+ return TRUE;
+ }
+ else
+ {
+ g_dbus_action_group_async_init (group);
+ group->strict = TRUE;
+
+ return FALSE;
+ }
}
-/**
- * g_dbus_action_group_new_finish:
- * @res: A #GAsyncResult obtained from the #GAsyncReadyCallback
- * function passed to g_dbus_action_group_new()
- * @error: Return location for error or %NULL
- *
- * Finishes creating a #GDBusActionGroup.
- *
- * Returns: A #GDBusProxy or %NULL if @error is set. Free with g_object_unref().
- */
-GDBusActionGroup *
-g_dbus_action_group_new_finish (GAsyncResult *result,
- GError **error)
+static void
+g_dbus_action_group_activate_action_full (GRemoteActionGroup *remote,
+ const gchar *action_name,
+ GVariant *parameter,
+ GVariant *platform_data)
{
- GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result);
+ GDBusActionGroup *group = G_DBUS_ACTION_GROUP (remote);
+ GVariantBuilder builder;
- g_return_val_if_fail (g_simple_async_result_is_valid (result,
- g_simple_async_result_get_op_res_gpointer (simple),
- g_dbus_action_group_new), NULL);
+ g_variant_builder_init (&builder, G_VARIANT_TYPE ("av"));
- if (g_simple_async_result_propagate_error (simple, error))
- return NULL;
+ if (parameter)
+ g_variant_builder_add (&builder, "v", parameter);
+
+ g_dbus_connection_call (group->connection, group->bus_name, group->object_path, "org.gtk.Actions", "Activate",
+ g_variant_new ("(sav@a{sv})", action_name, &builder, platform_data),
+ NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
+}
+
+static void
+g_dbus_action_group_change_action_state_full (GRemoteActionGroup *remote,
+ const gchar *action_name,
+ GVariant *value,
+ GVariant *platform_data)
+{
+ GDBusActionGroup *group = G_DBUS_ACTION_GROUP (remote);
+
+ g_dbus_connection_call (group->connection, group->bus_name, group->object_path, "org.gtk.Actions", "SetState",
+ g_variant_new ("(sv@a{sv})", action_name, value, platform_data),
+ NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
+}
+
+static void
+g_dbus_action_group_change_state (GActionGroup *group,
+ const gchar *action_name,
+ GVariant *value)
+{
+ g_dbus_action_group_change_action_state_full (G_REMOTE_ACTION_GROUP (group),
+ action_name, value, g_variant_new ("a{sv}", NULL));
+}
+
+static void
+g_dbus_action_group_activate (GActionGroup *group,
+ const gchar *action_name,
+ GVariant *parameter)
+{
+ g_dbus_action_group_activate_action_full (G_REMOTE_ACTION_GROUP (group),
+ action_name, parameter, g_variant_new ("a{sv}", NULL));
+}
+
+static void
+g_dbus_action_group_finalize (GObject *object)
+{
+ GDBusActionGroup *group = G_DBUS_ACTION_GROUP (object);
+
+ if (group->subscription_id)
+ g_dbus_connection_signal_unsubscribe (group->connection, group->subscription_id);
+
+ if (group->actions)
+ g_hash_table_unref (group->actions);
+
+ g_object_unref (group->connection);
+ g_free (group->object_path);
+ g_free (group->bus_name);
+
+ G_OBJECT_CLASS (g_dbus_action_group_parent_class)
+ ->finalize (object);
+}
+
+static void
+g_dbus_action_group_init (GDBusActionGroup *group)
+{
+}
+
+static void
+g_dbus_action_group_class_init (GDBusActionGroupClass *class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (class);
+
+ object_class->finalize = g_dbus_action_group_finalize;
+}
+
+static void
+g_dbus_action_group_remote_iface_init (GRemoteActionGroupInterface *iface)
+{
+ iface->activate_action_full = g_dbus_action_group_activate_action_full;
+ iface->change_action_state_full = g_dbus_action_group_change_action_state_full;
+}
- return g_object_ref (g_simple_async_result_get_op_res_gpointer (simple));
+static void
+g_dbus_action_group_iface_init (GActionGroupInterface *iface)
+{
+ iface->list_actions = g_dbus_action_group_list_actions;
+ iface->query_action = g_dbus_action_group_query_action;
+ iface->change_action_state = g_dbus_action_group_change_state;
+ iface->activate_action = g_dbus_action_group_activate;
}
/**
- * g_dbus_action_group_new_sync:
+ * g_dbus_action_group_get:
* @connection: A #GDBusConnection
* @bus_name: the bus name which exports the action group
* @object_path: the object path at which the action group is exported
- * @cancellable: A #GCancellable or %NULL
- * @error: Return location for error or %NULL
*
- * This is a synchronous failable constructor. See g_dbus_action_group_new()
- * and g_dbus_action_group_new_finish() for the asynchronous version.
+ * Obtains a #GDBusActionGroup for the action group which is exported at
+ * the given @bus_name and @object_path.
*
- * Returns: A #GDBusProxy or %NULL if @error is set. Free with g_object_unref().
+ * The thread default main context is taken at the time of this call.
+ * All signals on the menu model (and any linked models) are reported
+ * with respect to this context. All calls on the returned menu model
+ * (and linked models) must also originate from this same context, with
+ * the thread default main context unchanged.
+ *
+ * This call is non-blocking. The returned action group may or may not
+ * already be filled in. The correct thing to do is connect the signals
+ * for the action group to monitor for changes and then to call
+ * g_action_group_list_actions() to get the initial list.
+ *
+ * Returns: (transfer full): a #GDBusActionGroup
+ *
+ * Since: 2.32
*/
GDBusActionGroup *
-g_dbus_action_group_new_sync (GDBusConnection *connection,
- const gchar *bus_name,
- const gchar *object_path,
- GCancellable *cancellable,
- GError **error)
+g_dbus_action_group_get (GDBusConnection *connection,
+ const gchar *bus_name,
+ const gchar *object_path)
{
GDBusActionGroup *group;
- GVariant *reply;
group = g_object_new (G_TYPE_DBUS_ACTION_GROUP, NULL);
group->connection = g_object_ref (connection);
group->bus_name = g_strdup (bus_name);
group->object_path = g_strdup (object_path);
+ return group;
+}
+
+gboolean
+g_dbus_action_group_sync (GDBusActionGroup *group,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GVariant *reply;
+
+ g_assert (group->subscription_id == 0);
+
group->subscription_id =
- g_dbus_connection_signal_subscribe (connection, bus_name, "org.gtk.Actions", "Changed", object_path, NULL,
- G_DBUS_SIGNAL_FLAGS_NONE, g_dbus_action_group_changed, group, NULL);
+ g_dbus_connection_signal_subscribe (group->connection, group->bus_name, "org.gtk.Actions", "Changed", group->object_path,
+ NULL, G_DBUS_SIGNAL_FLAGS_NONE, g_dbus_action_group_changed, group, NULL);
- reply = g_dbus_connection_call_sync (connection, bus_name, object_path, "org.gtk.Actions",
+ reply = g_dbus_connection_call_sync (group->connection, group->bus_name, group->object_path, "org.gtk.Actions",
"DescribeAll", NULL, G_VARIANT_TYPE ("(a{s(bgav)})"),
G_DBUS_CALL_FLAGS_NONE, -1, cancellable, error);
GVariantIter *iter;
ActionInfo *action;
+ g_assert (group->actions == NULL);
+ group->actions = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, action_info_free);
+
g_variant_get (reply, "(a{s(bgav)})", &iter);
while ((action = action_info_new_from_iter (iter)))
g_hash_table_insert (group->actions, action->name, action);
g_variant_iter_free (iter);
g_variant_unref (reply);
}
- else
- {
- g_object_unref (group);
- return NULL;
- }
- return group;
+ return reply != NULL;
}