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