[gtk+] GtkSearchEngineTracker: port to tracker 0.12



commit 7466f846b7843e27b912335d610347c522262456
Author: Matthias Clasen <mclasen redhat com>
Date:   Mon Sep 12 21:31:12 2011 -0400

    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

 gtk/gtksearchenginetracker.c |  487 +++++++++++++++++++++---------------------
 1 files changed, 247 insertions(+), 240 deletions(-)
---
diff --git a/gtk/gtksearchenginetracker.c b/gtk/gtksearchenginetracker.c
index 51f1b9a..a2b4b93 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);



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]