[gnome-shell] ShellAppSystem: Support loading a .desktop file directly



commit 9bd22dc033caabb086621c0deaec0cc854254ed2
Author: Colin Walters <walters verbum org>
Date:   Wed Aug 5 11:27:06 2009 -0400

    ShellAppSystem: Support loading a .desktop file directly
    
    Previously, ShellAppSystem only loaded (and cached) the set of
    .desktop files from applications.menu and settings.menu, using
    the gnome-menus library.  The ShellAppInfo structure was
    a "hidden typedef" for GMenuTreeEntry.
    
    But we need to support loading an arbitrary .desktop file.  Thus,
    refactor the ShellAppInfo into a real struct, with a refcount,
    and allow it to point to either a GMenuTreeEntry or a GKeyFile.
    
    Also, in the case where we fail to lookup an icon for an
    application, ensure we return a 0 opacity texture.

 js/ui/appDisplay.js    |    4 +-
 js/ui/widget.js        |    2 +-
 src/shell-app-system.c |  226 +++++++++++++++++++++++++++++++++++++++++-------
 src/shell-app-system.h |   14 ++--
 4 files changed, 206 insertions(+), 40 deletions(-)
---
diff --git a/js/ui/appDisplay.js b/js/ui/appDisplay.js
index 1558f71..5ec1a4b 100644
--- a/js/ui/appDisplay.js
+++ b/js/ui/appDisplay.js
@@ -278,7 +278,7 @@ AppDisplay.prototype = {
     _getMostUsed: function() {
         let context = "";
         return this._appMonitor.get_most_used_apps(context, 30).map(Lang.bind(this, function (id) {
-            return this._appSystem.lookup_app(id);
+            return this._appSystem.lookup_cached_app(id);
         })).filter(function (e) { return e != null });
     },
 
@@ -774,7 +774,7 @@ AppWell.prototype = {
         let result = [];
         for (let i = 0; i < appIds.length; i++) {
             let id = appIds[i];
-            let app = this._appSystem.lookup_app(id);
+            let app = this._appSystem.lookup_cached_app(id);
             if (!app)
                 continue;
             result.push(app);
diff --git a/js/ui/widget.js b/js/ui/widget.js
index b332657..b33028a 100644
--- a/js/ui/widget.js
+++ b/js/ui/widget.js
@@ -318,7 +318,7 @@ AppsWidget.prototype = {
         let appSystem = Shell.AppSystem.get_default();
         let apps = appSystem.get_favorites();
         for (let i = 0; i < apps.length; i++) {
-            let app = appSystem.lookup_app(apps[i]);
+            let app = appSystem.lookup_cached_app(apps[i]);
             if (!app)
                 continue;
             this.addItem(new AppsWidgetInfo(app));
diff --git a/src/shell-app-system.c b/src/shell-app-system.c
index cee5fc8..3ac35b3 100644
--- a/src/shell-app-system.c
+++ b/src/shell-app-system.c
@@ -54,16 +54,80 @@ static void reread_favorite_apps (ShellAppSystem *system);
 
 G_DEFINE_TYPE(ShellAppSystem, shell_app_system, G_TYPE_OBJECT);
 
+typedef enum {
+  SHELL_APP_INFO_TYPE_ENTRY,
+  SHELL_APP_INFO_TYPE_DESKTOP_FILE
+} ShellAppInfoType;
+
+struct _ShellAppInfo {
+  ShellAppInfoType type;
+
+  /* We need this for two reasons.  First, GKeyFile doesn't have a refcount.
+   * http://bugzilla.gnome.org/show_bug.cgi?id=590808
+   *
+   * But more generally we'll always need it so we know when to free this
+   * structure (short of weak references on each item).
+   */
+  guint refcount;
+
+  GMenuTreeItem *entry;
+
+  GKeyFile *keyfile;
+  char *keyfile_path;
+};
+
 ShellAppInfo*
 shell_app_info_ref (ShellAppInfo *info)
 {
-  return gmenu_tree_item_ref ((GMenuTreeItem*)info);
+  info->refcount++;
+  return info;
 }
 
 void
 shell_app_info_unref (ShellAppInfo *info)
 {
-  gmenu_tree_item_unref ((GMenuTreeItem *)info);
+  if (--info->refcount > 0)
+    return;
+  switch (info->type)
+  {
+  case SHELL_APP_INFO_TYPE_ENTRY:
+    gmenu_tree_item_unref (info->entry);
+    break;
+  case SHELL_APP_INFO_TYPE_DESKTOP_FILE:
+    g_key_file_free (info->keyfile);
+    g_free (info->keyfile_path);
+    break;
+  }
+  g_slice_free (ShellAppInfo, info);
+}
+
+static ShellAppInfo *
+shell_app_info_new_from_tree_item (GMenuTreeItem *item)
+{
+  ShellAppInfo *info;
+
+  if (!item)
+    return NULL;
+
+  info = g_slice_alloc (sizeof (ShellAppInfo));
+  info->type = SHELL_APP_INFO_TYPE_ENTRY;
+  info->refcount = 1;
+  info->entry = gmenu_tree_item_ref (item);
+  return info;
+}
+
+static ShellAppInfo *
+shell_app_info_new_from_keyfile_take_ownership (GKeyFile   *keyfile,
+                                                const char *path)
+{
+  ShellAppInfo *info;
+
+  info = g_slice_alloc (sizeof (ShellAppInfo));
+  info->type = SHELL_APP_INFO_TYPE_DESKTOP_FILE;
+  info->refcount = 1;
+  info->keyfile = keyfile;
+  info->keyfile_path = g_strdup (path);
+  return info;
 }
 
 static gpointer
@@ -237,7 +301,8 @@ gather_entries_recurse (ShellAppSystem     *monitor,
         {
           case GMENU_TREE_ITEM_ENTRY:
             {
-              apps = g_slist_prepend (apps, item);
+              ShellAppInfo *app = shell_app_info_new_from_tree_item (item);
+              apps = g_slist_prepend (apps, app);
             }
             break;
           case GMENU_TREE_ITEM_DIRECTORY:
@@ -245,12 +310,11 @@ gather_entries_recurse (ShellAppSystem     *monitor,
               GMenuTreeDirectory *dir = (GMenuTreeDirectory*)item;
               apps = gather_entries_recurse (monitor, apps, dir);
             }
-            gmenu_tree_item_unref (item);
             break;
           default:
-            gmenu_tree_item_unref (item);
             break;
         }
+      gmenu_tree_item_unref (item);
     }
 
   g_slist_free (contents);
@@ -285,7 +349,7 @@ cache_by_id (ShellAppSystem *self, GSList *apps, gboolean ref)
     {
       ShellAppInfo *info = iter->data;
       if (ref)
-        gmenu_tree_item_ref ((GMenuTreeItem*) info);
+        shell_app_info_ref (info);
       /* the name is owned by the info itself */
       g_hash_table_insert (self->priv->app_id_to_app, (char*)shell_app_info_get_id (info),
                            info);
@@ -557,14 +621,52 @@ shell_app_system_remove_favorite (ShellAppSystem *system, const char *id)
  * Return value: (transfer full): The #ShellAppInfo for id, or %NULL if none
  */
 ShellAppInfo *
-shell_app_system_lookup_app (ShellAppSystem *self, const char *id)
+shell_app_system_lookup_cached_app (ShellAppSystem *self, const char *id)
+{
+  ShellAppInfo *info;
+
+  info = g_hash_table_lookup (self->priv->app_id_to_app, id);
+  if (info)
+    shell_app_info_ref (info);
+  return info;
+}
+
+ShellAppInfo *
+shell_app_system_load_from_desktop_file (ShellAppSystem   *system,
+                                         const char       *filename,
+                                         GError          **error)
 {
-  GMenuTreeEntry *entry;
+  ShellAppInfo *appinfo;
+  GKeyFile *keyfile;
+  char *full_path = NULL;
+  gboolean success;
+
+  keyfile = g_key_file_new ();
+
+  if (strchr (filename, '/') != NULL)
+    {
+      success = g_key_file_load_from_file (keyfile, filename, G_KEY_FILE_NONE, error);
+      full_path = g_strdup (filename);
+    }
+  else
+    {
+      char *app_path = g_build_filename ("applications", filename, NULL);
+      success = g_key_file_load_from_data_dirs (keyfile, app_path, &full_path,
+                                                G_KEY_FILE_NONE, error);
+      g_free (app_path);
+    }
+
+  if (!success)
+    {
+      g_key_file_free (keyfile);
+      g_free (full_path);
+      return NULL;
+    }
+
+  appinfo = shell_app_info_new_from_keyfile_take_ownership (keyfile, full_path);
+  g_free (full_path);
 
-  entry = g_hash_table_lookup (self->priv->app_id_to_app, id);
-  if (entry)
-    gmenu_tree_item_ref ((GMenuTreeItem*) entry);
-  return (ShellAppInfo*)entry;
+  return appinfo;
 }
 
 /**
@@ -584,7 +686,7 @@ shell_app_system_lookup_heuristic_basename (ShellAppSystem *system,
   char *tmpid;
   ShellAppInfo *result;
 
-  result = shell_app_system_lookup_app (system, name);
+  result = shell_app_system_lookup_cached_app (system, name);
   if (result != NULL)
     return result;
 
@@ -593,13 +695,13 @@ shell_app_system_lookup_heuristic_basename (ShellAppSystem *system,
    * prefix.  So try stripping them.
    */
   tmpid = g_strjoin ("", "gnome-", name, NULL);
-  result = shell_app_system_lookup_app (system, tmpid);
+  result = shell_app_system_lookup_cached_app (system, tmpid);
   g_free (tmpid);
   if (result != NULL)
     return result;
 
   tmpid = g_strjoin ("", "fedora-", name, NULL);
-  result = shell_app_system_lookup_app (system, tmpid);
+  result = shell_app_system_lookup_cached_app (system, tmpid);
   g_free (tmpid);
   if (result != NULL)
     return result;
@@ -610,37 +712,79 @@ shell_app_system_lookup_heuristic_basename (ShellAppSystem *system,
 const char *
 shell_app_info_get_id (ShellAppInfo *info)
 {
-  return gmenu_tree_entry_get_desktop_file_id ((GMenuTreeEntry*)info);
+  switch (info->type)
+  {
+    case SHELL_APP_INFO_TYPE_ENTRY:
+      return gmenu_tree_entry_get_desktop_file_id ((GMenuTreeEntry*)info->entry);
+    case SHELL_APP_INFO_TYPE_DESKTOP_FILE:
+      return info->keyfile_path;
+  }
+  g_assert_not_reached ();
+  return NULL;
 }
 
-const char *
+#define DESKTOP_ENTRY_GROUP "Desktop Entry"
+
+char *
 shell_app_info_get_name (ShellAppInfo *info)
 {
-  return gmenu_tree_entry_get_name ((GMenuTreeEntry*)info);
+  switch (info->type)
+  {
+    case SHELL_APP_INFO_TYPE_ENTRY:
+      return g_strdup (gmenu_tree_entry_get_name ((GMenuTreeEntry*)info->entry));
+    case SHELL_APP_INFO_TYPE_DESKTOP_FILE:
+      return g_key_file_get_locale_string (info->keyfile, DESKTOP_ENTRY_GROUP, "Name", NULL, NULL);
+  }
+  g_assert_not_reached ();
+  return NULL;
 }
 
-const char *
+char *
 shell_app_info_get_description (ShellAppInfo *info)
 {
-  return gmenu_tree_entry_get_comment ((GMenuTreeEntry*)info);
+  switch (info->type)
+  {
+    case SHELL_APP_INFO_TYPE_ENTRY:
+      return g_strdup (gmenu_tree_entry_get_comment ((GMenuTreeEntry*)info->entry));
+    case SHELL_APP_INFO_TYPE_DESKTOP_FILE:
+      return g_key_file_get_locale_string (info->keyfile, DESKTOP_ENTRY_GROUP, "Comment", NULL, NULL);
+  }
+  g_assert_not_reached ();
+  return NULL;
 }
 
-const char *
+char *
 shell_app_info_get_executable (ShellAppInfo *info)
 {
-  return gmenu_tree_entry_get_exec ((GMenuTreeEntry*)info);
+  switch (info->type)
+  {
+    case SHELL_APP_INFO_TYPE_ENTRY:
+      return g_strdup (gmenu_tree_entry_get_exec ((GMenuTreeEntry*)info->entry));
+    case SHELL_APP_INFO_TYPE_DESKTOP_FILE:
+      return g_key_file_get_string (info->keyfile, DESKTOP_ENTRY_GROUP, "Exec", NULL);
+  }
+  g_assert_not_reached ();
+  return NULL;
 }
 
-const char *
+char *
 shell_app_info_get_desktop_file_path (ShellAppInfo *info)
 {
-  return gmenu_tree_entry_get_desktop_file_path ((GMenuTreeEntry*)info);
+  switch (info->type)
+  {
+    case SHELL_APP_INFO_TYPE_ENTRY:
+      return g_strdup (gmenu_tree_entry_get_desktop_file_path ((GMenuTreeEntry*)info->entry));
+    case SHELL_APP_INFO_TYPE_DESKTOP_FILE:
+      return g_strdup (info->keyfile_path);;
+  }
+  g_assert_not_reached ();
+  return NULL;
 }
 
 GIcon *
 shell_app_info_get_icon (ShellAppInfo *info)
 {
-  const char *iconname;
+  char *iconname;
   GIcon *icon;
 
   /* This code adapted from gdesktopappinfo.c
@@ -649,7 +793,16 @@ shell_app_info_get_icon (ShellAppInfo *info)
    * LGPL
    */
 
-  iconname = gmenu_tree_entry_get_icon ((GMenuTreeEntry*)info);
+  switch (info->type)
+  {
+    case SHELL_APP_INFO_TYPE_ENTRY:
+      iconname = g_strdup (gmenu_tree_entry_get_icon ((GMenuTreeEntry*)info->entry));
+      break;
+    case SHELL_APP_INFO_TYPE_DESKTOP_FILE:
+      iconname = g_key_file_get_locale_string (info->keyfile, DESKTOP_ENTRY_GROUP, "Icon", NULL, NULL);
+      break;
+  }
+
   if (!iconname)
     return NULL;
 
@@ -677,6 +830,8 @@ shell_app_info_get_icon (ShellAppInfo *info)
       icon = g_themed_icon_new (tmp_name);
       g_free (tmp_name);
     }
+  g_free (iconname);
+
   return icon;
 }
 
@@ -689,7 +844,15 @@ shell_app_info_get_categories (ShellAppInfo *info)
 gboolean
 shell_app_info_get_is_nodisplay (ShellAppInfo *info)
 {
-  return gmenu_tree_entry_get_is_nodisplay ((GMenuTreeEntry*)info);
+  switch (info->type)
+  {
+    case SHELL_APP_INFO_TYPE_ENTRY:
+      return gmenu_tree_entry_get_is_nodisplay ((GMenuTreeEntry*)info);
+    case SHELL_APP_INFO_TYPE_DESKTOP_FILE:
+      return FALSE;
+  }
+  g_assert_not_reached ();
+  return TRUE;
 }
 
 /**
@@ -710,7 +873,7 @@ shell_app_info_create_icon_texture (ShellAppInfo *info, float size)
   if (!icon)
     {
       ret = clutter_texture_new ();
-      g_object_set (ret, "width", size, "height", size, NULL);
+      g_object_set (ret, "opacity", 0, "width", size, "height", size, NULL);
       return ret;
     }
 
@@ -734,7 +897,7 @@ shell_app_info_launch_full (ShellAppInfo *info,
                             GError      **error)
 {
   GDesktopAppInfo *gapp;
-  const char *filename;
+  char *filename;
   GdkAppLaunchContext *context;
   gboolean ret;
   ShellGlobal *global;
@@ -744,9 +907,10 @@ shell_app_info_launch_full (ShellAppInfo *info,
   if (startup_id)
     *startup_id = NULL;
 
-  filename = gmenu_tree_entry_get_desktop_file_path ((GMenuTreeEntry*) info);
-
+  filename = shell_app_info_get_desktop_file_path (info);
   gapp = g_desktop_app_info_new_from_filename (filename);
+  g_free (filename);
+
   if (!gapp)
     {
       g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "Not found");
diff --git a/src/shell-app-system.h b/src/shell-app-system.h
index 0ec6861..7691eaf 100644
--- a/src/shell-app-system.h
+++ b/src/shell-app-system.h
@@ -45,19 +45,19 @@ struct _ShellAppMenuEntry {
 
 GType shell_app_menu_entry_get_type (void);
 
-/* Hidden typedef for a GMenuTreeEntry */
 typedef struct _ShellAppInfo ShellAppInfo;
 
+#define SHELL_TYPE_APP_INFO (shell_app_info_get_type ())
 GType shell_app_info_get_type (void);
 
 ShellAppInfo* shell_app_info_ref (ShellAppInfo *info);
 void shell_app_info_unref (ShellAppInfo *info);
 
 const char *shell_app_info_get_id (ShellAppInfo *info);
-const char *shell_app_info_get_name (ShellAppInfo *info);
-const char *shell_app_info_get_description (ShellAppInfo *info);
-const char *shell_app_info_get_executable (ShellAppInfo *info);
-const char *shell_app_info_get_desktop_file_path (ShellAppInfo *info);
+char *shell_app_info_get_name (ShellAppInfo *info);
+char *shell_app_info_get_description (ShellAppInfo *info);
+char *shell_app_info_get_executable (ShellAppInfo *info);
+char *shell_app_info_get_desktop_file_path (ShellAppInfo *info);
 GIcon *shell_app_info_get_icon (ShellAppInfo *info);
 ClutterActor *shell_app_info_create_icon_texture (ShellAppInfo *info, float size);
 GSList *shell_app_info_get_categories (ShellAppInfo *info);
@@ -71,7 +71,9 @@ gboolean shell_app_info_launch_full (ShellAppInfo *info,
 gboolean shell_app_info_launch (ShellAppInfo *info,
                                 GError      **error);
 
-ShellAppInfo *shell_app_system_lookup_app (ShellAppSystem *system, const char *id);
+ShellAppInfo *shell_app_system_load_from_desktop_file (ShellAppSystem *system, const char *filename, GError **error);
+
+ShellAppInfo *shell_app_system_lookup_cached_app (ShellAppSystem *system, const char *id);
 
 ShellAppInfo *shell_app_system_lookup_heuristic_basename (ShellAppSystem *system, const char *id);
 



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]