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.
This commit is contained in:
Colin Walters 2009-08-05 11:27:06 -04:00
parent 5064d873bb
commit 9bd22dc033
4 changed files with 206 additions and 40 deletions

View File

@ -278,7 +278,7 @@ AppDisplay.prototype = {
_getMostUsed: function() { _getMostUsed: function() {
let context = ""; let context = "";
return this._appMonitor.get_most_used_apps(context, 30).map(Lang.bind(this, function (id) { 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 }); })).filter(function (e) { return e != null });
}, },
@ -774,7 +774,7 @@ AppWell.prototype = {
let result = []; let result = [];
for (let i = 0; i < appIds.length; i++) { for (let i = 0; i < appIds.length; i++) {
let id = appIds[i]; let id = appIds[i];
let app = this._appSystem.lookup_app(id); let app = this._appSystem.lookup_cached_app(id);
if (!app) if (!app)
continue; continue;
result.push(app); result.push(app);

View File

@ -318,7 +318,7 @@ AppsWidget.prototype = {
let appSystem = Shell.AppSystem.get_default(); let appSystem = Shell.AppSystem.get_default();
let apps = appSystem.get_favorites(); let apps = appSystem.get_favorites();
for (let i = 0; i < apps.length; i++) { 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) if (!app)
continue; continue;
this.addItem(new AppsWidgetInfo(app)); this.addItem(new AppsWidgetInfo(app));

View File

@ -54,16 +54,80 @@ static void reread_favorite_apps (ShellAppSystem *system);
G_DEFINE_TYPE(ShellAppSystem, shell_app_system, G_TYPE_OBJECT); 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* ShellAppInfo*
shell_app_info_ref (ShellAppInfo *info) shell_app_info_ref (ShellAppInfo *info)
{ {
return gmenu_tree_item_ref ((GMenuTreeItem*)info); info->refcount++;
return info;
} }
void void
shell_app_info_unref (ShellAppInfo *info) 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 static gpointer
@ -237,7 +301,8 @@ gather_entries_recurse (ShellAppSystem *monitor,
{ {
case GMENU_TREE_ITEM_ENTRY: 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; break;
case GMENU_TREE_ITEM_DIRECTORY: case GMENU_TREE_ITEM_DIRECTORY:
@ -245,12 +310,11 @@ gather_entries_recurse (ShellAppSystem *monitor,
GMenuTreeDirectory *dir = (GMenuTreeDirectory*)item; GMenuTreeDirectory *dir = (GMenuTreeDirectory*)item;
apps = gather_entries_recurse (monitor, apps, dir); apps = gather_entries_recurse (monitor, apps, dir);
} }
gmenu_tree_item_unref (item);
break; break;
default: default:
gmenu_tree_item_unref (item);
break; break;
} }
gmenu_tree_item_unref (item);
} }
g_slist_free (contents); g_slist_free (contents);
@ -285,7 +349,7 @@ cache_by_id (ShellAppSystem *self, GSList *apps, gboolean ref)
{ {
ShellAppInfo *info = iter->data; ShellAppInfo *info = iter->data;
if (ref) if (ref)
gmenu_tree_item_ref ((GMenuTreeItem*) info); shell_app_info_ref (info);
/* the name is owned by the info itself */ /* 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), g_hash_table_insert (self->priv->app_id_to_app, (char*)shell_app_info_get_id (info),
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 * Return value: (transfer full): The #ShellAppInfo for id, or %NULL if none
*/ */
ShellAppInfo * ShellAppInfo *
shell_app_system_lookup_app (ShellAppSystem *self, const char *id) shell_app_system_lookup_cached_app (ShellAppSystem *self, const char *id)
{ {
GMenuTreeEntry *entry; ShellAppInfo *info;
entry = g_hash_table_lookup (self->priv->app_id_to_app, id); info = g_hash_table_lookup (self->priv->app_id_to_app, id);
if (entry) if (info)
gmenu_tree_item_ref ((GMenuTreeItem*) entry); shell_app_info_ref (info);
return (ShellAppInfo*)entry; return info;
}
ShellAppInfo *
shell_app_system_load_from_desktop_file (ShellAppSystem *system,
const char *filename,
GError **error)
{
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);
return appinfo;
} }
/** /**
@ -584,7 +686,7 @@ shell_app_system_lookup_heuristic_basename (ShellAppSystem *system,
char *tmpid; char *tmpid;
ShellAppInfo *result; ShellAppInfo *result;
result = shell_app_system_lookup_app (system, name); result = shell_app_system_lookup_cached_app (system, name);
if (result != NULL) if (result != NULL)
return result; return result;
@ -593,13 +695,13 @@ shell_app_system_lookup_heuristic_basename (ShellAppSystem *system,
* prefix. So try stripping them. * prefix. So try stripping them.
*/ */
tmpid = g_strjoin ("", "gnome-", name, NULL); 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); g_free (tmpid);
if (result != NULL) if (result != NULL)
return result; return result;
tmpid = g_strjoin ("", "fedora-", name, NULL); 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); g_free (tmpid);
if (result != NULL) if (result != NULL)
return result; return result;
@ -610,37 +712,79 @@ shell_app_system_lookup_heuristic_basename (ShellAppSystem *system,
const char * const char *
shell_app_info_get_id (ShellAppInfo *info) 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) 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) 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) 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) 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 * GIcon *
shell_app_info_get_icon (ShellAppInfo *info) shell_app_info_get_icon (ShellAppInfo *info)
{ {
const char *iconname; char *iconname;
GIcon *icon; GIcon *icon;
/* This code adapted from gdesktopappinfo.c /* This code adapted from gdesktopappinfo.c
@ -649,7 +793,16 @@ shell_app_info_get_icon (ShellAppInfo *info)
* LGPL * 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) if (!iconname)
return NULL; return NULL;
@ -677,6 +830,8 @@ shell_app_info_get_icon (ShellAppInfo *info)
icon = g_themed_icon_new (tmp_name); icon = g_themed_icon_new (tmp_name);
g_free (tmp_name); g_free (tmp_name);
} }
g_free (iconname);
return icon; return icon;
} }
@ -689,7 +844,15 @@ shell_app_info_get_categories (ShellAppInfo *info)
gboolean gboolean
shell_app_info_get_is_nodisplay (ShellAppInfo *info) 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) if (!icon)
{ {
ret = clutter_texture_new (); 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; return ret;
} }
@ -734,7 +897,7 @@ shell_app_info_launch_full (ShellAppInfo *info,
GError **error) GError **error)
{ {
GDesktopAppInfo *gapp; GDesktopAppInfo *gapp;
const char *filename; char *filename;
GdkAppLaunchContext *context; GdkAppLaunchContext *context;
gboolean ret; gboolean ret;
ShellGlobal *global; ShellGlobal *global;
@ -744,9 +907,10 @@ shell_app_info_launch_full (ShellAppInfo *info,
if (startup_id) if (startup_id)
*startup_id = NULL; *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); gapp = g_desktop_app_info_new_from_filename (filename);
g_free (filename);
if (!gapp) if (!gapp)
{ {
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "Not found"); g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "Not found");

View File

@ -45,19 +45,19 @@ struct _ShellAppMenuEntry {
GType shell_app_menu_entry_get_type (void); GType shell_app_menu_entry_get_type (void);
/* Hidden typedef for a GMenuTreeEntry */
typedef struct _ShellAppInfo ShellAppInfo; typedef struct _ShellAppInfo ShellAppInfo;
#define SHELL_TYPE_APP_INFO (shell_app_info_get_type ())
GType shell_app_info_get_type (void); GType shell_app_info_get_type (void);
ShellAppInfo* shell_app_info_ref (ShellAppInfo *info); ShellAppInfo* shell_app_info_ref (ShellAppInfo *info);
void shell_app_info_unref (ShellAppInfo *info); void shell_app_info_unref (ShellAppInfo *info);
const char *shell_app_info_get_id (ShellAppInfo *info); const char *shell_app_info_get_id (ShellAppInfo *info);
const char *shell_app_info_get_name (ShellAppInfo *info); char *shell_app_info_get_name (ShellAppInfo *info);
const char *shell_app_info_get_description (ShellAppInfo *info); char *shell_app_info_get_description (ShellAppInfo *info);
const char *shell_app_info_get_executable (ShellAppInfo *info); char *shell_app_info_get_executable (ShellAppInfo *info);
const char *shell_app_info_get_desktop_file_path (ShellAppInfo *info); char *shell_app_info_get_desktop_file_path (ShellAppInfo *info);
GIcon *shell_app_info_get_icon (ShellAppInfo *info); GIcon *shell_app_info_get_icon (ShellAppInfo *info);
ClutterActor *shell_app_info_create_icon_texture (ShellAppInfo *info, float size); ClutterActor *shell_app_info_create_icon_texture (ShellAppInfo *info, float size);
GSList *shell_app_info_get_categories (ShellAppInfo *info); 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, gboolean shell_app_info_launch (ShellAppInfo *info,
GError **error); 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); ShellAppInfo *shell_app_system_lookup_heuristic_basename (ShellAppSystem *system, const char *id);