~jbicha/hud/build-depend-on-valac-not-gir

« back to all changes in this revision

Viewing changes to src/hudmenumodelcollector.c

  • Committer: Tarmac
  • Author(s): Ted Gould, Pete Woods, Antti Kaijanmäki, Ted Gould, Albert Astals, Ryan Lortie, Łukasz 'sil2100' Zemczak, Albert Astals Cid, Mathieu Trudel-Lapierre, Kaleo, Tarmac, Ricardo Salveti de Araujo, Michael Terry, Automatic PS uploader
  • Date: 2013-04-10 16:04:51 UTC
  • mfrom: (227.3.148 phablet)
  • Revision ID: tarmac-20130410160451-o3owpv3zaxulm5of
HUD 2.0 Merge.

Approved by PS Jenkins bot, Mathieu Trudel-Lapierre.

Show diffs side-by-side

added added

removed removed

Lines of Context:
16
16
 * Author: Ryan Lortie <desrt@desrt.ca>
17
17
 */
18
18
 
 
19
#define G_LOG_DOMAIN "hudmenumodelcollector"
 
20
 
19
21
#include "hudmenumodelcollector.h"
20
22
 
21
23
#include "hudsource.h"
22
24
#include "hudresult.h"
23
25
#include "huditem.h"
 
26
#include "hudkeywordmapping.h"
 
27
#include "action-muxer.h"
24
28
 
25
 
#include <libbamf/libbamf.h>
26
29
#include <gio/gio.h>
 
30
#include <string.h>
 
31
 
 
32
#define DEFAULT_MENU_DEPTH  10
 
33
#define RECURSE_DATA        "hud-menu-model-recurse-level"
 
34
#define EXPORT_PATH         "hud-menu-model-export-path"
 
35
#define EXPORT_MENU         "hud-menu-model-export-menu"
27
36
 
28
37
/**
29
38
 * SECTION:hudmenumodelcollector
67
76
  GDBusConnection *session;
68
77
  gchar *unique_bus_name;
69
78
 
70
 
  /* If this is an application, is_application will be set and
71
 
   * 'application' and 'window' will contain the two action groups for
72
 
   * the window that we are collecting.
73
 
   *
74
 
   * If this is an indicator, is_application will be false and the
75
 
   * (singular) action group for the indicator will be in 'application'.
76
 
   */
77
 
  GDBusActionGroup *application;
78
 
  GDBusActionGroup *window;
79
 
  gboolean is_application;
80
 
 
81
 
  /* The GMenuModel for the app menu.
82
 
   *
83
 
   * If this is an indicator, the indicator menu is stored here.
84
 
   *
85
 
   * app_menu_is_hud_aware is TRUE if we should send HudActiveChanged
86
 
   * calls to app_menu_object_path when our use_count goes above 0.
87
 
   */
88
 
  GDBusMenuModel *app_menu;
89
 
  gchar *app_menu_object_path;
90
 
  gboolean app_menu_is_hud_aware;
91
 
 
92
 
  /* Ditto for the menubar.
93
 
   *
94
 
   * If this is an indicator then these will all be unset.
95
 
   */
96
 
  GDBusMenuModel *menubar;
97
 
  gchar *menubar_object_path;
98
 
  gboolean menubar_is_hud_aware;
 
79
  /* GActionGroup's indexed by their prefix */
 
80
  GActionMuxer * muxer;
99
81
 
100
82
  /* Boring details about the app/indicator we are showing. */
101
 
  gchar *prefix;
102
 
  gchar *desktop_file;
 
83
  gchar *app_id;
103
84
  gchar *icon;
104
85
  guint penalty;
105
86
 
 
87
  /* Class to map labels to keywords */
 
88
  HudKeywordMapping* keyword_mapping;
 
89
 
106
90
  /* Each time we see a new menumodel added we add it to 'models', start
107
91
   * watching it for changes and add its contents to 'items', possibly
108
92
   * finding more menumodels to do the same to.
121
105
   * apps and indicators.
122
106
   */
123
107
  gint use_count;
124
 
};
125
 
 
126
 
typedef struct
127
 
{
 
108
 
 
109
  /* This is the export path that we've been given */
 
110
  gchar * base_export_path;
 
111
  guint muxer_export;
 
112
 
 
113
  HudSourceItemType type;
 
114
};
 
115
 
 
116
/* Structure for when we're tracking a model, all the info
 
117
   that we need to keep on it. */
 
118
typedef struct _model_data_t model_data_t;
 
119
struct _model_data_t {
 
120
        GDBusConnection *session;
 
121
 
 
122
        GMenuModel * model;
 
123
        gboolean is_hud_aware;
 
124
        GCancellable * cancellable;
 
125
        gchar * path;
 
126
        gchar * label;
 
127
        guint recurse;
 
128
};
 
129
 
 
130
/* Structure to pass two values in a single pointer, amazing! */
 
131
typedef struct _exported_menu_id_t exported_menu_id_t;
 
132
struct _exported_menu_id_t {
 
133
        guint id;
 
134
        GDBusConnection * bus;
 
135
};
 
136
 
 
137
struct _HudModelItem {
128
138
  HudItem parent_instance;
129
139
 
130
140
  GRemoteActionGroup *group;
131
141
  gchar *action_name;
 
142
  gchar *action_name_full;
 
143
  gchar *action_path;
132
144
  GVariant *target;
133
 
} HudModelItem;
 
145
  HudClientQueryToolbarItems toolbar_item;
 
146
 
 
147
  GMenuModel * submodel;
 
148
};
134
149
 
135
150
typedef HudItemClass HudModelItemClass;
136
151
 
 
152
/* Prototypes */
 
153
static void model_data_free                           (gpointer      data);
 
154
static void hud_menu_model_collector_hud_awareness_cb (GObject      *source,
 
155
                                                       GAsyncResult *result,
 
156
                                                       gpointer      user_data);
 
157
static GList * hud_menu_model_collector_get_items (HudSource * source);
 
158
static void hud_menu_model_collector_activate_toolbar (HudSource *   source,
 
159
                                                       HudClientQueryToolbarItems titem,
 
160
                                                       GVariant  *   platform_data);
 
161
static const gchar * hud_menu_model_collector_get_app_icon (HudSource * collector);
 
162
static const gchar * hud_menu_model_collector_get_app_id (HudSource * collector);
 
163
 
 
164
/* Functions */
137
165
static gchar *
138
 
hud_menu_model_context_get_action_name (HudMenuModelContext *context,
139
 
                                        const gchar         *action_name)
 
166
hud_menu_model_context_get_prefix (HudMenuModelContext *context,
 
167
                                   const gchar         *action_name)
140
168
{
141
169
  if (context && context->action_namespace)
142
170
    /* Note: this will (intentionally) work if action_name is NULL */
143
 
    return g_strjoin (".", context->action_namespace, action_name, NULL);
144
 
  else
145
 
    return g_strdup (action_name);
 
171
    return g_strdup (context->action_namespace);
 
172
  else {
 
173
    gchar * retval = g_strdup (action_name);
 
174
    gchar * dot = g_strstr_len(retval, -1, ".");
 
175
    if (dot != NULL) {
 
176
      dot[0] = '\0';
 
177
    } else {
 
178
      retval[0] = '\0';
 
179
    }
 
180
    return retval;
 
181
  }
146
182
}
147
183
 
148
184
static HudStringList *
157
193
    return hud_string_list_ref (parent_tokens);
158
194
}
159
195
 
 
196
static HudStringList *
 
