~canonical-dx-team/unity/unity.fix-ql-losing-focus

798.5.1 by Mikkel Kamstrup Erlandsen
Add initial stubs to support the com.canonical.Unity.LauncherEntry DBus API in Unity
1
// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*-
2
/*
3
 * Copyright (C) 2011 Canonical Ltd
4
 *
5
 * This program is free software: you can redistribute it and/or modify
6
 * it under the terms of the GNU General Public License version 3 as
7
 * published by the Free Software Foundation.
8
 *
9
 * This program is distributed in the hope that it will be useful,
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
 * GNU General Public License for more details.
13
 *
14
 * You should have received a copy of the GNU General Public License
15
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16
 *
17
 * Authored by: Mikkel Kamstrup Erlandsen <mikkel.kamstrup@canonical.com>
18
 */
19
798.5.2 by Mikkel Kamstrup Erlandsen
More stubbing out of the Launcher DBus API, still not functional, but compiles without warnings
20
#include "LauncherEntryRemoteModel.h"
798.5.1 by Mikkel Kamstrup Erlandsen
Add initial stubs to support the com.canonical.Unity.LauncherEntry DBus API in Unity
21
798.5.3 by Mikkel Kamstrup Erlandsen
Still not done, but most of the management stuff is ready, now we just need to compile GVariant -> LauncherEntryRemote
22
static void nux_object_destroy_notify (nux::Object *obj);
23
798.5.1 by Mikkel Kamstrup Erlandsen
Add initial stubs to support the com.canonical.Unity.LauncherEntry DBus API in Unity
24
/**
25
 * Helper class implementing the remote API to control the icons in the
26
 * launcher. Also known as the com.canonical.Unity.LauncherEntry DBus API.
27
 * It enables clients to dynamically control their launcher icons by
28
 * adding a counter, progress indicator, or emblem to it. It also supports
29
 * adding a quicklist menu by means of the dbusmenu library.
30
 *
31
 * We take care to print out any client side errors or oddities in detail,
32
 * in order to help third party developers as much as possible when integrating
33
 * with Unity.
34
 */
798.5.2 by Mikkel Kamstrup Erlandsen
More stubbing out of the Launcher DBus API, still not functional, but compiles without warnings
35
LauncherEntryRemoteModel::LauncherEntryRemoteModel()
798.5.1 by Mikkel Kamstrup Erlandsen
Add initial stubs to support the com.canonical.Unity.LauncherEntry DBus API in Unity
36
{
37
  GError          *error;
38
798.5.3 by Mikkel Kamstrup Erlandsen
Still not done, but most of the management stuff is ready, now we just need to compile GVariant -> LauncherEntryRemote
39
  _launcher_entry_dbus_signal_id = 0;
798.5.1 by Mikkel Kamstrup Erlandsen
Add initial stubs to support the com.canonical.Unity.LauncherEntry DBus API in Unity
40
41
  error = NULL;
798.5.3 by Mikkel Kamstrup Erlandsen
Still not done, but most of the management stuff is ready, now we just need to compile GVariant -> LauncherEntryRemote
42
  _conn = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
798.5.1 by Mikkel Kamstrup Erlandsen
Add initial stubs to support the com.canonical.Unity.LauncherEntry DBus API in Unity
43
  if (error)
44
    {
45
      g_warning ("Unable to connect to session bus: %s", error->message);
46
      g_error_free (error);
47
      return;
48
    }
49
  
798.5.3 by Mikkel Kamstrup Erlandsen
Still not done, but most of the management stuff is ready, now we just need to compile GVariant -> LauncherEntryRemote
50
  _entries_by_uri = g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
51
                                           (GDestroyNotify) nux_object_destroy_notify);
52
798.5.1 by Mikkel Kamstrup Erlandsen
Add initial stubs to support the com.canonical.Unity.LauncherEntry DBus API in Unity
53
  /* Listen for *all* signals on the "com.canonical.Unity.LauncherEntry"
54
   * interface, no matter who the sender is */
798.5.3 by Mikkel Kamstrup Erlandsen
Still not done, but most of the management stuff is ready, now we just need to compile GVariant -> LauncherEntryRemote
55
  _launcher_entry_dbus_signal_id =
