diff --git a/config.h.meson b/config.h.meson index 175d3a5c43..06705aefc7 100644 --- a/config.h.meson +++ b/config.h.meson @@ -292,3 +292,6 @@ #mesondefine HAVE_PANGOFT #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 8c69b47832..40328d0cc4 100644 --- a/gtk/gtksearchengine.c +++ b/gtk/gtksearchengine.c @@ -21,14 +21,16 @@ #include "config.h" #include "gtksearchengine.h" -#include "gtksearchenginetracker.h" #include "gtksearchenginemodel.h" #include "gtksearchenginequartz.h" #include "gtkintl.h" #include /* 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 @@ -334,7 +336,14 @@ _gtk_search_engine_new (void) engine = g_object_new (GTK_TYPE_SEARCH_ENGINE, NULL); -#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); + } +#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..1debfc24ca --- /dev/null +++ b/gtk/gtksearchenginetracker3.c @@ -0,0 +1,338 @@ +/* + * Copyright (C) 2020 Red Hat Inc + * Copyright (C) 2009-2011 Nokia + * + * 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 . + * + * Authors: Carlos Garnacho + * Jürg Billeter + * Martyn Russell + * + * Based on nautilus-search-engine-tracker.c + */ + +#include "config.h" + +#include + +#include +#include +#include +#include +#include + +#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") + +struct _GtkSearchEngineTracker3 +{ + GtkSearchEngine parent; + TrackerSparqlConnection *sparql_conn; + TrackerSparqlStatement *search_query; + TrackerSparqlStatement *search_recursive_query; + TrackerSparqlStatement *search_location_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); + 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; + + 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); +} diff --git a/gtk/gtksearchenginetracker3.h b/gtk/gtksearchenginetracker3.h new file mode 100644 index 0000000000..37af1fb3c2 --- /dev/null +++ b/gtk/gtksearchenginetracker3.h @@ -0,0 +1,41 @@ +/* + * 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 . + * + * Author: Carlos Garnacho + * 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) + +GtkSearchEngine * gtk_search_engine_tracker3_new (void); + +G_END_DECLS + +#endif /* __GTK_SEARCH_ENGINE_TRACKER3_H__ */ diff --git a/gtk/meson.build b/gtk/meson.build index ecccb3f642..dbe20414be 100644 --- a/gtk/meson.build +++ b/gtk/meson.build @@ -671,6 +671,10 @@ foreach p: proto_sources endif endforeach +if os_unix and tracker3_enabled + gtk_sources += 'gtksearchenginetracker3.c' +endif + if os_unix gtk_unix_print_headers = [ 'gtkpagesetupunixdialog.h', @@ -958,6 +962,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 594dc2205a..146c54b23d 100644 --- a/meson.build +++ b/meson.build @@ -386,6 +386,16 @@ if os_unix platform_gio_dep = giounix_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 73f26da7da..c14136f073 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -23,6 +23,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 : 'cups,file',