2008-04-30 A. Walton <awalton@gnome.org>
[nautilus.git] / libnautilus-private / nautilus-directory.c
blob01b8aaa5bf188eba06431b2bcbd2c0b825b1b1f3
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*-
3 nautilus-directory.c: Nautilus directory model.
5 Copyright (C) 1999, 2000, 2001 Eazel, Inc.
7 This program is free software; you can redistribute it and/or
8 modify it under the terms of the GNU General Public License as
9 published by the Free Software Foundation; either version 2 of the
10 License, or (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
17 You should have received a copy of the GNU General Public
18 License along with this program; if not, write to the
19 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 Boston, MA 02111-1307, USA.
22 Author: Darin Adler <darin@bentspoon.com>
25 #include <config.h>
26 #include "nautilus-directory-private.h"
28 #include "nautilus-directory-metafile.h"
29 #include "nautilus-directory-notify.h"
30 #include "nautilus-file-attributes.h"
31 #include "nautilus-file-private.h"
32 #include "nautilus-file-utilities.h"
33 #include "nautilus-search-directory.h"
34 #include "nautilus-global-preferences.h"
35 #include "nautilus-lib-self-check-functions.h"
36 #include "nautilus-marshal.h"
37 #include "nautilus-metadata.h"
38 #include "nautilus-metafile.h"
39 #include "nautilus-desktop-directory.h"
40 #include "nautilus-vfs-directory.h"
41 #include <eel/eel-glib-extensions.h>
42 #include <eel/eel-gtk-macros.h>
43 #include <eel/eel-string.h>
44 #include <gtk/gtkmain.h>
45 #include <gtk/gtksignal.h>
47 enum {
48 FILES_ADDED,
49 FILES_CHANGED,
50 DONE_LOADING,
51 LOAD_ERROR,
52 LAST_SIGNAL
55 static guint signals[LAST_SIGNAL];
57 /* Specifications for parallel-directory metafile. */
58 #define METAFILES_DIRECTORY_NAME "metafiles"
59 #define METAFILE_SUFFIX ".xml"
60 #define METAFILES_DIRECTORY_PERMISSIONS \
61 (S_IRUSR | S_IWUSR | S_IXUSR | \
62 S_IRGRP | S_IWGRP | S_IXGRP | \
63 S_IROTH | S_IWOTH | S_IXOTH)
65 static GHashTable *directories;
67 static void nautilus_directory_finalize (GObject *object);
68 static void nautilus_directory_init (gpointer object,
69 gpointer klass);
70 static void nautilus_directory_class_init (NautilusDirectoryClass *klass);
71 static NautilusDirectory *nautilus_directory_new (GFile *location);
72 static char * real_get_name_for_self_as_new_file (NautilusDirectory *directory);
73 static GList * real_get_file_list (NautilusDirectory *directory);
74 static gboolean real_is_editable (NautilusDirectory *directory);
75 static void set_directory_location (NautilusDirectory *directory,
76 GFile *location);
78 EEL_CLASS_BOILERPLATE (NautilusDirectory,
79 nautilus_directory,
80 G_TYPE_OBJECT)
82 static void
83 nautilus_directory_class_init (NautilusDirectoryClass *klass)
85 GObjectClass *object_class;
87 object_class = G_OBJECT_CLASS (klass);
89 object_class->finalize = nautilus_directory_finalize;
91 signals[FILES_ADDED] =
92 g_signal_new ("files_added",
93 G_TYPE_FROM_CLASS (object_class),
94 G_SIGNAL_RUN_LAST,
95 G_STRUCT_OFFSET (NautilusDirectoryClass, files_added),
96 NULL, NULL,
97 g_cclosure_marshal_VOID__POINTER,
98 G_TYPE_NONE, 1, G_TYPE_POINTER);
99 signals[FILES_CHANGED] =
100 g_signal_new ("files_changed",
101 G_TYPE_FROM_CLASS (object_class),
102 G_SIGNAL_RUN_LAST,
103 G_STRUCT_OFFSET (NautilusDirectoryClass, files_changed),
104 NULL, NULL,
105 g_cclosure_marshal_VOID__POINTER,
106 G_TYPE_NONE, 1, G_TYPE_POINTER);
107 signals[DONE_LOADING] =
108 g_signal_new ("done_loading",
109 G_TYPE_FROM_CLASS (object_class),
110 G_SIGNAL_RUN_LAST,
111 G_STRUCT_OFFSET (NautilusDirectoryClass, done_loading),
112 NULL, NULL,
113 g_cclosure_marshal_VOID__VOID,
114 G_TYPE_NONE, 0);
115 signals[LOAD_ERROR] =
116 g_signal_new ("load_error",
117 G_TYPE_FROM_CLASS (object_class),
118 G_SIGNAL_RUN_LAST,
119 G_STRUCT_OFFSET (NautilusDirectoryClass, load_error),
120 NULL, NULL,
121 g_cclosure_marshal_VOID__POINTER,
122 G_TYPE_NONE, 1, G_TYPE_POINTER);
124 klass->get_name_for_self_as_new_file = real_get_name_for_self_as_new_file;
125 klass->get_file_list = real_get_file_list;
126 klass->is_editable = real_is_editable;
128 g_type_class_add_private (klass, sizeof (NautilusDirectoryDetails));
131 static void
132 nautilus_directory_init (gpointer object, gpointer klass)
134 NautilusDirectory *directory;
136 directory = NAUTILUS_DIRECTORY(object);
138 directory->details = G_TYPE_INSTANCE_GET_PRIVATE ((directory), NAUTILUS_TYPE_DIRECTORY, NautilusDirectoryDetails);
139 directory->details->file_hash = g_hash_table_new (g_str_hash, g_str_equal);
140 directory->details->high_priority_queue = nautilus_file_queue_new ();
141 directory->details->low_priority_queue = nautilus_file_queue_new ();
142 directory->details->extension_queue = nautilus_file_queue_new ();
143 directory->details->idle_queue = nautilus_idle_queue_new ();
146 NautilusDirectory *
147 nautilus_directory_ref (NautilusDirectory *directory)
149 if (directory == NULL) {
150 return directory;
153 g_return_val_if_fail (NAUTILUS_IS_DIRECTORY (directory), NULL);
155 g_object_ref (directory);
156 return directory;
159 void
160 nautilus_directory_unref (NautilusDirectory *directory)
162 if (directory == NULL) {
163 return;
166 g_return_if_fail (NAUTILUS_IS_DIRECTORY (directory));
168 g_object_unref (directory);
171 static void
172 nautilus_directory_finalize (GObject *object)
174 NautilusDirectory *directory;
176 directory = NAUTILUS_DIRECTORY (object);
178 g_hash_table_remove (directories, directory->details->location);
180 nautilus_directory_cancel (directory);
181 g_assert (directory->details->count_in_progress == NULL);
182 g_assert (directory->details->top_left_read_state == NULL);
184 if (directory->details->monitor_list != NULL) {
185 g_warning ("destroying a NautilusDirectory while it's being monitored");
186 eel_g_list_free_deep (directory->details->monitor_list);
189 if (directory->details->monitor != NULL) {
190 nautilus_monitor_cancel (directory->details->monitor);
193 if (directory->details->metafile_monitored) {
194 nautilus_directory_unregister_metadata_monitor (directory);
197 if (directory->details->metafile != NULL) {
198 g_object_unref (directory->details->metafile);
201 if (directory->details->dequeue_pending_idle_id != 0) {
202 g_source_remove (directory->details->dequeue_pending_idle_id);
205 if (directory->details->call_ready_idle_id != 0) {
206 g_source_remove (directory->details->call_ready_idle_id);
209 if (directory->details->location) {
210 g_object_unref (directory->details->location);
213 g_assert (directory->details->file_list == NULL);
214 g_hash_table_destroy (directory->details->file_hash);
216 if (directory->details->hidden_file_hash) {
217 g_hash_table_destroy (directory->details->hidden_file_hash);
220 nautilus_file_queue_destroy (directory->details->high_priority_queue);
221 nautilus_file_queue_destroy (directory->details->low_priority_queue);
222 nautilus_file_queue_destroy (directory->details->extension_queue);
223 nautilus_idle_queue_destroy (directory->details->idle_queue);
224 g_assert (directory->details->directory_load_in_progress == NULL);
225 g_assert (directory->details->count_in_progress == NULL);
226 g_assert (directory->details->dequeue_pending_idle_id == 0);
227 eel_g_object_list_free (directory->details->pending_file_info);
229 EEL_CALL_PARENT (G_OBJECT_CLASS, finalize, (object));
232 static void
233 invalidate_one_count (gpointer key, gpointer value, gpointer user_data)
235 NautilusDirectory *directory;
237 g_assert (key != NULL);
238 g_assert (NAUTILUS_IS_DIRECTORY (value));
239 g_assert (user_data == NULL);
241 directory = NAUTILUS_DIRECTORY (value);
243 nautilus_directory_invalidate_count_and_mime_list (directory);
246 static void
247 filtering_changed_callback (gpointer callback_data)
249 g_assert (callback_data == NULL);
251 /* Preference about which items to show has changed, so we
252 * can't trust any of our precomputed directory counts.
254 g_hash_table_foreach (directories, invalidate_one_count, NULL);
257 void
258 emit_change_signals_for_all_files (NautilusDirectory *directory)
260 GList *files;
262 files = g_list_copy (directory->details->file_list);
263 if (directory->details->as_file != NULL) {
264 files = g_list_prepend (files, directory->details->as_file);
267 nautilus_file_list_ref (files);
268 nautilus_directory_emit_change_signals (directory, files);
270 nautilus_file_list_free (files);
273 static void
274 collect_all_directories (gpointer key, gpointer value, gpointer callback_data)
276 NautilusDirectory *directory;
277 GList **dirs;
278 GFile *location;
280 location = (GFile *) key;
281 directory = NAUTILUS_DIRECTORY (value);
282 dirs = callback_data;
284 *dirs = g_list_prepend (*dirs, nautilus_directory_ref (directory));
287 void
288 emit_change_signals_for_all_files_in_all_directories (void)
290 GList *dirs, *l;
291 NautilusDirectory *directory;
293 dirs = NULL;
294 g_hash_table_foreach (directories,
295 collect_all_directories,
296 &dirs);
298 for (l = dirs; l != NULL; l = l->next) {
299 directory = NAUTILUS_DIRECTORY (l->data);
300 emit_change_signals_for_all_files (directory);
301 nautilus_directory_unref (directory);
304 g_list_free (dirs);
307 static void
308 async_state_changed_one (gpointer key, gpointer value, gpointer user_data)
310 NautilusDirectory *directory;
312 g_assert (key != NULL);
313 g_assert (NAUTILUS_IS_DIRECTORY (value));
314 g_assert (user_data == NULL);
316 directory = NAUTILUS_DIRECTORY (value);
318 nautilus_directory_async_state_changed (directory);
319 emit_change_signals_for_all_files (directory);
322 static void
323 async_data_preference_changed_callback (gpointer callback_data)
325 g_assert (callback_data == NULL);
327 /* Preference involving fetched async data has changed, so
328 * we have to kick off refetching all async data, and tell
329 * each file that it (might have) changed.
331 g_hash_table_foreach (directories, async_state_changed_one, NULL);
334 static void
335 add_preferences_callbacks (void)
337 nautilus_global_preferences_init ();
339 eel_preferences_add_callback (NAUTILUS_PREFERENCES_SHOW_HIDDEN_FILES,
340 filtering_changed_callback,
341 NULL);
342 eel_preferences_add_callback (NAUTILUS_PREFERENCES_SHOW_BACKUP_FILES,
343 filtering_changed_callback,
344 NULL);
345 eel_preferences_add_callback (NAUTILUS_PREFERENCES_SHOW_TEXT_IN_ICONS,
346 async_data_preference_changed_callback,
347 NULL);
348 eel_preferences_add_callback (NAUTILUS_PREFERENCES_SHOW_DIRECTORY_ITEM_COUNTS,
349 async_data_preference_changed_callback,
350 NULL);
351 eel_preferences_add_callback (NAUTILUS_PREFERENCES_DATE_FORMAT,
352 async_data_preference_changed_callback,
353 NULL);
357 * nautilus_directory_get_by_uri:
358 * @uri: URI of directory to get.
360 * Get a directory given a uri.
361 * Creates the appropriate subclass given the uri mappings.
362 * Returns a referenced object, not a floating one. Unref when finished.
363 * If two windows are viewing the same uri, the directory object is shared.
365 NautilusDirectory *
366 nautilus_directory_get_internal (GFile *location, gboolean create)
368 NautilusDirectory *directory;
370 /* Create the hash table first time through. */
371 if (directories == NULL) {
372 directories = eel_g_hash_table_new_free_at_exit
373 (g_file_hash, (GCompareFunc)g_file_equal, "nautilus-directory.c: directories");
375 add_preferences_callbacks ();
378 /* If the object is already in the hash table, look it up. */
380 directory = g_hash_table_lookup (directories,
381 location);
382 if (directory != NULL) {
383 nautilus_directory_ref (directory);
384 } else if (create) {
385 /* Create a new directory object instead. */
386 directory = nautilus_directory_new (location);
387 if (directory == NULL) {
388 return NULL;
391 /* Put it in the hash table. */
392 g_hash_table_insert (directories,
393 directory->details->location,
394 directory);
397 return directory;
400 NautilusDirectory *
401 nautilus_directory_get (GFile *location)
403 if (location == NULL) {
404 return NULL;
407 return nautilus_directory_get_internal (location, TRUE);
410 NautilusDirectory *
411 nautilus_directory_get_existing (GFile *location)
413 if (location == NULL) {
414 return NULL;
417 return nautilus_directory_get_internal (location, FALSE);
421 NautilusDirectory *
422 nautilus_directory_get_by_uri (const char *uri)
424 NautilusDirectory *directory;
425 GFile *location;
427 if (uri == NULL) {
428 return NULL;
431 location = g_file_new_for_uri (uri);
433 directory = nautilus_directory_get_internal (location, TRUE);
434 g_object_unref (location);
435 return directory;
438 NautilusDirectory *
439 nautilus_directory_get_for_file (NautilusFile *file)
441 char *uri;
442 NautilusDirectory *directory;
444 g_return_val_if_fail (NAUTILUS_IS_FILE (file), NULL);
446 uri = nautilus_file_get_uri (file);
447 directory = nautilus_directory_get_by_uri (uri);
448 g_free (uri);
449 return directory;
452 /* Returns a reffed NautilusFile object for this directory.
454 NautilusFile *
455 nautilus_directory_get_corresponding_file (NautilusDirectory *directory)
457 NautilusFile *file;
458 char *uri;
460 file = nautilus_directory_get_existing_corresponding_file (directory);
461 if (file == NULL) {
462 uri = nautilus_directory_get_uri (directory);
463 file = nautilus_file_get_by_uri (uri);
464 g_free (uri);
467 return file;
470 /* Returns a reffed NautilusFile object for this directory, but only if the
471 * NautilusFile object has already been created.
473 NautilusFile *
474 nautilus_directory_get_existing_corresponding_file (NautilusDirectory *directory)
476 NautilusFile *file;
477 char *uri;
479 file = directory->details->as_file;
480 if (file != NULL) {
481 nautilus_file_ref (file);
482 return file;
485 uri = nautilus_directory_get_uri (directory);
486 file = nautilus_file_get_existing_by_uri (uri);
487 g_free (uri);
488 return file;
491 /* nautilus_directory_get_name_for_self_as_new_file:
493 * Get a name to display for the file representing this
494 * directory. This is called only when there's no VFS
495 * directory for this NautilusDirectory.
497 char *
498 nautilus_directory_get_name_for_self_as_new_file (NautilusDirectory *directory)
500 g_return_val_if_fail (NAUTILUS_IS_DIRECTORY (directory), NULL);
502 return EEL_CALL_METHOD_WITH_RETURN_VALUE
503 (NAUTILUS_DIRECTORY_CLASS, directory,
504 get_name_for_self_as_new_file, (directory));
507 static char *
508 real_get_name_for_self_as_new_file (NautilusDirectory *directory)
510 char *directory_uri;
511 char *name, *colon;
513 directory_uri = nautilus_directory_get_uri (directory);
515 colon = strchr (directory_uri, ':');
516 if (colon == NULL || colon == directory_uri) {
517 name = g_strdup (directory_uri);
518 } else {
519 name = g_strndup (directory_uri, colon - directory_uri);
521 g_free (directory_uri);
523 return name;
526 char *
527 nautilus_directory_get_uri (NautilusDirectory *directory)
529 g_return_val_if_fail (NAUTILUS_IS_DIRECTORY (directory), NULL);
531 return g_file_get_uri (directory->details->location);
534 GFile *
535 nautilus_directory_get_location (NautilusDirectory *directory)
537 g_return_val_if_fail (NAUTILUS_IS_DIRECTORY (directory), NULL);
539 return g_object_ref (directory->details->location);
542 static NautilusDirectory *
543 nautilus_directory_new (GFile *location)
545 NautilusDirectory *directory;
546 char *uri;
548 uri = g_file_get_uri (location);
550 if (eel_uri_is_desktop (uri)) {
551 directory = NAUTILUS_DIRECTORY (g_object_new (NAUTILUS_TYPE_DESKTOP_DIRECTORY, NULL));
552 } else if (eel_uri_is_search (uri)) {
553 directory = NAUTILUS_DIRECTORY (g_object_new (NAUTILUS_TYPE_SEARCH_DIRECTORY, NULL));
554 } else if (g_str_has_suffix (uri, NAUTILUS_SAVED_SEARCH_EXTENSION)) {
555 directory = NAUTILUS_DIRECTORY (nautilus_search_directory_new_from_saved_search (uri));
556 } else {
557 directory = NAUTILUS_DIRECTORY (g_object_new (NAUTILUS_TYPE_VFS_DIRECTORY, NULL));
560 set_directory_location (directory, location);
562 g_free (uri);
564 return directory;
567 gboolean
568 nautilus_directory_is_local (NautilusDirectory *directory)
570 g_return_val_if_fail (NAUTILUS_IS_DIRECTORY (directory), FALSE);
572 if (directory->details->location == NULL) {
573 return TRUE;
575 return g_file_is_native (directory->details->location);
578 gboolean
579 nautilus_directory_is_in_trash (NautilusDirectory *directory)
581 g_assert (NAUTILUS_IS_DIRECTORY (directory));
583 if (directory->details->location == NULL) {
584 return FALSE;
587 return g_file_has_uri_scheme (directory->details->location, "trash");
590 gboolean
591 nautilus_directory_are_all_files_seen (NautilusDirectory *directory)
593 g_return_val_if_fail (NAUTILUS_IS_DIRECTORY (directory), FALSE);
595 return EEL_CALL_METHOD_WITH_RETURN_VALUE
596 (NAUTILUS_DIRECTORY_CLASS, directory,
597 are_all_files_seen, (directory));
600 static void
601 add_to_hash_table (NautilusDirectory *directory, NautilusFile *file, GList *node)
603 const char *name;
605 name = eel_ref_str_peek (file->details->name);
607 g_assert (node != NULL);
608 g_assert (g_hash_table_lookup (directory->details->file_hash,
609 name) == NULL);
610 g_hash_table_insert (directory->details->file_hash, (char *) name, node);
613 static GList *
614 extract_from_hash_table (NautilusDirectory *directory, NautilusFile *file)
616 const char *name;
617 GList *node;
619 name = eel_ref_str_peek (file->details->name);
620 if (name == NULL) {
621 return NULL;
624 /* Find the list node in the hash table. */
625 node = g_hash_table_lookup (directory->details->file_hash, name);
626 g_hash_table_remove (directory->details->file_hash, name);
628 return node;
631 void
632 nautilus_directory_add_file (NautilusDirectory *directory, NautilusFile *file)
634 GList *node;
636 g_assert (NAUTILUS_IS_DIRECTORY (directory));
637 g_assert (NAUTILUS_IS_FILE (file));
638 g_assert (file->details->name != NULL);
640 /* Add to list. */
641 node = g_list_prepend (directory->details->file_list, file);
642 directory->details->file_list = node;
644 /* Add to hash table. */
645 add_to_hash_table (directory, file, node);
647 directory->details->confirmed_file_count++;
649 /* Ref if we are monitoring. */
650 if (nautilus_directory_is_file_list_monitored (directory)) {
651 nautilus_file_ref (file);
652 nautilus_directory_add_file_to_work_queue (directory, file);
656 void
657 nautilus_directory_remove_file (NautilusDirectory *directory, NautilusFile *file)
659 GList *node;
661 g_assert (NAUTILUS_IS_DIRECTORY (directory));
662 g_assert (NAUTILUS_IS_FILE (file));
663 g_assert (file->details->name != NULL);
665 /* Find the list node in the hash table. */
666 node = extract_from_hash_table (directory, file);
667 g_assert (node != NULL);
668 g_assert (node->data == file);
670 /* Remove the item from the list. */
671 directory->details->file_list = g_list_remove_link
672 (directory->details->file_list, node);
673 g_list_free_1 (node);
675 nautilus_directory_remove_file_from_work_queue (directory, file);
677 if (!file->details->unconfirmed) {
678 directory->details->confirmed_file_count--;
681 /* Unref if we are monitoring. */
682 if (nautilus_directory_is_file_list_monitored (directory)) {
683 nautilus_file_unref (file);
687 #define NAUTILUS_DIRECTORY_FILE_LIST_DEFAULT_LIMIT 4000
689 gboolean
690 nautilus_directory_file_list_length_reached (NautilusDirectory *directory)
692 static gboolean inited = FALSE;
693 static int directory_limit = 0;
695 if (!inited) {
696 eel_preferences_add_auto_integer
697 ("/apps/nautilus/preferences/directory_limit",
698 &directory_limit);
699 inited = TRUE;
701 if (directory_limit < 0) { /* unlimited */
702 return FALSE;
704 if (directory_limit == 0) { /* dead gconfd */
705 directory_limit = NAUTILUS_DIRECTORY_FILE_LIST_DEFAULT_LIMIT;
708 return directory->details->confirmed_file_count >= directory_limit;
711 GList *
712 nautilus_directory_begin_file_name_change (NautilusDirectory *directory,
713 NautilusFile *file)
715 /* Find the list node in the hash table. */
716 return extract_from_hash_table (directory, file);
719 void
720 nautilus_directory_end_file_name_change (NautilusDirectory *directory,
721 NautilusFile *file,
722 GList *node)
724 /* Add the list node to the hash table. */
725 if (node != NULL) {
726 add_to_hash_table (directory, file, node);
730 NautilusFile *
731 nautilus_directory_find_file_by_name (NautilusDirectory *directory,
732 const char *name)
734 GList *node;
736 g_return_val_if_fail (NAUTILUS_IS_DIRECTORY (directory), NULL);
737 g_return_val_if_fail (name != NULL, NULL);
739 node = g_hash_table_lookup (directory->details->file_hash,
740 name);
741 return node == NULL ? NULL : NAUTILUS_FILE (node->data);
744 /* "." for the directory-as-file, otherwise the filename */
745 NautilusFile *
746 nautilus_directory_find_file_by_internal_filename (NautilusDirectory *directory,
747 const char *internal_filename)
749 NautilusFile *result;
751 if (eel_strcmp (internal_filename, ".") == 0) {
752 result = nautilus_directory_get_existing_corresponding_file (directory);
753 if (result != NULL) {
754 nautilus_file_unref (result);
756 } else {
757 result = nautilus_directory_find_file_by_name (directory, internal_filename);
760 return result;
763 void
764 nautilus_directory_emit_files_added (NautilusDirectory *directory,
765 GList *added_files)
767 if (added_files != NULL) {
768 g_signal_emit (directory,
769 signals[FILES_ADDED], 0,
770 added_files);
774 void
775 nautilus_directory_emit_files_changed (NautilusDirectory *directory,
776 GList *changed_files)
778 if (changed_files != NULL) {
779 g_signal_emit (directory,
780 signals[FILES_CHANGED], 0,
781 changed_files);
785 void
786 nautilus_directory_emit_change_signals (NautilusDirectory *directory,
787 GList *changed_files)
789 GList *p;
791 for (p = changed_files; p != NULL; p = p->next) {
792 nautilus_file_emit_changed (p->data);
794 nautilus_directory_emit_files_changed (directory, changed_files);
797 void
798 nautilus_directory_emit_done_loading (NautilusDirectory *directory)
800 g_signal_emit (directory,
801 signals[DONE_LOADING], 0);
804 void
805 nautilus_directory_emit_load_error (NautilusDirectory *directory,
806 GError *error)
808 g_signal_emit (directory,
809 signals[LOAD_ERROR], 0,
810 error);
813 /* Return a directory object for this one's parent. */
814 static NautilusDirectory *
815 get_parent_directory (GFile *location)
817 NautilusDirectory *directory;
818 GFile *parent;
820 parent = g_file_get_parent (location);
821 if (parent) {
822 directory = nautilus_directory_get_internal (parent, TRUE);
823 g_object_unref (parent);
824 return directory;
826 return NULL;
829 /* If a directory object exists for this one's parent, then
830 * return it, otherwise return NULL.
832 static NautilusDirectory *
833 get_parent_directory_if_exists (GFile *location)
835 NautilusDirectory *directory;
836 GFile *parent;
838 parent = g_file_get_parent (location);
839 if (parent) {
840 directory = nautilus_directory_get_internal (parent, FALSE);
841 g_object_unref (parent);
842 return directory;
844 return NULL;
847 static void
848 hash_table_list_prepend (GHashTable *table, gconstpointer key, gpointer data)
850 GList *list;
852 list = g_hash_table_lookup (table, key);
853 list = g_list_prepend (list, data);
854 g_hash_table_insert (table, (gpointer) key, list);
857 static void
858 call_files_added_free_list (gpointer key, gpointer value, gpointer user_data)
860 g_assert (NAUTILUS_IS_DIRECTORY (key));
861 g_assert (value != NULL);
862 g_assert (user_data == NULL);
864 g_signal_emit (key,
865 signals[FILES_ADDED], 0,
866 value);
867 g_list_free (value);
870 static void
871 call_files_changed_common (NautilusDirectory *directory, GList *file_list)
873 GList *node;
874 NautilusFile *file;
876 for (node = file_list; node != NULL; node = node->next) {
877 file = node->data;
878 if (file->details->directory == directory) {
879 nautilus_directory_add_file_to_work_queue (directory,
880 file);
883 nautilus_directory_async_state_changed (directory);
884 nautilus_directory_emit_change_signals (directory, file_list);
887 static void
888 call_files_changed_free_list (gpointer key, gpointer value, gpointer user_data)
890 g_assert (value != NULL);
891 g_assert (user_data == NULL);
893 call_files_changed_common (NAUTILUS_DIRECTORY (key), value);
894 g_list_free (value);
897 static void
898 call_files_changed_unref_free_list (gpointer key, gpointer value, gpointer user_data)
900 g_assert (value != NULL);
901 g_assert (user_data == NULL);
903 call_files_changed_common (NAUTILUS_DIRECTORY (key), value);
904 nautilus_file_list_free (value);
907 static void
908 call_get_file_info_free_list (gpointer key, gpointer value, gpointer user_data)
910 NautilusDirectory *directory;
911 GList *files;
913 g_assert (NAUTILUS_IS_DIRECTORY (key));
914 g_assert (value != NULL);
915 g_assert (user_data == NULL);
917 directory = key;
918 files = value;
920 nautilus_directory_get_info_for_new_files (directory, files);
921 g_list_foreach (files, (GFunc) g_object_unref, NULL);
922 g_list_free (files);
925 static void
926 invalidate_count_and_unref (gpointer key, gpointer value, gpointer user_data)
928 g_assert (NAUTILUS_IS_DIRECTORY (key));
929 g_assert (value == key);
930 g_assert (user_data == NULL);
932 nautilus_directory_invalidate_count_and_mime_list (key);
933 nautilus_directory_unref (key);
936 static void
937 collect_parent_directories (GHashTable *hash_table, NautilusDirectory *directory)
939 g_assert (hash_table != NULL);
940 g_assert (NAUTILUS_IS_DIRECTORY (directory));
942 if (g_hash_table_lookup (hash_table, directory) == NULL) {
943 nautilus_directory_ref (directory);
944 g_hash_table_insert (hash_table, directory, directory);
948 void
949 nautilus_directory_notify_files_added (GList *files)
951 GHashTable *added_lists;
952 GList *p;
953 NautilusDirectory *directory;
954 GHashTable *parent_directories;
955 NautilusFile *file;
956 GFile *location, *parent;
958 /* Make a list of added files in each directory. */
959 added_lists = g_hash_table_new (NULL, NULL);
961 /* Make a list of parent directories that will need their counts updated. */
962 parent_directories = g_hash_table_new (NULL, NULL);
964 for (p = files; p != NULL; p = p->next) {
965 location = p->data;
967 /* See if the directory is already known. */
968 directory = get_parent_directory_if_exists (location);
969 if (directory == NULL) {
970 /* In case the directory is not being
971 * monitored, but the corresponding file is,
972 * we must invalidate it's item count.
976 file = NULL;
977 parent = g_file_get_parent (location);
978 if (parent) {
979 file = nautilus_file_get_existing (parent);
980 g_object_unref (parent);
983 if (file != NULL) {
984 nautilus_file_invalidate_count_and_mime_list (file);
985 nautilus_file_unref (file);
988 continue;
991 collect_parent_directories (parent_directories, directory);
993 /* If no one is monitoring files in the directory, nothing to do. */
994 if (!nautilus_directory_is_file_list_monitored (directory)) {
995 nautilus_directory_unref (directory);
996 continue;
999 file = nautilus_file_get_existing (location);
1000 /* We check is_added here, because the file could have been added
1001 * to the directory by a nautilus_file_get() but not gotten
1002 * files_added emitted
1004 if (file && file->details->is_added) {
1005 /* A file already exists, it was probably renamed.
1006 * If it was renamed this could be ignored, but
1007 * queue a change just in case */
1008 nautilus_file_changed (file);
1009 nautilus_file_unref (file);
1010 } else {
1011 hash_table_list_prepend (added_lists,
1012 directory,
1013 g_object_ref (location));
1015 nautilus_directory_unref (directory);
1018 /* Now get file info for the new files. This creates NautilusFile
1019 * objects for the new files, and sends out a files_added signal.
1021 g_hash_table_foreach (added_lists, call_get_file_info_free_list, NULL);
1022 g_hash_table_destroy (added_lists);
1024 /* Invalidate count for each parent directory. */
1025 g_hash_table_foreach (parent_directories, invalidate_count_and_unref, NULL);
1026 g_hash_table_destroy (parent_directories);
1029 static GList *
1030 uri_list_to_file_list (GList *uris)
1032 GList *l, *file_list;
1033 const char *uri;
1034 GFile *file;
1037 file_list = NULL;
1039 for (l = uris; l != NULL; l = l->next) {
1040 uri = l->data;
1041 file = g_file_new_for_uri (uri);
1042 file_list = g_list_prepend (file_list, file);
1044 return g_list_reverse (file_list);
1047 static void
1048 g_file_pair_free (GFilePair *pair)
1050 g_object_unref (pair->to);
1051 g_object_unref (pair->from);
1052 g_free (pair);
1055 static GList *
1056 uri_pairs_to_file_pairs (GList *uri_pairs)
1058 GList *l, *file_pair_list;
1059 GFilePair *file_pair;
1060 URIPair *uri_pair;
1062 file_pair_list = NULL;
1064 for (l = uri_pairs; l != NULL; l = l->next) {
1065 uri_pair = l->data;
1066 file_pair = g_new (GFilePair, 1);
1067 file_pair->from = g_file_new_for_uri (uri_pair->from_uri);
1068 file_pair->to = g_file_new_for_uri (uri_pair->to_uri);
1070 file_pair_list = g_list_prepend (file_pair_list, file_pair);
1072 return g_list_reverse (file_pair_list);
1075 void
1076 nautilus_directory_notify_files_added_by_uri (GList *uris)
1078 GList *files;
1080 files = uri_list_to_file_list (uris);
1081 nautilus_directory_notify_files_added (files);
1082 eel_g_object_list_free (files);
1085 void
1086 nautilus_directory_notify_files_changed (GList *files)
1088 GHashTable *changed_lists;
1089 GList *node;
1090 GFile *location;
1091 NautilusFile *file;
1093 /* Make a list of changed files in each directory. */
1094 changed_lists = g_hash_table_new (NULL, NULL);
1096 /* Go through all the notifications. */
1097 for (node = files; node != NULL; node = node->next) {
1098 location = node->data;
1100 /* Find the file. */
1101 file = nautilus_file_get_existing (location);
1102 if (file != NULL) {
1103 /* Tell it to re-get info now, and later emit
1104 * a changed signal.
1106 file->details->file_info_is_up_to_date = FALSE;
1107 file->details->top_left_text_is_up_to_date = FALSE;
1108 file->details->link_info_is_up_to_date = FALSE;
1109 nautilus_file_invalidate_extension_info_internal (file);
1111 hash_table_list_prepend (changed_lists,
1112 file->details->directory,
1113 file);
1117 /* Now send out the changed signals. */
1118 g_hash_table_foreach (changed_lists, call_files_changed_unref_free_list, NULL);
1119 g_hash_table_destroy (changed_lists);
1122 void
1123 nautilus_directory_notify_files_changed_by_uri (GList *uris)
1125 GList *files;
1127 files = uri_list_to_file_list (uris);
1128 nautilus_directory_notify_files_changed (files);
1129 eel_g_object_list_free (files);
1132 void
1133 nautilus_directory_notify_files_removed (GList *files)
1135 GHashTable *changed_lists;
1136 GList *p;
1137 NautilusDirectory *directory;
1138 GHashTable *parent_directories;
1139 NautilusFile *file;
1140 GFile *location;
1142 /* Make a list of changed files in each directory. */
1143 changed_lists = g_hash_table_new (NULL, NULL);
1145 /* Make a list of parent directories that will need their counts updated. */
1146 parent_directories = g_hash_table_new (NULL, NULL);
1148 /* Go through all the notifications. */
1149 for (p = files; p != NULL; p = p->next) {
1150 location = p->data;
1152 /* Update file count for parent directory if anyone might care. */
1153 directory = get_parent_directory_if_exists (location);
1154 if (directory != NULL) {
1155 collect_parent_directories (parent_directories, directory);
1156 nautilus_directory_unref (directory);
1159 /* Find the file. */
1160 file = nautilus_file_get_existing (location);
1161 if (file != NULL && !nautilus_file_rename_in_progress (file)) {
1162 /* Mark it gone and prepare to send the changed signal. */
1163 nautilus_file_mark_gone (file);
1164 hash_table_list_prepend (changed_lists,
1165 file->details->directory,
1166 nautilus_file_ref (file));
1168 nautilus_file_unref (file);
1171 /* Now send out the changed signals. */
1172 g_hash_table_foreach (changed_lists, call_files_changed_unref_free_list, NULL);
1173 g_hash_table_destroy (changed_lists);
1175 /* Invalidate count for each parent directory. */
1176 g_hash_table_foreach (parent_directories, invalidate_count_and_unref, NULL);
1177 g_hash_table_destroy (parent_directories);
1180 void
1181 nautilus_directory_notify_files_removed_by_uri (GList *uris)
1183 GList *files;
1185 files = uri_list_to_file_list (uris);
1186 nautilus_directory_notify_files_changed (files);
1187 eel_g_object_list_free (files);
1190 static void
1191 set_directory_location (NautilusDirectory *directory,
1192 GFile *location)
1194 if (directory->details->location) {
1195 g_object_unref (directory->details->location);
1197 directory->details->location = g_object_ref (location);
1201 static void
1202 change_directory_location (NautilusDirectory *directory,
1203 GFile *new_location)
1205 char *new_uri;
1207 /* I believe it's impossible for a self-owned file/directory
1208 * to be moved. But if that did somehow happen, this function
1209 * wouldn't do enough to handle it.
1211 g_assert (directory->details->as_file == NULL);
1213 g_hash_table_remove (directories,
1214 directory->details->location);
1216 set_directory_location (directory, new_location);
1218 g_hash_table_insert (directories,
1219 directory->details->location,
1220 directory);
1222 new_uri = g_file_get_uri (new_location);
1223 nautilus_directory_rename_directory_metadata (directory, new_uri);
1224 g_free (new_uri);
1227 typedef struct {
1228 GFile *container;
1229 GList *directories;
1230 } CollectData;
1232 static void
1233 collect_directories_by_container (gpointer key, gpointer value, gpointer callback_data)
1235 NautilusDirectory *directory;
1236 CollectData *collect_data;
1237 GFile *location;
1239 location = (GFile *) key;
1240 directory = NAUTILUS_DIRECTORY (value);
1241 collect_data = (CollectData *) callback_data;
1243 if (g_file_has_prefix (location, collect_data->container) ||
1244 g_file_equal (collect_data->container, location)) {
1245 nautilus_directory_ref (directory);
1246 collect_data->directories =
1247 g_list_prepend (collect_data->directories,
1248 directory);
1252 static GList *
1253 nautilus_directory_moved_internal (GFile *old_location,
1254 GFile *new_location)
1256 CollectData collection;
1257 NautilusDirectory *directory;
1258 GList *node, *affected_files;
1259 GFile *new_directory_location;
1260 char *relative_path;
1262 collection.container = old_location;
1263 collection.directories = NULL;
1265 g_hash_table_foreach (directories,
1266 collect_directories_by_container,
1267 &collection);
1269 affected_files = NULL;
1271 for (node = collection.directories; node != NULL; node = node->next) {
1272 directory = NAUTILUS_DIRECTORY (node->data);
1273 new_directory_location = NULL;
1275 if (g_file_equal (directory->details->location, old_location)) {
1276 new_directory_location = g_object_ref (new_location);
1277 } else {
1278 relative_path = g_file_get_relative_path (old_location,
1279 directory->details->location);
1280 if (relative_path != NULL) {
1281 new_directory_location = g_file_resolve_relative_path (new_location, relative_path);
1282 g_free (relative_path);
1287 if (new_directory_location) {
1288 change_directory_location (directory, new_directory_location);
1289 g_object_unref (new_directory_location);
1291 /* Collect affected files. */
1292 if (directory->details->as_file != NULL) {
1293 affected_files = g_list_prepend
1294 (affected_files,
1295 nautilus_file_ref (directory->details->as_file));
1297 affected_files = g_list_concat
1298 (affected_files,
1299 nautilus_file_list_copy (directory->details->file_list));
1302 nautilus_directory_unref (directory);
1305 g_list_free (collection.directories);
1307 return affected_files;
1310 void
1311 nautilus_directory_moved (const char *old_uri,
1312 const char *new_uri)
1314 GList *list, *node;
1315 GHashTable *hash;
1316 NautilusFile *file;
1317 GFile *old_location;
1318 GFile *new_location;
1320 hash = g_hash_table_new (NULL, NULL);
1322 old_location = g_file_new_for_uri (old_uri);
1323 new_location = g_file_new_for_uri (new_uri);
1325 list = nautilus_directory_moved_internal (old_location, new_location);
1326 for (node = list; node != NULL; node = node->next) {
1327 file = NAUTILUS_FILE (node->data);
1328 hash_table_list_prepend (hash,
1329 file->details->directory,
1330 nautilus_file_ref (file));
1332 nautilus_file_list_free (list);
1334 g_object_unref (old_location);
1335 g_object_unref (new_location);
1337 g_hash_table_foreach (hash, call_files_changed_unref_free_list, NULL);
1338 g_hash_table_destroy (hash);
1341 void
1342 nautilus_directory_notify_files_moved (GList *file_pairs)
1344 GList *p, *affected_files, *node;
1345 GFilePair *pair;
1346 NautilusFile *file;
1347 NautilusDirectory *old_directory, *new_directory;
1348 GHashTable *parent_directories;
1349 GList *new_files_list, *unref_list;
1350 GHashTable *added_lists, *changed_lists;
1351 char *name;
1352 NautilusFileAttributes cancel_attributes;
1353 GFile *to_location, *from_location;
1355 /* Make a list of added and changed files in each directory. */
1356 new_files_list = NULL;
1357 added_lists = g_hash_table_new (NULL, NULL);
1358 changed_lists = g_hash_table_new (NULL, NULL);
1359 unref_list = NULL;
1361 /* Make a list of parent directories that will need their counts updated. */
1362 parent_directories = g_hash_table_new (NULL, NULL);
1364 cancel_attributes = nautilus_file_get_all_attributes ();
1366 for (p = file_pairs; p != NULL; p = p->next) {
1367 pair = p->data;
1368 from_location = pair->from;
1369 to_location = pair->to;
1371 /* Handle overwriting a file. */
1372 file = nautilus_file_get_existing (to_location);
1373 if (file != NULL) {
1374 /* Mark it gone and prepare to send the changed signal. */
1375 nautilus_file_mark_gone (file);
1376 new_directory = file->details->directory;
1377 hash_table_list_prepend (changed_lists,
1378 new_directory,
1379 file);
1380 collect_parent_directories (parent_directories,
1381 new_directory);
1384 /* Update any directory objects that are affected. */
1385 affected_files = nautilus_directory_moved_internal (from_location,
1386 to_location);
1387 for (node = affected_files; node != NULL; node = node->next) {
1388 file = NAUTILUS_FILE (node->data);
1389 hash_table_list_prepend (changed_lists,
1390 file->details->directory,
1391 file);
1393 unref_list = g_list_concat (unref_list, affected_files);
1395 /* Move an existing file. */
1396 file = nautilus_file_get_existing (from_location);
1397 if (file == NULL) {
1398 /* Handle this as if it was a new file. */
1399 new_files_list = g_list_prepend (new_files_list,
1400 to_location);
1401 } else {
1402 /* Handle notification in the old directory. */
1403 old_directory = file->details->directory;
1404 collect_parent_directories (parent_directories, old_directory);
1406 /* Cancel loading of attributes in the old directory */
1407 nautilus_directory_cancel_loading_file_attributes
1408 (old_directory, file, cancel_attributes);
1410 /* Locate the new directory. */
1411 new_directory = get_parent_directory (to_location);
1412 collect_parent_directories (parent_directories, new_directory);
1413 /* We can unref now -- new_directory is in the
1414 * parent directories list so it will be
1415 * around until the end of this function
1416 * anyway.
1418 nautilus_directory_unref (new_directory);
1420 /* Update the file's name and directory. */
1421 name = g_file_get_basename (to_location);
1422 nautilus_file_update_name_and_directory
1423 (file, name, new_directory);
1424 g_free (name);
1426 hash_table_list_prepend (changed_lists,
1427 old_directory,
1428 file);
1429 if (old_directory != new_directory) {
1430 hash_table_list_prepend (added_lists,
1431 new_directory,
1432 file);
1435 /* Unref each file once to balance out nautilus_file_get_by_uri. */
1436 unref_list = g_list_prepend (unref_list, file);
1440 /* Now send out the changed and added signals for existing file objects. */
1441 g_hash_table_foreach (changed_lists, call_files_changed_free_list, NULL);
1442 g_hash_table_destroy (changed_lists);
1443 g_hash_table_foreach (added_lists, call_files_added_free_list, NULL);
1444 g_hash_table_destroy (added_lists);
1446 /* Let the file objects go. */
1447 nautilus_file_list_free (unref_list);
1449 /* Invalidate count for each parent directory. */
1450 g_hash_table_foreach (parent_directories, invalidate_count_and_unref, NULL);
1451 g_hash_table_destroy (parent_directories);
1453 /* Separate handling for brand new file objects. */
1454 nautilus_directory_notify_files_added (new_files_list);
1455 g_list_free (new_files_list);
1458 void
1459 nautilus_directory_notify_files_moved_by_uri (GList *uri_pairs)
1461 GList *file_pairs;
1463 file_pairs = uri_pairs_to_file_pairs (uri_pairs);
1464 nautilus_directory_notify_files_moved (file_pairs);
1465 g_list_foreach (file_pairs, (GFunc)g_file_pair_free, NULL);
1466 g_list_free (file_pairs);
1469 void
1470 nautilus_directory_schedule_metadata_copy (GList *file_pairs)
1472 GList *p;
1473 GFilePair *pair;
1474 NautilusDirectory *source_directory, *destination_directory;
1475 char *source_basename, *destination_basename;
1477 for (p = file_pairs; p != NULL; p = p->next) {
1478 pair = p->data;
1480 source_directory = get_parent_directory (pair->from);
1481 destination_directory = get_parent_directory (pair->to);
1483 source_basename = g_file_get_basename (pair->from);
1484 destination_basename = g_file_get_basename (pair->to);
1486 if (source_directory != NULL && destination_directory != NULL) {
1487 nautilus_directory_copy_file_metadata (source_directory,
1488 source_basename,
1489 destination_directory,
1490 destination_basename);
1493 g_free (source_basename);
1494 g_free (destination_basename);
1496 nautilus_directory_unref (source_directory);
1497 nautilus_directory_unref (destination_directory);
1501 void
1502 nautilus_directory_schedule_metadata_copy_by_uri (GList *uri_pairs)
1504 GList *file_pairs;
1506 file_pairs = uri_pairs_to_file_pairs (uri_pairs);
1507 nautilus_directory_schedule_metadata_copy (file_pairs);
1508 g_list_foreach (file_pairs, (GFunc)g_file_pair_free, NULL);
1509 g_list_free (file_pairs);
1513 void
1514 nautilus_directory_schedule_metadata_move (GList *file_pairs)
1516 GList *p;
1517 GFilePair *pair;
1518 NautilusDirectory *source_directory, *destination_directory;
1519 char *source_basename, *destination_basename;
1521 for (p = file_pairs; p != NULL; p = p->next) {
1522 pair = p->data;
1524 source_directory = get_parent_directory (pair->from);
1525 destination_directory = get_parent_directory (pair->to);
1527 source_basename = g_file_get_basename (pair->from);
1528 destination_basename = g_file_get_basename (pair->to);
1530 nautilus_directory_copy_file_metadata (source_directory,
1531 source_basename,
1532 destination_directory,
1533 destination_basename);
1534 nautilus_directory_remove_file_metadata (source_directory,
1535 source_basename);
1537 g_free (source_basename);
1538 g_free (destination_basename);
1540 nautilus_directory_unref (source_directory);
1541 nautilus_directory_unref (destination_directory);
1545 void
1546 nautilus_directory_schedule_metadata_move_by_uri (GList *uri_pairs)
1548 GList *file_pairs;
1550 file_pairs = uri_pairs_to_file_pairs (uri_pairs);
1551 nautilus_directory_schedule_metadata_move (file_pairs);
1552 g_list_foreach (file_pairs, (GFunc)g_file_pair_free, NULL);
1553 g_list_free (file_pairs);
1556 void
1557 nautilus_directory_schedule_metadata_remove (GList *files)
1559 GList *p;
1560 NautilusDirectory *directory;
1561 char *basename;
1562 GFile *location;
1564 for (p = files; p != NULL; p = p->next) {
1565 location = p->data;
1567 directory = get_parent_directory (location);
1569 basename = g_file_get_basename (location);
1570 nautilus_directory_remove_file_metadata (directory, basename);
1571 g_free (basename);
1574 nautilus_directory_unref (directory);
1578 void
1579 nautilus_directory_schedule_metadata_remove_by_uri (GList *uris)
1581 GList *files;
1583 files = uri_list_to_file_list (uris);
1584 nautilus_directory_schedule_metadata_remove (files);
1585 eel_g_object_list_free (files);
1588 void
1589 nautilus_directory_schedule_position_set (GList *position_setting_list)
1591 GList *p;
1592 const NautilusFileChangesQueuePosition *item;
1593 NautilusFile *file;
1594 char str[64];
1596 for (p = position_setting_list; p != NULL; p = p->next) {
1597 item = (NautilusFileChangesQueuePosition *) p->data;
1599 file = nautilus_file_get (item->location);
1601 if (item->set) {
1602 g_snprintf (str, sizeof (str), "%d,%d", item->point.x, item->point.y);
1603 } else {
1604 str[0] = 0;
1606 nautilus_file_set_metadata
1607 (file,
1608 NAUTILUS_METADATA_KEY_ICON_POSITION,
1609 NULL,
1610 str);
1612 if (item->set) {
1613 g_snprintf (str, sizeof (str), "%d", item->screen);
1614 } else {
1615 str[0] = 0;
1617 nautilus_file_set_metadata
1618 (file,
1619 NAUTILUS_METADATA_KEY_SCREEN,
1620 NULL,
1621 str);
1623 nautilus_file_unref (file);
1627 gboolean
1628 nautilus_directory_contains_file (NautilusDirectory *directory,
1629 NautilusFile *file)
1631 g_return_val_if_fail (NAUTILUS_IS_DIRECTORY (directory), FALSE);
1632 g_return_val_if_fail (NAUTILUS_IS_FILE (file), FALSE);
1634 if (nautilus_file_is_gone (file)) {
1635 return FALSE;
1638 return EEL_CALL_METHOD_WITH_RETURN_VALUE
1639 (NAUTILUS_DIRECTORY_CLASS, directory,
1640 contains_file, (directory, file));
1643 char *
1644 nautilus_directory_get_file_uri (NautilusDirectory *directory,
1645 const char *file_name)
1647 GFile *child;
1648 char *result;
1650 g_return_val_if_fail (NAUTILUS_IS_DIRECTORY (directory), NULL);
1651 g_return_val_if_fail (file_name != NULL, NULL);
1653 result = NULL;
1655 child = g_file_get_child (directory->details->location, file_name);
1656 result = g_file_get_uri (child);
1657 g_object_unref (child);
1659 return result;
1662 void
1663 nautilus_directory_call_when_ready (NautilusDirectory *directory,
1664 NautilusFileAttributes file_attributes,
1665 gboolean wait_for_all_files,
1666 NautilusDirectoryCallback callback,
1667 gpointer callback_data)
1669 g_return_if_fail (NAUTILUS_IS_DIRECTORY (directory));
1670 g_return_if_fail (callback != NULL);
1672 EEL_CALL_METHOD
1673 (NAUTILUS_DIRECTORY_CLASS, directory,
1674 call_when_ready, (directory, file_attributes, wait_for_all_files,
1675 callback, callback_data));
1678 void
1679 nautilus_directory_cancel_callback (NautilusDirectory *directory,
1680 NautilusDirectoryCallback callback,
1681 gpointer callback_data)
1683 g_return_if_fail (NAUTILUS_IS_DIRECTORY (directory));
1684 g_return_if_fail (callback != NULL);
1686 EEL_CALL_METHOD
1687 (NAUTILUS_DIRECTORY_CLASS, directory,
1688 cancel_callback, (directory, callback, callback_data));
1691 void
1692 nautilus_directory_file_monitor_add (NautilusDirectory *directory,
1693 gconstpointer client,
1694 gboolean monitor_hidden_files,
1695 gboolean monitor_backup_files,
1696 NautilusFileAttributes file_attributes,
1697 NautilusDirectoryCallback callback,
1698 gpointer callback_data)
1700 g_return_if_fail (NAUTILUS_IS_DIRECTORY (directory));
1701 g_return_if_fail (client != NULL);
1703 EEL_CALL_METHOD
1704 (NAUTILUS_DIRECTORY_CLASS, directory,
1705 file_monitor_add, (directory, client,
1706 monitor_hidden_files,
1707 monitor_backup_files,
1708 file_attributes,
1709 callback, callback_data));
1712 void
1713 nautilus_directory_file_monitor_remove (NautilusDirectory *directory,
1714 gconstpointer client)
1716 g_return_if_fail (NAUTILUS_IS_DIRECTORY (directory));
1717 g_return_if_fail (client != NULL);
1719 EEL_CALL_METHOD
1720 (NAUTILUS_DIRECTORY_CLASS, directory,
1721 file_monitor_remove, (directory, client));
1724 void
1725 nautilus_directory_force_reload (NautilusDirectory *directory)
1727 g_return_if_fail (NAUTILUS_IS_DIRECTORY (directory));
1729 EEL_CALL_METHOD
1730 (NAUTILUS_DIRECTORY_CLASS, directory,
1731 force_reload, (directory));
1734 gboolean
1735 nautilus_directory_is_not_empty (NautilusDirectory *directory)
1737 g_return_val_if_fail (NAUTILUS_IS_DIRECTORY (directory), FALSE);
1739 return EEL_CALL_METHOD_WITH_RETURN_VALUE
1740 (NAUTILUS_DIRECTORY_CLASS, directory,
1741 is_not_empty, (directory));
1744 static gboolean
1745 is_tentative (gpointer data, gpointer callback_data)
1747 NautilusFile *file;
1749 g_assert (callback_data == NULL);
1751 file = NAUTILUS_FILE (data);
1752 /* Avoid returning files with !is_added, because these
1753 * will later be sent with the files_added signal, and a
1754 * user doing get_file_list + files_added monitoring will
1755 * then see the file twice */
1756 return !file->details->got_file_info || !file->details->is_added;
1759 GList *
1760 nautilus_directory_get_file_list (NautilusDirectory *directory)
1762 return EEL_CALL_METHOD_WITH_RETURN_VALUE
1763 (NAUTILUS_DIRECTORY_CLASS, directory,
1764 get_file_list, (directory));
1767 static GList *
1768 real_get_file_list (NautilusDirectory *directory)
1770 GList *tentative_files, *non_tentative_files;
1772 tentative_files = eel_g_list_partition
1773 (g_list_copy (directory->details->file_list),
1774 is_tentative, NULL, &non_tentative_files);
1775 g_list_free (tentative_files);
1777 nautilus_file_list_ref (non_tentative_files);
1778 return non_tentative_files;
1781 static gboolean
1782 real_is_editable (NautilusDirectory *directory)
1784 return TRUE;
1787 gboolean
1788 nautilus_directory_is_editable (NautilusDirectory *directory)
1790 return EEL_CALL_METHOD_WITH_RETURN_VALUE
1791 (NAUTILUS_DIRECTORY_CLASS, directory,
1792 is_editable, (directory));
1795 GList *
1796 nautilus_directory_match_pattern (NautilusDirectory *directory, const char *pattern)
1798 GList *files, *l, *ret;
1799 GPatternSpec *spec;
1802 ret = NULL;
1803 spec = g_pattern_spec_new (pattern);
1805 files = nautilus_directory_get_file_list (directory);
1806 for (l = files; l; l = l->next) {
1807 NautilusFile *file;
1808 char *name;
1810 file = NAUTILUS_FILE (l->data);
1811 name = nautilus_file_get_display_name (file);
1813 if (g_pattern_match_string (spec, name)) {
1814 ret = g_list_prepend(ret, nautilus_file_ref (file));
1817 g_free (name);
1820 g_pattern_spec_free (spec);
1821 nautilus_file_list_free (files);
1823 return ret;
1827 * nautilus_directory_list_ref
1829 * Ref all the directories in a list.
1830 * @list: GList of directories.
1832 GList *
1833 nautilus_directory_list_ref (GList *list)
1835 g_list_foreach (list, (GFunc) nautilus_directory_ref, NULL);
1836 return list;
1840 * nautilus_directory_list_unref
1842 * Unref all the directories in a list.
1843 * @list: GList of directories.
1845 void
1846 nautilus_directory_list_unref (GList *list)
1848 g_list_foreach (list, (GFunc) nautilus_directory_unref, NULL);
1852 * nautilus_directory_list_free
1854 * Free a list of directories after unrefing them.
1855 * @list: GList of directories.
1857 void
1858 nautilus_directory_list_free (GList *list)
1860 nautilus_directory_list_unref (list);
1861 g_list_free (list);
1865 * nautilus_directory_list_copy
1867 * Copy the list of directories, making a new ref of each,
1868 * @list: GList of directories.
1870 GList *
1871 nautilus_directory_list_copy (GList *list)
1873 return g_list_copy (nautilus_directory_list_ref (list));
1876 static int
1877 compare_by_uri (NautilusDirectory *a, NautilusDirectory *b)
1879 char *uri_a, *uri_b;
1880 int res;
1882 uri_a = g_file_get_uri (a->details->location);
1883 uri_b = g_file_get_uri (b->details->location);
1885 res = strcmp (uri_a, uri_b);
1887 g_free (uri_a);
1888 g_free (uri_b);
1890 return res;
1893 static int
1894 compare_by_uri_cover (gconstpointer a, gconstpointer b)
1896 return compare_by_uri (NAUTILUS_DIRECTORY (a), NAUTILUS_DIRECTORY (b));
1900 * nautilus_directory_list_sort_by_uri
1902 * Sort the list of directories by directory uri.
1903 * @list: GList of directories.
1905 GList *
1906 nautilus_directory_list_sort_by_uri (GList *list)
1908 return g_list_sort (list, compare_by_uri_cover);
1911 gboolean
1912 nautilus_directory_is_desktop_directory (NautilusDirectory *directory)
1914 if (directory->details->location == NULL) {
1915 return FALSE;
1918 return nautilus_is_desktop_directory (directory->details->location);
1921 #if !defined (NAUTILUS_OMIT_SELF_CHECK)
1923 #include <eel/eel-debug.h>
1924 #include "nautilus-file-attributes.h"
1926 static int data_dummy;
1927 static gboolean got_metadata_flag;
1928 static gboolean got_files_flag;
1930 static void
1931 got_metadata_callback (NautilusDirectory *directory, GList *files, gpointer callback_data)
1933 g_assert (NAUTILUS_IS_DIRECTORY (directory));
1934 g_assert (callback_data == &data_dummy);
1936 got_metadata_flag = TRUE;
1939 static void
1940 got_files_callback (NautilusDirectory *directory, GList *files, gpointer callback_data)
1942 g_assert (NAUTILUS_IS_DIRECTORY (directory));
1943 g_assert (g_list_length (files) > 10);
1944 g_assert (callback_data == &data_dummy);
1946 got_files_flag = TRUE;
1949 /* Return the number of extant NautilusDirectories */
1951 nautilus_directory_number_outstanding (void)
1953 return directories ? g_hash_table_size (directories) : 0;
1956 void
1957 nautilus_self_check_directory (void)
1959 NautilusDirectory *directory;
1960 NautilusFile *file;
1962 directory = nautilus_directory_get_by_uri ("file:///etc");
1963 file = nautilus_file_get_by_uri ("file:///etc/passwd");
1965 EEL_CHECK_INTEGER_RESULT (g_hash_table_size (directories), 1);
1967 nautilus_directory_file_monitor_add
1968 (directory, &data_dummy,
1969 TRUE, TRUE, 0, NULL, NULL);
1971 got_metadata_flag = FALSE;
1973 nautilus_directory_call_when_ready (directory, NAUTILUS_FILE_ATTRIBUTE_METADATA, TRUE,
1974 got_metadata_callback, &data_dummy);
1976 while (!got_metadata_flag) {
1977 gtk_main_iteration ();
1980 nautilus_file_set_metadata (file, "test", "default", "value");
1981 EEL_CHECK_STRING_RESULT (nautilus_file_get_metadata (file, "test", "default"), "value");
1983 nautilus_file_set_boolean_metadata (file, "test_boolean", TRUE, TRUE);
1984 EEL_CHECK_BOOLEAN_RESULT (nautilus_file_get_boolean_metadata (file, "test_boolean", TRUE), TRUE);
1985 nautilus_file_set_boolean_metadata (file, "test_boolean", TRUE, FALSE);
1986 EEL_CHECK_BOOLEAN_RESULT (nautilus_file_get_boolean_metadata (file, "test_boolean", TRUE), FALSE);
1987 EEL_CHECK_BOOLEAN_RESULT (nautilus_file_get_boolean_metadata (NULL, "test_boolean", TRUE), TRUE);
1989 nautilus_file_set_integer_metadata (file, "test_integer", 0, 17);
1990 EEL_CHECK_INTEGER_RESULT (nautilus_file_get_integer_metadata (file, "test_integer", 0), 17);
1991 nautilus_file_set_integer_metadata (file, "test_integer", 0, -1);
1992 EEL_CHECK_INTEGER_RESULT (nautilus_file_get_integer_metadata (file, "test_integer", 0), -1);
1993 nautilus_file_set_integer_metadata (file, "test_integer", 42, 42);
1994 EEL_CHECK_INTEGER_RESULT (nautilus_file_get_integer_metadata (file, "test_integer", 42), 42);
1995 EEL_CHECK_INTEGER_RESULT (nautilus_file_get_integer_metadata (NULL, "test_integer", 42), 42);
1996 EEL_CHECK_INTEGER_RESULT (nautilus_file_get_integer_metadata (file, "nonexistent_key", 42), 42);
1998 EEL_CHECK_BOOLEAN_RESULT (nautilus_directory_get_by_uri ("file:///etc") == directory, TRUE);
1999 nautilus_directory_unref (directory);
2001 EEL_CHECK_BOOLEAN_RESULT (nautilus_directory_get_by_uri ("file:///etc/") == directory, TRUE);
2002 nautilus_directory_unref (directory);
2004 EEL_CHECK_BOOLEAN_RESULT (nautilus_directory_get_by_uri ("file:///etc////") == directory, TRUE);
2005 nautilus_directory_unref (directory);
2007 nautilus_file_unref (file);
2009 nautilus_directory_file_monitor_remove (directory, &data_dummy);
2011 nautilus_directory_unref (directory);
2013 while (g_hash_table_size (directories) != 0) {
2014 gtk_main_iteration ();
2017 EEL_CHECK_INTEGER_RESULT (g_hash_table_size (directories), 0);
2019 directory = nautilus_directory_get_by_uri ("file:///etc");
2021 got_metadata_flag = FALSE;
2022 nautilus_directory_call_when_ready (directory, NAUTILUS_FILE_ATTRIBUTE_METADATA, TRUE,
2023 got_metadata_callback, &data_dummy);
2025 while (!got_metadata_flag) {
2026 gtk_main_iteration ();
2029 EEL_CHECK_BOOLEAN_RESULT (nautilus_directory_is_metadata_read (directory), TRUE);
2031 got_files_flag = FALSE;
2033 nautilus_directory_call_when_ready (directory,
2034 NAUTILUS_FILE_ATTRIBUTE_INFO |
2035 NAUTILUS_FILE_ATTRIBUTE_DEEP_COUNTS,
2036 TRUE,
2037 got_files_callback, &data_dummy);
2039 while (!got_files_flag) {
2040 gtk_main_iteration ();
2043 EEL_CHECK_BOOLEAN_RESULT (directory->details->file_list == NULL, TRUE);
2045 EEL_CHECK_INTEGER_RESULT (g_hash_table_size (directories), 1);
2047 file = nautilus_file_get_by_uri ("file:///etc/passwd");
2049 EEL_CHECK_STRING_RESULT (nautilus_file_get_metadata (file, "test", "default"), "value");
2051 nautilus_file_unref (file);
2053 nautilus_directory_unref (directory);
2055 EEL_CHECK_INTEGER_RESULT (g_hash_table_size (directories), 0);
2058 #endif /* !NAUTILUS_OMIT_SELF_CHECK */