Merge lp:~larsu/indicator-appmenu/lp1042824 into lp:indicator-appmenu/12.10

Proposed by Lars Karlitski
Status: Merged
Approved by: Lars Karlitski
Approved revision: 220
Merge reported by: Lars Karlitski
Merged at revision: not available
Proposed branch: lp:~larsu/indicator-appmenu/lp1042824
Merge into: lp:indicator-appmenu/12.10
Diff against target: 585 lines (+290/-103)
3 files modified
src/hudindicatorsource.c (+32/-10)
src/hudmenumodelcollector.c (+251/-93)
src/hudmenumodelcollector.h (+7/-0)
To merge this branch: bzr merge lp:~larsu/indicator-appmenu/lp1042824
Reviewer Review Type Date Requested Status
Allison Karlitskaya (community) Approve
Review via email: mp+126448@code.launchpad.net
To post a comment you must log in.
212. By Lars Karlitski

hudmenumodelcollector: only install a single qdata models

213. By Lars Karlitski

hudmenumodelcollector: introduce explicit application mode

Previously, indicator actions prefixed with "win." would be ignored.

214. By Lars Karlitski

hudmenumodelcollector: get rid of conditional free

215. By Lars Karlitski

hudmenumodelcollector: move context and namespace logic into hud_item_context_new

216. By Allison Karlitskaya

Clean up HudMenuModelCollector a bit

Mostly, add some functionality to HudMenuModelContext (formerly
HudItemContext) and HudModelItem in order to reduce the growing
complexity of hud_menu_model_context_model_changed().

Add some comments.

217. By Allison Karlitskaya

HudMenuModelCollector: store the prefix in the instance

When the menu is refreshed (which can happen any time an item is
removed) the menus are rebuilt and we need to know the prefix in order
to handle that.

218. By Allison Karlitskaya

HudMenuModelCollector: untangle code a bit (no functional changes)

Move some functions around so that all of HudMenuModelContext is grouped
together, at the top.

219. By Lars Karlitski

hudmenumodelcollector: call add_model after the collector is initialized

220. By Lars Karlitski

hudmenumodelcollector: don't show underscores in the hud

