+++ /dev/null
-/*
- * Copyright (C) 2009-2011 Nokia <ivan.frade@nokia.com>
- *
- * 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/>.
- *
- * Authors: Jürg Billeter <juerg.billeter@codethink.co.uk>
- * Martyn Russell <martyn@lanedo.com>
- *
- * Based on nautilus-search-engine-tracker.c
- */
-
-#include "config.h"
-
-#include <string.h>
-
-#include <gio/gio.h>
-#include <gmodule.h>
-#include <gdk/gdk.h>
-#include <gtk/gtk.h>
-
-#include "gtksearchenginetracker.h"
-
-#define DBUS_SERVICE_RESOURCES "org.freedesktop.Tracker1"
-#define DBUS_PATH_RESOURCES "/org/freedesktop/Tracker1/Resources"
-#define DBUS_INTERFACE_RESOURCES "org.freedesktop.Tracker1.Resources"
-
-#define DBUS_SERVICE_STATUS "org.freedesktop.Tracker1"
-#define DBUS_PATH_STATUS "/org/freedesktop/Tracker1/Status"
-#define DBUS_INTERFACE_STATUS "org.freedesktop.Tracker1.Status"
-
-/* Time in second to wait for service before deciding it's not available */
-#define WAIT_TIMEOUT_SECONDS 1
-
-/* Time in second to wait for query results to come back */
-#define QUERY_TIMEOUT_SECONDS 10
-
-/* If defined, we use fts:match, this has to be enabled in Tracker to
- * work which it usually is. The alternative is to undefine it and
- * use filename matching instead. This doesn’t use the content of the
- * file however.
- */
-#define FTS_MATCHING
-
-struct _GtkSearchEngineTracker
-{
- GtkSearchEngine parent;
- GDBusConnection *connection;
- GCancellable *cancellable;
- GtkQuery *query;
- gboolean query_pending;
- GPtrArray *indexed_locations;
-};
-
-struct _GtkSearchEngineTrackerClass
-{
- GtkSearchEngineClass parent_class;
-};
-
-G_DEFINE_TYPE (GtkSearchEngineTracker, _gtk_search_engine_tracker, GTK_TYPE_SEARCH_ENGINE)
-
-static void
-finalize (GObject *object)
-{
- GtkSearchEngineTracker *tracker;
-
- g_debug ("Finalizing GtkSearchEngineTracker");
-
- tracker = GTK_SEARCH_ENGINE_TRACKER (object);
-
- if (tracker->cancellable)
- {
- g_cancellable_cancel (tracker->cancellable);
- g_object_unref (tracker->cancellable);
- }
-
- g_clear_object (&tracker->query);
- g_clear_object (&tracker->connection);
-
- g_ptr_array_unref (tracker->indexed_locations);
-
- G_OBJECT_CLASS (_gtk_search_engine_tracker_parent_class)->finalize (object);
-}
-
-static GDBusConnection *
-get_connection (void)
-{
- GDBusConnection *connection;
- GError *error = NULL;
- GVariant *reply;
-
- /* Normally I hate sync calls with UIs, but we need to return NULL
- * or a GtkSearchEngine as a result of this function.
- */
- connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
-
- if (error)
- {
- g_debug ("Couldn't connect to D-Bus session bus, %s", error->message);
- g_error_free (error);
- return NULL;
- }
-
- /* If connection is set, we know it worked. */
- g_debug ("Finding out if Tracker is available via D-Bus...");
-
- /* We only wait 1 second max, we expect it to be very fast. If we
- * don't get a response by then, clearly we're replaying a journal
- * or cleaning up the DB internally. Either way, services is not
- * available.
- *
- * We use the sync call here because we don't expect to be waiting
- * long enough to block UI painting.
- */
- reply = g_dbus_connection_call_sync (connection,
- DBUS_SERVICE_STATUS,
- DBUS_PATH_STATUS,
- DBUS_INTERFACE_STATUS,
- "Wait",
- NULL,
- NULL,
- G_DBUS_CALL_FLAGS_NONE,
- WAIT_TIMEOUT_SECONDS * 1000,
- NULL,
- &error);
-
- if (error)
- {
- g_debug ("Tracker is not available, %s", error->message);
- g_error_free (error);
- g_object_unref (connection);
- return NULL;
- }
-
- g_variant_unref (reply);
-
- g_debug ("Tracker is ready");
-
- return connection;
-}
-
-static void
-get_query_results (GtkSearchEngineTracker *engine,
- const gchar *sparql,
- GAsyncReadyCallback callback,
- gpointer user_data)
-{
- g_dbus_connection_call (engine->connection,
- DBUS_SERVICE_RESOURCES,
- DBUS_PATH_RESOURCES,
- DBUS_INTERFACE_RESOURCES,
- "SparqlQuery",
- g_variant_new ("(s)", sparql),
- NULL,
- G_DBUS_CALL_FLAGS_NONE,
- QUERY_TIMEOUT_SECONDS * 1000,
- engine->cancellable,
- callback,
- user_data);
-}
-
-/* Stolen from libtracker-sparql */
-static gchar *
-sparql_escape_string (const gchar *literal)
-{
- GString *str;
- const gchar *p;
-
- g_return_val_if_fail (literal != NULL, NULL);
-
- str = g_string_new ("");
- p = literal;
-
- while (TRUE)
- {
- gsize len;
-
- if (!((*p) != '\0'))
- break;
-
- len = strcspn ((const gchar *) p, "\t\n\r\b\f\"\\");
- g_string_append_len (str, (const gchar *) p, (gssize) ((glong) len));
- p = p + len;
-
- switch (*p)
- {
- case '\t':
- g_string_append (str, "\\t");
- break;
- case '\n':
- g_string_append (str, "\\n");
- break;
- case '\r':
- g_string_append (str, "\\r");
- break;
- case '\b':
- g_string_append (str, "\\b");
- break;
- case '\f':
- g_string_append (str, "\\f");
- break;
- case '"':
- g_string_append (str, "\\\"");
- break;
- case '\\':
- g_string_append (str, "\\\\");
- break;
- default:
- continue;
- }
-
- p++;
- }
- return g_string_free (str, FALSE);
- }
-
-static void
-sparql_append_string_literal (GString *sparql,
- const gchar *str,
- gboolean glob,
- gboolean is_dir_uri,
- gboolean quoted)
-{
- gchar *s;
-
- s = sparql_escape_string (str);
-
- g_string_append_c (sparql, '"');
- if (quoted)
- g_string_append (sparql, "\\\"");
- g_string_append (sparql, s);
-
- if (is_dir_uri)
- g_string_append_c (sparql, '/');
- if (quoted)
- g_string_append (sparql, "\\\"");
- if (glob)
- g_string_append_c (sparql, '*');
- g_string_append_c (sparql, '"');
-
- g_free (s);
-}
-
-static void
-sparql_append_string_literal_lower_case (GString *sparql,
- const gchar *str)
-{
- gchar *s;
-
- s = g_utf8_strdown (str, -1);
- sparql_append_string_literal (sparql, s, FALSE, FALSE, FALSE);
- g_free (s);
-}
-
-static void
-query_callback (GObject *object,
- GAsyncResult *res,
- gpointer user_data)
-{
- GtkSearchEngineTracker *tracker;
- GList *hits;
- GVariant *reply;
- GVariant *r;
- GVariantIter iter;
- GError *error = NULL;
- gint i, n;
- GtkSearchHit *hit;
-
- tracker = GTK_SEARCH_ENGINE_TRACKER (user_data);
-
- tracker->query_pending = FALSE;
-
- reply = g_dbus_connection_call_finish (tracker->connection, res, &error);
- if (error)
- {
- _gtk_search_engine_error (GTK_SEARCH_ENGINE (tracker), error->message);
- g_error_free (error);
- g_object_unref (tracker);
- return;
- }
-
- if (!reply)
- {
- _gtk_search_engine_finished (GTK_SEARCH_ENGINE (tracker), FALSE);
- g_object_unref (tracker);
- return;
- }
-
- r = g_variant_get_child_value (reply, 0);
- g_variant_iter_init (&iter, r);
- n = g_variant_iter_n_children (&iter);
- hit = g_new (GtkSearchHit, n);
- hits = NULL;
- for (i = 0; i < n; i++)
- {
- GVariant *v;
- const gchar **strv;
-
- v = g_variant_iter_next_value (&iter);
- strv = g_variant_get_strv (v, NULL);
- hit[i].file = g_file_new_for_uri (strv[0]);
- hit[i].info = NULL;
- g_free (strv);
- hits = g_list_prepend (hits, &hit[i]);
- }
-
- _gtk_search_engine_hits_added (GTK_SEARCH_ENGINE (tracker), hits);
- _gtk_search_engine_finished (GTK_SEARCH_ENGINE (tracker), i > 0);
-
- g_list_free (hits);
- for (i = 0; i < n; i++)
- g_object_unref (hit[i].file);
- g_free (hit);
-
- g_variant_unref (reply);
- g_variant_unref (r);
-
- g_object_unref (tracker);
-}
-
-static void
-gtk_search_engine_tracker_start (GtkSearchEngine *engine)
-{
- GtkSearchEngineTracker *tracker;
- const gchar *search_text;
- GFile *location;
- GString *sparql;
- gboolean recursive;
-
- tracker = GTK_SEARCH_ENGINE_TRACKER (engine);
-
- if (tracker->query_pending)
- {
- g_debug ("Attempt to start a new search while one is pending, doing nothing");
- return;
- }
-
- if (tracker->query == NULL)
- {
- g_debug ("Attempt to start a new search with no GtkQuery, doing nothing");
- return;
- }
-
- search_text = gtk_query_get_text (tracker->query);
- location = gtk_query_get_location (tracker->query);
- recursive = _gtk_search_engine_get_recursive (engine);
-
- sparql = g_string_new ("SELECT nie:url(?urn) "
- "WHERE {"
- " ?urn a nfo:FileDataObject ;"
- " tracker:available true ; "
- " nfo:belongsToContainer ?parent; ");
-
-#ifdef FTS_MATCHING
- /* Using FTS: */
- g_string_append (sparql, "fts:match ");
- sparql_append_string_literal (sparql, search_text, TRUE, FALSE, TRUE);
-#endif
-
- g_string_append (sparql, ". FILTER (BOUND(nie:url(?urn)) && ");
-
- g_string_append (sparql, "fn:contains(fn:lower-case(nfo:fileName(?urn)),");
- sparql_append_string_literal_lower_case (sparql, search_text);
- g_string_append (sparql, ")");
-
- if (location)
- {
- gchar *location_uri = g_file_get_uri (location);
- g_string_append (sparql, " && ");
- if (recursive)
- {
- g_string_append (sparql, "fn:starts-with(nie:url(?urn),");
- sparql_append_string_literal (sparql, location_uri, FALSE, TRUE, FALSE);
- g_string_append (sparql, ")");
- }
- else
- {
- g_string_append (sparql, "nie:url(?parent) = ");
- sparql_append_string_literal (sparql, location_uri, FALSE, FALSE, FALSE);
- }
- g_free (location_uri);
- }
-
- g_string_append (sparql, ")");
-
-#ifdef FTS_MATCHING
- g_string_append (sparql, " } ORDER BY DESC(fts:rank(?urn)) DESC(nie:url(?urn))");
-#else /* FTS_MATCHING */
- g_string_append (sparql, "} ORDER BY DESC(nie:url(?urn)) DESC(nfo:fileName(?urn))");
-#endif /* FTS_MATCHING */
-
- tracker->query_pending = TRUE;
-
- g_debug ("SearchEngineTracker: query: %s", sparql->str);
-
- get_query_results (tracker, sparql->str, query_callback, g_object_ref (tracker));
-
- g_string_free (sparql, TRUE);
-}
-
-static void
-gtk_search_engine_tracker_stop (GtkSearchEngine *engine)
-{
- GtkSearchEngineTracker *tracker;
-
- tracker = GTK_SEARCH_ENGINE_TRACKER (engine);
-
- if (tracker->query && tracker->query_pending)
- {
- g_cancellable_cancel (tracker->cancellable);
- tracker->query_pending = FALSE;
- }
-}
-
-static void
-gtk_search_engine_tracker_set_query (GtkSearchEngine *engine,
- GtkQuery *query)
-{
- GtkSearchEngineTracker *tracker;
-
- tracker = GTK_SEARCH_ENGINE_TRACKER (engine);
-
- if (query)
- g_object_ref (query);
-
- if (tracker->query)
- g_object_unref (tracker->query);
-
- tracker->query = query;
-}
-
-static void
-_gtk_search_engine_tracker_class_init (GtkSearchEngineTrackerClass *class)
-{
- GObjectClass *gobject_class;
- GtkSearchEngineClass *engine_class;
-
- gobject_class = G_OBJECT_CLASS (class);
- gobject_class->finalize = finalize;
-
- engine_class = GTK_SEARCH_ENGINE_CLASS (class);
- engine_class->set_query = gtk_search_engine_tracker_set_query;
- engine_class->start = gtk_search_engine_tracker_start;
- engine_class->stop = gtk_search_engine_tracker_stop;
-}
-
-static void get_indexed_locations (GtkSearchEngineTracker *engine);
-
-static void
-_gtk_search_engine_tracker_init (GtkSearchEngineTracker *engine)
-{
- engine->cancellable = g_cancellable_new ();
- engine->query_pending = FALSE;
- engine->indexed_locations = g_ptr_array_new_with_free_func (g_object_unref);
-
- get_indexed_locations (engine);
-}
-
-GtkSearchEngine *
-_gtk_search_engine_tracker_new (void)
-{
- GtkSearchEngineTracker *engine;
- GDBusConnection *connection;
-
- g_debug ("--");
-
- connection = get_connection ();
- if (!connection)
- return NULL;
-
- g_debug ("Creating GtkSearchEngineTracker...");
-
- engine = g_object_new (GTK_TYPE_SEARCH_ENGINE_TRACKER, NULL);
-
- engine->connection = connection;
-
- return GTK_SEARCH_ENGINE (engine);
-}
-
-#define TRACKER_SCHEMA "org.freedesktop.Tracker.Miner.Files"
-#define TRACKER_KEY_RECURSIVE_DIRECTORIES "index-recursive-directories"
-
-static const gchar *
-get_user_special_dir_if_not_home (GUserDirectory idx)
-{
- const gchar *path;
- path = g_get_user_special_dir (idx);
- if (g_strcmp0 (path, g_get_home_dir ()) == 0)
- return NULL;
-
- return path;
-}
-
-static const gchar *
-path_from_tracker_dir (const gchar *value)
-{
- const gchar *path;
-
- if (g_strcmp0 (value, "&DESKTOP") == 0)
- path = get_user_special_dir_if_not_home (G_USER_DIRECTORY_DESKTOP);
- else if (g_strcmp0 (value, "&DOCUMENTS") == 0)
- path = get_user_special_dir_if_not_home (G_USER_DIRECTORY_DOCUMENTS);
- else if (g_strcmp0 (value, "&DOWNLOAD") == 0)
- path = get_user_special_dir_if_not_home (G_USER_DIRECTORY_DOWNLOAD);
- else if (g_strcmp0 (value, "&MUSIC") == 0)
- path = get_user_special_dir_if_not_home (G_USER_DIRECTORY_MUSIC);
- else if (g_strcmp0 (value, "&PICTURES") == 0)
- path = get_user_special_dir_if_not_home (G_USER_DIRECTORY_PICTURES);
- else if (g_strcmp0 (value, "&PUBLIC_SHARE") == 0)
- path = get_user_special_dir_if_not_home (G_USER_DIRECTORY_PUBLIC_SHARE);
- else if (g_strcmp0 (value, "&TEMPLATES") == 0)
- path = get_user_special_dir_if_not_home (G_USER_DIRECTORY_TEMPLATES);
- else if (g_strcmp0 (value, "&VIDEOS") == 0)
- path = get_user_special_dir_if_not_home (G_USER_DIRECTORY_VIDEOS);
- else if (g_strcmp0 (value, "$HOME") == 0)
- path = g_get_home_dir ();
- else
- path = value;
-
- return path;
-}
-
-static void
-get_indexed_locations (GtkSearchEngineTracker *engine)
-{
- GSettingsSchemaSource *source;
- GSettingsSchema *schema;
- GSettings *settings;
- gchar **locations;
- gint i;
- GFile *location;
- const gchar *path;
-
- source = g_settings_schema_source_get_default ();
- schema = g_settings_schema_source_lookup (source, TRACKER_SCHEMA, FALSE);
- if (!schema)
- return;
-
- settings = g_settings_new_full (schema, NULL, NULL);
- g_settings_schema_unref (schema);
-
- locations = g_settings_get_strv (settings, TRACKER_KEY_RECURSIVE_DIRECTORIES);
-
- for (i = 0; locations[i] != NULL; i++)
- {
- path = path_from_tracker_dir (locations[i]);
- if (path == NULL)
- continue;
-
- location = g_file_new_for_path (path);
- g_ptr_array_add (engine->indexed_locations, location);
- }
-
- g_strfreev (locations);
- g_object_unref (settings);
-}
-
-gboolean
-_gtk_search_engine_tracker_is_indexed (GFile *location,
- gpointer data)
-{
- GtkSearchEngineTracker *engine = data;
- gint i;
- GFile *place;
-
- for (i = 0; i < engine->indexed_locations->len; i++)
- {
- place = g_ptr_array_index (engine->indexed_locations, i);
- if (g_file_equal (location, place) || g_file_has_prefix (location, place))
- return TRUE;
- }
-
- return FALSE;
-}