Merge lp://qastaging/~indicator-applet-developers/libindicator/ubuntu into lp://qastaging/~ubuntu-desktop/libindicator/ubuntu

Proposed by Ted Gould
Status: Merged
Merged at revision: not available
Proposed branch: lp://qastaging/~indicator-applet-developers/libindicator/ubuntu
Merge into: lp://qastaging/~ubuntu-desktop/libindicator/ubuntu
Diff against target: 899 lines (+811/-2)
9 files modified
.bzrignore (+4/-0)
configure.ac (+4/-2)
debian/changelog (+7/-0)
libindicator/Makefile.am (+2/-0)
libindicator/indicator-desktop-shortcuts.c (+523/-0)
libindicator/indicator-desktop-shortcuts.h (+74/-0)
tests/Makefile.am (+36/-0)
tests/test-desktop-shortcuts.c (+137/-0)
tests/test-well-formed.desktop (+24/-0)
To merge this branch: bzr merge lp://qastaging/~indicator-applet-developers/libindicator/ubuntu
Reviewer Review Type Date Requested Status
Sebastien Bacher Pending
Review via email: mp+19626@code.qastaging.launchpad.net
To post a comment you must log in.
Revision history for this message
Ted Gould (ted) wrote :

0.3.3

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file '.bzrignore'
2--- .bzrignore 2010-01-15 21:16:30 +0000
3+++ .bzrignore 2010-02-18 16:14:13 +0000
4@@ -147,3 +147,7 @@
5 libindicator/indicator-object-marshal.h
6 libindicator/libindicator_la-indicator-object-marshal.lo
7 libindicator/stamp-marshal
8+libindicator/libindicator_la-indicator-desktop-shortcuts.lo
9+tests/test-desktop-shortcuts
10+tests/test-desktop-shortcuts-tester
11+tests/test-desktop-shortcuts-touch-test
12
13=== modified file 'configure.ac'
14--- configure.ac 2010-02-05 01:37:47 +0000
15+++ configure.ac 2010-02-18 16:14:13 +0000
16@@ -1,10 +1,10 @@
17
18-AC_INIT(libindicator, 0.3.2, ted@canonical.com)
19+AC_INIT(libindicator, 0.3.3, ted@canonical.com)
20
21 AC_PREREQ(2.53)
22
23 AM_CONFIG_HEADER(config.h)
24-AM_INIT_AUTOMAKE(libindicator, 0.3.2)
25+AM_INIT_AUTOMAKE(libindicator, 0.3.3)
26
27 AM_MAINTAINER_MODE
28 m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES])
29@@ -28,8 +28,10 @@
30
31 GTK_REQUIRED_VERSION=2.18
32 DBUS_REQUIRED_VERSION=0.76
33+GIO_UNIX_REQUIRED_VERSION=2.23
34
35 PKG_CHECK_MODULES(LIBINDICATOR, gtk+-2.0 >= $GTK_REQUIRED_VERSION
36+ gio-unix-2.0 >= $GIO_UNIX_REQUIRED_VERSION
37 dbus-glib-1 >= $DBUS_REQUIRED_VERSION)
38
39 AC_SUBST(LIBINDICATOR_CFLAGS)
40
41=== modified file 'debian/changelog'
42--- debian/changelog 2010-02-05 03:00:38 +0000
43+++ debian/changelog 2010-02-18 16:14:13 +0000
44@@ -1,3 +1,10 @@
45+libindicator (0.3.3-0ubuntu1~ppa1) lucid; urgency=low
46+
47+ * Upstream release 0.3.3
48+ * Adding new object for parsing desktop files for Shortcuts
49+
50+ -- Ted Gould <ted@ubuntu.com> Thu, 18 Feb 2010 10:11:21 -0600
51+
52 libindicator (0.3.2-0ubuntu1) lucid; urgency=low
53
54 * Upstream release 0.3.2
55
56=== modified file 'libindicator/Makefile.am'
57--- libindicator/Makefile.am 2010-01-15 21:16:30 +0000
58+++ libindicator/Makefile.am 2010-02-18 16:14:13 +0000
59@@ -10,6 +10,7 @@
60
61 indicator_headers = \
62 indicator.h \
63+ indicator-desktop-shortcuts.h \
64 indicator-object.h \
65 indicator-service.h \
66 indicator-service-manager.h
67@@ -24,6 +25,7 @@
68 $(indicator_headers) \
69 dbus-shared.h \
70 indicator-object.c \
71+ indicator-desktop-shortcuts.c \
72 indicator-object-marshal.h \
73 indicator-object-marshal.c \
74 indicator-service.c \
75
76=== added file 'libindicator/indicator-desktop-shortcuts.c'
77--- libindicator/indicator-desktop-shortcuts.c 1970-01-01 00:00:00 +0000
78+++ libindicator/indicator-desktop-shortcuts.c 2010-02-18 16:14:13 +0000
79@@ -0,0 +1,523 @@
80+/*
81+A small file to parse through the actions that are available
82+in the desktop file and making those easily usable.
83+
84+Copyright 2010 Canonical Ltd.
85+
86+Authors:
87+ Ted Gould <ted@canonical.com>
88+
89+This library is free software; you can redistribute it and/or
90+modify it under the terms of the GNU General Public License
91+version 3.0 as published by the Free Software Foundation.
92+
93+This library is distributed in the hope that it will be useful,
94+but WITHOUT ANY WARRANTY; without even the implied warranty of
95+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
96+GNU General Public License version 3.0 for more details.
97+
98+You should have received a copy of the GNU General Public
99+License along with this library. If not, see
100+<http://www.gnu.org/licenses/>.
101+*/
102+
103+#ifdef HAVE_CONFIG_H
104+#include "config.h"
105+#endif
106+
107+#include <gio/gdesktopappinfo.h>
108+#include "indicator-desktop-shortcuts.h"
109+
110+#define GROUP_SUFFIX "Shortcut Group"
111+#define SHORTCUTS_KEY "X-Ayatana-Desktop-Shortcuts"
112+
113+#define PROP_DESKTOP_FILE_S "desktop-file"
114+#define PROP_IDENTITY_S "identity"
115+
116+typedef struct _IndicatorDesktopShortcutsPrivate IndicatorDesktopShortcutsPrivate;
117+struct _IndicatorDesktopShortcutsPrivate {
118+ GKeyFile * keyfile;
119+ gchar * identity;
120+ GArray * nicks;
121+};
122+
123+enum {
124+ PROP_0,
125+ PROP_DESKTOP_FILE,
126+ PROP_IDENTITY
127+};
128+
129+#define INDICATOR_DESKTOP_SHORTCUTS_GET_PRIVATE(o) \
130+ (G_TYPE_INSTANCE_GET_PRIVATE ((o), INDICATOR_TYPE_DESKTOP_SHORTCUTS, IndicatorDesktopShortcutsPrivate))
131+
132+static void indicator_desktop_shortcuts_class_init (IndicatorDesktopShortcutsClass *klass);
133+static void indicator_desktop_shortcuts_init (IndicatorDesktopShortcuts *self);
134+static void indicator_desktop_shortcuts_dispose (GObject *object);
135+static void indicator_desktop_shortcuts_finalize (GObject *object);
136+static void set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec);
137+static void get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec);
138+static void parse_keyfile (IndicatorDesktopShortcuts * ids);
139+static gboolean should_show (GKeyFile * keyfile, const gchar * group, const gchar * identity);
140+
141+G_DEFINE_TYPE (IndicatorDesktopShortcuts, indicator_desktop_shortcuts, G_TYPE_OBJECT);
142+
143+/* Build up the class */
144+static void
145+indicator_desktop_shortcuts_class_init (IndicatorDesktopShortcutsClass *klass)
146+{
147+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
148+
149+ g_type_class_add_private (klass, sizeof (IndicatorDesktopShortcutsPrivate));
150+
151+ object_class->dispose = indicator_desktop_shortcuts_dispose;
152+ object_class->finalize = indicator_desktop_shortcuts_finalize;
153+
154+ /* Property funcs */
155+ object_class->set_property = set_property;
156+ object_class->get_property = get_property;
157+
158+ g_object_class_install_property(object_class, PROP_DESKTOP_FILE,
159+ g_param_spec_string(PROP_DESKTOP_FILE_S,
160+ "The path of the desktop file to read",
161+ "A path to a desktop file that we'll look for shortcuts in.",
162+ NULL,
163+ G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY));
164+ g_object_class_install_property(object_class, PROP_IDENTITY,
165+ g_param_spec_string(PROP_IDENTITY_S,
166+ "The string that represents the identity that we're acting as.",
167+ "Used to process ShowIn and NotShownIn fields of the desktop shortcust to get the proper list.",
168+ NULL,
169+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY));
170+
171+ return;
172+}
173+
174+/* Initialize instance data */
175+static void
176+indicator_desktop_shortcuts_init (IndicatorDesktopShortcuts *self)
177+{
178+ IndicatorDesktopShortcutsPrivate * priv = INDICATOR_DESKTOP_SHORTCUTS_GET_PRIVATE(self);
179+
180+ priv->keyfile = NULL;
181+ priv->identity = NULL;
182+ priv->nicks = g_array_new(TRUE, TRUE, sizeof(gchar *));
183+
184+ return;
185+}
186+
187+/* Clear object references */
188+static void
189+indicator_desktop_shortcuts_dispose (GObject *object)
190+{
191+ IndicatorDesktopShortcutsPrivate * priv = INDICATOR_DESKTOP_SHORTCUTS_GET_PRIVATE(object);
192+
193+ if (priv->keyfile) {
194+ g_key_file_free(priv->keyfile);
195+ priv->keyfile = NULL;
196+ }
197+
198+ G_OBJECT_CLASS (indicator_desktop_shortcuts_parent_class)->dispose (object);
199+ return;
200+}
201+
202+/* Free all memory */
203+static void
204+indicator_desktop_shortcuts_finalize (GObject *object)
205+{
206+ IndicatorDesktopShortcutsPrivate * priv = INDICATOR_DESKTOP_SHORTCUTS_GET_PRIVATE(object);
207+
208+ if (priv->identity != NULL) {
209+ g_free(priv->identity);
210+ priv->identity = NULL;
211+ }
212+
213+ if (priv->nicks != NULL) {
214+ gint i;
215+ for (i = 0; i < priv->nicks->len; i++) {
216+ gchar * nick = g_array_index(priv->nicks, gchar *, i);
217+ g_free(nick);
218+ }
219+ g_array_free(priv->nicks, TRUE);
220+ priv->nicks = NULL;
221+ }
222+
223+ G_OBJECT_CLASS (indicator_desktop_shortcuts_parent_class)->finalize (object);
224+ return;
225+}
226+
227+/* Sets one of the two properties we have, only at construction though */
228+static void
229+set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec)
230+{
231+ g_return_if_fail(INDICATOR_IS_DESKTOP_SHORTCUTS(object));
232+ IndicatorDesktopShortcutsPrivate * priv = INDICATOR_DESKTOP_SHORTCUTS_GET_PRIVATE(object);
233+
234+ switch(prop_id) {
235+ case PROP_DESKTOP_FILE: {
236+ GError * error = NULL;
237+ GKeyFile * keyfile = g_key_file_new();
238+ g_key_file_load_from_file(keyfile, g_value_get_string(value), G_KEY_FILE_NONE, &error);
239+
240+ if (error != NULL) {
241+ g_warning("Unable to load keyfile from file '%s': %s", g_value_get_string(value), error->message);
242+ g_error_free(error);
243+ g_key_file_free(keyfile);
244+ break;
245+ }
246+
247+ if (!g_key_file_has_key(keyfile, G_KEY_FILE_DESKTOP_GROUP, SHORTCUTS_KEY, NULL)) {
248+ g_warning("Keyfile from file '%s' does not have '" SHORTCUTS_KEY "' key", g_value_get_string(value));
249+ g_key_file_free(keyfile);
250+ break;
251+ }
252+
253+ priv->keyfile = keyfile;
254+ parse_keyfile(INDICATOR_DESKTOP_SHORTCUTS(object));
255+ break;
256+ }
257+ case PROP_IDENTITY:
258+ if (priv->identity != NULL) {
259+ g_warning("Identity already set to '%s' and trying to set it to '%s'.", priv->identity, g_value_get_string(value));
260+ return;
261+ }
262+ priv->identity = g_value_dup_string(value);
263+ parse_keyfile(INDICATOR_DESKTOP_SHORTCUTS(object));
264+ break;
265+ /* *********************** */
266+ default:
267+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
268+ break;
269+ }
270+
271+ return;
272+}
273+
274+/* Gets either the desktop file our the identity. */
275+static void
276+get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec)
277+{
278+ g_return_if_fail(INDICATOR_IS_DESKTOP_SHORTCUTS(object));
279+ IndicatorDesktopShortcutsPrivate * priv = INDICATOR_DESKTOP_SHORTCUTS_GET_PRIVATE(object);
280+
281+ switch(prop_id) {
282+ case PROP_IDENTITY:
283+ g_value_set_string(value, priv->identity);
284+ break;
285+ /* *********************** */
286+ default:
287+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
288+ break;
289+ }
290+
291+ return;
292+}
293+
294+/* Checks to see if we can, and if we can it goes through
295+ and parses the keyfile entries. */
296+static void
297+parse_keyfile (IndicatorDesktopShortcuts * ids)
298+{
299+ IndicatorDesktopShortcutsPrivate * priv = INDICATOR_DESKTOP_SHORTCUTS_GET_PRIVATE(ids);
300+
301+ if (priv->keyfile == NULL) {
302+ return;
303+ }
304+
305+ if (priv->identity == NULL) {
306+ return;
307+ }
308+
309+ /* Okay, we've got everything we need. Let's get it on! */
310+ gint i;
311+ gsize num_nicks = 0;
312+ gchar ** nicks = g_key_file_get_string_list(priv->keyfile, G_KEY_FILE_DESKTOP_GROUP, SHORTCUTS_KEY, &num_nicks, NULL);
313+
314+ /* If there is an error from get_string_list num_nicks should still
315+ be zero, so this loop will drop out. */
316+ for (i = 0; i < num_nicks; i++) {
317+ /* g_debug("Looking at group nick %s", nicks[i]); */
318+ gchar * groupname = g_strdup_printf("%s " GROUP_SUFFIX, nicks[i]);
319+ if (!g_key_file_has_group(priv->keyfile, groupname)) {
320+ g_warning("Unable to find group '%s'", groupname);
321+ g_free(groupname);
322+ continue;
323+ }
324+
325+ if (!should_show(priv->keyfile, G_KEY_FILE_DESKTOP_GROUP, priv->identity)) {
326+ g_free(groupname);
327+ continue;
328+ }
329+
330+ if (!should_show(priv->keyfile, groupname, priv->identity)) {
331+ g_free(groupname);
332+ continue;
333+ }
334+
335+ gchar * nickalloc = g_strdup(nicks[i]);
336+ g_array_append_val(priv->nicks, nickalloc);
337+ }
338+
339+ if (nicks != NULL) {
340+ g_strfreev(nicks);
341+ }
342+
343+ return;
344+}
345+
346+/* Checks the ONLY_SHOW_IN and NOT_SHOW_IN keys for a group to
347+ see if we should be showing ourselves. */
348+static gboolean
349+should_show (GKeyFile * keyfile, const gchar * group, const gchar * identity)
350+{
351+ /* If there is a list of OnlyShowIn entries we need to check
352+ to see if we're in that list. If not, we drop this nick */
353+ if (g_key_file_has_key(keyfile, group, G_KEY_FILE_DESKTOP_KEY_ONLY_SHOW_IN, NULL)) {
354+ gint j;
355+ gsize num_only = 0;
356+ gchar ** onlies = g_key_file_get_string_list(keyfile, group, G_KEY_FILE_DESKTOP_KEY_ONLY_SHOW_IN, &num_only, NULL);
357+
358+ for (j = 0; j < num_only; j++) {
359+ if (g_strcmp0(onlies[j], identity) == 0) {
360+ break;
361+ }
362+ }
363+
364+ if (onlies != NULL) {
365+ g_strfreev(onlies);
366+ }
367+
368+ if (j == num_only) {
369+ return FALSE;
370+ }
371+ }
372+
373+ /* If there is a NotShowIn entry we need to make sure that we're
374+ not in that list. If we are, we need to drop out. */
375+ if (g_key_file_has_key(keyfile, group, G_KEY_FILE_DESKTOP_KEY_NOT_SHOW_IN, NULL)) {
376+ gint j;
377+ gsize num_not = 0;
378+ gchar ** nots = g_key_file_get_string_list(keyfile, group, G_KEY_FILE_DESKTOP_KEY_NOT_SHOW_IN, &num_not, NULL);
379+
380+ for (j = 0; j < num_not; j++) {
381+ if (g_strcmp0(nots[j], identity) == 0) {
382+ break;
383+ }
384+ }
385+
386+ if (nots != NULL) {
387+ g_strfreev(nots);
388+ }
389+
390+ if (j != num_not) {
391+ return FALSE;
392+ }
393+ }
394+
395+ return TRUE;
396+}
397+
398+/* Looks through the nicks ot see if this one is in the list,
399+ and thus valid to use. */
400+static gboolean
401+is_valid_nick (gchar ** list, const gchar * nick)
402+{
403+ if (*list == NULL)
404+ return FALSE;
405+ /* g_debug("Checking Nick: %s", list[0]); */
406+ if (g_strcmp0(list[0], nick) == 0)
407+ return TRUE;
408+ return is_valid_nick(&list[1], nick);
409+}
410+
411+/* API */
412+
413+/**
414+ indicator_desktop_shortcuts_new:
415+ @file: The desktop file that would be opened to
416+ find the actions.
417+ @identity: This is a string that represents the identity
418+ that should be used in searching those actions. It
419+ relates to the ShowIn and NotShownIn properties.
420+
421+ This function creates the basic object. It involves opening
422+ the file and parsing it. It could potentially block on IO. At
423+ the end of the day you'll have a fully functional object.
424+
425+ Return value: A new #IndicatorDesktopShortcuts object.
426+*/
427+IndicatorDesktopShortcuts *
428+indicator_desktop_shortcuts_new (const gchar * file, const gchar * identity)
429+{
430+ GObject * obj = g_object_new(INDICATOR_TYPE_DESKTOP_SHORTCUTS,
431+ PROP_DESKTOP_FILE_S, file,
432+ PROP_IDENTITY_S, identity,
433+ NULL);
434+ return INDICATOR_DESKTOP_SHORTCUTS(obj);
435+}
436+
437+/**
438+ indicator_desktop_shortcuts_get_nicks:
439+ @ids: The #IndicatorDesktopShortcuts object to look in
440+
441+ Give you the list of commands that are available for this desktop
442+ file given the identity that was passed in at creation. This will
443+ filter out the various items in the desktop file. These nicks can
444+ then be used as keys for working with the desktop file.
445+
446+ Return value: A #NULL terminated list of strings. This memory
447+ is managed by the @ids object.
448+*/
449+const gchar **
450+indicator_desktop_shortcuts_get_nicks (IndicatorDesktopShortcuts * ids)
451+{
452+ g_return_val_if_fail(INDICATOR_IS_DESKTOP_SHORTCUTS(ids), NULL);
453+ IndicatorDesktopShortcutsPrivate * priv = INDICATOR_DESKTOP_SHORTCUTS_GET_PRIVATE(ids);
454+ return (const gchar **)priv->nicks->data;
455+}
456+
457+/**
458+ indicator_desktop_shortcuts_nick_get_name:
459+ @ids: The #IndicatorDesktopShortcuts object to look in
460+ @nick: Which command that we're referencing.
461+
462+ This function looks in a desktop file for a nick to find the
463+ user visible name for that shortcut. The @nick parameter
464+ should be gotten from #indicator_desktop_shortcuts_get_nicks
465+ though it's not required that the exact memory location
466+ be the same.
467+
468+ Return value: A user visible string for the shortcut or
469+ #NULL on error.
470+*/
471+gchar *
472+indicator_desktop_shortcuts_nick_get_name (IndicatorDesktopShortcuts * ids, const gchar * nick)
473+{
474+ g_return_val_if_fail(INDICATOR_IS_DESKTOP_SHORTCUTS(ids), NULL);
475+ IndicatorDesktopShortcutsPrivate * priv = INDICATOR_DESKTOP_SHORTCUTS_GET_PRIVATE(ids);
476+
477+ g_return_val_if_fail(priv->keyfile != NULL, NULL);
478+ g_return_val_if_fail(is_valid_nick((gchar **)priv->nicks->data, nick), NULL);
479+
480+ gchar * groupheader = g_strdup_printf("%s " GROUP_SUFFIX, nick);
481+ if (!g_key_file_has_group(priv->keyfile, groupheader)) {
482+ g_warning("The group for nick '%s' doesn't exist anymore.", nick);
483+ g_free(groupheader);
484+ return NULL;
485+ }
486+
487+ if (!g_key_file_has_key(priv->keyfile, groupheader, G_KEY_FILE_DESKTOP_KEY_NAME, NULL)) {
488+ g_warning("No name available for nick '%s'", nick);
489+ g_free(groupheader);
490+ return NULL;
491+ }
492+
493+ gchar * name = g_key_file_get_locale_string(priv->keyfile,
494+ groupheader,
495+ G_KEY_FILE_DESKTOP_KEY_NAME,
496+ NULL,
497+ NULL);
498+
499+ g_free(groupheader);
500+
501+ return name;
502+}
503+
504+/**
505+ indicator_desktop_shortcuts_nick_exec:
506+ @ids: The #IndicatorDesktopShortcuts object to look in
507+ @nick: Which command that we're referencing.
508+
509+ Here we take a @nick and try and execute the action that is
510+ associated with it. The @nick parameter should be gotten
511+ from #indicator_desktop_shortcuts_get_nicks though it's not
512+ required that the exact memory location be the same.
513+
514+ Return value: #TRUE on success or #FALSE on error.
515+*/
516+gboolean
517+indicator_desktop_shortcuts_nick_exec (IndicatorDesktopShortcuts * ids, const gchar * nick)
518+{
519+ GError * error = NULL;
520+
521+ g_return_val_if_fail(INDICATOR_IS_DESKTOP_SHORTCUTS(ids), FALSE);
522+ IndicatorDesktopShortcutsPrivate * priv = INDICATOR_DESKTOP_SHORTCUTS_GET_PRIVATE(ids);
523+
524+ g_return_val_if_fail(priv->keyfile != NULL, FALSE);
525+ g_return_val_if_fail(is_valid_nick((gchar **)priv->nicks->data, nick), FALSE);
526+
527+ gchar * groupheader = g_strdup_printf("%s " GROUP_SUFFIX, nick);
528+ if (!g_key_file_has_group(priv->keyfile, groupheader)) {
529+ g_warning("The group for nick '%s' doesn't exist anymore.", nick);
530+ g_free(groupheader);
531+ return FALSE;
532+ }
533+
534+ if (!g_key_file_has_key(priv->keyfile, groupheader, G_KEY_FILE_DESKTOP_KEY_NAME, NULL)) {
535+ g_warning("No name available for nick '%s'", nick);
536+ g_free(groupheader);
537+ return FALSE;
538+ }
539+
540+ if (!g_key_file_has_key(priv->keyfile, groupheader, G_KEY_FILE_DESKTOP_KEY_EXEC, NULL)) {
541+ g_warning("No exec available for nick '%s'", nick);
542+ g_free(groupheader);
543+ return FALSE;
544+ }
545+
546+ /* Grab the name and the exec entries out of our current group */
547+ gchar * name = g_key_file_get_locale_string(priv->keyfile,
548+ groupheader,
549+ G_KEY_FILE_DESKTOP_KEY_NAME,
550+ NULL,
551+ NULL);
552+
553+ gchar * exec = g_key_file_get_locale_string(priv->keyfile,
554+ groupheader,
555+ G_KEY_FILE_DESKTOP_KEY_EXEC,
556+ NULL,
557+ NULL);
558+
559+ /* Build a new desktop file with the name and exec in the desktop
560+ group. We have to do this with data as apparently there isn't
561+ and add_group function in g_key_file. Go figure. */
562+ gchar * desktopdata = g_strdup_printf("[" G_KEY_FILE_DESKTOP_GROUP "]\n"
563+ G_KEY_FILE_DESKTOP_KEY_TYPE "=" G_KEY_FILE_DESKTOP_TYPE_APPLICATION "\n"
564+ G_KEY_FILE_DESKTOP_KEY_NAME "=%s\n"
565+ G_KEY_FILE_DESKTOP_KEY_EXEC "=%s\n",
566+ name, exec);
567+
568+
569+ g_free(name); g_free(exec);
570+ /* g_debug("Desktop file: \n%s", desktopdata); */
571+
572+ GKeyFile * launcher = g_key_file_new();
573+ g_key_file_load_from_data(launcher, desktopdata, -1, G_KEY_FILE_NONE, &error);
574+ g_free(desktopdata);
575+
576+ if (error != NULL) {
577+ g_warning("Unable to build desktop keyfile for executing shortcut '%s': %s", nick, error->message);
578+ g_error_free(error);
579+ return FALSE;
580+ }
581+
582+ GDesktopAppInfo * appinfo = g_desktop_app_info_new_from_keyfile(launcher);
583+ if (appinfo == NULL) {
584+ g_warning("Unable to build Desktop App info (unknown)");
585+ g_key_file_free(launcher);
586+ return FALSE;
587+ }
588+
589+ gboolean launched = g_app_info_launch(G_APP_INFO(appinfo), NULL, NULL, &error);
590+
591+ if (error != NULL) {
592+ g_warning("Unable to launch file from nick '%s': %s", nick, error->message);
593+ g_error_free(error);
594+ g_key_file_free(launcher);
595+ return FALSE;
596+ }
597+
598+ g_object_unref(appinfo);
599+ g_key_file_free(launcher);
600+
601+ return launched;
602+}
603
604=== added file 'libindicator/indicator-desktop-shortcuts.h'
605--- libindicator/indicator-desktop-shortcuts.h 1970-01-01 00:00:00 +0000
606+++ libindicator/indicator-desktop-shortcuts.h 2010-02-18 16:14:13 +0000
607@@ -0,0 +1,74 @@
608+/*
609+A small file to parse through the actions that are available
610+in the desktop file and making those easily usable.
611+
612+Copyright 2010 Canonical Ltd.
613+
614+Authors:
615+ Ted Gould <ted@canonical.com>
616+
617+This library is free software; you can redistribute it and/or
618+modify it under the terms of the GNU General Public License
619+version 3.0 as published by the Free Software Foundation.
620+
621+This library is distributed in the hope that it will be useful,
622+but WITHOUT ANY WARRANTY; without even the implied warranty of
623+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
624+GNU General Public License version 3.0 for more details.
625+
626+You should have received a copy of the GNU General Public
627+License along with this library. If not, see
628+<http://www.gnu.org/licenses/>.
629+*/
630+
631+#ifndef __INDICATOR_DESKTOP_SHORTCUTS_H__
632+#define __INDICATOR_DESKTOP_SHORTCUTS_H__
633+
634+#include <glib.h>
635+#include <glib-object.h>
636+
637+G_BEGIN_DECLS
638+
639+#define INDICATOR_TYPE_DESKTOP_SHORTCUTS (indicator_desktop_shortcuts_get_type ())
640+#define INDICATOR_DESKTOP_SHORTCUTS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), INDICATOR_TYPE_DESKTOP_SHORTCUTS, IndicatorDesktopShortcuts))
641+#define INDICATOR_DESKTOP_SHORTCUTS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), INDICATOR_TYPE_DESKTOP_SHORTCUTS, IndicatorDesktopShortcutsClass))
642+#define INDICATOR_IS_DESKTOP_SHORTCUTS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), INDICATOR_TYPE_DESKTOP_SHORTCUTS))
643+#define INDICATOR_IS_DESKTOP_SHORTCUTS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), INDICATOR_TYPE_DESKTOP_SHORTCUTS))
644+#define INDICATOR_DESKTOP_SHORTCUTS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), INDICATOR_TYPE_DESKTOP_SHORTCUTS, IndicatorDesktopShortcutsClass))
645+
646+typedef struct _IndicatorDesktopShortcuts IndicatorDesktopShortcuts;
647+typedef struct _IndicatorDesktopShortcutsClass IndicatorDesktopShortcutsClass;
648+
649+/**
650+ IndicatorDesktopShortcutsClass:
651+ @parent_class: Space for #GObjectClass
652+
653+ The vtable for our precious #IndicatorDesktopShortcutsClass.
654+*/
655+struct _IndicatorDesktopShortcutsClass {
656+ GObjectClass parent_class;
657+};
658+
659+/**
660+ IndicatorDesktopShortcuts:
661+ @parent: The parent data from #GObject
662+
663+ The public data for an instance of the class
664+ #IndicatorDesktopShortcuts.
665+*/
666+struct _IndicatorDesktopShortcuts {
667+ GObject parent;
668+};
669+
670+GType indicator_desktop_shortcuts_get_type (void);
671+IndicatorDesktopShortcuts * indicator_desktop_shortcuts_new (const gchar * file,
672+ const gchar * identity);
673+const gchar ** indicator_desktop_shortcuts_get_nicks (IndicatorDesktopShortcuts * ids);
674+gchar * indicator_desktop_shortcuts_nick_get_name (IndicatorDesktopShortcuts * ids,
675+ const gchar * nick);
676+gboolean indicator_desktop_shortcuts_nick_exec (IndicatorDesktopShortcuts * ids,
677+ const gchar * nick);
678+
679+G_END_DECLS
680+
681+#endif
682
683=== modified file 'tests/Makefile.am'
684--- tests/Makefile.am 2010-02-02 20:10:23 +0000
685+++ tests/Makefile.am 2010-02-18 16:14:13 +0000
686@@ -34,6 +34,42 @@
687 -lindicator
688
689 #############################
690+# Test Desktop Shortcuts
691+#############################
692+
693+check_PROGRAMS += test-desktop-shortcuts
694+
695+test_desktop_shortcuts_SOURCES = \
696+ test-desktop-shortcuts.c
697+
698+test_desktop_shortcuts_CFLAGS = \
699+ -Wall -Werror \
700+ -DSRCDIR="\"$(srcdir)\"" \
701+ $(LIBINDICATOR_CFLAGS) -I$(top_srcdir) \
702+ -DBUILD_DIR="\"$(builddir)\""
703+
704+test_desktop_shortcuts_LDADD = \
705+ $(LIBINDICATOR_LIBS) \
706+ -L$(top_builddir)/libindicator/.libs \
707+ -lindicator
708+
709+DS_XML_REPORT = desktop-shortcuts-check-results.xml
710+DS_HTML_REPORT = desktop-shortcuts-check-results.html
711+
712+test-desktop-shortcuts-tester: test-desktop-shortcuts Makefile.am
713+ @echo "#!/bin/bash" > $@
714+ @echo $(XVFB_RUN) >> $@
715+ @echo gtester -k --verbose -o=$(XML_REPORT) ./test-desktop-shortcuts >> $@
716+ @chmod +x $@
717+
718+TESTS += test-desktop-shortcuts-tester
719+DISTCLEANFILES += test-desktop-shortcuts-tester \
720+ test-desktop-shortcuts-touch-test \
721+ $(DS_XML_REPORT) \
722+ $(DS_HTML_REPORT)
723+EXTRA_DIST += test-well-formed.desktop
724+
725+#############################
726 # Dummy Indicator Blank
727 #############################
728
729
730=== added file 'tests/test-desktop-shortcuts.c'
731--- tests/test-desktop-shortcuts.c 1970-01-01 00:00:00 +0000
732+++ tests/test-desktop-shortcuts.c 2010-02-18 16:14:13 +0000
733@@ -0,0 +1,137 @@
734+#include <gtk/gtk.h>
735+#include "libindicator/indicator-desktop-shortcuts.h"
736+
737+/* Basic object creation and destruction. Stop big
738+ f*** ups here. */
739+void
740+test_desktop_shortcuts_creation (void)
741+{
742+
743+ IndicatorDesktopShortcuts * ids = indicator_desktop_shortcuts_new(SRCDIR "/test-well-formed.desktop", "France");
744+ g_assert(ids != NULL);
745+
746+ g_object_add_weak_pointer(G_OBJECT(ids), (gpointer *)&ids);
747+ g_object_unref(G_OBJECT(ids));
748+
749+ g_assert(ids == NULL);
750+ return;
751+}
752+
753+/* Tests that the NotShowIn the desktop group is watched
754+ for */
755+void
756+test_desktop_shortcuts_globalnoshow (void)
757+{
758+
759+ IndicatorDesktopShortcuts * ids = indicator_desktop_shortcuts_new(SRCDIR "/test-well-formed.desktop", "Germany");
760+ g_assert(ids != NULL);
761+
762+ const gchar ** nicks = indicator_desktop_shortcuts_get_nicks(ids);
763+ g_assert(nicks[0] == NULL);
764+
765+ g_object_unref(ids);
766+
767+ return;
768+}
769+
770+gboolean
771+nicks_contains (const gchar ** nicks, const gchar * search)
772+{
773+ if (nicks[0] == NULL)
774+ return FALSE;
775+ if (g_strcmp0(nicks[0], search) == 0)
776+ return TRUE;
777+ return nicks_contains(&nicks[1], search);
778+}
779+
780+/* Checking that the local show OnlyIn works. */
781+void
782+test_desktop_shortcuts_localfilter (void)
783+{
784+ IndicatorDesktopShortcuts * ids = indicator_desktop_shortcuts_new(SRCDIR "/test-well-formed.desktop", "France");
785+ g_assert(ids != NULL);
786+
787+ const gchar ** nicks = indicator_desktop_shortcuts_get_nicks(ids);
788+
789+ g_assert(nicks_contains(nicks, "bob"));
790+ g_assert(nicks_contains(nicks, "alvin"));
791+ g_assert(!nicks_contains(nicks, "jim"));
792+
793+ g_object_unref(ids);
794+
795+ return;
796+}
797+
798+/* Nick names -- checks to see they all have names */
799+void
800+test_desktop_shortcuts_nicknames (void)
801+{
802+ IndicatorDesktopShortcuts * ids = indicator_desktop_shortcuts_new(SRCDIR "/test-well-formed.desktop", "France");
803+ g_assert(ids != NULL);
804+
805+ const gchar ** nicks = indicator_desktop_shortcuts_get_nicks(ids);
806+ gint i = 0;
807+ while (nicks[i] != NULL) {
808+ gchar * expectedstr = g_strdup_printf("%s's shortcut", nicks[i]);
809+ gchar * name = indicator_desktop_shortcuts_nick_get_name(ids, nicks[i]);
810+ g_assert(name != NULL);
811+
812+ gboolean same = (g_strcmp0(expectedstr, name) == 0);
813+
814+ g_free(name);
815+ g_free(expectedstr);
816+
817+ g_assert(same);
818+
819+ i++;
820+ }
821+
822+
823+ g_object_unref(ids);
824+
825+ return;
826+}
827+
828+/* Try executing a shortcut which will touch a file */
829+void
830+test_desktop_shortcuts_launch (void)
831+{
832+ IndicatorDesktopShortcuts * ids = indicator_desktop_shortcuts_new(SRCDIR "/test-well-formed.desktop", "TouchTest");
833+ g_assert(ids != NULL);
834+
835+ const gchar ** nicks = indicator_desktop_shortcuts_get_nicks(ids);
836+ g_assert(nicks_contains(nicks, "touch"));
837+
838+ g_assert(indicator_desktop_shortcuts_nick_exec(ids, "touch"));
839+ g_assert(g_file_test(BUILD_DIR "/test-desktop-shortcuts-touch-test", G_FILE_TEST_EXISTS));
840+
841+ g_object_unref(ids);
842+
843+ return;
844+}
845+
846+/* Build our test suite */
847+void
848+test_desktop_shortcuts_suite (void)
849+{
850+ g_test_add_func ("/libindicator/desktopshortcuts/creation", test_desktop_shortcuts_creation);
851+ g_test_add_func ("/libindicator/desktopshortcuts/globalnosho", test_desktop_shortcuts_globalnoshow);
852+ g_test_add_func ("/libindicator/desktopshortcuts/nicknames", test_desktop_shortcuts_nicknames);
853+ g_test_add_func ("/libindicator/desktopshortcuts/launch", test_desktop_shortcuts_launch);
854+
855+ return;
856+}
857+
858+int
859+main (int argc, char ** argv)
860+{
861+ g_type_init ();
862+ g_test_init (&argc, &argv, NULL);
863+ gtk_init(&argc, &argv);
864+
865+ test_desktop_shortcuts_suite();
866+
867+ g_log_set_always_fatal(G_LOG_LEVEL_CRITICAL);
868+
869+ return g_test_run();
870+}
871
872=== added file 'tests/test-well-formed.desktop'
873--- tests/test-well-formed.desktop 1970-01-01 00:00:00 +0000
874+++ tests/test-well-formed.desktop 2010-02-18 16:14:13 +0000
875@@ -0,0 +1,24 @@
876+[Desktop Entry]
877+Name=My Application
878+Exec=ls
879+NotShowIn=Germany
880+X-Ayatana-Desktop-Shortcuts=bob;alvin;jim;touch
881+
882+[bob Shortcut Group]
883+Name=bob's shortcut
884+Exec=ls bob
885+
886+[alvin Shortcut Group]
887+Name=alvin's shortcut
888+Exec=ls alvin
889+OnlyShowIn=France
890+
891+[jim Shortcut Group]
892+Name=Jim's shortcut
893+Exec=ls jim
894+NotShowIn=France
895+
896+[touch Shortcut Group]
897+Name=Touch Test
898+Exec=touch test-desktop-shortcuts-touch-test
899+OnlyShowIn=TouchTest

Subscribers

People subscribed via source and target branches

to all changes: