nautilus-3.6.3/libnautilus-private/nautilus-directory.c

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 */