Compare commits

...

15 Commits

Author SHA1 Message Date
Jasper St. Pierre
45797a977b app-system: Add back StartupWMClass matching
While unfortunate that we still have to scan all apps with get_all(),
support for this feature will be short-lived, so hopefully we can drop
it in the future as new apps adapt to the desktop file / app ID
recommendations.

For now, simply scan all desktop IDs.
2013-10-02 18:22:28 -04:00
Jasper St. Pierre
884b94233e app-system: Put back support for the installed-changed signal
Use the new GAppInfoMonitor that Ryan added to glib to know when the
set of apps has changed.
2013-10-02 18:22:27 -04:00
Jasper St. Pierre
74d3e3139f app-system: Lazily create ShellApps for apps we care about
Rather than create all ShellApps up-front, create them lazily. We really
had no reason to do this before as we were scanning GMenu to get all the
apps, but doing this can remove a need for get_all, which is slow and
memory-hungry.
2013-10-02 18:22:27 -04:00
Jasper St. Pierre
77b5385cc3 appDisplay: Use the desktop file index for app searching
Rather than scanning all apps for searching, use Ryan's new desktop
file index and the glib support APIs for app searching instead of our
own system.
2013-10-02 18:22:27 -04:00
Jasper St. Pierre
d749d646be appDisplay: Use a proper string key for the app search provider
We were always sort of cheating when we used objects as the search ID.
Since the new desktop file index will return us a list of desktop file
IDs, just use those as IDs instead.
2013-10-02 18:22:27 -04:00
Jasper St. Pierre
fa8224d7b5 app-system: Remove use of gnome-menus internally
We want to transition to a system in the future where we have a desktop
file cache. As we no longer differentiate categories or similar, it no
longer makes sense to have app visibility based on categories. Thus,
we no longer need to use gnome-menus to list all apps. The potential
issue here is reloading all desktop files when new files are created,
but this can be dealt with individually.

The "All Applications" view still uses gnome-menus.

https://bugzilla.gnome.org/show_bug.cgi?id=698486
2013-10-02 18:22:27 -04:00
Jasper St. Pierre
f687197ccc appDisplay: Ignore the NoDisplay flag for directories
This makes us match the native app search.

https://bugzilla.gnome.org/show_bug.cgi?id=698486
2013-10-02 18:22:15 -04:00
Jasper St. Pierre
28a6aefb6c app-system: Remove visible_id_to_app
Since appDisplay.js makes its own GMenu tree, it's not necessary
anymore. This does mean that searches will show apps in NoDisplay
categories, but that's an obscure enough edge case not to matter.

https://bugzilla.gnome.org/show_bug.cgi?id=698486
2013-10-02 18:22:15 -04:00
Jasper St. Pierre
96c2a90e11 app-system: Remove lookup_app_for_path
It's absurdly silly. Just modify the one place that uses it
to be better.

https://bugzilla.gnome.org/show_bug.cgi?id=698486
2013-10-02 18:22:15 -04:00
Jasper St. Pierre
63cf46e49b app-system: Remove known_vendor_prefixes
This does remove support for legacy prefixed app infos with
subdirs, but since we want to remove support for the menu spec,
let's not even bother.

https://bugzilla.gnome.org/show_bug.cgi?id=698486
2013-10-02 18:22:15 -04:00
Jasper St. Pierre
200a9ef1af app-system: Remove get_tree
Make clients construct their own gmenu tree if they need it.

https://bugzilla.gnome.org/show_bug.cgi?id=698486
2013-10-02 18:22:08 -04:00
Jasper St. Pierre
6050ca6e0c app-system: Map wmclass to ID rather than apps
This makes the refcounting and memory management easier to understand.
2013-10-02 18:22:08 -04:00
Jasper St. Pierre
8bd7db9227 app-system: Don't use gmenu_tree_entry_get_desktop_app_info
It's a broken method when it comes to giving us a useful GDesktopAppInfo,
and it's hard to fix libgmenu properly, so simply recreate the app info
using the desktop file ID that libgmenu has.
2013-10-02 18:22:07 -04:00
Jasper St. Pierre
982feb85c1 app: Port to be based on GDesktopAppInfo
We weren't using the GMenuTreeEntry for anything special anymore,
so remove it.

https://bugzilla.gnome.org/show_bug.cgi?id=698486
2013-10-02 18:14:18 -04:00
Jasper St. Pierre
f165cc23c0 app-system: Remove lookup_app_by_tree_entry
We want to move away from gnome-menus eventually, so the simple
utility method isn't really worth keeping around. Reimplement it
in the one place that uses it.

https://bugzilla.gnome.org/show_bug.cgi?id=698486
2013-10-02 17:39:36 -04:00
10 changed files with 131 additions and 612 deletions

View File

@ -70,7 +70,6 @@ POLKIT_MIN_VERSION=0.100
STARTUP_NOTIFICATION_MIN_VERSION=0.11
GCR_MIN_VERSION=3.7.5
GNOME_DESKTOP_REQUIRED_VERSION=3.7.90
GNOME_MENUS_REQUIRED_VERSION=3.5.3
NETWORKMANAGER_MIN_VERSION=0.9.8
PULSE_MIN_VERS=2.0
@ -80,7 +79,6 @@ SHARED_PCS="gio-unix-2.0 >= $GIO_MIN_VERSION
gtk+-3.0 >= $GTK_MIN_VERSION
atk-bridge-2.0
gjs-internals-1.0 >= $GJS_MIN_VERSION
libgnome-menu-3.0 >= $GNOME_MENUS_REQUIRED_VERSION
$recorder_modules
gdk-x11-3.0 libsoup-2.4
xtst

