diff options
author | Matthias Clasen <mclasen@redhat.com> | 2011-09-12 21:31:12 -0400 |
---|---|---|
committer | Matthias Clasen <mclasen@redhat.com> | 2011-09-12 21:35:31 -0400 |
commit | 7466f846b7843e27b912335d610347c522262456 (patch) | |
tree | 858b2d818e0e906ce82c1d967a33e4f8db2208cf | |
parent | 353c7c622e3d5f88f4ff03bb962794158968747b (diff) | |
download | gtk+-7466f846b7843e27b912335d610347c522262456.tar.gz |
GtkSearchEngineTracker: port to tracker 0.12
We simply use the Tracker DBus api here, caching and direct
access that come with libtracker-sparql are probably not needed
here. Based on a patch by Martyn Russell.
https://bugzilla.gnome.org/show_bug.cgi?id=658272
-rw-r--r-- | gtk/gtksearchenginetracker.c | 487 |
1 files changed, 247 insertions, 240 deletions
diff --git a/gtk/gtksearchenginetracker.c b/gtk/gtksearchenginetracker.c index 51f1b9a780..a2b4b93a19 100644 --- a/gtk/gtksearchenginetracker.c +++ b/gtk/gtksearchenginetracker.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2010 Nokia <ivan.frade@nokia.com> + * 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 @@ -23,12 +23,29 @@ #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 @@ -36,135 +53,34 @@ */ #undef FTS_MATCHING -#define MODULE_FILENAME "libtracker-sparql-0.10.so.0" - -#define MODULE_MAP(a) { #a, (gpointer *)&a } - -/* Connection object */ -typedef struct _TrackerSparqlConnection TrackerSparqlConnection; - -#define TRACKER_SPARQL_TYPE_CONNECTION (tracker_sparql_connection_get_type ()) -#define TRACKER_SPARQL_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TRACKER_SPARQL_TYPE_CONNECTION, TrackerSparqlConnection)) - -/* Cursor object */ -typedef struct _TrackerSparqlCursor TrackerSparqlCursor; - -#define TRACKER_SPARQL_TYPE_CURSOR (tracker_sparql_cursor_get_type ()) -#define TRACKER_SPARQL_CURSOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TRACKER_SPARQL_TYPE_CURSOR, TrackerSparqlCursor)) - -/* API */ -static GType (*tracker_sparql_connection_get_type) (void) = NULL; -static TrackerSparqlConnection * (*tracker_sparql_connection_get) (GCancellable *cancellable, - GError **error) = NULL; -static void (*tracker_sparql_connection_query_async) (TrackerSparqlConnection *self, - const gchar *sparql, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) = NULL; -static TrackerSparqlCursor * (*tracker_sparql_connection_query_finish) (TrackerSparqlConnection *self, - GAsyncResult *_res_, - GError **error) = NULL; -static GType (*tracker_sparql_cursor_get_type) (void) = NULL; -static void (*tracker_sparql_cursor_next_async) (TrackerSparqlCursor *self, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) = NULL; -static gboolean (*tracker_sparql_cursor_next_finish) (TrackerSparqlCursor *self, - GAsyncResult *_res_, - GError **error) = NULL; -static const gchar * (*tracker_sparql_cursor_get_string) (TrackerSparqlCursor *self, - gint *column, - glong *length) = NULL; -static gchar * (*tracker_sparql_escape_string) (const gchar *literal) = NULL; - -static struct TrackerFunctions -{ - const char *name; - gpointer *pointer; -} funcs[] = { - MODULE_MAP (tracker_sparql_connection_get_type), - MODULE_MAP (tracker_sparql_connection_get), - MODULE_MAP (tracker_sparql_connection_query_async), - MODULE_MAP (tracker_sparql_connection_query_finish), - MODULE_MAP (tracker_sparql_cursor_get_type), - MODULE_MAP (tracker_sparql_cursor_next_async), - MODULE_MAP (tracker_sparql_cursor_next_finish), - MODULE_MAP (tracker_sparql_cursor_get_string), - MODULE_MAP (tracker_sparql_escape_string) -}; - -static gboolean -init (void) -{ - static gboolean inited = FALSE; - gint i; - GModule *m; - GModuleFlags flags; - - if (inited) - return TRUE; - - flags = G_MODULE_BIND_LAZY | G_MODULE_BIND_LOCAL; - - /* Only support 0.10 onwards */ - if ((m = g_module_open (MODULE_FILENAME, flags)) == NULL) - { - g_debug ("No tracker backend available or it is not new enough"); - g_debug ("Only available using '%s'", MODULE_FILENAME); - return FALSE; - } - - inited = TRUE; - - /* Check for the symbols we need */ - for (i = 0; i < G_N_ELEMENTS (funcs); i++) - { - if (!g_module_symbol (m, funcs[i].name, funcs[i].pointer)) - { - g_warning ("Missing symbol '%s' in libtracker-sparql\n", - funcs[i].name); - g_module_close (m); - - for (i = 0; i < G_N_ELEMENTS (funcs); i++) - funcs[i].pointer = NULL; - - return FALSE; - } - } - - return TRUE; -} - /* * GtkSearchEngineTracker object */ struct _GtkSearchEngineTrackerPrivate { - TrackerSparqlConnection *connection; - GCancellable *cancellable; + GDBusConnection *connection; + GCancellable *cancellable; GtkQuery *query; gboolean query_pending; }; G_DEFINE_TYPE (GtkSearchEngineTracker, _gtk_search_engine_tracker, GTK_TYPE_SEARCH_ENGINE); -static void cursor_callback (GObject *object, - GAsyncResult *result, - gpointer user_data); - static void finalize (GObject *object) { GtkSearchEngineTracker *tracker; + g_debug ("Finalizing GtkSearchEngineTracker"); + tracker = GTK_SEARCH_ENGINE_TRACKER (object); if (tracker->priv->cancellable) - { - g_cancellable_cancel (tracker->priv->cancellable); - g_object_unref (tracker->priv->cancellable); - tracker->priv->cancellable = NULL; - } + { + g_cancellable_cancel (tracker->priv->cancellable); + g_object_unref (tracker->priv->cancellable); + tracker->priv->cancellable = NULL; + } if (tracker->priv->query) { @@ -173,125 +89,168 @@ finalize (GObject *object) } if (tracker->priv->connection) - { - g_object_unref (tracker->priv->connection); - tracker->priv->connection = NULL; - } + { + g_object_unref (tracker->priv->connection); + tracker->priv->connection = NULL; + } G_OBJECT_CLASS (_gtk_search_engine_tracker_parent_class)->finalize (object); } -static void -cursor_next (GtkSearchEngineTracker *tracker, - TrackerSparqlCursor *cursor) +static GDBusConnection * +get_connection (void) { - tracker_sparql_cursor_next_async (cursor, - tracker->priv->cancellable, - cursor_callback, - tracker); -} - -static void -cursor_callback (GObject *object, - GAsyncResult *result, - gpointer user_data) -{ - GtkSearchEngineTracker *tracker; + GDBusConnection *connection; GError *error = NULL; - TrackerSparqlCursor *cursor; - GList *hits; - gboolean success; - - gdk_threads_enter (); - - tracker = GTK_SEARCH_ENGINE_TRACKER (user_data); + GVariant *reply; - cursor = TRACKER_SPARQL_CURSOR (object); - success = tracker_sparql_cursor_next_finish (cursor, result, &error); + /* 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) { - _gtk_search_engine_error (GTK_SEARCH_ENGINE (tracker), error->message); - + g_debug ("Couldn't connect to D-Bus session bus, %s", error->message); g_error_free (error); - - if (cursor) - g_object_unref (cursor); - - gdk_threads_leave (); - return; + return NULL; } - if (!success) - { - _gtk_search_engine_finished (GTK_SEARCH_ENGINE (tracker)); - - if (cursor) - g_object_unref (cursor); - - gdk_threads_leave (); - return; - } + /* 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); - /* We iterate result by result, not n at a time. */ - hits = g_list_append (NULL, (gchar*) tracker_sparql_cursor_get_string (cursor, 0, NULL)); - _gtk_search_engine_hits_added (GTK_SEARCH_ENGINE (tracker), hits); - g_list_free (hits); + if (error) + { + g_debug ("Tracker is not available, %s", error->message); + g_error_free (error); + g_object_unref (connection); + return NULL; + } - /* Get next */ - cursor_next (tracker, cursor); + g_variant_unref (reply); - gdk_threads_leave (); + g_debug ("Tracker is ready"); + return connection; } static void -query_callback (GObject *object, - GAsyncResult *result, - gpointer user_data) +get_query_results (GtkSearchEngineTracker *engine, + const gchar *sparql, + GAsyncReadyCallback callback, + gpointer user_data) { - GtkSearchEngineTracker *tracker; - TrackerSparqlConnection *connection; - TrackerSparqlCursor *cursor; - GError *error = NULL; - - gdk_threads_enter (); - - tracker = GTK_SEARCH_ENGINE_TRACKER (user_data); - - tracker->priv->query_pending = FALSE; + g_dbus_connection_call (engine->priv->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->priv->cancellable, + callback, + user_data); +} - connection = TRACKER_SPARQL_CONNECTION (object); - cursor = tracker_sparql_connection_query_finish (connection, - result, - &error); +/* Stolen from libtracker-common */ +static GList * +string_list_to_gslist (gchar **strv) +{ + GList *list; + gsize i; - if (error) - { - _gtk_search_engine_error (GTK_SEARCH_ENGINE (tracker), error->message); - g_error_free (error); - gdk_threads_leave (); - return; - } + list = NULL; - if (!cursor) - { - _gtk_search_engine_finished (GTK_SEARCH_ENGINE (tracker)); - gdk_threads_leave (); - return; - } + for (i = 0; strv[i]; i++) + list = g_list_prepend (list, g_strdup (strv[i])); - cursor_next (tracker, cursor); - gdk_threads_leave (); + return g_list_reverse (list); } +/* 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) { gchar *s; - s = tracker_sparql_escape_string (str); + s = sparql_escape_string (str); g_string_append_c (sparql, '"'); g_string_append (sparql, s); @@ -308,11 +267,73 @@ sparql_append_string_literal_lower_case (GString *sparql, s = g_utf8_strdown (str, -1); sparql_append_string_literal (sparql, s); - g_free (s); } static void +query_callback (GObject *object, + GAsyncResult *res, + gpointer user_data) +{ + GtkSearchEngineTracker *tracker; + GList *hits; + GVariant *reply; + GVariant *r; + GVariantIter iter; + gchar **result; + GError *error = NULL; + gint i, n; + + gdk_threads_enter (); + + tracker = GTK_SEARCH_ENGINE_TRACKER (user_data); + + tracker->priv->query_pending = FALSE; + + reply = g_dbus_connection_call_finish (tracker->priv->connection, res, &error); + if (error) + { + _gtk_search_engine_error (GTK_SEARCH_ENGINE (tracker), error->message); + g_error_free (error); + gdk_threads_leave (); + return; + } + + if (!reply) + { + _gtk_search_engine_finished (GTK_SEARCH_ENGINE (tracker)); + gdk_threads_leave (); + return; + } + + r = g_variant_get_child_value (reply, 0); + g_variant_iter_init (&iter, r); + n = g_variant_iter_n_children (&iter); + result = g_new0 (gchar *, n + 1); + 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); + result[i] = (gchar*)strv[0]; + g_free (strv); + } + + /* We iterate result by result, not n at a time. */ + hits = string_list_to_gslist (result); + _gtk_search_engine_hits_added (GTK_SEARCH_ENGINE (tracker), hits); + _gtk_search_engine_finished (GTK_SEARCH_ENGINE (tracker)); + g_list_free (hits); + g_free (result); + g_variant_unref (reply); + g_variant_unref (r); + + gdk_threads_leave (); +} + +static void gtk_search_engine_tracker_start (GtkSearchEngine *engine) { GtkSearchEngineTracker *tracker; @@ -325,16 +346,16 @@ gtk_search_engine_tracker_start (GtkSearchEngine *engine) tracker = GTK_SEARCH_ENGINE_TRACKER (engine); if (tracker->priv->query_pending) - { - g_debug ("Attempt to start a new search while one is pending, doing nothing"); - return; - } + { + g_debug ("Attempt to start a new search while one is pending, doing nothing"); + return; + } if (tracker->priv->query == NULL) - { - g_debug ("Attempt to start a new search with no GtkQuery, doing nothing"); - return; - } + { + g_debug ("Attempt to start a new search with no GtkQuery, doing nothing"); + return; + } search_text = _gtk_query_get_text (tracker->priv->query); @@ -349,11 +370,11 @@ gtk_search_engine_tracker_start (GtkSearchEngine *engine) sparql_append_string_literal (sparql, search_text); if (location_uri) - { - g_string_append (sparql, " . FILTER (fn:starts-with(nie:url(?urn),"); - sparql_append_string_literal (sparql, location_uri); - g_string_append (sparql, "))"); - } + { + g_string_append (sparql, " . FILTER (fn:starts-with(nie:url(?urn),"); + sparql_append_string_literal (sparql, location_uri); + g_string_append (sparql, "))"); + } g_string_append (sparql, " } ORDER BY DESC(fts:rank(?urn)) ASC(nie:url(?urn))"); #else /* FTS_MATCHING */ @@ -365,19 +386,16 @@ gtk_search_engine_tracker_start (GtkSearchEngine *engine) " FILTER (fn:contains(fn:lower-case(nfo:fileName(?urn)),"); sparql_append_string_literal_lower_case (sparql, search_text); - g_string_append (sparql, + g_string_append (sparql, "))" "} ORDER BY DESC(nie:url(?urn)) DESC(nfo:fileName(?urn))"); #endif /* FTS_MATCHING */ - tracker_sparql_connection_query_async (tracker->priv->connection, - sparql->str, - tracker->priv->cancellable, - query_callback, - tracker); - g_string_free (sparql, TRUE); - tracker->priv->query_pending = TRUE; + + get_query_results (tracker, sparql->str, query_callback, tracker); + + g_string_free (sparql, TRUE); g_free (search_text); } @@ -390,7 +408,7 @@ gtk_search_engine_tracker_stop (GtkSearchEngine *engine) if (tracker->priv->query && tracker->priv->query_pending) { - g_cancellable_cancel (tracker->priv->cancellable); + g_cancellable_cancel (tracker->priv->cancellable); tracker->priv->query_pending = FALSE; } } @@ -433,7 +451,8 @@ _gtk_search_engine_tracker_class_init (GtkSearchEngineTrackerClass *class) engine_class->stop = gtk_search_engine_tracker_stop; engine_class->is_indexed = gtk_search_engine_tracker_is_indexed; - g_type_class_add_private (gobject_class, sizeof (GtkSearchEngineTrackerPrivate)); + g_type_class_add_private (gobject_class, + sizeof (GtkSearchEngineTrackerPrivate)); } static void @@ -449,32 +468,20 @@ GtkSearchEngine * _gtk_search_engine_tracker_new (void) { GtkSearchEngineTracker *engine; - TrackerSparqlConnection *connection; - GCancellable *cancellable; - GError *error = NULL; + GDBusConnection *connection; - if (!init ()) - return NULL; + g_debug ("--"); - cancellable = g_cancellable_new (); - connection = tracker_sparql_connection_get (cancellable, &error); + connection = get_connection (); + if (!connection) + return NULL; - if (error) - { - g_warning ("Could not establish a connection to Tracker: %s", error->message); - g_error_free (error); - return NULL; - } - else if (!connection) - { - g_warning ("Could not establish a connection to Tracker, no TrackerSparqlConnection was returned"); - return NULL; - } + g_debug ("Creating GtkSearchEngineTracker..."); engine = g_object_new (GTK_TYPE_SEARCH_ENGINE_TRACKER, NULL); engine->priv->connection = connection; - engine->priv->cancellable = cancellable; + engine->priv->cancellable = g_cancellable_new (); engine->priv->query_pending = FALSE; return GTK_SEARCH_ENGINE (engine); |