static void shell_app_system_finalize (GObject *object) { ShellAppSystem *self = SHELL_APP_SYSTEM (object); ShellAppSystemPrivate *priv = self->priv; gmenu_tree_remove_monitor (priv->apps_tree, on_tree_changed_cb, self); gmenu_tree_remove_monitor (priv->settings_tree, on_tree_changed_cb, self); gmenu_tree_unref (priv->apps_tree); gmenu_tree_unref (priv->settings_tree); g_hash_table_destroy (priv->app_id_to_info); g_hash_table_destroy (priv->app_id_to_app); g_slist_foreach (priv->cached_flattened_apps, (GFunc)shell_app_info_unref, NULL); g_slist_free (priv->cached_flattened_apps); priv->cached_flattened_apps = NULL; g_slist_foreach (priv->known_vendor_prefixes, (GFunc)g_free, NULL); g_slist_free (priv->known_vendor_prefixes); priv->known_vendor_prefixes = NULL; g_slist_foreach (priv->cached_settings, (GFunc)shell_app_info_unref, NULL); g_slist_free (priv->cached_settings); priv->cached_settings = NULL; G_OBJECT_CLASS (shell_app_system_parent_class)->finalize(object); }
static gboolean on_tree_changed (gpointer user_data) { ShellAppSystem *self = SHELL_APP_SYSTEM (user_data); reread_menus (self); g_signal_emit (self, signals[INSTALLED_CHANGED], 0); self->priv->app_change_timeout_id = 0; return FALSE; }
static void shell_app_system_finalize (GObject *object) { ShellAppSystem *self = SHELL_APP_SYSTEM (object); ShellAppSystemPrivate *priv = self->priv; g_hash_table_destroy (priv->running_apps); g_hash_table_destroy (priv->id_to_app); g_hash_table_destroy (priv->startup_wm_class_to_id); G_OBJECT_CLASS (shell_app_system_parent_class)->finalize (object); }
static void shell_app_system_finalize (GObject *object) { ShellAppSystem *self = SHELL_APP_SYSTEM (object); ShellAppSystemPrivate *priv = self->priv; g_object_unref (priv->apps_tree); g_object_unref (priv->settings_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->setting_id_to_app); g_slist_free_full (priv->known_vendor_prefixes, g_free); priv->known_vendor_prefixes = NULL; G_OBJECT_CLASS (shell_app_system_parent_class)->finalize (object); }
static void on_settings_tree_changed_cb (GMenuTree *tree, gpointer user_data) { ShellAppSystem *self = SHELL_APP_SYSTEM (user_data); GError *error = NULL; GHashTable *new_settings; GHashTableIter iter; gpointer key, value; g_assert (tree == self->priv->settings_tree); g_hash_table_remove_all (self->priv->setting_id_to_app); if (!gmenu_tree_load_sync (self->priv->settings_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_settings = get_flattened_entries_from_tree (tree); g_hash_table_iter_init (&iter, new_settings); while (g_hash_table_iter_next (&iter, &key, &value)) { const char *id = key; GMenuTreeEntry *entry = value; ShellApp *app; app = _shell_app_new (entry); g_hash_table_replace (self->priv->setting_id_to_app, (char*)id, app); } g_hash_table_destroy (new_settings); }
static void on_tree_changed_cb (GMenuTree *monitor, gpointer user_data) { ShellAppSystem *self = SHELL_APP_SYSTEM (user_data); /* GMenu currently gives us a separate notification on the entire * menu tree for each node in the tree that might potentially have * changed. (See http://bugzilla.gnome.org/show_bug.cgi?id=172046.) * We need to compress these to avoid doing large extra amounts of * work. * * Even when that bug is fixed, compression is still useful; for one * thing we want to need to compress across notifications of changes * to the settings tree. Second we want to compress if multiple * changes are made to the desktop files at different times but in * short succession. */ if (self->priv->app_change_timeout_id != 0) return; self->priv->app_change_timeout_id = g_timeout_add_full (G_PRIORITY_DEFAULT_IDLE, 3000, (GSourceFunc) on_tree_changed, self, NULL); }
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; GSList *removed_apps = NULL; GSList *removed_node; 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; 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) 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)) removed_apps = g_slist_prepend (removed_apps, (char*)id); } for (removed_node = removed_apps; removed_node; removed_node = removed_node->next) { const char *id = removed_node->data; g_hash_table_remove (self->priv->id_to_app, id); } g_slist_free (removed_apps); g_hash_table_destroy (new_apps); g_signal_emit (self, signals[INSTALLED_CHANGED], 0); }