434 lines
12 KiB
C
434 lines
12 KiB
C
|
/*
|
||
|
* 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 "gtksearchenginetracker3private.h"
|
||
|
|
||
|
#define N_RESULT_BATCH_ITEMS 50
|
||
|
|
||
|
#define MINER_FS_BUS_NAME "org.freedesktop.Tracker3.Miner.Files"
|
||
|
|
||
|
#define SEARCH_QUERY_BASE(__PATTERN__) \
|
||
|
"SELECT ?url " \
|
||
|
" nfo:fileName(?urn) " \
|
||
|
" nie:mimeType(?ie)" \
|
||
|
" nfo:fileSize(?urn)" \
|
||
|
" nfo:fileLastModified(?urn)" \
|
||
|
"FROM tracker:FileSystem " \
|
||
|
"WHERE {" \
|
||
|
" ?urn a nfo:FileDataObject ;" \
|
||
|
" nie:url ?url ; " \
|
||
|
" fts:match ~match . " \
|
||
|
" OPTIONAL { ?urn nie:interpretedAs ?ie } ." \
|
||
|
__PATTERN__ \
|
||
|
"} " \
|
||
|
"ORDER BY ASC(?url)"
|
||
|
|
||
|
#define SEARCH_QUERY SEARCH_QUERY_BASE("")
|
||
|
#define SEARCH_RECURSIVE_QUERY SEARCH_QUERY_BASE("FILTER (STRSTARTS (?url, CONCAT (~location, '/')))")
|
||
|
|
||
|
struct _GtkSearchEngineTracker3
|
||
|
{
|
||
|
GtkSearchEngine parent;
|
||
|
TrackerSparqlConnection *sparql_conn;
|
||
|
TrackerSparqlStatement *search_query;
|
||
|
TrackerSparqlStatement *search_recursive_query;
|
||
|
GCancellable *cancellable;
|
||
|
guint idle_id;
|
||
|
GtkQuery *query;
|
||
|
gboolean query_pending;
|
||
|
};
|
||
|
|
||
|
struct _GtkSearchEngineTracker3Class
|
||
|
{
|
||
|
GtkSearchEngineClass parent_class;
|
||
|
};
|
||
|
|
||
|
typedef struct
|
||
|
{
|
||
|
TrackerSparqlCursor *cursor;
|
||
|
GtkSearchEngineTracker3 *engine;
|
||
|
gboolean got_results;
|
||
|
} CursorData;
|
||
|
|
||
|
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_handle_id (&engine->idle_id, g_source_remove);
|
||
|
|
||
|
g_clear_object (&engine->search_query);
|
||
|
g_clear_object (&engine->search_recursive_query);
|
||
|
|
||
|
if (engine->sparql_conn != NULL)
|
||
|
{
|
||
|
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_free (hit);
|
||
|
}
|
||
|
|
||
|
static GFileInfo *
|
||
|
create_file_info (GFile *file,
|
||
|
TrackerSparqlCursor *cursor)
|
||
|
{
|
||
|
GFileInfo *info;
|
||
|
const char *str;
|
||
|
GDateTime *creation;
|
||
|
|
||
|
info = g_file_info_new ();
|
||
|
str = tracker_sparql_cursor_get_string (cursor, 1, NULL);
|
||
|
if (str)
|
||
|
g_file_info_set_display_name (info, str);
|
||
|
|
||
|
str = tracker_sparql_cursor_get_string (cursor, 2, NULL);
|
||
|
if (str)
|
||
|
{
|
||
|
g_file_info_set_content_type (info, str);
|
||
|
g_file_info_set_attribute_uint32 (info, "standard::type",
|
||
|
strcmp (str, "inode/directory") == 0 ?
|
||
|
G_FILE_TYPE_DIRECTORY :
|
||
|
G_FILE_TYPE_REGULAR);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
g_file_info_set_content_type (info, "application/text");
|
||
|
g_file_info_set_attribute_uint32 (info, "standard::type", G_FILE_TYPE_UNKNOWN);
|
||
|
}
|
||
|
|
||
|
g_file_info_set_size (info,
|
||
|
tracker_sparql_cursor_get_integer (cursor, 3));
|
||
|
|
||
|
str = tracker_sparql_cursor_get_string (cursor, 4, NULL);
|
||
|
if (str)
|
||
|
{
|
||
|
creation = g_date_time_new_from_iso8601 (str, NULL);
|
||
|
g_file_info_set_modification_date_time (info, creation);
|
||
|
g_date_time_unref (creation);
|
||
|
}
|
||
|
|
||
|
g_file_info_set_attribute_object (info, "standard::file", G_OBJECT (file));
|
||
|
g_file_info_set_attribute_boolean (info, "filechooser::filtered-out", FALSE);
|
||
|
g_file_info_set_attribute_boolean (info, "filechooser::visible", TRUE);
|
||
|
|
||
|
return info;
|
||
|
}
|
||
|
|
||
|
static gboolean
|
||
|
handle_cursor_idle_cb (gpointer user_data)
|
||
|
{
|
||
|
CursorData *data = user_data;
|
||
|
GtkSearchEngineTracker3 *engine = data->engine;
|
||
|
TrackerSparqlCursor *cursor = data->cursor;
|
||
|
gboolean has_next;
|
||
|
GList *hits = NULL;
|
||
|
GtkSearchHit *hit;
|
||
|
int i = 0;
|
||
|
|
||
|
for (i = 0; i < N_RESULT_BATCH_ITEMS; i++)
|
||
|
{
|
||
|
const gchar *url;
|
||
|
|
||
|
has_next = tracker_sparql_cursor_next (cursor, NULL, NULL);
|
||
|
if (!has_next)
|
||
|
break;
|
||
|
|
||
|
url = tracker_sparql_cursor_get_string (cursor, 0, NULL);
|
||
|
hit = g_new0 (GtkSearchHit, 1);
|
||
|
hit->file = g_file_new_for_uri (url);
|
||
|
hit->info = create_file_info (hit->file, cursor);
|
||
|
hits = g_list_prepend (hits, hit);
|
||
|
data->got_results = TRUE;
|
||
|
}
|
||
|
|
||
|
_gtk_search_engine_hits_added (GTK_SEARCH_ENGINE (engine), hits);
|
||
|
|
||
|
g_list_free_full (hits, free_hit);
|
||
|
|
||
|
if (has_next)
|
||
|
return G_SOURCE_CONTINUE;
|
||
|
else
|
||
|
{
|
||
|
engine->idle_id = 0;
|
||
|
return G_SOURCE_REMOVE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
cursor_data_free (gpointer user_data)
|
||
|
{
|
||
|
CursorData *data = user_data;
|
||
|
|
||
|
tracker_sparql_cursor_close (data->cursor);
|
||
|
_gtk_search_engine_finished (GTK_SEARCH_ENGINE (data->engine),
|
||
|
data->got_results);
|
||
|
g_object_unref (data->cursor);
|
||
|
g_object_unref (data->engine);
|
||
|
g_free (data);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
query_callback (TrackerSparqlStatement *statement,
|
||
|
GAsyncResult *res,
|
||
|
gpointer user_data)
|
||
|
{
|
||
|
GtkSearchEngineTracker3 *engine;
|
||
|
TrackerSparqlCursor *cursor;
|
||
|
GError *error = NULL;
|
||
|
CursorData *data;
|
||
|
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
data = g_new0 (CursorData, 1);
|
||
|
data->cursor = cursor;
|
||
|
data->engine = engine;
|
||
|
|
||
|
engine->idle_id =
|
||
|
g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
|
||
|
handle_cursor_idle_cb,
|
||
|
data, cursor_data_free);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
gtk_search_engine_tracker3_start (GtkSearchEngine *engine)
|
||
|
{
|
||
|
GtkSearchEngineTracker3 *tracker;
|
||
|
TrackerSparqlStatement *statement;
|
||
|
const char *search_text;
|
||
|
char *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;
|
||
|
}
|
||
|
|
||
|
search_text = gtk_query_get_text (tracker->query);
|
||
|
location = gtk_query_get_location (tracker->query);
|
||
|
|
||
|
if (strlen (search_text) <= 1)
|
||
|
return;
|
||
|
|
||
|
tracker->query_pending = TRUE;
|
||
|
|
||
|
if (location)
|
||
|
{
|
||
|
char *location_uri = g_file_get_uri (location);
|
||
|
|
||
|
g_debug ("Recursive search query in location: %s", location_uri);
|
||
|
statement = tracker->search_recursive_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;
|
||
|
}
|
||
|
|
||
|
g_clear_handle_id (&tracker->idle_id, g_source_remove);
|
||
|
}
|
||
|
|
||
|
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;
|
||
|
|
||
|
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_warning ("Could not init tracker3 search engine: %s",
|
||
|
error->message);
|
||
|
g_error_free (error);
|
||
|
}
|
||
|
|
||
|
return GTK_SEARCH_ENGINE (engine);
|
||
|
}
|