From 0a7b552d936fef90111dfb69c112fcf2a8add8ca Mon Sep 17 00:00:00 2001 From: Cosimo Cecchi Date: Fri, 24 Oct 2014 12:55:48 -0700 Subject: [PATCH] search: use g_get_system_data_dirs() to discover providers Currently, the search panel looks for search providers in DATADIR/gnome-shell/search-providers. This won't work when providers are located in a different directory which is still part of XDG_DATA_DIRS, which is a valid use case and supported by gnome-shell. This commit refactors the loader code to scan all the directories upfront in a separate thread. https://bugzilla.gnome.org/show_bug.cgi?id=739148 --- panels/search/cc-search-panel.c | 201 ++++++++++++++++++++------------ 1 file changed, 127 insertions(+), 74 deletions(-) diff --git a/panels/search/cc-search-panel.c b/panels/search/cc-search-panel.c index 717c3e74f..d2a0f3b79 100644 --- a/panels/search/cc-search-panel.c +++ b/panels/search/cc-search-panel.c @@ -36,6 +36,7 @@ struct _CcSearchPanelPrivate GtkWidget *up_button; GtkWidget *down_button; + GCancellable *load_cancellable; GSettings *search_settings; GHashTable *sort_order; @@ -545,95 +546,146 @@ search_panel_add_one_provider (CcSearchPanel *self, } static void -next_search_provider_ready (GObject *source, - GAsyncResult *res, - gpointer user_data) +search_providers_discover_ready (GObject *source, + GAsyncResult *result, + gpointer user_data) { - CcSearchPanel *self = user_data; - GFile *providers_location, *provider; - GList *files; + GList *providers, *l; + GFile *provider; + CcSearchPanel *self = CC_SEARCH_PANEL (source); GError *error = NULL; - gchar *path; - files = g_file_enumerator_next_files_finish (G_FILE_ENUMERATOR (source), - res, &error); - providers_location = g_file_enumerator_get_container (G_FILE_ENUMERATOR (source)); + providers = g_task_propagate_pointer (G_TASK (result), &error); - if (error != NULL) + if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { - path = g_file_get_path (providers_location); - - g_warning ("Error reading from %s: %s - search providers might be missing from the panel", - path, error->message); - - g_error_free (error); - g_free (path); - } - - if (files != NULL) - { - provider = g_file_get_child (providers_location, g_file_info_get_name (files->data)); - search_panel_add_one_provider (self, provider); - g_object_unref (provider); - - g_file_enumerator_next_files_async (G_FILE_ENUMERATOR (source), 1, - G_PRIORITY_DEFAULT, NULL, - next_search_provider_ready, self); - } - else - { - /* propagate a write to GSettings, to make sure we always have - * all the providers in the list. - */ - search_panel_propagate_sort_order (self); - } - - g_list_free_full (files, g_object_unref); -} - -static void -enumerate_search_providers_ready (GObject *source, - GAsyncResult *res, - gpointer user_data) -{ - CcSearchPanel *self = user_data; - GFileEnumerator *enumerator; - GError *error = NULL; - gchar *path; - - enumerator = g_file_enumerate_children_finish (G_FILE (source), res, &error); - if (error != NULL) - { - path = g_file_get_path (G_FILE (source)); - - if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) - g_warning ("Error opening %s: %s - search provider configuration won't be possible", - path, error->message); - - search_panel_set_no_providers (self); g_error_free (error); return; } - g_file_enumerator_next_files_async (enumerator, 1, - G_PRIORITY_DEFAULT, NULL, - next_search_provider_ready, self); - g_object_unref (enumerator); + g_clear_object (&self->priv->load_cancellable); + + if (providers == NULL) + { + search_panel_set_no_providers (self); + return; + } + + for (l = providers; l != NULL; l = l->next) + { + provider = l->data; + search_panel_add_one_provider (self, provider); + g_object_unref (provider); + } + + /* propagate a write to GSettings, to make sure we always have + * all the providers in the list. + */ + search_panel_propagate_sort_order (self); + g_list_free (providers); +} + +static GList * +search_providers_discover_one_directory (const gchar *system_dir, + GCancellable *cancellable) +{ + GList *providers = NULL; + gchar *providers_path; + GFile *providers_location, *provider; + GFileInfo *info; + GFileEnumerator *enumerator; + GError *error = NULL; + + providers_path = g_build_filename (system_dir, "gnome-shell", "search-providers", NULL); + providers_location = g_file_new_for_path (providers_path); + + enumerator = g_file_enumerate_children (providers_location, + "standard::type,standard::name,standard::content-type", + G_FILE_QUERY_INFO_NONE, + cancellable, &error); + + if (error != NULL) + { + if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND) && + !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_warning ("Error opening %s: %s - search provider configuration won't be possible", + providers_path, error->message); + g_clear_error (&error); + + goto out; + } + + while ((info = g_file_enumerator_next_file (enumerator, cancellable, &error)) != NULL) + { + provider = g_file_get_child (providers_location, g_file_info_get_name (info)); + providers = g_list_prepend (providers, provider); + g_object_unref (info); + } + + if (error != NULL) + { + if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_warning ("Error reading from %s: %s - search providers might be missing from the panel", + providers_path, error->message); + g_clear_error (&error); + } + + out: + g_clear_object (&enumerator); + g_clear_object (&providers_location); + g_free (providers_path); + + return providers; +} + +static void +search_providers_discover_thread (GTask *task, + gpointer source_object, + gpointer task_data, + GCancellable *cancellable) +{ + GList *providers = NULL; + const gchar * const *system_data_dirs; + int idx; + + system_data_dirs = g_get_system_data_dirs (); + for (idx = 0; system_data_dirs[idx] != NULL; idx++) + { + providers = g_list_concat (search_providers_discover_one_directory (system_data_dirs[idx], cancellable), + providers); + + if (g_task_return_error_if_cancelled (task)) + { + g_list_free_full (providers, g_object_unref); + return; + } + } + + g_task_return_pointer (task, providers, NULL); } static void populate_search_providers (CcSearchPanel *self) { - GFile *providers_location; + GTask *task; - providers_location = g_file_new_for_path (DATADIR "/gnome-shell/search-providers"); - g_file_enumerate_children_async (providers_location, - "standard::type,standard::name,standard::content-type", - G_FILE_QUERY_INFO_NONE, - G_PRIORITY_DEFAULT, - NULL, - enumerate_search_providers_ready, self); - g_object_unref (providers_location); + self->priv->load_cancellable = g_cancellable_new (); + task = g_task_new (self, self->priv->load_cancellable, + search_providers_discover_ready, self); + g_task_run_in_thread (task, search_providers_discover_thread); + g_object_unref (task); +} + +static void +cc_search_panel_dispose (GObject *object) +{ + CcSearchPanelPrivate *priv = CC_SEARCH_PANEL (object)->priv; + + if (priv->load_cancellable != NULL) + g_cancellable_cancel (priv->load_cancellable); + g_clear_object (&priv->load_cancellable); + + G_OBJECT_CLASS (cc_search_panel_parent_class)->dispose (object); } static void @@ -752,6 +804,7 @@ cc_search_panel_class_init (CcSearchPanelClass *klass) GObjectClass *oclass = G_OBJECT_CLASS (klass); oclass->constructed = cc_search_panel_constructed; + oclass->dispose = cc_search_panel_dispose; oclass->finalize = cc_search_panel_finalize; g_type_class_add_private (klass, sizeof (CcSearchPanelPrivate));