]> Pileus Git - ~andy/gtk/blob - gtk/gtksearchenginetracker.c
e46691a944a75d9e8b5d3fd242715d373b4ba86d
[~andy/gtk] / gtk / gtksearchenginetracker.c
1 /*
2  * Copyright (C) 2005 Jamie McCracken <jamiemcc@gnome.org>
3  * Copyright (C) 2009-2010 Nokia <ivan.frade@nokia.com>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  *
19  * Authors: Jamie McCracken <jamiemcc@gnome.org>
20  *          Jürg Billeter <juerg.billeter@codethink.co.uk>
21  *          Martyn Russell <martyn@lanedo.com>
22  *
23  * Based on nautilus-search-engine-tracker.c
24  */
25
26 #include "config.h"
27 #include <gmodule.h>
28 #include "gtksearchenginetracker.h"
29
30 /* we dlopen() libtracker at runtime */
31
32 typedef struct _TrackerClient TrackerClient;
33
34 typedef enum
35 {
36   TRACKER_UNAVAILABLE = 0,
37   TRACKER_0_6 = 1 << 0,
38   TRACKER_0_7 = 1 << 1,
39   TRACKER_0_8 = 1 << 2,
40   TRACKER_0_9 = 1 << 3
41 } TrackerVersion;
42
43
44 /* Tracker 0.6 API */
45 typedef void (*TrackerArrayReply) (char **result,
46                                    GError *error,
47                                    gpointer user_data);
48
49 static TrackerClient *
50             (*tracker_connect)                                    (gboolean           enable_warnings,
51                                                                    gint               timeout)   = NULL;
52 static void (*tracker_disconnect)                                 (TrackerClient     *client)    = NULL;
53 static int  (*tracker_get_version)                                (TrackerClient     *client,
54                                                                    GError           **error)     = NULL;
55 static void (*tracker_cancel_last_call)                           (TrackerClient     *client)    = NULL;
56
57 static void (*tracker_search_metadata_by_text_async)              (TrackerClient     *client,
58                                                                    const char        *query,
59                                                                    TrackerArrayReply  callback,
60                                                                    gpointer           user_data) = NULL;
61 static void (*tracker_search_metadata_by_text_and_location_async) (TrackerClient     *client,
62                                                                    const char        *query,
63                                                                    const char        *location,
64                                                                    TrackerArrayReply  callback,
65                                                                    gpointer           user_data) = NULL;
66
67
68 /* Tracker 0.7->0.9 API */
69 typedef enum {
70         TRACKER_CLIENT_ENABLE_WARNINGS = 1 << 0
71 } TrackerClientFlags;
72
73 typedef void (*TrackerReplyGPtrArray) (GPtrArray *result,
74                                        GError    *error,
75                                        gpointer   user_data);
76
77 static TrackerClient *  (*tracker_client_new)                    (TrackerClientFlags      flags,
78                                                                   gint                    timeout)   = NULL;
79 static gchar *          (*tracker_sparql_escape)                 (const gchar            *str)       = NULL;
80 static guint            (*tracker_resources_sparql_query_async)  (TrackerClient          *client,
81                                                                   const gchar            *query,
82                                                                   TrackerReplyGPtrArray   callback,
83                                                                   gpointer                user_data) = NULL;
84
85
86 static struct TrackerDlMapping
87 {
88   const char *fn_name;
89   gpointer *fn_ptr_ref;
90   TrackerVersion versions;
91 } tracker_dl_mapping[] =
92 {
93 #define MAP(a,v) { #a, (gpointer *)&a, v }
94   MAP (tracker_connect, TRACKER_0_6 | TRACKER_0_7),
95   MAP (tracker_disconnect, TRACKER_0_6 | TRACKER_0_7),
96   MAP (tracker_get_version, TRACKER_0_6),
97   MAP (tracker_cancel_last_call, TRACKER_0_6 | TRACKER_0_7 | TRACKER_0_8 | TRACKER_0_9),
98   MAP (tracker_search_metadata_by_text_async, TRACKER_0_6 | TRACKER_0_7),
99   MAP (tracker_search_metadata_by_text_and_location_async, TRACKER_0_6 | TRACKER_0_7),
100   MAP (tracker_client_new, TRACKER_0_8 | TRACKER_0_9),
101   MAP (tracker_sparql_escape, TRACKER_0_8 | TRACKER_0_9),
102   MAP (tracker_resources_sparql_query_async, TRACKER_0_8 | TRACKER_0_9)
103 #undef MAP
104 };
105
106 static TrackerVersion
107 open_libtracker (void)
108 {
109   static gboolean done = FALSE;
110   static TrackerVersion version = TRACKER_UNAVAILABLE;
111
112   if (!done)
113     {
114       gint i;
115       GModule *tracker;
116       GModuleFlags flags;
117
118       done = TRUE;
119       flags = G_MODULE_BIND_LAZY | G_MODULE_BIND_LOCAL;
120
121       /* So this is the order:
122        *
123        * - 0.9 (latest unstable)
124        * - 0.8 (stable)
125        * - 0.7 (unstable, 0.6 sucks so badly)
126        * - 0.6 (stable)
127        */
128       if ((tracker = g_module_open ("libtracker-client-0.9.so.0", flags)) != NULL)
129         version = TRACKER_0_9;
130       else if ((tracker = g_module_open ("libtracker-client-0.8.so.0", flags)) != NULL)
131         version = TRACKER_0_8;
132       else if ((tracker = g_module_open ("libtracker-client-0.7.so.0", flags)) != NULL)
133         version = TRACKER_0_7;
134       else if ((tracker = g_module_open ("libtrackerclient.so.0", flags)) != NULL)
135         version = TRACKER_0_6;
136       else
137         {
138           g_debug ("No tracker backend available");
139           return TRACKER_UNAVAILABLE;
140         }
141
142       for (i = 0; i < G_N_ELEMENTS (tracker_dl_mapping); i++)
143         {
144           if ((tracker_dl_mapping[i].versions & version) == 0)
145             continue;
146
147           if (!g_module_symbol (tracker,
148                                 tracker_dl_mapping[i].fn_name,
149                                 tracker_dl_mapping[i].fn_ptr_ref))
150             {
151               g_warning ("Missing symbol '%s' in libtracker\n",
152                          tracker_dl_mapping[i].fn_name);
153               g_module_close (tracker);
154
155               for (i = 0; i < G_N_ELEMENTS (tracker_dl_mapping); i++)
156                 tracker_dl_mapping[i].fn_ptr_ref = NULL;
157
158               return TRACKER_UNAVAILABLE;
159             }
160         }
161     }
162
163   return version;
164 }
165
166 struct _GtkSearchEngineTrackerPrivate
167 {
168   GtkQuery      *query;
169   TrackerClient *client;
170   gboolean       query_pending;
171   TrackerVersion version;
172 };
173
174 G_DEFINE_TYPE (GtkSearchEngineTracker, _gtk_search_engine_tracker, GTK_TYPE_SEARCH_ENGINE);
175
176
177 static void
178 finalize (GObject *object)
179 {
180   GtkSearchEngineTracker *tracker;
181
182   tracker = GTK_SEARCH_ENGINE_TRACKER (object);
183
184   if (tracker->priv->query)
185     {
186       g_object_unref (tracker->priv->query);
187       tracker->priv->query = NULL;
188     }
189
190   if (tracker->priv->version == TRACKER_0_8 ||
191       tracker->priv->version == TRACKER_0_9)
192     g_object_unref (tracker->priv->client);
193   else
194     tracker_disconnect (tracker->priv->client);
195
196   G_OBJECT_CLASS (_gtk_search_engine_tracker_parent_class)->finalize (object);
197 }
198
199
200 /* stolen from tracker sources, tracker.c */
201 static void
202 sparql_append_string_literal (GString     *sparql,
203                               const gchar *str)
204 {
205   gchar *s;
206
207   s = tracker_sparql_escape (str);
208
209   g_string_append_c (sparql, '"');
210   g_string_append (sparql, s);
211   g_string_append_c (sparql, '"');
212
213   g_free (s);
214 }
215
216
217 static void
218 search_callback (gpointer results,
219                  GError  *error,
220                  gpointer user_data)
221 {
222   GtkSearchEngineTracker *tracker;
223   gchar **results_p;
224   GList *hit_uris;
225   GPtrArray *OUT_result;
226   gchar *uri;
227   gint i;
228
229   tracker = GTK_SEARCH_ENGINE_TRACKER (user_data);
230   hit_uris = NULL;
231
232   tracker->priv->query_pending = FALSE;
233
234   if (error)
235     {
236       _gtk_search_engine_error (GTK_SEARCH_ENGINE (tracker), error->message);
237       g_error_free (error);
238       return;
239     }
240
241   if (!results)
242     return;
243
244   if (tracker->priv->version == TRACKER_0_8 ||
245       tracker->priv->version == TRACKER_0_9)
246     {
247       OUT_result = (GPtrArray*) results;
248
249       for (i = 0; i < OUT_result->len; i++)
250         {
251           uri = g_strdup (((gchar **) OUT_result->pdata[i])[0]);
252           if (uri)
253             hit_uris = g_list_prepend (hit_uris, uri);
254         }
255
256       g_ptr_array_foreach (OUT_result, (GFunc) g_free, NULL);
257       g_ptr_array_free (OUT_result, TRUE);
258     }
259   else
260     {
261       for (results_p = results; *results_p; results_p++)
262         {
263           if (tracker->priv->version == TRACKER_0_6)
264             uri = g_filename_to_uri (*results_p, NULL, NULL);
265           else
266             uri = g_strdup (*results_p);
267
268           if (uri)
269             hit_uris = g_list_prepend (hit_uris, uri);
270         }
271       g_strfreev ((gchar **) results);
272     }
273
274   _gtk_search_engine_hits_added (GTK_SEARCH_ENGINE (tracker), hit_uris);
275   _gtk_search_engine_finished (GTK_SEARCH_ENGINE (tracker));
276
277   g_list_foreach (hit_uris, (GFunc) g_free, NULL);
278   g_list_free (hit_uris);
279 }
280
281
282 static void
283 gtk_search_engine_tracker_start (GtkSearchEngine *engine)
284 {
285   GtkSearchEngineTracker *tracker;
286   gchar *search_text, *location, *location_uri;
287   GString *sparql;
288
289   tracker = GTK_SEARCH_ENGINE_TRACKER (engine);
290
291   if (tracker->priv->query_pending)
292     return;
293
294   if (tracker->priv->query == NULL)
295     return;
296
297   search_text = _gtk_query_get_text (tracker->priv->query);
298   location_uri = _gtk_query_get_location (tracker->priv->query);
299
300   location = NULL;
301   if (location_uri)
302     {
303       if (tracker->priv->version == TRACKER_0_6)
304         {
305           location = g_filename_from_uri (location_uri, NULL, NULL);
306           g_free (location_uri);
307         }
308       else
309         location = location_uri;
310     }
311
312   if (tracker->priv->version == TRACKER_0_8 ||
313       tracker->priv->version == TRACKER_0_9)
314     {
315       sparql = g_string_new ("SELECT nie:url(?urn) WHERE { ?urn a nfo:FileDataObject; fts:match ");
316       sparql_append_string_literal (sparql, search_text);
317       if (location)
318         {
319           g_string_append (sparql, " . FILTER (fn:starts-with(nie:url(?urn),");
320           sparql_append_string_literal (sparql, location);
321           g_string_append (sparql, "))");
322         }
323       g_string_append (sparql, " } ORDER BY DESC(fts:rank(?urn)) ASC(nie:url(?urn))");
324
325       tracker_resources_sparql_query_async (tracker->priv->client,
326                                             sparql->str,
327                                             (TrackerReplyGPtrArray) search_callback,
328                                             tracker);
329       g_string_free (sparql, TRUE);
330     }
331   else
332     {
333       if (location)
334         {
335           tracker_search_metadata_by_text_and_location_async (tracker->priv->client,
336                                                               search_text,
337                                                               location,
338                                                               (TrackerArrayReply) search_callback,
339                                                               tracker);
340         }
341       else
342         {
343           tracker_search_metadata_by_text_async (tracker->priv->client,
344                                                  search_text,
345                                                  (TrackerArrayReply) search_callback,
346                                                  tracker);
347         }
348     }
349
350   tracker->priv->query_pending = TRUE;
351   g_free (search_text);
352   g_free (location);
353 }
354
355 static void
356 gtk_search_engine_tracker_stop (GtkSearchEngine *engine)
357 {
358   GtkSearchEngineTracker *tracker;
359
360   tracker = GTK_SEARCH_ENGINE_TRACKER (engine);
361
362   if (tracker->priv->query && tracker->priv->query_pending)
363     {
364       tracker_cancel_last_call (tracker->priv->client);
365       tracker->priv->query_pending = FALSE;
366     }
367 }
368
369 static gboolean
370 gtk_search_engine_tracker_is_indexed (GtkSearchEngine *engine)
371 {
372   return TRUE;
373 }
374
375 static void
376 gtk_search_engine_tracker_set_query (GtkSearchEngine *engine,
377                                      GtkQuery        *query)
378 {
379   GtkSearchEngineTracker *tracker;
380
381   tracker = GTK_SEARCH_ENGINE_TRACKER (engine);
382
383   if (query)
384     g_object_ref (query);
385
386   if (tracker->priv->query)
387     g_object_unref (tracker->priv->query);
388
389   tracker->priv->query = query;
390 }
391
392 static void
393 _gtk_search_engine_tracker_class_init (GtkSearchEngineTrackerClass *class)
394 {
395   GObjectClass *gobject_class;
396   GtkSearchEngineClass *engine_class;
397
398   gobject_class = G_OBJECT_CLASS (class);
399   gobject_class->finalize = finalize;
400
401   engine_class = GTK_SEARCH_ENGINE_CLASS (class);
402   engine_class->set_query = gtk_search_engine_tracker_set_query;
403   engine_class->start = gtk_search_engine_tracker_start;
404   engine_class->stop = gtk_search_engine_tracker_stop;
405   engine_class->is_indexed = gtk_search_engine_tracker_is_indexed;
406
407   g_type_class_add_private (gobject_class, sizeof (GtkSearchEngineTrackerPrivate));
408 }
409
410 static void
411 _gtk_search_engine_tracker_init (GtkSearchEngineTracker *engine)
412 {
413   engine->priv = G_TYPE_INSTANCE_GET_PRIVATE (engine, GTK_TYPE_SEARCH_ENGINE_TRACKER, GtkSearchEngineTrackerPrivate);
414 }
415
416
417 GtkSearchEngine *
418 _gtk_search_engine_tracker_new (void)
419 {
420   GtkSearchEngineTracker *engine;
421   TrackerClient *tracker_client;
422   TrackerVersion version;
423   GError *err = NULL;
424
425   version = open_libtracker ();
426
427   if (version == TRACKER_0_8 ||
428       version == TRACKER_0_9)
429     {
430       tracker_client = tracker_client_new (TRACKER_CLIENT_ENABLE_WARNINGS, G_MAXINT);
431     }
432   else
433     {
434       if (!tracker_connect)
435         return NULL;
436
437       tracker_client = tracker_connect (FALSE, -1);
438     }
439
440   if (!tracker_client)
441     return NULL;
442
443
444   if (version == TRACKER_0_6)
445     {
446       if (!tracker_get_version)
447         return NULL;
448
449       tracker_get_version (tracker_client, &err);
450
451       if (err != NULL)
452         {
453           g_error_free (err);
454           tracker_disconnect (tracker_client);
455           return NULL;
456         }
457     }
458
459   engine = g_object_new (GTK_TYPE_SEARCH_ENGINE_TRACKER, NULL);
460
461   engine->priv->client = tracker_client;
462   engine->priv->query_pending = FALSE;
463   engine->priv->version = version;
464
465   return GTK_SEARCH_ENGINE (engine);
466 }