197
hud_menu_model_context_get_tokens (const gchar         *label,
 
198
                                  HudKeywordMapping   *keyword_mapping)
 
199
{
 
200
  HudStringList *tokens = NULL;
 
201
 
 
202
  if (label)
 
203
  {
 
204
    GPtrArray *keywords = hud_keyword_mapping_transform (keyword_mapping,
 
205
        label);
 
206
    gint i;
 
207
    for (i = 0; i < keywords->len; i++)
 
208
    {
 
209
      tokens = hud_string_list_cons_label (
 
210
          (gchar*) g_ptr_array_index(keywords, i), tokens);
 
211
    }
 
212
    return tokens;
 
213
  }
 
214
  else
 
215
    return NULL;
 
216
}
 
217
 
160
218
static HudMenuModelContext *
161
219
hud_menu_model_context_ref (HudMenuModelContext *context)
162
220
{
180
238
static HudMenuModelContext *
181
239
hud_menu_model_context_new (HudMenuModelContext *parent,
182
240
                            const gchar         *namespace,
183
 
                            const gchar         *label)
 
241
                            const gchar         *label,
 
242
                            HudKeywordMapping   *keyword_mapping)
184
243
{
185
244
  HudMenuModelContext *context;
186
245
 
189
248
    return hud_menu_model_context_ref (parent);
190
249
 
191
250
  context = g_slice_new (HudMenuModelContext);
192
 
  context->action_namespace = hud_menu_model_context_get_action_name (parent, namespace);
 
251
  if (parent != NULL && parent->action_namespace != NULL) {
 
252
          context->action_namespace = g_strjoin(".", parent->action_namespace, namespace, NULL);
 
253
  } else {
 
254
    context->action_namespace = g_strdup(namespace);
 
255
  }
193
256
  context->tokens = hud_menu_model_context_get_label (parent, label);
194
257
  context->ref_count = 1;
195
258
 
208
271
}
209
272
 
210
273
static void
 
274
hud_model_item_activate_toolbar (HudModelItem  *hud_item,
 
275
                                 HudClientQueryToolbarItems item,
 
276
                                 GVariant *platform_data)
 
277
{
 
278
        if (hud_item->toolbar_item == item) {
 
279
                hud_item_activate(HUD_ITEM(hud_item), platform_data);
 
280
        }
 
281
 
 
282
        return;
 
283
}
 
284
 
 
285
static void
211
286
hud_model_item_finalize (GObject *object)
212
287
{
213
288
  HudModelItem *item = (HudModelItem *) object;
214
289
 
 
290
  g_clear_object(&item->submodel);
215
291
  g_object_unref (item->group);
216
292
  g_free (item->action_name);
 
293
  g_free (item->action_name_full);
 
294
  g_free (item->action_path);
217
295
 
218
296
  if (item->target)
219
297
    g_variant_unref (item->target);
225
303
static void
226
304
hud_model_item_init (HudModelItem *item)
227
305
{
 
306
  item->toolbar_item = -1;
228
307
}
229
308
 
230
309
static void
237
316
  class->activate = hud_model_item_activate;
238
317
}
239
318
 
 
319
/* Breaks apart the string and adds it to the list */
 
320
static HudStringList *
 
321
append_keywords_string (HudStringList * list, const gchar * string)
 
322
{
 
323
        if (string == NULL) {
 
324
                return list;
 
325
        }
 
326
 
 
327
        /* Limiting to 64 keywords.  A bit arbitrary, but we need to ensure
 
328
           that bad apps can't hurt us. */
 
329
        gchar ** split = g_strsplit(string, ";", 64);
 
330
 
 
331
        if (split == NULL) {
 
332
                return list;
 
333
        }
 
334
 
 
335
        int i;
 
336
        for (i = 0; split[i] != NULL; i++) {
 
337
                if (split[i][0] != '\0')
 
338
                        list = hud_string_list_cons(split[i], list);
 
339
        }
 
340
 
 
341
        g_strfreev(split);
 
342
        return list;
 
343
}
 
344
 
240
345
static HudItem *
241
346
hud_model_item_new (HudMenuModelCollector *collector,
242
347
                    HudMenuModelContext   *context,
243
348
                    const gchar           *label,
244
349
                    const gchar           *action_name,
245
 
                    GVariant              *target)
 
350
                    const gchar           *accel,
 
351
                    const gchar           *description,
 
352
                    const gchar           *keywords_string,
 
353
                    GVariant              *target,
 
354
                    const gchar           *toolbar)
246
355
{
247
356
  HudModelItem *item;
248
357
  const gchar *stripped_action_name;
249
 
  gchar *full_action_name;
250
 
  GDBusActionGroup *group = NULL;
251
 
  HudStringList *full_label;
252
 
 
253
 
  full_action_name = hud_menu_model_context_get_action_name (context, action_name);
254
 
 
255
 
  if (collector->is_application)
256
 
    {
257
 
      /* For applications we support "app." and "win." actions and
258
 
       * deliver them to the application or the window, with the prefix
259
 
       * removed.
260
 
       */
261
 
      if (g_str_has_prefix (full_action_name, "app."))
262
 
        group = collector->application;
263
 
      else if (g_str_has_prefix (full_action_name, "win."))
264
 
        group = collector->window;
265
 
 
266
 
      stripped_action_name = full_action_name + 4;
267
 
    }
268
 
  else
269
 
    {
270
 
      /* For indicators, we deliver directly to the (one) action group
271
 
       * that we were given the object path for at construction.
272
 
       */
273
 
      stripped_action_name = full_action_name;
274
 
      group = collector->application;
275
 
    }
 
358
  gchar *prefix;
 
359
  GActionGroup *group = NULL;
 
360
  HudStringList *full_label, *keywords;
 
361
 
 
362
  prefix = hud_menu_model_context_get_prefix (context, action_name);
 
363
 
 
364
  if (prefix[0] == '\0')
 
365
    g_clear_pointer(&prefix, g_free);
 
366
 
 
367
  group = g_action_muxer_get(collector->muxer, prefix);
276
368
 
277
369
  if (!group)
278
370
    {
279
 
      g_free (full_action_name);
 
371
      g_free (prefix);
280
372
      return NULL;
281
373
    }
282
374
 
 
375
  /* Kinda silly, think we can do better here */
 
376
  if (prefix != NULL && g_str_has_prefix(action_name, prefix)) {
 
377
    stripped_action_name = action_name + strlen(prefix) + 1;
 
378
  } else {
 
379
    stripped_action_name = action_name;
 
380
  }
 
381
 
283
382
  full_label = hud_menu_model_context_get_label (context, label);
 
383
  keywords = hud_menu_model_context_get_tokens(label, collector->keyword_mapping);
 
384
  keywords = append_keywords_string(keywords, keywords_string);
284
385
 
285
 
  item = hud_item_construct (hud_model_item_get_type (), full_label, collector->desktop_file, collector->icon, TRUE);
 
386
  item = hud_item_construct (hud_model_item_get_type (), full_label, keywords, accel, collector->app_id, collector->icon, description, TRUE);
286
387
  item->group = g_object_ref (group);
287
388
  item->action_name = g_strdup (stripped_action_name);
 
389
  item->action_name_full = g_strdup (action_name);
288
390
  item->target = target ? g_variant_ref_sink (target) : NULL;
289
391
 
 
392
  if (toolbar != NULL)
 
393
    {
 
394
      item->toolbar_item = hud_client_query_toolbar_items_get_value_from_nick(toolbar);
 
395
    }
 
396
 
290
397
  hud_string_list_unref (full_label);
291
 
  g_free (full_action_name);
 
398
  hud_string_list_unref (keywords);
 
399
  g_free (prefix);
292
400
 
293
401
  return HUD_ITEM (item);
294
402
}
295
403
 
 
404
/* Set the submenu property for the item */
 
