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