56
      g_dbus_connection_signal_subscribe (_conn,
798.5.1 by Mikkel Kamstrup Erlandsen
Add initial stubs to support the com.canonical.Unity.LauncherEntry DBus API in Unity
57
                                          NULL,                       // sender
58
                                          "com.canonical.Unity.LauncherEntry",
59
                                          NULL,                       // member
60
                                          NULL,                       // path
61
                                          NULL,                       // arg0
62
                                          G_DBUS_SIGNAL_FLAGS_NONE,
850.1.1 by Jason Smith
reset icon state when remote model drops off bus
63
                                          &on_launcher_entry_signal_received,
64
                                          this,
65
                                          NULL);
66
67
  _dbus_name_owner_changed_signal_id =
68
      g_dbus_connection_signal_subscribe (_conn,
69
                                          "org.freedesktop.DBus",     // sender
70
                                          "org.freedesktop.DBus",     // interface
71
                                          "NameOwnerChanged",         // member
72
                                          "/org/freedesktop/DBus",    // path
73
                                          NULL,                       // arg0
74
                                          G_DBUS_SIGNAL_FLAGS_NONE,
75
                                          &on_dbus_name_owner_changed_signal_received,
798.5.1 by Mikkel Kamstrup Erlandsen
Add initial stubs to support the com.canonical.Unity.LauncherEntry DBus API in Unity
76
                                          this,
77
                                          NULL);
78
}
79
798.5.2 by Mikkel Kamstrup Erlandsen
More stubbing out of the Launcher DBus API, still not functional, but compiles without warnings
80
LauncherEntryRemoteModel::~LauncherEntryRemoteModel()
798.5.1 by Mikkel Kamstrup Erlandsen
Add initial stubs to support the com.canonical.Unity.LauncherEntry DBus API in Unity
81
{
798.5.3 by Mikkel Kamstrup Erlandsen
Still not done, but most of the management stuff is ready, now we just need to compile GVariant -> LauncherEntryRemote
82
  g_hash_table_destroy (_entries_by_uri);
83
  _entries_by_uri = NULL;
84
85
  if (_launcher_entry_dbus_signal_id && _conn)
86
    {
87
      g_dbus_connection_signal_unsubscribe (_conn,
88
                                            _launcher_entry_dbus_signal_id);
89
      _launcher_entry_dbus_signal_id = 0;
90
    }
798.5.1 by Mikkel Kamstrup Erlandsen
Add initial stubs to support the com.canonical.Unity.LauncherEntry DBus API in Unity
91
  
798.5.3 by Mikkel Kamstrup Erlandsen
Still not done, but most of the management stuff is ready, now we just need to compile GVariant -> LauncherEntryRemote
92
  if (_conn)
93
    {
94
      g_object_unref (_conn);
95
      _conn = NULL;
96
    }
798.5.1 by Mikkel Kamstrup Erlandsen
Add initial stubs to support the com.canonical.Unity.LauncherEntry DBus API in Unity
97
}
98
798.5.5 by Mikkel Kamstrup Erlandsen
Make LauncherEntryRemoteModel a singleton
99
/**
100
 * Get a pointer to the default LauncherEntryRemoteModel singleton for this
101
 * process. The return value should not be freed.
102
 */
103
LauncherEntryRemoteModel*
104
LauncherEntryRemoteModel::GetDefault()
105
{
106
  static LauncherEntryRemoteModel *singleton = NULL;
107
108
  if (singleton == NULL)
109
    singleton = new LauncherEntryRemoteModel ();
110
111
  return singleton;
112
}
113
114
/**
115
 * Return the number of unique LauncherEntryRemote objects managed by the model.
116
 * The entries are identified by their LauncherEntryRemote::AppUri property.
117
 */
