[gtk/wip/carlosg/tracker3-3-24: 3/4] gtksearchengine: Add tracker3 search engine



commit ae776046fc94cb755294bbe7d9cc838c57d37477
Author: Carlos Garnacho <carlosg gnome org>
Date:   Sun May 17 00:04:56 2020 +0200

    gtksearchengine: Add tracker3 search engine
    
    Make this dependency optional at build time, and prefer it over
    the old tracker <= 2.x implementation.

 config.h.meson                |   3 +
 gtk/gtksearchengine.c         |  23 ++-
 gtk/gtksearchenginetracker3.c | 383 ++++++++++++++++++++++++++++++++++++++++++
 gtk/gtksearchenginetracker3.h |  45 +++++
 gtk/meson.build               |   8 +
 meson.build                   |  10 ++
 meson_options.txt             |   2 +
 7 files changed, 469 insertions(+), 5 deletions(-)
---
diff --git a/config.h.meson b/config.h.meson
index 480f5689eb..2af55473fc 100644
--- a/config.h.meson
+++ b/config.h.meson
@@ -281,3 +281,6 @@
 #mesondefine GTK_LOCALEDIR
 
 #mesondefine ISO_CODES_PREFIX
+
+/* Define if tracker3 is available */
+#mesondefine HAVE_TRACKER3
\ No newline at end of file
diff --git a/gtk/gtksearchengine.c b/gtk/gtksearchengine.c
index b30bc4c38e..49ae39ce32 100644
--- a/gtk/gtksearchengine.c
+++ b/gtk/gtksearchengine.c
@@ -22,17 +22,19 @@
 #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 */
+#if defined(HAVE_TRACKER3)
+#include "gtksearchenginetracker3.h"
+#elif !defined G_OS_WIN32 /* No tracker on windows */
+#include "gtksearchenginetracker.h"
 #define HAVE_TRACKER 1
 #endif
 
