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 "gtksearchengineprivate.h"
24#include "gtksearchenginemodelprivate.h"
25#include "gtksearchenginequartzprivate.h"
26#include "gtkintl.h"
27
28#include <gdk/gdk.h> /* for GDK_WINDOWING_MACOS */
29
30#if defined(HAVE_TRACKER3)
31#include "gtksearchenginetracker3private.h"
32#endif
33
34struct _GtkSearchEnginePrivate {
35 GtkSearchEngine *native;
36 gboolean native_running;
37 gboolean got_results;
38 char *native_error;
39
40 GtkSearchEngine *model;
41 gboolean model_running;
42 char *model_error;
43
44 gboolean running;
45 gboolean recursive;
46 GHashTable *hits;
47
48 GtkQuery *query;
49};
50
51enum
52{
53 HITS_ADDED,
54 FINISHED,
55 ERROR,
56 LAST_SIGNAL
57};
58
59static guint signals[LAST_SIGNAL];
60
61G_DEFINE_TYPE_WITH_PRIVATE (GtkSearchEngine, _gtk_search_engine, G_TYPE_OBJECT);
62
63static void
64set_query (GtkSearchEngine *engine,
65 GtkQuery *query)
66{
67 g_set_object (&engine->priv->query, query);
68
69 if (engine->priv->native)
70 _gtk_search_engine_set_query (engine: engine->priv->native, query);
71
72 if (engine->priv->model)
73 _gtk_search_engine_set_query (engine: engine->priv->model, query);
74}
75
76static void
77start (GtkSearchEngine *engine)
78{
79 g_hash_table_remove_all (hash_table: engine->priv->hits);
80
81 if (engine->priv->native)
82 {
83 g_clear_pointer (&engine->priv->native_error, g_free);
84 _gtk_search_engine_start (engine: engine->priv->native);
85 engine->priv->native_running = TRUE;
86 }
87
88 if (engine->priv->model)
89 {
90 g_clear_pointer (&engine->priv->model_error, g_free);
91 _gtk_search_engine_start (engine: engine->priv->model);
92 engine->priv->model_running = TRUE;
93 }
94
95 engine->priv->running = TRUE;
96}
97
98static void
99stop (GtkSearchEngine *engine)
100{
101 if (engine->priv->native)
102 {
103 _gtk_search_engine_stop (engine: engine->priv->native);
104 engine->priv->native_running = FALSE;
105 }
106
107 if (engine->priv->model)
108 {
109 _gtk_search_engine_stop (engine: engine->priv->model);
110 engine->priv->model_running = FALSE;
111 }
112
113 engine->priv->running = FALSE;
114
115 g_hash_table_remove_all (hash_table: engine->priv->hits);
116}
117
118static void
119finalize (GObject *object)
120{
121 GtkSearchEngine *engine = GTK_SEARCH_ENGINE (object);
122
123 g_clear_object (&engine->priv->native);
124 g_free (mem: engine->priv->native_error);
125
126 g_clear_object (&engine->priv->model);
127 g_free (mem: engine->priv->model_error);
128
129 g_clear_pointer (&engine->priv->hits, g_hash_table_unref);
130
131 g_clear_object (&engine->priv->query);
132
133 G_OBJECT_CLASS (_gtk_search_engine_parent_class)->finalize (object);
134}
135
136static void
137_gtk_search_engine_class_init (GtkSearchEngineClass *class)
138{
139 GObjectClass *object_class = G_OBJECT_CLASS (class);
140
141 object_class->finalize = finalize;
142
143 class->set_query = set_query;
144 class->start = start;
145 class->stop = stop;
146
147 signals[HITS_ADDED] =
148 g_signal_new (I_("hits-added"),
149 G_TYPE_FROM_CLASS (class),
150 signal_flags: G_SIGNAL_RUN_LAST,
151 G_STRUCT_OFFSET (GtkSearchEngineClass, hits_added),
152 NULL, NULL,
153 NULL,
154 G_TYPE_NONE, n_params: 1,
155 G_TYPE_POINTER);
156
157 signals[FINISHED] =
158 g_signal_new (I_("finished"),
159 G_TYPE_FROM_CLASS (class),
160 signal_flags: G_SIGNAL_RUN_LAST,
161 G_STRUCT_OFFSET (GtkSearchEngineClass, finished),
162 NULL, NULL,
163 NULL,
164 G_TYPE_NONE, n_params: 1, G_TYPE_BOOLEAN);
165
166 signals[ERROR] =
167 g_signal_new (I_("error"),
168 G_TYPE_FROM_CLASS (class),
169 signal_flags: G_SIGNAL_RUN_LAST,
170 G_STRUCT_OFFSET (GtkSearchEngineClass, error),
171 NULL, NULL,
172 NULL,
173 G_TYPE_NONE, n_params: 1,
174 G_TYPE_STRING);
175}
176
177static void
178_gtk_search_engine_init (GtkSearchEngine *engine)
179{
180 engine->priv = _gtk_search_engine_get_instance_private (self: engine);
181}
182
183static void
184hits_added (GtkSearchEngine *engine,
185 GList *hits,
186 gpointer data)
187{
188 GtkSearchEngine *composite = GTK_SEARCH_ENGINE (data);
189 GList *added, *l;
190 GtkSearchHit *hit;
191
192 added = NULL;
193
194 for (l = hits; l; l = l->next)
195 {
196 hit = l->data;
197
198 if (!g_hash_table_contains (hash_table: composite->priv->hits, key: hit))
199 {
200 hit = _gtk_search_hit_dup (hit);
201 g_hash_table_add (hash_table: composite->priv->hits, key: hit);
202 added = g_list_prepend (list: added, data: hit);
203 }
204 }
205
206 if (added)
207 {
208 _gtk_search_engine_hits_added (engine: composite, hits: added);
209 g_list_free (list: added);
210 }
211}
212
213static void
214update_status (GtkSearchEngine *engine)
215{
216 gboolean running;
217
218 running = engine->priv->native_running;
219
220 if (running != engine->priv->running)
221 {
222 engine->priv->running = running;
223
224 if (!running)
225 {
226 if (engine->priv->native_error)
227 _gtk_search_engine_error (engine, error_message: engine->priv->native_error);
228 else if (engine->priv->model_error)
229 _gtk_search_engine_error (engine, error_message: engine->priv->model_error);
230 else
231 _gtk_search_engine_finished (engine, got_results: engine->priv->got_results);
232
233 engine->priv->got_results = FALSE;
234 }
235 }
236}
237
238static void
239finished (GtkSearchEngine *engine,
240 gboolean got_results,
241 gpointer data)
242{
243 GtkSearchEngine *composite = GTK_SEARCH_ENGINE (data);
244
245 if (engine == composite->priv->native)
246 composite->priv->native_running = FALSE;
247 else if (engine == composite->priv->model)
248 composite->priv->model_running = FALSE;
249
250 composite->priv->got_results |= got_results;
251 update_status (engine: composite);
252}
253
254static void
255error (GtkSearchEngine *engine,
256 const char *message,
257 gpointer data)
258{
259 GtkSearchEngine *composite = GTK_SEARCH_ENGINE (data);
260
261 if (engine == composite->priv->native)
262 {
263 g_free (mem: composite->priv->native_error);
264 composite->priv->native_error = g_strdup (str: message);
265 composite->priv->native_running = FALSE;
266 }
267 else if (engine == composite->priv->model)
268 {
269 g_free (mem: composite->priv->model_error);
270 composite->priv->model_error = g_strdup (str: message);
271 composite->priv->model_running = FALSE;
272 }
273
274 update_status (engine: composite);
275}
276
277static gboolean
278search_hit_equal (gconstpointer a, gconstpointer b)
279{
280 const GtkSearchHit *ha = (const GtkSearchHit *)a;
281 const GtkSearchHit *hb = (const GtkSearchHit *)b;
282
283 return g_file_equal (file1: ha->file, file2: hb->file);
284}
285
286
287static guint
288search_hit_hash (gconstpointer a)
289{
290 const GtkSearchHit *ha = (const GtkSearchHit *)a;
291
292 return g_file_hash (file: ha->file);
293}
294
295GtkSearchHit *
296_gtk_search_hit_dup (GtkSearchHit *hit)
297{
298 GtkSearchHit *dup;
299
300 dup = g_new (GtkSearchHit, 1);
301 dup->file = g_object_ref (hit->file);
302 if (hit->info)
303 dup->info = g_object_ref (hit->info);
304 else
305 dup->info = NULL;
306
307 return dup;
308}
309
310void
311_gtk_search_hit_free (GtkSearchHit *hit)
312{
313 g_clear_object (&hit->file);
314 g_clear_object (&hit->info);
315 g_free (mem: hit);
316}
317
318static void
319connect_engine_signals (GtkSearchEngine *engine,
320 gpointer data)
321{
322 g_signal_connect_object (instance: engine, detailed_signal: "hits-added", G_CALLBACK (hits_added), gobject: data, connect_flags: 0);
323 g_signal_connect_object (instance: engine, detailed_signal: "finished", G_CALLBACK (finished), gobject: data, connect_flags: 0);
324 g_signal_connect_object (instance: engine, detailed_signal: "error", G_CALLBACK (error), gobject: data, connect_flags: 0);
325}
326
327GtkSearchEngine *
328_gtk_search_engine_new (void)
329{
330 GtkSearchEngine *engine;
331
332 engine = g_object_new (GTK_TYPE_SEARCH_ENGINE, NULL);
333
334#if defined(HAVE_TRACKER3)
335 engine->priv->native = gtk_search_engine_tracker3_new ();
336 if (engine->priv->native)
337 {
338 g_debug ("Using Tracker3 search engine");
339 connect_engine_signals (engine->priv->native, engine);
340 }
341#endif
342
343#ifdef GDK_WINDOWING_MACOS
344 engine->priv->native = _gtk_search_engine_quartz_new ();
345 if (engine->priv->native)
346 {
347 g_debug ("Using Quartz search engine");
348 connect_engine_signals (engine->priv->native, engine);
349 }
350#endif
351
352 engine->priv->hits = g_hash_table_new_full (hash_func: search_hit_hash, key_equal_func: search_hit_equal,
353 key_destroy_func: (GDestroyNotify)_gtk_search_hit_free, NULL);
354
355 return engine;
356}
357
358void
359_gtk_search_engine_set_query (GtkSearchEngine *engine,
360 GtkQuery *query)
361{
362 g_return_if_fail (GTK_IS_SEARCH_ENGINE (engine));
363 g_return_if_fail (GTK_SEARCH_ENGINE_GET_CLASS (engine)->set_query != NULL);
364
365 GTK_SEARCH_ENGINE_GET_CLASS (engine)->set_query (engine, query);
366}
367
368void
369_gtk_search_engine_start (GtkSearchEngine *engine)
370{
371 g_return_if_fail (GTK_IS_SEARCH_ENGINE (engine));
372 g_return_if_fail (GTK_SEARCH_ENGINE_GET_CLASS (engine)->start != NULL);
373
374 GTK_SEARCH_ENGINE_GET_CLASS (engine)->start (engine);
375}
376
377void
378_gtk_search_engine_stop (GtkSearchEngine *engine)
379{
380 g_return_if_fail (GTK_IS_SEARCH_ENGINE (engine));
381 g_return_if_fail (GTK_SEARCH_ENGINE_GET_CLASS (engine)->stop != NULL);
382
383 GTK_SEARCH_ENGINE_GET_CLASS (engine)->stop (engine);
384}
385
386void
387_gtk_search_engine_hits_added (GtkSearchEngine *engine,
388 GList *hits)
389{
390 g_return_if_fail (GTK_IS_SEARCH_ENGINE (engine));
391
392 g_signal_emit (instance: engine, signal_id: signals[HITS_ADDED], detail: 0, hits);
393}
394
395void
396_gtk_search_engine_finished (GtkSearchEngine *engine,
397 gboolean got_results)
398{
399 g_return_if_fail (GTK_IS_SEARCH_ENGINE (engine));
400
401 g_signal_emit (instance: engine, signal_id: signals[FINISHED], detail: 0, got_results);
402}
403
404void
405_gtk_search_engine_error (GtkSearchEngine *engine,
406 const char *error_message)
407{
408 g_return_if_fail (GTK_IS_SEARCH_ENGINE (engine));
409
410 g_signal_emit (instance: engine, signal_id: signals[ERROR], detail: 0, error_message);
411}
412
413void
414_gtk_search_engine_set_model (GtkSearchEngine *engine,
415 GtkFileSystemModel *model)
416{
417 g_clear_object (&engine->priv->model);
418 if (model)
419 {
420 engine->priv->model = _gtk_search_engine_model_new (model);
421 connect_engine_signals (engine: engine->priv->model, data: engine);
422 if (engine->priv->query)
423 _gtk_search_engine_set_query (engine: engine->priv->model, query: engine->priv->query);
424 }
425}
426

source code of gtk/gtk/gtksearchengine.c