405
static void
 
406
hud_model_item_set_submenu (HudModelItem * item, GMenuModel * submenu, const gchar * action_path)
 
407
{
 
408
        g_return_if_fail(G_IS_MENU_MODEL(submenu));
 
409
        g_return_if_fail(g_object_get_data(G_OBJECT(submenu), EXPORT_PATH) != NULL);
 
410
 
 
411
        item->submodel = g_object_ref(submenu);
 
412
        item->action_path = g_strdup(action_path);
 
413
 
 
414
        return;
 
415
}
 
416
 
 
417
/**
 
418
 * hud_model_item_is_toolbar:
 
419
 * @item: A #HudModelItem to check
 
420
 *
 
421
 * Check to see if an item has a toolbar representation
 
422
 *
 
423
 * Return value: Whether this is in the toolbar
 
424
 */
 
425
gboolean
 
426
hud_model_item_is_toolbar (HudModelItem * item)
 
427
{
 
428
        g_return_val_if_fail(HUD_IS_MODEL_ITEM(item), FALSE);
 
429
 
 
430
        return item->toolbar_item != -1;
 
431
}
 
432
 
 
433
/**
 
434
 * hud_model_item_is_parameterized:
 
435
 * @item: A #HudModelItem to check
 
436
 *
 
437
 * Check to see if an item represents a parameterized set of
 
438
 * actions.
 
439
 *
 
440
 * Return value: Whether this has parameterized actions or not
 
441
 */
 
442
gboolean
 
443
hud_model_item_is_parameterized (HudModelItem * item)
 
444
{
 
445
        g_return_val_if_fail(HUD_IS_MODEL_ITEM(item), FALSE);
 
446
 
 
447
        return item->submodel != NULL;
 
448
}
 
449
 
 
450
/**
 
451
 * hud_model_item_activate_parameterized:
 
452
 * @item: A #HudModelItem to check
 
453
 * @timestamp: The time of the user event from the dispaly server
 
454
 * @base_action: Action to activate the base events on
 
455
 * @action_path: Path that the actions can be found on
 
456
 * @model_path: Path to the models
 
457
 * @section: Model section to use
 
458
 * 
 
459
 * Uses the item to find all the information about creating a parameterized
 
460
 * view of the item in the HUD.
 
461
 */
 
462
void
 
463
hud_model_item_activate_parameterized (HudModelItem * item, guint32 timestamp, const gchar ** base_action, const gchar ** action_path, const gchar ** model_path, gint * section)
 
464
{
 
465
        g_return_if_fail(HUD_IS_MODEL_ITEM(item));
 
466
 
 
467
        /* Make sure we have a place to put things, we require it */
 
468
        g_return_if_fail(base_action != NULL);
 
469
        g_return_if_fail(action_path != NULL);
 
470
        g_return_if_fail(model_path != NULL);
 
471
        g_return_if_fail(section != NULL);
 
472
        g_return_if_fail(hud_model_item_is_parameterized(item));
 
473
 
 
474
        *base_action = item->action_name_full;
 
475
        *model_path = (const gchar *)g_object_get_data(G_OBJECT(item->submodel), EXPORT_PATH);
 
476
        *action_path = item->action_path;
 
477
        *section = 1;
 
478
 
 
479
        hud_item_mark_usage(HUD_ITEM(item));
 
480
 
 
481
        return;
 
482
}
 
483
 
296
484
typedef GObjectClass HudMenuModelCollectorClass;
297
485
 
298
486
static void hud_menu_model_collector_iface_init (HudSourceInterface *iface);
308
496
 * receiving menus from untrusted sources, we need to take another look,
309
497
 * though.
310
498
 */
311
 
static void hud_menu_model_collector_add_model  (HudMenuModelCollector *collector,
312
 
                                                 GMenuModel            *model,
313
 
                                                 HudMenuModelContext   *parent_context,
314
 
                                                 const gchar           *action_namespace,
315
 
                                                 const gchar           *label);
 
499
static void hud_menu_model_collector_add_model_internal  (HudMenuModelCollector *collector,
 
500
                                                          GMenuModel            *model,
 
501
                                                          const gchar           *path,
 
502
                                                          HudMenuModelContext   *parent_context,
 
503
                                                          const gchar           *action_namespace,
 
504
                                                          const gchar           *label,
 
505
                                                          guint                  recurse,
 
506
                                                          HudSourceItemType      type);
 
507
 
316
508
static void hud_menu_model_collector_disconnect (gpointer               data,
317
509
                                                 gpointer               user_data);
318
510
 
 
511
/* Takes a model data and adds it as a model */
 
512
static void
 
513
readd_models (gpointer data, gpointer user_data)
 
514
{
 
515
        HudMenuModelCollector *collector = user_data;
 
516
        model_data_t * model_data = data;
 
517
 
 
518
    hud_menu_model_collector_add_model_internal (collector, model_data->model, model_data->path, NULL, NULL, model_data->label, model_data->recurse, collector->type);
 
519
        return;
 
520
}
 
521
 
319
522
static gboolean
320
523
hud_menu_model_collector_refresh (gpointer user_data)
321
524
{
326
529
  free_list = collector->models;
327
530
  collector->models = NULL;
328
531
 
329
 
  if (collector->app_menu)
330
 
    hud_menu_model_collector_add_model (collector, G_MENU_MODEL (collector->app_menu), NULL, NULL, collector->prefix);
331
 
  if (collector->menubar)
332
 
    hud_menu_model_collector_add_model (collector, G_MENU_MODEL (collector->menubar), NULL, NULL, collector->prefix);
 
532
  g_slist_foreach(free_list, readd_models, collector);
333
533
 
334
534
  g_slist_foreach (free_list, hud_menu_model_collector_disconnect, collector);
335
 
  g_slist_free_full (free_list, g_object_unref);
 
535
  g_slist_free_full (free_list, model_data_free);
336
536
 
337
537
  return G_SOURCE_REMOVE;
338
538
}
348
548
  return context_quark;
349
549
}
350
550
 
 
551
/* Function to convert from the GMenu format for the accel to something
 
552
   usable by humans. */
 
553
static gchar *
 
554
format_accel_for_users (gchar * accel)
 
555
{
 
556
        if (accel == NULL) {
 
557
                return g_strdup("");
 
558
        }
 
559
 
 
560
        GString * output = g_string_new("");
 
561
        gchar * head = accel;
 
562
 
 
563
        /* YEAH! String parsing, always my favorite. */
 
564
        while (head[0] != '\0') {
 
565
                if (head[0] == '<') {
 
566
                        /* We're in modifier land */
 
567
                        if (strncmp(head, "<Alt>", strlen("<Alt>")) == 0) {
 
568
                                g_string_append(output, "Alt + ");
 
569
                                head += strlen("<Alt>");
 
570
                        } else if (strncmp(head, "<Primary>", strlen("<Primary>")) == 0) {
 
571
                                g_string_append(output, "Ctrl + ");
 
572
                                head += strlen("<Primary>");
 
573
                        } else if (strncmp(head, "<Control>", strlen("<Control>")) == 0) {
 
574
                                g_string_append(output, "Ctrl + ");
 
575
                                head += strlen("<Control>");
 
576
                        } else if (strncmp(head, "<Shift>", strlen("<Shift>")) == 0) {
 
577
                                g_string_append(output, "Shift + ");
 
578
                                head += strlen("<Shift>");
 
579
                        } else if (strncmp(head, "<Super>", strlen("<Super>")) == 0) {
 
580
                                g_string_append(output, "Super + ");
 
581
                                head += strlen("<Super>");
 
582
                        } else {
 
583
                                /* Go to the close of the modifier */
 
584
                                head = g_strstr_len(head, -1, ">") + 1;
 
585
                        }
 
586
                        continue;
 
587
                }
 
588
 
 
589
                g_string_append(output, head);
 
590
                break;
 
591
        }
 
592
 
 
593
        g_free(accel);
 
594
 
 
595
        return g_string_free(output, FALSE);
 
596
}
 
