Back to index

libindicator  12.10.0
indicator-desktop-shortcuts.c
Go to the documentation of this file.
00001 /*
00002 A small file to parse through the actions that are available
00003 in the desktop file and making those easily usable.
00004 
00005 Copyright 2010 Canonical Ltd.
00006 
00007 Authors:
00008     Ted Gould <ted@canonical.com>
00009 
00010 This library is free software; you can redistribute it and/or
00011 modify it under the terms of the GNU General Public License
00012 version 3.0 as published by the Free Software Foundation.
00013 
00014 This library is distributed in the hope that it will be useful,
00015 but WITHOUT ANY WARRANTY; without even the implied warranty of
00016 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00017 GNU General Public License version 3.0 for more details.
00018 
00019 You should have received a copy of the GNU General Public
00020 License along with this library. If not, see
00021 <http://www.gnu.org/licenses/>.
00022 */
00023 
00024 #ifdef HAVE_CONFIG_H
00025 #include "config.h"
00026 #endif
00027 
00028 #include <gio/gdesktopappinfo.h>
00029 #include "indicator-desktop-shortcuts.h"
00030 
00031 #define ACTIONS_KEY               "Actions"
00032 #define ACTION_GROUP_PREFIX       "Desktop Action"
00033 
00034 #define OLD_GROUP_SUFFIX          "Shortcut Group"
00035 #define OLD_SHORTCUTS_KEY         "X-Ayatana-Desktop-Shortcuts"
00036 #define OLD_ENVIRON_KEY           "TargetEnvironment"
00037 
00038 #define PROP_DESKTOP_FILE_S   "desktop-file"
00039 #define PROP_IDENTITY_S       "identity"
00040 
00041 typedef enum _actions_t actions_t;
00042 enum _actions_t {
00043        ACTIONS_NONE,
00044        ACTIONS_XAYATANA,
00045        ACTIONS_DESKTOP_SPEC
00046 };
00047 
00048 typedef struct _IndicatorDesktopShortcutsPrivate IndicatorDesktopShortcutsPrivate;
00049 struct _IndicatorDesktopShortcutsPrivate {
00050        actions_t actions;
00051        GKeyFile * keyfile;
00052        gchar * identity;
00053        GArray * nicks;
00054        gchar * domain;
00055 };
00056 
00057 enum {
00058        PROP_0,
00059        PROP_DESKTOP_FILE,
00060        PROP_IDENTITY
00061 };
00062 
00063 #define INDICATOR_DESKTOP_SHORTCUTS_GET_PRIVATE(o) \
00064               (G_TYPE_INSTANCE_GET_PRIVATE ((o), INDICATOR_TYPE_DESKTOP_SHORTCUTS, IndicatorDesktopShortcutsPrivate))
00065 
00066 static void indicator_desktop_shortcuts_class_init (IndicatorDesktopShortcutsClass *klass);
00067 static void indicator_desktop_shortcuts_init       (IndicatorDesktopShortcuts *self);
00068 static void indicator_desktop_shortcuts_dispose    (GObject *object);
00069 static void indicator_desktop_shortcuts_finalize   (GObject *object);
00070 static void set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec);
00071 static void get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec);
00072 static void parse_keyfile (IndicatorDesktopShortcuts * ids);
00073 static gboolean should_show (GKeyFile * keyfile, const gchar * group, const gchar * identity, gboolean should_have_target);
00074 
00075 G_DEFINE_TYPE (IndicatorDesktopShortcuts, indicator_desktop_shortcuts, G_TYPE_OBJECT);
00076 
00077 /* Build up the class */
00078 static void
00079 indicator_desktop_shortcuts_class_init (IndicatorDesktopShortcutsClass *klass)
00080 {
00081        GObjectClass *object_class = G_OBJECT_CLASS (klass);
00082 
00083        g_type_class_add_private (klass, sizeof (IndicatorDesktopShortcutsPrivate));
00084 
00085        object_class->dispose = indicator_desktop_shortcuts_dispose;
00086        object_class->finalize = indicator_desktop_shortcuts_finalize;
00087 
00088        /* Property funcs */
00089        object_class->set_property = set_property;
00090        object_class->get_property = get_property;
00091 
00092        g_object_class_install_property(object_class, PROP_DESKTOP_FILE,
00093                                        g_param_spec_string(PROP_DESKTOP_FILE_S,
00094                                                            "The path of the desktop file to read",
00095                                                            "A path to a desktop file that we'll look for shortcuts in.",
00096                                                            NULL,
00097                                                            G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY));
00098        g_object_class_install_property(object_class, PROP_IDENTITY,
00099                                        g_param_spec_string(PROP_IDENTITY_S,
00100                                                            "The string that represents the identity that we're acting as.",
00101                                                            "Used to process ShowIn and NotShownIn fields of the desktop shortcust to get the proper list.",
00102                                                            NULL,
00103                                                            G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY));
00104 
00105        return;
00106 }
00107 
00108 /* Initialize instance data */
00109 static void
00110 indicator_desktop_shortcuts_init (IndicatorDesktopShortcuts *self)
00111 {
00112        IndicatorDesktopShortcutsPrivate * priv = INDICATOR_DESKTOP_SHORTCUTS_GET_PRIVATE(self);
00113 
00114        priv->keyfile = NULL;
00115        priv->identity = NULL;
00116        priv->domain = NULL;
00117        priv->nicks = g_array_new(TRUE, TRUE, sizeof(gchar *));
00118        priv->actions = ACTIONS_NONE;
00119 
00120        return;
00121 }
00122 
00123 /* Clear object references */
00124 static void
00125 indicator_desktop_shortcuts_dispose (GObject *object)
00126 {
00127        IndicatorDesktopShortcutsPrivate * priv = INDICATOR_DESKTOP_SHORTCUTS_GET_PRIVATE(object);
00128 
00129        if (priv->keyfile) {
00130               g_key_file_free(priv->keyfile);
00131               priv->keyfile = NULL;
00132        }
00133 
00134        G_OBJECT_CLASS (indicator_desktop_shortcuts_parent_class)->dispose (object);
00135        return;
00136 }
00137 
00138 /* Free all memory */
00139 static void
00140 indicator_desktop_shortcuts_finalize (GObject *object)
00141 {
00142        IndicatorDesktopShortcutsPrivate * priv = INDICATOR_DESKTOP_SHORTCUTS_GET_PRIVATE(object);
00143 
00144        if (priv->identity != NULL) {
00145               g_free(priv->identity);
00146               priv->identity = NULL;
00147        }
00148 
00149        if (priv->domain != NULL) {
00150               g_free(priv->domain);
00151               priv->domain = NULL;
00152        }
00153 
00154        if (priv->nicks != NULL) {
00155               gint i;
00156               for (i = 0; i < priv->nicks->len; i++) {
00157                      gchar * nick = g_array_index(priv->nicks, gchar *, i);
00158                      g_free(nick);
00159               }
00160               g_array_free(priv->nicks, TRUE);
00161               priv->nicks = NULL;
00162        }
00163 
00164        G_OBJECT_CLASS (indicator_desktop_shortcuts_parent_class)->finalize (object);
00165        return;
00166 }
00167 
00168 /* Sets one of the two properties we have, only at construction though */
00169 static void
00170 set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec)
00171 {
00172        g_return_if_fail(INDICATOR_IS_DESKTOP_SHORTCUTS(object));
00173        IndicatorDesktopShortcutsPrivate * priv = INDICATOR_DESKTOP_SHORTCUTS_GET_PRIVATE(object);
00174 
00175        switch(prop_id) {
00176        case PROP_DESKTOP_FILE: {
00177               if (priv->keyfile != NULL) {
00178                      g_key_file_free(priv->keyfile);
00179                      priv->keyfile = NULL;
00180                      priv->actions = ACTIONS_NONE;
00181               }
00182 
00183               GError * error = NULL;
00184               GKeyFile * keyfile = g_key_file_new();
00185               g_key_file_load_from_file(keyfile, g_value_get_string(value), G_KEY_FILE_NONE, &error);
00186 
00187               if (error != NULL) {
00188                      g_warning("Unable to load keyfile from file '%s': %s", g_value_get_string(value), error->message);
00189                      g_error_free(error);
00190                      g_key_file_free(keyfile);
00191                      break;
00192               }
00193 
00194               /* Always prefer the desktop spec if we can get it */
00195               if (priv->actions == ACTIONS_NONE && g_key_file_has_key(keyfile, G_KEY_FILE_DESKTOP_GROUP, ACTIONS_KEY, NULL)) {
00196                      priv->actions = ACTIONS_DESKTOP_SPEC;
00197               }
00198 
00199               /* But fallback if we can't */
00200               if (priv->actions == ACTIONS_NONE && g_key_file_has_key(keyfile, G_KEY_FILE_DESKTOP_GROUP, OLD_SHORTCUTS_KEY, NULL)) {
00201                      priv->actions = ACTIONS_XAYATANA;
00202                      g_warning("Desktop file '%s' is using a deprecated format for its actions that will be dropped soon.", g_value_get_string(value));
00203               }
00204 
00205               if (priv->actions == ACTIONS_NONE) {
00206                      g_key_file_free(keyfile);
00207                      break;
00208               }
00209 
00210               priv->keyfile = keyfile;
00211               parse_keyfile(INDICATOR_DESKTOP_SHORTCUTS(object));
00212               break;
00213        }
00214        case PROP_IDENTITY:
00215               if (priv->identity != NULL) {
00216                      g_warning("Identity already set to '%s' and trying to set it to '%s'.", priv->identity, g_value_get_string(value));
00217                      return;
00218               }
00219               priv->identity = g_value_dup_string(value);
00220               parse_keyfile(INDICATOR_DESKTOP_SHORTCUTS(object));
00221               break;
00222        /* *********************** */
00223        default:
00224               G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
00225               break;
00226        }
00227 
00228        return;
00229 }
00230 
00231 /* Gets either the desktop file our the identity. */
00232 static void
00233 get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec)
00234 {
00235        g_return_if_fail(INDICATOR_IS_DESKTOP_SHORTCUTS(object));
00236        IndicatorDesktopShortcutsPrivate * priv = INDICATOR_DESKTOP_SHORTCUTS_GET_PRIVATE(object);
00237 
00238        switch(prop_id) {
00239        case PROP_IDENTITY:
00240               g_value_set_string(value, priv->identity);
00241               break;
00242        /* *********************** */
00243        default:
00244               G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
00245               break;
00246        }
00247 
00248        return;
00249 }
00250 
00251 /* Checks to see if we can, and if we can it goes through
00252    and parses the keyfile entries. */
00253 static void
00254 parse_keyfile (IndicatorDesktopShortcuts * ids)
00255 {
00256        IndicatorDesktopShortcutsPrivate * priv = INDICATOR_DESKTOP_SHORTCUTS_GET_PRIVATE(ids);
00257 
00258        if (priv->keyfile == NULL) {
00259               return;
00260        }
00261 
00262        if (priv->identity == NULL) {
00263               return;
00264        }
00265 
00266        /* Remove a previous translation domain if we had one
00267           from a previously parsed file. */
00268        if (priv->domain != NULL) {
00269               g_free(priv->domain);
00270               priv->domain = NULL;
00271        }
00272 
00273        /* Check to see if there is a custom translation domain that
00274           we should take into account. */
00275        if (priv->domain == NULL &&
00276                      g_key_file_has_key(priv->keyfile, G_KEY_FILE_DESKTOP_GROUP, "X-GNOME-Gettext-Domain", NULL)) {
00277               priv->domain = g_key_file_get_string(priv->keyfile, G_KEY_FILE_DESKTOP_GROUP, "X-GNOME-Gettext-Domain", NULL);
00278        }
00279 
00280        if (priv->domain == NULL &&
00281                      g_key_file_has_key(priv->keyfile, G_KEY_FILE_DESKTOP_GROUP, "X-Ubuntu-Gettext-Domain", NULL)) {
00282               priv->domain = g_key_file_get_string(priv->keyfile, G_KEY_FILE_DESKTOP_GROUP, "X-Ubuntu-Gettext-Domain", NULL);
00283        }
00284 
00285        /* We need to figure out what we're looking for and what we want to
00286           look for in the rest of the file */
00287        const gchar * list_name = NULL;
00288        const gchar * group_format = NULL;
00289        gboolean should_have_target = FALSE;
00290 
00291        switch (priv->actions) {
00292        case ACTIONS_NONE:
00293               /* None, let's just get outta here */
00294               return;
00295        case ACTIONS_XAYATANA:
00296               list_name = OLD_SHORTCUTS_KEY;
00297               group_format = "%s " OLD_GROUP_SUFFIX;
00298               should_have_target = TRUE;
00299               break;
00300        case ACTIONS_DESKTOP_SPEC:
00301               list_name = ACTIONS_KEY;
00302               group_format = ACTION_GROUP_PREFIX " %s";
00303               should_have_target = FALSE;
00304               break;
00305        default:
00306               g_assert_not_reached();
00307               return;
00308        }
00309 
00310        /* Okay, we've got everything we need.  Let's get it on! */
00311        gint i;
00312        gsize num_nicks = 0;
00313        gchar ** nicks = g_key_file_get_string_list(priv->keyfile, G_KEY_FILE_DESKTOP_GROUP, list_name, &num_nicks, NULL);
00314 
00315        /* If there is an error from get_string_list num_nicks should still
00316           be zero, so this loop will drop out. */
00317        for (i = 0; i < num_nicks; i++) {
00318               /* g_debug("Looking at group nick %s", nicks[i]); */
00319               gchar * groupname = g_strdup_printf(group_format, nicks[i]);
00320               if (!g_key_file_has_group(priv->keyfile, groupname)) {
00321                      g_warning("Unable to find group '%s'", groupname);
00322                      g_free(groupname);
00323                      continue;
00324               }
00325 
00326               if (!should_show(priv->keyfile, G_KEY_FILE_DESKTOP_GROUP, priv->identity, FALSE)) {
00327                      g_free(groupname);
00328                      continue;
00329               }
00330 
00331               if (!should_show(priv->keyfile, groupname, priv->identity, should_have_target)) {
00332                      g_free(groupname);
00333                      continue;
00334               }
00335 
00336               gchar * nickalloc = g_strdup(nicks[i]);
00337               g_array_append_val(priv->nicks, nickalloc);
00338               g_free(groupname);
00339        }
00340 
00341        if (nicks != NULL) {
00342               g_strfreev(nicks);
00343        }
00344 
00345        return;
00346 }
00347 
00348 /* Checks the ONLY_SHOW_IN and NOT_SHOW_IN keys for a group to
00349    see if we should be showing ourselves. */
00350 static gboolean
00351 should_show (GKeyFile * keyfile, const gchar * group, const gchar * identity, gboolean should_have_target)
00352 {
00353        if (should_have_target && g_key_file_has_key(keyfile, group, OLD_ENVIRON_KEY, NULL)) {
00354               /* If we've got this key, we're going to return here and not
00355                  process the deprecated keys. */
00356               gint j;
00357               gsize num_env = 0;
00358               gchar ** envs = g_key_file_get_string_list(keyfile, group, OLD_ENVIRON_KEY, &num_env, NULL);
00359 
00360               for (j = 0; j < num_env; j++) {
00361                      if (g_strcmp0(envs[j], identity) == 0) {
00362                             break;
00363                      }
00364               }
00365 
00366               if (envs != NULL) {
00367                      g_strfreev(envs);
00368               }
00369 
00370               if (j == num_env) {
00371                      return FALSE;
00372               }
00373               return TRUE;  
00374        }
00375 
00376        /* If there is a list of OnlyShowIn entries we need to check
00377           to see if we're in that list.  If not, we drop this nick */
00378        if (g_key_file_has_key(keyfile, group, G_KEY_FILE_DESKTOP_KEY_ONLY_SHOW_IN, NULL)) {
00379               gint j;
00380               gsize num_only = 0;
00381               gchar ** onlies = g_key_file_get_string_list(keyfile, group, G_KEY_FILE_DESKTOP_KEY_ONLY_SHOW_IN, &num_only, NULL);
00382 
00383               for (j = 0; j < num_only; j++) {
00384                      if (g_strcmp0(onlies[j], identity) == 0) {
00385                             break;
00386                      }
00387               }
00388 
00389               if (onlies != NULL) {
00390                      g_strfreev(onlies);
00391               }
00392 
00393               if (j == num_only) {
00394                      return FALSE;
00395               }
00396        }
00397 
00398        /* If there is a NotShowIn entry we need to make sure that we're
00399           not in that list.  If we are, we need to drop out. */
00400        if (g_key_file_has_key(keyfile, group, G_KEY_FILE_DESKTOP_KEY_NOT_SHOW_IN, NULL)) {
00401               gint j;
00402               gsize num_not = 0;
00403               gchar ** nots = g_key_file_get_string_list(keyfile, group, G_KEY_FILE_DESKTOP_KEY_NOT_SHOW_IN, &num_not, NULL);
00404 
00405               for (j = 0; j < num_not; j++) {
00406                      if (g_strcmp0(nots[j], identity) == 0) {
00407                             break;
00408                      }
00409               }
00410 
00411               if (nots != NULL) {
00412                      g_strfreev(nots);
00413               }
00414 
00415               if (j != num_not) {
00416                      return FALSE;
00417               }
00418        }
00419 
00420        return TRUE;
00421 }
00422 
00423 /* Looks through the nicks to see if this one is in the list,
00424    and thus valid to use. */
00425 static gboolean
00426 is_valid_nick (gchar ** list, const gchar * nick)
00427 {
00428        if (*list == NULL)
00429               return FALSE;
00430        /* g_debug("Checking Nick: %s", list[0]); */
00431        if (g_strcmp0(list[0], nick) == 0)
00432               return TRUE;
00433        return is_valid_nick(&list[1], nick);
00434 }
00435 
00436 /* API */
00437 
00452 IndicatorDesktopShortcuts *
00453 indicator_desktop_shortcuts_new (const gchar * file, const gchar * identity)
00454 {
00455        GObject * obj = g_object_new(INDICATOR_TYPE_DESKTOP_SHORTCUTS,
00456                                     PROP_DESKTOP_FILE_S, file,
00457                                     PROP_IDENTITY_S, identity,
00458                                     NULL);
00459        return INDICATOR_DESKTOP_SHORTCUTS(obj);
00460 }
00461 
00474 const gchar **
00475 indicator_desktop_shortcuts_get_nicks (IndicatorDesktopShortcuts * ids)
00476 {
00477        g_return_val_if_fail(INDICATOR_IS_DESKTOP_SHORTCUTS(ids), NULL);
00478        IndicatorDesktopShortcutsPrivate * priv = INDICATOR_DESKTOP_SHORTCUTS_GET_PRIVATE(ids);
00479        return (const gchar **)priv->nicks->data;
00480 }
00481 
00496 gchar *
00497 indicator_desktop_shortcuts_nick_get_name (IndicatorDesktopShortcuts * ids, const gchar * nick)
00498 {
00499        g_return_val_if_fail(INDICATOR_IS_DESKTOP_SHORTCUTS(ids), NULL);
00500        IndicatorDesktopShortcutsPrivate * priv = INDICATOR_DESKTOP_SHORTCUTS_GET_PRIVATE(ids);
00501 
00502        g_return_val_if_fail(priv->actions != ACTIONS_NONE, NULL);
00503        g_return_val_if_fail(priv->keyfile != NULL, NULL);
00504        g_return_val_if_fail(is_valid_nick((gchar **)priv->nicks->data, nick), NULL);
00505 
00506        const gchar * group_format = NULL;
00507 
00508        switch (priv->actions) {
00509        case ACTIONS_XAYATANA:
00510               group_format = "%s " OLD_GROUP_SUFFIX;
00511               break;
00512        case ACTIONS_DESKTOP_SPEC:
00513               group_format = ACTION_GROUP_PREFIX " %s";
00514               break;
00515        default:
00516               g_assert_not_reached();
00517               return NULL;
00518        }
00519 
00520        gchar * groupheader = g_strdup_printf(group_format, nick);
00521        if (!g_key_file_has_group(priv->keyfile, groupheader)) {
00522               g_warning("The group for nick '%s' doesn't exist anymore.", nick);
00523               g_free(groupheader);
00524               return NULL;
00525        }
00526 
00527        if (!g_key_file_has_key(priv->keyfile, groupheader, G_KEY_FILE_DESKTOP_KEY_NAME, NULL)) {
00528               g_warning("No name available for nick '%s'", nick);
00529               g_free(groupheader);
00530               return NULL;
00531        }
00532 
00533        gchar * name = NULL;
00534        gchar * keyvalue = g_key_file_get_string(priv->keyfile,
00535                                                 groupheader,
00536                                                 G_KEY_FILE_DESKTOP_KEY_NAME,
00537                                                 NULL);
00538        gchar * localeval = g_key_file_get_locale_string(priv->keyfile,
00539                                                   groupheader,
00540                                                   G_KEY_FILE_DESKTOP_KEY_NAME,
00541                                                   NULL,
00542                                                   NULL);
00543        g_free(groupheader);
00544 
00545        if (priv->domain != NULL && g_strcmp0(keyvalue, localeval) == 0) {
00546               name = g_strdup(g_dgettext(priv->domain, keyvalue));
00547               g_free(localeval);
00548        } else {
00549               name = localeval;
00550        }
00551 
00552        g_free(keyvalue);
00553 
00554        return name;
00555 }
00556 
00569 gboolean
00570 indicator_desktop_shortcuts_nick_exec (IndicatorDesktopShortcuts * ids, const gchar * nick)
00571 {
00572        GError * error = NULL;
00573 
00574        g_return_val_if_fail(INDICATOR_IS_DESKTOP_SHORTCUTS(ids), FALSE);
00575        IndicatorDesktopShortcutsPrivate * priv = INDICATOR_DESKTOP_SHORTCUTS_GET_PRIVATE(ids);
00576 
00577        g_return_val_if_fail(priv->actions != ACTIONS_NONE, FALSE);
00578        g_return_val_if_fail(priv->keyfile != NULL, FALSE);
00579        g_return_val_if_fail(is_valid_nick((gchar **)priv->nicks->data, nick), FALSE);
00580 
00581        const gchar * group_format = NULL;
00582 
00583        switch (priv->actions) {
00584        case ACTIONS_XAYATANA:
00585               group_format = "%s " OLD_GROUP_SUFFIX;
00586               break;
00587        case ACTIONS_DESKTOP_SPEC:
00588               group_format = ACTION_GROUP_PREFIX " %s";
00589               break;
00590        default:
00591               g_assert_not_reached();
00592               return FALSE;
00593        }
00594 
00595        gchar * groupheader = g_strdup_printf(group_format, nick);
00596        if (!g_key_file_has_group(priv->keyfile, groupheader)) {
00597               g_warning("The group for nick '%s' doesn't exist anymore.", nick);
00598               g_free(groupheader);
00599               return FALSE;
00600        }
00601 
00602        if (!g_key_file_has_key(priv->keyfile, groupheader, G_KEY_FILE_DESKTOP_KEY_NAME, NULL)) {
00603               g_warning("No name available for nick '%s'", nick);
00604               g_free(groupheader);
00605               return FALSE;
00606        }
00607 
00608        if (!g_key_file_has_key(priv->keyfile, groupheader, G_KEY_FILE_DESKTOP_KEY_EXEC, NULL)) {
00609               g_warning("No exec available for nick '%s'", nick);
00610               g_free(groupheader);
00611               return FALSE;
00612        }
00613 
00614        /* Grab the name and the exec entries out of our current group */
00615        gchar * name = g_key_file_get_locale_string(priv->keyfile,
00616                                                    groupheader,
00617                                                    G_KEY_FILE_DESKTOP_KEY_NAME,
00618                                                    NULL,
00619                                                    NULL);
00620 
00621        gchar * exec = g_key_file_get_locale_string(priv->keyfile,
00622                                                    groupheader,
00623                                                    G_KEY_FILE_DESKTOP_KEY_EXEC,
00624                                                    NULL,
00625                                                    NULL);
00626 
00627        /* Build a new desktop file with the name and exec in the desktop
00628           group.  We have to do this with data as apparently there isn't
00629           and add_group function in g_key_file.  Go figure. */
00630        gchar * desktopdata = g_strdup_printf("[" G_KEY_FILE_DESKTOP_GROUP "]\n"
00631                                              G_KEY_FILE_DESKTOP_KEY_TYPE "=" G_KEY_FILE_DESKTOP_TYPE_APPLICATION "\n"
00632                                              G_KEY_FILE_DESKTOP_KEY_NAME "=%s\n"
00633                                              G_KEY_FILE_DESKTOP_KEY_EXEC "=%s\n",
00634                                              name, exec);
00635        
00636 
00637        g_free(name); g_free(exec);
00638        /* g_debug("Desktop file: \n%s", desktopdata); */
00639 
00640        GKeyFile * launcher = g_key_file_new();
00641        g_key_file_load_from_data(launcher, desktopdata, -1, G_KEY_FILE_NONE, &error);
00642        g_free(desktopdata);
00643 
00644        if (error != NULL) {
00645               g_warning("Unable to build desktop keyfile for executing shortcut '%s': %s", nick, error->message);
00646               g_error_free(error);
00647               return FALSE;
00648        }
00649 
00650        GDesktopAppInfo * appinfo = g_desktop_app_info_new_from_keyfile(launcher);
00651        if (appinfo == NULL) {
00652               g_warning("Unable to build Desktop App info (unknown)");
00653               g_key_file_free(launcher);
00654               return FALSE;
00655        }
00656 
00657        gboolean launched = g_app_info_launch(G_APP_INFO(appinfo), NULL, NULL, &error);
00658 
00659        if (error != NULL) {
00660               g_warning("Unable to launch file from nick '%s': %s", nick, error->message);
00661               g_error_free(error);
00662               g_key_file_free(launcher);
00663               return FALSE;
00664        }
00665 
00666        g_object_unref(appinfo);
00667        g_key_file_free(launcher);
00668 
00669        return launched;
00670 }