~didrocks/unity/fixes-natty-finale

697.1.5 by Neil Jagdish Patel
Do the initial loading of a .place file, bar the entries
1
/*
2
 * Copyright (C) 2010 Canonical Ltd
3
 *
4
 * This program is free software: you can redistribute it and/or modify
5
 * it under the terms of the GNU General Public License version 3 as
6
 * published by the Free Software Foundation.
7
 *
8
 * This program is distributed in the hope that it will be useful,
9
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
 * GNU General Public License for more details.
12
 *
13
 * You should have received a copy of the GNU General Public License
14
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
15
 *
16
 * Authored by: Neil Jagdish Patel <neil.patel@canonical.com>
17
 */
18
19
#include "PlaceRemote.h"
20
697.1.11 by Neil Jagdish Patel
Basic sync between daemon and cached entries
21
#include <algorithm>
697.1.6 by Neil Jagdish Patel
Some more hooking up
22
#include "PlaceEntryRemote.h"
23
697.1.5 by Neil Jagdish Patel
Do the initial loading of a .place file, bar the entries
24
#define PLACE_GROUP      "Place"
25
#define DBUS_NAME        "DBusName"
26
#define DBUS_PATH        "DBusObjectPath"
27
28
#define ENTRY_PREFIX     "Entry:"
29
30
#define ACTIVATION_GROUP "Activation"
31
#define URI_PATTERN      "URIPattern"
32
#define MIME_PATTERN     "MimetypePattern"
33
697.1.9 by Neil Jagdish Patel
Grab and unpack GetEntries()
34
#define PLACE_IFACE "com.canonical.Unity.Place"
35
36
static void on_service_proxy_ready (GObject      *source,
37
                                    GAsyncResult *result,
38
                                    gpointer      user_data);
39
40
static void on_service_proxy_signal_received (GDBusProxy *proxy,
41
                                              gchar      *sender_name,
42
                                              gchar      *signal_name,
43
                                              GVariant   *parameters,
44
                                              gpointer    user_data);
45
46
static void on_service_proxy_get_entries_received (GObject      *source,
47
                                                   GAsyncResult *result,
48
                                                   gpointer      user_data);
49
697.1.5 by Neil Jagdish Patel
Do the initial loading of a .place file, bar the entries
50
PlaceRemote::PlaceRemote (const char *path)
51
: _path (NULL),
52
  _dbus_name (NULL),
53
  _dbus_path (NULL),
54
  _uri_regex (NULL),
697.1.8 by Neil Jagdish Patel
More keyfile loading
55
  _mime_regex (NULL),
697.1.9 by Neil Jagdish Patel
Grab and unpack GetEntries()
56
  _valid (false),
57
  _service_proxy (NULL),
58
  _activation_proxy (NULL)
