1 /*
2 * Copyright (C) 2005 Novell, Inc.
3 *
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.
8 *
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.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
16 *
17 * Author: Anders Carlsson <andersca@imendio.com>
18 *
19 * Based on nautilus-search-engine.c
20 */
21
22 #include "config.h"
23 #include "gtksearchengine.h"
24 #include "gtksearchenginesimple.h"
25 #include "gtksearchenginemodel.h"
26 #include "gtksearchenginequartz.h"
27 #include "gtkintl.h"
28
29 #if defined(HAVE_TRACKER3)
30 #include "gtksearchenginetracker3.h"
31 #endif
32 #if !defined G_OS_WIN32 /* No tracker on windows */
33 #include "gtksearchenginetracker.h"
34 #define HAVE_TRACKER 1
35 #endif
36
37 #include <gdk/gdk.h> /* for GDK_WINDOWING_QUARTZ */
38
39 struct _GtkSearchEnginePrivate {
40 GtkSearchEngine *native;
41 gboolean native_running;
42 gchar *native_error;
43
44 GtkSearchEngine *simple;
45 gboolean simple_running;
46 gboolean got_results;
47 gchar *simple_error;
48
49 GtkSearchEngine *model;
50 gboolean model_running;
51 gchar *model_error;
52
53 gboolean running;
54 gboolean recursive;
55 GHashTable *hits;
56
57 GtkQuery *query;
58 };
59
60 enum
61 {
62 HITS_ADDED,
63 FINISHED,
64 ERROR,
65 LAST_SIGNAL
66 };
67
68 static guint signals[LAST_SIGNAL];
69
70 G_DEFINE_TYPE_WITH_PRIVATE (GtkSearchEngine, _gtk_search_engine, G_TYPE_OBJECT);
71
72 static void
set_query(GtkSearchEngine * engine,GtkQuery * query)73 set_query (GtkSearchEngine *engine,
74 GtkQuery *query)
75 {
76 g_set_object (&engine->priv->query, query);
77
78 if (engine->priv->native)
79 _gtk_search_engine_set_query (engine->priv->native, query);
80
81 if (engine->priv->simple)
82 _gtk_search_engine_set_query (engine->priv->simple, query);
83
84 if (engine->priv->model)
85 _gtk_search_engine_set_query (engine->priv->model, query);
86 }
87
88 static void
start(GtkSearchEngine * engine)89 start (GtkSearchEngine *engine)
90 {
91 g_hash_table_remove_all (engine->priv->hits);
92
93 if (engine->priv->native)
94 {
95 g_clear_pointer (&engine->priv->native_error, g_free);
96 _gtk_search_engine_start (engine->priv->native);
97 engine->priv->native_running = TRUE;
98 }
99
100 if (engine->priv->simple)
101 {
102 g_clear_pointer (&engine->priv->simple_error, g_free);
103 _gtk_search_engine_start (engine->priv->simple);
104 engine->priv->simple_running = TRUE;
105 }
106
107 if (engine->priv->model)
108 {
109 g_clear_pointer (&engine->priv->model_error, g_free);
110 _gtk_search_engine_start (engine->priv->model);
111 engine->priv->model_running = TRUE;
112 }
113
114 engine->priv->running = TRUE;
115 }
116
117 static void
stop(GtkSearchEngine * engine)118 stop (GtkSearchEngine *engine)
119 {
120 if (engine->priv->native)
121 {
122 _gtk_search_engine_stop (engine->priv->native);
123 engine->priv->native_running = FALSE;
124 }
125
126 if (engine->priv->simple)
127 {
128 _gtk_search_engine_stop (engine->priv->simple);
129 engine->priv->simple_running = FALSE;
130 }
131
132 if (engine->priv->model)
133 {
134 _gtk_search_engine_stop (engine->priv->model);
135 engine->priv->model_running = FALSE;
136 }
137
138 engine->priv->running = FALSE;
139
140 g_hash_table_remove_all (engine->priv->hits);
141 }
142
143 static void
finalize(GObject * object)144 finalize (GObject *object)
145 {
146 GtkSearchEngine *engine = GTK_SEARCH_ENGINE (object);
147
148 g_clear_object (&engine->priv->native);
149 g_free (engine->priv->native_error);
150
151 g_clear_object (&engine->priv->simple);
152 g_free (engine->priv->simple_error);
153
154 g_clear_object (&engine->priv->model);
155 g_free (engine->priv->model_error);
156
157 g_clear_pointer (&engine->priv->hits, g_hash_table_unref);
158
159 g_clear_object (&engine->priv->query);
160
161 G_OBJECT_CLASS (_gtk_search_engine_parent_class)->finalize (object);
162 }
163
164 static void
_gtk_search_engine_class_init(GtkSearchEngineClass * class)165 _gtk_search_engine_class_init (GtkSearchEngineClass *class)
166 {
167 GObjectClass *object_class = G_OBJECT_CLASS (class);
168
169 object_class->finalize = finalize;
170
171 class->set_query = set_query;
172 class->start = start;
173 class->stop = stop;
174
175 signals[HITS_ADDED] =
176 g_signal_new (I_("hits-added"),
177 G_TYPE_FROM_CLASS (class),
178 G_SIGNAL_RUN_LAST,
179 G_STRUCT_OFFSET (GtkSearchEngineClass, hits_added),
180 NULL, NULL,
181 NULL,
182 G_TYPE_NONE, 1,
183 G_TYPE_POINTER);
184
185 signals[FINISHED] =
186 g_signal_new (I_("finished"),
187 G_TYPE_FROM_CLASS (class),
188 G_SIGNAL_RUN_LAST,
189 G_STRUCT_OFFSET (GtkSearchEngineClass, finished),
190 NULL, NULL,
191 NULL,
192 G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
193
194 signals[ERROR] =
195 g_signal_new (I_("error"),
196 G_TYPE_FROM_CLASS (class),
197 G_SIGNAL_RUN_LAST,
198 G_STRUCT_OFFSET (GtkSearchEngineClass, error),
199 NULL, NULL,
200 NULL,
201 G_TYPE_NONE, 1,
202 G_TYPE_STRING);
203 }
204
205 static void
_gtk_search_engine_init(GtkSearchEngine * engine)206 _gtk_search_engine_init (GtkSearchEngine *engine)
207 {
208 engine->priv = _gtk_search_engine_get_instance_private (engine);
209
210 engine->priv->recursive = TRUE;
211 }
212
213 static void
hits_added(GtkSearchEngine * engine,GList * hits,gpointer data)214 hits_added (GtkSearchEngine *engine,
215 GList *hits,
216 gpointer data)
217 {
218 GtkSearchEngine *composite = GTK_SEARCH_ENGINE (data);
219 GList *added, *l;
220 GtkSearchHit *hit;
221
222 added = NULL;
223
224 for (l = hits; l; l = l->next)
225 {
226 hit = l->data;
227
228 if (!g_hash_table_contains (composite->priv->hits, hit))
229 {
230 hit = _gtk_search_hit_dup (hit);
231 g_hash_table_add (composite->priv->hits, hit);
232 added = g_list_prepend (added, hit);
233 }
234 }
235
236 if (added)
237 {
238 _gtk_search_engine_hits_added (composite, added);
239 g_list_free (added);
240 }
241 }
242
243 static void
update_status(GtkSearchEngine * engine)244 update_status (GtkSearchEngine *engine)
245 {
246 gboolean running;
247
248 running = engine->priv->native_running || engine->priv->simple_running;
249
250 if (running != engine->priv->running)
251 {
252 engine->priv->running = running;
253
254 if (!running)
255 {
256 if (engine->priv->native_error)
257 _gtk_search_engine_error (engine, engine->priv->native_error);
258 else if (engine->priv->simple_error)
259 _gtk_search_engine_error (engine, engine->priv->simple_error);
260 else if (engine->priv->model_error)
261 _gtk_search_engine_error (engine, engine->priv->model_error);
262 else
263 _gtk_search_engine_finished (engine, engine->priv->got_results);
264
265 engine->priv->got_results = FALSE;
266 }
267 }
268 }
269
270 static void
finished(GtkSearchEngine * engine,gboolean got_results,gpointer data)271 finished (GtkSearchEngine *engine,
272 gboolean got_results,
273 gpointer data)
274 {
275 GtkSearchEngine *composite = GTK_SEARCH_ENGINE (data);
276
277 if (engine == composite->priv->native)
278 composite->priv->native_running = FALSE;
279 else if (engine == composite->priv->simple)
280 composite->priv->simple_running = FALSE;
281 else if (engine == composite->priv->model)
282 composite->priv->model_running = FALSE;
283
284 composite->priv->got_results |= got_results;
285 update_status (composite);
286 }
287
288 static void
error(GtkSearchEngine * engine,const gchar * message,gpointer data)289 error (GtkSearchEngine *engine,
290 const gchar *message,
291 gpointer data)
292 {
293 GtkSearchEngine *composite = GTK_SEARCH_ENGINE (data);
294
295 if (engine == composite->priv->native)
296 {
297 g_free (composite->priv->native_error);
298 composite->priv->native_error = g_strdup (message);
299 composite->priv->native_running = FALSE;
300 }
301 else if (engine == composite->priv->simple)
302 {
303 g_free (composite->priv->simple_error);
304 composite->priv->simple_error = g_strdup (message);
305 composite->priv->simple_running = FALSE;
306 }
307 else if (engine == composite->priv->model)
308 {
309 g_free (composite->priv->model_error);
310 composite->priv->model_error = g_strdup (message);
311 composite->priv->model_running = FALSE;
312 }
313
314 update_status (composite);
315 }
316
317 static gboolean
search_hit_equal(gconstpointer a,gconstpointer b)318 search_hit_equal (gconstpointer a, gconstpointer b)
319 {
320 const GtkSearchHit *ha = (const GtkSearchHit *)a;
321 const GtkSearchHit *hb = (const GtkSearchHit *)b;
322
323 return g_file_equal (ha->file, hb->file);
324 }
325
326
327 static guint
search_hit_hash(gconstpointer a)328 search_hit_hash (gconstpointer a)
329 {
330 const GtkSearchHit *ha = (const GtkSearchHit *)a;
331
332 return g_file_hash (ha->file);
333 }
334
335 GtkSearchHit *
_gtk_search_hit_dup(GtkSearchHit * hit)336 _gtk_search_hit_dup (GtkSearchHit *hit)
337 {
338 GtkSearchHit *dup;
339
340 dup = g_new (GtkSearchHit, 1);
341 dup->file = g_object_ref (hit->file);
342 if (hit->info)
343 dup->info = g_object_ref (hit->info);
344 else
345 dup->info = NULL;
346
347 return dup;
348 }
349
350 void
_gtk_search_hit_free(GtkSearchHit * hit)351 _gtk_search_hit_free (GtkSearchHit *hit)
352 {
353 g_clear_object (&hit->file);
354 g_clear_object (&hit->info);
355 g_free (hit);
356 }
357
358 static void
connect_engine_signals(GtkSearchEngine * engine,gpointer data)359 connect_engine_signals (GtkSearchEngine *engine,
360 gpointer data)
361 {
362 g_signal_connect_object (engine, "hits-added", G_CALLBACK (hits_added), data, 0);
363 g_signal_connect_object (engine, "finished", G_CALLBACK (finished), data, 0);
364 g_signal_connect_object (engine, "error", G_CALLBACK (error), data, 0);
365 }
366
367 GtkSearchEngine *
_gtk_search_engine_new(void)368 _gtk_search_engine_new (void)
369 {
370 GtkSearchEngine *engine;
371
372 engine = g_object_new (GTK_TYPE_SEARCH_ENGINE, NULL);
373
374 engine->priv->simple = _gtk_search_engine_simple_new ();
375 g_debug ("Using simple search engine");
376 connect_engine_signals (engine->priv->simple, engine);
377
378 #if defined(HAVE_TRACKER3)
379 engine->priv->native = gtk_search_engine_tracker3_new ();
380 if (engine->priv->native)
381 {
382 g_debug ("Using Tracker3 search engine");
383 connect_engine_signals (engine->priv->native, engine);
384 _gtk_search_engine_simple_set_indexed_cb (GTK_SEARCH_ENGINE_SIMPLE (engine->priv->simple),
385 gtk_search_engine_tracker3_is_indexed,
386 g_object_ref (engine->priv->native),
387 g_object_unref);
388 }
389 #endif
390
391 #ifdef HAVE_TRACKER
392 if (!engine->priv->native)
393 {
394 engine->priv->native = _gtk_search_engine_tracker_new ();
395 if (engine->priv->native)
396 {
397 g_debug ("Using Tracker search engine");
398 connect_engine_signals (engine->priv->native, engine);
399 _gtk_search_engine_simple_set_indexed_cb (GTK_SEARCH_ENGINE_SIMPLE (engine->priv->simple),
400 _gtk_search_engine_tracker_is_indexed,
401 g_object_ref (engine->priv->native),
402 g_object_unref);
403 }
404 }
405 #endif
406
407 #ifdef GDK_WINDOWING_QUARTZ
408 engine->priv->native = _gtk_search_engine_quartz_new ();
409 if (engine->priv->native)
410 {
411 g_debug ("Using Quartz search engine");
412 connect_engine_signals (engine->priv->native, engine);
413 }
414 #endif
415
416 engine->priv->hits = g_hash_table_new_full (search_hit_hash, search_hit_equal,
417 (GDestroyNotify)_gtk_search_hit_free, NULL);
418
419 return engine;
420 }
421
422 void
_gtk_search_engine_set_query(GtkSearchEngine * engine,GtkQuery * query)423 _gtk_search_engine_set_query (GtkSearchEngine *engine,
424 GtkQuery *query)
425 {
426 g_return_if_fail (GTK_IS_SEARCH_ENGINE (engine));
427 g_return_if_fail (GTK_SEARCH_ENGINE_GET_CLASS (engine)->set_query != NULL);
428
429 GTK_SEARCH_ENGINE_GET_CLASS (engine)->set_query (engine, query);
430 }
431
432 void
_gtk_search_engine_start(GtkSearchEngine * engine)433 _gtk_search_engine_start (GtkSearchEngine *engine)
434 {
435 g_return_if_fail (GTK_IS_SEARCH_ENGINE (engine));
436 g_return_if_fail (GTK_SEARCH_ENGINE_GET_CLASS (engine)->start != NULL);
437
438 GTK_SEARCH_ENGINE_GET_CLASS (engine)->start (engine);
439 }
440
441 void
_gtk_search_engine_stop(GtkSearchEngine * engine)442 _gtk_search_engine_stop (GtkSearchEngine *engine)
443 {
444 g_return_if_fail (GTK_IS_SEARCH_ENGINE (engine));
445 g_return_if_fail (GTK_SEARCH_ENGINE_GET_CLASS (engine)->stop != NULL);
446
447 GTK_SEARCH_ENGINE_GET_CLASS (engine)->stop (engine);
448 }
449
450 void
_gtk_search_engine_hits_added(GtkSearchEngine * engine,GList * hits)451 _gtk_search_engine_hits_added (GtkSearchEngine *engine,
452 GList *hits)
453 {
454 g_return_if_fail (GTK_IS_SEARCH_ENGINE (engine));
455
456 g_signal_emit (engine, signals[HITS_ADDED], 0, hits);
457 }
458
459 void
_gtk_search_engine_finished(GtkSearchEngine * engine,gboolean got_results)460 _gtk_search_engine_finished (GtkSearchEngine *engine,
461 gboolean got_results)
462 {
463 g_return_if_fail (GTK_IS_SEARCH_ENGINE (engine));
464
465 g_signal_emit (engine, signals[FINISHED], 0, got_results);
466 }
467
468 void
_gtk_search_engine_error(GtkSearchEngine * engine,const gchar * error_message)469 _gtk_search_engine_error (GtkSearchEngine *engine,
470 const gchar *error_message)
471 {
472 g_return_if_fail (GTK_IS_SEARCH_ENGINE (engine));
473
474 g_signal_emit (engine, signals[ERROR], 0, error_message);
475 }
476
477 void
_gtk_search_engine_set_recursive(GtkSearchEngine * engine,gboolean recursive)478 _gtk_search_engine_set_recursive (GtkSearchEngine *engine,
479 gboolean recursive)
480 {
481 g_return_if_fail (GTK_IS_SEARCH_ENGINE (engine));
482
483 g_assert (!engine->priv->running);
484
485 engine->priv->recursive = recursive;
486
487 if (engine->priv->native)
488 _gtk_search_engine_set_recursive (engine->priv->native, recursive);
489
490 if (engine->priv->simple)
491 _gtk_search_engine_set_recursive (engine->priv->simple, recursive);
492 }
493
494 gboolean
_gtk_search_engine_get_recursive(GtkSearchEngine * engine)495 _gtk_search_engine_get_recursive (GtkSearchEngine *engine)
496 {
497 g_return_val_if_fail (GTK_IS_SEARCH_ENGINE (engine), TRUE);
498
499 return engine->priv->recursive;
500 }
501
502 void
_gtk_search_engine_set_model(GtkSearchEngine * engine,GtkFileSystemModel * model)503 _gtk_search_engine_set_model (GtkSearchEngine *engine,
504 GtkFileSystemModel *model)
505 {
506 g_clear_object (&engine->priv->model);
507 if (model)
508 {
509 engine->priv->model = _gtk_search_engine_model_new (model);
510 connect_engine_signals (engine->priv->model, engine);
511 if (engine->priv->query)
512 _gtk_search_engine_set_query (engine->priv->model, engine->priv->query);
513 }
514 }
515