798.5.3 by Mikkel Kamstrup Erlandsen
Still not done, but most of the management stuff is ready, now we just need to compile GVariant -> LauncherEntryRemote
118
guint
798.5.2 by Mikkel Kamstrup Erlandsen
More stubbing out of the Launcher DBus API, still not functional, but compiles without warnings
119
LauncherEntryRemoteModel::Size ()
120
{
798.5.3 by Mikkel Kamstrup Erlandsen
Still not done, but most of the management stuff is ready, now we just need to compile GVariant -> LauncherEntryRemote
121
  return g_hash_table_size (_entries_by_uri);
122
}
123
124
/**
125
 * Return a pointer to a LauncherEntryRemote if there is one for app_uri,
126
 * otherwise NULL. The returned object should not be freed.
798.5.10 by Mikkel Kamstrup Erlandsen
Add convenience API to LauncherEntryRemoteModel - LookupByDesktopId() and LookupByDesktopFile()
127
 *
128
 * App Uris look like application://$desktop_file_id, where desktop_file_id
129
 * is the base name of the .desktop file for the application including the
130
 * .desktop extension. Eg. application://firefox.desktop.
798.5.3 by Mikkel Kamstrup Erlandsen
Still not done, but most of the management stuff is ready, now we just need to compile GVariant -> LauncherEntryRemote
131
 */
132
LauncherEntryRemote*
133
LauncherEntryRemoteModel::LookupByUri (const gchar *app_uri)
134
{
135
  gpointer entry;
136
137
  g_return_val_if_fail (app_uri != NULL, NULL);
138
139
  entry = g_hash_table_lookup (_entries_by_uri,  app_uri);
140
  return static_cast<LauncherEntryRemote *> (entry);
141
}
142
143
/**
798.5.10 by Mikkel Kamstrup Erlandsen
Add convenience API to LauncherEntryRemoteModel - LookupByDesktopId() and LookupByDesktopFile()
144
 * Return a pointer to a LauncherEntryRemote if there is one for desktop_id,
145
 * otherwise NULL. The returned object should not be freed.
146
 *
147
 * The desktop id is the base name of the .desktop file for the application
148
 * including the .desktop extension. Eg. firefox.desktop.
149
 */
150
LauncherEntryRemote*
151
LauncherEntryRemoteModel::LookupByDesktopId (const gchar *desktop_id)
152
{
153
  LauncherEntryRemote *entry;
154
  gchar               *app_uri;
155
156
  g_return_val_if_fail (desktop_id != NULL, NULL);
157
158
  app_uri = g_strconcat ("application://", desktop_id, NULL);
159
  entry = LookupByUri (app_uri);
160
  g_free (app_uri);
161
162
  return entry;
163
}
164
165
/**
166
 * Return a pointer to a LauncherEntryRemote if there is one for
167
 * desktop_file_path, otherwise NULL. The returned object should not be freed.
168
 */
169
LauncherEntryRemote*
170
LauncherEntryRemoteModel::LookupByDesktopFile (const gchar *desktop_file_path)
171
{
172
  LauncherEntryRemote *entry;
173
  gchar               *desktop_id, *app_uri;
174
175
  g_return_val_if_fail (desktop_file_path != NULL, NULL);
176
177
  desktop_id = g_path_get_basename (desktop_file_path);
178
  app_uri = g_strconcat ("application://", desktop_id, NULL);
179
  entry = LookupByUri (app_uri);
180
  g_free (app_uri);
181
  g_free (desktop_id);
182
183
  return entry;
184
}
185
186
/**
798.5.3 by Mikkel Kamstrup Erlandsen
Still not done, but most of the management stuff is ready, now we just need to compile GVariant -> LauncherEntryRemote
187
 * Get a list of all application URIs which have registered with the launcher
188
 * API. The returned GList should be freed with g_list_free(), but the URIs
189
 * should not be changed or freed.
190
 */
191
GList*
192
LauncherEntryRemoteModel::GetUris ()
193
{
194
  return (GList*) g_hash_table_get_keys (_entries_by_uri);
798.5.2 by Mikkel Kamstrup Erlandsen
More stubbing out of the Launcher DBus API, still not functional, but compiles without warnings
195
}
196
798.5.4 by Mikkel Kamstrup Erlandsen
Launcher API fully implemented, but is so far untested in the real world :-) It's still not wired up to the UI yet.
197
/**
198
 * Add or update a remote launcher entry.
199
 *
200
 * If 'entry' has a floating reference it will be consumed.
201
 */
