summaryrefslogtreecommitdiffstats
path: root/src/nautilus-shell-search-provider.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nautilus-shell-search-provider.c')
-rw-r--r--src/nautilus-shell-search-provider.c875
1 files changed, 875 insertions, 0 deletions
diff --git a/src/nautilus-shell-search-provider.c b/src/nautilus-shell-search-provider.c
new file mode 100644
index 0000000..55e9e1a
--- /dev/null
+++ b/src/nautilus-shell-search-provider.c
@@ -0,0 +1,875 @@
+/*
+ * nautilus-shell-search-provider.c - Implementation of a GNOME Shell
+ * search provider
+ *
+ * Copyright (C) 2012 Red Hat, Inc.
+ *
+ * Nautilus is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * Nautilus is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Cosimo Cecchi <cosimoc@gnome.org>
+ *
+ */
+
+#include <config.h>
+
+#include <gio/gio.h>
+#include <string.h>
+#include <glib/gi18n.h>
+#include <gdk/gdk.h>
+
+#include "nautilus-file.h"
+#include "nautilus-file-utilities.h"
+#include "nautilus-search-engine.h"
+#include "nautilus-search-provider.h"
+#include "nautilus-ui-utilities.h"
+
+#include "nautilus-application.h"
+#include "nautilus-bookmark-list.h"
+#include "nautilus-shell-search-provider-generated.h"
+#include "nautilus-shell-search-provider.h"
+
+typedef struct
+{
+ NautilusShellSearchProvider *self;
+
+ NautilusSearchEngine *engine;
+ NautilusQuery *query;
+
+ GHashTable *hits;
+ GDBusMethodInvocation *invocation;
+
+ gint64 start_time;
+} PendingSearch;
+
+struct _NautilusShellSearchProvider
+{
+ GObject parent;
+
+ NautilusShellSearchProvider2 *skeleton;
+
+ PendingSearch *current_search;
+
+ GList *metas_requests;
+ GHashTable *metas_cache;
+};
+
+G_DEFINE_TYPE (NautilusShellSearchProvider, nautilus_shell_search_provider, G_TYPE_OBJECT)
+
+static gchar *
+get_display_name (NautilusShellSearchProvider *self,
+ NautilusFile *file)
+{
+ GFile *location;
+ NautilusBookmark *bookmark;
+ NautilusBookmarkList *bookmarks;
+
+ bookmarks = nautilus_application_get_bookmarks (NAUTILUS_APPLICATION (g_application_get_default ()));
+
+ location = nautilus_file_get_location (file);
+ bookmark = nautilus_bookmark_list_item_with_location (bookmarks, location, NULL);
+ g_object_unref (location);
+
+ if (bookmark)
+ {
+ return g_strdup (nautilus_bookmark_get_name (bookmark));
+ }
+ else
+ {
+ return nautilus_file_get_display_name (file);
+ }
+}
+
+static GIcon *
+get_gicon (NautilusShellSearchProvider *self,
+ NautilusFile *file)
+{
+ GFile *location;
+ NautilusBookmark *bookmark;
+ NautilusBookmarkList *bookmarks;
+
+ bookmarks = nautilus_application_get_bookmarks (NAUTILUS_APPLICATION (g_application_get_default ()));
+
+ location = nautilus_file_get_location (file);
+ bookmark = nautilus_bookmark_list_item_with_location (bookmarks, location, NULL);
+ g_object_unref (location);
+
+ if (bookmark)
+ {
+ return nautilus_bookmark_get_icon (bookmark);
+ }
+ else
+ {
+ return nautilus_file_get_gicon (file, 0);
+ }
+}
+
+static void
+pending_search_free (PendingSearch *search)
+{
+ g_hash_table_destroy (search->hits);
+ g_clear_object (&search->query);
+ g_clear_object (&search->engine);
+ g_clear_object (&search->invocation);
+
+ g_slice_free (PendingSearch, search);
+}
+
+static void
+pending_search_finish (PendingSearch *search,
+ GDBusMethodInvocation *invocation,
+ GVariant *result)
+{
+ NautilusShellSearchProvider *self = search->self;
+
+ g_dbus_method_invocation_return_value (invocation, result);
+
+ if (search == self->current_search)
+ {
+ self->current_search = NULL;
+ }
+
+ g_application_release (g_application_get_default ());
+ pending_search_free (search);
+}
+
+static void
+cancel_current_search (NautilusShellSearchProvider *self)
+{
+ if (self->current_search != NULL)
+ {
+ g_debug ("*** Cancel current search");
+ nautilus_search_provider_stop (NAUTILUS_SEARCH_PROVIDER (self->current_search->engine));
+ }
+}
+
+static void
+cancel_current_search_ignoring_partial_results (NautilusShellSearchProvider *self)
+{
+ if (self->current_search != NULL)
+ {
+ cancel_current_search (self);
+ g_signal_handlers_disconnect_by_data (G_OBJECT (self->current_search->engine),
+ self->current_search);
+ pending_search_finish (self->current_search, self->current_search->invocation,
+ g_variant_new ("(as)", NULL));
+ }
+}
+
+static void
+search_hits_added_cb (NautilusSearchEngine *engine,
+ GList *hits,
+ gpointer user_data)
+{
+ PendingSearch *search = user_data;
+ GList *l;
+ NautilusSearchHit *hit;
+ const gchar *hit_uri;
+
+ g_debug ("*** Search engine hits added");
+
+ for (l = hits; l != NULL; l = l->next)
+ {
+ hit = l->data;
+ nautilus_search_hit_compute_scores (hit, search->query);
+ hit_uri = nautilus_search_hit_get_uri (hit);
+ g_debug (" %s", hit_uri);
+
+ g_hash_table_replace (search->hits, g_strdup (hit_uri), g_object_ref (hit));
+ }
+}
+
+static gint
+search_hit_compare_relevance (gconstpointer a,
+ gconstpointer b)
+{
+ NautilusSearchHit *hit_a, *hit_b;
+ gdouble relevance_a, relevance_b;
+
+ hit_a = NAUTILUS_SEARCH_HIT ((gpointer) a);
+ hit_b = NAUTILUS_SEARCH_HIT ((gpointer) b);
+
+ relevance_a = nautilus_search_hit_get_relevance (hit_a);
+ relevance_b = nautilus_search_hit_get_relevance (hit_b);
+
+ if (relevance_a > relevance_b)
+ {
+ return -1;
+ }
+ else if (relevance_a == relevance_b)
+ {
+ return 0;
+ }
+
+ return 1;
+}
+
+static void
+search_finished_cb (NautilusSearchEngine *engine,
+ NautilusSearchProviderStatus status,
+ gpointer user_data)
+{
+ PendingSearch *search = user_data;
+ GList *hits, *l;
+ NautilusSearchHit *hit;
+ GVariantBuilder builder;
+ gint64 current_time;
+
+ current_time = g_get_monotonic_time ();
+ g_debug ("*** Search engine search finished - time elapsed %dms",
+ (gint) ((current_time - search->start_time) / 1000));
+
+ hits = g_hash_table_get_values (search->hits);
+ hits = g_list_sort (hits, search_hit_compare_relevance);
+
+ g_variant_builder_init (&builder, G_VARIANT_TYPE ("as"));
+
+ for (l = hits; l != NULL; l = l->next)
+ {
+ hit = l->data;
+ g_variant_builder_add (&builder, "s", nautilus_search_hit_get_uri (hit));
+ }
+
+ g_list_free (hits);
+ pending_search_finish (search, search->invocation,
+ g_variant_new ("(as)", &builder));
+}
+
+static void
+search_error_cb (NautilusSearchEngine *engine,
+ const gchar *error_message,
+ gpointer user_data)
+{
+ NautilusShellSearchProvider *self = user_data;
+ PendingSearch *search = self->current_search;
+
+ g_debug ("*** Search engine search error");
+ pending_search_finish (search, search->invocation,
+ g_variant_new ("(as)", NULL));
+}
+
+typedef struct
+{
+ gchar *uri;
+ gchar *string_for_compare;
+} SearchHitCandidate;
+
+static void
+search_hit_candidate_free (SearchHitCandidate *candidate)
+{
+ g_free (candidate->uri);
+ g_free (candidate->string_for_compare);
+
+ g_slice_free (SearchHitCandidate, candidate);
+}
+
+static SearchHitCandidate *
+search_hit_candidate_new (const gchar *uri,
+ const gchar *name)
+{
+ SearchHitCandidate *candidate = g_slice_new0 (SearchHitCandidate);
+
+ candidate->uri = g_strdup (uri);
+ candidate->string_for_compare = g_strdup (name);
+
+ return candidate;
+}
+
+static void
+search_add_volumes_and_bookmarks (PendingSearch *search)
+{
+ NautilusSearchHit *hit;
+ NautilusBookmark *bookmark;
+ const gchar *name;
+ gchar *string, *uri;
+ gdouble match;
+ GList *l, *m, *drives, *volumes, *mounts, *mounts_to_check, *candidates;
+ GDrive *drive;
+ GVolume *volume;
+ GMount *mount;
+ GFile *location;
+ SearchHitCandidate *candidate;
+ NautilusBookmarkList *bookmarks;
+ GList *all_bookmarks;
+ GVolumeMonitor *volume_monitor;
+
+ bookmarks = nautilus_application_get_bookmarks (NAUTILUS_APPLICATION (g_application_get_default ()));
+ all_bookmarks = nautilus_bookmark_list_get_all (bookmarks);
+ volume_monitor = g_volume_monitor_get ();
+ candidates = NULL;
+
+ /* first add bookmarks */
+ for (l = all_bookmarks; l != NULL; l = l->next)
+ {
+ bookmark = NAUTILUS_BOOKMARK (l->data);
+ name = nautilus_bookmark_get_name (bookmark);
+ if (name == NULL)
+ {
+ continue;
+ }
+
+ uri = nautilus_bookmark_get_uri (bookmark);
+ candidate = search_hit_candidate_new (uri, name);
+ candidates = g_list_prepend (candidates, candidate);
+
+ g_free (uri);
+ }
+
+ /* home dir */
+ uri = nautilus_get_home_directory_uri ();
+ candidate = search_hit_candidate_new (uri, _("Home"));
+ candidates = g_list_prepend (candidates, candidate);
+ g_free (uri);
+
+ /* trash */
+ candidate = search_hit_candidate_new ("trash:///", _("Trash"));
+ candidates = g_list_prepend (candidates, candidate);
+
+ /* now add mounts */
+ mounts_to_check = NULL;
+
+ /* first check all connected drives */
+ drives = g_volume_monitor_get_connected_drives (volume_monitor);
+ for (l = drives; l != NULL; l = l->next)
+ {
+ drive = l->data;
+ volumes = g_drive_get_volumes (drive);
+
+ for (m = volumes; m != NULL; m = m->next)
+ {
+ volume = m->data;
+ mount = g_volume_get_mount (volume);
+ if (mount != NULL)
+ {
+ mounts_to_check = g_list_prepend (mounts_to_check, mount);
+ }
+ }
+
+ g_list_free_full (volumes, g_object_unref);
+ }
+ g_list_free_full (drives, g_object_unref);
+
+ /* then volumes that don't have a drive */
+ volumes = g_volume_monitor_get_volumes (volume_monitor);
+ for (l = volumes; l != NULL; l = l->next)
+ {
+ volume = l->data;
+ drive = g_volume_get_drive (volume);
+
+ if (drive == NULL)
+ {
+ mount = g_volume_get_mount (volume);
+ if (mount != NULL)
+ {
+ mounts_to_check = g_list_prepend (mounts_to_check, mount);
+ }
+ }
+ g_clear_object (&drive);
+ }
+ g_list_free_full (volumes, g_object_unref);
+
+ /* then mounts that have no volume */
+ mounts = g_volume_monitor_get_mounts (volume_monitor);
+ for (l = mounts; l != NULL; l = l->next)
+ {
+ mount = l->data;
+
+ if (g_mount_is_shadowed (mount))
+ {
+ continue;
+ }
+
+ volume = g_mount_get_volume (mount);
+ if (volume == NULL)
+ {
+ mounts_to_check = g_list_prepend (mounts_to_check, g_object_ref (mount));
+ }
+ g_clear_object (&volume);
+ }
+ g_list_free_full (mounts, g_object_unref);
+
+ /* actually add mounts to candidates */
+ for (l = mounts_to_check; l != NULL; l = l->next)
+ {
+ mount = l->data;
+
+ string = g_mount_get_name (mount);
+ if (string == NULL)
+ {
+ continue;
+ }
+
+ location = g_mount_get_default_location (mount);
+ uri = g_file_get_uri (location);
+ candidate = search_hit_candidate_new (uri, string);
+ candidates = g_list_prepend (candidates, candidate);
+
+ g_free (uri);
+ g_free (string);
+ g_object_unref (location);
+ }
+ g_list_free_full (mounts_to_check, g_object_unref);
+
+ /* now do the actual string matching */
+ candidates = g_list_reverse (candidates);
+
+ for (l = candidates; l != NULL; l = l->next)
+ {
+ candidate = l->data;
+ match = nautilus_query_matches_string (search->query,
+ candidate->string_for_compare);
+
+ if (match > -1)
+ {
+ hit = nautilus_search_hit_new (candidate->uri);
+ nautilus_search_hit_set_fts_rank (hit, match);
+ nautilus_search_hit_compute_scores (hit, search->query);
+ g_hash_table_replace (search->hits, g_strdup (candidate->uri), hit);
+ }
+ }
+ g_list_free_full (candidates, (GDestroyNotify) search_hit_candidate_free);
+ g_object_unref (volume_monitor);
+}
+
+static NautilusQuery *
+shell_query_new (gchar **terms)
+{
+ NautilusQuery *query;
+ g_autoptr (GFile) home = NULL;
+ g_autofree gchar *terms_joined = NULL;
+
+ terms_joined = g_strjoinv (" ", terms);
+ home = g_file_new_for_path (g_get_home_dir ());
+
+ query = nautilus_query_new ();
+ nautilus_query_set_text (query, terms_joined);
+ nautilus_query_set_location (query, home);
+
+ return query;
+}
+
+static void
+execute_search (NautilusShellSearchProvider *self,
+ GDBusMethodInvocation *invocation,
+ gchar **terms)
+{
+ NautilusQuery *query;
+ PendingSearch *pending_search;
+
+ cancel_current_search (self);
+
+ /* don't attempt searches for a single character */
+ if (g_strv_length (terms) == 1 &&
+ g_utf8_strlen (terms[0], -1) == 1)
+ {
+ g_dbus_method_invocation_return_value (invocation, g_variant_new ("(as)", NULL));
+ return;
+ }
+
+ query = shell_query_new (terms);
+ nautilus_query_set_recursive (query, NAUTILUS_QUERY_RECURSIVE_INDEXED_ONLY);
+ nautilus_query_set_show_hidden_files (query, FALSE);
+
+ pending_search = g_slice_new0 (PendingSearch);
+ pending_search->invocation = g_object_ref (invocation);
+ pending_search->hits = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
+ pending_search->query = query;
+ pending_search->engine = nautilus_search_engine_new ();
+ pending_search->start_time = g_get_monotonic_time ();
+ pending_search->self = self;
+
+ g_signal_connect (pending_search->engine, "hits-added",
+ G_CALLBACK (search_hits_added_cb), pending_search);
+ g_signal_connect (pending_search->engine, "finished",
+ G_CALLBACK (search_finished_cb), pending_search);
+ g_signal_connect (pending_search->engine, "error",
+ G_CALLBACK (search_error_cb), pending_search);
+
+ self->current_search = pending_search;
+ g_application_hold (g_application_get_default ());
+
+ search_add_volumes_and_bookmarks (pending_search);
+
+ /* start searching */
+ g_debug ("*** Search engine search started");
+ nautilus_search_provider_set_query (NAUTILUS_SEARCH_PROVIDER (pending_search->engine),
+ query);
+ nautilus_search_provider_start (NAUTILUS_SEARCH_PROVIDER (pending_search->engine));
+}
+
+static gboolean
+handle_get_initial_result_set (NautilusShellSearchProvider2 *skeleton,
+ GDBusMethodInvocation *invocation,
+ gchar **terms,
+ gpointer user_data)
+{
+ NautilusShellSearchProvider *self = user_data;
+
+ g_debug ("****** GetInitialResultSet");
+ execute_search (self, invocation, terms);
+ return TRUE;
+}
+
+static gboolean
+handle_get_subsearch_result_set (NautilusShellSearchProvider2 *skeleton,
+ GDBusMethodInvocation *invocation,
+ gchar **previous_results,
+ gchar **terms,
+ gpointer user_data)
+{
+ NautilusShellSearchProvider *self = user_data;
+
+ g_debug ("****** GetSubSearchResultSet");
+ execute_search (self, invocation, terms);
+ return TRUE;
+}
+
+typedef struct
+{
+ NautilusShellSearchProvider *self;
+
+ gint64 start_time;
+ NautilusFileListHandle *handle;
+ GDBusMethodInvocation *invocation;
+
+ gchar **uris;
+} ResultMetasData;
+
+static void
+result_metas_data_free (ResultMetasData *data)
+{
+ g_clear_pointer (&data->handle, nautilus_file_list_cancel_call_when_ready);
+ g_clear_object (&data->self);
+ g_clear_object (&data->invocation);
+ g_strfreev (data->uris);
+
+ g_slice_free (ResultMetasData, data);
+}
+
+static void
+result_metas_return_from_cache (ResultMetasData *data)
+{
+ GVariantBuilder builder;
+ GVariant *meta;
+ gint64 current_time;
+ gint idx;
+
+ g_variant_builder_init (&builder, G_VARIANT_TYPE ("aa{sv}"));
+
+ if (data->uris)
+ {
+ for (idx = 0; data->uris[idx] != NULL; idx++)
+ {
+ meta = g_hash_table_lookup (data->self->metas_cache,
+ data->uris[idx]);
+ g_variant_builder_add_value (&builder, meta);
+ }
+ }
+
+ current_time = g_get_monotonic_time ();
+ g_debug ("*** GetResultMetas completed - time elapsed %dms",
+ (gint) ((current_time - data->start_time) / 1000));
+
+ g_dbus_method_invocation_return_value (data->invocation,
+ g_variant_new ("(aa{sv})", &builder));
+}
+
+static void
+result_metas_return_empty (ResultMetasData *data)
+{
+ g_clear_pointer (&data->uris, g_strfreev);
+ result_metas_return_from_cache (data);
+ result_metas_data_free (data);
+}
+
+static void
+cancel_result_meta_requests (NautilusShellSearchProvider *self)
+{
+ g_debug ("*** Cancel Results Meta requests");
+
+ g_list_free_full (self->metas_requests,
+ (GDestroyNotify) result_metas_return_empty);
+ self->metas_requests = NULL;
+}
+
+static void
+result_list_attributes_ready_cb (GList *file_list,
+ gpointer user_data)
+{
+ ResultMetasData *data = user_data;
+ GVariantBuilder meta;
+ NautilusFile *file;
+ GFile *file_location;
+ GList *l;
+ gchar *uri, *display_name;
+ gchar *path, *description;
+ gchar *thumbnail_path;
+ GIcon *gicon;
+ GFile *location;
+ GVariant *meta_variant;
+ gint icon_scale;
+
+ icon_scale = gdk_monitor_get_scale_factor (g_list_model_get_item (gdk_display_get_monitors (gdk_display_get_default ()), 0));
+
+ for (l = file_list; l != NULL; l = l->next)
+ {
+ file = l->data;
+ g_variant_builder_init (&meta, G_VARIANT_TYPE ("a{sv}"));
+
+ uri = nautilus_file_get_uri (file);
+ display_name = get_display_name (data->self, file);
+ file_location = nautilus_file_get_location (file);
+ path = g_file_get_path (file_location);
+ description = path ? g_path_get_dirname (path) : NULL;
+
+ g_variant_builder_add (&meta, "{sv}",
+ "id", g_variant_new_string (uri));
+ g_variant_builder_add (&meta, "{sv}",
+ "name", g_variant_new_string (display_name));
+ /* Some backends like trash:/// don't have a path, so we show the uri itself. */
+ g_variant_builder_add (&meta, "{sv}",
+ "description", g_variant_new_string (description ? description : uri));
+
+ gicon = NULL;
+ thumbnail_path = nautilus_file_get_thumbnail_path (file);
+
+ if (thumbnail_path != NULL)
+ {
+ location = g_file_new_for_path (thumbnail_path);
+ gicon = g_file_icon_new (location);
+
+ g_free (thumbnail_path);
+ g_object_unref (location);
+ }
+ else
+ {
+ gicon = get_gicon (data->self, file);
+ }
+
+ if (gicon == NULL)
+ {
+ gicon = G_ICON (nautilus_file_get_icon_texture (file, 128,
+ icon_scale,
+ NAUTILUS_FILE_ICON_FLAGS_USE_THUMBNAILS));
+ }
+
+ g_variant_builder_add (&meta, "{sv}",
+ "icon", g_icon_serialize (gicon));
+ g_object_unref (gicon);
+
+ meta_variant = g_variant_builder_end (&meta);
+ g_hash_table_insert (data->self->metas_cache,
+ g_strdup (uri), g_variant_ref_sink (meta_variant));
+
+ g_free (display_name);
+ g_free (path);
+ g_free (description);
+ g_free (uri);
+ }
+
+ data->handle = NULL;
+ data->self->metas_requests = g_list_remove (data->self->metas_requests, data);
+
+ result_metas_return_from_cache (data);
+ result_metas_data_free (data);
+}
+
+static gboolean
+handle_get_result_metas (NautilusShellSearchProvider2 *skeleton,
+ GDBusMethodInvocation *invocation,
+ gchar **results,
+ gpointer user_data)
+{
+ NautilusShellSearchProvider *self = user_data;
+ GList *missing_files = NULL;
+ const gchar *uri;
+ ResultMetasData *data;
+ gint idx;
+
+ g_debug ("****** GetResultMetas");
+
+ for (idx = 0; results[idx] != NULL; idx++)
+ {
+ uri = results[idx];
+
+ if (!g_hash_table_lookup (self->metas_cache, uri))
+ {
+ missing_files = g_list_prepend (missing_files, nautilus_file_get_by_uri (uri));
+ }
+ }
+
+ data = g_slice_new0 (ResultMetasData);
+ data->self = g_object_ref (self);
+ data->invocation = g_object_ref (invocation);
+ data->start_time = g_get_monotonic_time ();
+ data->uris = g_strdupv (results);
+
+ if (missing_files == NULL)
+ {
+ result_metas_return_from_cache (data);
+ result_metas_data_free (data);
+ return TRUE;
+ }
+
+ nautilus_file_list_call_when_ready (missing_files,
+ NAUTILUS_FILE_ATTRIBUTES_FOR_ICON,
+ &data->handle,
+ result_list_attributes_ready_cb,
+ data);
+ self->metas_requests = g_list_prepend (self->metas_requests, data);
+ nautilus_file_list_free (missing_files);
+ return TRUE;
+}
+
+typedef struct
+{
+ GFile *file;
+ NautilusShellSearchProvider2 *skeleton;
+ GDBusMethodInvocation *invocation;
+} ShowURIData;
+
+static void
+show_uri_callback (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ ShowURIData *data = user_data;
+
+ if (!gtk_show_uri_full_finish (NULL, result, NULL))
+ {
+ g_application_open (g_application_get_default (), &data->file, 1, "");
+ }
+
+ nautilus_shell_search_provider2_complete_activate_result (data->skeleton, data->invocation);
+
+ g_object_unref (data->file);
+ g_free (data);
+}
+
+static gboolean
+handle_activate_result (NautilusShellSearchProvider2 *skeleton,
+ GDBusMethodInvocation *invocation,
+ gchar *result,
+ gchar **terms,
+ guint32 timestamp,
+ gpointer user_data)
+{
+ ShowURIData *data;
+
+ data = g_new (ShowURIData, 1);
+ data->file = g_file_new_for_uri (result);
+ data->skeleton = skeleton;
+ data->invocation = invocation;
+
+ gtk_show_uri_full (NULL, result, timestamp, NULL, show_uri_callback, data);
+
+ return TRUE;
+}
+
+static gboolean
+handle_launch_search (NautilusShellSearchProvider2 *skeleton,
+ GDBusMethodInvocation *invocation,
+ gchar **terms,
+ guint32 timestamp,
+ gpointer user_data)
+{
+ GApplication *app = g_application_get_default ();
+ g_autoptr (NautilusQuery) query = shell_query_new (terms);
+ NautilusQueryRecursive recursive = location_settings_search_get_recursive ();
+
+ /*
+ * If no recursive search is enabled, we still want to be able to
+ * show the same results we presented in the overview when nautilus
+ * is explicitly launched to access to more results, and thus we perform
+ * a query showing results coming from index-based search engines.
+ * Otherwise we respect the global setting for recursivity.
+ */
+ if (recursive == NAUTILUS_QUERY_RECURSIVE_NEVER)
+ {
+ nautilus_query_set_recursive (query,
+ NAUTILUS_QUERY_RECURSIVE_INDEXED_ONLY);
+ }
+ else
+ {
+ nautilus_query_set_recursive (query, recursive);
+ }
+
+ nautilus_application_search (NAUTILUS_APPLICATION (app), query);
+
+ nautilus_shell_search_provider2_complete_launch_search (skeleton, invocation);
+ return TRUE;
+}
+
+static void
+search_provider_dispose (GObject *obj)
+{
+ NautilusShellSearchProvider *self = NAUTILUS_SHELL_SEARCH_PROVIDER (obj);
+
+ g_clear_object (&self->skeleton);
+ g_hash_table_destroy (self->metas_cache);
+ cancel_current_search_ignoring_partial_results (self);
+ cancel_result_meta_requests (self);
+
+ G_OBJECT_CLASS (nautilus_shell_search_provider_parent_class)->dispose (obj);
+}
+
+static void
+nautilus_shell_search_provider_init (NautilusShellSearchProvider *self)
+{
+ self->metas_cache = g_hash_table_new_full (g_str_hash, g_str_equal,
+ g_free, (GDestroyNotify) g_variant_unref);
+
+ self->skeleton = nautilus_shell_search_provider2_skeleton_new ();
+
+ g_signal_connect (self->skeleton, "handle-get-initial-result-set",
+ G_CALLBACK (handle_get_initial_result_set), self);
+ g_signal_connect (self->skeleton, "handle-get-subsearch-result-set",
+ G_CALLBACK (handle_get_subsearch_result_set), self);
+ g_signal_connect (self->skeleton, "handle-get-result-metas",
+ G_CALLBACK (handle_get_result_metas), self);
+ g_signal_connect (self->skeleton, "handle-activate-result",
+ G_CALLBACK (handle_activate_result), self);
+ g_signal_connect (self->skeleton, "handle-launch-search",
+ G_CALLBACK (handle_launch_search), self);
+}
+
+static void
+nautilus_shell_search_provider_class_init (NautilusShellSearchProviderClass *klass)
+{
+ GObjectClass *oclass = G_OBJECT_CLASS (klass);
+
+ oclass->dispose = search_provider_dispose;
+}
+
+NautilusShellSearchProvider *
+nautilus_shell_search_provider_new (void)
+{
+ return g_object_new (nautilus_shell_search_provider_get_type (),
+ NULL);
+}
+
+gboolean
+nautilus_shell_search_provider_register (NautilusShellSearchProvider *self,
+ GDBusConnection *connection,
+ GError **error)
+{
+ return g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (self->skeleton),
+ connection,
+ "/org/gnome/Nautilus" PROFILE "/SearchProvider", error);
+}
+
+void
+nautilus_shell_search_provider_unregister (NautilusShellSearchProvider *self)
+{
+ g_dbus_interface_skeleton_unexport (G_DBUS_INTERFACE_SKELETON (self->skeleton));
+}