/*
* 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;
}