697.1.5 by Neil Jagdish Patel
Do the initial loading of a .place file, bar the entries
59
{
60
  GKeyFile *key_file;
61
  GError   *error = NULL;
62
63
  g_debug ("Loading Place: %s", path);
64
  _path = g_strdup (path);
65
66
  // A .place file is a keyfile, so we create on representing the .place file to
67
  // read it's contents
68
  key_file = g_key_file_new ();
69
  if (!g_key_file_load_from_file (key_file,
70
                                  path,
71
                                  G_KEY_FILE_NONE,
72
                                  &error))
73
  {
74
    g_warning ("Unable to load Place %s: %s",
75
               path,
76
               error ? error->message : "Unknown");
77
    if (error)
78
      g_error_free (error);
79
80
    return;
81
  }
82
83
  if (!g_key_file_has_group (key_file, PLACE_GROUP))
84
  {
85
    g_warning ("Unable to load Place %s: keyfile does not contain a 'Place' group",
86
               path);
87
    g_key_file_free (key_file);
88
    return;
89
  }
90
91
  // Seems valid, so let's try and load the main values
92
  _dbus_name = g_key_file_get_string (key_file, PLACE_GROUP, DBUS_NAME, &error);
93
  if (error)
94
  {
95
    g_warning ("Unable to load Place %s: keyfile does not contain a '" DBUS_NAME "' key, %s",
96
               path,
97
               error->message);
98
    g_error_free (error);
99
    g_key_file_free (key_file);
100
    return;
101
  }
102
103
  _dbus_path = g_key_file_get_string (key_file, PLACE_GROUP, DBUS_PATH, &error);
104
  if (error)
105
  {
106
    g_warning ("Unable to load Place %s: keyfile does not contain a '" DBUS_PATH "' key, %s",
107
               path,
108
               error->message);
109
    g_error_free (error);
110
    g_key_file_free (key_file);
111
    return;
112
  }
113
114
  // Now the optional bits
115
  if (g_key_file_has_group (key_file, ACTIVATION_GROUP))
116
  {
117
    gchar *uri_match;
118
    gchar *mime_match;
119
120
    uri_match = g_key_file_get_string (key_file, ACTIVATION_GROUP, URI_PATTERN, &error);
121
    if (error)
122
    {
123
      // Fail silently as it's not a requirement
124
      g_error_free (error);
125
      error = NULL;
126
    }
127
    else if (uri_match)
128
    {
129
      _uri_regex = g_regex_new (uri_match,
130
                                (GRegexCompileFlags)0,
131
                                (GRegexMatchFlags)0,
132
                                &error);
133
      if (error)
134
      {
135
        g_warning ("Unable to compile '"URI_PATTERN"' regex for %s: %s",
136
                   path,
137
                   error->message);
138
        g_error_free (error);
139
        error = NULL;
140
      }
141
    }
142
143
    mime_match = g_key_file_get_string (key_file, ACTIVATION_GROUP, MIME_PATTERN, &error);
144
    if (error)
145
    {
146
      // Fail silently as it's not a requirement
147
      g_error_free (error);
148
      error = NULL;
149
    }
150
    else if (mime_match)
151
    {
152
      _mime_regex = g_regex_new (mime_match,
153
                                (GRegexCompileFlags)0,
154
                                (GRegexMatchFlags)0,
155
                                &error);
156
      if (error)
157
      {
158
        g_warning ("Unable to compile '"MIME_PATTERN"' regex for %s: %s",
159
                   path,
160
                   error->message);
161
        g_error_free (error);
162
        error = NULL;
163
      }
164
    }
165
166
    g_free (uri_match);
167
    g_free (mime_match);
168
  }
697.1.6 by Neil Jagdish Patel
Some more hooking up
169
170
  LoadKeyFileEntries (key_file);
697.1.8 by Neil Jagdish Patel
More keyfile loading
171
172
  _valid = true;
697.1.9 by Neil Jagdish Patel
Grab and unpack GetEntries()
173
174
  Connect ();
697.1.5 by Neil Jagdish Patel
Do the initial loading of a .place file, bar the entries
175
    
176
  g_key_file_free (key_file);
177
}
178
179
PlaceRemote::~PlaceRemote ()
180
{
181
  std::vector<PlaceEntry*>::iterator it;
182
  
183
  for (it = _entries.begin(); it != _entries.end(); it++)
184
  {
185
    PlaceEntry *entry = static_cast<PlaceEntry *> (*it);
186
    delete entry;
187
  }
188
189
  _entries.erase (_entries.begin (), _entries.end ());
190
191
  g_free (_path);
192
  g_free (_dbus_name);
193
  g_free (_dbus_path);
194
  
195
  if (_uri_regex)
196
    g_regex_unref (_uri_regex);
197
  if (_mime_regex)
198
    g_regex_unref (_mime_regex);
697.1.9 by Neil Jagdish Patel
Grab and unpack GetEntries()
199
200
  if (_service_proxy)
201
    g_object_unref (_service_proxy);
202
203
  if (_activation_proxy)
204
    g_object_unref (_activation_proxy);
205
}
206
207
void
208
PlaceRemote::Connect ()
209
{
210
  g_dbus_proxy_new_for_bus (G_BUS_TYPE_SESSION,
211
                            G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
212
                            NULL,
213
                            _dbus_name,
214
                            _dbus_path,
215
                            PLACE_IFACE,
216
                            NULL,
217
                            on_service_proxy_ready,
218
                            this);
697.1.5 by Neil Jagdish Patel
Do the initial loading of a .place file, bar the entries
219
}
220
221
std::vector<PlaceEntry *>&
222
PlaceRemote::GetEntries ()
223
{
224
  return _entries;
225
}
226
227
guint32
228
PlaceRemote::GetNEntries ()
229
{
230
  return _entries.size ();
231
}
697.1.6 by Neil Jagdish Patel
Some more hooking up
232
233
void
234
PlaceRemote::LoadKeyFileEntries (GKeyFile *key_file)
235
{
236
  gchar **groups;
237
  gint    i = 0;
238
239
  groups = g_key_file_get_groups (key_file, NULL);
240
241
  while (groups[i])
242
  {
243
    const gchar *group = groups[i];
244
245
    if (g_str_has_prefix (group, ENTRY_PREFIX))
246
    {
697.1.15 by Neil Jagdish Patel
Connect to the entry
247
      PlaceEntryRemote *entry = new PlaceEntryRemote (_dbus_name);
697.1.7 by Neil Jagdish Patel
Getting there...slowly
248
      entry->InitFromKeyFile (key_file, group);
697.1.6 by Neil Jagdish Patel
Some more hooking up
249
      
697.1.7 by Neil Jagdish Patel
Getting there...slowly
250
      if (entry->IsValid ())
251
      {
252
        _entries.push_back (entry);
253
        entry_added.emit (entry);
254
      }
255
      else
256
        delete entry;
697.1.6 by Neil Jagdish Patel
Some more hooking up
257
    }
258
259
    i++;
260
  }
261
262
  g_strfreev (groups);
263
}
697.1.8 by Neil Jagdish Patel
More keyfile loading
264
265
bool
266
PlaceRemote::IsValid ()
267
{
268
  return _valid;
269
}
697.1.9 by Neil Jagdish Patel
Grab and unpack GetEntries()
270
271
void
272
PlaceRemote::OnServiceProxyReady (GObject *source, GAsyncResult *result)
273
{
274
  GError *error = NULL;
275
  gchar  *name_owner = NULL;
276
277
  _service_proxy = g_dbus_proxy_new_for_bus_finish (result, &error);
278
  name_owner = g_dbus_proxy_get_name_owner (_service_proxy);
279
280
  if (error || !name_owner)
281
  {
697.1.15 by Neil Jagdish Patel
Connect to the entry
282
    g_warning ("Unable to connect to PlaceRemote %s: %s",
697.1.9 by Neil Jagdish Patel
Grab and unpack GetEntries()
283
               _dbus_name,
284
               error ? error->message : "No name owner");
285
    if (error)
286
      g_error_free (error);
287
288
    g_free (name_owner);
289
    return;
290
  }
291
292
  g_signal_connect (_service_proxy, "g-signal",
293
                    G_CALLBACK (on_service_proxy_signal_received), this);
294
  g_dbus_proxy_call (_service_proxy,
295
                     "GetEntries",
296
                     NULL,
297
                     G_DBUS_CALL_FLAGS_NONE,
298
                     -1, 
299
                     NULL,
300
                     on_service_proxy_get_entries_received,
301
                     this);
302
  
303
  g_free (name_owner);
304
}
305
306
void
307
PlaceRemote::OnEntriesReceived (GVariant *args)
308
{
309
  GVariantIter *iter;
310
  gchar        *dbus_path;
311
  gchar        *name;
312
  gchar        *icon;
313
  guint32       position;
697.1.24 by Neil Jagdish Patel
implement review fixes
314
  GVariantIter *mimetypes;
697.1.9 by Neil Jagdish Patel
Grab and unpack GetEntries()
315
  gboolean      sensitive;
316
  gchar        *sections_model;
317
  GVariantIter *hints;
318
  gchar        *entry_renderer;
319
  gchar        *entry_groups_model;
320
  gchar        *entry_results_model;
321
  GVariantIter *entry_hints;
322
  gchar        *global_renderer;
323
  gchar        *global_groups_model;
324
  gchar        *global_results_model;
325
  GVariantIter *global_hints;
697.1.10 by Neil Jagdish Patel
some more progress
326
  std::vector<PlaceEntry*>::iterator it;
697.1.11 by Neil Jagdish Patel
Basic sync between daemon and cached entries
327
  std::vector<PlaceEntry*> old;
697.1.10 by Neil Jagdish Patel
some more progress
328
697.1.19 by Neil Jagdish Patel
Some fixes, sections model support
329
   // Clear the main entries vector as we now need to figure out if there is a diff between
697.1.10 by Neil Jagdish Patel
some more progress
330
  // what was sent and what we loaded from the place file
331
  for (it = _entries.begin(); it != _entries.end(); it++)
332
  {
333
    PlaceEntryRemote *entry = static_cast<PlaceEntryRemote *> (*it);
334
335
    entry->dirty = true;
336
  }
337
  
697.1.9 by Neil Jagdish Patel
Grab and unpack GetEntries()
338
  g_variant_get (args, "(a(sssuasbsa{ss}(sssa{ss})(sssa{ss})))", &iter);
339
  while (g_variant_iter_loop (iter, "(sssuasbsa{ss}(sssa{ss})(sssa{ss}))",
340
                              &dbus_path,
341
                              &name,
342
                              &icon,
343
                              &position,
344
                              &mimetypes,
345
                              &sensitive,
346
                              &sections_model,
347
                              &hints,
348
                              &entry_renderer,
349
                              &entry_groups_model,
350
                              &entry_results_model,
351
                              &entry_hints,
352
                              &global_renderer,
353
                              &global_groups_model,
354
                              &global_results_model,
355
                              &global_hints))
356
  {
697.1.10 by Neil Jagdish Patel
some more progress
357
    PlaceEntryRemote *existing = NULL;
358
359
    for (it = _entries.begin (); it != _entries.end (); it++)
360
    {
361
      PlaceEntryRemote *entry = static_cast<PlaceEntryRemote *> (*it);
362
363
      if (g_strcmp0 (entry->GetPath (), dbus_path) == 0)
364
      {
365
        existing = entry;
697.1.11 by Neil Jagdish Patel
Basic sync between daemon and cached entries
366
        break;
697.1.10 by Neil Jagdish Patel
some more progress
367
      }
368
    }
369
697.1.11 by Neil Jagdish Patel
Basic sync between daemon and cached entries
370
    if (!existing)
697.1.10 by Neil Jagdish Patel
some more progress
371
    {
697.1.15 by Neil Jagdish Patel
Connect to the entry
372
      existing = new PlaceEntryRemote (_dbus_name);
697.1.11 by Neil Jagdish Patel
Basic sync between daemon and cached entries
373
374
      _entries.push_back (existing);
375
      entry_added.emit (existing);
697.1.10 by Neil Jagdish Patel
some more progress
376
    }
697.1.11 by Neil Jagdish Patel
Basic sync between daemon and cached entries
377
378
    existing->Update (dbus_path,
379
                      name,
380
                      icon,
381
                      position,
697.1.24 by Neil Jagdish Patel
implement review fixes
382
                      mimetypes,
697.1.11 by Neil Jagdish Patel
Basic sync between daemon and cached entries
383
                      sensitive,
384
                      sections_model,
385
                      hints,
386
                      entry_renderer,
387
                      entry_groups_model,
388
                      entry_results_model,
389
                      entry_hints,
390
                      global_renderer,
391
                      global_groups_model,
392
                      global_results_model,
393
                      global_hints);
394
395
    existing->dirty = false;
396
  }
397
398
  for (it = _entries.begin (); it != _entries.end (); it++)
399
  {
400
    PlaceEntryRemote *entry = static_cast<PlaceEntryRemote *> (*it);
401
    if (entry->dirty)
402
      old.push_back (entry);
403
  }
404
  for (it = old.begin (); it != old.end (); it++)
405
  {
406
    PlaceEntryRemote *entry = static_cast<PlaceEntryRemote *> (*it);
407
    std::vector<PlaceEntry *>::iterator i;
408
409
    if ((i = std::find (_entries.begin (), _entries.end (), entry)) != _entries.end ())
697.1.10 by Neil Jagdish Patel
some more progress
410
    {
697.1.11 by Neil Jagdish Patel
Basic sync between daemon and cached entries
411
       entry_removed.emit (entry);
412
      _entries.erase (i);
413
      delete entry;
697.1.10 by Neil Jagdish Patel
some more progress
414
    }
697.1.9 by Neil Jagdish Patel
Grab and unpack GetEntries()
415
  }
416
417
  g_variant_iter_free (iter);
418
}
419
697.1.12 by Neil Jagdish Patel
Finish off the Place bits (bar Activation)
420
void
421
PlaceRemote::OnEntryAdded (GVariant *args)
422
{
423
  gchar        *dbus_path = NULL;
424
  gchar        *name = NULL;
425
  gchar        *icon = NULL;
426
  guint32       position = 0;
697.1.24 by Neil Jagdish Patel
implement review fixes
427
  GVariantIter *mimetypes = NULL;
697.1.12 by Neil Jagdish Patel
Finish off the Place bits (bar Activation)
428
  gboolean      sensitive = true;
429
  gchar        *sections_model = NULL;
430
  GVariantIter *hints = NULL;
431
  gchar        *entry_renderer = NULL;
432
  gchar        *entry_groups_model = NULL;
433
  gchar        *entry_results_model = NULL;
434
  GVariantIter *entry_hints = NULL;
435
  gchar        *global_renderer = NULL;
436
  gchar        *global_groups_model = NULL;
437
  gchar        *global_results_model = NULL;
438
  GVariantIter *global_hints = NULL;
439
  PlaceEntryRemote *entry;
440
441
  g_variant_get (args, "(sssuasbsa{ss}(sssa{ss})(sssa{ss}))",
442
                 &dbus_path,
443
                 &name,
444
                 &icon,
445
                 &position,
446
                 &mimetypes,
447
                 &sensitive,
448
                 &sections_model,
449
                 &hints,
450
                 &entry_renderer,
451
                 &entry_groups_model,
452
                 &entry_results_model,
453
                 &entry_hints,
454
                 &global_renderer,
455
                 &global_groups_model,
456
                 &global_results_model,
457
                 &global_hints);
458
697.1.15 by Neil Jagdish Patel
Connect to the entry
459
  entry = new PlaceEntryRemote (_dbus_name);
697.1.12 by Neil Jagdish Patel
Finish off the Place bits (bar Activation)
460
  entry->Update (dbus_path,
461
                 name,
462
                 icon,
463
                 position,
697.1.24 by Neil Jagdish Patel
implement review fixes
464
                 mimetypes,
697.1.12 by Neil Jagdish Patel
Finish off the Place bits (bar Activation)
465
                 sensitive,
466
                 sections_model,
467
                 hints,
468
                 entry_renderer,
469
                 entry_groups_model,
470
                 entry_results_model,
471
                 entry_hints,
472
                 global_renderer,
473
                 global_groups_model,
474
                 global_results_model,
475
                 global_hints);
476
  
477
  _entries.push_back (entry);
478
  entry_added.emit (entry);
479
  
480
  g_free (dbus_path);
481
  g_free (name);
482
  g_free (icon);
697.1.24 by Neil Jagdish Patel
implement review fixes
483
  g_variant_iter_free (mimetypes);
697.1.12 by Neil Jagdish Patel
Finish off the Place bits (bar Activation)
484
  g_free (sections_model);
485
  g_variant_iter_free (hints);
486
  g_free (entry_renderer);
487
  g_free (entry_groups_model);
488
  g_free (entry_results_model);
489
  g_variant_iter_free (entry_hints);
490
  g_free (global_renderer);
491
  g_free (global_groups_model);
492
  g_free (global_results_model);
493
  g_variant_iter_free (global_hints);
494
}
495
496
void
497
PlaceRemote::OnEntryRemoved (const gchar *dbus_path)
498
{
499
  std::vector<PlaceEntry *>::iterator it;
500
501
  for (it = _entries.begin (); it != _entries.end (); it++)
502
  {
503
    PlaceEntryRemote *entry = static_cast<PlaceEntryRemote *> (*it);
504
505
    if (g_strcmp0 (entry->GetPath (), dbus_path) == 0)
506
    {
507
      entry_removed.emit (entry);
508
      _entries.erase (it);
509
      delete entry;
510
      break;
511
    }
512
  }
513
}
514
697.1.9 by Neil Jagdish Patel
Grab and unpack GetEntries()
515
/*
516
 * C callbacks
517
 */
