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