Merge lp:~unity-team/unity/launcher-api into lp:unity
- launcher-api
- Merge into trunk
Proposed by
Mikkel Kamstrup Erlandsen
Status: | Merged | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Merged at revision: | 814 | ||||||||||||
Proposed branch: | lp:~unity-team/unity/launcher-api | ||||||||||||
Merge into: | lp:unity | ||||||||||||
Diff against target: |
916 lines (+854/-0) 6 files modified
src/LauncherController.cpp (+10/-0) src/LauncherController.h (+6/-0) src/LauncherEntryRemote.cpp (+388/-0) src/LauncherEntryRemote.h (+96/-0) src/LauncherEntryRemoteModel.cpp (+298/-0) src/LauncherEntryRemoteModel.h (+56/-0) |
||||||||||||
To merge this branch: | bzr merge lp:~unity-team/unity/launcher-api | ||||||||||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Jason Smith (community) | Approve | ||
Review via email: mp+48152@code.launchpad.net |
Commit message
Description of the change
First cut at the Unity-side support for the Launcher DBus API.
It implements a set of helper classes that keeps everything in sync, but they are not wired up to the UI yet (except for a small dummy hook).
With this branch in the plan is to do something like:
* In LauncherController link the LauncherEntryRe
* In LauncherController remove the LauncherEntryRe
* Add missing rendering bits to the launcher:
- 'emblem' and 'count' seems to be lacking, but 'progress' looks like it's there
- quicklists probably need a fair deal of work
To post a comment you must log in.
Revision history for this message
Mikkel Kamstrup Erlandsen (kamstrup) wrote : | # |
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'src/LauncherController.cpp' |
2 | --- src/LauncherController.cpp 2011-01-26 17:48:19 +0000 |
3 | +++ src/LauncherController.cpp 2011-02-01 13:19:16 +0000 |
4 | @@ -53,6 +53,9 @@ |
5 | |
6 | _launcher->request_reorder_smart.connect (sigc::mem_fun (this, &LauncherController::OnLauncherRequestReorderSmart)); |
7 | _launcher->request_reorder_before.connect (sigc::mem_fun (this, &LauncherController::OnLauncherRequestReorderBefore)); |
8 | + |
9 | + _remote_model = LauncherEntryRemoteModel::GetDefault(); |
10 | + _remote_model->entry_added.connect (sigc::mem_fun (this, &LauncherController::OnLauncerEntryRemoteAdded)); |
11 | } |
12 | |
13 | LauncherController::~LauncherController() |
14 | @@ -188,6 +191,13 @@ |
15 | } |
16 | |
17 | void |
18 | +LauncherController::OnLauncerEntryRemoteAdded (LauncherEntryRemote *entry) |
19 | +{ |
20 | + g_debug ("LAUNCHER ENTRY ADDED: %s %s", entry->DBusName (), entry->AppUri ()); |
21 | + // FIXME: Wire the signals on entry up to matching LauncherIcon from the LauncherModel |
22 | +} |
23 | + |
24 | +void |
25 | LauncherController::OnExpoClicked (int button) |
26 | { |
27 | if (button == 1) |
28 | |
29 | === modified file 'src/LauncherController.h' |
30 | --- src/LauncherController.h 2011-01-24 23:05:38 +0000 |
31 | +++ src/LauncherController.h 2011-02-01 13:19:16 +0000 |
32 | @@ -33,6 +33,9 @@ |
33 | #include "FavoriteStore.h" |
34 | #include "PlaceLauncherSection.h" |
35 | |
36 | +#include "LauncherEntryRemote.h" |
37 | +#include "LauncherEntryRemoteModel.h" |
38 | + |
39 | #include <libbamf/libbamf.h> |
40 | #include <sigc++/sigc++.h> |
41 | |
42 | @@ -57,6 +60,7 @@ |
43 | int _sort_priority; |
44 | PlaceLauncherSection* _place_section; |
45 | DeviceLauncherSection* _device_section; |
46 | + LauncherEntryRemoteModel* _remote_model; |
47 | |
48 | void SortAndSave (); |
49 | |
50 | @@ -64,6 +68,8 @@ |
51 | void OnLauncherRequestReorderBefore (LauncherIcon *icon, LauncherIcon *before, bool save); |
52 | void OnIconAdded (LauncherIcon *icon); |
53 | |
54 | + void OnLauncerEntryRemoteAdded (LauncherEntryRemote *entry); |
55 | + |
56 | void InsertExpoAction (); |
57 | |
58 | void InsertTrash (); |
59 | |
60 | === added file 'src/LauncherEntryRemote.cpp' |
61 | --- src/LauncherEntryRemote.cpp 1970-01-01 00:00:00 +0000 |
62 | +++ src/LauncherEntryRemote.cpp 2011-02-01 13:19:16 +0000 |
63 | @@ -0,0 +1,388 @@ |
64 | +// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- |
65 | +/* |
66 | + * Copyright (C) 2011 Canonical Ltd |
67 | + * |
68 | + * This program is free software: you can redistribute it and/or modify |
69 | + * it under the terms of the GNU General Public License version 3 as |
70 | + * published by the Free Software Foundation. |
71 | + * |
72 | + * This program is distributed in the hope that it will be useful, |
73 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
74 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
75 | + * GNU General Public License for more details. |
76 | + * |
77 | + * You should have received a copy of the GNU General Public License |
78 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
79 | + * |
80 | + * Authored by: Mikkel Kamstrup Erlandsen <mikkel.kamstrup@canonical.com> |
81 | + */ |
82 | + |
83 | +#include "LauncherEntryRemote.h" |
84 | + |
85 | +NUX_IMPLEMENT_OBJECT_TYPE (LauncherEntryRemote); |
86 | + |
87 | +/** |
88 | + * Create a new LauncherEntryRemote parsed from the raw DBus wire format |
89 | + * of the com.canonical.Unity.LauncherEntry.Update signal '(sa{sv})'. The |
90 | + * dbus_name argument should be the unique bus name of the process owning |
91 | + * the launcher entry. |
92 | + * |
93 | + * You don't normally need to use this constructor yourself. The |
94 | + * LauncherEntryRemoteModel will do that for you when needed. |
95 | + */ |
96 | +LauncherEntryRemote::LauncherEntryRemote(const gchar *dbus_name, GVariant *val) |
97 | +{ |
98 | + gchar *app_uri, *prop_key; |
99 | + GVariantIter *prop_iter; |
100 | + GVariant *prop_value; |
101 | + |
102 | + g_return_if_fail (dbus_name != NULL); |
103 | + g_return_if_fail (val != NULL); |
104 | + |
105 | + _dbus_name = g_strdup (dbus_name); |
106 | + |
107 | + _emblem = NULL; |
108 | + _count = G_GINT64_CONSTANT (0); |
109 | + _progress = 0.0; |
110 | + _quicklist = NULL; |
111 | + |
112 | + _emblem_visible = FALSE; |
113 | + _count_visible = FALSE; |
114 | + _progress_visible = FALSE; |
115 | + |
116 | + g_variant_ref_sink (val); |
117 | + g_variant_get (val, "(sa{sv})", &app_uri, &prop_iter); |
118 | + |
119 | + _app_uri = app_uri; // steal ref |
120 | + |
121 | + while (g_variant_iter_loop (prop_iter, "{sv}", &prop_key, &prop_value)) |
122 | + { |
123 | + if (g_str_equal ("emblem", prop_key)) |
124 | + g_variant_get (prop_value, "s", &_emblem); |
125 | + else if (g_str_equal ("count", prop_key)) |
126 | + _count = g_variant_get_int64 (prop_value); |
127 | + else if (g_str_equal ("progress", prop_key)) |
128 | + _progress = g_variant_get_double (prop_value); |
129 | + else if (g_str_equal ("emblem-visible", prop_key)) |
130 | + _emblem_visible = g_variant_get_boolean (prop_value); |
131 | + else if (g_str_equal ("count-visible", prop_key)) |
132 | + _count_visible = g_variant_get_boolean (prop_value); |
133 | + else if (g_str_equal ("progress-visible", prop_key)) |
134 | + _progress_visible = g_variant_get_boolean (prop_value); |
135 | + else if (g_str_equal ("quicklist", prop_key)) |
136 | + { |
137 | + const gchar *ql_path; |
138 | + ql_path = g_variant_get_string (prop_value, NULL); |
139 | + _quicklist = dbusmenu_client_new (_dbus_name, ql_path); |
140 | + } |
141 | + } |
142 | + |
143 | + g_variant_iter_free (prop_iter); |
144 | + g_variant_unref (val); |
145 | +} |
146 | + |
147 | +LauncherEntryRemote::~LauncherEntryRemote() |
148 | +{ |
149 | + if (_dbus_name) |
150 | + { |
151 | + g_free (_dbus_name); |
152 | + _dbus_name = NULL; |
153 | + } |
154 | + |
155 | + if (_app_uri) |
156 | + { |
157 | + g_free (_app_uri); |
158 | + _app_uri = NULL; |
159 | + } |
160 | + |
161 | + if (_emblem) |
162 | + { |
163 | + g_free (_emblem); |
164 | + _emblem = NULL; |
165 | + } |
166 | + |
167 | + if (_quicklist) |
168 | + { |
169 | + g_object_unref (_quicklist); |
170 | + _quicklist = NULL; |
171 | + } |
172 | +} |
173 | + |
174 | +/** |
175 | + * The appuri property is on the form application://$desktop_file_id and is |
176 | + * used within the LauncherEntryRemoteModel to uniquely identify the various |
177 | + * applications. |
178 | + * |
179 | + * Note that a desktop file id is defined to be the base name of the desktop |
180 | + * file including the extension. Eg. gedit.desktop. Thus a full appuri could be |
181 | + * application://gedit.desktop. |
182 | + */ |
183 | +const gchar* |
184 | +LauncherEntryRemote::AppUri() |
185 | +{ |
186 | + return _app_uri; |
187 | +} |
188 | + |
189 | +/** |
190 | + * Return the unique DBus name for the remote party owning this launcher entry. |
191 | + */ |
192 | +const gchar* |
193 | +LauncherEntryRemote::DBusName() |
194 | +{ |
195 | + return _dbus_name; |
196 | +} |
197 | + |
198 | +const gchar* |
199 | +LauncherEntryRemote::Emblem() |
200 | +{ |
201 | + return _emblem; |
202 | +} |
203 | + |
204 | +gint64 |
205 | +LauncherEntryRemote::Count() |
206 | +{ |
207 | + return _count; |
208 | +} |
209 | + |
210 | +gdouble |
211 | +LauncherEntryRemote::Progress() |
212 | +{ |
213 | + return _progress; |
214 | +} |
215 | + |
216 | +/** |
217 | + * Return a pointer to the current quicklist of the launcher entry; NULL if |
218 | + * it's unset. |
219 | + * |
220 | + * The returned object should not be freed. |
221 | + */ |
222 | +DbusmenuClient* |
223 | +LauncherEntryRemote::Quicklist() |
224 | +{ |
225 | + return _quicklist; |
226 | +} |
227 | + |
228 | +gboolean |
229 | +LauncherEntryRemote::EmblemVisible() |
230 | +{ |
231 | + return _emblem_visible; |
232 | +} |
233 | + |
234 | +gboolean |
235 | +LauncherEntryRemote::CountVisible() |
236 | +{ |
237 | + return _count_visible; |
238 | +} |
239 | + |
240 | +gboolean |
241 | +LauncherEntryRemote::ProgressVisible() |
242 | +{ |
243 | + return _progress_visible; |
244 | +} |
245 | + |
246 | +/** |
247 | + * Set the unique DBus name for the process owning the launcher entry. |
248 | + * |
249 | + * If the entry has any exported quicklist it will be removed. |
250 | + */ |
251 | +void |
252 | +LauncherEntryRemote::SetDBusName(const gchar* dbus_name) |
253 | +{ |
254 | + gchar *old_name; |
255 | + |
256 | + if (g_strcmp0 (_dbus_name, dbus_name) == 0) |
257 | + return; |
258 | + |
259 | + old_name = _dbus_name; |
260 | + _dbus_name = g_strdup (dbus_name); |
261 | + |
262 | + /* Remove the quicklist since we can't know if it it'll be valid on the |
263 | + * new name, and we certainly don't want the quicklist to operate on a |
264 | + * different name than the rest of the launcher API */ |
265 | + SetQuicklist (NULL); |
266 | + |
267 | + dbus_name_changed.emit (old_name); |
268 | + g_free (old_name); |
269 | +} |
270 | + |
271 | +void |
272 | +LauncherEntryRemote::SetEmblem(const gchar* emblem) |
273 | +{ |
274 | + if (g_strcmp0 (_emblem, emblem) == 0) |
275 | + return; |
276 | + |
277 | + if (_emblem) |
278 | + g_free (_emblem); |
279 | + |
280 | + _emblem = g_strdup (emblem); |
281 | + emblem_changed.emit (); |
282 | +} |
283 | + |
284 | +void |
285 | +LauncherEntryRemote::SetCount(gint64 count) |
286 | +{ |
287 | + if (_count == count) |
288 | + return; |
289 | + |
290 | + _count = count; |
291 | + count_changed.emit (); |
292 | +} |
293 | + |
294 | +void |
295 | +LauncherEntryRemote::SetProgress(gdouble progress) |
296 | +{ |
297 | + if (_progress == progress) |
298 | + return; |
299 | + |
300 | + _progress = progress; |
301 | + progress_changed.emit (); |
302 | +} |
303 | + |
304 | +/** |
305 | + * Set the quicklist of this entry to be the Dbusmenu on the given path. |
306 | + * If entry already has a quicklist with the same path this call is a no-op. |
307 | + * |
308 | + * To unset the quicklist pass in a NULL path. |
309 | + */ |
310 | +void |
311 | +LauncherEntryRemote::SetQuicklistPath(const gchar *dbus_path) |
312 | +{ |
313 | + /* Check if existing quicklist have exact same path |
314 | + * and ignore the change in that case */ |
315 | + if (_quicklist) |
316 | + { |
317 | + gchar *ql_path; |
318 | + g_object_get (_quicklist, DBUSMENU_CLIENT_PROP_DBUS_OBJECT, &ql_path, NULL); |
319 | + if (g_strcmp0 (dbus_path, ql_path) == 0) |
320 | + { |
321 | + g_free (ql_path); |
322 | + return; |
323 | + } |
324 | + |
325 | + /* Prepare for a new quicklist */ |
326 | + g_free (ql_path); |
327 | + g_object_unref (_quicklist); |
328 | + } |
329 | + else if (_quicklist == NULL && dbus_path == NULL) |
330 | + return; |
331 | + |
332 | + if (dbus_path != NULL) |
333 | + _quicklist = dbusmenu_client_new (_dbus_name, dbus_path); |
334 | + else |
335 | + _quicklist = NULL; |
336 | + |
337 | + quicklist_changed.emit (); |
338 | +} |
339 | + |
340 | +/** |
341 | + * Set the quicklist of this entry to a given DbusmenuClient. |
342 | + * If entry already has a quicklist with the same path this call is a no-op. |
343 | + * |
344 | + * To unset the quicklist for this entry pass in NULL as the quicklist to set. |
345 | + */ |
346 | +void |
347 | +LauncherEntryRemote::SetQuicklist(DbusmenuClient *quicklist) |
348 | +{ |
349 | + /* Check if existing quicklist have exact same path as the new one |
350 | + * and ignore the change in that case. We also assert that the quicklist |
351 | + * uses the same name as the connection owning this launcher entry */ |
352 | + if (_quicklist) |
353 | + { |
354 | + gchar *ql_path, *new_ql_path, *new_ql_name; |
355 | + g_object_get (_quicklist, DBUSMENU_CLIENT_PROP_DBUS_OBJECT, &ql_path, NULL); |
356 | + |
357 | + if (quicklist == NULL) |
358 | + { |
359 | + new_ql_path = NULL; |
360 | + new_ql_name = NULL; |
361 | + } |
362 | + else |
363 | + { |
364 | + g_object_get (quicklist, DBUSMENU_CLIENT_PROP_DBUS_OBJECT, &new_ql_path, NULL); |
365 | + g_object_get (quicklist, DBUSMENU_CLIENT_PROP_DBUS_NAME, &new_ql_name, NULL); |
366 | + } |
367 | + |
368 | + if (quicklist != NULL && g_strcmp0 (new_ql_name, _dbus_name) != 0) |
369 | + { |
370 | + g_critical ("Mismatch between quicklist- and launcher entry owner:" |
371 | + "%s and %s respectively", new_ql_name, _dbus_name); |
372 | + g_free (ql_path); |
373 | + g_free (new_ql_path); |
374 | + g_free (new_ql_name); |
375 | + return; |
376 | + } |
377 | + |
378 | + if (g_strcmp0 (new_ql_path, ql_path) == 0) |
379 | + { |
380 | + g_free (ql_path); |
381 | + g_free (new_ql_path); |
382 | + g_free (new_ql_name); |
383 | + return; |
384 | + } |
385 | + |
386 | + /* Prepare for a new quicklist */ |
387 | + g_free (ql_path); |
388 | + g_free (new_ql_path); |
389 | + g_free (new_ql_name); |
390 | + g_object_unref (_quicklist); |
391 | + } |
392 | + else if (_quicklist == NULL && quicklist == NULL) |
393 | + return; |
394 | + |
395 | + if (quicklist == NULL) |
396 | + _quicklist = NULL; |
397 | + else |
398 | + _quicklist = (DbusmenuClient *) g_object_ref (quicklist); |
399 | + |
400 | + quicklist_changed.emit (); |
401 | +} |
402 | + |
403 | +void |
404 | +LauncherEntryRemote::SetEmblemVisible(gboolean visible) |
405 | +{ |
406 | + if (_emblem_visible == visible) |
407 | + return; |
408 | + |
409 | + _emblem_visible = visible; |
410 | + emblem_visible_changed.emit (); |
411 | +} |
412 | + |
413 | +void |
414 | +LauncherEntryRemote::SetCountVisible(gboolean visible) |
415 | +{ |
416 | + if (_count_visible == visible) |
417 | + return; |
418 | + |
419 | + _count_visible = visible; |
420 | + count_visible_changed.emit (); |
421 | +} |
422 | + |
423 | +void |
424 | +LauncherEntryRemote::SetProgressVisible(gboolean visible) |
425 | +{ |
426 | + if (_progress_visible == visible) |
427 | + return; |
428 | + |
429 | + _progress_visible = visible; |
430 | + progress_visible_changed.emit (); |
431 | +} |
432 | + |
433 | +/** |
434 | + * Set all properties from 'other' on 'this'. |
435 | + */ |
436 | +void |
437 | +LauncherEntryRemote::Update(LauncherEntryRemote *other) |
438 | +{ |
439 | + /* It's important that we update the DBus name first since it might |
440 | + * unset the quicklist if it changes */ |
441 | + SetDBusName (other->DBusName ()); |
442 | + |
443 | + SetEmblem (other->Emblem ()); |
444 | + SetCount (other->Count ()); |
445 | + SetProgress (other->Progress ()); |
446 | + SetQuicklist (other->Quicklist()); |
447 | + |
448 | + SetEmblemVisible (other->EmblemVisible ()); |
449 | + SetCountVisible(other->CountVisible ()); |
450 | + SetProgressVisible(other->ProgressVisible()); |
451 | +} |
452 | |
453 | === added file 'src/LauncherEntryRemote.h' |
454 | --- src/LauncherEntryRemote.h 1970-01-01 00:00:00 +0000 |
455 | +++ src/LauncherEntryRemote.h 2011-02-01 13:19:16 +0000 |
456 | @@ -0,0 +1,96 @@ |
457 | +// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- |
458 | +/* |
459 | + * Copyright (C) 2011 Canonical Ltd |
460 | + * |
461 | + * This program is free software: you can redistribute it and/or modify |
462 | + * it under the terms of the GNU General Public License version 3 as |
463 | + * published by the Free Software Foundation. |
464 | + * |
465 | + * This program is distributed in the hope that it will be useful, |
466 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
467 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
468 | + * GNU General Public License for more details. |
469 | + * |
470 | + * You should have received a copy of the GNU General Public License |
471 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
472 | + * |
473 | + * Authored by: Mikkel Kamstrup Erlandsen <mikkel.kamstrup@canonical.com> |
474 | + */ |
475 | + |
476 | +#ifndef LAUNCHER_ENTRY_REMOTE_H |
477 | +#define LAUNCHER_ENTRY_REMOTE_H |
478 | + |
479 | +#include <Nux/Nux.h> |
480 | +#include <glib.h> |
481 | +#include <sigc++/sigc++.h> |
482 | +#include <libdbusmenu-glib/client.h> |
483 | +#include <libdbusmenu-glib/menuitem.h> |
484 | + |
485 | +/** |
486 | + * Instances of this class mirrors the remote metadata for a laucnher entry |
487 | + * exposed by an application via the com.canonical.Unity.LauncherEntry DBus API. |
488 | + * |
489 | + * You do not create instances of LauncherEntryRemote yourself. Instead they |
490 | + * are created and managed dynamically by a LauncherEntryRemoteModel. |
491 | + */ |
492 | +class LauncherEntryRemote : public nux::InitiallyUnownedObject, public sigc::trackable |
493 | +{ |
494 | + NUX_DECLARE_OBJECT_TYPE (LauncherEntryRemote, nux::InitiallyUnownedObject); |
495 | +public: |
496 | + |
497 | + LauncherEntryRemote(const gchar *dbus_name, GVariant *val); |
498 | + ~LauncherEntryRemote(); |
499 | + |
500 | + const gchar* AppUri(); |
501 | + const gchar* DBusName(); |
502 | + const gchar* Emblem(); |
503 | + gint64 Count(); |
504 | + gdouble Progress(); |
505 | + DbusmenuClient* Quicklist (); |
506 | + |
507 | + gboolean EmblemVisible(); |
508 | + gboolean CountVisible(); |
509 | + gboolean ProgressVisible(); |
510 | + |
511 | + sigc::signal<void, const gchar * > dbus_name_changed; // gives the old name as arg |
512 | + sigc::signal<void> emblem_changed; |
513 | + sigc::signal<void> count_changed; |
514 | + sigc::signal<void> progress_changed; |
515 | + sigc::signal<void> quicklist_changed; |
516 | + |
517 | + sigc::signal<void> emblem_visible_changed; |
518 | + sigc::signal<void> count_visible_changed; |
519 | + sigc::signal<void> progress_visible_changed; |
520 | + |
521 | +private: |
522 | + |
523 | + gchar *_app_uri; |
524 | + gchar *_emblem; |
525 | + gint64 _count; |
526 | + gdouble _progress; |
527 | + |
528 | + gchar *_dbus_name; |
529 | + gchar *_quicklist_dbus_path; |
530 | + DbusmenuClient *_quicklist; |
531 | + |
532 | + gboolean _emblem_visible; |
533 | + gboolean _count_visible; |
534 | + gboolean _progress_visible; |
535 | + |
536 | + void SetDBusName (const gchar *dbus_name); |
537 | + void SetEmblem (const gchar *emblem); |
538 | + void SetCount (gint64 count); |
539 | + void SetProgress (gdouble progress); |
540 | + void SetQuicklistPath (const gchar *dbus_path); |
541 | + void SetQuicklist (DbusmenuClient *quicklist); |
542 | + |
543 | + void SetEmblemVisible (gboolean visible); |
544 | + void SetCountVisible (gboolean visible); |
545 | + void SetProgressVisible (gboolean visible); |
546 | + |
547 | + void Update (LauncherEntryRemote *other); |
548 | + |
549 | + friend class LauncherEntryRemoteModel; |
550 | +}; |
551 | + |
552 | +#endif // LAUNCHER_ENTRY_REMOTE_H |
553 | |
554 | === added file 'src/LauncherEntryRemoteModel.cpp' |
555 | --- src/LauncherEntryRemoteModel.cpp 1970-01-01 00:00:00 +0000 |
556 | +++ src/LauncherEntryRemoteModel.cpp 2011-02-01 13:19:16 +0000 |
557 | @@ -0,0 +1,298 @@ |
558 | +// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- |
559 | +/* |
560 | + * Copyright (C) 2011 Canonical Ltd |
561 | + * |
562 | + * This program is free software: you can redistribute it and/or modify |
563 | + * it under the terms of the GNU General Public License version 3 as |
564 | + * published by the Free Software Foundation. |
565 | + * |
566 | + * This program is distributed in the hope that it will be useful, |
567 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
568 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
569 | + * GNU General Public License for more details. |
570 | + * |
571 | + * You should have received a copy of the GNU General Public License |
572 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
573 | + * |
574 | + * Authored by: Mikkel Kamstrup Erlandsen <mikkel.kamstrup@canonical.com> |
575 | + */ |
576 | + |
577 | +#include "LauncherEntryRemoteModel.h" |
578 | + |
579 | +static void on_launcher_entry_signal_received (GDBusConnection *connection, |
580 | + const gchar *sender_name, |
581 | + const gchar *object_path, |
582 | + const gchar *interface_name, |
583 | + const gchar *signal_name, |
584 | + GVariant *parameters, |
585 | + gpointer user_data); |
586 | + |
587 | +static void nux_object_destroy_notify (nux::Object *obj); |
588 | + |
589 | +/** |
590 | + * Helper class implementing the remote API to control the icons in the |
591 | + * launcher. Also known as the com.canonical.Unity.LauncherEntry DBus API. |
592 | + * It enables clients to dynamically control their launcher icons by |
593 | + * adding a counter, progress indicator, or emblem to it. It also supports |
594 | + * adding a quicklist menu by means of the dbusmenu library. |
595 | + * |
596 | + * We take care to print out any client side errors or oddities in detail, |
597 | + * in order to help third party developers as much as possible when integrating |
598 | + * with Unity. |
599 | + */ |
600 | +LauncherEntryRemoteModel::LauncherEntryRemoteModel() |
601 | +{ |
602 | + GError *error; |
603 | + |
604 | + _launcher_entry_dbus_signal_id = 0; |
605 | + |
606 | + error = NULL; |
607 | + _conn = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error); |
608 | + if (error) |
609 | + { |
610 | + g_warning ("Unable to connect to session bus: %s", error->message); |
611 | + g_error_free (error); |
612 | + return; |
613 | + } |
614 | + |
615 | + _entries_by_uri = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, |
616 | + (GDestroyNotify) nux_object_destroy_notify); |
617 | + |
618 | + /* Listen for *all* signals on the "com.canonical.Unity.LauncherEntry" |
619 | + * interface, no matter who the sender is */ |
620 | + _launcher_entry_dbus_signal_id = |
621 | + g_dbus_connection_signal_subscribe (_conn, |
622 | + NULL, // sender |
623 | + "com.canonical.Unity.LauncherEntry", |
624 | + NULL, // member |
625 | + NULL, // path |
626 | + NULL, // arg0 |
627 | + G_DBUS_SIGNAL_FLAGS_NONE, |
628 | + on_launcher_entry_signal_received, |
629 | + this, |
630 | + NULL); |
631 | +} |
632 | + |
633 | +LauncherEntryRemoteModel::~LauncherEntryRemoteModel() |
634 | +{ |
635 | + g_hash_table_destroy (_entries_by_uri); |
636 | + _entries_by_uri = NULL; |
637 | + |
638 | + if (_launcher_entry_dbus_signal_id && _conn) |
639 | + { |
640 | + g_dbus_connection_signal_unsubscribe (_conn, |
641 | + _launcher_entry_dbus_signal_id); |
642 | + _launcher_entry_dbus_signal_id = 0; |
643 | + } |
644 | + |
645 | + if (_conn) |
646 | + { |
647 | + g_object_unref (_conn); |
648 | + _conn = NULL; |
649 | + } |
650 | +} |
651 | + |
652 | +/** |
653 | + * Get a pointer to the default LauncherEntryRemoteModel singleton for this |
654 | + * process. The return value should not be freed. |
655 | + */ |
656 | +LauncherEntryRemoteModel* |
657 | +LauncherEntryRemoteModel::GetDefault() |
658 | +{ |
659 | + static LauncherEntryRemoteModel *singleton = NULL; |
660 | + |
661 | + if (singleton == NULL) |
662 | + singleton = new LauncherEntryRemoteModel (); |
663 | + |
664 | + return singleton; |
665 | +} |
666 | + |
667 | +/** |
668 | + * Return the number of unique LauncherEntryRemote objects managed by the model. |
669 | + * The entries are identified by their LauncherEntryRemote::AppUri property. |
670 | + */ |
671 | +guint |
672 | +LauncherEntryRemoteModel::Size () |
673 | +{ |
674 | + return g_hash_table_size (_entries_by_uri); |
675 | +} |
676 | + |
677 | +/** |
678 | + * Return a pointer to a LauncherEntryRemote if there is one for app_uri, |
679 | + * otherwise NULL. The returned object should not be freed. |
680 | + * |
681 | + * App Uris look like application://$desktop_file_id, where desktop_file_id |
682 | + * is the base name of the .desktop file for the application including the |
683 | + * .desktop extension. Eg. application://firefox.desktop. |
684 | + */ |
685 | +LauncherEntryRemote* |
686 | +LauncherEntryRemoteModel::LookupByUri (const gchar *app_uri) |
687 | +{ |
688 | + gpointer entry; |
689 | + |
690 | + g_return_val_if_fail (app_uri != NULL, NULL); |
691 | + |
692 | + entry = g_hash_table_lookup (_entries_by_uri, app_uri); |
693 | + return static_cast<LauncherEntryRemote *> (entry); |
694 | +} |
695 | + |
696 | +/** |
697 | + * Return a pointer to a LauncherEntryRemote if there is one for desktop_id, |
698 | + * otherwise NULL. The returned object should not be freed. |
699 | + * |
700 | + * The desktop id is the base name of the .desktop file for the application |
701 | + * including the .desktop extension. Eg. firefox.desktop. |
702 | + */ |
703 | +LauncherEntryRemote* |
704 | +LauncherEntryRemoteModel::LookupByDesktopId (const gchar *desktop_id) |
705 | +{ |
706 | + LauncherEntryRemote *entry; |
707 | + gchar *app_uri; |
708 | + |
709 | + g_return_val_if_fail (desktop_id != NULL, NULL); |
710 | + |
711 | + app_uri = g_strconcat ("application://", desktop_id, NULL); |
712 | + entry = LookupByUri (app_uri); |
713 | + g_free (app_uri); |
714 | + |
715 | + return entry; |
716 | +} |
717 | + |
718 | +/** |
719 | + * Return a pointer to a LauncherEntryRemote if there is one for |
720 | + * desktop_file_path, otherwise NULL. The returned object should not be freed. |
721 | + */ |
722 | +LauncherEntryRemote* |
723 | +LauncherEntryRemoteModel::LookupByDesktopFile (const gchar *desktop_file_path) |
724 | +{ |
725 | + LauncherEntryRemote *entry; |
726 | + gchar *desktop_id, *app_uri; |
727 | + |
728 | + g_return_val_if_fail (desktop_file_path != NULL, NULL); |
729 | + |
730 | + desktop_id = g_path_get_basename (desktop_file_path); |
731 | + app_uri = g_strconcat ("application://", desktop_id, NULL); |
732 | + entry = LookupByUri (app_uri); |
733 | + g_free (app_uri); |
734 | + g_free (desktop_id); |
735 | + |
736 | + return entry; |
737 | +} |
738 | + |
739 | +/** |
740 | + * Get a list of all application URIs which have registered with the launcher |
741 | + * API. The returned GList should be freed with g_list_free(), but the URIs |
742 | + * should not be changed or freed. |
743 | + */ |
744 | +GList* |
745 | +LauncherEntryRemoteModel::GetUris () |
746 | +{ |
747 | + return (GList*) g_hash_table_get_keys (_entries_by_uri); |
748 | +} |
749 | + |
750 | +/** |
751 | + * Add or update a remote launcher entry. |
752 | + * |
753 | + * If 'entry' has a floating reference it will be consumed. |
754 | + */ |
755 | +void |
756 | +LauncherEntryRemoteModel::AddEntry (LauncherEntryRemote *entry) |
757 | +{ |
758 | + LauncherEntryRemote *existing_entry; |
759 | + |
760 | + g_return_if_fail (entry != NULL); |
761 | + |
762 | + entry->SinkReference (); |
763 | + |
764 | + existing_entry = LookupByUri (entry->AppUri ()); |
765 | + if (existing_entry != NULL) |
766 | + { |
767 | + existing_entry->Update (entry); |
768 | + entry->UnReference (); |
769 | + } |
770 | + else |
771 | + { |
772 | + /* The ref on entry will be removed by the hash table itself */ |
773 | + g_hash_table_insert (_entries_by_uri, g_strdup (entry->AppUri ()), entry); |
774 | + entry_added.emit (entry); |
775 | + } |
776 | +} |
777 | + |
778 | +/** |
779 | + * Add or update a remote launcher entry. |
780 | + * |
781 | + * If 'entry' has a floating reference it will be consumed. |
782 | + */ |
783 | +void |
784 | +LauncherEntryRemoteModel::RemoveEntry (LauncherEntryRemote *entry) |
785 | +{ |
786 | + g_return_if_fail (entry != NULL); |
787 | + |
788 | + /* We need a temp ref on entry to keep it alive during signal emission */ |
789 | + entry->SinkReference (); |
790 | + |
791 | + if (g_hash_table_remove (_entries_by_uri, entry->AppUri ())) |
792 | + { |
793 | + entry_removed.emit (entry); |
794 | + } |
795 | + |
796 | + entry->UnReference (); |
797 | +} |
798 | + |
799 | +static void |
800 | +on_launcher_entry_signal_received (GDBusConnection *connection, |
801 | + const gchar *sender_name, |
802 | + const gchar *object_path, |
803 | + const gchar *interface_name, |
804 | + const gchar *signal_name, |
805 | + GVariant *parameters, |
806 | + gpointer user_data) |
807 | +{ |
808 | + LauncherEntryRemoteModel *self; |
809 | + LauncherEntryRemote *entry; |
810 | + |
811 | + self = static_cast<LauncherEntryRemoteModel *> (user_data); |
812 | + |
813 | + if (parameters == NULL) |
814 | + { |
815 | + g_warning ("Received DBus signal '%s.%s' with empty payload from %s", |
816 | + interface_name, signal_name, sender_name); |
817 | + return; |
818 | + } |
819 | + |
820 | + if (g_strcmp0 (signal_name, "Update") == 0) |
821 | + { |
822 | + if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(sa{sv})"))) |
823 | + { |
824 | + g_warning ("Received 'com.canonical.Unity.LauncherEntry.Update' with" |
825 | + " illegal payload signature '%s'. Expected '(sa{sv})'.", |
826 | + g_variant_get_type_string (parameters)); |
827 | + return; |
828 | + } |
829 | + |
830 | + if (sender_name == NULL) |
831 | + { |
832 | + g_critical ("Received 'com.canonical.Unity.LauncherEntry.Update' from" |
833 | + " an undefined sender. This may happen if you are trying " |
834 | + "to run Unity on a p2p DBus connection."); |
835 | + return; |
836 | + } |
837 | + |
838 | + entry = new LauncherEntryRemote (sender_name, parameters); |
839 | + self->AddEntry (entry); // consumes floating ref on entry |
840 | + } |
841 | + else |
842 | + { |
843 | + g_warning ("Unknown signal '%s.%s' from %s", |
844 | + interface_name, signal_name, sender_name); |
845 | + } |
846 | + |
847 | + |
848 | +} |
849 | + |
850 | +static void |
851 | +nux_object_destroy_notify (nux::Object *obj) |
852 | +{ |
853 | + if (G_LIKELY (obj != NULL)) |
854 | + obj->UnReference(); |
855 | +} |
856 | |
857 | === added file 'src/LauncherEntryRemoteModel.h' |
858 | --- src/LauncherEntryRemoteModel.h 1970-01-01 00:00:00 +0000 |
859 | +++ src/LauncherEntryRemoteModel.h 2011-02-01 13:19:16 +0000 |
860 | @@ -0,0 +1,56 @@ |
861 | +// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- |
862 | +/* |
863 | + * Copyright (C) 2011 Canonical Ltd |
864 | + * |
865 | + * This program is free software: you can redistribute it and/or modify |
866 | + * it under the terms of the GNU General Public License version 3 as |
867 | + * published by the Free Software Foundation. |
868 | + * |
869 | + * This program is distributed in the hope that it will be useful, |
870 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
871 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
872 | + * GNU General Public License for more details. |
873 | + * |
874 | + * You should have received a copy of the GNU General Public License |
875 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
876 | + * |
877 | + * Authored by: Mikkel Kamstrup Erlandsen <mikkel.kamstrup@canonical.com> |
878 | + */ |
879 | + |
880 | +#ifndef LAUNCHER_ENTRY_REMOTE_MODEL_H |
881 | +#define LAUNCHER_ENTRY_REMOTE_MODEL_H |
882 | + |
883 | +#include <glib.h> |
884 | +#include <sigc++/sigc++.h> |
885 | + |
886 | +#include "LauncherEntryRemote.h" |
887 | + |
888 | +class LauncherEntryRemoteModel : public sigc::trackable |
889 | +{ |
890 | + |
891 | +public: |
892 | + |
893 | + static LauncherEntryRemoteModel* GetDefault (); |
894 | + |
895 | + guint Size (); |
896 | + LauncherEntryRemote* LookupByUri (const gchar *app_uri); |
897 | + LauncherEntryRemote* LookupByDesktopId (const gchar *desktop_id); |
898 | + LauncherEntryRemote* LookupByDesktopFile (const gchar *desktop_file_path); |
899 | + GList* GetUris (); |
900 | + |
901 | + void AddEntry (LauncherEntryRemote *entry); |
902 | + void RemoveEntry (LauncherEntryRemote *entry); |
903 | + |
904 | + sigc::signal<void, LauncherEntryRemote *> entry_added; |
905 | + sigc::signal<void, LauncherEntryRemote *> entry_removed; |
906 | + |
907 | +private: |
908 | + LauncherEntryRemoteModel(); |
909 | + ~LauncherEntryRemoteModel(); |
910 | + |
911 | + GDBusConnection *_conn; |
912 | + guint _launcher_entry_dbus_signal_id; |
913 | + GHashTable *_entries_by_uri; |
914 | +}; |
915 | + |
916 | +#endif // LAUNCHER_ENTRY_REMOTE_MODEL_H |
Forgot to add - you need lp:libunity trunk (as of today) for the client side libs if you wanna have a play