597
 
351
598
static void
352
599
hud_menu_model_collector_model_changed (GMenuModel *model,
353
600
                                        gint        position,
359
606
  HudMenuModelContext *context;
360
607
  gboolean changed;
361
608
  gint i;
 
609
  guint recurse = 0;
362
610
 
363
611
  if (collector->refresh_id)
364
612
    /* We have a refresh scheduled already.  Ignore. */
377
625
    }
378
626
 
379
627
  context = g_object_get_qdata (G_OBJECT (model), hud_menu_model_collector_context_quark ());
 
628
  recurse = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(model), RECURSE_DATA));
380
629
 
381
630
  changed = FALSE;
382
631
  for (i = position; i < position + added; i++)
385
634
      gchar *label = NULL;
386
635
      gchar *action_namespace = NULL;
387
636
      gchar *action = NULL;
 
637
      gchar *accel = NULL;
 
638
      gchar *toolbar = NULL;
 
639
      gchar *description = NULL;
 
640
      gchar *keywords = NULL;
 
641
      HudItem *item = NULL;
 
642
 
388
643
 
389
644
      g_menu_model_get_item_attribute (model, i, "action-namespace", "s", &action_namespace);
390
645
      g_menu_model_get_item_attribute (model, i, G_MENU_ATTRIBUTE_ACTION, "s", &action);
391
646
      g_menu_model_get_item_attribute (model, i, G_MENU_ATTRIBUTE_LABEL, "s", &label);
 
647
      g_menu_model_get_item_attribute (model, i, "accel", "s", &accel);
 
648
      g_menu_model_get_item_attribute (model, i, "hud-toolbar-item", "s", &toolbar);
 
649
      g_menu_model_get_item_attribute (model, i, "description", "s", &description);
 
650
      g_menu_model_get_item_attribute (model, i, "keywords", "s", &keywords);
 
651
 
 
652
      accel = format_accel_for_users(accel);
392
653
 
393
654
      /* Check if this is an action.  Here's where we may end up
394
655
       * creating a HudItem.
396
657
      if (action && label)
397
658
        {
398
659
          GVariant *target;
399
 
          HudItem *item;
400
660
 
401
661
          target = g_menu_model_get_item_attribute_value (model, i, G_MENU_ATTRIBUTE_TARGET, NULL);
402
662
 
403
 
          item = hud_model_item_new (collector, context, label, action, target);
 
663
          item = hud_model_item_new (collector, context, label, action, accel, description, keywords, target, toolbar);
404
664
 
405
665
          if (item)
406
666
            g_ptr_array_add (collector->items, item);
417
677
       */
418
678
      if ((link = g_menu_model_get_item_link (model, i, G_MENU_LINK_SECTION)))
419
679
        {
420
 
          hud_menu_model_collector_add_model (collector, link, context, action_namespace, label);
 
680
          hud_menu_model_collector_add_model_internal (collector, link, NULL, context, action_namespace, label, recurse, collector->type);
421
681
          g_object_unref (link);
422
682
        }
423
683
 
424
684
      if ((link = g_menu_model_get_item_link (model, i, G_MENU_LINK_SUBMENU)))
425
685
        {
426
 
          hud_menu_model_collector_add_model (collector, link, context, action_namespace, label);
 
686
          hud_menu_model_collector_add_model_internal (collector, link, NULL, context, action_namespace, label, recurse - 1, collector->type);
 
687
          if (item != NULL && recurse <= 1)
 
688
            {
 
689
              hud_model_item_set_submenu((HudModelItem *)item, link, collector->base_export_path);
 
690
            }
427
691
          g_object_unref (link);
428
692
        }
429
693
 
430
694
      g_free (action_namespace);
431
695
      g_free (action);
432
696
      g_free (label);
 
697
      g_free (accel);
 
698
      g_free (toolbar);
 
699
      g_free (description);
 
700
      g_free (keywords);
433
701
    }
434
702
 
435
703
  if (changed)
436
704
    hud_source_changed (HUD_SOURCE (collector));
437
705
}
438
706
 
439
 
static void
440
 
hud_menu_model_collector_add_model (HudMenuModelCollector *collector,
441
 
                                    GMenuModel            *model,
442
 
                                    HudMenuModelContext   *parent_context,
443
 
                                    const gchar           *action_namespace,
444
 
                                    const gchar           *label)
445
 
{
 
707
/* Unexport a menu and unref the bus we kept */
 
708
static void
 
709
unexport_menu (gpointer user_data)
 
710
{
 
711
        exported_menu_id_t * idt = (exported_menu_id_t *)user_data;
 
712
 
 
713
        g_dbus_connection_unexport_menu_model(idt->bus, idt->id);
 
714
        g_object_unref(idt->bus);
 
715
 
 
716
        g_free(user_data);
 
717
        return;
 
718
}
 
719
 
 
720
static void
 
721
hud_menu_model_collector_add_model_internal (HudMenuModelCollector *collector,
 
722
                                             GMenuModel            *model,
 
723
                                             const gchar           *path,
 
724
                                             HudMenuModelContext   *parent_context,
 
725
                                             const gchar           *action_namespace,
 
726
                                             const gchar           *label,
 
727
                                             guint                  recurse,
 
728
                                             HudSourceItemType      type)
 
729
{
 
730
  g_return_if_fail(G_IS_MENU_MODEL(model));
 
731
 
 
732
  collector->type = type;
 
733
 
 
734
  /* We don't want to parse this one, just export it so that
 
735
     the UI could use it if needed */
 
736
  if (recurse == 0 && collector->base_export_path != NULL) {
 
737
    /* Create the exported model */
 
738
    GMenu * export = g_menu_new();
 
739
    GMenuItem * item = g_menu_item_new_submenu("Root Export", model);
 
740
    g_menu_append_item(export, item);
 
741
    g_object_unref(item);
 
742
 
 
743
        /* Build a struct for all the info we need */
 
744
        exported_menu_id_t * idt = g_new0(exported_menu_id_t, 1);
 
745
        idt->bus = g_object_ref(collector->session);
 
746
 
 
747
        /* Export */
 
748
    gchar * menu_path = g_strdup_printf("%s/menu%X", collector->base_export_path, GPOINTER_TO_UINT(model));
 
749
    g_debug("Exporting menu model: %s", menu_path);
 
750
    idt->id = g_dbus_connection_export_menu_model(collector->session, menu_path, G_MENU_MODEL(export), NULL);
 
751
 
 
752
        /* Make sure we're ready to clean up */
 
753
    g_object_set_data_full(G_OBJECT(model), EXPORT_PATH, menu_path, g_free);
 
754
    g_object_set_data_full(G_OBJECT(model), EXPORT_MENU, export, g_object_unref);
 
755
 
 
756
    /* All the callers of this function assume that we take the responsibility
 
757
     * to manage the lifecycle of the model. Or in other words, HudMenuModelCollector
 
758
     * takes the responsibility.
 
759
     *
 
760
     * g_dbus_connection_export_menu_model() increases the reference count of the given
 
761
     * model and the model is not disposed before it's unexported.
 
762
     *
 
763
     * By using g_object_set_data_full() we guarantee that the model gets unexported
 
764
     * and cleaned up when the collector disposes it self.
 
765
     *
 
766
     * menu_path is simply used as a unique key to store the idt in collector GObject.
 
767
     * There is no need to retrieve the idt using g_object_get_data().
 
768
     *
 
769
     * The reason that it can't be on the model is because the model is referenced
 
770
     * by the exported item.  So the model will never be free'd until the export is
 
771
     * first.
 
772
     */
 
773
    g_object_set_data_full(G_OBJECT(collector), menu_path, idt, unexport_menu);
 
774
 
 
775
    return;
 
776
  }
 
777
 
446
778
  gint n_items;
447
779
 
 
780
  g_object_set_data (G_OBJECT(model), RECURSE_DATA, GINT_TO_POINTER(recurse));
448
781
  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));
 