518
static void
519
on_service_proxy_ready (GObject      *source,
520
                        GAsyncResult *result,
521
                        gpointer      user_data)
522
{
523
  PlaceRemote *self = static_cast<PlaceRemote *> (user_data);
524
525
  self->OnServiceProxyReady (source, result);
526
}
527
528
static void
529
on_service_proxy_signal_received (GDBusProxy *proxy,
530
                                  gchar      *sender_name,
531
                                  gchar      *signal_name,
532
                                  GVariant   *parameters,
533
                                  gpointer    user_data)
697.1.12 by Neil Jagdish Patel
Finish off the Place bits (bar Activation)
534
{ 
535
  PlaceRemote *self = static_cast<PlaceRemote *> (user_data);
536
  
537
  if (g_strcmp0 (signal_name, "EntryAdded") == 0)
538
  {
539
    self->OnEntryAdded (parameters);
540
  }
541
  else if (g_strcmp0 (signal_name, "EntryRemoved") == 0)
542
  {
543
    self->OnEntryRemoved (g_variant_get_string (g_variant_get_child_value (parameters, 0), NULL));
544
  }
697.1.9 by Neil Jagdish Patel
Grab and unpack GetEntries()
545
}
546
547
static void
548
on_service_proxy_get_entries_received (GObject      *source,
549
                                       GAsyncResult *result,
550
                                       gpointer      user_data)
551
{
552
  PlaceRemote *self = static_cast<PlaceRemote *> (user_data);
553
  GVariant    *args;
554
  GError      *error = NULL;
555
556
  args = g_dbus_proxy_call_finish ((GDBusProxy *)source, result, &error);
557
  if (error)
558
  {
559
    g_warning ("Unable to call GetEntries() on: %s",
560
               error->message);
561
    g_error_free (error);
562
    return;
563
  }
564
565
  self->OnEntriesReceived (args);
566
567
  g_variant_unref (args);
568
}