]> Pileus Git - ~andy/gtk/blob - gtk/gtksearchenginesimple.c
55e8f7b9e1f66775aa34c0ea7e238850375a6923
[~andy/gtk] / gtk / gtksearchenginesimple.c
1 /*
2  * Copyright (C) 2005 Red Hat, 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, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  *
18  * Author: Alexander Larsson <alexl@redhat.com>
19  *
20  * Based on nautilus-search-engine-simple.c
21  */
22
23 #include "config.h"
24
25 /* these must be defined even when HAVE_GNU_FTW is not defined
26  * because (really) old versions of GNU libc have ftw.h but do
27  * export ftw() and friends only if _XOPEN_SOURCE and _GNU_SOURCE
28  * are defined. see bug #444097.
29  */
30 #define _XOPEN_SOURCE 500
31 #define _GNU_SOURCE 
32
33 #ifdef HAVE_FTW_H
34 #include <ftw.h>
35 #endif
36
37 #include <gdk/gdk.h>
38
39 #include "gtksearchenginesimple.h"
40 #include "gtkprivate.h"
41
42 #include <string.h>
43
44 #define BATCH_SIZE 500
45
46 typedef struct 
47 {
48   GtkSearchEngineSimple *engine;
49   
50   gchar *path;
51   gchar **words;
52   GList *found_list;
53   
54   gint n_processed_files;
55   GList *uri_hits;
56   
57   /* accessed on both threads: */
58   volatile gboolean cancelled;
59 } SearchThreadData;
60
61
62 struct _GtkSearchEngineSimplePrivate 
63 {
64   GtkQuery *query;
65   
66   SearchThreadData *active_search;
67   
68   gboolean query_finished;
69 };
70
71
72 G_DEFINE_TYPE (GtkSearchEngineSimple, _gtk_search_engine_simple, GTK_TYPE_SEARCH_ENGINE);
73
74 static void
75 gtk_search_engine_simple_dispose (GObject *object)
76 {
77   GtkSearchEngineSimple *simple;
78   GtkSearchEngineSimplePrivate *priv;
79   
80   simple = GTK_SEARCH_ENGINE_SIMPLE (object);
81   priv = simple->priv;
82   
83   if (priv->query) 
84     {
85       g_object_unref (priv->query);
86       priv->query = NULL;
87     }
88
89   if (priv->active_search)
90     {
91       priv->active_search->cancelled = TRUE;
92       priv->active_search = NULL;
93     }
94   
95   G_OBJECT_CLASS (_gtk_search_engine_simple_parent_class)->dispose (object);
96 }
97
98 static SearchThreadData *
99 search_thread_data_new (GtkSearchEngineSimple *engine,
100                         GtkQuery              *query)
101 {
102   SearchThreadData *data;
103   char *text, *lower, *uri;
104   
105   data = g_new0 (SearchThreadData, 1);
106   
107   data->engine = g_object_ref (engine);
108   uri = _gtk_query_get_location (query);
109   if (uri != NULL) 
110     {
111       data->path = g_filename_from_uri (uri, NULL, NULL);
112       g_free (uri);
113     }
114   if (data->path == NULL)
115     data->path = g_strdup (g_get_home_dir ());
116         
117   text = _gtk_query_get_text (query);
118   lower = g_ascii_strdown (text, -1);
119   data->words = g_strsplit (lower, " ", -1);
120   g_free (text);
121   g_free (lower);
122   
123   return data;
124 }
125
126 static void 
127 search_thread_data_free (SearchThreadData *data)
128 {
129   g_object_unref (data->engine);
130   g_free (data->path);
131   g_strfreev (data->words);
132   g_free (data);
133 }
134
135 static gboolean
136 search_thread_done_idle (gpointer user_data)
137 {
138   SearchThreadData *data;
139
140   data = user_data;
141   
142   if (!data->cancelled)
143     _gtk_search_engine_finished (GTK_SEARCH_ENGINE (data->engine));
144      
145   data->engine->priv->active_search = NULL;
146   search_thread_data_free (data);
147   
148   return FALSE;
149 }
150
151 typedef struct 
152 {
153   GList *uris;
154   SearchThreadData *thread_data;
155 } SearchHits;
156
157
158 static gboolean
159 search_thread_add_hits_idle (gpointer user_data)
160 {
161   SearchHits *hits;
162
163   hits = user_data;
164
165   if (!hits->thread_data->cancelled) 
166     {
167       _gtk_search_engine_hits_added (GTK_SEARCH_ENGINE (hits->thread_data->engine),
168                                     hits->uris);
169     }
170
171   g_list_free_full (hits->uris, g_free);
172   g_free (hits);
173         
174   return FALSE;
175 }
176
177 static void
178 send_batch (SearchThreadData *data)
179 {
180   SearchHits *hits;
181   
182   data->n_processed_files = 0;
183   
184   if (data->uri_hits) 
185     {
186       hits = g_new (SearchHits, 1);
187       hits->uris = data->uri_hits;
188       hits->thread_data = data;
189       
190       gdk_threads_add_idle (search_thread_add_hits_idle, hits);
191     }
192
193   data->uri_hits = NULL;
194 }
195
196 static GPrivate search_thread_data;
197
198 #ifdef HAVE_FTW_H
199 static int
200 search_visit_func (const char        *fpath,
201                    const struct stat *sb,
202                    int                typeflag,
203                    struct FTW        *ftwbuf)
204 {
205   SearchThreadData *data;
206   gint i;
207   const gchar *name;
208   gchar *lower_name;
209   gchar *uri;
210   gboolean hit;
211   gboolean is_hidden;
212
213   data = (SearchThreadData*)g_private_get (&search_thread_data);
214
215   if (data->cancelled)
216 #ifdef HAVE_GNU_FTW
217     return FTW_STOP;
218 #else
219     return 1;
220 #endif /* HAVE_GNU_FTW */
221
222   name = strrchr (fpath, '/');
223   if (name)
224     name++;
225   else
226     name = fpath;
227
228   is_hidden = *name == '.';
229         
230   hit = FALSE;
231   
232   if (!is_hidden) 
233     {
234       lower_name = g_ascii_strdown (name, -1);
235       
236       hit = TRUE;
237       for (i = 0; data->words[i] != NULL; i++) 
238         {
239           if (strstr (lower_name, data->words[i]) == NULL) 
240             {
241               hit = FALSE;
242               break;
243             }
244         }
245       g_free (lower_name);
246     }
247
248   if (hit) 
249     {
250       uri = g_filename_to_uri (fpath, NULL, NULL);
251       data->uri_hits = g_list_prepend (data->uri_hits, uri);
252     }
253
254   data->n_processed_files++;
255   
256   if (data->n_processed_files > BATCH_SIZE)
257     send_batch (data);
258
259 #ifdef HAVE_GNU_FTW
260   if (is_hidden)
261     return FTW_SKIP_SUBTREE;
262   else
263     return FTW_CONTINUE;
264 #else
265   return 0;
266 #endif /* HAVE_GNU_FTW */
267 }
268 #endif /* HAVE_FTW_H */
269
270 static gpointer 
271 search_thread_func (gpointer user_data)
272 {
273 #ifdef HAVE_FTW_H
274   SearchThreadData *data;
275   
276   data = user_data;
277   
278   g_private_set (&search_thread_data, data);
279
280   nftw (data->path, search_visit_func, 20,
281 #ifdef HAVE_GNU_FTW
282         FTW_ACTIONRETVAL |
283 #endif
284         FTW_PHYS);
285
286   send_batch (data);
287   
288   gdk_threads_add_idle (search_thread_done_idle, data);
289 #endif /* HAVE_FTW_H */
290   
291   return NULL;
292 }
293
294 static void
295 gtk_search_engine_simple_start (GtkSearchEngine *engine)
296 {
297   GtkSearchEngineSimple *simple;
298   SearchThreadData *data;
299   
300   simple = GTK_SEARCH_ENGINE_SIMPLE (engine);
301   
302   if (simple->priv->active_search != NULL)
303     return;
304   
305   if (simple->priv->query == NULL)
306     return;
307         
308   data = search_thread_data_new (simple, simple->priv->query);
309   
310   g_thread_unref (g_thread_new ("file-search", search_thread_func, data));
311   
312   simple->priv->active_search = data;
313 }
314
315 static void
316 gtk_search_engine_simple_stop (GtkSearchEngine *engine)
317 {
318   GtkSearchEngineSimple *simple;
319   
320   simple = GTK_SEARCH_ENGINE_SIMPLE (engine);
321   
322   if (simple->priv->active_search != NULL) 
323     {
324       simple->priv->active_search->cancelled = TRUE;
325       simple->priv->active_search = NULL;
326     }
327 }
328
329 static gboolean
330 gtk_search_engine_simple_is_indexed (GtkSearchEngine *engine)
331 {
332   return FALSE;
333 }
334
335 static void
336 gtk_search_engine_simple_set_query (GtkSearchEngine *engine, 
337                                     GtkQuery        *query)
338 {
339   GtkSearchEngineSimple *simple;
340   
341   simple = GTK_SEARCH_ENGINE_SIMPLE (engine);
342   
343   if (query)
344     g_object_ref (query);
345
346   if (simple->priv->query) 
347     g_object_unref (simple->priv->query);
348
349   simple->priv->query = query;
350 }
351
352 static void
353 _gtk_search_engine_simple_class_init (GtkSearchEngineSimpleClass *class)
354 {
355   GObjectClass *gobject_class;
356   GtkSearchEngineClass *engine_class;
357   
358   gobject_class = G_OBJECT_CLASS (class);
359   gobject_class->dispose = gtk_search_engine_simple_dispose;
360   
361   engine_class = GTK_SEARCH_ENGINE_CLASS (class);
362   engine_class->set_query = gtk_search_engine_simple_set_query;
363   engine_class->start = gtk_search_engine_simple_start;
364   engine_class->stop = gtk_search_engine_simple_stop;
365   engine_class->is_indexed = gtk_search_engine_simple_is_indexed;
366
367   g_type_class_add_private (gobject_class, sizeof (GtkSearchEngineSimplePrivate));
368 }
369
370 static void
371 _gtk_search_engine_simple_init (GtkSearchEngineSimple *engine)
372 {
373   engine->priv = G_TYPE_INSTANCE_GET_PRIVATE (engine, GTK_TYPE_SEARCH_ENGINE_SIMPLE, GtkSearchEngineSimplePrivate);
374 }
375
376 GtkSearchEngine *
377 _gtk_search_engine_simple_new (void)
378 {
379 #ifdef HAVE_FTW_H
380   return g_object_new (GTK_TYPE_SEARCH_ENGINE_SIMPLE, NULL);
381 #else
382   return NULL;
383 #endif
384 }