Merge lp:~unity-team/unity/launcher-api into lp:unity

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
Reviewer Review Type Date Requested Status
Jason Smith (community) Approve
Review via email: mp+48152@code.launchpad.net

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 LauncherEntryRemotes with LauncherIcons
 * In LauncherController remove the LauncherEntryRemotes from the LauncherEntryRemoteModel when we see apps drop off
 * 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 :

Forgot to add - you need lp:libunity trunk (as of today) for the client side libs if you wanna have a play

Revision history for this message
Jason Smith (jassmith) wrote :

+1

review: Approve

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