2
* Copyright © 2012 Canonical Ltd.
4
* This program is free software: you can redistribute it and/or modify
5
* it under the terms of either or both of the following licences:
7
* 1) the GNU Lesser General Public License version 3, as published by
8
* the Free Software Foundation; and/or
9
* 2) the GNU Lesser General Public License version 2.1, as published by
10
* the Free Software Foundation.
12
* This program is distributed in the hope that it will be useful, but
13
* WITHOUT ANY WARRANTY; without even the implied warranties of
14
* MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR
15
* PURPOSE. See the applicable version of the GNU Lesser General Public
16
* License for more details.
18
* You should have received a copy of both the GNU Lesser General Public
19
* License version 3 and version 2.1 along with this program. If not,
20
* see <http://www.gnu.org/licenses/>
22
* Author: Ryan Lortie <desrt@desrt.ca>
25
#include "action-publisher.h"
32
typedef GMenuModelClass HudAuxClass;
36
GMenuModel parent_instance;
38
HudActionPublisher *publisher;
41
static void hud_aux_init_action_group_iface (GActionGroupInterface *iface);
42
static void hud_aux_init_remote_action_group_iface (GRemoteActionGroupInterface *iface);
43
G_DEFINE_TYPE_WITH_CODE (HudAux, _hud_aux, G_TYPE_MENU_MODEL,
44
G_IMPLEMENT_INTERFACE (G_TYPE_ACTION_GROUP, hud_aux_init_action_group_iface)
45
G_IMPLEMENT_INTERFACE (G_TYPE_REMOTE_ACTION_GROUP, hud_aux_init_remote_action_group_iface))
47
typedef GObjectClass HudActionPublisherClass;
49
struct _HudActionPublisher
51
GObject parent_instance;
54
GApplication *application;
59
GSequence *descriptions;
62
GList * action_groups;
67
SIGNAL_ACTION_GROUP_ADDED,
68
SIGNAL_ACTION_GROUP_REMOVED,
72
guint hud_action_publisher_signals[N_SIGNALS];
74
G_DEFINE_TYPE (HudActionPublisher, hud_action_publisher, G_TYPE_OBJECT)
76
typedef GObjectClass HudActionDescriptionClass;
78
struct _HudActionDescription
80
GObject parent_instance;
89
guint hud_action_description_changed_signal;
91
G_DEFINE_TYPE (HudActionDescription, hud_action_description, G_TYPE_OBJECT)
95
hud_aux_is_mutable (GMenuModel *model)
101
hud_aux_get_n_items (GMenuModel *model)
103
HudAux *aux = (HudAux *) model;
105
return g_sequence_get_length (aux->publisher->descriptions);
109
hud_aux_get_item_attributes (GMenuModel *model,
111
GHashTable **attributes)
113
HudAux *aux = (HudAux *) model;
115
HudActionDescription *description;
117
iter = g_sequence_get_iter_at_pos (aux->publisher->descriptions, item_index);
118
description = g_sequence_get (iter);
120
*attributes = g_hash_table_ref (description->attrs);
124
hud_aux_get_item_links (GMenuModel *model,
128
HudAux *aux = (HudAux *) model;
130
HudActionDescription *description;
132
iter = g_sequence_get_iter_at_pos (aux->publisher->descriptions, item_index);
133
description = g_sequence_get (iter);
135
if (description->links != NULL)
136
*links = g_hash_table_ref(description->links);
138
*links = g_hash_table_new (NULL, NULL);
142
_hud_aux_init (HudAux *aux)
147
hud_aux_init_action_group_iface (GActionGroupInterface *iface)
152
hud_aux_init_remote_action_group_iface (GRemoteActionGroupInterface *iface)
157
_hud_aux_class_init (HudAuxClass *class)
159
class->is_mutable = hud_aux_is_mutable;
160
class->get_n_items = hud_aux_get_n_items;
161
class->get_item_attributes = hud_aux_get_item_attributes;
162
class->get_item_links = hud_aux_get_item_links;
166
hud_action_publisher_dispose (GObject *object)
168
HudActionPublisher *self = HUD_ACTION_PUBLISHER(object);
170
g_clear_pointer(&self->id, g_variant_unref);
172
g_clear_object(&self->aux);
173
g_clear_object(&self->application);
175
g_debug("Un-exporting menu model at with id [%d]", self->export_id);
176
g_dbus_connection_unexport_menu_model (self->bus, self->export_id);
178
g_clear_pointer(&self->path, g_free);
179
g_clear_pointer(&self->descriptions, g_sequence_free);
181
g_list_free_full(self->action_groups, g_free);
183
g_clear_object(&self->bus);
185
G_OBJECT_CLASS (hud_action_publisher_parent_class)->dispose (object);
189
hud_action_publisher_finalize (GObject *object)
191
G_OBJECT_CLASS (hud_action_publisher_parent_class)->finalize (object);
196
hud_action_publisher_init (HudActionPublisher *publisher)
198
static guint64 next_id;
200
publisher->descriptions = g_sequence_new (g_object_unref);
201
publisher->aux = g_object_new (_hud_aux_get_type (), NULL);
202
publisher->aux->publisher = publisher;
203
publisher->bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
210
guint64 id = next_id++;
213
publisher->path = g_strdup_printf ("/com/canonical/hud/publisher");
215
publisher->path = g_strdup_printf ("/com/canonical/hud/publisher%" G_GUINT64_FORMAT, id);
217
publisher->export_id = g_dbus_connection_export_menu_model (publisher->bus, publisher->path,
218
G_MENU_MODEL (publisher->aux), NULL);
219
g_debug("Exporting menu model at [%s] with id [%d]", publisher->path, publisher->export_id);
221
if (!publisher->export_id)
223
g_free (publisher->path);
225
while (publisher->path == NULL);
229
hud_action_publisher_class_init (HudActionPublisherClass *class)
231
class->dispose = hud_action_publisher_dispose;
232
class->finalize = hud_action_publisher_finalize;
235
* HudActionPublisher::action-group-added:
236
* @param1: Prefix for the action group
237
* @param2: Path group is exported on DBus
239
* Emitted when a new action group is added to the publisher
241
hud_action_publisher_signals[SIGNAL_ACTION_GROUP_ADDED] = g_signal_new (HUD_ACTION_PUBLISHER_SIGNAL_ACTION_GROUP_ADDED, HUD_TYPE_ACTION_PUBLISHER,
242
G_SIGNAL_RUN_LAST, 0, NULL, NULL,
243
_hud_marshal_VOID__STRING_STRING,
244
G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_STRING);
246
* HudActionPublisher::action-group-removed:
247
* @param1: Prefix for the action group
248
* @param2: Path group is exported on DBus
250
* Emitted when a new action group is removed from the publisher
252
hud_action_publisher_signals[SIGNAL_ACTION_GROUP_REMOVED] = g_signal_new (HUD_ACTION_PUBLISHER_SIGNAL_ACTION_GROUP_REMOVED, HUD_TYPE_ACTION_PUBLISHER,
253
G_SIGNAL_RUN_LAST, 0, NULL, NULL,
254
_hud_marshal_VOID__STRING_STRING,
255
G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_STRING);
259
* hud_action_publisher_new_for_application:
260
* @application: A #GApplication object
262
* Creates a new #HudActionPublisher and automatically registers the
263
* default actions under the "app" prefix.
265
* Return value: (transfer full): A new #HudActionPublisher object
268
hud_action_publisher_new_for_application (GApplication *application)
270
HudActionPublisher *publisher;
272
g_return_val_if_fail (G_IS_APPLICATION (application), NULL);
273
g_return_val_if_fail (g_application_get_application_id (application), NULL);
274
g_return_val_if_fail (g_application_get_is_registered (application), NULL);
275
g_return_val_if_fail (!g_application_get_is_remote (application), NULL);
277
publisher = g_object_new (HUD_TYPE_ACTION_PUBLISHER, NULL);
278
publisher->application = g_object_ref (application);
280
hud_action_publisher_add_action_group (publisher, "app",
281
g_application_get_dbus_object_path (application));
287
* hud_action_publisher_new_for_id:
290
* Creates a new #HudActionPublisher based on the window ID passed
293
* Return value: (transfer full): A new #HudActionPublisher object
296
hud_action_publisher_new_for_id (GVariant * id)
298
HudActionPublisher * publisher;
299
publisher = g_object_new (HUD_TYPE_ACTION_PUBLISHER, NULL);
302
publisher->id = g_variant_ref_sink(id);
306
publisher->id = g_variant_ref_sink(g_variant_new_int32(-1));
313
format_identifier (const gchar *action_name,
314
GVariant *action_target)
321
targetstr = g_variant_print (action_target, TRUE);
322
identifier = g_strdup_printf ("%s(%s)", action_name, targetstr);
327
identifier = g_strdup_printf ("%s()", action_name);
333
compare_descriptions (gconstpointer a,
337
const HudActionDescription *da = a;
338
const HudActionDescription *db = b;
340
return strcmp (da->identifier, db->identifier);
344
description_changed (HudActionDescription *description,
345
const gchar *attribute_name,
348
HudActionPublisher *publisher = user_data;
351
iter = g_sequence_lookup (publisher->descriptions, description, compare_descriptions, NULL);
352
g_assert (g_sequence_get (iter) == description);
354
g_menu_model_items_changed (G_MENU_MODEL (publisher->aux), g_sequence_iter_get_position (iter), 1, 1);
358
disconnect_handler (gpointer data,
361
HudActionPublisher *publisher = user_data;
362
HudActionDescription *description = data;
364
g_signal_handlers_disconnect_by_func (description, description_changed, publisher);
368
* hud_action_publisher_add_description:
369
* @publisher: the #HudActionPublisher
370
* @description: an action description
372
* Adds @description to the list of actions that the application exports
375
* If the application is already exporting an action with the same name
376
* and target value as @description then it will be replaced.
378
* You should only use this API for situations like recent documents and
382
hud_action_publisher_add_description (HudActionPublisher *publisher,
383
HudActionDescription *description)
387
iter = g_sequence_lookup (publisher->descriptions, description, compare_descriptions, NULL);
391
/* We are not replacing -- add new. */
392
iter = g_sequence_insert_sorted (publisher->descriptions, description, compare_descriptions, NULL);
394
/* Signal that we added the items */
395
g_menu_model_items_changed (G_MENU_MODEL (publisher->aux), g_sequence_iter_get_position (iter), 0, 1);
399
/* We are replacing an existing item. */
400
disconnect_handler (g_sequence_get (iter), publisher);
401
g_sequence_set (iter, description);
403
/* A replace is 1 remove and 1 add */
404
g_menu_model_items_changed (G_MENU_MODEL (publisher->aux), g_sequence_iter_get_position (iter), 1, 1);
407
g_object_ref (description);
409
g_signal_connect (description, "changed", G_CALLBACK (description_changed), publisher);
413
* hud_action_publisher_add_action_group:
414
* @publisher: a #HudActionPublisher
415
* @prefix: the action prefix for the group (like "app")
416
* @object_path: the object path of the exported group
418
* Informs the HUD of the existance of an action group.
420
* The action group must be published on the shared session bus
421
* connection of this process at @object_path.
423
* The @prefix defines the type of action group. Currently "app" and
424
* "win" are supported. For example, if the exported action group
425
* contained a "quit" action and you wanted to refer to it as "app.quit"
426
* from action descriptions, then you would use the @prefix "app" here.
428
* @identifier is a piece of identifying information, depending on which
429
* @prefix is used. Currently, this should be %NULL for the "app"
430
* @prefix and should be a uint32 specifying the window ID (eg: XID) for
433
* You do not need to manually export your action groups if you are
434
* using #GApplication.
437
hud_action_publisher_add_action_group (HudActionPublisher *publisher,
439
const gchar *object_path)
441
g_return_if_fail(HUD_IS_ACTION_PUBLISHER(publisher));
442
g_return_if_fail(prefix != NULL);
443
g_return_if_fail(object_path != NULL);
445
HudActionPublisherActionGroupSet * group = g_new0(HudActionPublisherActionGroupSet, 1);
447
group->prefix = g_strdup(prefix);
448
group->path = g_strdup(object_path);
450
publisher->action_groups = g_list_prepend(publisher->action_groups, group);
456
* hud_action_publisher_remove_action_group:
457
* @publisher: a #HudActionPublisher
458
* @prefix: the action prefix for the group (like "app")
459
* @identifier: (allow none): an identifier, or %NULL
461
* Informs the HUD that an action group no longer exists.
463
* This reverses the effect of a previous call to
464
* hud_action_publisher_add_action_group() with the same parameters.
467
hud_action_publisher_remove_action_group (HudActionPublisher *publisher,
469
GVariant *identifier)
471
//hud_manager_remove_actions (publisher->application_id, prefix, identifier);
475
* hud_action_publisher_get_id:
476
* @publisher: A #HudActionPublisher object
478
* Grabs the ID for this publisher
480
* Return value: (transfer none): The ID this publisher was created with
483
hud_action_publisher_get_id (HudActionPublisher *publisher)
485
g_return_val_if_fail(HUD_IS_ACTION_PUBLISHER(publisher), NULL);
486
return publisher->id;
490
* hud_action_publisher_get_action_groups:
491
* @publisher: A #HudActionPublisher object
493
* Grabs the action groups for this publisher
495
* Return value: (transfer container) (element-type HudActionPublisherActionGroupSet *): The groups in this publisher
498
hud_action_publisher_get_action_groups (HudActionPublisher *publisher)
500
g_return_val_if_fail(HUD_IS_ACTION_PUBLISHER(publisher), NULL);
501
/* TODO: Flesh out */
503
return publisher->action_groups;
507
* hud_action_publisher_get_description_path:
508
* @publisher: A #HudActionPublisher object
510
* Grabs the object path of the description for this publisher
512
* Return value: The object path of the descriptions
515
hud_action_publisher_get_description_path (HudActionPublisher *publisher)
517
g_return_val_if_fail(HUD_IS_ACTION_PUBLISHER(publisher), NULL);
518
return publisher->path;
522
* HudActionDescription:
524
* A description of an action that is accessible from the HUD. This is
525
* an opaque structure type and all accesses must be made via the API.
529
hud_action_description_finalize (GObject *object)
531
HudActionDescription *description = HUD_ACTION_DESCRIPTION (object);
533
g_free (description->identifier);
534
g_free (description->action);
535
if (description->target)
536
g_variant_unref (description->target);
537
g_hash_table_unref (description->attrs);
538
g_clear_pointer(&description->links, g_hash_table_unref);
540
G_OBJECT_CLASS (hud_action_description_parent_class)
545
hud_action_description_init (HudActionDescription *description)
547
description->attrs = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_variant_unref);
551
hud_action_description_class_init (HudActionDescriptionClass *class)
553
class->finalize = hud_action_description_finalize;
556
* HudActionDescription::changed:
557
* @param1: Name of changed property
559
* Emitted when a property of the action description gets
562
hud_action_description_changed_signal = g_signal_new ("changed", HUD_TYPE_ACTION_DESCRIPTION,
563
G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, 0, NULL, NULL,
564
g_cclosure_marshal_VOID__STRING,
565
G_TYPE_NONE, 1, G_TYPE_STRING);
569
* hud_action_description_new:
570
* @action_name: a (namespaced) action name
571
* @action_target: an action target
573
* Creates a new #HudActionDescription.
575
* The situations in which you want to do this are limited to "dynamic"
576
* types of actions -- things like bookmarks or recent documents.
578
* Use hud_action_publisher_add_descriptions_from_file() to take care of
579
* the bulk of the actions in your application.
581
* Returns: a new #HudActionDescription with no attributes
583
HudActionDescription *
584
hud_action_description_new (const gchar *action_name,
585
GVariant *action_target)
587
HudActionDescription *description;
589
g_return_val_if_fail (action_name != NULL, NULL);
591
description = g_object_new (HUD_TYPE_ACTION_DESCRIPTION, NULL);
592
description->action = g_strdup (action_name);
593
description->target = action_target ? g_variant_ref_sink (action_target) : NULL;
594
description->identifier = format_identifier (action_name, action_target);
596
g_hash_table_insert (description->attrs, g_strdup ("action"),
597
g_variant_ref_sink (g_variant_new_string (action_name)));
600
g_hash_table_insert (description->attrs, g_strdup ("target"), g_variant_ref_sink (action_target));
606
* hud_action_description_set_attribute_value:
607
* @description: a #HudActionDescription
608
* @attribute_name: an attribute name
609
* @value: (allow none): the new value for the attribute
611
* Sets or unsets an attribute on @description.
613
* You may not change the "action" or "target" attributes.
615
* If @value is non-%NULL then it is the new value for attribute. A
616
* %NULL @value unsets @attribute_name.
618
* @value is consumed if it is floating.
621
hud_action_description_set_attribute_value (HudActionDescription *description,
622
const gchar *attribute_name,
625
/* Don't allow setting the action or target as these form the
626
* identity of the description and are stored separately...
628
g_return_if_fail (!g_str_equal (attribute_name, "action"));
629
g_return_if_fail (!g_str_equal (attribute_name, "target"));
632
g_hash_table_insert (description->attrs, g_strdup (attribute_name), g_variant_ref_sink (value));
634
g_hash_table_remove (description->attrs, attribute_name);
636
g_signal_emit (description, hud_action_description_changed_signal,
637
g_quark_try_string (attribute_name), attribute_name);
641
* hud_action_description_set_attribute:
642
* @description: a #HudActionDescription
643
* @attribute_name: an attribute name
644
* @format_string: (allow none): a #GVariant format string
645
* @...: arguments to @format_string
647
* Sets or unsets an attribute on @description.
649
* You may not change the "action" or "target" attributes.
651
* If @format_string is non-%NULL then this call is equivalent to
652
* g_variant_new() and hud_action_description_set_attribute_value().
654
* If @format_string is %NULL then this call is equivalent to
655
* hud_action_description_set_attribute_value() with a %NULL value.
658
hud_action_description_set_attribute (HudActionDescription *description,
659
const gchar *attribute_name,
660
const gchar *format_string,
665
if (format_string != NULL)
669
va_start (ap, format_string);
670
value = g_variant_new_va (format_string, NULL, &ap);
676
hud_action_description_set_attribute_value (description, attribute_name, value);
680
* hud_action_description_get_action_name:
681
* @description: a #HudActionDescription
683
* Gets the action name of @description.
685
* This, together with the action target, uniquely identify an action
688
* Returns: (transfer none): the action name
691
hud_action_description_get_action_name (HudActionDescription *description)
693
return description->action;
697
* hud_action_description_get_action_target:
698
* @description: a #HudActionDescription
700
* Gets the action target of @description (ie: the #GVariant that will
701
* be passed to invocations of the action).
705
* This, together with the action name, uniquely identify an action
708
* Returns: (transfer none): the target value
711
hud_action_description_get_action_target (HudActionDescription *description)
713
return description->target;
717
* hud_action_description_set_parameterized:
718
* @parent: a #HudActionDescription
719
* @child: The child #GMenuModel to add
721
* A function to put one action description as a child for the first
722
* one. This is used for parameterized actions where one can set up
723
* children that are displayed on the 'dialog' mode of the HUD.
726
hud_action_description_set_parameterized (HudActionDescription * parent, GMenuModel * child)
728
g_return_if_fail(HUD_IS_ACTION_DESCRIPTION(parent));
729
g_return_if_fail(child == NULL || G_IS_MENU_MODEL(child)); /* NULL is allowed to clear it */
731
if (parent->links == NULL) {
732
parent->links = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_object_unref);
736
g_hash_table_insert(parent->links, g_strdup(G_MENU_LINK_SUBMENU), g_object_ref(child));
738
g_hash_table_remove(parent->links, G_MENU_LINK_SUBMENU);
741
g_signal_emit (parent, hud_action_description_changed_signal,
742
g_quark_try_string (G_MENU_LINK_SUBMENU), G_MENU_LINK_SUBMENU);
748
* hud_action_description_ref:
749
* @description: A #HudActionDescription
751
* Increase the reference count to an action description
753
* Return value: (transfer none): Value of @description
755
HudActionDescription *
756
hud_action_description_ref (HudActionDescription * description)
758
g_return_val_if_fail(HUD_IS_ACTION_DESCRIPTION(description), NULL);
760
return HUD_ACTION_DESCRIPTION(g_object_ref(description));
764
* hud_action_description_unref:
765
* @description: A #HudActionDescription
767
* Decrease the reference count to an action description
770
hud_action_description_unref (HudActionDescription * description)
772
g_return_if_fail(HUD_IS_ACTION_DESCRIPTION(description));
774
return g_object_unref(description);