490 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			490 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * Copyright (C) 2005 Novell, Inc.
 | 
						|
 *
 | 
						|
 * This library is free software; you can redistribute it and/or
 | 
						|
 * modify it under the terms of the GNU Lesser General Public
 | 
						|
 * License as published by the Free Software Foundation; either
 | 
						|
 * version 2 of the License, or (at your option) any later version.
 | 
						|
 *
 | 
						|
 * This library 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
 | 
						|
 * Lesser General Public License for more details.
 | 
						|
 *
 | 
						|
 * You should have received a copy of the GNU Lesser General Public
 | 
						|
 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
 | 
						|
 *
 | 
						|
 * Author: Anders Carlsson <andersca@imendio.com>
 | 
						|
 *
 | 
						|
 * Based on nautilus-search-engine.c
 | 
						|
 */
 | 
						|
 | 
						|
#include "config.h"
 | 
						|
#include "gtksearchengine.h"
 | 
						|
#include "gtksearchenginesimple.h"
 | 
						|
#include "gtksearchenginetracker.h"
 | 
						|
#include "gtksearchenginemodel.h"
 | 
						|
#include "gtksearchenginequartz.h"
 | 
						|
#include "gtkintl.h"
 | 
						|
 | 
						|
#include <gdk/gdk.h> /* for GDK_WINDOWING_QUARTZ */
 | 
						|
 | 
						|
#ifndef G_OS_WIN32  /* No tracker on Windows */
 | 
						|
#define HAVE_TRACKER 1
 | 
						|
#endif
 | 
						|
 | 
						|
struct _GtkSearchEnginePrivate {
 | 
						|
  GtkSearchEngine *native;
 | 
						|
  gboolean native_running;
 | 
						|
  gchar *native_error;
 | 
						|
 | 
						|
  GtkSearchEngine *simple;
 | 
						|
  gboolean simple_running;
 | 
						|
  gchar *simple_error;
 | 
						|
 | 
						|
  GtkSearchEngine *model;
 | 
						|
  gboolean model_running;
 | 
						|
  gchar *model_error;
 | 
						|
 | 
						|
  gboolean running;
 | 
						|
  gboolean recursive;
 | 
						|
  GHashTable *hits;
 | 
						|
 | 
						|
  GtkQuery *query;
 | 
						|
};
 | 
						|
 | 
						|
enum
 | 
						|
{
 | 
						|
  HITS_ADDED,
 | 
						|
  FINISHED,
 | 
						|
  ERROR,
 | 
						|
  LAST_SIGNAL
 | 
						|
};
 | 
						|
 | 
						|
static guint signals[LAST_SIGNAL];
 | 
						|
 | 
						|
G_DEFINE_TYPE_WITH_PRIVATE (GtkSearchEngine, _gtk_search_engine, G_TYPE_OBJECT);
 | 
						|
 | 
						|
static void
 | 
						|
