2 * Copyright (C) 2005 Mr Jamie McCracken
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Author: Jamie McCracken <jamiemcc@gnome.org>
20 * Based on nautilus-search-engine-tracker.c
25 #include "gtksearchenginetracker.h"
27 /* we dlopen() libtracker at runtime */
29 typedef struct _TrackerClient TrackerClient;
33 TRACKER_UNAVAILABLE = 0,
42 typedef void (*TrackerArrayReply) (char **result, GError *error, gpointer user_data);
44 static TrackerClient * (*tracker_connect) (gboolean enable_warnings, gint timeout) = NULL;
45 static void (*tracker_disconnect) (TrackerClient *client) = NULL;
46 static int (*tracker_get_version) (TrackerClient *client, GError **error) = NULL;
47 static void (*tracker_cancel_last_call) (TrackerClient *client) = NULL;
49 static void (*tracker_search_metadata_by_text_async) (TrackerClient *client,
51 TrackerArrayReply callback,
52 gpointer user_data) = NULL;
53 static void (*tracker_search_metadata_by_text_and_location_async) (TrackerClient *client,
56 TrackerArrayReply callback,
57 gpointer user_data) = NULL;
58 /* Tracker 0.7->0.9 API */
60 TRACKER_CLIENT_ENABLE_WARNINGS = 1 << 0
63 typedef void (*TrackerReplyGPtrArray) (GPtrArray *result,
67 static TrackerClient * (*tracker_client_new) (TrackerClientFlags flags,
69 static gchar * (*tracker_sparql_escape) (const gchar *str) = NULL;
70 static guint (*tracker_resources_sparql_query_async) (TrackerClient *client,
72 TrackerReplyGPtrArray callback,
73 gpointer user_data) = NULL;
76 static struct TrackerDlMapping
80 TrackerVersion versions;
81 } tracker_dl_mapping[] =
83 #define MAP(a,v) { #a, (gpointer *)&a, v }
84 MAP (tracker_connect, TRACKER_0_6 | TRACKER_0_7),
85 MAP (tracker_disconnect, TRACKER_0_6 | TRACKER_0_7),
86 MAP (tracker_get_version, TRACKER_0_6),
87 MAP (tracker_cancel_last_call, TRACKER_0_6 | TRACKER_0_7 | TRACKER_0_8 | TRACKER_0_9),
88 MAP (tracker_search_metadata_by_text_async, TRACKER_0_6 | TRACKER_0_7),
89 MAP (tracker_search_metadata_by_text_and_location_async, TRACKER_0_6 | TRACKER_0_7),
90 MAP (tracker_client_new, TRACKER_0_8 | TRACKER_0_9),
91 MAP (tracker_sparql_escape, TRACKER_0_8 | TRACKER_0_9),
92 MAP (tracker_resources_sparql_query_async, TRACKER_0_8 | TRACKER_0_9)
97 open_libtracker (void)
99 static gboolean done = FALSE;
100 static TrackerVersion version = TRACKER_UNAVAILABLE;
110 flags = G_MODULE_BIND_LAZY | G_MODULE_BIND_LOCAL;
112 /* So this is the order:
114 * - 0.9 (latest unstable)
116 * - 0.7 (unstable, 0.6 sucks so badly)
119 if ((tracker = g_module_open ("libtracker-client-0.9.so.0", flags)) != NULL)
120 version = TRACKER_0_9;
121 else if ((tracker = g_module_open ("libtracker-client-0.8.so.0", flags)) != NULL)
122 version = TRACKER_0_8;
123 else if ((tracker = g_module_open ("libtracker-client-0.7.so.0", flags)) != NULL)
124 version = TRACKER_0_7;
125 else if ((tracker = g_module_open ("libtrackerclient.so.0", flags)) != NULL)
126 version = TRACKER_0_6;
129 g_debug ("No tracker backend available");
130 return TRACKER_UNAVAILABLE;
133 for (i = 0; i < G_N_ELEMENTS (tracker_dl_mapping); i++)
135 if ((tracker_dl_mapping[i].versions & version) == 0)
138 if (!g_module_symbol (tracker,
139 tracker_dl_mapping[i].fn_name,
140 tracker_dl_mapping[i].fn_ptr_ref))
142 g_warning ("Missing symbol '%s' in libtracker\n",
143 tracker_dl_mapping[i].fn_name);
144 g_module_close (tracker);
146 for (i = 0; i < G_N_ELEMENTS (tracker_dl_mapping); i++)
147 tracker_dl_mapping[i].fn_ptr_ref = NULL;
149 return TRACKER_UNAVAILABLE;
157 struct _GtkSearchEngineTrackerPrivate
160 TrackerClient *client;
161 gboolean query_pending;
162 TrackerVersion version;
165 G_DEFINE_TYPE (GtkSearchEngineTracker, _gtk_search_engine_tracker, GTK_TYPE_SEARCH_ENGINE);
169 finalize (GObject *object)
171 GtkSearchEngineTracker *tracker;
173 tracker = GTK_SEARCH_ENGINE_TRACKER (object);
175 if (tracker->priv->query)
177 g_object_unref (tracker->priv->query);
178 tracker->priv->query = NULL;
181 if (tracker->priv->version == TRACKER_0_8 ||
182 tracker->priv->version == TRACKER_0_9)
183 g_object_unref (tracker->priv->client);
185 tracker_disconnect (tracker->priv->client);
187 G_OBJECT_CLASS (_gtk_search_engine_tracker_parent_class)->finalize (object);
191 /* stolen from tracker sources, tracker.c */
193 sparql_append_string_literal (GString *sparql,
198 s = tracker_sparql_escape (str);
200 g_string_append_c (sparql, '"');
201 g_string_append (sparql, s);
202 g_string_append_c (sparql, '"');
209 search_callback (gpointer results,
213 GtkSearchEngineTracker *tracker;
216 GPtrArray *OUT_result;
220 tracker = GTK_SEARCH_ENGINE_TRACKER (user_data);
223 tracker->priv->query_pending = FALSE;
227 _gtk_search_engine_error (GTK_SEARCH_ENGINE (tracker), error->message);
228 g_error_free (error);
235 if (tracker->priv->version == TRACKER_0_8 ||
236 tracker->priv->version == TRACKER_0_9)
238 OUT_result = (GPtrArray*) results;
240 for (i = 0; i < OUT_result->len; i++)
242 uri = g_strdup (((gchar **) OUT_result->pdata[i])[0]);
244 hit_uris = g_list_prepend (hit_uris, uri);
247 g_ptr_array_foreach (OUT_result, (GFunc) g_free, NULL);
248 g_ptr_array_free (OUT_result, TRUE);
252 for (results_p = results; *results_p; results_p++)
254 if (tracker->priv->version == TRACKER_0_6)
255 uri = g_filename_to_uri (*results_p, NULL, NULL);
257 uri = g_strdup (*results_p);
260 hit_uris = g_list_prepend (hit_uris, uri);
262 g_strfreev ((gchar **) results);
265 _gtk_search_engine_hits_added (GTK_SEARCH_ENGINE (tracker), hit_uris);
266 _gtk_search_engine_finished (GTK_SEARCH_ENGINE (tracker));
268 g_list_foreach (hit_uris, (GFunc) g_free, NULL);
269 g_list_free (hit_uris);
274 gtk_search_engine_tracker_start (GtkSearchEngine *engine)
276 GtkSearchEngineTracker *tracker;
277 gchar *search_text, *location, *location_uri;
280 tracker = GTK_SEARCH_ENGINE_TRACKER (engine);
282 if (tracker->priv->query_pending)
285 if (tracker->priv->query == NULL)
288 search_text = _gtk_query_get_text (tracker->priv->query);
289 location_uri = _gtk_query_get_location (tracker->priv->query);
294 if (tracker->priv->version == TRACKER_0_6)
296 location = g_filename_from_uri (location_uri, NULL, NULL);
297 g_free (location_uri);
300 location = location_uri;
303 if (tracker->priv->version == TRACKER_0_8 ||
304 tracker->priv->version == TRACKER_0_9)
306 sparql = g_string_new ("SELECT nie:url(?urn) WHERE { ?urn a nfo:FileDataObject; fts:match ");
307 sparql_append_string_literal (sparql, search_text);
310 g_string_append (sparql, " . FILTER (fn:starts-with(nie:url(?urn),");
311 sparql_append_string_literal (sparql, location);
312 g_string_append (sparql, "))");
314 g_string_append (sparql, " } ORDER BY DESC(fts:rank(?urn)) ASC(nie:url(?urn))");
316 tracker_resources_sparql_query_async (tracker->priv->client,
318 (TrackerReplyGPtrArray) search_callback,
320 g_string_free (sparql, TRUE);
326 tracker_search_metadata_by_text_and_location_async (tracker->priv->client,
329 (TrackerArrayReply) search_callback,
334 tracker_search_metadata_by_text_async (tracker->priv->client,
336 (TrackerArrayReply) search_callback,
341 tracker->priv->query_pending = TRUE;
342 g_free (search_text);
347 gtk_search_engine_tracker_stop (GtkSearchEngine *engine)
349 GtkSearchEngineTracker *tracker;
351 tracker = GTK_SEARCH_ENGINE_TRACKER (engine);
353 if (tracker->priv->query && tracker->priv->query_pending)
355 tracker_cancel_last_call (tracker->priv->client);
356 tracker->priv->query_pending = FALSE;
361 gtk_search_engine_tracker_is_indexed (GtkSearchEngine *engine)
367 gtk_search_engine_tracker_set_query (GtkSearchEngine *engine,
370 GtkSearchEngineTracker *tracker;
372 tracker = GTK_SEARCH_ENGINE_TRACKER (engine);
375 g_object_ref (query);
377 if (tracker->priv->query)
378 g_object_unref (tracker->priv->query);
380 tracker->priv->query = query;
384 _gtk_search_engine_tracker_class_init (GtkSearchEngineTrackerClass *class)
386 GObjectClass *gobject_class;
387 GtkSearchEngineClass *engine_class;
389 gobject_class = G_OBJECT_CLASS (class);
390 gobject_class->finalize = finalize;
392 engine_class = GTK_SEARCH_ENGINE_CLASS (class);
393 engine_class->set_query = gtk_search_engine_tracker_set_query;
394 engine_class->start = gtk_search_engine_tracker_start;
395 engine_class->stop = gtk_search_engine_tracker_stop;
396 engine_class->is_indexed = gtk_search_engine_tracker_is_indexed;
398 g_type_class_add_private (gobject_class, sizeof (GtkSearchEngineTrackerPrivate));
402 _gtk_search_engine_tracker_init (GtkSearchEngineTracker *engine)
404 engine->priv = G_TYPE_INSTANCE_GET_PRIVATE (engine, GTK_TYPE_SEARCH_ENGINE_TRACKER, GtkSearchEngineTrackerPrivate);
409 _gtk_search_engine_tracker_new (void)
411 GtkSearchEngineTracker *engine;
412 TrackerClient *tracker_client;
413 TrackerVersion version;
416 version = open_libtracker ();
418 if (version == TRACKER_0_8 ||
419 version == TRACKER_0_9)
421 tracker_client = tracker_client_new (TRACKER_CLIENT_ENABLE_WARNINGS, G_MAXINT);
425 if (!tracker_connect)
428 tracker_client = tracker_connect (FALSE, -1);
435 if (version == TRACKER_0_6)
437 if (!tracker_get_version)
440 tracker_get_version (tracker_client, &err);
445 tracker_disconnect (tracker_client);
450 engine = g_object_new (GTK_TYPE_SEARCH_ENGINE_TRACKER, NULL);
452 engine->priv->client = tracker_client;
453 engine->priv->query_pending = FALSE;
454 engine->priv->version = version;
456 return GTK_SEARCH_ENGINE (engine);