View File

@ -55,13 +55,13 @@ function _loadCategory(dir, view) {
while ((nextType = iter.next()) != GMenu.TreeItemType.INVALID) {
if (nextType == GMenu.TreeItemType.ENTRY) {
let entry = iter.get_entry();
let app = appSystem.lookup_app_by_tree_entry(entry);
if (!entry.get_app_info().get_nodisplay())
let appInfo = entry.get_app_info();
let app = appSystem.lookup_app(entry.get_desktop_file_id());
if (appInfo.should_show())
view.addApp(app);
} else if (nextType == GMenu.TreeItemType.DIRECTORY) {
let itemDir = iter.get_directory();
if (!itemDir.get_is_nodisplay())
_loadCategory(itemDir, view);
_loadCategory(itemDir, view);
}
}
};
@ -691,8 +691,7 @@ const AppDisplay = new Lang.Class({
Name: 'AppDisplay',
_init: function() {
this._appSystem = Shell.AppSystem.get_default();
this._appSystem.connect('installed-changed', Lang.bind(this, function() {
Shell.AppSystem.get_default().connect('installed-changed', Lang.bind(this, function() {
Main.queueDeferredWork(this._allAppsWorkId);
}));
Main.overview.connect('showing', Lang.bind(this, function() {
@ -808,7 +807,8 @@ const AppDisplay = new Lang.Class({
view.removeAll();
let tree = this._appSystem.get_tree();
let tree = new GMenu.Tree({ menu_basename: "applications.menu" });
tree.load_sync();
let root = tree.get_root_directory();
let iter = root.iter();
@ -817,8 +817,6 @@ const AppDisplay = new Lang.Class({
while ((nextType = iter.next()) != GMenu.TreeItemType.INVALID) {
if (nextType == GMenu.TreeItemType.DIRECTORY) {
let dir = iter.get_directory();
if (dir.get_is_nodisplay())
continue;
if (folderCategories.indexOf(dir.get_menu_id()) != -1)
view.addFolder(dir);
@ -866,8 +864,8 @@ const AppSearchProvider = new Lang.Class({
getResultMetas: function(apps, callback) {
let metas = [];
for (let i = 0; i < apps.length; i++) {
let app = apps[i];
metas.push({ 'id': app,
let app = this._appSys.lookup_app(apps[i]);
metas.push({ 'id': app.get_id(),
'name': app.get_name(),
'createIcon': function(size) {
return app.create_icon_texture(size);
@ -877,15 +875,23 @@ const AppSearchProvider = new Lang.Class({
callback(metas);
},
_compareResults: function(a, b) {
let usage = Shell.AppUsage.get_default();
return usage.compare('', a, b);
},
getInitialResultSet: function(terms) {
this.searchSystem.setResults(this, this._appSys.initial_search(terms));
let query = terms.join(' ');
let results = Gio.DesktopAppInfo.search(query, Lang.bind(this, this._compareResults), MAX_COLUMNS);
this.searchSystem.setResults(this, results);
},
getSubsearchResultSet: function(previousResults, terms) {
this.searchSystem.setResults(this, this._appSys.subsearch(previousResults, terms));
this.getInitialResultSet(terms);
},
activateResult: function(app) {
activateResult: function(result) {
let app = this._appSys.lookup_app(result);
let event = Clutter.get_current_event();
let modifiers = event ? event.get_state() : 0;
let openNewWindow = modifiers & Clutter.ModifierType.CONTROL_MASK;
@ -905,7 +911,7 @@ const AppSearchProvider = new Lang.Class({
},
createResultObject: function (resultMeta, terms) {
let app = resultMeta['id'];
let app = this._appSys.lookup_app(resultMeta['id']);
return new AppIcon(app);
}
});

View File

@ -12,9 +12,9 @@ G_BEGIN_DECLS
ShellApp* _shell_app_new_for_window (MetaWindow *window);
ShellApp* _shell_app_new (GMenuTreeEntry *entry);
ShellApp* _shell_app_new (GDesktopAppInfo *info);
void _shell_app_set_entry (ShellApp *app, GMenuTreeEntry *entry);
void _shell_app_set_app_info (ShellApp *app, GDesktopAppInfo *info);
void _shell_app_handle_startup_sequence (ShellApp *app, SnStartupSequence *sequence);

View File

@ -38,18 +38,12 @@ enum {
static guint signals[LAST_SIGNAL] = { 0 };
struct _ShellAppSystemPrivate {
GMenuTree *apps_tree;
GHashTable *running_apps;
GHashTable *visible_id_to_app;
GHashTable *id_to_app;
GHashTable *startup_wm_class_to_app;
GSList *known_vendor_prefixes;
GHashTable *startup_wm_class_to_id;
};
static void shell_app_system_finalize (GObject *object);
static void on_apps_tree_changed_cb (GMenuTree *tree, gpointer user_data);
G_DEFINE_TYPE(ShellAppSystem, shell_app_system, G_TYPE_OBJECT);
@ -77,10 +71,45 @@ static void shell_app_system_class_init(ShellAppSystemClass *klass)
g_type_class_add_private (gobject_class, sizeof (ShellAppSystemPrivate));
}
static void
scan_startup_wm_class_to_id (ShellAppSystem *self)
{
ShellAppSystemPrivate *priv = self->priv;
GList *apps, *l;
g_hash_table_remove_all (priv->startup_wm_class_to_id);
apps = g_app_info_get_all ();
for (l = apps; l != NULL; l = l->next)
{
GAppInfo *info = l->data;
const char *startup_wm_class, *id;
id = g_app_info_get_id (info);
startup_wm_class = g_desktop_app_info_get_startup_wm_class (G_DESKTOP_APP_INFO (info));
if (startup_wm_class != NULL)
g_hash_table_insert (priv->startup_wm_class_to_id, (char *) startup_wm_class, (char *) id);
}
g_list_free_full (apps, g_object_unref);
}
static void
installed_changed (GAppInfoMonitor *monitor,
gpointer user_data)
{
ShellAppSystem *self = user_data;
scan_startup_wm_class_to_id (self);
g_signal_emit (self, signals[INSTALLED_CHANGED], 0, NULL);
}
static void
shell_app_system_init (ShellAppSystem *self)
{
ShellAppSystemPrivate *priv;
GAppInfoMonitor *monitor;
self->priv = priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
SHELL_TYPE_APP_SYSTEM,
@ -91,19 +120,11 @@ shell_app_system_init (ShellAppSystem *self)
NULL,
(GDestroyNotify)g_object_unref);
/* All the objects in this hash table are owned by id_to_app */
priv->visible_id_to_app = g_hash_table_new (g_str_hash, g_str_equal);
priv->startup_wm_class_to_id = g_hash_table_new (g_str_hash, g_str_equal);
priv->startup_wm_class_to_app = g_hash_table_new_full (g_str_hash, g_str_equal,
NULL,
(GDestroyNotify)g_object_unref);
/* We want to track NoDisplay apps, so we add INCLUDE_NODISPLAY. We'll
* filter NoDisplay apps out when showing them to the user. */
priv->apps_tree = gmenu_tree_new ("applications.menu", GMENU_TREE_FLAGS_INCLUDE_NODISPLAY);
g_signal_connect (priv->apps_tree, "changed", G_CALLBACK (on_apps_tree_changed_cb), self);
on_apps_tree_changed_cb (priv->apps_tree, self);
monitor = g_app_info_monitor_get ();
g_signal_connect (monitor, "changed", G_CALLBACK (installed_changed), self);
installed_changed (monitor, self);
}
static void
@ -112,313 +133,13 @@ shell_app_system_finalize (GObject *object)
ShellAppSystem *self = SHELL_APP_SYSTEM (object);
ShellAppSystemPrivate *priv = self->priv;
g_object_unref (priv->apps_tree);
g_hash_table_destroy (priv->running_apps);
g_hash_table_destroy (priv->id_to_app);
g_hash_table_destroy (priv->visible_id_to_app);
g_hash_table_destroy (priv->startup_wm_class_to_app);
g_slist_free_full (priv->known_vendor_prefixes, g_free);
priv->known_vendor_prefixes = NULL;
g_hash_table_destroy (priv->startup_wm_class_to_id);
G_OBJECT_CLASS (shell_app_system_parent_class)->finalize (object);
}
static char *
get_prefix_for_entry (GMenuTreeEntry *entry)
{
char *prefix = NULL, *file_prefix = NULL;
const char *id;
GFile *file;
char *name;
int i = 0;
id = gmenu_tree_entry_get_desktop_file_id (entry);
file = g_file_new_for_path (gmenu_tree_entry_get_desktop_file_path (entry));
name = g_file_get_basename (file);
if (!name)
{
g_object_unref (file);
return NULL;
}
for (i = 0; vendor_prefixes[i]; i++)
{
if (g_str_has_prefix (name, vendor_prefixes[i]))
{
file_prefix = g_strdup (vendor_prefixes[i]);
break;
}
}
while (strcmp (name, id) != 0)
{
char *t;
char *pname;
GFile *parent = g_file_get_parent (file);
if (!parent)
{
g_warn_if_reached ();
break;
}
pname = g_file_get_basename (parent);
if (!pname)
{
g_object_unref (parent);
break;
}
if (!g_strstr_len (id, -1, pname))
{
/* handle <LegacyDir prefix="..."> */
char *t;
size_t name_len = strlen (name);
size_t id_len = strlen (id);
char *t_id = g_strdup (id);
t_id[id_len - name_len] = '\0';
t = g_strdup(t_id);
g_free (prefix);
g_free (t_id);
g_free (name);
name = g_strdup (id);
prefix = t;
g_object_unref (file);
file = parent;
g_free (pname);
g_free (file_prefix);
file_prefix = NULL;
break;
}
t = g_strconcat (pname, "-", name, NULL);
g_free (name);
name = t;
t = g_strconcat (pname, "-", prefix, NULL);
g_free (prefix);
prefix = t;
g_object_unref (file);
file = parent;
g_free (pname);
}
if (file)
g_object_unref (file);
if (strcmp (name, id) == 0)
{
g_free (name);
if (file_prefix && !prefix)
return file_prefix;
if (file_prefix)
{
char *t = g_strconcat (prefix, "-", file_prefix, NULL);
g_free (prefix);
g_free (file_prefix);
prefix = t;
}
return prefix;
}
g_free (name);
g_free (prefix);
g_free (file_prefix);
g_return_val_if_reached (NULL);
}
static void
get_flattened_entries_recurse (GMenuTreeDirectory *dir,
GHashTable *entry_set)
{
GMenuTreeIter *iter = gmenu_tree_directory_iter (dir);
GMenuTreeItemType next_type;
while ((next_type = gmenu_tree_iter_next (iter)) != GMENU_TREE_ITEM_INVALID)
{
gpointer item = NULL;
switch (next_type)
{
case GMENU_TREE_ITEM_ENTRY:
{
GMenuTreeEntry *entry;
item = entry = gmenu_tree_iter_get_entry (iter);
/* Key is owned by entry */
g_hash_table_replace (entry_set,
(char*)gmenu_tree_entry_get_desktop_file_id (entry),
gmenu_tree_item_ref (entry));
}
break;
case GMENU_TREE_ITEM_DIRECTORY:
{
item = gmenu_tree_iter_get_directory (iter);
get_flattened_entries_recurse ((GMenuTreeDirectory*)item, entry_set);
}
break;
default:
break;
}
if (item != NULL)
gmenu_tree_item_unref (item);
}
gmenu_tree_iter_unref (iter);
}
static GHashTable *
get_flattened_entries_from_tree (GMenuTree *tree)
{
GHashTable *table;
GMenuTreeDirectory *root;
table = g_hash_table_new_full (g_str_hash, g_str_equal,
(GDestroyNotify) NULL,
(GDestroyNotify) gmenu_tree_item_unref);
root = gmenu_tree_get_root_directory (tree);
if (root != NULL)
get_flattened_entries_recurse (root, table);
gmenu_tree_item_unref (root);
return table;
}
static void
on_apps_tree_changed_cb (GMenuTree *tree,
gpointer user_data)
{
ShellAppSystem *self = SHELL_APP_SYSTEM (user_data);
GError *error = NULL;
GHashTable *new_apps;
GHashTableIter iter;
gpointer key, value;
g_assert (tree == self->priv->apps_tree);
g_hash_table_remove_all (self->priv->visible_id_to_app);
g_slist_free_full (self->priv->known_vendor_prefixes, g_free);
self->priv->known_vendor_prefixes = NULL;
if (!gmenu_tree_load_sync (self->priv->apps_tree, &error))
{
if (error)
{
g_warning ("Failed to load apps: %s", error->message);
g_error_free (error);
}
else
{
g_warning ("Failed to load apps");
}
return;
}
new_apps = get_flattened_entries_from_tree (self->priv->apps_tree);
g_hash_table_iter_init (&iter, new_apps);
while (g_hash_table_iter_next (&iter, &key, &value))
{
const char *id = key;
GMenuTreeEntry *entry = value;
GMenuTreeEntry *old_entry;
char *prefix;
ShellApp *app;
GDesktopAppInfo *info;
const char *startup_wm_class;
prefix = get_prefix_for_entry (entry);
if (prefix != NULL
&& !g_slist_find_custom (self->priv->known_vendor_prefixes, prefix,
(GCompareFunc)g_strcmp0))
self->priv->known_vendor_prefixes = g_slist_append (self->priv->known_vendor_prefixes,
prefix);
else
g_free (prefix);
app = g_hash_table_lookup (self->priv->id_to_app, id);
if (app != NULL)
{
/* We hold a reference to the original entry temporarily,
* because otherwise the hash table would be referencing
* potentially free'd memory until we replace it below with
* the new data.
*/
old_entry = shell_app_get_tree_entry (app);
gmenu_tree_item_ref (old_entry);
_shell_app_set_entry (app, entry);
g_object_ref (app); /* Extra ref, removed in _replace below */
}
else
{
old_entry = NULL;
app = _shell_app_new (entry);
}
/* Note that "id" is owned by app->entry. Since we're always
* setting a new entry, even if the app already exists in the
* hash table we need to replace the key so that the new id
* string is pointed to.
*/
g_hash_table_replace (self->priv->id_to_app, (char*)id, app);
if (!gmenu_tree_entry_get_is_nodisplay_recurse (entry))
g_hash_table_replace (self->priv->visible_id_to_app, (char*)id, app);
if (old_entry)
{
GDesktopAppInfo *old_info;
const gchar *old_startup_wm_class;
old_info = gmenu_tree_entry_get_app_info (old_entry);
old_startup_wm_class = g_desktop_app_info_get_startup_wm_class (old_info);
if (old_startup_wm_class)
g_hash_table_remove (self->priv->startup_wm_class_to_app, old_startup_wm_class);
}
info = gmenu_tree_entry_get_app_info (entry);
startup_wm_class = g_desktop_app_info_get_startup_wm_class (info);
if (startup_wm_class)
g_hash_table_replace (self->priv->startup_wm_class_to_app,
(char*)startup_wm_class, g_object_ref (app));
if (old_entry)
gmenu_tree_item_unref (old_entry);
}
/* Now iterate over the apps again; we need to unreference any apps
* which have been removed. The JS code may still be holding a
* reference; that's fine.
*/
g_hash_table_iter_init (&iter, self->priv->id_to_app);
while (g_hash_table_iter_next (&iter, &key, &value))
{
const char *id = key;
if (!g_hash_table_lookup (new_apps, id))
g_hash_table_iter_remove (&iter);
}
g_hash_table_destroy (new_apps);
g_signal_emit (self, signals[INSTALLED_CHANGED], 0);
}
/**
* shell_app_system_get_tree:
*
* Return Value: (transfer none): The #GMenuTree for apps
*/
GMenuTree *
shell_app_system_get_tree (ShellAppSystem *self)
{
return self->priv->apps_tree;
}
/**
* shell_app_system_get_default:
*
@ -446,61 +167,20 @@ ShellApp *
shell_app_system_lookup_app (ShellAppSystem *self,
const char *id)
{
return g_hash_table_lookup (self->priv->id_to_app, id);
}
/**
* shell_app_system_lookup_app_by_tree_entry:
* @system: a #ShellAppSystem
* @entry: a #GMenuTreeEntry
*
* Find a #ShellApp corresponding to a #GMenuTreeEntry.
*
* Return value: (transfer none): The #ShellApp for @entry, or %NULL if none
*/
ShellApp *
shell_app_system_lookup_app_by_tree_entry (ShellAppSystem *self,
GMenuTreeEntry *entry)
{
/* If we looked up directly in ->entry_to_app, we'd lose the
* override of running apps. Thus, indirect through the id.
*/
return shell_app_system_lookup_app (self, gmenu_tree_entry_get_desktop_file_id (entry));
}
/**
* shell_app_system_lookup_app_for_path:
* @system: a #ShellAppSystem
* @desktop_path: (type utf8): UTF-8 encoded absolute file name
*
* Find or create a #ShellApp corresponding to a given absolute file
* name which must be in the standard paths (XDG_DATA_DIRS). For
* files outside the datadirs, this function returns %NULL.
*
* Return value: (transfer none): The #ShellApp for id, or %NULL if none
*/
ShellApp *
shell_app_system_lookup_app_for_path (ShellAppSystem *system,
const char *desktop_path)
{
const char *basename;
const char *app_path;
ShellAppSystemPrivate *priv = self->priv;
ShellApp *app;
GDesktopAppInfo *info;
basename = g_strrstr (desktop_path, "/");
if (basename)
basename += 1;
else
basename = desktop_path;
app = g_hash_table_lookup (priv->id_to_app, id);
if (app)
return app;
app = shell_app_system_lookup_app (system, basename);
if (!app)
return NULL;
app_path = g_desktop_app_info_get_filename (shell_app_get_app_info (app));
if (strcmp (desktop_path, app_path) != 0)
info = g_desktop_app_info_new (id);
if (!info)
return NULL;
app = _shell_app_new (info);
g_hash_table_insert (priv->id_to_app, (char *) id, app);
return app;
}
@ -520,15 +200,15 @@ shell_app_system_lookup_heuristic_basename (ShellAppSystem *system,
const char *name)
{
ShellApp *result;
GSList *prefix;
const char *const *prefix;
result = shell_app_system_lookup_app (system, name);
if (result != NULL)
return result;
for (prefix = system->priv->known_vendor_prefixes; prefix; prefix = g_slist_next (prefix))
for (prefix = vendor_prefixes; *prefix != NULL; prefix++)
{
char *tmpid = g_strconcat ((char*)prefix->data, name, NULL);
char *tmpid = g_strconcat (*prefix, name, NULL);
result = shell_app_system_lookup_app (system, tmpid);
g_free (tmpid);
if (result != NULL)
@ -603,10 +283,16 @@ ShellApp *
shell_app_system_lookup_startup_wmclass (ShellAppSystem *system,
const char *wmclass)
{
const char *id;
if (wmclass == NULL)
return NULL;
return g_hash_table_lookup (system->priv->startup_wm_class_to_app, wmclass);
id = g_hash_table_lookup (system->priv->startup_wm_class_to_id, wmclass);
if (id == NULL)
return NULL;
return shell_app_system_lookup_app (system, id);
}
void
@ -661,136 +347,3 @@ shell_app_system_get_running (ShellAppSystem *self)
return ret;
}
static gint
compare_apps_by_usage (gconstpointer a,
gconstpointer b,
gpointer data)
{
ShellAppUsage *usage = shell_app_usage_get_default ();
ShellApp *app_a = (ShellApp*)a;
ShellApp *app_b = (ShellApp*)b;
return shell_app_usage_compare (usage, "", app_a, app_b);
}
static GSList *
sort_and_concat_results (ShellAppSystem *system,
GSList *prefix_matches,
GSList *substring_matches)
{
prefix_matches = g_slist_sort_with_data (prefix_matches,
compare_apps_by_usage,
system);
substring_matches = g_slist_sort_with_data (substring_matches,
compare_apps_by_usage,
system);
return g_slist_concat (prefix_matches, substring_matches);
}
/**
* normalize_terms:
* @terms: (element-type utf8): Input search terms
*
* Returns: (element-type utf8) (transfer full): Unicode-normalized and lowercased terms
*/
static GSList *
normalize_terms (GSList *terms)
{
GSList *normalized_terms = NULL;
GSList *iter;
for (iter = terms; iter; iter = iter->next)
{
const char *term = iter->data;
normalized_terms = g_slist_prepend (normalized_terms,
shell_util_normalize_casefold_and_unaccent (term));
}
return normalized_terms;
}
static GSList *
search_tree (ShellAppSystem *self,
GSList *terms,
GHashTable *apps)
{
GSList *prefix_results = NULL;
GSList *substring_results = NULL;
GSList *normalized_terms;
GHashTableIter iter;
gpointer key, value;
normalized_terms = normalize_terms (terms);
g_hash_table_iter_init (&iter, apps);
while (g_hash_table_iter_next (&iter, &key, &value))
{
ShellApp *app = value;
_shell_app_do_match (app, normalized_terms,
&prefix_results,
&substring_results);
}
g_slist_free_full (normalized_terms, g_free);
return sort_and_concat_results (self, prefix_results, substring_results);
}
/**
* shell_app_system_initial_search:
* @system: A #ShellAppSystem
* @terms: (element-type utf8): List of terms, logical AND
*
* Search through applications for the given search terms.
*
* Returns: (transfer container) (element-type ShellApp): List of applications
*/
GSList *
shell_app_system_initial_search (ShellAppSystem *self,
GSList *terms)
{
return search_tree (self, terms, self->priv->visible_id_to_app);
}
/**
* shell_app_system_subsearch:
* @system: A #ShellAppSystem
* @previous_results: (element-type ShellApp): List of previous results
* @terms: (element-type utf8): List of terms, logical AND
*
* Search through a previous result set; for more information, see
* js/ui/search.js. Note the value of @prefs must be
* the same as passed to shell_app_system_initial_search(). Note that returned
* strings are only valid until a return to the main loop.
*
* Returns: (transfer container) (element-type ShellApp): List of application identifiers
*/
GSList *
shell_app_system_subsearch (ShellAppSystem *system,
GSList *previous_results,
GSList *terms)
{
GSList *iter;
GSList *prefix_results = NULL;
GSList *substring_results = NULL;
GSList *normalized_terms = normalize_terms (terms);
previous_results = g_slist_reverse (previous_results);
for (iter = previous_results; iter; iter = iter->next)
{
ShellApp *app = iter->data;
_shell_app_do_match (app, normalized_terms,
&prefix_results,
&substring_results);
}
g_slist_free_full (normalized_terms, g_free);
/* Note that a shorter term might have matched as a prefix, but
when extended only as a substring, so we have to redo the
sort rather than reusing the existing ordering */
return sort_and_concat_results (system, prefix_results, substring_results);
}

View File

@ -5,8 +5,6 @@
#include <gio/gio.h>
#include <clutter/clutter.h>
#include <meta/window.h>
#define GMENU_I_KNOW_THIS_IS_UNSTABLE
#include <gmenu-tree.h>
#include "shell-app.h"
@ -39,14 +37,8 @@ struct _ShellAppSystemClass
GType shell_app_system_get_type (void) G_GNUC_CONST;
ShellAppSystem *shell_app_system_get_default (void);
GMenuTree *shell_app_system_get_tree (ShellAppSystem *system);
ShellApp *shell_app_system_lookup_app (ShellAppSystem *system,
const char *id);
ShellApp *shell_app_system_lookup_app_by_tree_entry (ShellAppSystem *system,
GMenuTreeEntry *entry);
ShellApp *shell_app_system_lookup_app_for_path (ShellAppSystem *system,
const char *desktop_path);
ShellApp *shell_app_system_lookup_heuristic_basename (ShellAppSystem *system,
const char *id);
@ -57,10 +49,4 @@ ShellApp *shell_app_system_lookup_desktop_wmclass (ShellAppSystem *s
GSList *shell_app_system_get_running (ShellAppSystem *self);
GSList *shell_app_system_initial_search (ShellAppSystem *system,
GSList *terms);
GSList *shell_app_system_subsearch (ShellAppSystem *system,
GSList *previous_results,
GSList *terms);
#endif /* __SHELL_APP_SYSTEM_H__ */

View File

@ -527,19 +527,19 @@ shell_app_usage_get_most_used (ShellAppUsage *self,
* shell_app_usage_compare:
* @self: the usage instance to request
* @context: Activity identifier
* @app_a: First app
* @app_b: Second app
* @id_a: ID of first app
* @id_b: ID of second app
*
* Compare @app_a and @app_b based on frequency of use.
* Compare @id_a and @id_b based on frequency of use.
*
* Returns: -1 if @app_a ranks higher than @app_b, 1 if @app_b ranks higher
* than @app_a, and 0 if both rank equally.
* Returns: -1 if @id_a ranks higher than @id_b, 1 if @id_b ranks higher
* than @id_a, and 0 if both rank equally.
*/
int
shell_app_usage_compare (ShellAppUsage *self,
const char *context,
ShellApp *app_a,
ShellApp *app_b)
const char *id_a,
const char *id_b)
{
GHashTable *usages;
UsageData *usage_a, *usage_b;
@ -548,8 +548,8 @@ shell_app_usage_compare (ShellAppUsage *self,
if (usages == NULL)
return 0;
usage_a = g_hash_table_lookup (usages, shell_app_get_id (app_a));
usage_b = g_hash_table_lookup (usages, shell_app_get_id (app_b));
usage_a = g_hash_table_lookup (usages, id_a);
usage_b = g_hash_table_lookup (usages, id_b);
if (usage_a == NULL && usage_b == NULL)
return 0;

View File

@ -31,8 +31,8 @@ GSList *shell_app_usage_get_most_used (ShellAppUsage *usage,
const char *context);
int shell_app_usage_compare (ShellAppUsage *self,
const char *context,
ShellApp *app_a,
ShellApp *app_b);
const char *id_a,
const char *id_b);
G_END_DECLS

View File

@ -53,7 +53,7 @@ typedef struct {
* SECTION:shell-app
* @short_description: Object representing an application
*
* This object wraps a #GMenuTreeEntry, providing methods and signals
* This object wraps a #GDesktopAppInfo, providing methods and signals
* primarily useful for running applications.
*/
struct _ShellApp
@ -64,7 +64,7 @@ struct _ShellApp
ShellAppState state;
GMenuTreeEntry *entry; /* If NULL, this app is backed by one or more
GDesktopAppInfo *info; /* If NULL, this app is backed by one or more
* MetaWindow. For purposes of app title
* etc., we use the first window added,
* because it's most likely to be what we
@ -137,15 +137,15 @@ shell_app_get_property (GObject *gobject,
const char *
shell_app_get_id (ShellApp *app)
{
if (app->entry)
return gmenu_tree_entry_get_desktop_file_id (app->entry);
if (app->info)
return g_app_info_get_id (G_APP_INFO (app->info));
return app->window_id_string;
}
static MetaWindow *
window_backed_app_get_window (ShellApp *app)
{
g_assert (app->entry == NULL);
g_assert (app->info == NULL);
g_assert (app->running_state);
g_assert (app->running_state->windows);
return app->running_state->windows->data;
@ -194,10 +194,10 @@ shell_app_create_icon_texture (ShellApp *app,
ret = NULL;
if (app->entry == NULL)
if (app->info == NULL)
return window_backed_app_get_icon (app, size);
icon = g_app_info_get_icon (G_APP_INFO (gmenu_tree_entry_get_app_info (app->entry)));
icon = g_app_info_get_icon (G_APP_INFO (app->info));
if (icon != NULL)
ret = st_texture_cache_load_gicon (st_texture_cache_get_default (), NULL, icon, size);
@ -245,7 +245,7 @@ shell_app_create_faded_icon_cpu (StTextureCache *cache,
info = NULL;
icon = g_app_info_get_icon (G_APP_INFO (gmenu_tree_entry_get_app_info (app->entry)));
icon = g_app_info_get_icon (G_APP_INFO (app->info));
if (icon != NULL)
{
info = gtk_icon_theme_lookup_by_gicon (gtk_icon_theme_get_default (),
@ -347,7 +347,7 @@ shell_app_get_faded_icon (ShellApp *app, int size, ClutterTextDirection directio
* property tracking bits, and this helps us visually distinguish
* app-tracked from not.
*/
if (!app->entry)
if (!app->info)
return window_backed_app_get_icon (app, size);
/* Use icon: prefix so that we get evicted from the cache on
@ -384,8 +384,8 @@ shell_app_get_faded_icon (ShellApp *app, int size, ClutterTextDirection directio
const char *
shell_app_get_name (ShellApp *app)
{
if (app->entry)
return g_app_info_get_name (G_APP_INFO (gmenu_tree_entry_get_app_info (app->entry)));
if (app->info)
return g_app_info_get_name (G_APP_INFO (app->info));
else
{
MetaWindow *window = window_backed_app_get_window (app);
@ -401,8 +401,8 @@ shell_app_get_name (ShellApp *app)
const char *
shell_app_get_description (ShellApp *app)
{
if (app->entry)
return g_app_info_get_description (G_APP_INFO (gmenu_tree_entry_get_app_info (app->entry)));
if (app->info)
return g_app_info_get_description (G_APP_INFO (app->info));
else
return NULL;
}
@ -417,7 +417,7 @@ shell_app_get_description (ShellApp *app)
gboolean
shell_app_is_window_backed (ShellApp *app)
{
return app->entry == NULL;
return app->info == NULL;
}
typedef struct {
@ -670,7 +670,7 @@ void
shell_app_open_new_window (ShellApp *app,
int workspace)
{
g_return_if_fail (app->entry != NULL);
g_return_if_fail (app->info != NULL);
/* Here we just always launch the application again, even if we know
* it was already running. For most applications this
@ -865,24 +865,23 @@ _shell_app_new_for_window (MetaWindow *window)
}
ShellApp *
_shell_app_new (GMenuTreeEntry *info)
_shell_app_new (GDesktopAppInfo *info)
{
ShellApp *app;
app = g_object_new (SHELL_TYPE_APP, NULL);
_shell_app_set_entry (app, info);
_shell_app_set_app_info (app, info);
return app;
}
void
_shell_app_set_entry (ShellApp *app,
GMenuTreeEntry *entry)
_shell_app_set_app_info (ShellApp *app,
GDesktopAppInfo *info)
{
if (app->entry != NULL)
gmenu_tree_item_unref (app->entry);
app->entry = gmenu_tree_item_ref (entry);
g_clear_object (&app->info);
app->info = g_object_ref (info);
if (app->name_collation_key != NULL)
g_free (app->name_collation_key);
@ -1188,7 +1187,6 @@ shell_app_launch (ShellApp *app,
char **startup_id,
GError **error)
{
GDesktopAppInfo *gapp;
GdkAppLaunchContext *context;
gboolean ret;
ShellGlobal *global;
@ -1198,7 +1196,7 @@ shell_app_launch (ShellApp *app,
if (startup_id)
*startup_id = NULL;
if (app->entry == NULL)
if (app->info == NULL)
{
MetaWindow *window = window_backed_app_get_window (app);
/* We can't pass URIs into a window; shouldn't hit this
@ -1224,8 +1222,7 @@ shell_app_launch (ShellApp *app,
gdk_app_launch_context_set_timestamp (context, timestamp);
gdk_app_launch_context_set_desktop (context, workspace);
gapp = gmenu_tree_entry_get_app_info (app->entry);
ret = g_desktop_app_info_launch_uris_as_manager (gapp, uris,
ret = g_desktop_app_info_launch_uris_as_manager (app->info, uris,
G_APP_LAUNCH_CONTEXT (context),
G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD,
NULL, NULL,
@ -1245,21 +1242,7 @@ shell_app_launch (ShellApp *app,
GDesktopAppInfo *
shell_app_get_app_info (ShellApp *app)
{
if (app->entry)
return gmenu_tree_entry_get_app_info (app->entry);
return NULL;
}
/**
* shell_app_get_tree_entry:
* @app: a #ShellApp
*
* Returns: (transfer none): The #GMenuTreeEntry for this app, or %NULL if backed by a window
*/
GMenuTreeEntry *
shell_app_get_tree_entry (ShellApp *app)
{
return app->entry;
return app->info;
}
static void
@ -1374,24 +1357,22 @@ shell_app_init_search_data (ShellApp *app)
const char *exec;
const char * const *keywords;
char *normalized_exec;
GDesktopAppInfo *appinfo;
appinfo = gmenu_tree_entry_get_app_info (app->entry);
name = g_app_info_get_name (G_APP_INFO (appinfo));
name = g_app_info_get_name (G_APP_INFO (app->info));
app->casefolded_name = shell_util_normalize_casefold_and_unaccent (name);
generic_name = g_desktop_app_info_get_generic_name (appinfo);
generic_name = g_desktop_app_info_get_generic_name (app->info);
if (generic_name)
app->casefolded_generic_name = shell_util_normalize_casefold_and_unaccent (generic_name);
else
app->casefolded_generic_name = NULL;
exec = g_app_info_get_executable (G_APP_INFO (appinfo));
exec = g_app_info_get_executable (G_APP_INFO (app->info));
normalized_exec = shell_util_normalize_casefold_and_unaccent (exec);
app->casefolded_exec = trim_exec_line (normalized_exec);
g_free (normalized_exec);
keywords = g_desktop_app_info_get_keywords (appinfo);
keywords = g_desktop_app_info_get_keywords (app->info);
if (keywords)
{
@ -1512,16 +1493,14 @@ _shell_app_do_match (ShellApp *app,
GSList **substring_results)
{
ShellAppSearchMatch match;
GAppInfo *appinfo;
g_assert (app != NULL);
/* Skip window-backed apps */
appinfo = (GAppInfo*)shell_app_get_app_info (app);
if (appinfo == NULL)
if (app->info == NULL)
return;
/* Skip not-visible apps */
if (!g_app_info_should_show (appinfo))
if (!g_app_info_should_show (G_APP_INFO (app->info)))
return;
match = _shell_app_match_search_terms (app, terms);
@ -1550,11 +1529,7 @@ shell_app_dispose (GObject *object)
{
ShellApp *app = SHELL_APP (object);
if (app->entry)
{
gmenu_tree_item_unref (app->entry);
app->entry = NULL;
}
g_clear_object (&app->info);
if (app->running_state)
{

View File

@ -4,9 +4,8 @@
#include <clutter/clutter.h>
#include <gio/gio.h>
#include <gio/gdesktopappinfo.h>
#include <meta/window.h>
#define GMENU_I_KNOW_THIS_IS_UNSTABLE
#include <gmenu-tree.h>
G_BEGIN_DECLS
@ -39,7 +38,6 @@ GType shell_app_get_type (void) G_GNUC_CONST;
const char *shell_app_get_id (ShellApp *app);
GMenuTreeEntry *shell_app_get_tree_entry (ShellApp *app);
GDesktopAppInfo *shell_app_get_app_info (ShellApp *app);
ClutterActor *shell_app_create_icon_texture (ShellApp *app, int size);

View File

@ -844,6 +844,7 @@ ShellApp *
shell_startup_sequence_get_app (ShellStartupSequence *sequence)
{
const char *appid;
char *basename;
ShellAppSystem *appsys;
ShellApp *app;
@ -851,8 +852,10 @@ shell_startup_sequence_get_app (ShellStartupSequence *sequence)
if (!appid)
return NULL;
basename = g_path_get_basename (appid);
appsys = shell_app_system_get_default ();
app = shell_app_system_lookup_app_for_path (appsys, appid);
app = shell_app_system_lookup_app (appsys, basename);
g_free (basename);
return app;
}