202
void
203
LauncherEntryRemoteModel::AddEntry (LauncherEntryRemote *entry)
204
{
205
  LauncherEntryRemote *existing_entry;
206
207
  g_return_if_fail (entry != NULL);
208
209
  entry->SinkReference ();
210
211
  existing_entry = LookupByUri (entry->AppUri ());
212
  if (existing_entry != NULL)
213
    {
214
      existing_entry->Update (entry);
215
      entry->UnReference ();
216
    }
217
  else
218
    {
219
      /* The ref on entry will be removed by the hash table itself */
220
      g_hash_table_insert (_entries_by_uri, g_strdup (entry->AppUri ()), entry);
221
      entry_added.emit (entry);
222
    }
223
}
224
225
/**
226
 * Add or update a remote launcher entry.
227
 *
228
 * If 'entry' has a floating reference it will be consumed.
229
 */
230
void
231
LauncherEntryRemoteModel::RemoveEntry (LauncherEntryRemote *entry)
232
{
233
  g_return_if_fail (entry != NULL);
234
850.1.1 by Jason Smith
reset icon state when remote model drops off bus
235
  entry->Reference ();
798.5.4 by Mikkel Kamstrup Erlandsen
Launcher API fully implemented, but is so far untested in the real world :-) It's still not wired up to the UI yet.
236
237
  if (g_hash_table_remove (_entries_by_uri, entry->AppUri ()))
850.1.1 by Jason Smith
reset icon state when remote model drops off bus
238
    entry_removed.emit (entry);
239
  
798.5.4 by Mikkel Kamstrup Erlandsen
Launcher API fully implemented, but is so far untested in the real world :-) It's still not wired up to the UI yet.
240
  entry->UnReference ();
798.5.1 by Mikkel Kamstrup Erlandsen
Add initial stubs to support the com.canonical.Unity.LauncherEntry DBus API in Unity
241
}
242
811.2.1 by Mikkel Kamstrup Erlandsen
Slight change in how Update() DBus signals are treated in the Launcher API. By definition they only contain the "delta" - ie. the changed properties. Before this fix we always replaced all entry properties wholesale.
243
/**
244
 * Handle an incoming Update() signal from DBus
245
 */
246
void
247
LauncherEntryRemoteModel::HandleUpdateRequest (const gchar *sender_name,
248
                                               GVariant    *parameters)
249
{
250
  LauncherEntryRemote      *entry;
251
  gchar                    *app_uri;
252
  GVariantIter             *prop_iter;
253
254
  g_return_if_fail (sender_name != NULL);
255
  g_return_if_fail (parameters != NULL);
256
257
  if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(sa{sv})")))
258
    {
259
      g_warning ("Received 'com.canonical.Unity.LauncherEntry.Update' with"
260
                 " illegal payload signature '%s'. Expected '(sa{sv})'.",
261
                 g_variant_get_type_string (parameters));
262
      return;
263
    }
264
265
  if (sender_name == NULL)
266
    {
267
      g_critical ("Received 'com.canonical.Unity.LauncherEntry.Update' from"
268
                  " an undefined sender. This may happen if you are trying "
269
                  "to run Unity on a p2p DBus connection.");
270
      return;
271
    }
272
273
  g_variant_get (parameters, "(sa{sv})", &app_uri, &prop_iter);
274
  entry = LookupByUri (app_uri);
275
276
  if (entry)
277
    {
278
      /* It's important that we update the DBus name first since it might
279
       * unset the quicklist if it changes */
280
      entry->SetDBusName (sender_name);
281
      entry->Update (prop_iter);
282
    }
283
  else
284
    {
285
      entry = new LauncherEntryRemote (sender_name, parameters);
286
      AddEntry (entry); // consumes floating ref on entry
287
    }
288
289
  g_variant_iter_free (prop_iter);
290
  g_free (app_uri);
291
}
292
850.1.1 by Jason Smith
reset icon state when remote model drops off bus
293
void
294
LauncherEntryRemoteModel::on_launcher_entry_signal_received (GDBusConnection *connection,
798.5.1 by Mikkel Kamstrup Erlandsen
Add initial stubs to support the com.canonical.Unity.LauncherEntry DBus API in Unity
295
                                   const gchar     *sender_name,
296
                                   const gchar     *object_path,
297
                                   const gchar     *interface_name,
298
                                   const gchar     *signal_name,
299
                                   GVariant        *parameters,
300
                                   gpointer         user_data)
