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 | |
34 | struct _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 | |
51 | enum |
52 | { |
53 | HITS_ADDED, |
54 | FINISHED, |
55 | ERROR, |
56 | LAST_SIGNAL |
57 | }; |
58 | |
59 | static guint signals[LAST_SIGNAL]; |
60 | |
61 | G_DEFINE_TYPE_WITH_PRIVATE (GtkSearchEngine, _gtk_search_engine, G_TYPE_OBJECT); |
62 | |
63 | static void |
64 | set_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 | |
76 | static void |
77 | start (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 | |
98 | static void |
99 | stop (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 | |
118 | static void |
119 | finalize (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 | |
136 | static 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 | |
177 | static void |
178 | _gtk_search_engine_init (GtkSearchEngine *engine) |
179 | { |
180 | engine->priv = _gtk_search_engine_get_instance_private (self: engine); |
181 | } |
182 | |
183 | static void |
184 | hits_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 | |
213 | static void |
214 | update_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 | |
238 | static void |
239 | finished (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 | |
254 | static void |
255 | error (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 | |
277 | static gboolean |
278 | search_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 | |
287 | static guint |
288 | search_hit_hash (gconstpointer a) |
289 | { |
290 | const GtkSearchHit *ha = (const GtkSearchHit *)a; |
291 | |
292 | return g_file_hash (file: ha->file); |
293 | } |
294 | |
295 | GtkSearchHit * |
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 | |
310 | void |
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 | |
318 | static void |
319 | connect_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 | |
327 | GtkSearchEngine * |
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 | |
358 | void |
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 | |
368 | void |
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 | |
377 | void |
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 | |
386 | void |
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 | |
395 | void |
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 | |
404 | void |
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 | |
413 | void |
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 | |