1 /*
2 * Copyright (C) 2005 Novell, Inc
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of the
7 * License, or (at your option) any later version.
8 *
9 * This program 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 * General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public
15 * License along with this program; if not, see <http://www.gnu.org/licenses/>.
16 *
17 * Author: Anders Carlsson <andersca@imendio.com>
18 */
19
20 #include "nautilus-search-directory.h"
21
22 #include <eel/eel-glib-extensions.h>
23 #include <gio/gio.h>
24 #include <gtk/gtk.h>
25 #include <string.h>
26 #include <sys/time.h>
27
28 #include "nautilus-directory-private.h"
29 #include "nautilus-file-private.h"
30 #include "nautilus-file-utilities.h"
31 #include "nautilus-file.h"
32 #include "nautilus-query.h"
33 #include "nautilus-search-directory-file.h"
34 #include "nautilus-search-engine-model.h"
35 #include "nautilus-search-engine.h"
36 #include "nautilus-search-provider.h"
37
38 struct _NautilusSearchDirectory
39 {
40 NautilusDirectory parent_instance;
41
42 NautilusQuery *query;
43
44 NautilusSearchEngine *engine;
45
46 gboolean search_running;
47 /* When the search directory is stopped or cancelled, we migth wait
48 * until all data and signals from previous search are stopped and removed
49 * from the search engine. While this situation happens we don't want to connect
50 * clients to our signals, and we will wait until the search data and signals
51 * are valid and ready.
52 * The worst thing that can happens if we don't do this is that new clients
53 * migth get the information of old searchs if they are waiting_for_file_list.
54 * But that shouldn't be a big deal since old clients have the old information.
55 * But anyway it's currently unused for this case since the only client is
56 * nautilus-view and is not waiting_for_file_list :) .
57 *
58 * The other use case is for letting clients know if information of the directory
59 * is outdated or not valid. This might happens for automatic
60 * scheduled timeouts. */
61 gboolean search_ready_and_valid;
62
63 GList *files;
64 GHashTable *files_hash;
65
66 GList *monitor_list;
67 GList *callback_list;
68 GList *pending_callback_list;
69
70 GBinding *binding;
71
72 NautilusDirectory *base_model;
73 };
74
75 typedef struct
76 {
77 gboolean monitor_hidden_files;
78 NautilusFileAttributes monitor_attributes;
79
80 gconstpointer client;
81 } SearchMonitor;
82
83 typedef struct
84 {
85 NautilusSearchDirectory *search_directory;
86
87 NautilusDirectoryCallback callback;
88 gpointer callback_data;
89
90 NautilusFileAttributes wait_for_attributes;
91 gboolean wait_for_file_list;
92 GList *file_list;
93 GHashTable *non_ready_hash;
94 } SearchCallback;
95
96 enum
97 {
98 PROP_0,
99 PROP_BASE_MODEL,
100 PROP_QUERY,
101 NUM_PROPERTIES
102 };
103
104 G_DEFINE_TYPE_WITH_CODE (NautilusSearchDirectory, nautilus_search_directory, NAUTILUS_TYPE_DIRECTORY,
105 nautilus_ensure_extension_points ();
106 /* It looks like you’re implementing an extension point.
107 * Did you modify nautilus_ensure_extension_builtins() accordingly?
108 *
109 * • Yes
110 * • Doing it right now
111 */
112 g_io_extension_point_implement (NAUTILUS_DIRECTORY_PROVIDER_EXTENSION_POINT_NAME,
113 g_define_type_id,
114 NAUTILUS_SEARCH_DIRECTORY_PROVIDER_NAME,
115 0));
116
117 static GParamSpec *properties[NUM_PROPERTIES] = { NULL, };
118
119 static void search_engine_hits_added (NautilusSearchEngine *engine,
120 GList *hits,
121 NautilusSearchDirectory *self);
122 static void search_engine_error (NautilusSearchEngine *engine,
123 const char *error,
124 NautilusSearchDirectory *self);
125 static void search_callback_file_ready_callback (NautilusFile *file,
126 gpointer data);
127 static void file_changed (NautilusFile *file,
128 NautilusSearchDirectory *self);
129
130 static void
reset_file_list(NautilusSearchDirectory * self)131 reset_file_list (NautilusSearchDirectory *self)
132 {
133 GList *list, *monitor_list;
134 NautilusFile *file;
135 SearchMonitor *monitor;
136
137 /* Remove file connections */
138 for (list = self->files; list != NULL; list = list->next)
139 {
140 file = list->data;
141
142 /* Disconnect change handler */
143 g_signal_handlers_disconnect_by_func (file, file_changed, self);
144
145 /* Remove monitors */
146 for (monitor_list = self->monitor_list; monitor_list;
147 monitor_list = monitor_list->next)
148 {
149 monitor = monitor_list->data;
150 nautilus_file_monitor_remove (file, monitor);
151 }
152 }
153
154 nautilus_file_list_free (self->files);
155 self->files = NULL;
156
157 g_hash_table_remove_all (self->files_hash);
158 }
159
160 static void
set_hidden_files(NautilusSearchDirectory * self)161 set_hidden_files (NautilusSearchDirectory *self)
162 {
163 GList *l;
164 SearchMonitor *monitor;
165 gboolean monitor_hidden = FALSE;
166
167 for (l = self->monitor_list; l != NULL; l = l->next)
168 {
169 monitor = l->data;
170 monitor_hidden |= monitor->monitor_hidden_files;
171
172 if (monitor_hidden)
173 {
174 break;
175 }
176 }
177
178 nautilus_query_set_show_hidden_files (self->query, monitor_hidden);
179 }
180
181 static void
start_search(NautilusSearchDirectory * self)182 start_search (NautilusSearchDirectory *self)
183 {
184 NautilusSearchEngineModel *model_provider;
185
186 if (!self->query)
187 {
188 return;
189 }
190
191 if (self->search_running)
192 {
193 return;
194 }
195
196 if (!self->monitor_list && !self->pending_callback_list)
197 {
198 return;
199 }
200
201 /* We need to start the search engine */
202 self->search_running = TRUE;
203 self->search_ready_and_valid = FALSE;
204
205 set_hidden_files (self);
206 nautilus_search_provider_set_query (NAUTILUS_SEARCH_PROVIDER (self->engine),
207 self->query);
208
209 model_provider = nautilus_search_engine_get_model_provider (self->engine);
210 nautilus_search_engine_model_set_model (model_provider, self->base_model);
211
212 reset_file_list (self);
213
214 nautilus_search_provider_start (NAUTILUS_SEARCH_PROVIDER (self->engine));
215 }
216
217 static void
stop_search(NautilusSearchDirectory * self)218 stop_search (NautilusSearchDirectory *self)
219 {
220 if (!self->search_running)
221 {
222 return;
223 }
224
225 self->search_running = FALSE;
226 nautilus_search_provider_stop (NAUTILUS_SEARCH_PROVIDER (self->engine));
227
228 reset_file_list (self);
229 }
230
231 static void
file_changed(NautilusFile * file,NautilusSearchDirectory * self)232 file_changed (NautilusFile *file,
233 NautilusSearchDirectory *self)
234 {
235 GList list;
236
237 list.data = file;
238 list.next = NULL;
239
240 nautilus_directory_emit_files_changed (NAUTILUS_DIRECTORY (self), &list);
241 }
242
243 static void
search_monitor_add(NautilusDirectory * directory,gconstpointer client,gboolean monitor_hidden_files,NautilusFileAttributes file_attributes,NautilusDirectoryCallback callback,gpointer callback_data)244 search_monitor_add (NautilusDirectory *directory,
245 gconstpointer client,
246 gboolean monitor_hidden_files,
247 NautilusFileAttributes file_attributes,
248 NautilusDirectoryCallback callback,
249 gpointer callback_data)
250 {
251 GList *list;
252 SearchMonitor *monitor;
253 NautilusSearchDirectory *self;
254 NautilusFile *file;
255
256 self = NAUTILUS_SEARCH_DIRECTORY (directory);
257
258 monitor = g_new0 (SearchMonitor, 1);
259 monitor->monitor_hidden_files = monitor_hidden_files;
260 monitor->monitor_attributes = file_attributes;
261 monitor->client = client;
262
263 self->monitor_list = g_list_prepend (self->monitor_list, monitor);
264
265 if (callback != NULL)
266 {
267 (*callback)(directory, self->files, callback_data);
268 }
269
270 for (list = self->files; list != NULL; list = list->next)
271 {
272 file = list->data;
273
274 /* Add monitors */
275 nautilus_file_monitor_add (file, monitor, file_attributes);
276 }
277
278 start_search (self);
279 }
280
281 static void
search_monitor_remove_file_monitors(SearchMonitor * monitor,NautilusSearchDirectory * self)282 search_monitor_remove_file_monitors (SearchMonitor *monitor,
283 NautilusSearchDirectory *self)
284 {
285 GList *list;
286 NautilusFile *file;
287
288 for (list = self->files; list != NULL; list = list->next)
289 {
290 file = list->data;
291
292 nautilus_file_monitor_remove (file, monitor);
293 }
294 }
295
296 static void
search_monitor_destroy(SearchMonitor * monitor,NautilusSearchDirectory * self)297 search_monitor_destroy (SearchMonitor *monitor,
298 NautilusSearchDirectory *self)
299 {
300 search_monitor_remove_file_monitors (monitor, self);
301
302 g_free (monitor);
303 }
304
305 static void
search_monitor_remove(NautilusDirectory * directory,gconstpointer client)306 search_monitor_remove (NautilusDirectory *directory,
307 gconstpointer client)
308 {
309 NautilusSearchDirectory *self;
310 SearchMonitor *monitor;
311 GList *list;
312
313 self = NAUTILUS_SEARCH_DIRECTORY (directory);
314
315 for (list = self->monitor_list; list != NULL; list = list->next)
316 {
317 monitor = list->data;
318
319 if (monitor->client == client)
320 {
321 self->monitor_list = g_list_delete_link (self->monitor_list, list);
322
323 search_monitor_destroy (monitor, self);
324
325 break;
326 }
327 }
328
329 if (!self->monitor_list)
330 {
331 stop_search (self);
332 }
333 }
334
335 static void
cancel_call_when_ready(gpointer key,gpointer value,gpointer user_data)336 cancel_call_when_ready (gpointer key,
337 gpointer value,
338 gpointer user_data)
339 {
340 SearchCallback *search_callback;
341 NautilusFile *file;
342
343 file = key;
344 search_callback = user_data;
345
346 nautilus_file_cancel_call_when_ready (file, search_callback_file_ready_callback,
347 search_callback);
348 }
349
350 static void
search_callback_destroy(SearchCallback * search_callback)351 search_callback_destroy (SearchCallback *search_callback)
352 {
353 if (search_callback->non_ready_hash)
354 {
355 g_hash_table_foreach (search_callback->non_ready_hash, cancel_call_when_ready, search_callback);
356 g_hash_table_destroy (search_callback->non_ready_hash);
357 }
358
359 nautilus_file_list_free (search_callback->file_list);
360
361 g_free (search_callback);
362 }
363
364 static void
search_callback_invoke_and_destroy(SearchCallback * search_callback)365 search_callback_invoke_and_destroy (SearchCallback *search_callback)
366 {
367 search_callback->callback (NAUTILUS_DIRECTORY (search_callback->search_directory),
368 search_callback->file_list,
369 search_callback->callback_data);
370
371 search_callback->search_directory->callback_list =
372 g_list_remove (search_callback->search_directory->callback_list, search_callback);
373
374 search_callback_destroy (search_callback);
375 }
376
377 static void
search_callback_file_ready_callback(NautilusFile * file,gpointer data)378 search_callback_file_ready_callback (NautilusFile *file,
379 gpointer data)
380 {
381 SearchCallback *search_callback = data;
382
383 g_hash_table_remove (search_callback->non_ready_hash, file);
384
385 if (g_hash_table_size (search_callback->non_ready_hash) == 0)
386 {
387 search_callback_invoke_and_destroy (search_callback);
388 }
389 }
390
391 static void
search_callback_add_file_callbacks(SearchCallback * callback)392 search_callback_add_file_callbacks (SearchCallback *callback)
393 {
394 GList *file_list_copy, *list;
395 NautilusFile *file;
396
397 file_list_copy = g_list_copy (callback->file_list);
398
399 for (list = file_list_copy; list != NULL; list = list->next)
400 {
401 file = list->data;
402
403 nautilus_file_call_when_ready (file,
404 callback->wait_for_attributes,
405 search_callback_file_ready_callback,
406 callback);
407 }
408 g_list_free (file_list_copy);
409 }
410
411 static SearchCallback *
search_callback_find(NautilusSearchDirectory * self,NautilusDirectoryCallback callback,gpointer callback_data)412 search_callback_find (NautilusSearchDirectory *self,
413 NautilusDirectoryCallback callback,
414 gpointer callback_data)
415 {
416 SearchCallback *search_callback;
417 GList *list;
418
419 for (list = self->callback_list; list != NULL; list = list->next)
420 {
421 search_callback = list->data;
422
423 if (search_callback->callback == callback &&
424 search_callback->callback_data == callback_data)
425 {
426 return search_callback;
427 }
428 }
429
430 return NULL;
431 }
432
433 static SearchCallback *
search_callback_find_pending(NautilusSearchDirectory * self,NautilusDirectoryCallback callback,gpointer callback_data)434 search_callback_find_pending (NautilusSearchDirectory *self,
435 NautilusDirectoryCallback callback,
436 gpointer callback_data)
437 {
438 SearchCallback *search_callback;
439 GList *list;
440
441 for (list = self->pending_callback_list; list != NULL; list = list->next)
442 {
443 search_callback = list->data;
444
445 if (search_callback->callback == callback &&
446 search_callback->callback_data == callback_data)
447 {
448 return search_callback;
449 }
450 }
451
452 return NULL;
453 }
454
455 static GHashTable *
file_list_to_hash_table(GList * file_list)456 file_list_to_hash_table (GList *file_list)
457 {
458 GList *list;
459 GHashTable *table;
460
461 if (!file_list)
462 {
463 return NULL;
464 }
465
466 table = g_hash_table_new (NULL, NULL);
467
468 for (list = file_list; list != NULL; list = list->next)
469 {
470 g_hash_table_insert (table, list->data, list->data);
471 }
472
473 return table;
474 }
475
476 static void
search_call_when_ready(NautilusDirectory * directory,NautilusFileAttributes file_attributes,gboolean wait_for_file_list,NautilusDirectoryCallback callback,gpointer callback_data)477 search_call_when_ready (NautilusDirectory *directory,
478 NautilusFileAttributes file_attributes,
479 gboolean wait_for_file_list,
480 NautilusDirectoryCallback callback,
481 gpointer callback_data)
482 {
483 NautilusSearchDirectory *self;
484 SearchCallback *search_callback;
485
486 self = NAUTILUS_SEARCH_DIRECTORY (directory);
487
488 search_callback = search_callback_find (self, callback, callback_data);
489 if (search_callback == NULL)
490 {
491 search_callback = search_callback_find_pending (self, callback, callback_data);
492 }
493
494 if (search_callback)
495 {
496 g_warning ("tried to add a new callback while an old one was pending");
497 return;
498 }
499
500 search_callback = g_new0 (SearchCallback, 1);
501 search_callback->search_directory = self;
502 search_callback->callback = callback;
503 search_callback->callback_data = callback_data;
504 search_callback->wait_for_attributes = file_attributes;
505 search_callback->wait_for_file_list = wait_for_file_list;
506
507 if (wait_for_file_list && !self->search_ready_and_valid)
508 {
509 /* Add it to the pending callback list, which will be
510 * processed when the directory has valid data from the new
511 * search and all data and signals from previous searchs is removed. */
512 self->pending_callback_list =
513 g_list_prepend (self->pending_callback_list, search_callback);
514
515 /* We might need to start the search engine */
516 start_search (self);
517 }
518 else
519 {
520 search_callback->file_list = nautilus_file_list_copy (self->files);
521 search_callback->non_ready_hash = file_list_to_hash_table (self->files);
522
523 if (!search_callback->non_ready_hash)
524 {
525 /* If there are no ready files, we invoke the callback
526 * with an empty list.
527 */
528 search_callback_invoke_and_destroy (search_callback);
529 }
530 else
531 {
532 self->callback_list = g_list_prepend (self->callback_list, search_callback);
533 search_callback_add_file_callbacks (search_callback);
534 }
535 }
536 }
537
538 static void
search_cancel_callback(NautilusDirectory * directory,NautilusDirectoryCallback callback,gpointer callback_data)539 search_cancel_callback (NautilusDirectory *directory,
540 NautilusDirectoryCallback callback,
541 gpointer callback_data)
542 {
543 NautilusSearchDirectory *self;
544 SearchCallback *search_callback;
545
546 self = NAUTILUS_SEARCH_DIRECTORY (directory);
547 search_callback = search_callback_find (self, callback, callback_data);
548
549 if (search_callback)
550 {
551 self->callback_list = g_list_remove (self->callback_list, search_callback);
552
553 search_callback_destroy (search_callback);
554
555 goto done;
556 }
557
558 /* Check for a pending callback */
559 search_callback = search_callback_find_pending (self, callback, callback_data);
560
561 if (search_callback)
562 {
563 self->pending_callback_list = g_list_remove (self->pending_callback_list, search_callback);
564
565 search_callback_destroy (search_callback);
566 }
567
568 done:
569 if (!self->callback_list && !self->pending_callback_list)
570 {
571 stop_search (self);
572 }
573 }
574
575 static void
search_callback_add_pending_file_callbacks(SearchCallback * callback)576 search_callback_add_pending_file_callbacks (SearchCallback *callback)
577 {
578 callback->file_list = nautilus_file_list_copy (callback->search_directory->files);
579 callback->non_ready_hash = file_list_to_hash_table (callback->search_directory->files);
580
581 search_callback_add_file_callbacks (callback);
582 }
583
584 static void
search_directory_add_pending_files_callbacks(NautilusSearchDirectory * self)585 search_directory_add_pending_files_callbacks (NautilusSearchDirectory *self)
586 {
587 /* Add all file callbacks */
588 g_list_foreach (self->pending_callback_list,
589 (GFunc) search_callback_add_pending_file_callbacks, NULL);
590 self->callback_list = g_list_concat (self->callback_list,
591 self->pending_callback_list);
592
593 g_list_free (self->pending_callback_list);
594 self->pending_callback_list = NULL;
595 }
596
597 static void
on_search_directory_search_ready_and_valid(NautilusSearchDirectory * self)598 on_search_directory_search_ready_and_valid (NautilusSearchDirectory *self)
599 {
600 search_directory_add_pending_files_callbacks (self);
601 self->search_ready_and_valid = TRUE;
602 }
603
604 static void
search_engine_hits_added(NautilusSearchEngine * engine,GList * hits,NautilusSearchDirectory * self)605 search_engine_hits_added (NautilusSearchEngine *engine,
606 GList *hits,
607 NautilusSearchDirectory *self)
608 {
609 GList *hit_list;
610 GList *file_list;
611 NautilusFile *file;
612 SearchMonitor *monitor;
613 GList *monitor_list;
614
615 file_list = NULL;
616
617 for (hit_list = hits; hit_list != NULL; hit_list = hit_list->next)
618 {
619 NautilusSearchHit *hit = hit_list->data;
620 const char *uri;
621
622 uri = nautilus_search_hit_get_uri (hit);
623
624 nautilus_search_hit_compute_scores (hit, self->query);
625
626 file = nautilus_file_get_by_uri (uri);
627 nautilus_file_set_search_relevance (file, nautilus_search_hit_get_relevance (hit));
628 nautilus_file_set_search_fts_snippet (file, nautilus_search_hit_get_fts_snippet (hit));
629
630 for (monitor_list = self->monitor_list; monitor_list; monitor_list = monitor_list->next)
631 {
632 monitor = monitor_list->data;
633
634 /* Add monitors */
635 nautilus_file_monitor_add (file, monitor, monitor->monitor_attributes);
636 }
637
638 g_signal_connect (file, "changed", G_CALLBACK (file_changed), self),
639
640 file_list = g_list_prepend (file_list, file);
641 g_hash_table_add (self->files_hash, file);
642 }
643
644 self->files = g_list_concat (self->files, file_list);
645
646 nautilus_directory_emit_files_added (NAUTILUS_DIRECTORY (self), file_list);
647
648 file = nautilus_directory_get_corresponding_file (NAUTILUS_DIRECTORY (self));
649 nautilus_file_emit_changed (file);
650 nautilus_file_unref (file);
651
652 search_directory_add_pending_files_callbacks (self);
653 }
654
655 static void
search_engine_error(NautilusSearchEngine * engine,const char * error_message,NautilusSearchDirectory * self)656 search_engine_error (NautilusSearchEngine *engine,
657 const char *error_message,
658 NautilusSearchDirectory *self)
659 {
660 GError *error;
661
662 error = g_error_new_literal (G_IO_ERROR, G_IO_ERROR_FAILED,
663 error_message);
664 nautilus_directory_emit_load_error (NAUTILUS_DIRECTORY (self),
665 error);
666 g_error_free (error);
667 }
668
669 static void
search_engine_finished(NautilusSearchEngine * engine,NautilusSearchProviderStatus status,NautilusSearchDirectory * self)670 search_engine_finished (NautilusSearchEngine *engine,
671 NautilusSearchProviderStatus status,
672 NautilusSearchDirectory *self)
673 {
674 /* If the search engine is going to restart means it finished an old search
675 * that was stopped or cancelled.
676 * Don't emit the done loading signal in this case, since this means the search
677 * directory tried to start a new search before all the search providers were finished
678 * in the search engine.
679 * If we emit the done-loading signal in this situation the client will think
680 * that it finished the current search, not an old one like it's actually
681 * happening. */
682 if (status == NAUTILUS_SEARCH_PROVIDER_STATUS_NORMAL)
683 {
684 on_search_directory_search_ready_and_valid (self);
685 nautilus_directory_emit_done_loading (NAUTILUS_DIRECTORY (self));
686 }
687 else if (status == NAUTILUS_SEARCH_PROVIDER_STATUS_RESTARTING)
688 {
689 /* Remove file monitors of the files from an old search that just
690 * actually finished */
691 reset_file_list (self);
692 }
693 }
694
695 static void
search_force_reload(NautilusDirectory * directory)696 search_force_reload (NautilusDirectory *directory)
697 {
698 NautilusSearchDirectory *self;
699 NautilusFile *file;
700
701 self = NAUTILUS_SEARCH_DIRECTORY (directory);
702
703 if (!self->query)
704 {
705 return;
706 }
707
708 self->search_ready_and_valid = FALSE;
709
710 /* Remove file monitors */
711 reset_file_list (self);
712 stop_search (self);
713
714 file = nautilus_directory_get_corresponding_file (directory);
715 nautilus_file_invalidate_all_attributes (file);
716 nautilus_file_unref (file);
717 }
718
719 static gboolean
search_are_all_files_seen(NautilusDirectory * directory)720 search_are_all_files_seen (NautilusDirectory *directory)
721 {
722 NautilusSearchDirectory *self;
723
724 self = NAUTILUS_SEARCH_DIRECTORY (directory);
725
726 return (!self->query ||
727 self->search_ready_and_valid);
728 }
729
730 static gboolean
search_contains_file(NautilusDirectory * directory,NautilusFile * file)731 search_contains_file (NautilusDirectory *directory,
732 NautilusFile *file)
733 {
734 NautilusSearchDirectory *self;
735
736 self = NAUTILUS_SEARCH_DIRECTORY (directory);
737 return (g_hash_table_lookup (self->files_hash, file) != NULL);
738 }
739
740 static GList *
search_get_file_list(NautilusDirectory * directory)741 search_get_file_list (NautilusDirectory *directory)
742 {
743 NautilusSearchDirectory *self;
744
745 self = NAUTILUS_SEARCH_DIRECTORY (directory);
746
747 return nautilus_file_list_copy (self->files);
748 }
749
750
751 static gboolean
search_is_editable(NautilusDirectory * directory)752 search_is_editable (NautilusDirectory *directory)
753 {
754 return FALSE;
755 }
756
757 static gboolean
real_handles_location(GFile * location)758 real_handles_location (GFile *location)
759 {
760 g_autofree gchar *uri = NULL;
761
762 uri = g_file_get_uri (location);
763
764 return eel_uri_is_search (uri);
765 }
766
767 static void
search_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)768 search_set_property (GObject *object,
769 guint property_id,
770 const GValue *value,
771 GParamSpec *pspec)
772 {
773 NautilusSearchDirectory *self = NAUTILUS_SEARCH_DIRECTORY (object);
774
775 switch (property_id)
776 {
777 case PROP_BASE_MODEL:
778 {
779 nautilus_search_directory_set_base_model (self, g_value_get_object (value));
780 }
781 break;
782
783 case PROP_QUERY:
784 {
785 nautilus_search_directory_set_query (self, g_value_get_object (value));
786 }
787 break;
788
789 default:
790 {
791 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
792 }
793 break;
794 }
795 }
796
797 static void
search_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)798 search_get_property (GObject *object,
799 guint property_id,
800 GValue *value,
801 GParamSpec *pspec)
802 {
803 NautilusSearchDirectory *self = NAUTILUS_SEARCH_DIRECTORY (object);
804
805 switch (property_id)
806 {
807 case PROP_BASE_MODEL:
808 {
809 g_value_set_object (value, nautilus_search_directory_get_base_model (self));
810 }
811 break;
812
813 case PROP_QUERY:
814 {
815 g_value_take_object (value, nautilus_search_directory_get_query (self));
816 }
817 break;
818
819 default:
820 {
821 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
822 }
823 break;
824 }
825 }
826
827 static void
clear_base_model(NautilusSearchDirectory * self)828 clear_base_model (NautilusSearchDirectory *self)
829 {
830 if (self->base_model != NULL)
831 {
832 nautilus_directory_file_monitor_remove (self->base_model,
833 &self->base_model);
834 g_clear_object (&self->base_model);
835 }
836 }
837
838 static void
search_connect_engine(NautilusSearchDirectory * self)839 search_connect_engine (NautilusSearchDirectory *self)
840 {
841 g_signal_connect (self->engine, "hits-added",
842 G_CALLBACK (search_engine_hits_added),
843 self);
844 g_signal_connect (self->engine, "error",
845 G_CALLBACK (search_engine_error),
846 self);
847 g_signal_connect (self->engine, "finished",
848 G_CALLBACK (search_engine_finished),
849 self);
850 }
851
852 static void
search_disconnect_engine(NautilusSearchDirectory * self)853 search_disconnect_engine (NautilusSearchDirectory *self)
854 {
855 g_signal_handlers_disconnect_by_func (self->engine,
856 search_engine_hits_added,
857 self);
858 g_signal_handlers_disconnect_by_func (self->engine,
859 search_engine_error,
860 self);
861 g_signal_handlers_disconnect_by_func (self->engine,
862 search_engine_finished,
863 self);
864 }
865
866 static void
search_dispose(GObject * object)867 search_dispose (GObject *object)
868 {
869 NautilusSearchDirectory *self;
870 GList *list;
871
872 self = NAUTILUS_SEARCH_DIRECTORY (object);
873
874 clear_base_model (self);
875
876 /* Remove search monitors */
877 if (self->monitor_list)
878 {
879 for (list = self->monitor_list; list != NULL; list = list->next)
880 {
881 search_monitor_destroy ((SearchMonitor *) list->data, self);
882 }
883
884 g_list_free (self->monitor_list);
885 self->monitor_list = NULL;
886 }
887
888 reset_file_list (self);
889
890 if (self->callback_list)
891 {
892 /* Remove callbacks */
893 g_list_foreach (self->callback_list,
894 (GFunc) search_callback_destroy, NULL);
895 g_list_free (self->callback_list);
896 self->callback_list = NULL;
897 }
898
899 if (self->pending_callback_list)
900 {
901 g_list_foreach (self->pending_callback_list,
902 (GFunc) search_callback_destroy, NULL);
903 g_list_free (self->pending_callback_list);
904 self->pending_callback_list = NULL;
905 }
906
907 g_clear_object (&self->query);
908 stop_search (self);
909 search_disconnect_engine (self);
910
911 g_clear_object (&self->engine);
912
913 G_OBJECT_CLASS (nautilus_search_directory_parent_class)->dispose (object);
914 }
915
916 static void
search_finalize(GObject * object)917 search_finalize (GObject *object)
918 {
919 NautilusSearchDirectory *self;
920
921 self = NAUTILUS_SEARCH_DIRECTORY (object);
922
923 g_hash_table_destroy (self->files_hash);
924
925 G_OBJECT_CLASS (nautilus_search_directory_parent_class)->finalize (object);
926 }
927
928 static void
nautilus_search_directory_init(NautilusSearchDirectory * self)929 nautilus_search_directory_init (NautilusSearchDirectory *self)
930 {
931 self->query = NULL;
932 self->files_hash = g_hash_table_new (g_direct_hash, g_direct_equal);
933
934 self->engine = nautilus_search_engine_new ();
935 search_connect_engine (self);
936 }
937
938 static void
nautilus_search_directory_class_init(NautilusSearchDirectoryClass * class)939 nautilus_search_directory_class_init (NautilusSearchDirectoryClass *class)
940 {
941 NautilusDirectoryClass *directory_class = NAUTILUS_DIRECTORY_CLASS (class);
942 GObjectClass *oclass = G_OBJECT_CLASS (class);
943
944 oclass->dispose = search_dispose;
945 oclass->finalize = search_finalize;
946 oclass->get_property = search_get_property;
947 oclass->set_property = search_set_property;
948
949 directory_class->are_all_files_seen = search_are_all_files_seen;
950 directory_class->contains_file = search_contains_file;
951 directory_class->force_reload = search_force_reload;
952 directory_class->call_when_ready = search_call_when_ready;
953 directory_class->cancel_callback = search_cancel_callback;
954
955 directory_class->file_monitor_add = search_monitor_add;
956 directory_class->file_monitor_remove = search_monitor_remove;
957
958 directory_class->get_file_list = search_get_file_list;
959 directory_class->is_editable = search_is_editable;
960 directory_class->handles_location = real_handles_location;
961
962 properties[PROP_BASE_MODEL] =
963 g_param_spec_object ("base-model",
964 "The base model",
965 "The base directory model for this directory",
966 NAUTILUS_TYPE_DIRECTORY,
967 G_PARAM_READWRITE);
968 properties[PROP_QUERY] =
969 g_param_spec_object ("query",
970 "The query",
971 "The query for this search directory",
972 NAUTILUS_TYPE_QUERY,
973 G_PARAM_READWRITE);
974
975 g_object_class_install_properties (oclass, NUM_PROPERTIES, properties);
976 }
977
978 void
nautilus_search_directory_set_base_model(NautilusSearchDirectory * self,NautilusDirectory * base_model)979 nautilus_search_directory_set_base_model (NautilusSearchDirectory *self,
980 NautilusDirectory *base_model)
981 {
982 if (self->base_model == base_model)
983 {
984 return;
985 }
986
987 if (self->query != NULL)
988 {
989 GFile *query_location, *model_location;
990 gboolean is_equal;
991
992 query_location = nautilus_query_get_location (self->query);
993 model_location = nautilus_directory_get_location (base_model);
994
995 is_equal = g_file_equal (model_location, query_location);
996
997 g_object_unref (model_location);
998 g_object_unref (query_location);
999
1000 if (!is_equal)
1001 {
1002 return;
1003 }
1004 }
1005
1006 clear_base_model (self);
1007 self->base_model = nautilus_directory_ref (base_model);
1008
1009 if (self->base_model != NULL)
1010 {
1011 nautilus_directory_file_monitor_add (base_model, &self->base_model,
1012 TRUE, NAUTILUS_FILE_ATTRIBUTE_INFO,
1013 NULL, NULL);
1014 }
1015
1016 g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_BASE_MODEL]);
1017 }
1018
1019 NautilusDirectory *
nautilus_search_directory_get_base_model(NautilusSearchDirectory * self)1020 nautilus_search_directory_get_base_model (NautilusSearchDirectory *self)
1021 {
1022 return self->base_model;
1023 }
1024
1025 char *
nautilus_search_directory_generate_new_uri(void)1026 nautilus_search_directory_generate_new_uri (void)
1027 {
1028 static int counter = 0;
1029 char *uri;
1030
1031 uri = g_strdup_printf (EEL_SEARCH_URI "//%d/", counter++);
1032
1033 return uri;
1034 }
1035
1036 void
nautilus_search_directory_set_query(NautilusSearchDirectory * self,NautilusQuery * query)1037 nautilus_search_directory_set_query (NautilusSearchDirectory *self,
1038 NautilusQuery *query)
1039 {
1040 NautilusFile *file;
1041 NautilusQuery *old_query;
1042
1043 old_query = self->query;
1044
1045 if (self->query != query)
1046 {
1047 self->query = g_object_ref (query);
1048
1049 g_clear_pointer (&self->binding, g_binding_unbind);
1050
1051 if (query)
1052 {
1053 self->binding = g_object_bind_property (self->engine, "running",
1054 query, "searching",
1055 G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE);
1056 }
1057
1058 g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_QUERY]);
1059
1060 g_clear_object (&old_query);
1061 }
1062
1063 file = nautilus_directory_get_existing_corresponding_file (NAUTILUS_DIRECTORY (self));
1064 if (file != NULL)
1065 {
1066 nautilus_search_directory_file_update_display_name (NAUTILUS_SEARCH_DIRECTORY_FILE (file));
1067 }
1068 nautilus_file_unref (file);
1069 }
1070
1071 NautilusQuery *
nautilus_search_directory_get_query(NautilusSearchDirectory * self)1072 nautilus_search_directory_get_query (NautilusSearchDirectory *self)
1073 {
1074 if (self->query != NULL)
1075 {
1076 return g_object_ref (self->query);
1077 }
1078
1079 return NULL;
1080 }
1081