301
{
798.5.2 by Mikkel Kamstrup Erlandsen
More stubbing out of the Launcher DBus API, still not functional, but compiles without warnings
302
  LauncherEntryRemoteModel *self;
798.5.4 by Mikkel Kamstrup Erlandsen
Launcher API fully implemented, but is so far untested in the real world :-) It's still not wired up to the UI yet.
303
798.5.2 by Mikkel Kamstrup Erlandsen
More stubbing out of the Launcher DBus API, still not functional, but compiles without warnings
304
  self = static_cast<LauncherEntryRemoteModel *> (user_data);
305
306
  if (parameters == NULL)
798.5.1 by Mikkel Kamstrup Erlandsen
Add initial stubs to support the com.canonical.Unity.LauncherEntry DBus API in Unity
307
    {
798.5.2 by Mikkel Kamstrup Erlandsen
More stubbing out of the Launcher DBus API, still not functional, but compiles without warnings
308
      g_warning ("Received DBus signal '%s.%s' with empty payload from %s",
798.5.1 by Mikkel Kamstrup Erlandsen
Add initial stubs to support the com.canonical.Unity.LauncherEntry DBus API in Unity
309
                 interface_name, signal_name, sender_name);
310
      return;
311
    }
312
313
  if (g_strcmp0 (signal_name, "Update") == 0)
314
    {
811.2.1 by Mikkel Kamstrup Erlandsen
Slight change in how Update() DBus signals are treated in the Launcher API. By definition they only contain the "delta" - ie. the changed properties. Before this fix we always replaced all entry properties wholesale.
315
      self->HandleUpdateRequest (sender_name, parameters);
798.5.2 by Mikkel Kamstrup Erlandsen
More stubbing out of the Launcher DBus API, still not functional, but compiles without warnings
316
    }
317
  else
318
    {
319
      g_warning ("Unknown signal '%s.%s' from %s",
320
                 interface_name, signal_name, sender_name);
321
    }
850.1.1 by Jason Smith
reset icon state when remote model drops off bus
322
}
323
324
void 
325
LauncherEntryRemoteModel::on_dbus_name_owner_changed_signal_received (GDBusConnection *connection,
326
                                            const gchar *sender_name,
327
                                            const gchar *object_path,
328
                                            const gchar *interface_name,
329
                                            const gchar *signal_name,
330
                                            GVariant *parameters,
331
                                            gpointer user_data)
332
{
333
  LauncherEntryRemoteModel *self;
334
  char *name, *before, *after;
335
  
336
  self = static_cast<LauncherEntryRemoteModel *> (user_data);
337
  
338
  if (parameters == NULL)
339
    return;
340
  
341
  g_variant_get (parameters, "(sss)", &name, &before, &after);
342
  
343
  if (!after[0])
344
  {
345
    // Name gone, find and destroy LauncherEntryRemote
346
    GHashTableIter iter;
347
    GList *remove = NULL, *l;
348
    gpointer key, value;
349
350
    g_hash_table_iter_init (&iter, self->_entries_by_uri);
351
    while (g_hash_table_iter_next (&iter, &key, &value)) 
352
    {
353
      /* do something with key and value */
354
      LauncherEntryRemote *entry = static_cast<LauncherEntryRemote *> (value);
355
      if (g_str_equal (entry->DBusName (), name))
356
        remove = g_list_prepend (remove, entry);
357
    }
358
    
359
    for (l = remove; l; l = l->next)
360
      self->RemoveEntry (static_cast<LauncherEntryRemote *> (l->data));
361
    
362
    g_list_free (remove);
363
  }
798.5.1 by Mikkel Kamstrup Erlandsen
Add initial stubs to support the com.canonical.Unity.LauncherEntry DBus API in Unity
364
}
798.5.3 by Mikkel Kamstrup Erlandsen
Still not done, but most of the management stuff is ready, now we just need to compile GVariant -> LauncherEntryRemote
365
366
static void
367
nux_object_destroy_notify (nux::Object *obj)
368
{
369
  if (G_LIKELY (obj != NULL))
370
    obj->UnReference();
371
}