2 * Copyright © 2010 Codethink Limited
3 * Copyright © 2011 Canonical Limited
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public License as published
7 * by the Free Software Foundation; either version 2 of the licence or (at
8 * your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General
16 * Public License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
18 * Boston, MA 02111-1307, USA.
20 * Authors: Ryan Lortie <desrt@desrt.ca>
25 #include "gdbusactiongroup.h"
27 #include "gremoteactiongroup.h"
28 #include "gdbusconnection.h"
29 #include "gactiongroup.h"
32 * SECTION:gdbusactiongroup
33 * @title: GDBusActionGroup
34 * @short_description: A D-Bus GActionGroup implementation
35 * @see_also: <link linkend="gio-GActionGroup-exporter">GActionGroup exporter</link>
37 * #GDBusActionGroup is an implementation of the #GActionGroup
38 * interface that can be used as a proxy for an action group
39 * that is exported over D-Bus with g_dbus_connection_export_action_group().
42 struct _GDBusActionGroup
44 GObject parent_instance
;
46 GDBusConnection
*connection
;
49 guint subscription_id
;
52 /* The 'strict' flag indicates that the non-existence of at least one
53 * action has potentially been observed through the API. This means
54 * that we should always emit 'action-added' signals for all new
57 * The user can observe the non-existence of an action by listing the
58 * actions or by performing a query (such as parameter type) on a
59 * non-existent action.
61 * If the user has no way of knowing that a given action didn't
62 * already exist then we can skip emitting 'action-added' signals
63 * since they have no way of knowing that it wasn't there from the
69 typedef GObjectClass GDBusActionGroupClass
;
74 GVariantType
*parameter_type
;
80 action_info_free (gpointer user_data
)
82 ActionInfo
*info
= user_data
;
87 g_variant_unref (info
->state
);
89 if (info
->parameter_type
)
90 g_variant_type_free (info
->parameter_type
);
92 g_slice_free (ActionInfo
, info
);
96 action_info_new_from_iter (GVariantIter
*iter
)
98 const gchar
*param_str
;
104 if (!g_variant_iter_next (iter
, "{s(b&g@av)}", &name
,
105 &enabled
, ¶m_str
, &state
))
108 info
= g_slice_new (ActionInfo
);
110 info
->enabled
= enabled
;
112 if (g_variant_n_children (state
))
113 g_variant_get_child (state
, 0, "v", &info
->state
);
116 g_variant_unref (state
);
119 info
->parameter_type
= g_variant_type_copy ((GVariantType
*) param_str
);
121 info
->parameter_type
= NULL
;
126 static void g_dbus_action_group_remote_iface_init (GRemoteActionGroupInterface
*iface
);
127 static void g_dbus_action_group_iface_init (GActionGroupInterface
*iface
);
128 G_DEFINE_TYPE_WITH_CODE (GDBusActionGroup
, g_dbus_action_group
, G_TYPE_OBJECT
,
129 G_IMPLEMENT_INTERFACE (G_TYPE_ACTION_GROUP
, g_dbus_action_group_iface_init
)
130 G_IMPLEMENT_INTERFACE (G_TYPE_REMOTE_ACTION_GROUP
, g_dbus_action_group_remote_iface_init
))
133 g_dbus_action_group_changed (GDBusConnection
*connection
,
135 const gchar
*object_path
,
136 const gchar
*interface_name
,
137 const gchar
*signal_name
,
138 GVariant
*parameters
,
141 GDBusActionGroup
*group
= user_data
;
142 GActionGroup
*g_group
= user_data
;
144 /* make sure that we've been fully initialised */
145 if (group
->actions
== NULL
)
148 if (g_str_equal (signal_name
, "Changed") &&
149 g_variant_is_of_type (parameters
, G_VARIANT_TYPE ("(asa{sb}a{sv}a{s(bgav)})")))
156 g_variant_get_child (parameters
, 0, "as", &iter
);
157 while (g_variant_iter_next (iter
, "&s", &name
))
159 if (g_hash_table_lookup (group
->actions
, name
))
161 g_hash_table_remove (group
->actions
, name
);
162 g_action_group_action_removed (g_group
, name
);
165 g_variant_iter_free (iter
);
174 g_variant_get_child (parameters
, 1, "a{sb}", &iter
);
175 while (g_variant_iter_next (iter
, "{&sb}", &name
, &enabled
))
179 info
= g_hash_table_lookup (group
->actions
, name
);
181 if (info
&& info
->enabled
!= enabled
)
183 info
->enabled
= enabled
;
184 g_action_group_action_enabled_changed (g_group
, name
, enabled
);
187 g_variant_iter_free (iter
);
196 g_variant_get_child (parameters
, 2, "a{sv}", &iter
);
197 while (g_variant_iter_next (iter
, "{&sv}", &name
, &state
))
201 info
= g_hash_table_lookup (group
->actions
, name
);
203 if (info
&& info
->state
&& !g_variant_equal (state
, info
->state
) &&
204 g_variant_is_of_type (state
, g_variant_get_type (info
->state
)))
206 g_variant_unref (info
->state
);
207 info
->state
= g_variant_ref (state
);
209 g_action_group_action_state_changed (g_group
, name
, state
);
212 g_variant_unref (state
);
214 g_variant_iter_free (iter
);
222 g_variant_get_child (parameters
, 3, "a{s(bgav)}", &iter
);
223 while ((info
= action_info_new_from_iter (iter
)))
225 if (!g_hash_table_lookup (group
->actions
, info
->name
))
227 g_hash_table_insert (group
->actions
, info
->name
, info
);
230 g_action_group_action_added (g_group
, info
->name
);
233 action_info_free (info
);
235 g_variant_iter_free (iter
);
242 g_dbus_action_group_describe_all_done (GObject
*source
,
243 GAsyncResult
*result
,
246 GDBusActionGroup
*group
= user_data
;
249 g_assert (group
->actions
== NULL
);
250 group
->actions
= g_hash_table_new_full (g_str_hash
, g_str_equal
, NULL
, action_info_free
);
252 g_assert (group
->connection
== (gpointer
) source
);
253 reply
= g_dbus_connection_call_finish (group
->connection
, result
, NULL
);
260 g_variant_get (reply
, "(a{s(bgav)})", &iter
);
261 while ((action
= action_info_new_from_iter (iter
)))
263 g_hash_table_insert (group
->actions
, action
->name
, action
);
266 g_action_group_action_added (G_ACTION_GROUP (group
), action
->name
);
268 g_variant_iter_free (iter
);
269 g_variant_unref (reply
);
275 g_dbus_action_group_async_init (GDBusActionGroup
*group
)
277 if (group
->subscription_id
!= 0)
280 group
->subscription_id
=
281 g_dbus_connection_signal_subscribe (group
->connection
, group
->bus_name
, "org.gtk.Actions", "Changed", group
->object_path
,
282 NULL
, G_DBUS_SIGNAL_FLAGS_NONE
, g_dbus_action_group_changed
, group
, NULL
);
284 g_dbus_connection_call (group
->connection
, group
->bus_name
, group
->object_path
, "org.gtk.Actions", "DescribeAll", NULL
,
285 G_VARIANT_TYPE ("(a{s(bgav)})"), G_DBUS_CALL_FLAGS_NONE
, -1, NULL
,
286 g_dbus_action_group_describe_all_done
, group
);
290 g_dbus_action_group_list_actions (GActionGroup
*g_group
)
292 GDBusActionGroup
*group
= G_DBUS_ACTION_GROUP (g_group
);
295 if (group
->actions
!= NULL
)
301 n
= g_hash_table_size (group
->actions
);
302 keys
= g_new (gchar
*, n
+ 1);
304 g_hash_table_iter_init (&iter
, group
->actions
);
305 while (g_hash_table_iter_next (&iter
, &key
, NULL
))
306 keys
[i
++] = g_strdup (key
);
307 g_assert_cmpint (i
, ==, n
);
312 g_dbus_action_group_async_init (group
);
313 keys
= g_new0 (gchar
*, 1);
316 group
->strict
= TRUE
;
322 g_dbus_action_group_query_action (GActionGroup
*g_group
,
323 const gchar
*action_name
,
325 const GVariantType
**parameter_type
,
326 const GVariantType
**state_type
,
327 GVariant
**state_hint
,
330 GDBusActionGroup
*group
= G_DBUS_ACTION_GROUP (g_group
);
333 if (group
->actions
!= NULL
)
335 info
= g_hash_table_lookup (group
->actions
, action_name
);
339 group
->strict
= TRUE
;
344 *enabled
= info
->enabled
;
347 *parameter_type
= info
->parameter_type
;
350 *state_type
= info
->state
? g_variant_get_type (info
->state
) : NULL
;
356 *state
= info
->state
? g_variant_ref (info
->state
) : NULL
;
362 g_dbus_action_group_async_init (group
);
369 g_dbus_action_group_activate_action_full (GRemoteActionGroup
*remote
,
370 const gchar
*action_name
,
372 GVariant
*platform_data
)
374 GDBusActionGroup
*group
= G_DBUS_ACTION_GROUP (remote
);
375 GVariantBuilder builder
;
377 g_variant_builder_init (&builder
, G_VARIANT_TYPE ("av"));
380 g_variant_builder_add (&builder
, "v", parameter
);
382 g_dbus_connection_call (group
->connection
, group
->bus_name
, group
->object_path
, "org.gtk.Actions", "Activate",
383 g_variant_new ("(sav@a{sv})", action_name
, &builder
, platform_data
),
384 NULL
, G_DBUS_CALL_FLAGS_NONE
, -1, NULL
, NULL
, NULL
);
388 g_dbus_action_group_change_action_state_full (GRemoteActionGroup
*remote
,
389 const gchar
*action_name
,
391 GVariant
*platform_data
)
393 GDBusActionGroup
*group
= G_DBUS_ACTION_GROUP (remote
);
395 g_dbus_connection_call (group
->connection
, group
->bus_name
, group
->object_path
, "org.gtk.Actions", "SetState",
396 g_variant_new ("(sv@a{sv})", action_name
, value
, platform_data
),
397 NULL
, G_DBUS_CALL_FLAGS_NONE
, -1, NULL
, NULL
, NULL
);
401 g_dbus_action_group_change_state (GActionGroup
*group
,
402 const gchar
*action_name
,
405 g_dbus_action_group_change_action_state_full (G_REMOTE_ACTION_GROUP (group
),
406 action_name
, value
, g_variant_new ("a{sv}", NULL
));
410 g_dbus_action_group_activate (GActionGroup
*group
,
411 const gchar
*action_name
,
414 g_dbus_action_group_activate_action_full (G_REMOTE_ACTION_GROUP (group
),
415 action_name
, parameter
, g_variant_new ("a{sv}", NULL
));
419 g_dbus_action_group_finalize (GObject
*object
)
421 GDBusActionGroup
*group
= G_DBUS_ACTION_GROUP (object
);
423 if (group
->subscription_id
)
424 g_dbus_connection_signal_unsubscribe (group
->connection
, group
->subscription_id
);
427 g_hash_table_unref (group
->actions
);
429 g_object_unref (group
->connection
);
430 g_free (group
->object_path
);
431 g_free (group
->bus_name
);
433 G_OBJECT_CLASS (g_dbus_action_group_parent_class
)
438 g_dbus_action_group_init (GDBusActionGroup
*group
)
443 g_dbus_action_group_class_init (GDBusActionGroupClass
*class)
445 GObjectClass
*object_class
= G_OBJECT_CLASS (class);
447 object_class
->finalize
= g_dbus_action_group_finalize
;
451 g_dbus_action_group_remote_iface_init (GRemoteActionGroupInterface
*iface
)
453 iface
->activate_action_full
= g_dbus_action_group_activate_action_full
;
454 iface
->change_action_state_full
= g_dbus_action_group_change_action_state_full
;
458 g_dbus_action_group_iface_init (GActionGroupInterface
*iface
)
460 iface
->list_actions
= g_dbus_action_group_list_actions
;
461 iface
->query_action
= g_dbus_action_group_query_action
;
462 iface
->change_action_state
= g_dbus_action_group_change_state
;
463 iface
->activate_action
= g_dbus_action_group_activate
;
467 * g_dbus_action_group_get:
468 * @connection: A #GDBusConnection
469 * @bus_name: the bus name which exports the action group
470 * @object_path: the object path at which the action group is exported
472 * Obtains a #GDBusActionGroup for the action group which is exported at
473 * the given @bus_name and @object_path.
475 * The thread default main context is taken at the time of this call.
476 * All signals on the menu model (and any linked models) are reported
477 * with respect to this context. All calls on the returned menu model
478 * (and linked models) must also originate from this same context, with
479 * the thread default main context unchanged.
481 * This call is non-blocking. The returned action group may or may not
482 * already be filled in. The correct thing to do is connect the signals
483 * for the action group to monitor for changes and then to call
484 * g_action_group_list_actions() to get the initial list.
486 * Returns: (transfer full): a #GDBusActionGroup
491 g_dbus_action_group_get (GDBusConnection
*connection
,
492 const gchar
*bus_name
,
493 const gchar
*object_path
)
495 GDBusActionGroup
*group
;
497 group
= g_object_new (G_TYPE_DBUS_ACTION_GROUP
, NULL
);
498 group
->connection
= g_object_ref (connection
);
499 group
->bus_name
= g_strdup (bus_name
);
500 group
->object_path
= g_strdup (object_path
);
505 G_GNUC_INTERNAL gboolean
506 g_dbus_action_group_sync (GDBusActionGroup
*group
,
507 GCancellable
*cancellable
,
512 g_assert (group
->subscription_id
== 0);
514 group
->subscription_id
=
515 g_dbus_connection_signal_subscribe (group
->connection
, group
->bus_name
, "org.gtk.Actions", "Changed", group
->object_path
,
516 NULL
, G_DBUS_SIGNAL_FLAGS_NONE
, g_dbus_action_group_changed
, group
, NULL
);
518 reply
= g_dbus_connection_call_sync (group
->connection
, group
->bus_name
, group
->object_path
, "org.gtk.Actions",
519 "DescribeAll", NULL
, G_VARIANT_TYPE ("(a{s(bgav)})"),
520 G_DBUS_CALL_FLAGS_NONE
, -1, cancellable
, error
);
527 g_assert (group
->actions
== NULL
);
528 group
->actions
= g_hash_table_new_full (g_str_hash
, g_str_equal
, NULL
, action_info_free
);
530 g_variant_get (reply
, "(a{s(bgav)})", &iter
);
531 while ((action
= action_info_new_from_iter (iter
)))
532 g_hash_table_insert (group
->actions
, action
->name
, action
);
533 g_variant_iter_free (iter
);
534 g_variant_unref (reply
);
537 return reply
!= NULL
;