+#include <gdk/gdk.h> /* for GDK_WINDOWING_QUARTZ */
+
 struct _GtkSearchEnginePrivate {
   GtkSearchEngine *native;
   gboolean native_running;
@@ -372,7 +374,18 @@ _gtk_search_engine_new (void)
   g_debug ("Using simple search engine");
   connect_engine_signals (engine->priv->simple, engine);
 
-#ifdef HAVE_TRACKER
+#if defined(HAVE_TRACKER3)
+  engine->priv->native = gtk_search_engine_tracker3_new ();
+  if (engine->priv->native)
+    {
+      g_debug ("Using Tracker3 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_tracker3_is_indexed,
+                                                g_object_ref (engine->priv->native),
+                                                g_object_unref);
+    }
+#elif defined(HAVE_TRACKER)
   engine->priv->native = _gtk_search_engine_tracker_new ();
   if (engine->priv->native)
     {
diff --git a/gtk/gtksearchenginetracker3.c b/gtk/gtksearchenginetracker3.c
new file mode 100644
index 0000000000..7221cd6c1c
--- /dev/null
+++ b/gtk/gtksearchenginetracker3.c
@@ -0,0 +1,383 @@
+/*
+ * Copyright (C) 2020 Red Hat Inc
+ * 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: Carlos Garnacho <carlosg gnome org>
+ *          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 <libtracker-sparql/tracker-sparql.h>
+
+#include "gtksearchenginetracker3.h"
+
+#define MINER_FS_BUS_NAME "org.freedesktop.Tracker3.Miner.Files"
+
+#define SEARCH_QUERY_BASE(__PATTERN__)                                 \
+  "SELECT ?url "                                                       \
+  "FROM tracker:FileSystem "                                           \
+  "WHERE {"                                                            \
+  "  ?urn a nfo:FileDataObject ;"                                      \
+  "       nie:url ?url ; "                                             \
+  "       fts:match ~match . "                                         \
+  __PATTERN__                                                          \
+  "} "                                                                 \
+  "ORDER BY DESC(fts:rank(?urn)) DESC(?url)"
+
+#define SEARCH_QUERY SEARCH_QUERY_BASE("")
+#define SEARCH_RECURSIVE_QUERY SEARCH_QUERY_BASE("?urn (nfo:belongsToContainer/nie:isStoredAs)+/nie:url 
~location")
+#define SEARCH_LOCATION_QUERY SEARCH_QUERY_BASE("?urn nfo:belongsToContainer/nie:isStoredAs/nie:url 
~location")
+#define FILE_CHECK_QUERY "ASK { ?urn nie:url ~url }"
+
+struct _GtkSearchEngineTracker3
+{
+  GtkSearchEngine parent;
+  TrackerSparqlConnection *sparql_conn;
+  TrackerSparqlStatement *search_query;
+  TrackerSparqlStatement *search_recursive_query;
+  TrackerSparqlStatement *search_location_query;
+  TrackerSparqlStatement *file_check_query;
+  GCancellable *cancellable;
+  GtkQuery *query;
+  gboolean query_pending;
+};
+
+struct _GtkSearchEngineTracker3Class
+{
+  GtkSearchEngineClass parent_class;
+};
+
+static void gtk_search_engine_tracker3_initable_iface_init (GInitableIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (GtkSearchEngineTracker3,
+                         gtk_search_engine_tracker3,
+                         GTK_TYPE_SEARCH_ENGINE,
+                         G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
+                                                gtk_search_engine_tracker3_initable_iface_init))
+
+static void
+finalize (GObject *object)
+{
+  GtkSearchEngineTracker3 *engine;
+
+  g_debug ("Finalizing GtkSearchEngineTracker3");
+
+  engine = GTK_SEARCH_ENGINE_TRACKER3 (object);
+
+  if (engine->cancellable)
+    {
+      g_cancellable_cancel (engine->cancellable);
+      g_object_unref (engine->cancellable);
+    }
+
+  g_clear_object (&engine->search_query);
+  g_clear_object (&engine->search_location_query);
+  g_clear_object (&engine->file_check_query);
+  tracker_sparql_connection_close (engine->sparql_conn);
+  g_clear_object (&engine->sparql_conn);
+
+  G_OBJECT_CLASS (gtk_search_engine_tracker3_parent_class)->finalize (object);
+}
+
+static void
+free_hit (gpointer data)
+{
+  GtkSearchHit *hit = data;
+
+  g_clear_object (&hit->file);
+  g_clear_object (&hit->info);
+  g_slice_free (GtkSearchHit, hit);
+}
+
+static void
+query_callback (TrackerSparqlStatement *statement,
+                GAsyncResult           *res,
+                gpointer                user_data)
+{
+  GtkSearchEngineTracker3 *engine;
+  TrackerSparqlCursor *cursor;
+  GList *hits = NULL;
+  GError *error = NULL;
+  GtkSearchHit *hit;
+
+  engine = GTK_SEARCH_ENGINE_TRACKER3 (user_data);
+
+  engine->query_pending = FALSE;
+
+  cursor = tracker_sparql_statement_execute_finish (statement, res, &error);
+
+  if (!cursor)
+    {
+      _gtk_search_engine_error (GTK_SEARCH_ENGINE (engine), error->message);
+      g_error_free (error);
+      g_object_unref (engine);
+      return;
+    }
+
+  while (tracker_sparql_cursor_next (cursor, NULL, NULL))
+    {
+      const gchar *url;
+
+      url = tracker_sparql_cursor_get_string (cursor, 0, NULL);
+      hit = g_slice_new0 (GtkSearchHit);
+      hit->file = g_file_new_for_uri (url);
+      hits = g_list_prepend (hits, hit);
+    }
+
+  tracker_sparql_cursor_close (cursor);
+
+  _gtk_search_engine_hits_added (GTK_SEARCH_ENGINE (engine), hits);
+  _gtk_search_engine_finished (GTK_SEARCH_ENGINE (engine), hits != NULL);
+
+  g_list_free_full (hits, free_hit);
+  g_object_unref (engine);
+  g_object_unref (cursor);
+}
+
+static void
+gtk_search_engine_tracker3_start (GtkSearchEngine *engine)
+{
+  GtkSearchEngineTracker3 *tracker;
+  TrackerSparqlStatement *statement;
+  const gchar *search_text;
+  gboolean recursive;
+  gchar *match;
+  GFile *location;
+
+  tracker = GTK_SEARCH_ENGINE_TRACKER3 (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;
+    }
+
+  tracker->query_pending = TRUE;
+  search_text = gtk_query_get_text (tracker->query);
+  location = gtk_query_get_location (tracker->query);
+  recursive = _gtk_search_engine_get_recursive (engine);
+
+  if (location)
+    {
+      gchar *location_uri = g_file_get_uri (location);
+
+      if (recursive)
+        {
+          g_debug ("Recursive search query in location: %s", location_uri);
+          statement = tracker->search_recursive_query;
+        }
+      else
+        {
+          g_debug ("Search query in location: %s", location_uri);
+          statement = tracker->search_location_query;
+        }
+
+      tracker_sparql_statement_bind_string (statement,
+                                            "location",
+                                            location_uri);
+      g_free (location_uri);
+    }
+  else
+    {
+      g_debug ("Search query");
+      statement = tracker->search_query;
+    }
+
+  match = g_strdup_printf ("%s*", search_text);
+  tracker_sparql_statement_bind_string (statement, "match", match);
+  g_debug ("search text: %s\n", match);
+  tracker_sparql_statement_execute_async (statement, tracker->cancellable,
+                                          (GAsyncReadyCallback) query_callback,
+                                          g_object_ref (tracker));
+  g_free (match);
+}
+
+static void
+gtk_search_engine_tracker3_stop (GtkSearchEngine *engine)
+{
+  GtkSearchEngineTracker3 *tracker;
+
+  tracker = GTK_SEARCH_ENGINE_TRACKER3 (engine);
+
+  if (tracker->query && tracker->query_pending)
+    {
+      g_cancellable_cancel (tracker->cancellable);
+      tracker->query_pending = FALSE;
+    }
+}
+
+static void
+gtk_search_engine_tracker3_set_query (GtkSearchEngine *engine,
+                                      GtkQuery        *query)
+{
+  GtkSearchEngineTracker3 *tracker;
+
+  tracker = GTK_SEARCH_ENGINE_TRACKER3 (engine);
+
+  if (query)
+    g_object_ref (query);
+
+  if (tracker->query)
+    g_object_unref (tracker->query);
+
+  tracker->query = query;
+}
+
+static void
+gtk_search_engine_tracker3_class_init (GtkSearchEngineTracker3Class *class)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (class);
+  GtkSearchEngineClass *engine_class = GTK_SEARCH_ENGINE_CLASS (class);
+
+  gobject_class->finalize = finalize;
+
+  engine_class->set_query = gtk_search_engine_tracker3_set_query;
+  engine_class->start = gtk_search_engine_tracker3_start;
+  engine_class->stop = gtk_search_engine_tracker3_stop;
+}
+
+static void
+gtk_search_engine_tracker3_init (GtkSearchEngineTracker3 *engine)
+{
+  engine->cancellable = g_cancellable_new ();
+  engine->query_pending = FALSE;
+}
+
+static gboolean
+gtk_search_engine_tracker3_initable_init (GInitable     *initable,
+                                          GCancellable  *cancellable,
+                                          GError       **error)
+{
+  GtkSearchEngineTracker3 *engine;
+
+  engine = GTK_SEARCH_ENGINE_TRACKER3 (initable);
+
+  engine->sparql_conn = tracker_sparql_connection_bus_new (MINER_FS_BUS_NAME,
+                                                           NULL, NULL,
+                                                           error);
+  if (!engine->sparql_conn)
+    return FALSE;
+
+  engine->search_query =
+    tracker_sparql_connection_query_statement (engine->sparql_conn,
+                                               SEARCH_QUERY,
+                                               cancellable,
+                                               error);
+  if (!engine->search_query)
+    return FALSE;
+
+  engine->search_recursive_query =
+    tracker_sparql_connection_query_statement (engine->sparql_conn,
+                                               SEARCH_RECURSIVE_QUERY,
+                                               cancellable,
+                                               error);
+  if (!engine->search_recursive_query)
+    return FALSE;
+
+  engine->search_location_query =
+    tracker_sparql_connection_query_statement (engine->sparql_conn,
+                                               SEARCH_LOCATION_QUERY,
+                                               cancellable,
+                                               error);
+  if (!engine->search_location_query)
+    return FALSE;
+
+  engine->file_check_query =
+    tracker_sparql_connection_query_statement (engine->sparql_conn,
+                                               FILE_CHECK_QUERY,
+                                               cancellable,
+                                               error);
+  if (!engine->file_check_query)
+    return FALSE;
+
+  return TRUE;
+}
+
+static void
+gtk_search_engine_tracker3_initable_iface_init (GInitableIface *iface)
+{
+  iface->init = gtk_search_engine_tracker3_initable_init;
+}
+
+GtkSearchEngine *
+gtk_search_engine_tracker3_new (void)
+{
+  GtkSearchEngineTracker3 *engine;
+  GError *error = NULL;
+
+  g_debug ("Creating GtkSearchEngineTracker3...");
+
+  engine = g_initable_new (GTK_TYPE_SEARCH_ENGINE_TRACKER3,
+                           NULL, &error, NULL);
+  if (!engine)
+    {
+      g_critical ("Could not init tracker3 search engine: %s",
+                  error->message);
+      g_error_free (error);
+    }
+
+  return GTK_SEARCH_ENGINE (engine);
+}
+
+gboolean
+gtk_search_engine_tracker3_is_indexed (GFile    *location,
+                                       gpointer  data)
+{
+  GtkSearchEngineTracker3 *engine = data;
+  TrackerSparqlCursor *cursor;
+  GError *error = NULL;
+  gboolean indexed;
+  gchar *uri;
+
+  uri = g_file_get_uri (location);
+  tracker_sparql_statement_bind_string (engine->file_check_query,
+                                        "url", uri);
+  cursor = tracker_sparql_statement_execute (engine->file_check_query,
+                                             engine->cancellable, &error);
+  g_free (uri);
+
+  if (!cursor ||
+      !tracker_sparql_cursor_next (cursor, NULL, NULL))
+    {
+      g_warning ("Error checking indexed file '%s': %s",
+                 uri, error->message);
+      g_error_free (error);
+      g_free (uri);
+      return FALSE;
+    }
+
+  indexed = tracker_sparql_cursor_get_boolean (cursor, 0);
+  tracker_sparql_cursor_close (cursor);
+  g_object_unref (cursor);
+
+  return indexed;
+}
diff --git a/gtk/gtksearchenginetracker3.h b/gtk/gtksearchenginetracker3.h
new file mode 100644
index 0000000000..76a181bec2
--- /dev/null
+++ b/gtk/gtksearchenginetracker3.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2020 Red Hat Inc
+ *               2005 Mr Jamie McCracken
+ *
+ * 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: Carlos Garnacho <carlosg gnome org>
+ *         Jamie McCracken (jamiemcc gnome org)
+ *
+ * Based on nautilus-search-engine-tracker.h
+ */
+
+#ifndef __GTK_SEARCH_ENGINE_TRACKER3_H__
+#define __GTK_SEARCH_ENGINE_TRACKER3_H__
+
+#include "gtksearchengine.h"
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_SEARCH_ENGINE_TRACKER3 (gtk_search_engine_tracker3_get_type ())
+G_DECLARE_FINAL_TYPE (GtkSearchEngineTracker3,
+                      gtk_search_engine_tracker3,
+                      GTK, SEARCH_ENGINE_TRACKER3,
+                      GtkSearchEngine)
+
+GType            gtk_search_engine_tracker3_get_type (void);
+
+GtkSearchEngine* gtk_search_engine_tracker3_new      (void);
+
+gboolean         gtk_search_engine_tracker3_is_indexed (GFile    *file,
+                                                        gpointer  data);
+G_END_DECLS
+
+#endif /* __GTK_SEARCH_ENGINE_TRACKER3_H__ */
diff --git a/gtk/meson.build b/gtk/meson.build
index 5a0b1547d8..2a72b91047 100644
--- a/gtk/meson.build
+++ b/gtk/meson.build
@@ -655,6 +655,10 @@ gtk_unix_sources = files(
   'gtksearchenginetracker.c',
 )
 
+if os_unix and tracker3_enabled
+  gtk_unix_sources += 'gtksearchenginetracker3.c'
+endif
+
 if os_unix
   gtk_sources += gtk_unix_sources
 endif
@@ -916,6 +920,10 @@ if cloudproviders_enabled
   gtk_deps += cloudproviders_dep
 endif
 
+if os_unix and tracker3_enabled
+  gtk_deps += tracker3_dep
+endif
+
 # Unconditional. If libintl isn't found,
 # the object just does nothing being in the deplist
 gtk_deps += libintl_dep
diff --git a/meson.build b/meson.build
index ee0cae773b..415a66c732 100644
--- a/meson.build
+++ b/meson.build
@@ -478,6 +478,16 @@ if require_harfbuzz and not harfbuzz_dep.found()
                               fallback: ['harfbuzz', 'libharfbuzz_dep'])
 endif
 
+tracker3_enabled = get_option('tracker3')
+if tracker3_enabled
+  tracker3_dep = dependency('tracker-sparql-3.0', required: false)
+  if tracker3_dep.found()
+    cdata.set('HAVE_TRACKER3', tracker3_dep.found())
+  else
+    error('Tracker3 not found, but was explicitly requested.')
+  endif
+endif
+
 if iso_codes_dep.found()
   cdata.set_quoted('ISO_CODES_PREFIX', iso_codes_dep.get_pkgconfig_variable('prefix'))
 else
diff --git a/meson_options.txt b/meson_options.txt
index 7544389345..4f605b41ba 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -17,6 +17,8 @@ option('cloudproviders', type: 'boolean', value: false,
   description : 'Enable the cloudproviders support')
 option('profiler', type: 'boolean', value: false,
   description : 'Enable profiler support')
+option('tracker3', type: 'boolean', value: false,
+  description : 'Enable Tracker3 filechooser search')
 
 # Print backends
 option('print_backends', type : 'string', value : 'auto',


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