782
 
 
783
  /* Setup the base structure */
 
784
  model_data_t * model_data = g_new0(model_data_t, 1);
 
785
  model_data->session = collector->session;
 
786
  model_data->model = g_object_ref(model);
 
787
  model_data->is_hud_aware = FALSE;
 
788
  model_data->cancellable = g_cancellable_new();
 
789
  model_data->path = g_strdup(path);
 
790
  model_data->label = g_strdup(label);
 
791
  model_data->recurse = recurse;
 
792
 
 
793
  /* Add to our list of models */
 
794
  collector->models = g_slist_prepend (collector->models, model_data);
 
795
 
 
796
  if (path != NULL) {
 
797
    g_dbus_connection_call (collector->session, collector->unique_bus_name, path,
 
798
                            "com.canonical.hud.Awareness", "CheckAwareness",
 
799
                            NULL, G_VARIANT_TYPE_UNIT, G_DBUS_CALL_FLAGS_NONE, -1, model_data->cancellable,
 
800
                            hud_menu_model_collector_hud_awareness_cb, &model_data->is_hud_aware);
 
801
  }
450
802
 
451
803
  /* The tokens in 'context' are the list of strings that got us up to
452
804
   * where we are now, like "View > Toolbars".
458
810
   */
459
811
  g_object_set_qdata_full (G_OBJECT (model),
460
812
                           hud_menu_model_collector_context_quark (),
461
 
                           hud_menu_model_context_new (parent_context, action_namespace, label),
 
813
                           hud_menu_model_context_new (parent_context, action_namespace, label, collector->keyword_mapping),
462
814
                           (GDestroyNotify) hud_menu_model_context_unref);
463
815
 
464
816
  n_items = g_menu_model_get_n_items (model);
473
825
  g_signal_handlers_disconnect_by_func (data, hud_menu_model_collector_model_changed, user_data);
474
826
}
475
827
 
 
828
/* Sends an awareness to a model that needs it */
 
829
static void
 
830
set_awareness (HudMenuModelCollector * collector, model_data_t * model_data, gboolean active)
 
831
{
 
832
        if (!model_data->is_hud_aware) {
 
833
                return;
 
834
        }
 
835
 
 
836
        g_dbus_connection_call (collector->session, collector->unique_bus_name, model_data->path,
 
837
                                "com.canonical.hud.Awareness", "HudActiveChanged", g_variant_new ("(b)", active),
 
838
                                NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
 
839
 
 
840
        return;
 
841
}
 
842
 
 
843
/* We're avoiding having to allocate a struct by using the function
 
844
   pointer here.  Kinda sneaky, but it works */
 
845
static void
 
846
make_aware (gpointer data, gpointer user_data)
 
847
{
 
848
        return set_awareness(HUD_MENU_MODEL_COLLECTOR(user_data), (model_data_t *)data, TRUE);
 
849
}
 
850
 
 
851
static void
 
852
make_unaware (gpointer data, gpointer user_data)
 
853
{
 
854
        return set_awareness(HUD_MENU_MODEL_COLLECTOR(user_data), (model_data_t *)data, FALSE);
 
855
}
 
856
 
 
857
/* Handles the activeness of this collector */
476
858
static void
477
859
hud_menu_model_collector_active_changed (HudMenuModelCollector *collector,
478
860
                                         gboolean               active)
479
861
{
480
 
  if (collector->app_menu_is_hud_aware)
481
 
    g_dbus_connection_call (collector->session, collector->unique_bus_name, collector->app_menu_object_path,
482
 
                            "com.canonical.hud.Awareness", "HudActiveChanged", g_variant_new ("(b)", active),
483
 
                            NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
 
862
  if (active) {
 
863
    g_slist_foreach(collector->models, make_aware, collector);
 
864
  } else {
 
865
    g_slist_foreach(collector->models, make_unaware, collector);
 
866
  }
484
867
 
485
 
  if (collector->menubar_is_hud_aware)
486
 
    g_dbus_connection_call (collector->session, collector->unique_bus_name, collector->menubar_object_path,
487
 
                            "com.canonical.hud.Awareness", "HudActiveChanged", g_variant_new ("(b)", active),
488
 
                            NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
 
868
  return;
489
869
}
490
870
 
491
871
static void
512
892
 
513
893
static void
514
894
hud_menu_model_collector_search (HudSource    *source,
515
 
                                 GPtrArray    *results_array,
516
 
                                 HudTokenList *search_string)
 
895
                                 HudTokenList *search_string,
 
896
                                 void        (*append_func) (HudResult * result, gpointer user_data),
 
897
                                 gpointer      user_data)
517
898
{
518
899
  HudMenuModelCollector *collector = HUD_MENU_MODEL_COLLECTOR (source);
519
900
  GPtrArray *items;
527
908
      HudItem *item;
528
909
 
529
910
      item = g_ptr_array_index (items, i);
 
911
 
 
912
      if (search_string == NULL && hud_model_item_is_toolbar(HUD_MODEL_ITEM(item))) {
 
913
        continue;
 
914
      }
 
915
 
530
916
      result = hud_result_get_if_matched (item, search_string, collector->penalty);
531
917
      if (result)
532
 
        g_ptr_array_add (results_array, result);
533
 
    }
534
 
}
 
918
        append_func(result, user_data);
 
919
    }
 
920
}
 
921
 
 
922
static void
 
923
hud_menu_model_collector_list_applications (HudSource    *source,
 
924
                                            HudTokenList *search_string,
 
925
                                            void        (*append_func) (const gchar *application_id, const gchar *application_icon, HudSourceItemType type, gpointer user_data),
 
926
                                            gpointer      user_data)
 
927
{
 
928
  HudMenuModelCollector *collector = HUD_MENU_MODEL_COLLECTOR (source);
 
929
  GPtrArray *items;
 
930
  gint i;
 
931
 
 
932
  items = collector->items;
 
933
 
 
934
  for (i = 0; i < items->len; i++)
 
935
    {
 
936
      HudResult *result;
 
937
      HudItem *item;
 
938
 
 
939
      item = g_ptr_array_index (items, i);
 
940
      result = hud_result_get_if_matched (item, search_string, collector->penalty);
 
941
      if (result) {
 
942
        append_func(collector->app_id, collector->icon, collector->type, user_data);
 
943
        g_object_unref(result);
 
944
        break;
 
945
      }
 
946
    }
 
947
}
 