set_query (GtkSearchEngine *engine,
 | 
						|
           GtkQuery        *query)
 | 
						|
{
 | 
						|
  g_set_object (&engine->priv->query, query);
 | 
						|
 | 
						|
  if (engine->priv->native)
 | 
						|
    _gtk_search_engine_set_query (engine->priv->native, query);
 | 
						|
 | 
						|
  if (engine->priv->simple)
 | 
						|
    _gtk_search_engine_set_query (engine->priv->simple, query);
 | 
						|
 | 
						|
  if (engine->priv->model)
 | 
						|
    _gtk_search_engine_set_query (engine->priv->model, query);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
start (GtkSearchEngine *engine)
 | 
						|
{
 | 
						|
  g_hash_table_remove_all (engine->priv->hits);
 | 
						|
 | 
						|
  if (engine->priv->native)
 | 
						|
    {
 | 
						|
      g_clear_pointer (&engine->priv->native_error, g_free);
 | 
						|
      _gtk_search_engine_start (engine->priv->native);
 | 
						|
      engine->priv->native_running = TRUE;
 | 
						|
    }
 | 
						|
 | 
						|
  if (engine->priv->simple)
 | 
						|
    {
 | 
						|
      g_clear_pointer (&engine->priv->simple_error, g_free);
 | 
						|
      _gtk_search_engine_start (engine->priv->simple);
 | 
						|
      engine->priv->simple_running = TRUE;
 | 
						|
    }
 | 
						|
 | 
						|
  if (engine->priv->model)
 | 
						|
    {
 | 
						|
      g_clear_pointer (&engine->priv->model_error, g_free);
 | 
						|
      _gtk_search_engine_start (engine->priv->model);
 | 
						|
      engine->priv->model_running = TRUE;
 | 
						|
    }
 | 
						|
 | 
						|
  engine->priv->running = TRUE;
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
stop (GtkSearchEngine *engine)
 | 
						|
{
 | 
						|
  if (engine->priv->native)
 | 
						|
    {
 | 
						|
      _gtk_search_engine_stop (engine->priv->native);
 | 
						|
      engine->priv->native_running = FALSE;
 | 
						|
    }
 | 
						|
 | 
						|
  if (engine->priv->simple)
 | 
						|
    {
 | 
						|
      _gtk_search_engine_stop (engine->priv->simple);
 | 
						|
      engine->priv->simple_running = FALSE;
 | 
						|
    }
 | 
						|
 | 
						|
  if (engine->priv->model)
 | 
						|
    {
 | 
						|
      _gtk_search_engine_stop (engine->priv->model);
 | 
						|
      engine->priv->model_running = FALSE;
 | 
						|
    }
 | 
						|
 | 
						|
  engine->priv->running = FALSE;
 | 
						|
 | 
						|
  g_hash_table_remove_all (engine->priv->hits);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
finalize (GObject *object)
 | 
						|
{
 | 
						|
  GtkSearchEngine *engine = GTK_SEARCH_ENGINE (object);
 | 
						|
 | 
						|
  g_clear_object (&engine->priv->native);
 | 
						|
  g_free (engine->priv->native_error);
 | 
						|
 | 
						|
  g_clear_object (&engine->priv->simple);
 | 
						|
  g_free (engine->priv->simple_error);
 | 
						|
 | 
						|
  g_clear_object (&engine->priv->model);
 | 
						|
  g_free (engine->priv->model_error);
 | 
						|
 | 
						|
  g_clear_pointer (&engine->priv->hits, g_hash_table_unref);
 | 
						|
 | 
						|
  g_clear_object (&engine->priv->query);
 | 
						|
 | 
						|
  G_OBJECT_CLASS (_gtk_search_engine_parent_class)->finalize (object);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
_gtk_search_engine_class_init (GtkSearchEngineClass *class)
 | 
						|
{
 | 
						|
  GObjectClass *object_class = G_OBJECT_CLASS (class);
 | 
						|
 | 
						|
  object_class->finalize = finalize;
 | 
						|
 | 
						|
  class->set_query = set_query;
 | 
						|
  class->start = start;
 | 
						|
  class->stop = stop;
 | 
						|
 | 
						|
  signals[HITS_ADDED] =
 | 
						|
    g_signal_new (I_("hits-added"),
 | 
						|
                  G_TYPE_FROM_CLASS (class),
 | 
						|
                  G_SIGNAL_RUN_LAST,
 | 
						|
                  G_STRUCT_OFFSET (GtkSearchEngineClass, hits_added),
 | 
						|
                  NULL, NULL,
 | 
						|
                  g_cclosure_marshal_VOID__POINTER,
 | 
						|
                  G_TYPE_NONE, 1,
 | 
						|
                  G_TYPE_POINTER);
 | 
						|
 | 
						|
  signals[FINISHED] =
 | 
						|
    g_signal_new (I_("finished"),
 | 
						|
                  G_TYPE_FROM_CLASS (class),
 | 
						|
                  G_SIGNAL_RUN_LAST,
 | 
						|
                  G_STRUCT_OFFSET (GtkSearchEngineClass, finished),
 | 
						|
                  NULL, NULL,
 | 
						|
                  g_cclosure_marshal_VOID__VOID,
 | 
						|
                  G_TYPE_NONE, 0);
 | 
						|
 | 
						|
  signals[ERROR] =
 | 
						|
    g_signal_new (I_("error"),
 | 
						|
                  G_TYPE_FROM_CLASS (class),
 | 
						|
                  G_SIGNAL_RUN_LAST,
 | 
						|
                  G_STRUCT_OFFSET (GtkSearchEngineClass, error),
 | 
						|
                  NULL, NULL,
 | 
						|
                  g_cclosure_marshal_VOID__STRING,
 | 
						|
                  G_TYPE_NONE, 1,
 | 
						|
                  G_TYPE_STRING);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
_gtk_search_engine_init (GtkSearchEngine *engine)
 | 
						|
{
 | 
						|
  engine->priv = _gtk_search_engine_get_instance_private (engine);
 | 
						|
 | 
						|
  engine->priv->recursive = TRUE;
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
hits_added (GtkSearchEngine *engine,
 | 
						|
            GList           *hits,
 | 
						|
            gpointer         data)
 | 
						|
{
 | 
						|
  GtkSearchEngine *composite = GTK_SEARCH_ENGINE (data);
 | 
						|
  GList *added, *l;
 | 
						|
  GtkSearchHit *hit;
 | 
						|
 | 
						|
  added = NULL;
 | 
						|
 | 
						|
  for (l = hits; l; l = l->next)
 | 
						|
    {
 | 
						|
      hit = l->data;
 | 
						|
 | 
						|
      if (!g_hash_table_contains (composite->priv->hits, hit))
 | 
						|
        {
 | 
						|
          hit = _gtk_search_hit_dup (hit);
 | 
						|
          g_hash_table_add (composite->priv->hits, hit);
 | 
						|
          added = g_list_prepend (added, hit);
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
  if (added)
 | 
						|
    {
 | 
						|
      _gtk_search_engine_hits_added (composite, added);
 | 
						|
      g_list_free (added);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
update_status (GtkSearchEngine *engine)
 | 
						|
{
 | 
						|
  gboolean running;
 | 
						|
 | 
						|
  running = engine->priv->native_running || engine->priv->simple_running;
 | 
						|
 | 
						|
  if (running != engine->priv->running)
 | 
						|
    {
 | 
						|
      engine->priv->running = running;
 | 
						|
 | 
						|
      if (!running)
 | 
						|
        {
 | 
						|
          if (engine->priv->native_error)
 | 
						|
            _gtk_search_engine_error (engine, engine->priv->native_error);
 | 
						|
          else if (engine->priv->simple_error)
 | 
						|
            _gtk_search_engine_error (engine, engine->priv->simple_error);
 | 
						|
          else if (engine->priv->model_error)
 | 
						|
            _gtk_search_engine_error (engine, engine->priv->model_error);
 | 
						|
          else
 | 
						|
            _gtk_search_engine_finished (engine);
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
finished (GtkSearchEngine *engine,
 | 
						|
          gpointer         data)
 | 
						|
{
 | 
						|
  GtkSearchEngine *composite = GTK_SEARCH_ENGINE (data);
 | 
						|
 | 
						|
  if (engine == composite->priv->native)
 | 
						|
    composite->priv->native_running = FALSE;
 | 
						|
  else if (engine == composite->priv->simple)
 | 
						|
    composite->priv->simple_running = FALSE;
 | 
						|
  else if (engine == composite->priv->model)
 | 
						|
    composite->priv->model_running = FALSE;
 | 
						|
 | 
						|
  update_status (composite);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
error (GtkSearchEngine *engine,
 | 
						|
       const gchar     *message,
 | 
						|
       gpointer         data)
 | 
						|
{
 | 
						|
  GtkSearchEngine *composite = GTK_SEARCH_ENGINE (data);
 | 
						|
 | 
						|
  if (engine == composite->priv->native)
 | 
						|
    {
 | 
						|
      g_free (composite->priv->native_error);
 | 
						|
      composite->priv->native_error = g_strdup (message);
 | 
						|
      composite->priv->native_running = FALSE;
 | 
						|
    }
 | 
						|
  else if (engine == composite->priv->simple)
 | 
						|
    {
 | 
						|
      g_free (composite->priv->simple_error);
 | 
						|
      composite->priv->simple_error = g_strdup (message);
 | 
						|
      composite->priv->simple_running = FALSE;
 | 
						|
    }
 | 
						|
  else if (engine == composite->priv->model)
 | 
						|
    {
 | 
						|
      g_free (composite->priv->model_error);
 | 
						|
      composite->priv->model_error = g_strdup (message);
 | 
						|
      composite->priv->model_running = FALSE;
 | 
						|
    }
 | 
						|
 | 
						|
  update_status (composite);
 | 
						|
}
 | 
						|
 | 
						|
static gboolean
 | 
						|
search_hit_equal (gconstpointer a, gconstpointer b)
 | 
						|
{
 | 
						|
  const GtkSearchHit *ha = (const GtkSearchHit *)a;
 | 
						|
  const GtkSearchHit *hb = (const GtkSearchHit *)b;
 | 
						|
 | 
						|
  return g_file_equal (ha->file, hb->file);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static guint
 | 
						|
search_hit_hash (gconstpointer a)
 | 
						|
{
 | 
						|
  const GtkSearchHit *ha = (const GtkSearchHit *)a;
 | 
						|
 | 
						|
  return g_file_hash (ha->file);
 | 
						|
}
 | 
						|
 | 
						|
GtkSearchHit *
 | 
						|
_gtk_search_hit_dup (GtkSearchHit *hit)
 | 
						|
{
 | 
						|
  GtkSearchHit *dup;
 | 
						|
 | 
						|
  dup = g_new (GtkSearchHit, 1);
 | 
						|
  dup->file = g_object_ref (hit->file);
 | 
						|
  if (hit->info)
 | 
						|
    dup->info = g_object_ref (hit->info);
 | 
						|
  else
 | 
						|
    dup->info = NULL;
 | 
						|
 | 
						|
  return dup;
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
_gtk_search_hit_free (GtkSearchHit *hit)
 | 
						|
{
 | 
						|
  g_clear_object (&hit->file);
 | 
						|
  g_clear_object (&hit->info);
 | 
						|
  g_free (hit);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
connect_engine_signals (GtkSearchEngine *engine,
 | 
						|
                        gpointer         data)
 | 
						|
{
 | 
						|
  g_signal_connect_object (engine, "hits-added", G_CALLBACK (hits_added), data, 0);
 | 
						|
  g_signal_connect_object (engine, "finished", G_CALLBACK (finished), data, 0);
 | 
						|
  g_signal_connect_object (engine, "error", G_CALLBACK (error), data, 0);
 | 
						|
}
 | 
						|
 | 
						|
GtkSearchEngine *
 | 
						|
_gtk_search_engine_new (void)
 | 
						|
{
 | 
						|
  GtkSearchEngine *engine;
 | 
						|
 | 
						|
  engine = g_object_new (GTK_TYPE_SEARCH_ENGINE, NULL);
 | 
						|
 | 
						|
  engine->priv->simple = _gtk_search_engine_simple_new ();
 | 
						|
  g_debug ("Using simple search engine");
 | 
						|
  connect_engine_signals (engine->priv->simple, engine);
 | 
						|
 | 
						|
#ifdef HAVE_TRACKER
 | 
						|
  engine->priv->native = _gtk_search_engine_tracker_new ();
 | 
						|
  if (engine->priv->native)
 | 
						|
    {
 | 
						|
      g_debug ("Using Tracker search engine");
 | 
						|
      connect_engine_signals (engine->priv->native, engine);
 | 
						|
      _gtk_search_engine_simple_set_indexed_cb (GTK_SEARCH_ENGINE_SIMPLE (engine->priv->simple),
 | 
						|
                                                _gtk_search_engine_tracker_is_indexed,
 | 
						|
                                                g_object_ref (engine->priv->native),
 | 
						|
                                                g_object_unref);
 | 
						|
    }
 | 
						|
#endif
 | 
						|
 | 
						|
#ifdef GDK_WINDOWING_QUARTZ
 | 
						|
  engine->priv->native = _gtk_search_engine_quartz_new ();
 | 
						|
  if (engine->priv->native)
 | 
						|
    {
 | 
						|
      g_debug ("Using Quartz search engine");
 | 
						|
      connect_engine_signals (engine->priv->native, engine);
 | 
						|
    }
 | 
						|
#endif
 | 
						|
 | 
						|
  engine->priv->hits = g_hash_table_new_full (search_hit_hash, search_hit_equal,
 | 
						|
                                              (GDestroyNotify)_gtk_search_hit_free, NULL);
 | 
						|
 | 
						|
  return engine;
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
_gtk_search_engine_set_query (GtkSearchEngine *engine,
 | 
						|
                              GtkQuery        *query)
 | 
						|
{
 | 
						|
  g_return_if_fail (GTK_IS_SEARCH_ENGINE (engine));
 | 
						|
  g_return_if_fail (GTK_SEARCH_ENGINE_GET_CLASS (engine)->set_query != NULL);
 | 
						|
 | 
						|
  GTK_SEARCH_ENGINE_GET_CLASS (engine)->set_query (engine, query);
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
_gtk_search_engine_start (GtkSearchEngine *engine)
 | 
						|
{
 | 
						|
  g_return_if_fail (GTK_IS_SEARCH_ENGINE (engine));
 | 
						|
  g_return_if_fail (GTK_SEARCH_ENGINE_GET_CLASS (engine)->start != NULL);
 | 
						|
 | 
						|
  GTK_SEARCH_ENGINE_GET_CLASS (engine)->start (engine);
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
_gtk_search_engine_stop (GtkSearchEngine *engine)
 | 
						|
{
 | 
						|
  g_return_if_fail (GTK_IS_SEARCH_ENGINE (engine));
 | 
						|
  g_return_if_fail (GTK_SEARCH_ENGINE_GET_CLASS (engine)->stop != NULL);
 | 
						|
 | 
						|
  GTK_SEARCH_ENGINE_GET_CLASS (engine)->stop (engine);
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
_gtk_search_engine_hits_added (GtkSearchEngine *engine,
 | 
						|
                               GList           *hits)
 | 
						|
{
 | 
						|
  g_return_if_fail (GTK_IS_SEARCH_ENGINE (engine));
 | 
						|
 | 
						|
  g_signal_emit (engine, signals[HITS_ADDED], 0, hits);
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
_gtk_search_engine_finished (GtkSearchEngine *engine)
 | 
						|
{
 | 
						|
  g_return_if_fail (GTK_IS_SEARCH_ENGINE (engine));
 | 
						|
 | 
						|
  g_signal_emit (engine, signals[FINISHED], 0);
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
_gtk_search_engine_error (GtkSearchEngine *engine,
 | 
						|
                          const gchar     *error_message)
 | 
						|
{
 | 
						|
  g_return_if_fail (GTK_IS_SEARCH_ENGINE (engine));
 | 
						|
 | 
						|
  g_signal_emit (engine, signals[ERROR], 0, error_message);
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
_gtk_search_engine_set_recursive (GtkSearchEngine *engine,
 | 
						|
                                  gboolean         recursive)
 | 
						|
{
 | 
						|
  g_return_if_fail (GTK_IS_SEARCH_ENGINE (engine));
 | 
						|
 | 
						|
  g_assert (!engine->priv->running);
 | 
						|
 | 
						|
  engine->priv->recursive = recursive;
 | 
						|
 | 
						|
  if (engine->priv->native)
 | 
						|
    _gtk_search_engine_set_recursive (engine->priv->native, recursive);
 | 
						|
 | 
						|
  if (engine->priv->simple)
 | 
						|
    _gtk_search_engine_set_recursive (engine->priv->simple, recursive);
 | 
						|
}
 | 
						|
 | 
						|
gboolean
 | 
						|
_gtk_search_engine_get_recursive (GtkSearchEngine *engine)
 | 
						|
{
 | 
						|
  g_return_val_if_fail (GTK_IS_SEARCH_ENGINE (engine), TRUE);
 | 
						|
 | 
						|
  return engine->priv->recursive;
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
_gtk_search_engine_set_model (GtkSearchEngine    *engine,
 | 
						|
                              GtkFileSystemModel *model)
 | 
						|
{
 | 
						|
  g_clear_object (&engine->priv->model);
 | 
						|
  if (model)
 | 
						|
    {
 | 
						|
      engine->priv->model = _gtk_search_engine_model_new (model);
 | 
						|
      connect_engine_signals (engine->priv->model, engine);
 | 
						|
      if (engine->priv->query)
 | 
						|
        _gtk_search_engine_set_query (engine->priv->model, engine->priv->query);
 | 
						|
    }
 | 
						|
}
 |