nautilus-3.6.3/libnautilus-private/nautilus-search-engine-simple.c

No issues found

  1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
  2 /*
  3  * Copyright (C) 2005 Red Hat, Inc
  4  *
  5  * Nautilus is free software; you can redistribute it and/or
  6  * modify it under the terms of the GNU General Public License as
  7  * published by the Free Software Foundation; either version 2 of the
  8  * License, or (at your option) any later version.
  9  *
 10  * Nautilus is distributed in the hope that it will be useful,
 11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 13  * General Public License for more details.
 14  *
 15  * You should have received a copy of the GNU General Public
 16  * License along with this program; see the file COPYING.  If not,
 17  * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 18  * Boston, MA 02111-1307, USA.
 19  *
 20  * Author: Alexander Larsson <alexl@redhat.com>
 21  *
 22  */
 23 
 24 #include <config.h>
 25 #include "nautilus-search-hit.h"
 26 #include "nautilus-search-provider.h"
 27 #include "nautilus-search-engine-simple.h"
 28 #include "nautilus-ui-utilities.h"
 29 #define DEBUG_FLAG NAUTILUS_DEBUG_SEARCH
 30 #include "nautilus-debug.h"
 31 
 32 #include <string.h>
 33 #include <glib.h>
 34 #include <gio/gio.h>
 35 
 36 #define BATCH_SIZE 500
 37 
 38 enum {
 39 	PROP_RECURSIVE = 1,
 40 	NUM_PROPERTIES
 41 };
 42 
 43 typedef struct {
 44 	NautilusSearchEngineSimple *engine;
 45 	GCancellable *cancellable;
 46 
 47 	GList *mime_types;
 48 	GList *found_list;
 49 
 50 	GQueue *directories; /* GFiles */
 51 
 52 	GHashTable *visited;
 53 
 54 	gboolean recursive;
 55 	gint n_processed_files;
 56 	GList *hits;
 57 
 58 	NautilusQuery *query;
 59 } SearchThreadData;
 60 
 61 
 62 struct NautilusSearchEngineSimpleDetails {
 63 	NautilusQuery *query;
 64 
 65 	SearchThreadData *active_search;
 66 
 67 	gboolean recursive;
 68 	gboolean query_finished;
 69 };
 70 
 71 static GParamSpec *properties[NUM_PROPERTIES] = { NULL, };
 72 
 73 static void nautilus_search_provider_init (NautilusSearchProviderIface  *iface);
 74 
 75 G_DEFINE_TYPE_WITH_CODE (NautilusSearchEngineSimple,
 76 			 nautilus_search_engine_simple,
 77 			 G_TYPE_OBJECT,
 78 			 G_IMPLEMENT_INTERFACE (NAUTILUS_TYPE_SEARCH_PROVIDER,
 79 						nautilus_search_provider_init))
 80 
 81 static void
 82 finalize (GObject *object)
 83 {
 84 	NautilusSearchEngineSimple *simple;
 85 
 86 	simple = NAUTILUS_SEARCH_ENGINE_SIMPLE (object);
 87 	g_clear_object (&simple->details->query);
 88 
 89 	G_OBJECT_CLASS (nautilus_search_engine_simple_parent_class)->finalize (object);
 90 }
 91 
 92 static SearchThreadData *
 93 search_thread_data_new (NautilusSearchEngineSimple *engine,
 94 			NautilusQuery *query)
 95 {
 96 	SearchThreadData *data;
 97 	char *uri;
 98 	GFile *location;
 99 	
100 	data = g_new0 (SearchThreadData, 1);
101 
102 	data->engine = g_object_ref (engine);
103 	data->directories = g_queue_new ();
104 	data->visited = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
105 	data->query = g_object_ref (query);
106 
107 	uri = nautilus_query_get_location (query);
108 	location = g_file_new_for_uri (uri);
109 	g_free (uri);
110 
111 	g_queue_push_tail (data->directories, location);
112 	data->mime_types = nautilus_query_get_mime_types (query);
113 
114 	data->cancellable = g_cancellable_new ();
115 	
116 	return data;
117 }
118 
119 static void 
120 search_thread_data_free (SearchThreadData *data)
121 {
122 	g_queue_foreach (data->directories,
123 			 (GFunc)g_object_unref, NULL);
124 	g_queue_free (data->directories);
125 	g_hash_table_destroy (data->visited);
126 	g_object_unref (data->cancellable);
127 	g_object_unref (data->query);
128 	g_list_free_full (data->mime_types, g_free);
129 	g_list_free_full (data->hits, g_object_unref);
130 	g_object_unref (data->engine);
131 
132 	g_free (data);
133 }
134 
135 static gboolean
136 search_thread_done_idle (gpointer user_data)
137 {
138 	SearchThreadData *data = user_data;
139 	NautilusSearchEngineSimple *engine = data->engine;
140 
141 	DEBUG ("Simple engine done");
142 
143 	engine->details->active_search = NULL;
144 	nautilus_search_provider_finished (NAUTILUS_SEARCH_PROVIDER (engine));
145 
146 	search_thread_data_free (data);
147 
148 	return FALSE;
149 }
150 
151 typedef struct {
152 	GList *hits;
153 	SearchThreadData *thread_data;
154 } SearchHitsData;
155 
156 
157 static gboolean
158 search_thread_add_hits_idle (gpointer user_data)
159 {
160 	SearchHitsData *data = user_data;
161 
162 	DEBUG ("Simple engine add hits");
163 
164 	if (!g_cancellable_is_cancelled (data->thread_data->cancellable)) {
165 		nautilus_search_provider_hits_added (NAUTILUS_SEARCH_PROVIDER (data->thread_data->engine),
166 						     data->hits);
167 	}
168 
169 	g_list_free_full (data->hits, g_object_unref);
170 	g_free (data);
171 	
172 	return FALSE;
173 }
174 
175 static void
176 send_batch (SearchThreadData *thread_data)
177 {
178 	SearchHitsData *data;
179 	
180 	thread_data->n_processed_files = 0;
181 	
182 	if (thread_data->hits) {
183 		data = g_new (SearchHitsData, 1);
184 		data->hits = thread_data->hits;
185 		data->thread_data = thread_data;
186 		g_idle_add (search_thread_add_hits_idle, data);
187 	}
188 	thread_data->hits = NULL;
189 }
190 
191 #define STD_ATTRIBUTES \
192 	G_FILE_ATTRIBUTE_STANDARD_NAME "," \
193 	G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME "," \
194 	G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN "," \
195 	G_FILE_ATTRIBUTE_STANDARD_TYPE "," \
196 	G_FILE_ATTRIBUTE_TIME_MODIFIED "," \
197 	G_FILE_ATTRIBUTE_ID_FILE
198 
199 static void
200 visit_directory (GFile *dir, SearchThreadData *data)
201 {
202 	GFileEnumerator *enumerator;
203 	GFileInfo *info;
204 	GFile *child;
205 	const char *mime_type, *display_name;
206 	gdouble match;
207 	gboolean is_hidden, found;
208 	GList *l;
209 	const char *id;
210 	gboolean visited;
211 
212 	enumerator = g_file_enumerate_children (dir,
213 						data->mime_types != NULL ?
214 						STD_ATTRIBUTES ","
215 						G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE
216 						:
217 						STD_ATTRIBUTES
218 						,
219 						0, data->cancellable, NULL);
220 	
221 	if (enumerator == NULL) {
222 		return;
223 	}
224 
225 	while ((info = g_file_enumerator_next_file (enumerator, data->cancellable, NULL)) != NULL) {
226 		display_name = g_file_info_get_display_name (info);
227 		if (display_name == NULL) {
228 			goto next;
229 		}
230 
231 		is_hidden = g_file_info_get_is_hidden (info) || g_file_info_get_is_backup (info);
232 		if (is_hidden && !nautilus_query_get_show_hidden_files (data->query)) {
233 			goto next;
234 		}
235 
236 		child = g_file_get_child (dir, g_file_info_get_name (info));
237 		match = nautilus_query_matches_string (data->query, display_name);
238 		found = (match > -1);
239 
240 		if (found && data->mime_types) {
241 			mime_type = g_file_info_get_content_type (info);
242 			found = FALSE;
243 			
244 			for (l = data->mime_types; mime_type != NULL && l != NULL; l = l->next) {
245 				if (g_content_type_is_a (mime_type, l->data)) {
246 					found = TRUE;
247 					break;
248 				}
249 			}
250 		}
251 		
252 		if (found) {
253 			NautilusSearchHit *hit;
254 			GTimeVal tv;
255 			GDateTime *dt;
256 			char *uri;
257 
258 			uri = g_file_get_uri (child);
259 			hit = nautilus_search_hit_new (uri);
260 			g_free (uri);
261 			nautilus_search_hit_set_fts_rank (hit, match);
262 			g_file_info_get_modification_time (info, &tv);
263 			dt = g_date_time_new_from_timeval_local (&tv);
264 			nautilus_search_hit_set_modification_time (hit, dt);
265 			g_date_time_unref (dt);
266 
267 			data->hits = g_list_prepend (data->hits, hit);
268 		}
269 		
270 		data->n_processed_files++;
271 		if (data->n_processed_files > BATCH_SIZE) {
272 			send_batch (data);
273 		}
274 
275 		if (data->engine->details->recursive && g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY) {
276 			id = g_file_info_get_attribute_string (info, G_FILE_ATTRIBUTE_ID_FILE);
277 			visited = FALSE;
278 			if (id) {
279 				if (g_hash_table_lookup_extended (data->visited,
280 								  id, NULL, NULL)) {
281 					visited = TRUE;
282 				} else {
283 					g_hash_table_insert (data->visited, g_strdup (id), NULL);
284 				}
285 			}
286 			
287 			if (!visited) {
288 				g_queue_push_tail (data->directories, g_object_ref (child));
289 			}
290 		}
291 		
292 		g_object_unref (child);
293 	next:
294 		g_object_unref (info);
295 	}
296 
297 	g_object_unref (enumerator);
298 }
299 
300 
301 static gpointer 
302 search_thread_func (gpointer user_data)
303 {
304 	SearchThreadData *data;
305 	GFile *dir;
306 	GFileInfo *info;
307 	const char *id;
308 
309 	data = user_data;
310 
311 	/* Insert id for toplevel directory into visited */
312 	dir = g_queue_peek_head (data->directories);
313 	info = g_file_query_info (dir, G_FILE_ATTRIBUTE_ID_FILE, 0, data->cancellable, NULL);
314 	if (info) {
315 		id = g_file_info_get_attribute_string (info, G_FILE_ATTRIBUTE_ID_FILE);
316 		if (id) {
317 			g_hash_table_insert (data->visited, g_strdup (id), NULL);
318 		}
319 		g_object_unref (info);
320 	}
321 	
322 	while (!g_cancellable_is_cancelled (data->cancellable) &&
323 	       (dir = g_queue_pop_head (data->directories)) != NULL) {
324 		visit_directory (dir, data);
325 		g_object_unref (dir);
326 	}
327 
328 	if (!g_cancellable_is_cancelled (data->cancellable)) {
329 		send_batch (data);
330 	}
331 
332 	g_idle_add (search_thread_done_idle, data);
333 	
334 	return NULL;
335 }
336 
337 static void
338 nautilus_search_engine_simple_start (NautilusSearchProvider *provider)
339 {
340 	NautilusSearchEngineSimple *simple;
341 	SearchThreadData *data;
342 	GThread *thread;
343 	
344 	simple = NAUTILUS_SEARCH_ENGINE_SIMPLE (provider);
345 
346 	if (simple->details->active_search != NULL) {
347 		return;
348 	}
349 
350 	DEBUG ("Simple engine start");
351 	
352 	data = search_thread_data_new (simple, simple->details->query);
353 
354 	thread = g_thread_new ("nautilus-search-simple", search_thread_func, data);
355 	simple->details->active_search = data;
356 
357 	g_thread_unref (thread);
358 }
359 
360 static void
361 nautilus_search_engine_simple_stop (NautilusSearchProvider *provider)
362 {
363 	NautilusSearchEngineSimple *simple;
364 
365 	simple = NAUTILUS_SEARCH_ENGINE_SIMPLE (provider);
366 
367 	if (simple->details->active_search != NULL) {
368 		DEBUG ("Simple engine stop");
369 		g_cancellable_cancel (simple->details->active_search->cancellable);
370 		simple->details->active_search = NULL;
371 	}
372 }
373 
374 static void
375 nautilus_search_engine_simple_set_query (NautilusSearchProvider *provider,
376 					 NautilusQuery          *query)
377 {
378 	NautilusSearchEngineSimple *simple;
379 
380 	simple = NAUTILUS_SEARCH_ENGINE_SIMPLE (provider);
381 
382 	g_object_ref (query);
383 	g_clear_object (&simple->details->query);
384 	simple->details->query = query;
385 }
386 
387 static void
388 nautilus_search_engine_simple_set_property (GObject *object,
389 					    guint arg_id,
390 					    const GValue *value,
391 					    GParamSpec *pspec)
392 {
393 	NautilusSearchEngineSimple *engine;
394 
395 	engine = NAUTILUS_SEARCH_ENGINE_SIMPLE (object);
396 
397 	switch (arg_id) {
398 	case PROP_RECURSIVE:
399 		engine->details->recursive = g_value_get_boolean (value);
400 		break;
401 	default:
402 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, arg_id, pspec);
403 		break;
404 	}
405 }
406 
407 static void
408 nautilus_search_engine_simple_get_property (GObject *object,
409 					    guint arg_id,
410 					    GValue *value,
411 					    GParamSpec *pspec)
412 {
413 	NautilusSearchEngineSimple *engine;
414 
415 	engine = NAUTILUS_SEARCH_ENGINE_SIMPLE (object);
416 
417 	switch (arg_id) {
418 	case PROP_RECURSIVE:
419 		g_value_set_boolean (value, engine->details->recursive);
420 		break;
421 	}
422 }
423 
424 static void
425 nautilus_search_provider_init (NautilusSearchProviderIface *iface)
426 {
427 	iface->set_query = nautilus_search_engine_simple_set_query;
428 	iface->start = nautilus_search_engine_simple_start;
429 	iface->stop = nautilus_search_engine_simple_stop;
430 }
431 
432 static void
433 nautilus_search_engine_simple_class_init (NautilusSearchEngineSimpleClass *class)
434 {
435 	GObjectClass *gobject_class;
436 
437 	gobject_class = G_OBJECT_CLASS (class);
438 	gobject_class->finalize = finalize;
439 	gobject_class->get_property = nautilus_search_engine_simple_get_property;
440 	gobject_class->set_property = nautilus_search_engine_simple_set_property;
441 
442 	properties[PROP_RECURSIVE] = g_param_spec_boolean ("recursive",
443 							   "recursive",
444 							   "recursive",
445 							   FALSE,
446 							   G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
447 
448 	g_object_class_install_properties (gobject_class, NUM_PROPERTIES, properties);
449 	g_type_class_add_private (class, sizeof (NautilusSearchEngineSimpleDetails));
450 }
451 
452 static void
453 nautilus_search_engine_simple_init (NautilusSearchEngineSimple *engine)
454 {
455 	engine->details = G_TYPE_INSTANCE_GET_PRIVATE (engine, NAUTILUS_TYPE_SEARCH_ENGINE_SIMPLE,
456 						       NautilusSearchEngineSimpleDetails);
457 }
458 
459 NautilusSearchEngineSimple *
460 nautilus_search_engine_simple_new (void)
461 {
462 	NautilusSearchEngineSimple *engine;
463 
464 	engine = g_object_new (NAUTILUS_TYPE_SEARCH_ENGINE_SIMPLE, NULL);
465 
466 	return engine;
467 }