948
 
 
949
static HudSource *
 
950
hud_menu_model_collector_get (HudSource   *source,
 
951
                              const gchar *application_id)
 
952
{
 
953
  HudMenuModelCollector *collector = HUD_MENU_MODEL_COLLECTOR (source);
 
954
 
 
955
  if (g_strcmp0(collector->app_id, application_id) == 0)
 
956
    return source;
 
957
 
 
958
  return NULL;
 
959
}
 
960
 
 
961
static void
 
962
hud_menu_model_collector_activate_toolbar (HudSource * source,
 
963
                              HudClientQueryToolbarItems titem,
 
964
                              GVariant *platform_data)
 
965
{
 
966
  HudMenuModelCollector *collector = HUD_MENU_MODEL_COLLECTOR (source);
 
967
 
 
968
  gint i;
 
969
  for (i = 0; i < collector->items->len; i++) {
 
970
    HudModelItem * item = g_ptr_array_index(collector->items, i);
 
971
    hud_model_item_activate_toolbar(item, titem, platform_data);
 
972
  }
 
973
 
 
974
  return;
 
975
}
 
976
 
 
977
/* Free's the model data structure */
 
978
static void
 
979
model_data_free (gpointer data)
 
980
{
 
981
        model_data_t * model_data = (model_data_t *)data;
 
982
 
 
983
        /* Make sure we don't have an operation outstanding */
 
984
        g_cancellable_cancel (model_data->cancellable);
 
985
        g_clear_object(&model_data->cancellable);
 
986
 
 
987
        g_clear_pointer(&model_data->path, g_free);
 
988
        g_clear_pointer(&model_data->label, g_free);
 
989
 
 
990
        g_clear_object(&model_data->model);
 
991
        g_free(model_data);
 
992
 
 
993
        return;
 
994
}
 
995
 
535
996
static void
536
997
hud_menu_model_collector_finalize (GObject *object)
537
998
{
543
1004
  if (collector->refresh_id)
544
1005
    g_source_remove (collector->refresh_id);
545
1006
 
546
 
  g_slist_free_full (collector->models, g_object_unref);
547
 
  g_clear_object (&collector->app_menu);
548
 
  g_clear_object (&collector->menubar);
549
 
  g_clear_object (&collector->application);
550
 
  g_clear_object (&collector->window);
 
1007
  if (collector->muxer_export) {
 
1008
    g_dbus_connection_unexport_action_group(collector->session, collector->muxer_export);
 
1009
    collector->muxer_export = 0;
 
1010
  }
 
1011
 
 
1012
  g_slist_free_full (collector->models, model_data_free);
 
1013
  g_clear_object (&collector->muxer);
551
1014
 
552
1015
  g_object_unref (collector->session);
553
1016
  g_free (collector->unique_bus_name);
554
 
  g_free (collector->app_menu_object_path);
555
 
  g_free (collector->menubar_object_path);
556
 
  g_free (collector->prefix);
557
 
  g_free (collector->desktop_file);
 
1017
  g_free (collector->app_id);
558
1018
  g_free (collector->icon);
 
1019
  g_object_unref (collector->keyword_mapping);
559
1020
 
560
1021
  g_ptr_array_unref (collector->items);
561
1022
 
 
1023
  g_clear_pointer(&collector->base_export_path, g_free);
 
1024
 
562
1025
  G_OBJECT_CLASS (hud_menu_model_collector_parent_class)
563
1026
    ->finalize (object);
564
1027
}
568
1031
{
569
1032
  collector->items = g_ptr_array_new_with_free_func (g_object_unref);
570
1033
  collector->cancellable = g_cancellable_new ();
 
1034
  collector->muxer = g_action_muxer_new();
 
1035
  collector->session = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL);
571
1036
}
572
1037
 
573
1038
static void
576
1041
  iface->use = hud_menu_model_collector_use;
577
1042
  iface->unuse = hud_menu_model_collector_unuse;
578
1043
  iface->search = hud_menu_model_collector_search;
 
1044
  iface->list_applications = hud_menu_model_collector_list_applications;
 
1045
  iface->get = hud_menu_model_collector_get;
 
1046
  iface->get_items = hud_menu_model_collector_get_items;
 
1047
  iface->activate_toolbar = hud_menu_model_collector_activate_toolbar;
 
1048
  iface->get_app_id = hud_menu_model_collector_get_app_id;
 
1049
  iface->get_app_icon = hud_menu_model_collector_get_app_icon;
579
1050
}
580
1051
 
581
1052
static void
582
1053
hud_menu_model_collector_class_init (HudMenuModelCollectorClass *class)
583
1054
{
584
 
  class->finalize = hud_menu_model_collector_finalize;
 
1055
  GObjectClass *gobject_class = G_OBJECT_CLASS (class);
 
1056
 
 
1057
  gobject_class->finalize = hud_menu_model_collector_finalize;
585
1058
}
586
1059
 