Revision history for this message
Allison Karlitskaya (desrt) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/hudindicatorsource.c'
2--- src/hudindicatorsource.c 2012-03-30 13:32:40 +0000
3+++ src/hudindicatorsource.c 2012-10-01 14:06:35 +0000
4@@ -24,6 +24,7 @@
5
6 #include "hudsettings.h"
7 #include "huddbusmenucollector.h"
8+#include "hudmenumodelcollector.h"
9 #include "hudsource.h"
10
11 /**
12@@ -49,6 +50,7 @@
13 const gchar *indicator_name;
14 const gchar *user_visible_name;
15 const gchar *icon;
16+ gboolean uses_gmenumodel;
17 } IndicatorInfo;
18
19 static const IndicatorInfo indicator_info[] = {
20@@ -85,7 +87,8 @@
21 .dbus_menu_path = "/com/canonical/indicator/messages/menu",
22 .indicator_name = "indicator-messages",
23 .user_visible_name = N_("Messages"),
24- .icon = "indicator-messages"
25+ .icon = "indicator-messages",
26+ .uses_gmenumodel = TRUE
27 }
28 };
29
30@@ -180,15 +183,34 @@
31 gpointer user_data)
32 {
33 HudIndicatorSourceIndicator *indicator = user_data;
34- HudDbusmenuCollector *collector;
35-
36- collector = hud_dbusmenu_collector_new_for_endpoint (indicator->info->indicator_name,
37- _(indicator->info->user_visible_name),
38- indicator->info->icon,
39- hud_settings.indicator_penalty,
40- name_owner, indicator->info->dbus_menu_path);
41- g_signal_connect (collector, "changed", G_CALLBACK (hud_indicator_source_collector_changed), indicator);
42- indicator->collector = HUD_SOURCE (collector);
43+
44+ if (indicator->info->uses_gmenumodel)
45+ {
46+ HudMenuModelCollector *collector;
47+
48+ collector = hud_menu_model_collector_new_for_endpoint (indicator->info->indicator_name,
49+ _(indicator->info->user_visible_name),
50+ indicator->info->icon,
51+ hud_settings.indicator_penalty,
52+ name_owner,
53+ indicator->info->dbus_menu_path);
54+
55+ indicator->collector = HUD_SOURCE (collector);
56+ }
57+ else
58+ {
59+ HudDbusmenuCollector *collector;
60+
61+ collector = hud_dbusmenu_collector_new_for_endpoint (indicator->info->indicator_name,
62+ _(indicator->info->user_visible_name),
63+ indicator->info->icon,
64+ hud_settings.indicator_penalty,
65+ name_owner, indicator->info->dbus_menu_path);
66+ indicator->collector = HUD_SOURCE (collector);
67+ }
68+
69+ g_signal_connect (indicator->collector, "changed",
70+ G_CALLBACK (hud_indicator_source_collector_changed), indicator);
71
72 /* Set initial use count on new indicator if query is active. */
73 if (indicator->source->use_count)
74
75=== modified file 'src/hudmenumodelcollector.c'
76--- src/hudmenumodelcollector.c 2012-03-30 13:32:40 +0000
77+++ src/hudmenumodelcollector.c 2012-10-01 14:06:35 +0000
78@@ -47,6 +47,15 @@
79 * This is an opaque structure type.
80 **/
81
82+typedef struct _HudMenuModelContext HudMenuModelContext;
83+
84+struct _HudMenuModelContext
85+{
86+ HudStringList *tokens;
87+ gchar *action_namespace;
88+ gint ref_count;
89+};
90+
91 struct _HudMenuModelCollector
92 {
93 GObject parent_instance;
94@@ -59,10 +68,14 @@
95 GDBusActionGroup *application;
96 GDBusActionGroup *window;
97
98+ gboolean is_application;
99+
100+ gchar *prefix;
101 gchar *desktop_file;
102 gchar *icon;
103 GPtrArray *items;
104 gint use_count;
105+ guint penalty;
106 };
107
108 typedef struct
109@@ -76,6 +89,71 @@
110
111 typedef HudItemClass HudModelItemClass;
112
113+static gchar *
114+hud_menu_model_context_get_action_name (HudMenuModelContext *context,
115+ const gchar *action_name)
116+{
117+ if (context && context->action_namespace)
118+ /* Note: this will (intentionally) work if action_name is NULL */
119+ return g_strjoin (".", context->action_namespace, action_name, NULL);
120+ else
121+ return g_strdup (action_name);
122+}
123+
124+static HudStringList *
125+hud_menu_model_context_get_label (HudMenuModelContext *context,
126+ const gchar *label)
127+{
128+ HudStringList *parent_tokens = context ? context->tokens : NULL;
129+
130+ if (label)
131+ return hud_string_list_cons_label (label, parent_tokens);
132+ else
133+ return hud_string_list_ref (parent_tokens);
134+}
135+
136+static HudMenuModelContext *
137+hud_menu_model_context_ref (HudMenuModelContext *context)
138+{
139+ if (context)
140+ g_atomic_int_inc (&context->ref_count);
141+
142+ return context;
143+}
144+
145+static void
146+hud_menu_model_context_unref (HudMenuModelContext *context)
147+{
148+ if (context && g_atomic_int_dec_and_test (&context->ref_count))
149+ {
150+ hud_string_list_unref (context->tokens);
151+ g_free (context->action_namespace);
152+ g_slice_free (HudMenuModelContext, context);
153+ }
154+}
155+
156+static HudMenuModelContext *
157+hud_menu_model_context_new (HudMenuModelContext *parent,
158+ const gchar *namespace,
159+ const gchar *label)
160+{
161+ HudMenuModelContext *context;
162+
163+ /* If we would be an unmodified copy of the parent, just take a ref */
164+ if (!namespace && !label)
165+ return hud_menu_model_context_ref (parent);
166+
167+ context = g_slice_new (HudMenuModelContext);
168+ context->action_namespace = hud_menu_model_context_get_action_name (parent, namespace);
169+ context->tokens = hud_menu_model_context_get_label (parent, label);
170+ context->ref_count = 1;
171+
172+ return context;
173+}
174+
175+
176+
177+
178 G_DEFINE_TYPE (HudModelItem, hud_model_item, HUD_TYPE_ITEM)
179
180 static void
181@@ -118,20 +196,58 @@
182 }
183
184 static HudItem *
185-hud_model_item_new (HudStringList *tokens,
186- const gchar *desktop_file,
187- const gchar *icon,
188- GRemoteActionGroup *action_group,
189- const gchar *action_name,
190- GVariant *target)
191+hud_model_item_new (HudMenuModelCollector *collector,
192+ HudMenuModelContext *context,
193+ const gchar *label,
194+ const gchar *action_name,
195+ GVariant *target)
196 {
197 HudModelItem *item;
198-
199- item = hud_item_construct (hud_model_item_get_type (), tokens, desktop_file, icon, TRUE);
200- item->group = g_object_ref (action_group);
201- item->action_name = g_strdup (action_name);
202+ const gchar *stripped_action_name;
203+ gchar *full_action_name;
204+ GDBusActionGroup *group = NULL;
205+ HudStringList *full_label;
206+
207+ full_action_name = hud_menu_model_context_get_action_name (context, action_name);
208+
209+ if (collector->is_application)
210+ {
211+ /* For applications we support "app." and "win." actions and
212+ * deliver them to the application or the window, with the prefix
213+ * removed.
214+ */
215+ if (g_str_has_prefix (full_action_name, "app."))
216+ group = collector->application;
217+ else if (g_str_has_prefix (full_action_name, "win."))
218+ group = collector->window;
219+
220+ stripped_action_name = full_action_name + 4;
221+ }
222+ else
223+ {
224+ /* For indicators, we deliver directly to the (one) action group
225+ * that we were given the object path for at construction.
226+ */
227+ stripped_action_name = full_action_name;
228+ group = collector->application;
229+ }
230+
231+ if (!group)
232+ {
233+ g_free (full_action_name);
234+ return NULL;
235+ }
236+
237+ full_label = hud_menu_model_context_get_label (context, label);
238+
239+ item = hud_item_construct (hud_model_item_get_type (), full_label, collector->desktop_file, collector->icon, TRUE);
240+ item->group = g_object_ref (group);
241+ item->action_name = g_strdup (stripped_action_name);
242 item->target = target ? g_variant_ref_sink (target) : NULL;
243
244+ hud_string_list_unref (full_label);
245+ g_free (full_action_name);
246+
247 return HUD_ITEM (item);
248 }
249
250@@ -151,7 +267,10 @@
251 * though.
252 */
253 static void hud_menu_model_collector_add_model (HudMenuModelCollector *collector,
254- GMenuModel *model);
255+ GMenuModel *model,
256+ HudMenuModelContext *parent_context,
257+ const gchar *action_namespace,
258+ const gchar *label);
259 static void hud_menu_model_collector_disconnect (gpointer data,
260 gpointer user_data);
261
262@@ -166,9 +285,9 @@
263 collector->models = NULL;
264
265 if (collector->app_menu)
266- hud_menu_model_collector_add_model (collector, G_MENU_MODEL (collector->app_menu));
267+ hud_menu_model_collector_add_model (collector, G_MENU_MODEL (collector->app_menu), NULL, NULL, collector->prefix);
268 if (collector->menubar)
269- hud_menu_model_collector_add_model (collector, G_MENU_MODEL (collector->menubar));
270+ hud_menu_model_collector_add_model (collector, G_MENU_MODEL (collector->menubar), NULL, NULL, collector->prefix);
271
272 g_slist_foreach (free_list, hud_menu_model_collector_disconnect, collector);
273 g_slist_free_full (free_list, g_object_unref);
274@@ -176,6 +295,17 @@
275 return G_SOURCE_REMOVE;
276 }
277
278+static GQuark
279+hud_menu_model_collector_context_quark ()
280+{
281+ static GQuark context_quark;
282+
283+ if (!context_quark)
284+ context_quark = g_quark_from_string ("menu item context");
285+
286+ return context_quark;
287+}
288+
289 static void
290 hud_menu_model_collector_model_changed (GMenuModel *model,
291 gint position,
292@@ -184,8 +314,7 @@
293 gpointer user_data)
294 {
295 HudMenuModelCollector *collector = user_data;
296- static GQuark context_quark;
297- HudStringList *context;
298+ HudMenuModelContext *context;
299 gboolean changed;
300 gint i;
301
302@@ -205,77 +334,39 @@
303 return;
304 }
305
306- if (!context_quark)
307- context_quark = g_quark_from_string ("menu item context");
308-
309- /* The 'context' is the list of strings that got us up to where we are
310- * now, like "View > Toolbars". We hang this on the GMenuModel with
311- * qdata.
312- *
313- * Strictly speaking, GMenuModel structures are DAGs, but we more or
314- * less assume that they are trees here and replace the data
315- * unconditionally when we visit it the second time (which will be
316- * more or less never, because really, a menu is a tree).
317- */
318- context = g_object_get_qdata (G_OBJECT (model), context_quark);
319+ context = g_object_get_qdata (G_OBJECT (model), hud_menu_model_collector_context_quark ());
320
321 changed = FALSE;
322 for (i = position; i < position + added; i++)
323 {
324- HudStringList *tokens;
325 GMenuModel *link;
326- gchar *value;
327+ gchar *label = NULL;
328+ gchar *action_namespace = NULL;
329+ gchar *action = NULL;
330
331- /* If this item has a label then we add it onto the context to get
332- * our 'tokens'. For example, if context is 'File' then we will
333- * have 'File > New'.
334- *
335- * If there is no label (which only really makes sense for
336- * sections) then we just reuse the existing context by taking a
337- * ref to it.
338- *
339- * Either way, we need to free it at the end of the loop.
340- */
341- if (g_menu_model_get_item_attribute (model, i, G_MENU_ATTRIBUTE_LABEL, "s", &value))
342- {
343- tokens = hud_string_list_cons_label (value, context);
344- g_free (value);
345- }
346- else
347- tokens = hud_string_list_ref (context);
348+ g_menu_model_get_item_attribute (model, i, "action-namespace", "s", &action_namespace);
349+ g_menu_model_get_item_attribute (model, i, G_MENU_ATTRIBUTE_ACTION, "s", &action);
350+ g_menu_model_get_item_attribute (model, i, G_MENU_ATTRIBUTE_LABEL, "s", &label);
351
352 /* Check if this is an action. Here's where we may end up
353 * creating a HudItem.
354 */
355- if (g_menu_model_get_item_attribute (model, i, G_MENU_ATTRIBUTE_ACTION, "s", &value))
356+ if (action && label)
357 {
358- GDBusActionGroup *action_group = NULL;
359-
360- /* It's an action, so add it. */
361- if (g_str_has_prefix (value, "app."))
362- action_group = collector->application;
363- else if (g_str_has_prefix (value, "win."))
364- action_group = collector->window;
365-
366- if (action_group)
367- {
368- GVariant *target;
369- HudItem *item;
370-
371- target = g_menu_model_get_item_attribute_value (model, i, G_MENU_ATTRIBUTE_TARGET, NULL);
372-
373- item = hud_model_item_new (tokens, collector->desktop_file, collector->icon,
374- G_REMOTE_ACTION_GROUP (action_group),
375- value + 4, target);
376- g_ptr_array_add (collector->items, item);
377-
378- if (target)
379- g_variant_unref (target);
380-
381- changed = TRUE;
382- }
383-
384- g_free (value);
385+ GVariant *target;
386+ HudItem *item;
387+
388+ target = g_menu_model_get_item_attribute_value (model, i, G_MENU_ATTRIBUTE_TARGET, NULL);
389+
390+ item = hud_model_item_new (collector, context, label, action, target);
391+
392+ if (item)
393+ g_ptr_array_add (collector->items, item);
394+
395+ if (target)
396+ g_variant_unref (target);
397+
398+ changed = TRUE;
399 }
400
401 /* For 'section' and 'submenu' links, we should recurse. This is
402@@ -284,24 +375,19 @@
403 */
404 if ((link = g_menu_model_get_item_link (model, i, G_MENU_LINK_SECTION)))
405 {
406- g_object_set_qdata_full (G_OBJECT (link), context_quark,
407- hud_string_list_ref (tokens),
408- (GDestroyNotify) hud_string_list_unref);
409- hud_menu_model_collector_add_model (collector, link);
410+ hud_menu_model_collector_add_model (collector, link, context, action_namespace, label);
411 g_object_unref (link);
412 }
413
414 if ((link = g_menu_model_get_item_link (model, i, G_MENU_LINK_SUBMENU)))
415 {
416- /* for submenus, we add the submenu label to the context */
417- g_object_set_qdata_full (G_OBJECT (link), context_quark,
418- hud_string_list_ref (tokens),
419- (GDestroyNotify) hud_string_list_unref);
420- hud_menu_model_collector_add_model (collector, link);
421+ hud_menu_model_collector_add_model (collector, link, context, action_namespace, label);
422 g_object_unref (link);
423 }
424
425- hud_string_list_unref (tokens);
426+ g_free (action_namespace);
427+ g_free (action);
428+ g_free (label);
429 }
430
431 if (changed)
432@@ -309,17 +395,33 @@
433 }
434
435 static void
436-hud_menu_model_collector_add_model (HudMenuModelCollector *search_data,
437- GMenuModel *model)
438+hud_menu_model_collector_add_model (HudMenuModelCollector *collector,
439+ GMenuModel *model,
440+ HudMenuModelContext *parent_context,
441+ const gchar *action_namespace,
442+ const gchar *label)
443 {
444 gint n_items;
445
446- g_signal_connect (model, "items-changed", G_CALLBACK (hud_menu_model_collector_model_changed), search_data);
447- search_data->models = g_slist_prepend (search_data->models, g_object_ref (model));
448+ g_signal_connect (model, "items-changed", G_CALLBACK (hud_menu_model_collector_model_changed), collector);
449+ collector->models = g_slist_prepend (collector->models, g_object_ref (model));
450+
451+ /* The tokens in 'context' are the list of strings that got us up to
452+ * where we are now, like "View > Toolbars".
453+ *
454+ * Strictly speaking, GMenuModel structures are DAGs, but we more or
455+ * less assume that they are trees here and replace the data
456+ * unconditionally when we visit it the second time (which will be
457+ * more or less never, because really, a menu is a tree).
458+ */
459+ g_object_set_qdata_full (G_OBJECT (model),
460+ hud_menu_model_collector_context_quark (),
461+ hud_menu_model_context_new (parent_context, action_namespace, label),
462+ (GDestroyNotify) hud_menu_model_context_unref);
463
464 n_items = g_menu_model_get_n_items (model);
465 if (n_items > 0)
466- hud_menu_model_collector_model_changed (model, 0, 0, n_items, search_data);
467+ hud_menu_model_collector_model_changed (model, 0, 0, n_items, collector);
468 }
469
470 static void
471@@ -362,7 +464,7 @@
472 HudItem *item;
473
474 item = g_ptr_array_index (items, i);
475- result = hud_result_get_if_matched (item, search_string, 0);
476+ result = hud_result_get_if_matched (item, search_string, collector->penalty);
477 if (result)
478 g_ptr_array_add (results_array, result);
479 }
480@@ -381,6 +483,7 @@
481 g_clear_object (&collector->application);
482 g_clear_object (&collector->window);
483
484+ g_free (collector->prefix);
485 g_free (collector->desktop_file);
486 g_free (collector->icon);
487
488@@ -454,13 +557,13 @@
489 if (app_menu_object_path)
490 {
491 collector->app_menu = g_dbus_menu_model_get (session, unique_bus_name, app_menu_object_path);
492- hud_menu_model_collector_add_model (collector, G_MENU_MODEL (collector->app_menu));
493+ hud_menu_model_collector_add_model (collector, G_MENU_MODEL (collector->app_menu), NULL, NULL, NULL);
494 }
495
496 if (menubar_object_path)
497 {
498 collector->menubar = g_dbus_menu_model_get (session, unique_bus_name, menubar_object_path);
499- hud_menu_model_collector_add_model (collector, G_MENU_MODEL (collector->menubar));
500+ hud_menu_model_collector_add_model (collector, G_MENU_MODEL (collector->menubar), NULL, NULL, NULL);
501 }
502
503 if (application_object_path)
504@@ -469,6 +572,7 @@
505 if (window_object_path)
506 collector->window = g_dbus_action_group_get (session, unique_bus_name, window_object_path);
507
508+ collector->is_application = TRUE;
509 collector->desktop_file = g_strdup (desktop_file);
510 collector->icon = g_strdup (icon);
511
512@@ -486,3 +590,57 @@
513
514 return collector;
515 }
516+
517+/**
518+ * hud_menu_model_collector_new_for_endpoint:
519+ * @application_id: a unique identifier for the application
520+ * @prefix: the title to prefix to all items
521+ * @icon: the icon for the appliction
522+ * @penalty: the penalty to apply to all results
523+ * @bus_name: a D-Bus bus name
524+ * @object_path: an object path at the destination given by @bus_name
525+ *
526+ * Creates a new #HudMenuModelCollector for the specified endpoint.
527+ *
528+ * This call is intended to be used for indicators.
529+ *
530+ * If @prefix is non-%NULL (which, for indicators, it ought to be), then
531+ * it is prefixed to every item created by the collector.
532+ *
533+ * If @penalty is non-zero then all results returned from the collector
534+ * have their distance increased by a percentage equal to the penalty.
535+ * This allows items from indicators to score lower than they would
536+ * otherwise.
537+ *
538+ * Returns: a new #HudMenuModelCollector
539+ */
540+HudMenuModelCollector *
541+hud_menu_model_collector_new_for_endpoint (const gchar *application_id,
542+ const gchar *prefix,
543+ const gchar *icon,
544+ guint penalty,
545+ const gchar *bus_name,
546+ const gchar *object_path)
547+{
548+ HudMenuModelCollector *collector;
549+ GDBusConnection *session;
550+
551+ collector = g_object_new (HUD_TYPE_MENU_MODEL_COLLECTOR, NULL);
552+
553+ session = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
554+
555+ collector->app_menu = g_dbus_menu_model_get (session, bus_name, object_path);
556+ collector->application = g_dbus_action_group_get (session, bus_name, object_path);
557+
558+ collector->is_application = FALSE;
559+ collector->prefix = g_strdup (prefix);
560+ collector->desktop_file = g_strdup (application_id);
561+ collector->icon = g_strdup (icon);
562+ collector->penalty = penalty;
563+
564+ hud_menu_model_collector_add_model (collector, G_MENU_MODEL (collector->app_menu), NULL, NULL, prefix);
565+
566+ g_object_unref (session);
567+
568+ return collector;
569+}
570
571=== modified file 'src/hudmenumodelcollector.h'
572--- src/hudmenumodelcollector.h 2012-03-23 15:35:01 +0000
573+++ src/hudmenumodelcollector.h 2012-10-01 14:06:35 +0000
574@@ -36,4 +36,11 @@
575 const gchar *desktop_file,
576 const gchar *icon);
577
578+HudMenuModelCollector * hud_menu_model_collector_new_for_endpoint (const gchar *application_id,
579+ const gchar *prefix,
580+ const gchar *icon,
581+ guint penalty,
582+ const gchar *bus_name,
583+ const gchar *object_path);
584+
585 #endif /* __HUD_MENU_MODEL_COLLECTOR_H__ */

Subscribers

People subscribed via source and target branches