/* * Copyright (C) 2005 Novell, 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; see the file COPYING. If not, * see . * * Author: Anders Carlsson * */ #include #include #include "nautilus-search-provider.h" #include "nautilus-search-engine.h" #include "nautilus-search-engine-simple.h" #include "nautilus-search-engine-model.h" #define DEBUG_FLAG NAUTILUS_DEBUG_SEARCH #include "nautilus-debug.h" #include "nautilus-search-engine-tracker.h" typedef struct { GQueue *providers; GHashTable *uris; guint providers_error; gboolean running; gboolean restart; NautilusDirectory *directory; NautilusQuery *query; gboolean recursive; } NautilusSearchEnginePrivate; enum { PROP_0, PROP_RECURSIVE, PROP_RUNNING, LAST_PROP }; static void nautilus_search_provider_init (NautilusSearchProviderInterface *iface); static gboolean nautilus_search_engine_is_running (NautilusSearchProvider *provider); static void connect_provider_signals (NautilusSearchEngine *engine, NautilusSearchProvider *provider); static void disconnect_provider_signals (NautilusSearchEngine *engine, NautilusSearchProvider *provider); G_DEFINE_TYPE_WITH_CODE (NautilusSearchEngine, nautilus_search_engine, G_TYPE_OBJECT, G_ADD_PRIVATE (NautilusSearchEngine) G_IMPLEMENT_INTERFACE (NAUTILUS_TYPE_SEARCH_PROVIDER, nautilus_search_provider_init)) static void clear_providers_queue (NautilusSearchEngine *engine) { NautilusSearchEnginePrivate *priv; GList *l; priv = nautilus_search_engine_get_instance_private (engine); for (l = g_queue_peek_head_link (priv->providers); l != NULL; l = l->next) { disconnect_provider_signals (engine, NAUTILUS_SEARCH_PROVIDER (l->data)); g_clear_object (&l->data); } g_queue_clear (priv->providers); } void nautilus_search_engine_set_model (NautilusSearchEngine *engine, NautilusDirectory *directory) { NautilusSearchEnginePrivate *priv; priv = nautilus_search_engine_get_instance_private (engine); g_clear_object (&priv->directory); priv->directory = g_object_ref (directory); } static void nautilus_search_engine_set_query (NautilusSearchProvider *provider, NautilusQuery *query) { NautilusSearchEngine *engine; NautilusSearchEnginePrivate *priv; engine = NAUTILUS_SEARCH_ENGINE (provider); priv = nautilus_search_engine_get_instance_private (engine); g_object_ref (query); g_clear_object (&priv->query); priv->query = query; } static void search_engine_start_real (NautilusSearchEngine *engine) { NautilusSearchEnginePrivate *priv; NautilusSearchEngineTracker *tracker; NautilusSearchEngineSimple *simple; NautilusSearchEngineModel *model; GList *l; priv = nautilus_search_engine_get_instance_private (engine); clear_providers_queue (engine); tracker = nautilus_search_engine_tracker_new (); g_queue_push_head (priv->providers, tracker); connect_provider_signals (engine, NAUTILUS_SEARCH_PROVIDER (tracker)); simple = nautilus_search_engine_simple_new (); connect_provider_signals (engine, NAUTILUS_SEARCH_PROVIDER (simple)); g_object_set (simple, "recursive", priv->recursive, NULL); g_queue_push_head (priv->providers, simple); if (priv->directory) { model = nautilus_search_engine_model_new (); connect_provider_signals (engine, NAUTILUS_SEARCH_PROVIDER (model)); nautilus_search_engine_model_set_model (model, priv->directory); g_queue_push_head (priv->providers, model); } priv->providers_error = 0; priv->restart = FALSE; DEBUG ("Search engine start real"); g_object_ref (engine); for (l = g_queue_peek_head_link (priv->providers); l != NULL; l = l->next) { nautilus_search_provider_set_query (NAUTILUS_SEARCH_PROVIDER (l->data), priv->query); nautilus_search_provider_start (NAUTILUS_SEARCH_PROVIDER (l->data)); } } static void nautilus_search_engine_start (NautilusSearchProvider *provider) { NautilusSearchEngine *engine; NautilusSearchEnginePrivate *priv; gboolean all_providers_finished; GList *l; engine = NAUTILUS_SEARCH_ENGINE (provider); priv = nautilus_search_engine_get_instance_private (engine); DEBUG ("Search engine start"); all_providers_finished = TRUE; for (l = g_queue_peek_head_link (priv->providers); l != NULL; l = l->next) { all_providers_finished &= nautilus_search_provider_is_finished (NAUTILUS_SEARCH_PROVIDER (l->data)); } if (priv->running) { if (all_providers_finished && priv->restart) { search_engine_start_real (engine); } return; } priv->running = TRUE; g_object_notify (G_OBJECT (provider), "running"); if (!all_providers_finished) { priv->restart = TRUE; } else { search_engine_start_real (engine); } } static void nautilus_search_engine_stop (NautilusSearchProvider *provider) { NautilusSearchEngine *engine; NautilusSearchEnginePrivate *priv; GList *l; engine = NAUTILUS_SEARCH_ENGINE (provider); priv = nautilus_search_engine_get_instance_private (engine); DEBUG ("Search engine stop"); for (l = g_queue_peek_head_link (priv->providers); l != NULL; l = l->next) { nautilus_search_provider_stop (NAUTILUS_SEARCH_PROVIDER (l->data)); } priv->running = FALSE; priv->restart = FALSE; g_object_notify (G_OBJECT (provider), "running"); } static void search_provider_hits_added (NautilusSearchProvider *provider, GList *hits, NautilusSearchEngine *engine) { NautilusSearchEnginePrivate *priv; GList *added = NULL; GList *l; priv = nautilus_search_engine_get_instance_private (engine); if (!priv->running || priv->restart) { DEBUG ("Ignoring hits-added, since engine is %s", !priv->running ? "not running" : "waiting to restart"); return; } for (l = hits; l != NULL; l = l->next) { NautilusSearchHit *hit = l->data; int count; const char *uri; uri = nautilus_search_hit_get_uri (hit); count = GPOINTER_TO_INT (g_hash_table_lookup (priv->uris, uri)); if (count == 0) { added = g_list_prepend (added, hit); } g_hash_table_replace (priv->uris, g_strdup (uri), GINT_TO_POINTER (++count)); } if (added != NULL) { added = g_list_reverse (added); nautilus_search_provider_hits_added (NAUTILUS_SEARCH_PROVIDER (engine), added); g_list_free (added); } } static void check_providers_status (NautilusSearchEngine *engine) { NautilusSearchEnginePrivate *priv; gboolean all_providers_finished; GList *l; priv = nautilus_search_engine_get_instance_private (engine); all_providers_finished = TRUE; for (l = g_queue_peek_head_link (priv->providers); l != NULL; l = l->next) { all_providers_finished &= nautilus_search_provider_is_finished (NAUTILUS_SEARCH_PROVIDER (l->data)); } if (!all_providers_finished) { return; } if (g_queue_get_length (priv->providers) == priv->providers_error) { DEBUG ("Search engine error"); nautilus_search_provider_error (NAUTILUS_SEARCH_PROVIDER (engine), _("Unable to complete the requested search")); } else { if (priv->restart) { DEBUG ("Search engine finished and restarting"); } else { DEBUG ("Search engine finished"); } nautilus_search_provider_finished (NAUTILUS_SEARCH_PROVIDER (engine), priv->restart ? NAUTILUS_SEARCH_PROVIDER_STATUS_RESTARTING : NAUTILUS_SEARCH_PROVIDER_STATUS_NORMAL); } priv->running = FALSE; g_object_notify (G_OBJECT (engine), "running"); g_hash_table_remove_all (priv->uris); if (priv->restart) { nautilus_search_engine_start (NAUTILUS_SEARCH_PROVIDER (engine)); } g_object_unref (engine); } static void search_provider_error (NautilusSearchProvider *provider, const char *error_message, NautilusSearchEngine *engine) { NautilusSearchEnginePrivate *priv; DEBUG ("Search provider error: %s", error_message); priv = nautilus_search_engine_get_instance_private (engine); priv->providers_error++; check_providers_status (engine); } static void search_provider_finished (NautilusSearchProvider *provider, NautilusSearchProviderStatus status, NautilusSearchEngine *engine) { DEBUG ("Search provider finished"); check_providers_status (engine); } static void connect_provider_signals (NautilusSearchEngine *engine, NautilusSearchProvider *provider) { g_signal_connect (provider, "hits-added", G_CALLBACK (search_provider_hits_added), engine); g_signal_connect (provider, "finished", G_CALLBACK (search_provider_finished), engine); g_signal_connect (provider, "error", G_CALLBACK (search_provider_error), engine); } static void disconnect_provider_signals (NautilusSearchEngine *engine, NautilusSearchProvider *provider) { g_signal_handlers_disconnect_by_func (provider, search_provider_hits_added, engine); g_signal_handlers_disconnect_by_func (provider, search_provider_finished, engine); g_signal_handlers_disconnect_by_func (provider, search_provider_error, engine); } static gboolean nautilus_search_engine_is_running (NautilusSearchProvider *provider) { NautilusSearchEngine *engine; NautilusSearchEnginePrivate *priv; engine = NAUTILUS_SEARCH_ENGINE (provider); priv = nautilus_search_engine_get_instance_private (engine); return priv->running; } static void nautilus_search_provider_init (NautilusSearchProviderInterface *iface) { iface->set_query = nautilus_search_engine_set_query; iface->start = nautilus_search_engine_start; iface->stop = nautilus_search_engine_stop; iface->is_running = nautilus_search_engine_is_running; } static void nautilus_search_engine_finalize (GObject *object) { NautilusSearchEngine *engine; NautilusSearchEnginePrivate *priv; engine = NAUTILUS_SEARCH_ENGINE (object); priv = nautilus_search_engine_get_instance_private (engine); g_hash_table_destroy (priv->uris); clear_providers_queue (engine); g_queue_free (priv->providers); g_clear_object (&priv->directory); g_clear_object (&priv->query); G_OBJECT_CLASS (nautilus_search_engine_parent_class)->finalize (object); } static void nautilus_search_engine_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { NautilusSearchEnginePrivate *priv; NautilusSearchProvider *self = NAUTILUS_SEARCH_PROVIDER (object); priv = nautilus_search_engine_get_instance_private (NAUTILUS_SEARCH_ENGINE (self)); switch (prop_id) { case PROP_RUNNING: { g_value_set_boolean (value, nautilus_search_engine_is_running (self)); } break; case PROP_RECURSIVE: { g_value_set_boolean (value, priv->recursive); } break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void nautilus_search_engine_set_property (GObject *object, guint arg_id, const GValue *value, GParamSpec *pspec) { NautilusSearchEnginePrivate *priv; NautilusSearchEngine *engine = NAUTILUS_SEARCH_ENGINE (object); priv = nautilus_search_engine_get_instance_private (NAUTILUS_SEARCH_ENGINE (engine)); switch (arg_id) { case PROP_RECURSIVE: { priv->recursive = g_value_get_boolean (value); } break; default: { G_OBJECT_WARN_INVALID_PROPERTY_ID (object, arg_id, pspec); } break; } } static void nautilus_search_engine_class_init (NautilusSearchEngineClass *class) { GObjectClass *object_class; object_class = (GObjectClass *) class; object_class->finalize = nautilus_search_engine_finalize; object_class->get_property = nautilus_search_engine_get_property; object_class->set_property = nautilus_search_engine_set_property; /** * NautilusSearchEngine::recursive: * * Whether the search is recursive or not. */ g_object_class_install_property (object_class, PROP_RECURSIVE, g_param_spec_boolean ("recursive", "recursive", "recursive", FALSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); /** * NautilusSearchEngine::running: * * Whether the search engine is running a search. */ g_object_class_override_property (object_class, PROP_RUNNING, "running"); } static void nautilus_search_engine_init (NautilusSearchEngine *engine) { NautilusSearchEnginePrivate *priv; priv = nautilus_search_engine_get_instance_private (engine); priv->uris = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); priv->providers = g_queue_new (); } NautilusSearchEngine * nautilus_search_engine_new (void) { NautilusSearchEngine *engine; engine = g_object_new (NAUTILUS_TYPE_SEARCH_ENGINE, NULL); return engine; }