587
1060
static void
591
1064
{
592
1065
  GVariant *reply;
593
1066
 
 
1067
  if (source == NULL)
 
1068
  {
 
1069
    g_debug("Callback invoked with null connection");
 
1070
    return;
 
1071
  }
 
1072
 
594
1073
  /* The goal of this function is to set either the
595
1074
   * app_menu_is_hud_aware or menubar_is_hud_aware flag (which we have a
596
1075
   * pointer to in user_data) to TRUE in the case that the remote
619
1098
}
620
1099
 
621
1100
/**
622
 
 * hud_menu_model_collector_get:
 
1101
 * hud_menu_model_collector_new:
 
1102
 * @application_id: a unique identifier for the application
 
1103
 * @icon: the icon for the appliction
 
1104
 * @penalty: the penalty to apply to all results
 
1105
 * @export_path: the path to export items on dbus
 
1106
 * @type: the type of items in this collector
 
1107
 *
 
1108
 * Create a new #HudMenuModelCollector object
 
1109
 *
 
1110
 * Return value: New #HudMenuModelCollector
 
1111
 */
 
1112
HudMenuModelCollector *
 
1113
hud_menu_model_collector_new (const gchar *application_id,
 
1114
                              const gchar *icon,
 
1115
                              guint        penalty,
 
1116
                              const gchar *export_path,
 
1117
                              HudSourceItemType type)
 
1118
{
 
1119
        g_return_val_if_fail(application_id != NULL, NULL);
 
1120
        g_return_val_if_fail(export_path != NULL, NULL);
 
1121
 
 
1122
        HudMenuModelCollector * collector = g_object_new(HUD_TYPE_MENU_MODEL_COLLECTOR, NULL);
 
1123
 
 
1124
        collector->app_id = g_strdup (application_id);
 
1125
        collector->icon = g_strdup (icon);
 
1126
        collector->penalty = penalty;
 
1127
        collector->base_export_path = g_strdup(export_path);
 
1128
        collector->type = type;
 
1129
 
 
1130
        collector->keyword_mapping = hud_keyword_mapping_new();
 
1131
        hud_keyword_mapping_load(collector->keyword_mapping, collector->app_id, DATADIR, GNOMELOCALEDIR);
 
1132
 
 
1133
        GError * error = NULL;
 
1134
        collector->muxer_export = g_dbus_connection_export_action_group(collector->session,
 
1135
                                                                        collector->base_export_path,
 
1136
                                                                        G_ACTION_GROUP(collector->muxer),
 
1137
                                                                        &error);
 
1138
 
 
1139
        if (error != NULL) {
 
1140
                g_warning("Unable to export action group: %s", error->message);
 
1141
                g_error_free(error);
 
1142
        }
 
1143
 
 
1144
        return collector;
 
1145
}
 
1146
 
 
1147
#ifdef HAVE_BAMF
 
1148
/**
 
1149
 * hud_menu_model_collector_add_window:
623
1150
 * @window: a #BamfWindow
624
 
 * @desktop_file: the desktop file of the application of @window
625
 
 * @icon: the application icon's name
626
1151
 *
627
1152
 * If the given @window has #GMenuModel-style menus then returns a
628
1153
 * collector for them, otherwise returns %NULL.
629
1154
 *
630
 
 * @desktop_file is used for usage tracking.
631
 
 *
632
1155
 * Returns: a #HudMenuModelCollector, or %NULL
633
1156
 **/
634
 
HudMenuModelCollector *
635
 
hud_menu_model_collector_get (BamfWindow  *window,
636
 
                              const gchar *desktop_file,
637
 
                              const gchar *icon)
 
1157
void
 
1158
hud_menu_model_collector_add_window (HudMenuModelCollector * collector,
 
1159
                                     BamfWindow  *window)
638
1160
{
639
 
  HudMenuModelCollector *collector;
 
1161
  g_return_if_fail(HUD_IS_MENU_MODEL_COLLECTOR(collector));
 
1162
 
640
1163
  gchar *unique_bus_name;
641
1164
  gchar *application_object_path;
642
1165
  gchar *window_object_path;
643
 
  GDBusConnection *session;
 
1166
  gchar *app_menu_object_path;
 
1167
  gchar *menubar_object_path;
 
1168
 
 
1169
  GDBusMenuModel * app_menu;
 
1170
  GDBusMenuModel * menubar;
644
1171
 
645
1172
  unique_bus_name = bamf_window_get_utf8_prop (window, "_GTK_UNIQUE_BUS_NAME");
646
1173
 
647
 
  if (!unique_bus_name) {
648
 
    return NULL;
649
 
  }
650
 
 
651
 
  session = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
652
 
 
653
 
  if (!session)
654
 
    return NULL;
655
 
 
656
 
  collector = g_object_new (HUD_TYPE_MENU_MODEL_COLLECTOR, NULL);
657
 
  collector->session = session;
658
 
  collector->unique_bus_name = unique_bus_name;
659
 
 
660
 
  collector->app_menu_object_path = bamf_window_get_utf8_prop (window, "_GTK_APP_MENU_OBJECT_PATH");
661
 
  collector->menubar_object_path = bamf_window_get_utf8_prop (window, "_GTK_MENUBAR_OBJECT_PATH");
 
1174
  if (!unique_bus_name)
 
1175
    /* If this isn't set, we won't get very far... */
 
1176
    return;
 
1177
 
 
1178
  if (!g_dbus_is_name(unique_bus_name)) {
 
1179
    g_warning("Invalid BUS name '%s'", unique_bus_name);
 
1180
    return;
 
1181
  }
 
1182
 
 
1183
  if (collector->unique_bus_name == NULL) {
 
1184
    collector->unique_bus_name = g_strdup(unique_bus_name);
 
1185
  }
 
1186
 
 
1187
  if (g_strcmp0(unique_bus_name, collector->unique_bus_name) != 0) {
 
1188
    g_warning("Bus name '%s' does not match '%s'", unique_bus_name, collector->unique_bus_name);
 
1189
    g_free(unique_bus_name);
 
1190
    return;
 
1191
  }
 
1192
  g_clear_pointer(&unique_bus_name, g_free);
 
1193
 
 
1194
  app_menu_object_path = bamf_window_get_utf8_prop (window, "_GTK_APP_MENU_OBJECT_PATH");
 
1195
  menubar_object_path = bamf_window_get_utf8_prop (window, "_GTK_MENUBAR_OBJECT_PATH");
662
1196
  application_object_path = bamf_window_get_utf8_prop (window, "_GTK_APPLICATION_OBJECT_PATH");
663
1197
  window_object_path = bamf_window_get_utf8_prop (window, "_GTK_WINDOW_OBJECT_PATH");
664
1198
 
665
 
  if (collector->app_menu_object_path)
 
1199
  if (app_menu_object_path)
666
1200
    {
667
 
      collector->app_menu = g_dbus_menu_model_get (session, unique_bus_name, collector->app_menu_object_path);
668
 
      hud_menu_model_collector_add_model (collector, G_MENU_MODEL (collector->app_menu), NULL, NULL, NULL);
669
 
      g_dbus_connection_call (session, unique_bus_name, collector->app_menu_object_path,
670
 
                              "com.canonical.hud.Awareness", "CheckAwareness",
671
 
                              NULL, G_VARIANT_TYPE_UNIT, G_DBUS_CALL_FLAGS_NONE, -1, collector->cancellable,
672
 
                              hud_menu_model_collector_hud_awareness_cb, &collector->app_menu_is_hud_aware);
 
1201
      app_menu = g_dbus_menu_model_get (collector->session, collector->unique_bus_name, app_menu_object_path);
 
1202
      hud_menu_model_collector_add_model_internal (collector, G_MENU_MODEL (app_menu), app_menu_object_path, NULL, NULL, NULL, DEFAULT_MENU_DEPTH, collector->type);
 
1203
      g_object_unref(app_menu);
673
1204
    }
674
1205
 
675
 
  if (collector->menubar_object_path)
 
1206
  if (menubar_object_path)
676
1207
    {
677
 
      collector->menubar = g_dbus_menu_model_get (session, unique_bus_name, collector->menubar_object_path);
678
 
      hud_menu_model_collector_add_model (collector, G_MENU_MODEL (collector->menubar), NULL, NULL, NULL);
679
 
      g_dbus_connection_call (session, unique_bus_name, collector->menubar_object_path,
680
 
                              "com.canonical.hud.Awareness", "CheckAwareness",
681
 
                              NULL, G_VARIANT_TYPE_UNIT, G_DBUS_CALL_FLAGS_NONE, -1, collector->cancellable,
682
 
                              hud_menu_model_collector_hud_awareness_cb, &collector->menubar_is_hud_aware);
 
1208
      menubar = g_dbus_menu_model_get (collector->session, collector->unique_bus_name, menubar_object_path);
 
1209
      hud_menu_model_collector_add_model_internal (collector, G_MENU_MODEL (menubar), menubar_object_path, NULL, NULL, NULL, DEFAULT_MENU_DEPTH, collector->type);
 
1210
      g_object_unref(menubar);
683
1211
    }
684
1212
 
685
1213
  if (application_object_path)
686
 
    collector->application = g_dbus_action_group_get (session, unique_bus_name, application_object_path);
 
1214
    hud_menu_model_collector_add_actions(collector, G_ACTION_GROUP(g_dbus_action_group_get (collector->session, collector->unique_bus_name, application_object_path)), "app");
687
1215
 
688
1216
  if (window_object_path)
689
 
    collector->window = g_dbus_action_group_get (session, unique_bus_name, window_object_path);
690
 
 
691
 
  collector->is_application = TRUE;
692
 
  collector->desktop_file = g_strdup (desktop_file);
693
 
  collector->icon = g_strdup (icon);
 
1217
    hud_menu_model_collector_add_actions(collector, G_ACTION_GROUP(g_dbus_action_group_get (collector->session, collector->unique_bus_name, window_object_path)), "win");
694
1218
 
695
1219
  /* when the action groups change, we could end up having items
696
1220
   * enabled/disabled.  how to deal with that?
699
1223
  g_free (application_object_path);
700
1224
  g_free (window_object_path);
701
1225
 
702
 
  return collector;
 
1226
  return;
703
1227
}
 
1228
#endif
704
1229
 
705
1230
/**
706
 
 * hud_menu_model_collector_new_for_endpoint:
707
 
 * @application_id: a unique identifier for the application
 
1231
 * hud_menu_model_collector_add_endpoint:
708
1232
 * @prefix: the title to prefix to all items
709
 
 * @icon: the icon for the appliction
710
 
 * @penalty: the penalty to apply to all results
711
1233
 * @bus_name: a D-Bus bus name
712
 
 * @object_path: an object path at the destination given by @bus_name
 
1234
 * @menu_path: an object path at the destination given by @bus_name for the menu model
 
1235
 * @action_path: an object path at the destination given by @bus_name for the actions
713
1236
 *
714
1237
 * Creates a new #HudMenuModelCollector for the specified endpoint.
715
1238
 *
725
1248
 *
726
1249
 * Returns: a new #HudMenuModelCollector
727
1250
 */
728
 
HudMenuModelCollector *
729
 
hud_menu_model_collector_new_for_endpoint (const gchar *application_id,
730
 
                                           const gchar *prefix,
731
 
                                           const gchar *icon,
732
 
                                           guint        penalty,
733
 
                                           const gchar *bus_name,
734
 
                                           const gchar *object_path)
735
 
{
736
 
  HudMenuModelCollector *collector;
737
 
 
738
 
  collector = g_object_new (HUD_TYPE_MENU_MODEL_COLLECTOR, NULL);
739
 
 
740
 
  collector->session = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
741
 
 
742
 
  collector->app_menu = g_dbus_menu_model_get (collector->session, bus_name, object_path);
743
 
  collector->application = g_dbus_action_group_get (collector->session, bus_name, object_path);
744
 
 
745
 
  collector->is_application = FALSE;
746
 
  collector->prefix = g_strdup (prefix);
747
 
  collector->desktop_file = g_strdup (application_id);
748
 
  collector->icon = g_strdup (icon);
749
 
  collector->penalty = penalty;
750
 
 
751
 
  hud_menu_model_collector_add_model (collector, G_MENU_MODEL (collector->app_menu), NULL, NULL, prefix);
752
 
 
753
 
  return collector;
 
1251
void
 
1252
hud_menu_model_collector_add_endpoint (HudMenuModelCollector * collector,
 
1253
                                       const gchar *prefix,
 
1254
                                       const gchar *bus_name,
 
1255
                                       const gchar *menu_path,
 
1256
                                       const gchar *action_path)
 
1257
{
 
1258
  g_return_if_fail(HUD_IS_MENU_MODEL_COLLECTOR(collector));
 
1259
 
 
1260
  GActionGroup * group = G_ACTION_GROUP(g_dbus_action_group_get (collector->session, bus_name, action_path));
 
1261
  hud_menu_model_collector_add_actions(collector, group, NULL);
 
1262
  g_object_unref(group);
 
1263
 
 
1264
  GDBusMenuModel * app_menu = g_dbus_menu_model_get (collector->session, bus_name, menu_path);
 
1265
  hud_menu_model_collector_add_model(collector, G_MENU_MODEL (app_menu), prefix, DEFAULT_MENU_DEPTH);
 
1266
  g_object_unref(app_menu);
 
1267
 
 
1268
  return;
 
1269
}
 
1270
 
 
1271
/**
 
1272
 * hud_menu_model_collector_add_model:
 
1273
 * @collector: A #HudMenuModelCollector object
 
1274
 * @model: Model to add
 
1275
 * @prefix: (allow none): Text prefix to add to all entries if needed
 
1276
 * @recurse: Amount of levels to go down the model.
 
1277
 *
 
1278
 * Adds a Menu Model to the collector.
 
1279
 */
 
1280
void
 
1281
hud_menu_model_collector_add_model (HudMenuModelCollector * collector, GMenuModel * model, const gchar * prefix, guint recurse)
 
1282
{
 
1283
        g_return_if_fail(HUD_IS_MENU_MODEL_COLLECTOR(collector));
 
1284
        g_return_if_fail(G_IS_MENU_MODEL(model));
 
1285
 
 
1286
        return hud_menu_model_collector_add_model_internal(collector, model, NULL, NULL, NULL, prefix, recurse, collector->type);
 
1287
}
 
1288
 
 
1289
/**
 
1290
 * hud_menu_model_collector_add_actions:
 
1291
 * @collector: A #HudMenuModelCollector object
 
1292
 * @group: Action Group to add
 
1293
 * @prefix: (allow none): Text prefix to add to all entries if needed
 
1294
 *
 
1295
 * Add a set of actios to the collector
 
1296
 */
 
1297
void
 
1298
hud_menu_model_collector_add_actions (HudMenuModelCollector * collector, GActionGroup * group, const gchar * prefix)
 
1299
{
 
1300
        g_return_if_fail(HUD_IS_MENU_MODEL_COLLECTOR(collector));
 
1301
        g_return_if_fail(G_IS_ACTION_GROUP(group));
 
1302
 
 
1303
        g_action_muxer_insert(collector->muxer, prefix, group);
 
1304
 
 
1305
        return;
 
1306
}
 
1307
 
 
1308
/**
 
1309
 * hud_menu_model_collector_get_items:
 
1310
 * @collector: A #HudMenuModelCollector
 
1311
 *
 
1312
 * Get the list of items that are currently being watched
 
1313
 *
 
1314
 * Return value: (transfer full) (element-type HudItem): Items to look at
 
1315
 */
 
1316
static GList *
 
1317
hud_menu_model_collector_get_items (HudSource * source)
 
1318
{
 
1319
        g_return_val_if_fail(HUD_IS_MENU_MODEL_COLLECTOR(source), NULL);
 
1320
        HudMenuModelCollector * mcollector = HUD_MENU_MODEL_COLLECTOR(source);
 
1321
 
 
1322
        GList * retval = NULL;
 
1323
        int i;
 
1324
        for (i = 0; i < mcollector->items->len; i++) {
 
1325
                retval = g_list_prepend(retval, g_object_ref(g_ptr_array_index(mcollector->items, i)));
 
1326
        }
 
1327
 
 
1328
        return retval;
 
1329
}
 
1330
 
 
1331
/**
 
1332
 * hud_menu_model_collector_get_app_id:
 
1333
 * @collector: A #HudMenuModelCollector
 
1334
 *
 
1335
 * The ID of the application here
 
1336
 *
 
1337
 * Return value: Application ID
 
1338
 */
 
1339
static const gchar *
 
1340
hud_menu_model_collector_get_app_id (HudSource * collector)
 
1341
{
 
1342
        g_return_val_if_fail(HUD_IS_MENU_MODEL_COLLECTOR(collector), NULL);
 
1343
        return HUD_MENU_MODEL_COLLECTOR(collector)->app_id;
 
1344
}
 
1345
 
 
1346
/**
 
1347
 * hud_menu_model_collector_get_app_icon:
 
1348
 * @collector: A #HudMenuModelCollector
 
1349
 *
 
1350
 * The icon of the application here
 
1351
 *
 
1352
 * Return value: Application icon
 
1353
 */
 
1354
static const gchar *
 
1355
hud_menu_model_collector_get_app_icon (HudSource * collector)
 
1356
{
 
1357
        g_return_val_if_fail(HUD_IS_MENU_MODEL_COLLECTOR(collector), NULL);
 
1358
        return HUD_MENU_MODEL_COLLECTOR(collector)->icon;
754
1359
}