1 /*
2 * nautilus-directory.c: Nautilus directory model.
3 *
4 * Copyright (C) 1999, 2000, 2001 Eazel, Inc.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; either version 2 of the
9 * License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public
17 * License along with this program; if not, see <http://www.gnu.org/licenses/>.
18 *
19 * Author: Darin Adler <darin@bentspoon.com>
20 */
21
22 #include "nautilus-directory-private.h"
23
24 #include <eel/eel-glib-extensions.h>
25 #include <eel/eel-string.h>
26 #include <glib/gi18n.h>
27 #include <gtk/gtk.h>
28
29 #include "nautilus-directory-notify.h"
30 #include "nautilus-directory-private.h"
31 #include "nautilus-enums.h"
32 #include "nautilus-file-private.h"
33 #include "nautilus-file-queue.h"
34 #include "nautilus-file-utilities.h"
35 #include "nautilus-global-preferences.h"
36 #include "nautilus-lib-self-check-functions.h"
37 #include "nautilus-metadata.h"
38 #include "nautilus-profile.h"
39 #include "nautilus-search-directory-file.h"
40 #include "nautilus-search-directory.h"
41 #include "nautilus-starred-directory.h"
42 #include "nautilus-vfs-directory.h"
43 #include "nautilus-vfs-file.h"
44
45 enum
46 {
47 FILES_ADDED,
48 FILES_CHANGED,
49 DONE_LOADING,
50 LOAD_ERROR,
51 LAST_SIGNAL
52 };
53
54 enum
55 {
56 PROP_LOCATION = 1,
57 NUM_PROPERTIES
58 };
59
60 static guint signals[LAST_SIGNAL];
61 static GParamSpec *properties[NUM_PROPERTIES] = { NULL, };
62
63 static GHashTable *directories;
64
65 static NautilusDirectory *nautilus_directory_new (GFile *location);
66 static void set_directory_location (NautilusDirectory *directory,
67 GFile *location);
68
69 G_DEFINE_TYPE (NautilusDirectory, nautilus_directory, G_TYPE_OBJECT);
70
71 static gboolean
real_contains_file(NautilusDirectory * self,NautilusFile * file)72 real_contains_file (NautilusDirectory *self,
73 NautilusFile *file)
74 {
75 NautilusDirectory *directory;
76
77 directory = nautilus_file_get_directory (file);
78
79 return directory == self;
80 }
81
82 static gboolean
real_are_all_files_seen(NautilusDirectory * directory)83 real_are_all_files_seen (NautilusDirectory *directory)
84 {
85 return directory->details->directory_loaded;
86 }
87
88 static gboolean
real_is_not_empty(NautilusDirectory * directory)89 real_is_not_empty (NautilusDirectory *directory)
90 {
91 return directory->details->file_list != NULL;
92 }
93
94 static gboolean
is_tentative(NautilusFile * file,gpointer callback_data)95 is_tentative (NautilusFile *file,
96 gpointer callback_data)
97 {
98 g_assert (callback_data == NULL);
99
100 /* Avoid returning files with !is_added, because these
101 * will later be sent with the files_added signal, and a
102 * user doing get_file_list + files_added monitoring will
103 * then see the file twice */
104 return !file->details->got_file_info || !file->details->is_added;
105 }
106
107 static GList *
real_get_file_list(NautilusDirectory * directory)108 real_get_file_list (NautilusDirectory *directory)
109 {
110 GList *tentative_files, *non_tentative_files;
111
112 tentative_files = nautilus_file_list_filter (directory->details->file_list,
113 &non_tentative_files, is_tentative, NULL);
114 nautilus_file_list_free (tentative_files);
115
116 return non_tentative_files;
117 }
118
119 static gboolean
real_is_editable(NautilusDirectory * directory)120 real_is_editable (NautilusDirectory *directory)
121 {
122 return TRUE;
123 }
124
125 static NautilusFile *
real_new_file_from_filename(NautilusDirectory * directory,const char * filename,gboolean self_owned)126 real_new_file_from_filename (NautilusDirectory *directory,
127 const char *filename,
128 gboolean self_owned)
129 {
130 NautilusFile *file;
131
132 g_assert (NAUTILUS_IS_DIRECTORY (directory));
133 g_assert (filename != NULL);
134 g_assert (filename[0] != '\0');
135
136 if (NAUTILUS_IS_SEARCH_DIRECTORY (directory))
137 {
138 if (self_owned)
139 {
140 file = NAUTILUS_FILE (g_object_new (NAUTILUS_TYPE_SEARCH_DIRECTORY_FILE, NULL));
141 }
142 else
143 {
144 /* This doesn't normally happen, unless the user somehow types in a uri
145 * that references a file like this. (See #349840) */
146 file = NAUTILUS_FILE (g_object_new (NAUTILUS_TYPE_VFS_FILE, NULL));
147 }
148 }
149 else
150 {
151 file = NAUTILUS_FILE (g_object_new (NAUTILUS_TYPE_VFS_FILE, NULL));
152 }
153 nautilus_file_set_directory (file, directory);
154
155 return file;
156 }
157
158 static gboolean
real_handles_location(GFile * location)159 real_handles_location (GFile *location)
160 {
161 /* This class is the fallback on handling any location */
162 return TRUE;
163 }
164
165 static void
nautilus_directory_finalize(GObject * object)166 nautilus_directory_finalize (GObject *object)
167 {
168 NautilusDirectory *directory;
169
170 directory = NAUTILUS_DIRECTORY (object);
171
172 g_hash_table_remove (directories, directory->details->location);
173
174 nautilus_directory_cancel (directory);
175 g_assert (directory->details->count_in_progress == NULL);
176
177 if (g_hash_table_size (directory->details->monitor_table) != 0)
178 {
179 GHashTableIter iter;
180 gpointer value;
181
182 g_warning ("destroying a NautilusDirectory while it's being monitored");
183
184 g_hash_table_iter_init (&iter, directory->details->monitor_table);
185 while (g_hash_table_iter_next (&iter, NULL, &value))
186 {
187 GList *list = value;
188 g_list_free_full (list, g_free);
189 }
190 g_hash_table_remove_all (directory->details->monitor_table);
191 }
192 g_hash_table_destroy (directory->details->monitor_table);
193
194 if (directory->details->monitor != NULL)
195 {
196 nautilus_monitor_cancel (directory->details->monitor);
197 }
198
199 if (directory->details->dequeue_pending_idle_id != 0)
200 {
201 g_source_remove (directory->details->dequeue_pending_idle_id);
202 }
203
204 if (directory->details->call_ready_idle_id != 0)
205 {
206 g_source_remove (directory->details->call_ready_idle_id);
207 }
208
209 if (directory->details->location)
210 {
211 g_object_unref (directory->details->location);
212 }
213
214 g_assert (directory->details->file_list == NULL);
215 g_hash_table_destroy (directory->details->file_hash);
216
217 nautilus_file_queue_destroy (directory->details->high_priority_queue);
218 nautilus_file_queue_destroy (directory->details->low_priority_queue);
219 nautilus_file_queue_destroy (directory->details->extension_queue);
220 g_assert (directory->details->directory_load_in_progress == NULL);
221 g_assert (directory->details->count_in_progress == NULL);
222 g_assert (directory->details->dequeue_pending_idle_id == 0);
223 g_list_free_full (directory->details->pending_file_info, g_object_unref);
224
225 G_OBJECT_CLASS (nautilus_directory_parent_class)->finalize (object);
226 }
227
228 static void
nautilus_directory_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)229 nautilus_directory_set_property (GObject *object,
230 guint property_id,
231 const GValue *value,
232 GParamSpec *pspec)
233 {
234 NautilusDirectory *directory = NAUTILUS_DIRECTORY (object);
235
236 switch (property_id)
237 {
238 case PROP_LOCATION:
239 {
240 set_directory_location (directory, g_value_get_object (value));
241 }
242 break;
243
244 default:
245 {
246 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
247 }
248 break;
249 }
250 }
251
252 static void
nautilus_directory_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)253 nautilus_directory_get_property (GObject *object,
254 guint property_id,
255 GValue *value,
256 GParamSpec *pspec)
257 {
258 NautilusDirectory *directory = NAUTILUS_DIRECTORY (object);
259
260 switch (property_id)
261 {
262 case PROP_LOCATION:
263 {
264 g_value_set_object (value, directory->details->location);
265 }
266 break;
267
268 default:
269 {
270 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
271 }
272 break;
273 }
274 }
275
276 static void
nautilus_directory_class_init(NautilusDirectoryClass * klass)277 nautilus_directory_class_init (NautilusDirectoryClass *klass)
278 {
279 GObjectClass *object_class;
280
281 object_class = G_OBJECT_CLASS (klass);
282
283 klass->contains_file = real_contains_file;
284 klass->are_all_files_seen = real_are_all_files_seen;
285 klass->is_not_empty = real_is_not_empty;
286 klass->get_file_list = real_get_file_list;
287 klass->is_editable = real_is_editable;
288 klass->new_file_from_filename = real_new_file_from_filename;
289 klass->handles_location = real_handles_location;
290
291 object_class->finalize = nautilus_directory_finalize;
292 object_class->set_property = nautilus_directory_set_property;
293 object_class->get_property = nautilus_directory_get_property;
294
295 signals[FILES_ADDED] =
296 g_signal_new ("files-added",
297 G_TYPE_FROM_CLASS (object_class),
298 G_SIGNAL_RUN_LAST,
299 G_STRUCT_OFFSET (NautilusDirectoryClass, files_added),
300 NULL, NULL,
301 g_cclosure_marshal_VOID__POINTER,
302 G_TYPE_NONE, 1, G_TYPE_POINTER);
303 signals[FILES_CHANGED] =
304 g_signal_new ("files-changed",
305 G_TYPE_FROM_CLASS (object_class),
306 G_SIGNAL_RUN_LAST,
307 G_STRUCT_OFFSET (NautilusDirectoryClass, files_changed),
308 NULL, NULL,
309 g_cclosure_marshal_VOID__POINTER,
310 G_TYPE_NONE, 1, G_TYPE_POINTER);
311 signals[DONE_LOADING] =
312 g_signal_new ("done-loading",
313 G_TYPE_FROM_CLASS (object_class),
314 G_SIGNAL_RUN_LAST,
315 G_STRUCT_OFFSET (NautilusDirectoryClass, done_loading),
316 NULL, NULL,
317 g_cclosure_marshal_VOID__VOID,
318 G_TYPE_NONE, 0);
319 signals[LOAD_ERROR] =
320 g_signal_new ("load-error",
321 G_TYPE_FROM_CLASS (object_class),
322 G_SIGNAL_RUN_LAST,
323 G_STRUCT_OFFSET (NautilusDirectoryClass, load_error),
324 NULL, NULL,
325 g_cclosure_marshal_VOID__POINTER,
326 G_TYPE_NONE, 1, G_TYPE_POINTER);
327
328 properties[PROP_LOCATION] =
329 g_param_spec_object ("location",
330 "The location",
331 "The location of this directory",
332 G_TYPE_FILE,
333 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
334
335 g_type_class_add_private (klass, sizeof (NautilusDirectoryDetails));
336 g_object_class_install_properties (object_class, NUM_PROPERTIES, properties);
337 }
338
339 static void
nautilus_directory_init(NautilusDirectory * directory)340 nautilus_directory_init (NautilusDirectory *directory)
341 {
342 directory->details = G_TYPE_INSTANCE_GET_PRIVATE ((directory), NAUTILUS_TYPE_DIRECTORY, NautilusDirectoryDetails);
343 directory->details->file_hash = g_hash_table_new_full (g_str_hash, g_str_equal,
344 g_free, NULL);
345 directory->details->high_priority_queue = nautilus_file_queue_new ();
346 directory->details->low_priority_queue = nautilus_file_queue_new ();
347 directory->details->extension_queue = nautilus_file_queue_new ();
348 directory->details->monitor_table = g_hash_table_new (NULL, NULL);
349 }
350
351 NautilusDirectory *
nautilus_directory_ref(NautilusDirectory * directory)352 nautilus_directory_ref (NautilusDirectory *directory)
353 {
354 if (directory == NULL)
355 {
356 return directory;
357 }
358
359 g_return_val_if_fail (NAUTILUS_IS_DIRECTORY (directory), NULL);
360
361 g_object_ref (directory);
362 return directory;
363 }
364
365 void
nautilus_directory_unref(NautilusDirectory * directory)366 nautilus_directory_unref (NautilusDirectory *directory)
367 {
368 if (directory == NULL)
369 {
370 return;
371 }
372
373 g_return_if_fail (NAUTILUS_IS_DIRECTORY (directory));
374
375 g_object_unref (directory);
376 }
377
378 static void
collect_all_directories(gpointer key,gpointer value,gpointer callback_data)379 collect_all_directories (gpointer key,
380 gpointer value,
381 gpointer callback_data)
382 {
383 NautilusDirectory *directory;
384 GList **dirs;
385
386 directory = NAUTILUS_DIRECTORY (value);
387 dirs = callback_data;
388
389 *dirs = g_list_prepend (*dirs, nautilus_directory_ref (directory));
390 }
391
392 static void
filtering_changed_callback(gpointer callback_data)393 filtering_changed_callback (gpointer callback_data)
394 {
395 g_autolist (NautilusDirectory) dirs = NULL;
396
397 g_assert (callback_data == NULL);
398
399 dirs = NULL;
400 g_hash_table_foreach (directories, collect_all_directories, &dirs);
401
402 /* Preference about which items to show has changed, so we
403 * can't trust any of our precomputed directory counts.
404 */
405 for (GList *l = dirs; l != NULL; l = l->next)
406 {
407 NautilusDirectory *directory;
408
409 directory = NAUTILUS_DIRECTORY (l->data);
410
411 nautilus_directory_invalidate_count_and_mime_list (directory);
412 }
413 }
414
415 void
emit_change_signals_for_all_files(NautilusDirectory * directory)416 emit_change_signals_for_all_files (NautilusDirectory *directory)
417 {
418 GList *files;
419
420 files = g_list_copy (directory->details->file_list);
421 if (directory->details->as_file != NULL)
422 {
423 files = g_list_prepend (files, directory->details->as_file);
424 }
425
426 nautilus_file_list_ref (files);
427 nautilus_directory_emit_change_signals (directory, files);
428
429 nautilus_file_list_free (files);
430 }
431
432 void
emit_change_signals_for_all_files_in_all_directories(void)433 emit_change_signals_for_all_files_in_all_directories (void)
434 {
435 GList *dirs, *l;
436 NautilusDirectory *directory;
437
438 dirs = NULL;
439 g_hash_table_foreach (directories,
440 collect_all_directories,
441 &dirs);
442
443 for (l = dirs; l != NULL; l = l->next)
444 {
445 directory = NAUTILUS_DIRECTORY (l->data);
446 emit_change_signals_for_all_files (directory);
447 nautilus_directory_unref (directory);
448 }
449
450 g_list_free (dirs);
451 }
452
453 static void
async_state_changed_one(gpointer key,gpointer value,gpointer user_data)454 async_state_changed_one (gpointer key,
455 gpointer value,
456 gpointer user_data)
457 {
458 NautilusDirectory *directory;
459
460 g_assert (key != NULL);
461 g_assert (NAUTILUS_IS_DIRECTORY (value));
462 g_assert (user_data == NULL);
463
464 directory = NAUTILUS_DIRECTORY (value);
465
466 nautilus_directory_async_state_changed (directory);
467 emit_change_signals_for_all_files (directory);
468 }
469
470 static void
async_data_preference_changed_callback(gpointer callback_data)471 async_data_preference_changed_callback (gpointer callback_data)
472 {
473 g_assert (callback_data == NULL);
474
475 /* Preference involving fetched async data has changed, so
476 * we have to kick off refetching all async data, and tell
477 * each file that it (might have) changed.
478 */
479 g_hash_table_foreach (directories, async_state_changed_one, NULL);
480 }
481
482 static void
add_preferences_callbacks(void)483 add_preferences_callbacks (void)
484 {
485 nautilus_global_preferences_init ();
486
487 g_signal_connect_swapped (gtk_filechooser_preferences,
488 "changed::" NAUTILUS_PREFERENCES_SHOW_HIDDEN_FILES,
489 G_CALLBACK (filtering_changed_callback),
490 NULL);
491 g_signal_connect_swapped (nautilus_preferences,
492 "changed::" NAUTILUS_PREFERENCES_SHOW_DIRECTORY_ITEM_COUNTS,
493 G_CALLBACK (async_data_preference_changed_callback),
494 NULL);
495 }
496
497 /**
498 * nautilus_directory_get_by_uri:
499 * @uri: URI of directory to get.
500 *
501 * Get a directory given a uri.
502 * Creates the appropriate subclass given the uri mappings.
503 * Returns a referenced object, not a floating one. Unref when finished.
504 * If two windows are viewing the same uri, the directory object is shared.
505 */
506 NautilusDirectory *
nautilus_directory_get_internal(GFile * location,gboolean create)507 nautilus_directory_get_internal (GFile *location,
508 gboolean create)
509 {
510 NautilusDirectory *directory;
511
512 /* Create the hash table first time through. */
513 if (directories == NULL)
514 {
515 directories = g_hash_table_new (g_file_hash, (GCompareFunc) g_file_equal);
516 add_preferences_callbacks ();
517 }
518
519 /* If the object is already in the hash table, look it up. */
520
521 directory = g_hash_table_lookup (directories,
522 location);
523 if (directory != NULL)
524 {
525 nautilus_directory_ref (directory);
526 }
527 else if (create)
528 {
529 /* Create a new directory object instead. */
530 directory = nautilus_directory_new (location);
531 if (directory == NULL)
532 {
533 return NULL;
534 }
535
536 /* Put it in the hash table. */
537 g_hash_table_insert (directories,
538 directory->details->location,
539 directory);
540 }
541
542 return directory;
543 }
544
545 NautilusDirectory *
nautilus_directory_get(GFile * location)546 nautilus_directory_get (GFile *location)
547 {
548 if (location == NULL)
549 {
550 return NULL;
551 }
552
553 return nautilus_directory_get_internal (location, TRUE);
554 }
555
556 NautilusDirectory *
nautilus_directory_get_existing(GFile * location)557 nautilus_directory_get_existing (GFile *location)
558 {
559 if (location == NULL)
560 {
561 return NULL;
562 }
563
564 return nautilus_directory_get_internal (location, FALSE);
565 }
566
567
568 NautilusDirectory *
nautilus_directory_get_by_uri(const char * uri)569 nautilus_directory_get_by_uri (const char *uri)
570 {
571 NautilusDirectory *directory;
572 GFile *location;
573
574 if (uri == NULL)
575 {
576 return NULL;
577 }
578
579 location = g_file_new_for_uri (uri);
580
581 directory = nautilus_directory_get_internal (location, TRUE);
582 g_object_unref (location);
583 return directory;
584 }
585
586 NautilusDirectory *
nautilus_directory_get_for_file(NautilusFile * file)587 nautilus_directory_get_for_file (NautilusFile *file)
588 {
589 char *uri;
590 NautilusDirectory *directory;
591
592 g_return_val_if_fail (NAUTILUS_IS_FILE (file), NULL);
593
594 uri = nautilus_file_get_uri (file);
595 directory = nautilus_directory_get_by_uri (uri);
596 g_free (uri);
597 return directory;
598 }
599
600 /* Returns a reffed NautilusFile object for this directory.
601 */
602 NautilusFile *
nautilus_directory_get_corresponding_file(NautilusDirectory * directory)603 nautilus_directory_get_corresponding_file (NautilusDirectory *directory)
604 {
605 NautilusFile *file;
606 char *uri;
607
608 file = nautilus_directory_get_existing_corresponding_file (directory);
609 if (file == NULL)
610 {
611 uri = nautilus_directory_get_uri (directory);
612 file = nautilus_file_get_by_uri (uri);
613 g_free (uri);
614 }
615
616 return file;
617 }
618
619 /* Returns a reffed NautilusFile object for this directory, but only if the
620 * NautilusFile object has already been created.
621 */
622 NautilusFile *
nautilus_directory_get_existing_corresponding_file(NautilusDirectory * directory)623 nautilus_directory_get_existing_corresponding_file (NautilusDirectory *directory)
624 {
625 NautilusFile *file;
626 char *uri;
627
628 file = directory->details->as_file;
629 if (file != NULL)
630 {
631 nautilus_file_ref (file);
632 return file;
633 }
634
635 uri = nautilus_directory_get_uri (directory);
636 file = nautilus_file_get_existing_by_uri (uri);
637 g_free (uri);
638 return file;
639 }
640
641 /* nautilus_directory_get_name_for_self_as_new_file:
642 *
643 * Get a name to display for the file representing this
644 * directory. This is called only when there's no VFS
645 * directory for this NautilusDirectory.
646 */
647 char *
nautilus_directory_get_name_for_self_as_new_file(NautilusDirectory * directory)648 nautilus_directory_get_name_for_self_as_new_file (NautilusDirectory *directory)
649 {
650 GFile *file;
651 char *directory_uri;
652 char *scheme;
653 char *name;
654 char *hostname = NULL;
655
656 directory_uri = nautilus_directory_get_uri (directory);
657 file = g_file_new_for_uri (directory_uri);
658 scheme = g_file_get_uri_scheme (file);
659 g_object_unref (file);
660
661 nautilus_uri_parse (directory_uri, &hostname, NULL, NULL);
662 if (hostname == NULL || (strlen (hostname) == 0))
663 {
664 name = g_strdup (directory_uri);
665 }
666 else if (scheme == NULL)
667 {
668 name = g_strdup (hostname);
669 }
670 else
671 {
672 /* Translators: this is of the format "hostname (uri-scheme)" */
673 name = g_strdup_printf (_("%s (%s)"), hostname, scheme);
674 }
675
676 g_free (directory_uri);
677 g_free (scheme);
678 g_free (hostname);
679
680 return name;
681 }
682
683 char *
nautilus_directory_get_uri(NautilusDirectory * directory)684 nautilus_directory_get_uri (NautilusDirectory *directory)
685 {
686 g_return_val_if_fail (NAUTILUS_IS_DIRECTORY (directory), NULL);
687
688 return g_file_get_uri (directory->details->location);
689 }
690
691 GFile *
nautilus_directory_get_location(NautilusDirectory * directory)692 nautilus_directory_get_location (NautilusDirectory *directory)
693 {
694 g_return_val_if_fail (NAUTILUS_IS_DIRECTORY (directory), NULL);
695
696 return g_object_ref (directory->details->location);
697 }
698
699 NautilusFile *
nautilus_directory_new_file_from_filename(NautilusDirectory * directory,const char * filename,gboolean self_owned)700 nautilus_directory_new_file_from_filename (NautilusDirectory *directory,
701 const char *filename,
702 gboolean self_owned)
703 {
704 return NAUTILUS_DIRECTORY_CLASS (G_OBJECT_GET_CLASS (directory))->new_file_from_filename (directory,
705 filename,
706 self_owned);
707 }
708
709 static GList *
nautilus_directory_provider_get_all(void)710 nautilus_directory_provider_get_all (void)
711 {
712 GIOExtensionPoint *extension_point;
713 GList *extensions;
714
715 extension_point = g_io_extension_point_lookup (NAUTILUS_DIRECTORY_PROVIDER_EXTENSION_POINT_NAME);
716 if (extension_point == NULL)
717 {
718 g_warning ("Directory provider extension point not registered. Did you call nautilus_ensure_extension_points()?");
719 }
720 extensions = g_io_extension_point_get_extensions (extension_point);
721
722 return extensions;
723 }
724
725 static NautilusDirectory *
nautilus_directory_new(GFile * location)726 nautilus_directory_new (GFile *location)
727 {
728 GList *extensions;
729 GList *l;
730 GIOExtension *gio_extension;
731 GType handling_provider_type;
732 gboolean handled = FALSE;
733 NautilusDirectoryClass *current_provider_class;
734 NautilusDirectory *handling_instance;
735
736 extensions = nautilus_directory_provider_get_all ();
737
738 for (l = extensions; l != NULL; l = l->next)
739 {
740 gio_extension = l->data;
741 current_provider_class = NAUTILUS_DIRECTORY_CLASS (g_io_extension_ref_class (gio_extension));
742 if (current_provider_class->handles_location (location))
743 {
744 handling_provider_type = g_io_extension_get_type (gio_extension);
745 handled = TRUE;
746 break;
747 }
748 }
749
750 if (!handled)
751 {
752 /* This class is the fallback for any location */
753 handling_provider_type = NAUTILUS_TYPE_VFS_DIRECTORY;
754 }
755
756 handling_instance = g_object_new (handling_provider_type,
757 "location", location,
758 NULL);
759
760
761 return handling_instance;
762 }
763
764 /**
765 * nautilus_directory_is_local_or_fuse:
766 *
767 * @directory: a #NautilusDirectory
768 *
769 * Checks whether this directory contains files with local paths. Usually, this
770 * means the local path can be obtained by calling g_file_get_path(). As an
771 * exception, the local URI for files in recent:// can only be obtained from the
772 * G_FILE_ATTRIBUTE_STANDARD_TARGET_URI attribute.
773 *
774 * Returns: %TRUE if a local path is known to be obtainable for all files in
775 * this directory. Otherwise, %FALSE.
776 */
777 gboolean
nautilus_directory_is_local_or_fuse(NautilusDirectory * directory)778 nautilus_directory_is_local_or_fuse (NautilusDirectory *directory)
779 {
780 g_return_val_if_fail (NAUTILUS_IS_DIRECTORY (directory), FALSE);
781 g_return_val_if_fail (directory->details->location, FALSE);
782
783
784 if (nautilus_directory_is_in_recent (directory)
785 || g_file_is_native (directory->details->location))
786 {
787 /* Native files have a local path by definition. The files in recent:/
788 * have a local URI stored in the standard::target-uri attribute. */
789 return TRUE;
790 }
791 else
792 {
793 g_autofree char *path = NULL;
794
795 /* Non-native files may have local paths in FUSE mounts. The only way to
796 * know if that's the case is to test if GIO reports a path.
797 */
798 path = g_file_get_path (directory->details->location);
799
800 return (path != NULL);
801 }
802 }
803
804 gboolean
nautilus_directory_is_in_trash(NautilusDirectory * directory)805 nautilus_directory_is_in_trash (NautilusDirectory *directory)
806 {
807 g_assert (NAUTILUS_IS_DIRECTORY (directory));
808
809 if (directory->details->location == NULL)
810 {
811 return FALSE;
812 }
813
814 return g_file_has_uri_scheme (directory->details->location, "trash");
815 }
816
817 gboolean
nautilus_directory_is_in_recent(NautilusDirectory * directory)818 nautilus_directory_is_in_recent (NautilusDirectory *directory)
819 {
820 g_assert (NAUTILUS_IS_DIRECTORY (directory));
821
822 if (directory->details->location == NULL)
823 {
824 return FALSE;
825 }
826
827 return g_file_has_uri_scheme (directory->details->location, "recent");
828 }
829
830 gboolean
nautilus_directory_is_in_starred(NautilusDirectory * directory)831 nautilus_directory_is_in_starred (NautilusDirectory *directory)
832 {
833 g_assert (NAUTILUS_IS_DIRECTORY (directory));
834
835 if (directory->details->location == NULL)
836 {
837 return FALSE;
838 }
839
840 return g_file_has_uri_scheme (directory->details->location, "starred");
841 }
842
843 gboolean
nautilus_directory_is_in_admin(NautilusDirectory * directory)844 nautilus_directory_is_in_admin (NautilusDirectory *directory)
845 {
846 g_assert (NAUTILUS_IS_DIRECTORY (directory));
847
848 if (directory->details->location == NULL)
849 {
850 return FALSE;
851 }
852
853 return g_file_has_uri_scheme (directory->details->location, "admin");
854 }
855
856 gboolean
nautilus_directory_are_all_files_seen(NautilusDirectory * directory)857 nautilus_directory_are_all_files_seen (NautilusDirectory *directory)
858 {
859 g_return_val_if_fail (NAUTILUS_IS_DIRECTORY (directory), FALSE);
860
861 return NAUTILUS_DIRECTORY_CLASS (G_OBJECT_GET_CLASS (directory))->are_all_files_seen (directory);
862 }
863
864 static void
add_to_hash_table(NautilusDirectory * directory,NautilusFile * file,GList * node)865 add_to_hash_table (NautilusDirectory *directory,
866 NautilusFile *file,
867 GList *node)
868 {
869 gchar *name;
870
871 name = nautilus_file_get_name (file);
872
873 g_assert (name != NULL);
874 g_assert (node != NULL);
875 g_assert (g_hash_table_lookup (directory->details->file_hash,
876 name) == NULL);
877 g_hash_table_insert (directory->details->file_hash, name, node);
878 }
879
880 static GList *
extract_from_hash_table(NautilusDirectory * directory,NautilusFile * file)881 extract_from_hash_table (NautilusDirectory *directory,
882 NautilusFile *file)
883 {
884 g_autofree gchar *name = NULL;
885 GList *node;
886
887 name = nautilus_file_get_name (file);
888 if (name == NULL)
889 {
890 return NULL;
891 }
892
893 /* Find the list node in the hash table. */
894 node = g_hash_table_lookup (directory->details->file_hash, name);
895 g_hash_table_remove (directory->details->file_hash, name);
896
897 return node;
898 }
899
900 void
nautilus_directory_add_file(NautilusDirectory * directory,NautilusFile * file)901 nautilus_directory_add_file (NautilusDirectory *directory,
902 NautilusFile *file)
903 {
904 GList *node;
905 gboolean add_to_work_queue;
906
907 g_assert (NAUTILUS_IS_DIRECTORY (directory));
908 g_assert (NAUTILUS_IS_FILE (file));
909
910 /* Add to list. */
911 node = g_list_prepend (directory->details->file_list, file);
912 directory->details->file_list = node;
913
914 /* Add to hash table. */
915 add_to_hash_table (directory, file, node);
916
917 directory->details->confirmed_file_count++;
918
919 add_to_work_queue = FALSE;
920 if (nautilus_directory_is_file_list_monitored (directory))
921 {
922 /* Ref if we are monitoring, since monitoring owns the file list. */
923 nautilus_file_ref (file);
924 add_to_work_queue = TRUE;
925 }
926 else if (nautilus_directory_has_active_request_for_file (directory, file))
927 {
928 /* We're waiting for the file in a call_when_ready. Make sure
929 * we add the file to the work queue so that said waiter won't
930 * wait forever for e.g. all files in the directory to be done */
931 add_to_work_queue = TRUE;
932 }
933
934 if (add_to_work_queue)
935 {
936 nautilus_directory_add_file_to_work_queue (directory, file);
937 }
938 }
939
940 void
nautilus_directory_remove_file(NautilusDirectory * directory,NautilusFile * file)941 nautilus_directory_remove_file (NautilusDirectory *directory,
942 NautilusFile *file)
943 {
944 GList *node;
945
946 g_assert (NAUTILUS_IS_DIRECTORY (directory));
947 g_assert (NAUTILUS_IS_FILE (file));
948
949 /* Find the list node in the hash table. */
950 node = extract_from_hash_table (directory, file);
951 g_assert (node != NULL);
952 g_assert (node->data == file);
953
954 /* Remove the item from the list. */
955 directory->details->file_list = g_list_remove_link
956 (directory->details->file_list, node);
957 g_list_free_1 (node);
958
959 nautilus_directory_remove_file_from_work_queue (directory, file);
960
961 if (!file->details->unconfirmed)
962 {
963 directory->details->confirmed_file_count--;
964 }
965
966 /* Unref if we are monitoring. */
967 if (nautilus_directory_is_file_list_monitored (directory))
968 {
969 nautilus_file_unref (file);
970 }
971 }
972
973 GList *
nautilus_directory_begin_file_name_change(NautilusDirectory * directory,NautilusFile * file)974 nautilus_directory_begin_file_name_change (NautilusDirectory *directory,
975 NautilusFile *file)
976 {
977 /* Find the list node in the hash table. */
978 return extract_from_hash_table (directory, file);
979 }
980
981 void
nautilus_directory_end_file_name_change(NautilusDirectory * directory,NautilusFile * file,GList * node)982 nautilus_directory_end_file_name_change (NautilusDirectory *directory,
983 NautilusFile *file,
984 GList *node)
985 {
986 /* Add the list node to the hash table. */
987 if (node != NULL)
988 {
989 add_to_hash_table (directory, file, node);
990 }
991 }
992
993 NautilusFile *
nautilus_directory_find_file_by_name(NautilusDirectory * directory,const char * name)994 nautilus_directory_find_file_by_name (NautilusDirectory *directory,
995 const char *name)
996 {
997 GList *node;
998
999 g_return_val_if_fail (NAUTILUS_IS_DIRECTORY (directory), NULL);
1000 g_return_val_if_fail (name != NULL, NULL);
1001
1002 node = g_hash_table_lookup (directory->details->file_hash,
1003 name);
1004 return node == NULL ? NULL : NAUTILUS_FILE (node->data);
1005 }
1006
1007 void
nautilus_directory_emit_files_added(NautilusDirectory * directory,GList * added_files)1008 nautilus_directory_emit_files_added (NautilusDirectory *directory,
1009 GList *added_files)
1010 {
1011 nautilus_profile_start (NULL);
1012 if (added_files != NULL)
1013 {
1014 g_signal_emit (directory,
1015 signals[FILES_ADDED], 0,
1016 added_files);
1017 }
1018 nautilus_profile_end (NULL);
1019 }
1020
1021 void
nautilus_directory_emit_files_changed(NautilusDirectory * directory,GList * changed_files)1022 nautilus_directory_emit_files_changed (NautilusDirectory *directory,
1023 GList *changed_files)
1024 {
1025 nautilus_profile_start (NULL);
1026 if (changed_files != NULL)
1027 {
1028 g_signal_emit (directory,
1029 signals[FILES_CHANGED], 0,
1030 changed_files);
1031 }
1032 nautilus_profile_end (NULL);
1033 }
1034
1035 void
nautilus_directory_emit_change_signals(NautilusDirectory * directory,GList * changed_files)1036 nautilus_directory_emit_change_signals (NautilusDirectory *directory,
1037 GList *changed_files)
1038 {
1039 GList *p;
1040
1041 nautilus_profile_start (NULL);
1042 for (p = changed_files; p != NULL; p = p->next)
1043 {
1044 nautilus_file_emit_changed (p->data);
1045 }
1046 nautilus_directory_emit_files_changed (directory, changed_files);
1047 nautilus_profile_end (NULL);
1048 }
1049
1050 void
nautilus_directory_emit_done_loading(NautilusDirectory * directory)1051 nautilus_directory_emit_done_loading (NautilusDirectory *directory)
1052 {
1053 g_signal_emit (directory,
1054 signals[DONE_LOADING], 0);
1055 }
1056
1057 void
nautilus_directory_emit_load_error(NautilusDirectory * directory,GError * error)1058 nautilus_directory_emit_load_error (NautilusDirectory *directory,
1059 GError *error)
1060 {
1061 g_signal_emit (directory,
1062 signals[LOAD_ERROR], 0,
1063 error);
1064 }
1065
1066 /* Return a directory object for this one's parent. */
1067 static NautilusDirectory *
get_parent_directory(GFile * location)1068 get_parent_directory (GFile *location)
1069 {
1070 NautilusDirectory *directory;
1071 GFile *parent;
1072
1073 parent = g_file_get_parent (location);
1074 if (parent)
1075 {
1076 directory = nautilus_directory_get_internal (parent, TRUE);
1077 g_object_unref (parent);
1078 return directory;
1079 }
1080 return NULL;
1081 }
1082
1083 /* If a directory object exists for this one's parent, then
1084 * return it, otherwise return NULL.
1085 */
1086 static NautilusDirectory *
get_parent_directory_if_exists(GFile * location)1087 get_parent_directory_if_exists (GFile *location)
1088 {
1089 NautilusDirectory *directory;
1090 GFile *parent;
1091
1092 parent = g_file_get_parent (location);
1093 if (parent)
1094 {
1095 directory = nautilus_directory_get_internal (parent, FALSE);
1096 g_object_unref (parent);
1097 return directory;
1098 }
1099 return NULL;
1100 }
1101
1102 static void
hash_table_list_prepend(GHashTable * table,gconstpointer key,gpointer data)1103 hash_table_list_prepend (GHashTable *table,
1104 gconstpointer key,
1105 gpointer data)
1106 {
1107 GList *list;
1108
1109 list = g_hash_table_lookup (table, key);
1110 list = g_list_prepend (list, data);
1111 g_hash_table_insert (table, (gpointer) key, list);
1112 }
1113
1114 static void
call_files_added_free_list(gpointer key,gpointer value,gpointer user_data)1115 call_files_added_free_list (gpointer key,
1116 gpointer value,
1117 gpointer user_data)
1118 {
1119 g_assert (NAUTILUS_IS_DIRECTORY (key));
1120 g_assert (value != NULL);
1121 g_assert (user_data == NULL);
1122
1123 g_signal_emit (key,
1124 signals[FILES_ADDED], 0,
1125 value);
1126 g_list_free (value);
1127 }
1128
1129 static void
call_files_changed_common(NautilusDirectory * self,GList * file_list)1130 call_files_changed_common (NautilusDirectory *self,
1131 GList *file_list)
1132 {
1133 GList *node;
1134 NautilusFile *file;
1135
1136 for (node = file_list; node != NULL; node = node->next)
1137 {
1138 NautilusDirectory *directory;
1139
1140 file = node->data;
1141 directory = nautilus_file_get_directory (file);
1142
1143 if (directory == self)
1144 {
1145 nautilus_directory_add_file_to_work_queue (self, file);
1146 }
1147 }
1148 nautilus_directory_async_state_changed (self);
1149 nautilus_directory_emit_change_signals (self, file_list);
1150 }
1151
1152 static void
call_files_changed_free_list(gpointer key,gpointer value,gpointer user_data)1153 call_files_changed_free_list (gpointer key,
1154 gpointer value,
1155 gpointer user_data)
1156 {
1157 g_assert (value != NULL);
1158 g_assert (user_data == NULL);
1159
1160 call_files_changed_common (NAUTILUS_DIRECTORY (key), value);
1161 g_list_free (value);
1162 }
1163
1164 static void
call_files_changed_unref_free_list(gpointer key,gpointer value,gpointer user_data)1165 call_files_changed_unref_free_list (gpointer key,
1166 gpointer value,
1167 gpointer user_data)
1168 {
1169 g_assert (value != NULL);
1170 g_assert (user_data == NULL);
1171
1172 call_files_changed_common (NAUTILUS_DIRECTORY (key), value);
1173 nautilus_file_list_free (value);
1174 }
1175
1176 static void
call_get_file_info_free_list(gpointer key,gpointer value,gpointer user_data)1177 call_get_file_info_free_list (gpointer key,
1178 gpointer value,
1179 gpointer user_data)
1180 {
1181 NautilusDirectory *directory;
1182 GList *files;
1183
1184 g_assert (NAUTILUS_IS_DIRECTORY (key));
1185 g_assert (value != NULL);
1186 g_assert (user_data == NULL);
1187
1188 directory = key;
1189 files = value;
1190
1191 nautilus_directory_get_info_for_new_files (directory, files);
1192 g_list_foreach (files, (GFunc) g_object_unref, NULL);
1193 g_list_free (files);
1194 }
1195
1196 static void
invalidate_count_and_unref(gpointer key,gpointer value,gpointer user_data)1197 invalidate_count_and_unref (gpointer key,
1198 gpointer value,
1199 gpointer user_data)
1200 {
1201 g_assert (NAUTILUS_IS_DIRECTORY (key));
1202 g_assert (value == key);
1203 g_assert (user_data == NULL);
1204
1205 nautilus_directory_invalidate_count_and_mime_list (key);
1206 nautilus_directory_unref (key);
1207 }
1208
1209 static void
collect_parent_directories(GHashTable * hash_table,NautilusDirectory * directory)1210 collect_parent_directories (GHashTable *hash_table,
1211 NautilusDirectory *directory)
1212 {
1213 g_assert (hash_table != NULL);
1214 g_assert (NAUTILUS_IS_DIRECTORY (directory));
1215
1216 if (g_hash_table_lookup (hash_table, directory) == NULL)
1217 {
1218 nautilus_directory_ref (directory);
1219 g_hash_table_insert (hash_table, directory, directory);
1220 }
1221 }
1222
1223 void
nautilus_directory_notify_files_added(GList * files)1224 nautilus_directory_notify_files_added (GList *files)
1225 {
1226 GHashTable *added_lists;
1227 GList *p;
1228 NautilusDirectory *directory;
1229 GHashTable *parent_directories;
1230 NautilusFile *file;
1231 GFile *location, *parent;
1232
1233 nautilus_profile_start (NULL);
1234
1235 /* Make a list of added files in each directory. */
1236 added_lists = g_hash_table_new (NULL, NULL);
1237
1238 /* Make a list of parent directories that will need their counts updated. */
1239 parent_directories = g_hash_table_new (NULL, NULL);
1240
1241 for (p = files; p != NULL; p = p->next)
1242 {
1243 location = p->data;
1244
1245 /* See if the directory is already known. */
1246 directory = get_parent_directory_if_exists (location);
1247 if (directory == NULL)
1248 {
1249 /* In case the directory is not being
1250 * monitored, but the corresponding file is,
1251 * we must invalidate it's item count.
1252 */
1253
1254
1255 file = NULL;
1256 parent = g_file_get_parent (location);
1257 if (parent)
1258 {
1259 file = nautilus_file_get_existing (parent);
1260 g_object_unref (parent);
1261 }
1262
1263 if (file != NULL)
1264 {
1265 nautilus_file_invalidate_count_and_mime_list (file);
1266 nautilus_file_unref (file);
1267 }
1268
1269 continue;
1270 }
1271
1272 collect_parent_directories (parent_directories, directory);
1273
1274 /* If no one is monitoring files in the directory, nothing to do. */
1275 if (!nautilus_directory_is_file_list_monitored (directory))
1276 {
1277 nautilus_directory_unref (directory);
1278 continue;
1279 }
1280
1281 file = nautilus_file_get_existing (location);
1282 /* We check is_added here, because the file could have been added
1283 * to the directory by a nautilus_file_get() but not gotten
1284 * files_added emitted
1285 */
1286 if (file && file->details->is_added)
1287 {
1288 /* A file already exists, it was probably renamed.
1289 * If it was renamed this could be ignored, but
1290 * queue a change just in case */
1291 nautilus_file_changed (file);
1292 }
1293 else
1294 {
1295 hash_table_list_prepend (added_lists,
1296 directory,
1297 g_object_ref (location));
1298 }
1299 nautilus_file_unref (file);
1300 nautilus_directory_unref (directory);
1301 }
1302
1303 /* Now get file info for the new files. This creates NautilusFile
1304 * objects for the new files, and sends out a files_added signal.
1305 */
1306 g_hash_table_foreach (added_lists, call_get_file_info_free_list, NULL);
1307 g_hash_table_destroy (added_lists);
1308
1309 /* Invalidate count for each parent directory. */
1310 g_hash_table_foreach (parent_directories, invalidate_count_and_unref, NULL);
1311 g_hash_table_destroy (parent_directories);
1312
1313 nautilus_profile_end (NULL);
1314 }
1315
1316 void
nautilus_directory_notify_files_changed(GList * files)1317 nautilus_directory_notify_files_changed (GList *files)
1318 {
1319 GHashTable *changed_lists;
1320 GList *node;
1321 GFile *location;
1322 NautilusFile *file;
1323
1324 /* Make a list of changed files in each directory. */
1325 changed_lists = g_hash_table_new (NULL, NULL);
1326
1327 /* Go through all the notifications. */
1328 for (node = files; node != NULL; node = node->next)
1329 {
1330 location = node->data;
1331
1332 /* Find the file. */
1333 file = nautilus_file_get_existing (location);
1334 if (file != NULL)
1335 {
1336 NautilusDirectory *directory;
1337
1338 directory = nautilus_file_get_directory (file);
1339
1340 /* Tell it to re-get info now, and later emit
1341 * a changed signal.
1342 */
1343 file->details->file_info_is_up_to_date = FALSE;
1344 nautilus_file_invalidate_extension_info_internal (file);
1345
1346 hash_table_list_prepend (changed_lists, directory, file);
1347 }
1348 }
1349
1350 /* Now send out the changed signals. */
1351 g_hash_table_foreach (changed_lists, call_files_changed_unref_free_list, NULL);
1352 g_hash_table_destroy (changed_lists);
1353 }
1354
1355 void
nautilus_directory_notify_files_removed(GList * files)1356 nautilus_directory_notify_files_removed (GList *files)
1357 {
1358 GHashTable *changed_lists;
1359 GList *p;
1360 GHashTable *parent_directories;
1361 NautilusFile *file;
1362 GFile *location;
1363
1364 /* Make a list of changed files in each directory. */
1365 changed_lists = g_hash_table_new (NULL, NULL);
1366
1367 /* Make a list of parent directories that will need their counts updated. */
1368 parent_directories = g_hash_table_new (NULL, NULL);
1369
1370 /* Go through all the notifications. */
1371 for (p = files; p != NULL; p = p->next)
1372 {
1373 NautilusDirectory *directory;
1374
1375 location = p->data;
1376
1377 /* Update file count for parent directory if anyone might care. */
1378 directory = get_parent_directory_if_exists (location);
1379 if (directory != NULL)
1380 {
1381 collect_parent_directories (parent_directories, directory);
1382 nautilus_directory_unref (directory);
1383 }
1384
1385 /* Find the file. */
1386 file = nautilus_file_get_existing (location);
1387 if (file != NULL && !nautilus_file_rename_in_progress (file))
1388 {
1389 directory = nautilus_file_get_directory (file);
1390
1391 /* Mark it gone and prepare to send the changed signal. */
1392 nautilus_file_mark_gone (file);
1393 hash_table_list_prepend (changed_lists,
1394 directory, nautilus_file_ref (file));
1395 }
1396 nautilus_file_unref (file);
1397 }
1398
1399 /* Now send out the changed signals. */
1400 g_hash_table_foreach (changed_lists, call_files_changed_unref_free_list, NULL);
1401 g_hash_table_destroy (changed_lists);
1402
1403 /* Invalidate count for each parent directory. */
1404 g_hash_table_foreach (parent_directories, invalidate_count_and_unref, NULL);
1405 g_hash_table_destroy (parent_directories);
1406 }
1407
1408 static void
set_directory_location(NautilusDirectory * directory,GFile * location)1409 set_directory_location (NautilusDirectory *directory,
1410 GFile *location)
1411 {
1412 if (directory->details->location)
1413 {
1414 g_object_unref (directory->details->location);
1415 }
1416 directory->details->location = g_object_ref (location);
1417
1418 g_object_notify_by_pspec (G_OBJECT (directory), properties[PROP_LOCATION]);
1419 }
1420
1421 static void
change_directory_location(NautilusDirectory * directory,GFile * new_location)1422 change_directory_location (NautilusDirectory *directory,
1423 GFile *new_location)
1424 {
1425 /* I believe it's impossible for a self-owned file/directory
1426 * to be moved. But if that did somehow happen, this function
1427 * wouldn't do enough to handle it.
1428 */
1429 g_assert (directory->details->as_file == NULL);
1430
1431 g_hash_table_remove (directories,
1432 directory->details->location);
1433
1434 set_directory_location (directory, new_location);
1435
1436 g_hash_table_insert (directories,
1437 directory->details->location,
1438 directory);
1439 }
1440
1441 typedef struct
1442 {
1443 GFile *container;
1444 GList *directories;
1445 } CollectData;
1446
1447 static void
collect_directories_by_container(gpointer key,gpointer value,gpointer callback_data)1448 collect_directories_by_container (gpointer key,
1449 gpointer value,
1450 gpointer callback_data)
1451 {
1452 NautilusDirectory *directory;
1453 CollectData *collect_data;
1454 GFile *location;
1455
1456 location = (GFile *) key;
1457 directory = NAUTILUS_DIRECTORY (value);
1458 collect_data = (CollectData *) callback_data;
1459
1460 if (g_file_has_prefix (location, collect_data->container) ||
1461 g_file_equal (collect_data->container, location))
1462 {
1463 nautilus_directory_ref (directory);
1464 collect_data->directories =
1465 g_list_prepend (collect_data->directories,
1466 directory);
1467 }
1468 }
1469
1470 static GList *
nautilus_directory_moved_internal(GFile * old_location,GFile * new_location)1471 nautilus_directory_moved_internal (GFile *old_location,
1472 GFile *new_location)
1473 {
1474 CollectData collection;
1475 NautilusDirectory *directory;
1476 GList *node, *affected_files;
1477 GFile *new_directory_location;
1478 char *relative_path;
1479
1480 collection.container = old_location;
1481 collection.directories = NULL;
1482
1483 g_hash_table_foreach (directories,
1484 collect_directories_by_container,
1485 &collection);
1486
1487 affected_files = NULL;
1488
1489 for (node = collection.directories; node != NULL; node = node->next)
1490 {
1491 directory = NAUTILUS_DIRECTORY (node->data);
1492 new_directory_location = NULL;
1493
1494 if (g_file_equal (directory->details->location, old_location))
1495 {
1496 new_directory_location = g_object_ref (new_location);
1497 }
1498 else
1499 {
1500 relative_path = g_file_get_relative_path (old_location,
1501 directory->details->location);
1502 if (relative_path != NULL)
1503 {
1504 new_directory_location = g_file_resolve_relative_path (new_location, relative_path);
1505 g_free (relative_path);
1506 }
1507 }
1508
1509 if (new_directory_location)
1510 {
1511 change_directory_location (directory, new_directory_location);
1512 g_object_unref (new_directory_location);
1513
1514 /* Collect affected files. */
1515 if (directory->details->as_file != NULL)
1516 {
1517 affected_files = g_list_prepend
1518 (affected_files,
1519 nautilus_file_ref (directory->details->as_file));
1520 }
1521 affected_files = g_list_concat
1522 (affected_files,
1523 nautilus_file_list_copy (directory->details->file_list));
1524 }
1525
1526 nautilus_directory_unref (directory);
1527 }
1528
1529 g_list_free (collection.directories);
1530
1531 return affected_files;
1532 }
1533
1534 void
nautilus_directory_moved(const char * old_uri,const char * new_uri)1535 nautilus_directory_moved (const char *old_uri,
1536 const char *new_uri)
1537 {
1538 GList *list, *node;
1539 GHashTable *hash;
1540 NautilusFile *file;
1541 GFile *old_location;
1542 GFile *new_location;
1543
1544 hash = g_hash_table_new (NULL, NULL);
1545
1546 old_location = g_file_new_for_uri (old_uri);
1547 new_location = g_file_new_for_uri (new_uri);
1548
1549 list = nautilus_directory_moved_internal (old_location, new_location);
1550 for (node = list; node != NULL; node = node->next)
1551 {
1552 NautilusDirectory *directory;
1553
1554 file = NAUTILUS_FILE (node->data);
1555 directory = nautilus_file_get_directory (file);
1556
1557 hash_table_list_prepend (hash, directory, nautilus_file_ref (file));
1558 }
1559 nautilus_file_list_free (list);
1560
1561 g_object_unref (old_location);
1562 g_object_unref (new_location);
1563
1564 g_hash_table_foreach (hash, call_files_changed_unref_free_list, NULL);
1565 g_hash_table_destroy (hash);
1566 }
1567
1568 void
nautilus_directory_notify_files_moved(GList * file_pairs)1569 nautilus_directory_notify_files_moved (GList *file_pairs)
1570 {
1571 GList *p, *affected_files, *node;
1572 GFilePair *pair;
1573 NautilusFile *file;
1574 NautilusDirectory *old_directory, *new_directory;
1575 GHashTable *parent_directories;
1576 GList *new_files_list, *unref_list;
1577 GHashTable *added_lists, *changed_lists;
1578 char *name;
1579 NautilusFileAttributes cancel_attributes;
1580 GFile *to_location, *from_location;
1581
1582 /* Make a list of added and changed files in each directory. */
1583 new_files_list = NULL;
1584 added_lists = g_hash_table_new (NULL, NULL);
1585 changed_lists = g_hash_table_new (NULL, NULL);
1586 unref_list = NULL;
1587
1588 /* Make a list of parent directories that will need their counts updated. */
1589 parent_directories = g_hash_table_new (NULL, NULL);
1590
1591 cancel_attributes = nautilus_file_get_all_attributes ();
1592
1593 for (p = file_pairs; p != NULL; p = p->next)
1594 {
1595 pair = p->data;
1596 from_location = pair->from;
1597 to_location = pair->to;
1598
1599 /* Handle overwriting a file. */
1600 file = nautilus_file_get_existing (to_location);
1601 if (file != NULL)
1602 {
1603 NautilusDirectory *directory;
1604
1605 directory = nautilus_file_get_directory (file);
1606
1607 /* Mark it gone and prepare to send the changed signal. */
1608 nautilus_file_mark_gone (file);
1609 hash_table_list_prepend (changed_lists, directory, file);
1610 collect_parent_directories (parent_directories, directory);
1611 }
1612
1613 /* Update any directory objects that are affected. */
1614 affected_files = nautilus_directory_moved_internal (from_location,
1615 to_location);
1616 for (node = affected_files; node != NULL; node = node->next)
1617 {
1618 NautilusDirectory *directory;
1619
1620 file = NAUTILUS_FILE (node->data);
1621 directory = nautilus_file_get_directory (file);
1622 hash_table_list_prepend (changed_lists, directory, file);
1623 }
1624 unref_list = g_list_concat (unref_list, affected_files);
1625
1626 /* Move an existing file. */
1627 file = nautilus_file_get_existing (from_location);
1628 if (file == NULL)
1629 {
1630 /* Handle this as if it was a new file. */
1631 new_files_list = g_list_prepend (new_files_list,
1632 to_location);
1633 }
1634 else
1635 {
1636 NautilusDirectory *directory;
1637
1638 directory = nautilus_file_get_directory (file);
1639
1640 /* Handle notification in the old directory. */
1641 old_directory = directory;
1642 collect_parent_directories (parent_directories, old_directory);
1643
1644 /* Cancel loading of attributes in the old directory */
1645 nautilus_directory_cancel_loading_file_attributes
1646 (old_directory, file, cancel_attributes);
1647
1648 /* Locate the new directory. */
1649 new_directory = get_parent_directory (to_location);
1650 collect_parent_directories (parent_directories, new_directory);
1651 /* We can unref now -- new_directory is in the
1652 * parent directories list so it will be
1653 * around until the end of this function
1654 * anyway.
1655 */
1656 nautilus_directory_unref (new_directory);
1657
1658 /* Update the file's name and directory. */
1659 name = g_file_get_basename (to_location);
1660 nautilus_file_update_name_and_directory
1661 (file, name, new_directory);
1662 g_free (name);
1663
1664 /* Update file attributes */
1665 nautilus_file_invalidate_attributes (file, NAUTILUS_FILE_ATTRIBUTE_INFO);
1666
1667 hash_table_list_prepend (changed_lists,
1668 old_directory,
1669 file);
1670 if (old_directory != new_directory)
1671 {
1672 hash_table_list_prepend (added_lists,
1673 new_directory,
1674 file);
1675 }
1676
1677 /* Unref each file once to balance out nautilus_file_get_by_uri. */
1678 unref_list = g_list_prepend (unref_list, file);
1679 }
1680 }
1681
1682 /* Now send out the changed and added signals for existing file objects. */
1683 g_hash_table_foreach (changed_lists, call_files_changed_free_list, NULL);
1684 g_hash_table_destroy (changed_lists);
1685 g_hash_table_foreach (added_lists, call_files_added_free_list, NULL);
1686 g_hash_table_destroy (added_lists);
1687
1688 /* Let the file objects go. */
1689 nautilus_file_list_free (unref_list);
1690
1691 /* Invalidate count for each parent directory. */
1692 g_hash_table_foreach (parent_directories, invalidate_count_and_unref, NULL);
1693 g_hash_table_destroy (parent_directories);
1694
1695 /* Separate handling for brand new file objects. */
1696 nautilus_directory_notify_files_added (new_files_list);
1697 g_list_free (new_files_list);
1698 }
1699
1700 gboolean
nautilus_directory_contains_file(NautilusDirectory * directory,NautilusFile * file)1701 nautilus_directory_contains_file (NautilusDirectory *directory,
1702 NautilusFile *file)
1703 {
1704 g_return_val_if_fail (NAUTILUS_IS_DIRECTORY (directory), FALSE);
1705 g_return_val_if_fail (NAUTILUS_IS_FILE (file), FALSE);
1706
1707 if (nautilus_file_is_gone (file))
1708 {
1709 return FALSE;
1710 }
1711
1712 return NAUTILUS_DIRECTORY_CLASS (G_OBJECT_GET_CLASS (directory))->contains_file (directory, file);
1713 }
1714
1715 NautilusFile *
nautilus_directory_get_file_by_name(NautilusDirectory * directory,const gchar * name)1716 nautilus_directory_get_file_by_name (NautilusDirectory *directory,
1717 const gchar *name)
1718 {
1719 GList *files;
1720 GList *l;
1721 NautilusFile *result = NULL;
1722
1723 files = nautilus_directory_get_file_list (directory);
1724
1725 for (l = files; l != NULL; l = l->next)
1726 {
1727 if (nautilus_file_compare_display_name (l->data, name) == 0)
1728 {
1729 result = nautilus_file_ref (l->data);
1730 break;
1731 }
1732 }
1733
1734 nautilus_file_list_free (files);
1735
1736 return result;
1737 }
1738
1739 void
nautilus_directory_call_when_ready(NautilusDirectory * directory,NautilusFileAttributes file_attributes,gboolean wait_for_all_files,NautilusDirectoryCallback callback,gpointer callback_data)1740 nautilus_directory_call_when_ready (NautilusDirectory *directory,
1741 NautilusFileAttributes file_attributes,
1742 gboolean wait_for_all_files,
1743 NautilusDirectoryCallback callback,
1744 gpointer callback_data)
1745 {
1746 g_return_if_fail (NAUTILUS_IS_DIRECTORY (directory));
1747 g_return_if_fail (callback != NULL);
1748
1749 NAUTILUS_DIRECTORY_CLASS (G_OBJECT_GET_CLASS (directory))->call_when_ready
1750 (directory, file_attributes, wait_for_all_files,
1751 callback, callback_data);
1752 }
1753
1754 void
nautilus_directory_cancel_callback(NautilusDirectory * directory,NautilusDirectoryCallback callback,gpointer callback_data)1755 nautilus_directory_cancel_callback (NautilusDirectory *directory,
1756 NautilusDirectoryCallback callback,
1757 gpointer callback_data)
1758 {
1759 g_return_if_fail (NAUTILUS_IS_DIRECTORY (directory));
1760 g_return_if_fail (callback != NULL);
1761
1762 NAUTILUS_DIRECTORY_CLASS (G_OBJECT_GET_CLASS (directory))->cancel_callback
1763 (directory, callback, callback_data);
1764 }
1765
1766 void
nautilus_directory_file_monitor_add(NautilusDirectory * directory,gconstpointer client,gboolean monitor_hidden_files,NautilusFileAttributes file_attributes,NautilusDirectoryCallback callback,gpointer callback_data)1767 nautilus_directory_file_monitor_add (NautilusDirectory *directory,
1768 gconstpointer client,
1769 gboolean monitor_hidden_files,
1770 NautilusFileAttributes file_attributes,
1771 NautilusDirectoryCallback callback,
1772 gpointer callback_data)
1773 {
1774 g_return_if_fail (NAUTILUS_IS_DIRECTORY (directory));
1775 g_return_if_fail (client != NULL);
1776
1777 NAUTILUS_DIRECTORY_CLASS (G_OBJECT_GET_CLASS (directory))->file_monitor_add
1778 (directory, client,
1779 monitor_hidden_files,
1780 file_attributes,
1781 callback, callback_data);
1782 }
1783
1784 void
nautilus_directory_file_monitor_remove(NautilusDirectory * directory,gconstpointer client)1785 nautilus_directory_file_monitor_remove (NautilusDirectory *directory,
1786 gconstpointer client)
1787 {
1788 g_return_if_fail (NAUTILUS_IS_DIRECTORY (directory));
1789 g_return_if_fail (client != NULL);
1790
1791 NAUTILUS_DIRECTORY_CLASS (G_OBJECT_GET_CLASS (directory))->file_monitor_remove
1792 (directory, client);
1793 }
1794
1795 void
nautilus_directory_force_reload(NautilusDirectory * directory)1796 nautilus_directory_force_reload (NautilusDirectory *directory)
1797 {
1798 g_return_if_fail (NAUTILUS_IS_DIRECTORY (directory));
1799
1800 NAUTILUS_DIRECTORY_CLASS (G_OBJECT_GET_CLASS (directory))->force_reload (directory);
1801 }
1802
1803 gboolean
nautilus_directory_is_not_empty(NautilusDirectory * directory)1804 nautilus_directory_is_not_empty (NautilusDirectory *directory)
1805 {
1806 g_return_val_if_fail (NAUTILUS_IS_DIRECTORY (directory), FALSE);
1807
1808 return NAUTILUS_DIRECTORY_CLASS (G_OBJECT_GET_CLASS (directory))->is_not_empty (directory);
1809 }
1810
1811 GList *
nautilus_directory_get_file_list(NautilusDirectory * directory)1812 nautilus_directory_get_file_list (NautilusDirectory *directory)
1813 {
1814 return NAUTILUS_DIRECTORY_CLASS (G_OBJECT_GET_CLASS (directory))->get_file_list (directory);
1815 }
1816
1817 gboolean
nautilus_directory_is_editable(NautilusDirectory * directory)1818 nautilus_directory_is_editable (NautilusDirectory *directory)
1819 {
1820 return NAUTILUS_DIRECTORY_CLASS (G_OBJECT_GET_CLASS (directory))->is_editable (directory);
1821 }
1822
1823 GList *
nautilus_directory_match_pattern(NautilusDirectory * directory,const char * pattern)1824 nautilus_directory_match_pattern (NautilusDirectory *directory,
1825 const char *pattern)
1826 {
1827 GList *files, *l, *ret;
1828 GPatternSpec *spec;
1829
1830
1831 ret = NULL;
1832 spec = g_pattern_spec_new (pattern);
1833
1834 files = nautilus_directory_get_file_list (directory);
1835 for (l = files; l; l = l->next)
1836 {
1837 NautilusFile *file;
1838 char *name;
1839
1840 file = NAUTILUS_FILE (l->data);
1841 name = nautilus_file_get_display_name (file);
1842
1843 if (g_pattern_match_string (spec, name))
1844 {
1845 ret = g_list_prepend (ret, nautilus_file_ref (file));
1846 }
1847
1848 g_free (name);
1849 }
1850
1851 g_pattern_spec_free (spec);
1852 nautilus_file_list_free (files);
1853
1854 return ret;
1855 }
1856
1857 /**
1858 * nautilus_directory_list_ref
1859 *
1860 * Ref all the directories in a list.
1861 * @list: GList of directories.
1862 **/
1863 GList *
nautilus_directory_list_ref(GList * list)1864 nautilus_directory_list_ref (GList *list)
1865 {
1866 g_list_foreach (list, (GFunc) nautilus_directory_ref, NULL);
1867 return list;
1868 }
1869
1870 /**
1871 * nautilus_directory_list_unref
1872 *
1873 * Unref all the directories in a list.
1874 * @list: GList of directories.
1875 **/
1876 void
nautilus_directory_list_unref(GList * list)1877 nautilus_directory_list_unref (GList *list)
1878 {
1879 g_list_foreach (list, (GFunc) nautilus_directory_unref, NULL);
1880 }
1881
1882 /**
1883 * nautilus_directory_list_free
1884 *
1885 * Free a list of directories after unrefing them.
1886 * @list: GList of directories.
1887 **/
1888 void
nautilus_directory_list_free(GList * list)1889 nautilus_directory_list_free (GList *list)
1890 {
1891 nautilus_directory_list_unref (list);
1892 g_list_free (list);
1893 }
1894
1895 /**
1896 * nautilus_directory_list_copy
1897 *
1898 * Copy the list of directories, making a new ref of each,
1899 * @list: GList of directories.
1900 **/
1901 GList *
nautilus_directory_list_copy(GList * list)1902 nautilus_directory_list_copy (GList *list)
1903 {
1904 return g_list_copy (nautilus_directory_list_ref (list));
1905 }
1906
1907 static int
compare_by_uri(NautilusDirectory * a,NautilusDirectory * b)1908 compare_by_uri (NautilusDirectory *a,
1909 NautilusDirectory *b)
1910 {
1911 char *uri_a, *uri_b;
1912 int res;
1913
1914 uri_a = g_file_get_uri (a->details->location);
1915 uri_b = g_file_get_uri (b->details->location);
1916
1917 res = strcmp (uri_a, uri_b);
1918
1919 g_free (uri_a);
1920 g_free (uri_b);
1921
1922 return res;
1923 }
1924
1925 static int
compare_by_uri_cover(gconstpointer a,gconstpointer b)1926 compare_by_uri_cover (gconstpointer a,
1927 gconstpointer b)
1928 {
1929 return compare_by_uri (NAUTILUS_DIRECTORY (a), NAUTILUS_DIRECTORY (b));
1930 }
1931
1932 /**
1933 * nautilus_directory_list_sort_by_uri
1934 *
1935 * Sort the list of directories by directory uri.
1936 * @list: GList of directories.
1937 **/
1938 GList *
nautilus_directory_list_sort_by_uri(GList * list)1939 nautilus_directory_list_sort_by_uri (GList *list)
1940 {
1941 return g_list_sort (list, compare_by_uri_cover);
1942 }
1943
1944 #if !defined (NAUTILUS_OMIT_SELF_CHECK)
1945
1946 #include <eel/eel-debug.h>
1947
1948 static int data_dummy;
1949 static gboolean got_files_flag;
1950
1951 static void
got_files_callback(NautilusDirectory * directory,GList * files,gpointer callback_data)1952 got_files_callback (NautilusDirectory *directory,
1953 GList *files,
1954 gpointer callback_data)
1955 {
1956 g_assert (NAUTILUS_IS_DIRECTORY (directory));
1957 g_assert (g_list_length (files) > 10);
1958 g_assert (callback_data == &data_dummy);
1959
1960 got_files_flag = TRUE;
1961 }
1962
1963 /* Return the number of extant NautilusDirectories */
1964 int
nautilus_directory_number_outstanding(void)1965 nautilus_directory_number_outstanding (void)
1966 {
1967 return directories ? g_hash_table_size (directories) : 0;
1968 }
1969
1970 void
nautilus_directory_dump(NautilusDirectory * directory)1971 nautilus_directory_dump (NautilusDirectory *directory)
1972 {
1973 g_autofree gchar *uri = NULL;
1974
1975 uri = g_file_get_uri (directory->details->location);
1976 g_print ("uri: %s\n", uri);
1977 g_print ("ref count: %d\n", G_OBJECT (directory)->ref_count);
1978 }
1979
1980 void
nautilus_self_check_directory(void)1981 nautilus_self_check_directory (void)
1982 {
1983 NautilusDirectory *directory;
1984 NautilusFile *file;
1985
1986 directory = nautilus_directory_get_by_uri ("file:///etc");
1987 file = nautilus_file_get_by_uri ("file:///etc/passwd");
1988
1989 EEL_CHECK_INTEGER_RESULT (g_hash_table_size (directories), 1);
1990
1991 nautilus_directory_file_monitor_add
1992 (directory, &data_dummy,
1993 TRUE, 0, NULL, NULL);
1994
1995 /* FIXME: these need to be updated to the new metadata infrastructure
1996 * as make check doesn't pass.
1997 * nautilus_file_set_metadata (file, "test", "default", "value");
1998 * EEL_CHECK_STRING_RESULT (nautilus_file_get_metadata (file, "test", "default"), "value");
1999 *
2000 * nautilus_file_set_boolean_metadata (file, "test_boolean", TRUE, TRUE);
2001 * EEL_CHECK_BOOLEAN_RESULT (nautilus_file_get_boolean_metadata (file, "test_boolean", TRUE), TRUE);
2002 * nautilus_file_set_boolean_metadata (file, "test_boolean", TRUE, FALSE);
2003 * EEL_CHECK_BOOLEAN_RESULT (nautilus_file_get_boolean_metadata (file, "test_boolean", TRUE), FALSE);
2004 * EEL_CHECK_BOOLEAN_RESULT (nautilus_file_get_boolean_metadata (NULL, "test_boolean", TRUE), TRUE);
2005 *
2006 * nautilus_file_set_integer_metadata (file, "test_integer", 0, 17);
2007 * EEL_CHECK_INTEGER_RESULT (nautilus_file_get_integer_metadata (file, "test_integer", 0), 17);
2008 * nautilus_file_set_integer_metadata (file, "test_integer", 0, -1);
2009 * EEL_CHECK_INTEGER_RESULT (nautilus_file_get_integer_metadata (file, "test_integer", 0), -1);
2010 * nautilus_file_set_integer_metadata (file, "test_integer", 42, 42);
2011 * EEL_CHECK_INTEGER_RESULT (nautilus_file_get_integer_metadata (file, "test_integer", 42), 42);
2012 * EEL_CHECK_INTEGER_RESULT (nautilus_file_get_integer_metadata (NULL, "test_integer", 42), 42);
2013 * EEL_CHECK_INTEGER_RESULT (nautilus_file_get_integer_metadata (file, "nonexistent_key", 42), 42);
2014 */
2015
2016 EEL_CHECK_BOOLEAN_RESULT (nautilus_directory_get_by_uri ("file:///etc") == directory, TRUE);
2017 nautilus_directory_unref (directory);
2018
2019 EEL_CHECK_BOOLEAN_RESULT (nautilus_directory_get_by_uri ("file:///etc/") == directory, TRUE);
2020 nautilus_directory_unref (directory);
2021
2022 EEL_CHECK_BOOLEAN_RESULT (nautilus_directory_get_by_uri ("file:///etc////") == directory, TRUE);
2023 nautilus_directory_unref (directory);
2024
2025 nautilus_file_unref (file);
2026
2027 nautilus_directory_file_monitor_remove (directory, &data_dummy);
2028
2029 nautilus_directory_unref (directory);
2030
2031 while (g_hash_table_size (directories) != 0)
2032 {
2033 gtk_main_iteration ();
2034 }
2035
2036 EEL_CHECK_INTEGER_RESULT (g_hash_table_size (directories), 0);
2037
2038 directory = nautilus_directory_get_by_uri ("file:///etc");
2039
2040 got_files_flag = FALSE;
2041
2042 nautilus_directory_call_when_ready (directory,
2043 NAUTILUS_FILE_ATTRIBUTE_INFO |
2044 NAUTILUS_FILE_ATTRIBUTE_DEEP_COUNTS,
2045 TRUE,
2046 got_files_callback, &data_dummy);
2047
2048 while (!got_files_flag)
2049 {
2050 gtk_main_iteration ();
2051 }
2052
2053 EEL_CHECK_BOOLEAN_RESULT (directory->details->file_list == NULL, TRUE);
2054
2055 EEL_CHECK_INTEGER_RESULT (g_hash_table_size (directories), 1);
2056
2057 file = nautilus_file_get_by_uri ("file:///etc/passwd");
2058
2059 /* EEL_CHECK_STRING_RESULT (nautilus_file_get_metadata (file, "test", "default"), "value"); */
2060
2061 nautilus_file_unref (file);
2062
2063 nautilus_directory_unref (directory);
2064
2065 EEL_CHECK_INTEGER_RESULT (g_hash_table_size (directories), 0);
2066 }
2067
2068 #endif /* !NAUTILUS_OMIT_SELF_CHECK */
2069