nautilus-3.6.3/src/nautilus-properties-window.c

No issues found

   1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
   2 
   3 /* fm-properties-window.c - window that lets user modify file properties
   4 
   5    Copyright (C) 2000 Eazel, Inc.
   6 
   7    The Gnome Library is free software; you can redistribute it and/or
   8    modify it under the terms of the GNU Library 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    The Gnome Library 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    Library General Public License for more details.
  16 
  17    You should have received a copy of the GNU Library General Public
  18    License along with the Gnome Library; see the file COPYING.LIB.  If not,
  19    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
  20    Boston, MA 02111-1307, USA.
  21 
  22    Authors: Darin Adler <darin@bentspoon.com>
  23 */
  24 
  25 #include <config.h>
  26 
  27 #include "nautilus-properties-window.h"
  28 
  29 #include "nautilus-desktop-item-properties.h"
  30 #include "nautilus-error-reporting.h"
  31 #include "nautilus-mime-actions.h"
  32 
  33 #include <gtk/gtk.h>
  34 #include <gdk/gdkkeysyms.h>
  35 #include <glib/gi18n.h>
  36 #include <string.h>
  37 #include <sys/stat.h>
  38 #include <cairo.h>
  39 
  40 #define GNOME_DESKTOP_USE_UNSTABLE_API
  41 #include <libgnome-desktop/gnome-desktop-thumbnail.h>
  42 
  43 #include <eel/eel-accessibility.h>
  44 #include <eel/eel-glib-extensions.h>
  45 #include <eel/eel-gnome-extensions.h>
  46 #include <eel/eel-gtk-extensions.h>
  47 #include <eel/eel-stock-dialogs.h>
  48 #include <eel/eel-string.h>
  49 #include <eel/eel-vfs-extensions.h>
  50 
  51 #include <libnautilus-extension/nautilus-property-page-provider.h>
  52 #include <libnautilus-private/nautilus-entry.h>
  53 #include <libnautilus-private/nautilus-file-attributes.h>
  54 #include <libnautilus-private/nautilus-file-operations.h>
  55 #include <libnautilus-private/nautilus-desktop-icon-file.h>
  56 #include <libnautilus-private/nautilus-global-preferences.h>
  57 #include <libnautilus-private/nautilus-link.h>
  58 #include <libnautilus-private/nautilus-metadata.h>
  59 #include <libnautilus-private/nautilus-mime-application-chooser.h>
  60 #include <libnautilus-private/nautilus-module.h>
  61 
  62 #if HAVE_SYS_VFS_H
  63 #include <sys/vfs.h>
  64 #elif HAVE_SYS_MOUNT_H
  65 #if HAVE_SYS_PARAM_H
  66 #include <sys/param.h>
  67 #endif
  68 #include <sys/mount.h>
  69 #endif
  70 
  71 #define UNKNOWN_FILL_R  0.5333333333333333
  72 #define UNKNOWN_FILL_G  0.5411764705882353
  73 #define UNKNOWN_FILL_B  0.5215686274509804
  74 
  75 #define USED_FILL_R  0.4470588235294118
  76 #define USED_FILL_G  0.6235294117647059
  77 #define USED_FILL_B  0.8117647058823529
  78 
  79 #define FREE_FILL_R  0.9333333333333333
  80 #define FREE_FILL_G  0.9333333333333333
  81 #define FREE_FILL_B  0.9254901960784314
  82 
  83 
  84 #define PREVIEW_IMAGE_WIDTH 96
  85 
  86 #define ROW_PAD 6
  87 
  88 static GHashTable *windows;
  89 static GHashTable *pending_lists;
  90 
  91 struct NautilusPropertiesWindowDetails {	
  92 	GList *original_files;
  93 	GList *target_files;
  94 	
  95 	GtkNotebook *notebook;
  96 	
  97 	GtkGrid *basic_grid;
  98 
  99 	GtkWidget *icon_button;
 100 	GtkWidget *icon_image;
 101 	GtkWidget *icon_chooser;
 102 
 103 	GtkLabel *name_label;
 104 	GtkWidget *name_field;
 105 	unsigned int name_row;
 106 	char *pending_name;
 107 
 108 	GtkLabel *directory_contents_title_field;
 109 	GtkLabel *directory_contents_value_field;
 110 	GtkWidget *directory_contents_spinner;
 111 	guint update_directory_contents_timeout_id;
 112 	guint update_files_timeout_id;
 113 
 114 	NautilusFile *group_change_file;
 115 	char         *group_change_group;
 116 	unsigned int  group_change_timeout;
 117 	NautilusFile *owner_change_file;
 118 	char         *owner_change_owner;
 119 	unsigned int  owner_change_timeout;
 120 
 121 	GList *permission_buttons;
 122 	GList *permission_combos;
 123 	GList *change_permission_combos;
 124 	GHashTable *initial_permissions;
 125 	gboolean has_recursive_apply;
 126 
 127 	GList *value_fields;
 128 
 129 	GList *mime_list;
 130 
 131 	gboolean deep_count_finished;
 132 	GList *deep_count_files;
 133 	guint deep_count_spinner_timeout_id;
 134 
 135 	guint total_count;
 136 	goffset total_size;
 137 
 138 	guint long_operation_underway;
 139 
 140 	GList *changed_files;
 141 
 142 	guint64 volume_capacity;
 143 	guint64 volume_free;
 144 	guint64 volume_used;
 145 
 146 	GdkRGBA used_color;
 147 	GdkRGBA free_color;
 148 	GdkRGBA unknown_color;
 149 	GdkRGBA used_stroke_color;
 150 	GdkRGBA free_stroke_color;
 151 	GdkRGBA unknown_stroke_color;
 152 };
 153 
 154 enum {
 155 	COLUMN_NAME,
 156 	COLUMN_VALUE,
 157 	COLUMN_USE_ORIGINAL,
 158 	COLUMN_ID,
 159 	NUM_COLUMNS
 160 };
 161 
 162 typedef struct {
 163 	GList *original_files;
 164 	GList *target_files;
 165 	GtkWidget *parent_widget;
 166 	char *startup_id;
 167 	char *pending_key;
 168 	GHashTable *pending_files;
 169 } StartupData;
 170 
 171 /* drag and drop definitions */
 172 
 173 enum {
 174 	TARGET_URI_LIST,
 175 	TARGET_GNOME_URI_LIST,
 176 };
 177 
 178 static const GtkTargetEntry target_table[] = {
 179 	{ "text/uri-list",  0, TARGET_URI_LIST },
 180 	{ "x-special/gnome-icon-list",  0, TARGET_GNOME_URI_LIST },
 181 };
 182 
 183 #define DIRECTORY_CONTENTS_UPDATE_INTERVAL	200 /* milliseconds */
 184 #define FILES_UPDATE_INTERVAL			200 /* milliseconds */
 185 
 186 /*
 187  * A timeout before changes through the user/group combo box will be applied.
 188  * When quickly changing owner/groups (i.e. by keyboard or scroll wheel),
 189  * this ensures that the GUI doesn't end up unresponsive.
 190  *
 191  * Both combos react on changes by scheduling a new change and unscheduling
 192  * or cancelling old pending changes.
 193  */
 194 #define CHOWN_CHGRP_TIMEOUT			300 /* milliseconds */
 195 
 196 static void schedule_directory_contents_update    (NautilusPropertiesWindow *window);
 197 static void directory_contents_value_field_update (NautilusPropertiesWindow *window);
 198 static void file_changed_callback                 (NautilusFile       *file,
 199 						   gpointer            user_data);
 200 static void permission_button_update              (NautilusPropertiesWindow *window,
 201 						   GtkToggleButton    *button);
 202 static void permission_combo_update               (NautilusPropertiesWindow *window,
 203 						   GtkComboBox        *combo);
 204 static void value_field_update                    (NautilusPropertiesWindow *window,
 205 						   GtkLabel           *field);
 206 static void properties_window_update              (NautilusPropertiesWindow *window,
 207 						   GList              *files);
 208 static void is_directory_ready_callback           (NautilusFile       *file,
 209 						   gpointer            data);
 210 static void cancel_group_change_callback          (NautilusPropertiesWindow *window);
 211 static void cancel_owner_change_callback          (NautilusPropertiesWindow *window);
 212 static void parent_widget_destroyed_callback      (GtkWidget          *widget,
 213 						   gpointer            callback_data);
 214 static void select_image_button_callback          (GtkWidget          *widget,
 215 						   NautilusPropertiesWindow *properties_window);
 216 static void set_icon                              (const char         *icon_path,
 217 						   NautilusPropertiesWindow *properties_window);
 218 static void remove_pending                        (StartupData        *data,
 219 						   gboolean            cancel_call_when_ready,
 220 						   gboolean            cancel_timed_wait,
 221 						   gboolean            cancel_destroy_handler);
 222 static void append_extension_pages                (NautilusPropertiesWindow *window);
 223 
 224 static gboolean name_field_focus_out              (NautilusEntry *name_field,
 225 						   GdkEventFocus *event,
 226 						   gpointer callback_data);
 227 static void name_field_activate                   (NautilusEntry *name_field,
 228 						   gpointer callback_data);
 229 static GtkLabel *attach_ellipsizing_value_label   (GtkGrid *grid,
 230 						   GtkWidget *sibling,
 231 						   const char *initial_text);
 232 						   
 233 static GtkWidget* create_pie_widget 		  (NautilusPropertiesWindow *window);
 234 
 235 G_DEFINE_TYPE (NautilusPropertiesWindow, nautilus_properties_window, GTK_TYPE_DIALOG);
 236 
 237 static gboolean
 238 is_multi_file_window (NautilusPropertiesWindow *window)
 239 {
 240 	GList *l;
 241 	int count;
 242 	
 243 	count = 0;
 244 	
 245 	for (l = window->details->original_files; l != NULL; l = l->next) {
 246 		if (!nautilus_file_is_gone (NAUTILUS_FILE (l->data))) {			
 247 			count++;
 248 			if (count > 1) {
 249 				return TRUE;
 250 			}	
 251 		}
 252 	}
 253 
 254 	return FALSE;
 255 }
 256 
 257 static int
 258 get_not_gone_original_file_count (NautilusPropertiesWindow *window)
 259 {
 260 	GList *l;
 261 	int count;
 262 
 263 	count = 0;
 264 
 265 	for (l = window->details->original_files; l != NULL; l = l->next) {
 266 		if (!nautilus_file_is_gone (NAUTILUS_FILE (l->data))) {
 267 			count++;
 268 		}
 269 	}
 270 
 271 	return count;
 272 }
 273 
 274 static NautilusFile *
 275 get_original_file (NautilusPropertiesWindow *window) 
 276 {
 277 	g_return_val_if_fail (!is_multi_file_window (window), NULL);
 278 
 279 	if (window->details->original_files == NULL) {
 280 		return NULL;
 281 	}
 282 
 283 	return NAUTILUS_FILE (window->details->original_files->data);
 284 }
 285 
 286 static NautilusFile *
 287 get_target_file_for_original_file (NautilusFile *file)
 288 {
 289 	NautilusFile *target_file;
 290 	GFile *location;
 291 	char *uri_to_display;
 292 	NautilusDesktopLink *link;
 293 
 294 	target_file = NULL;
 295 	if (NAUTILUS_IS_DESKTOP_ICON_FILE (file)) {
 296 		link = nautilus_desktop_icon_file_get_link (NAUTILUS_DESKTOP_ICON_FILE (file));
 297 
 298 		if (link != NULL) {
 299 			/* map to linked URI for these types of links */
 300 			location = nautilus_desktop_link_get_activation_location (link);
 301 			if (location) {
 302 				target_file = nautilus_file_get (location);
 303 				g_object_unref (location);
 304 			}
 305 			
 306 			g_object_unref (link);
 307 		}
 308         } else {
 309 		uri_to_display = nautilus_file_get_activation_uri (file);
 310 		if (uri_to_display != NULL) {
 311 			target_file = nautilus_file_get_by_uri (uri_to_display);
 312 			g_free (uri_to_display);
 313 		}
 314 	}
 315 	
 316 	if (target_file != NULL) {
 317 		return target_file;
 318 	}
 319 
 320 	/* Ref passed-in file here since we've decided to use it. */
 321 	nautilus_file_ref (file);
 322 	return file;
 323 }
 324 
 325 static NautilusFile *
 326 get_target_file (NautilusPropertiesWindow *window)
 327 {
 328 	return NAUTILUS_FILE (window->details->target_files->data);
 329 }
 330 
 331 static void
 332 add_prompt (GtkWidget *vbox, const char *prompt_text, gboolean pack_at_start)
 333 {
 334 	GtkWidget *prompt;
 335 
 336 	prompt = gtk_label_new (prompt_text);
 337    	gtk_label_set_justify (GTK_LABEL (prompt), GTK_JUSTIFY_LEFT);
 338 	gtk_label_set_line_wrap (GTK_LABEL (prompt), TRUE);
 339 	gtk_widget_show (prompt);
 340 	if (pack_at_start) {
 341 		gtk_box_pack_start (GTK_BOX (vbox), prompt, FALSE, FALSE, 0);
 342 	} else {
 343 		gtk_box_pack_end (GTK_BOX (vbox), prompt, FALSE, FALSE, 0);
 344 	}
 345 }
 346 
 347 static void
 348 add_prompt_and_separator (GtkWidget *vbox, const char *prompt_text)
 349 {
 350 	GtkWidget *separator_line;
 351 
 352 	add_prompt (vbox, prompt_text, FALSE);
 353 
 354 	separator_line = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL);
 355   	gtk_widget_show (separator_line);
 356   	gtk_box_pack_end (GTK_BOX (vbox), separator_line, TRUE, TRUE, 2*ROW_PAD);
 357 }
 358 
 359 static void
 360 get_image_for_properties_window (NautilusPropertiesWindow *window,
 361 				 char **icon_name,
 362 				 GdkPixbuf **icon_pixbuf)
 363 {
 364 	NautilusIconInfo *icon, *new_icon;
 365 	GList *l;
 366 	
 367 	icon = NULL;
 368 	for (l = window->details->original_files; l != NULL; l = l->next) {
 369 		NautilusFile *file;
 370 		
 371 		file = NAUTILUS_FILE (l->data);
 372 		
 373 		if (!icon) {
 374 			icon = nautilus_file_get_icon (file, NAUTILUS_ICON_SIZE_STANDARD, NAUTILUS_FILE_ICON_FLAGS_USE_THUMBNAILS | NAUTILUS_FILE_ICON_FLAGS_IGNORE_VISITING);
 375 		} else {
 376 			new_icon = nautilus_file_get_icon (file, NAUTILUS_ICON_SIZE_STANDARD, NAUTILUS_FILE_ICON_FLAGS_USE_THUMBNAILS | NAUTILUS_FILE_ICON_FLAGS_IGNORE_VISITING);
 377 			if (!new_icon || new_icon != icon) {
 378 				g_object_unref (icon);
 379 				g_object_unref (new_icon);
 380 				icon = NULL;
 381 				break;
 382 			}
 383 			g_object_unref (new_icon);
 384 		}
 385 	}
 386 
 387 	if (!icon) {
 388 		icon = nautilus_icon_info_lookup_from_name ("text-x-generic", NAUTILUS_ICON_SIZE_STANDARD);
 389 	}
 390 
 391 	if (icon_name != NULL) {
 392 		*icon_name = g_strdup (nautilus_icon_info_get_used_name (icon));
 393 	}
 394 
 395 	if (icon_pixbuf != NULL) {
 396 		*icon_pixbuf = nautilus_icon_info_get_pixbuf_at_size (icon, NAUTILUS_ICON_SIZE_STANDARD);
 397 	}
 398 
 399 	g_object_unref (icon);
 400 }
 401 
 402 
 403 static void
 404 update_properties_window_icon (GtkImage *image)
 405 {
 406 	NautilusPropertiesWindow *window;
 407 	GdkPixbuf *pixbuf;
 408 	char *name;
 409 
 410 	window = g_object_get_data (G_OBJECT (image), "properties_window");
 411 	
 412 	get_image_for_properties_window (window, &name, &pixbuf);
 413 
 414 	if (name != NULL) {
 415 		gtk_window_set_icon_name (GTK_WINDOW (window), name);
 416 	} else {
 417 		gtk_window_set_icon (GTK_WINDOW (window), pixbuf);
 418 	}
 419 
 420 	gtk_image_set_from_pixbuf (image, pixbuf);
 421 
 422 	g_free (name);
 423 	g_object_unref (pixbuf);
 424 }
 425 
 426 /* utility to test if a uri refers to a local image */
 427 static gboolean
 428 uri_is_local_image (const char *uri)
 429 {
 430 	GdkPixbuf *pixbuf;
 431 	char *image_path;
 432 	
 433 	image_path = g_filename_from_uri (uri, NULL, NULL);
 434 	if (image_path == NULL) {
 435 		return FALSE;
 436 	}
 437 
 438 	pixbuf = gdk_pixbuf_new_from_file (image_path, NULL);
 439 	g_free (image_path);
 440 	
 441 	if (pixbuf == NULL) {
 442 		return FALSE;
 443 	}
 444 	g_object_unref (pixbuf);
 445 	return TRUE;
 446 }
 447 
 448 
 449 static void
 450 reset_icon (NautilusPropertiesWindow *properties_window)
 451 {
 452 	GList *l;
 453 
 454 	for (l = properties_window->details->original_files; l != NULL; l = l->next) {
 455 		NautilusFile *file;
 456 		
 457 		file = NAUTILUS_FILE (l->data);
 458 		
 459 		nautilus_file_set_metadata (file,
 460 					    NAUTILUS_METADATA_KEY_ICON_SCALE,
 461 					    NULL, NULL);
 462 		nautilus_file_set_metadata (file,
 463 					    NAUTILUS_METADATA_KEY_CUSTOM_ICON,
 464 					    NULL, NULL);
 465 	}
 466 }
 467 
 468 
 469 static void  
 470 nautilus_properties_window_drag_data_received (GtkWidget *widget, GdkDragContext *context,
 471 					       int x, int y,
 472 					       GtkSelectionData *selection_data,
 473 					       guint info, guint time)
 474 {
 475 	char **uris;
 476 	gboolean exactly_one;
 477 	GtkImage *image;
 478  	GtkWindow *window; 
 479 
 480 	image = GTK_IMAGE (widget);
 481  	window = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (image)));
 482 
 483 	uris = g_strsplit ((const gchar *) gtk_selection_data_get_data (selection_data), "\r\n", 0);
 484 	exactly_one = uris[0] != NULL && (uris[1] == NULL || uris[1][0] == '\0');
 485 
 486 
 487 	if (!exactly_one) {
 488 		eel_show_error_dialog
 489 			(_("You cannot assign more than one custom icon at a time!"),
 490 			 _("Please drag just one image to set a custom icon."), 
 491 			 window);
 492 	} else {		
 493 		if (uri_is_local_image (uris[0])) {			
 494 			set_icon (uris[0], NAUTILUS_PROPERTIES_WINDOW (window));
 495 		} else {
 496 			GFile *f;
 497 
 498 			f = g_file_new_for_uri (uris[0]);
 499 			if (!g_file_is_native (f)) {
 500 				eel_show_error_dialog
 501 					(_("The file that you dropped is not local."),
 502 					 _("You can only use local images as custom icons."), 
 503 					 window);
 504 				
 505 			} else {
 506 				eel_show_error_dialog
 507 					(_("The file that you dropped is not an image."),
 508 					 _("You can only use local images as custom icons."),
 509 					 window);
 510 			}
 511 			g_object_unref (f);
 512 		}		
 513 	}
 514 	g_strfreev (uris);
 515 }
 516 
 517 static GtkWidget *
 518 create_image_widget (NautilusPropertiesWindow *window,
 519 		     gboolean is_customizable)
 520 {
 521  	GtkWidget *button;
 522 	GtkWidget *image;
 523 	GdkPixbuf *pixbuf;
 524 	
 525 	get_image_for_properties_window (window, NULL, &pixbuf);
 526 
 527 	image = gtk_image_new ();
 528 	gtk_widget_show (image);
 529 
 530 	button = NULL;
 531 	if (is_customizable) {
 532 		button = gtk_button_new ();
 533 		gtk_container_add (GTK_CONTAINER (button), image);
 534 
 535 		/* prepare the image to receive dropped objects to assign custom images */
 536 		gtk_drag_dest_set (GTK_WIDGET (image),
 537 				   GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_HIGHLIGHT | GTK_DEST_DEFAULT_DROP, 
 538 				   target_table, G_N_ELEMENTS (target_table),
 539 				   GDK_ACTION_COPY | GDK_ACTION_MOVE);
 540 
 541 		g_signal_connect (image, "drag_data_received",
 542 				  G_CALLBACK (nautilus_properties_window_drag_data_received), NULL);
 543 		g_signal_connect (button, "clicked",
 544 				  G_CALLBACK (select_image_button_callback), window);
 545 	}
 546 
 547 	gtk_image_set_from_pixbuf (GTK_IMAGE (image), pixbuf);
 548 
 549 	g_object_unref (pixbuf);
 550 
 551 	g_object_set_data (G_OBJECT (image), "properties_window", window);
 552 
 553 	window->details->icon_image = image;
 554 	window->details->icon_button = button;
 555 
 556 	return button != NULL ? button : image;
 557 }
 558 
 559 static void
 560 set_name_field (NautilusPropertiesWindow *window,
 561 		const gchar *original_name,
 562 		const gchar *name)
 563 {
 564 	gboolean new_widget;
 565 	gboolean use_label;
 566 
 567 	/* There are four cases here:
 568 	 * 1) Changing the text of a label
 569 	 * 2) Changing the text of an entry
 570 	 * 3) Creating label (potentially replacing entry)
 571 	 * 4) Creating entry (potentially replacing label)
 572 	 */
 573 	use_label = is_multi_file_window (window) || !nautilus_file_can_rename (get_original_file (window));
 574 	new_widget = !window->details->name_field || (use_label ? NAUTILUS_IS_ENTRY (window->details->name_field) : GTK_IS_LABEL (window->details->name_field));
 575 
 576 	if (new_widget) {
 577 		if (window->details->name_field) {
 578 			gtk_widget_destroy (window->details->name_field);
 579 		}
 580 
 581 		if (use_label) {
 582 			window->details->name_field = GTK_WIDGET 
 583 				(attach_ellipsizing_value_label (window->details->basic_grid,
 584 								 GTK_WIDGET (window->details->name_label),
 585 								 name));
 586 		} else {
 587 			window->details->name_field = nautilus_entry_new ();
 588 			gtk_entry_set_text (GTK_ENTRY (window->details->name_field), name);
 589 			gtk_widget_show (window->details->name_field);
 590 
 591 			gtk_grid_attach_next_to (window->details->basic_grid, window->details->name_field,
 592 						 GTK_WIDGET (window->details->name_label),
 593 						 GTK_POS_RIGHT, 1, 1);
 594 			gtk_label_set_mnemonic_widget (GTK_LABEL (window->details->name_label), window->details->name_field);
 595 
 596 			g_signal_connect_object (window->details->name_field, "focus_out_event",
 597 						 G_CALLBACK (name_field_focus_out), window, 0);
 598 			g_signal_connect_object (window->details->name_field, "activate",
 599 						 G_CALLBACK (name_field_activate), window, 0);
 600 		}
 601 
 602 		gtk_widget_show (window->details->name_field);
 603 	}
 604 	/* Only replace text if the file's name has changed. */ 
 605 	else if (original_name == NULL || strcmp (original_name, name) != 0) {
 606 		
 607 		if (use_label) {
 608 			gtk_label_set_text (GTK_LABEL (window->details->name_field), name);
 609 		} else {
 610 			/* Only reset the text if it's different from what is
 611 			 * currently showing. This causes minimal ripples (e.g.
 612 			 * selection change).
 613 			 */
 614 			gchar *displayed_name = gtk_editable_get_chars (GTK_EDITABLE (window->details->name_field), 0, -1);
 615 			if (strcmp (displayed_name, name) != 0) {
 616 				gtk_entry_set_text (GTK_ENTRY (window->details->name_field), name);
 617 			}
 618 			g_free (displayed_name);
 619 		}
 620 	}
 621 }
 622 
 623 static void
 624 update_name_field (NautilusPropertiesWindow *window)
 625 {
 626 	NautilusFile *file;
 627 
 628 	gtk_label_set_text_with_mnemonic (window->details->name_label,
 629 					  ngettext ("_Name:", "_Names:",
 630 						    get_not_gone_original_file_count (window)));
 631 
 632 	if (is_multi_file_window (window)) {
 633 		/* Multifile property dialog, show all names */
 634 		GString *str;
 635 		char *name;
 636 		gboolean first;
 637 		GList *l;
 638 		
 639 		str = g_string_new ("");
 640 
 641 		first = TRUE;
 642 
 643 		for (l = window->details->target_files; l != NULL; l = l->next) {
 644 			file = NAUTILUS_FILE (l->data);
 645 
 646 			if (!nautilus_file_is_gone (file)) {
 647 				if (!first) {
 648 					g_string_append (str, ", ");
 649 				} 
 650 				first = FALSE;
 651 				
 652 				name = nautilus_file_get_display_name (file);
 653 				g_string_append (str, name);
 654 				g_free (name);
 655 			}
 656 		}
 657 		set_name_field (window, NULL, str->str);
 658 		g_string_free (str, TRUE);
 659 	} else {
 660 		const char *original_name = NULL;
 661 		char *current_name;
 662 
 663 		file = get_original_file (window);
 664 
 665 		if (file == NULL || nautilus_file_is_gone (file)) {
 666 			current_name = g_strdup ("");
 667 		} else {
 668 			current_name = nautilus_file_get_display_name (file);
 669 		}
 670 
 671 		/* If the file name has changed since the original name was stored,
 672 		 * update the text in the text field, possibly (deliberately) clobbering
 673 		 * an edit in progress. If the name hasn't changed (but some other
 674 		 * aspect of the file might have), then don't clobber changes.
 675 		 */
 676 		if (window->details->name_field) {
 677 			original_name = (const char *) g_object_get_data (G_OBJECT (window->details->name_field), "original_name");
 678 		}
 679 
 680 		set_name_field (window, original_name, current_name);
 681 
 682 		if (original_name == NULL || 
 683 		    g_strcmp0 (original_name, current_name) != 0) {
 684 			g_object_set_data_full (G_OBJECT (window->details->name_field),
 685 						"original_name",
 686 						current_name,
 687 						g_free);
 688 		} else {
 689 			g_free (current_name);
 690 		}
 691 	}
 692 }
 693 
 694 static void
 695 name_field_restore_original_name (NautilusEntry *name_field)
 696 {
 697 	const char *original_name;
 698 	char *displayed_name;
 699 
 700 	original_name = (const char *) g_object_get_data (G_OBJECT (name_field),
 701 							  "original_name");
 702 
 703 	if (!original_name) {
 704 		return;
 705 	}
 706 
 707 	displayed_name = gtk_editable_get_chars (GTK_EDITABLE (name_field), 0, -1);
 708 
 709 	if (strcmp (original_name, displayed_name) != 0) {
 710 		gtk_entry_set_text (GTK_ENTRY (name_field), original_name);
 711 	}
 712 	nautilus_entry_select_all (name_field);
 713 
 714 	g_free (displayed_name);
 715 }
 716 
 717 static void
 718 rename_callback (NautilusFile *file, GFile *res_loc, GError *error, gpointer callback_data)
 719 {
 720 	NautilusPropertiesWindow *window;
 721 
 722 	window = NAUTILUS_PROPERTIES_WINDOW (callback_data);
 723 
 724 	/* Complain to user if rename failed. */
 725 	if (error != NULL) {
 726 		nautilus_report_error_renaming_file (file, 
 727 						     window->details->pending_name, 
 728 						     error,
 729 						     GTK_WINDOW (window));
 730 		if (window->details->name_field != NULL) {
 731 			name_field_restore_original_name (NAUTILUS_ENTRY (window->details->name_field));
 732 		}
 733 	}
 734 
 735 	g_object_unref (window);
 736 }
 737 
 738 static void
 739 set_pending_name (NautilusPropertiesWindow *window, const char *name)
 740 {
 741 	g_free (window->details->pending_name);
 742 	window->details->pending_name = g_strdup (name);
 743 }
 744 
 745 static void
 746 name_field_done_editing (NautilusEntry *name_field, NautilusPropertiesWindow *window)
 747 {
 748 	NautilusFile *file;
 749 	char *new_name;
 750 	const char *original_name;
 751 	
 752 	g_return_if_fail (NAUTILUS_IS_ENTRY (name_field));
 753 
 754 	/* Don't apply if the dialog has more than one file */
 755 	if (is_multi_file_window (window)) {
 756 		return;
 757 	}	
 758 
 759 	file = get_original_file (window);
 760 
 761 	/* This gets called when the window is closed, which might be
 762 	 * caused by the file having been deleted.
 763 	 */
 764 	if (file == NULL || nautilus_file_is_gone  (file)) {
 765 		return;
 766 	}
 767 
 768 	new_name = gtk_editable_get_chars (GTK_EDITABLE (name_field), 0, -1);
 769 
 770 	/* Special case: silently revert text if new text is empty. */
 771 	if (strlen (new_name) == 0) {
 772 		name_field_restore_original_name (NAUTILUS_ENTRY (name_field));
 773 	} else {
 774 		original_name = (const char *) g_object_get_data (G_OBJECT (window->details->name_field),
 775 								  "original_name");
 776 		/* Don't rename if not changed since we read the display name.
 777 		   This is needed so that we don't save the display name to the
 778 		   file when nothing is changed */
 779 		if (strcmp (new_name, original_name) != 0) {		
 780 			set_pending_name (window, new_name);
 781 			g_object_ref (window);
 782 			nautilus_file_rename (file, new_name,
 783 					      rename_callback, window);
 784 		}
 785 	}
 786 
 787 	g_free (new_name);
 788 }
 789 
 790 static gboolean
 791 name_field_focus_out (NautilusEntry *name_field,
 792 		      GdkEventFocus *event,
 793 		      gpointer callback_data)
 794 {
 795 	g_assert (NAUTILUS_IS_PROPERTIES_WINDOW (callback_data));
 796 
 797 	if (gtk_widget_get_sensitive (GTK_WIDGET (name_field))) {
 798 		name_field_done_editing (name_field, NAUTILUS_PROPERTIES_WINDOW (callback_data));
 799 	}
 800 
 801 	return FALSE;
 802 }
 803 
 804 static void
 805 name_field_activate (NautilusEntry *name_field, gpointer callback_data)
 806 {
 807 	g_assert (NAUTILUS_IS_ENTRY (name_field));
 808 	g_assert (NAUTILUS_IS_PROPERTIES_WINDOW (callback_data));
 809 
 810 	/* Accept changes. */
 811 	name_field_done_editing (name_field, NAUTILUS_PROPERTIES_WINDOW (callback_data));
 812 
 813 	nautilus_entry_select_all_at_idle (name_field);
 814 }
 815 
 816 static void
 817 update_properties_window_title (NautilusPropertiesWindow *window)
 818 {
 819 	char *name, *title;
 820 	NautilusFile *file;
 821 
 822 	g_return_if_fail (GTK_IS_WINDOW (window));
 823 
 824 	title = g_strdup_printf (_("Properties"));
 825 
 826 	if (!is_multi_file_window (window)) {
 827 		file = get_original_file (window);
 828 
 829 		if (file != NULL) {
 830 			g_free (title);
 831 			name = nautilus_file_get_display_name (file);
 832 			title = g_strdup_printf (_("%s Properties"), name);
 833 			g_free (name);
 834 		}
 835 	}
 836 	
 837   	gtk_window_set_title (GTK_WINDOW (window), title);
 838 
 839 	g_free (title);
 840 }
 841 
 842 static void
 843 clear_extension_pages (NautilusPropertiesWindow *window)
 844 {
 845 	int i;
 846 	int num_pages;
 847 	GtkWidget *page;
 848 
 849 	num_pages = gtk_notebook_get_n_pages
 850 				(GTK_NOTEBOOK (window->details->notebook));
 851 
 852 	for (i = 0; i < num_pages; i++) {
 853 		page = gtk_notebook_get_nth_page
 854 				(GTK_NOTEBOOK (window->details->notebook), i);
 855 
 856 		if (g_object_get_data (G_OBJECT (page), "is-extension-page")) {
 857 			gtk_notebook_remove_page
 858 				(GTK_NOTEBOOK (window->details->notebook), i);
 859 			num_pages--;
 860 			i--;
 861 		}
 862 	}
 863 }
 864 
 865 static void
 866 refresh_extension_pages (NautilusPropertiesWindow *window)
 867 {
 868 	clear_extension_pages (window);
 869 	append_extension_pages (window);	
 870 }
 871 
 872 static void
 873 remove_from_dialog (NautilusPropertiesWindow *window,
 874 		    NautilusFile *file)
 875 {
 876 	int index;
 877 	GList *original_link;
 878 	GList *target_link;
 879 	NautilusFile *original_file;
 880 	NautilusFile *target_file;
 881 
 882 	index = g_list_index (window->details->target_files, file);
 883 	if (index == -1) {
 884 		index = g_list_index (window->details->original_files, file);
 885 		g_return_if_fail (index != -1);
 886 	}	
 887 
 888 	original_link = g_list_nth (window->details->original_files, index);
 889 	target_link = g_list_nth (window->details->target_files, index);
 890 
 891 	g_return_if_fail (original_link && target_link);
 892 
 893 	original_file = NAUTILUS_FILE (original_link->data);
 894 	target_file = NAUTILUS_FILE (target_link->data);
 895 	
 896 	window->details->original_files = g_list_remove_link (window->details->original_files, original_link);
 897 	g_list_free (original_link);
 898 
 899 	window->details->target_files = g_list_remove_link (window->details->target_files, target_link);
 900 	g_list_free (target_link);
 901 
 902 	g_hash_table_remove (window->details->initial_permissions, target_file);
 903 
 904 	g_signal_handlers_disconnect_by_func (original_file,
 905 					      G_CALLBACK (file_changed_callback),
 906 					      window);
 907 	g_signal_handlers_disconnect_by_func (target_file,
 908 					      G_CALLBACK (file_changed_callback),
 909 					      window);
 910 
 911 	nautilus_file_monitor_remove (original_file, &window->details->original_files);
 912 	nautilus_file_monitor_remove (target_file, &window->details->target_files);
 913 
 914 	nautilus_file_unref (original_file);
 915 	nautilus_file_unref (target_file);
 916 	
 917 }
 918 
 919 static gboolean
 920 mime_list_equal (GList *a, GList *b)
 921 {
 922 	while (a && b) {
 923 		if (strcmp (a->data, b->data)) {
 924 			return FALSE;
 925 		}	
 926 		a = a->next;
 927 		b = b->next;
 928 	}
 929 
 930 	return (a == b);
 931 }
 932 
 933 static GList *
 934 get_mime_list (NautilusPropertiesWindow *window)
 935 {
 936 	GList *ret;
 937 	GList *l;
 938 	
 939 	ret = NULL;
 940 	for (l = window->details->target_files; l != NULL; l = l->next) {
 941 		ret = g_list_append (ret, nautilus_file_get_mime_type (NAUTILUS_FILE (l->data)));
 942 	}
 943 	ret = g_list_reverse (ret);
 944 	return ret;
 945 }
 946 
 947 static gboolean
 948 start_spinner_callback (NautilusPropertiesWindow *window)
 949 {
 950 	gtk_widget_show (window->details->directory_contents_spinner);
 951 	gtk_spinner_start (GTK_SPINNER (window->details->directory_contents_spinner));
 952 	window->details->deep_count_spinner_timeout_id = 0;
 953 
 954 	return FALSE;
 955 }
 956 
 957 static void
 958 schedule_start_spinner (NautilusPropertiesWindow *window)
 959 {
 960 	if (window->details->deep_count_spinner_timeout_id == 0) {
 961 		window->details->deep_count_spinner_timeout_id
 962 			= g_timeout_add_seconds (1,
 963 						 (GSourceFunc)start_spinner_callback,
 964 						 window);
 965 	}
 966 }
 967 
 968 static void
 969 stop_spinner (NautilusPropertiesWindow *window)
 970 {
 971 	gtk_spinner_stop (GTK_SPINNER (window->details->directory_contents_spinner));
 972 	gtk_widget_hide (window->details->directory_contents_spinner);
 973 	if (window->details->deep_count_spinner_timeout_id > 0) {
 974 		g_source_remove (window->details->deep_count_spinner_timeout_id);
 975 		window->details->deep_count_spinner_timeout_id = 0;
 976 	}
 977 }
 978 
 979 static void
 980 stop_deep_count_for_file (NautilusPropertiesWindow *window,
 981 			  NautilusFile             *file)
 982 {
 983 	if (g_list_find (window->details->deep_count_files, file)) {
 984 		g_signal_handlers_disconnect_by_func (file,
 985 						      G_CALLBACK (schedule_directory_contents_update),
 986 						      window);
 987 		nautilus_file_unref (file);
 988 		window->details->deep_count_files = g_list_remove (window->details->deep_count_files, file);
 989 	}
 990 }
 991 
 992 static void
 993 start_deep_count_for_file (NautilusPropertiesWindow *window,
 994 			   NautilusFile             *file)
 995 {
 996 	if (!g_list_find (window->details->deep_count_files, file)) {
 997 		nautilus_file_ref (file);
 998 		window->details->deep_count_files = g_list_prepend (window->details->deep_count_files, file);
 999 
1000 		nautilus_file_recompute_deep_counts (file);
1001 		if (!window->details->deep_count_finished) {
1002 			g_signal_connect_object (file,
1003 						 "updated_deep_count_in_progress",
1004 						 G_CALLBACK (schedule_directory_contents_update),
1005 						 window, G_CONNECT_SWAPPED);
1006 			schedule_start_spinner (window);
1007 		}
1008 	}
1009 }
1010 
1011 static void
1012 properties_window_update (NautilusPropertiesWindow *window, 
1013 			  GList *files)
1014 {
1015 	GList *l;
1016 	GList *mime_list;
1017 	GList *tmp;
1018 	NautilusFile *changed_file;
1019 	gboolean dirty_original = FALSE;
1020 	gboolean dirty_target = FALSE;
1021 
1022 	if (files == NULL) {
1023 		dirty_original = TRUE;
1024 		dirty_target = TRUE;
1025 	}
1026 
1027 	for (tmp = files; tmp != NULL; tmp = tmp->next) {
1028 		changed_file = NAUTILUS_FILE (tmp->data);
1029 
1030 		if (changed_file && nautilus_file_is_gone (changed_file)) {
1031 			/* Remove the file from the property dialog */
1032 			remove_from_dialog (window, changed_file);
1033 			changed_file = NULL;
1034 			
1035 			if (window->details->original_files == NULL) {
1036 				return;
1037 			}
1038 		}		
1039 		if (changed_file == NULL ||
1040 		    g_list_find (window->details->original_files, changed_file)) {
1041 			dirty_original = TRUE;
1042 		}
1043 		if (changed_file == NULL ||
1044 		    g_list_find (window->details->target_files, changed_file)) {
1045 			dirty_target = TRUE;
1046 		}
1047 		if (changed_file != NULL) {
1048 			start_deep_count_for_file (window, changed_file);
1049 		}
1050 	}
1051 
1052 	if (dirty_original) {
1053 		update_properties_window_title (window);
1054 		update_properties_window_icon (GTK_IMAGE (window->details->icon_image));
1055 
1056 		update_name_field (window);
1057 
1058 		/* If any of the value fields start to depend on the original
1059 		 * value, value_field_updates should be added here */
1060 	}
1061 
1062 	if (dirty_target) {
1063 		for (l = window->details->permission_buttons; l != NULL; l = l->next) {
1064 			permission_button_update (window, GTK_TOGGLE_BUTTON (l->data));
1065 		}
1066 		
1067 		for (l = window->details->permission_combos; l != NULL; l = l->next) {
1068 			permission_combo_update (window, GTK_COMBO_BOX (l->data));
1069 		}
1070 		
1071 		for (l = window->details->value_fields; l != NULL; l = l->next) {
1072 			value_field_update (window, GTK_LABEL (l->data));
1073 		}
1074 	}
1075 
1076 	mime_list = get_mime_list (window);
1077 
1078 	if (!window->details->mime_list) {
1079 		window->details->mime_list = mime_list;
1080 	} else {
1081 		if (!mime_list_equal (window->details->mime_list, mime_list)) {
1082 			refresh_extension_pages (window);			
1083 		}
1084 
1085 		g_list_free_full (window->details->mime_list, g_free);
1086 		window->details->mime_list = mime_list;
1087 	}
1088 }
1089 
1090 static gboolean
1091 update_files_callback (gpointer data)
1092 {
1093  	NautilusPropertiesWindow *window;
1094  
1095  	window = NAUTILUS_PROPERTIES_WINDOW (data);
1096  
1097 	window->details->update_files_timeout_id = 0;
1098 
1099 	properties_window_update (window, window->details->changed_files);
1100 	
1101 	if (window->details->original_files == NULL) {
1102 		/* Close the window if no files are left */
1103 		gtk_widget_destroy (GTK_WIDGET (window));
1104 	} else {
1105 		nautilus_file_list_free (window->details->changed_files);
1106 		window->details->changed_files = NULL;
1107 	}
1108 	
1109  	return FALSE;
1110  }
1111 
1112 static void
1113 schedule_files_update (NautilusPropertiesWindow *window)
1114  {
1115  	g_assert (NAUTILUS_IS_PROPERTIES_WINDOW (window));
1116  
1117 	if (window->details->update_files_timeout_id == 0) {
1118 		window->details->update_files_timeout_id
1119 			= g_timeout_add (FILES_UPDATE_INTERVAL,
1120 					 update_files_callback,
1121  					 window);
1122  	}
1123  }
1124 
1125 static gboolean
1126 file_list_attributes_identical (GList *file_list, const char *attribute_name)
1127 {
1128 	gboolean identical;
1129 	char *first_attr;
1130 	GList *l;
1131 	
1132 	first_attr = NULL;
1133 	identical = TRUE;
1134 	
1135 	for (l = file_list; l != NULL; l = l->next) {
1136 		NautilusFile *file;
1137 
1138 		file = NAUTILUS_FILE (l->data);
1139 	
1140 		if (nautilus_file_is_gone (file)) {
1141 			continue;
1142 		}
1143 
1144 		if (first_attr == NULL) {
1145 			first_attr = nautilus_file_get_string_attribute_with_default (file, attribute_name);
1146 		} else {
1147 			char *attr;
1148 			attr = nautilus_file_get_string_attribute_with_default (file, attribute_name);
1149 			if (strcmp (attr, first_attr)) {
1150 				identical = FALSE;
1151 				g_free (attr);
1152 				break;
1153 			}
1154 			g_free (attr);
1155 		}
1156 	}
1157 
1158 	g_free (first_attr);
1159 	return identical;
1160 }
1161 
1162 static char *
1163 file_list_get_string_attribute (GList *file_list, 
1164 				const char *attribute_name,
1165 				const char *inconsistent_value)
1166 {
1167 	if (file_list_attributes_identical (file_list, attribute_name)) {
1168 		GList *l;
1169 		
1170 		for (l = file_list; l != NULL; l = l->next) {
1171 			NautilusFile *file;
1172 			
1173 			file = NAUTILUS_FILE (l->data);
1174 			if (!nautilus_file_is_gone (file)) {
1175 				return nautilus_file_get_string_attribute_with_default
1176 					(file, 
1177 					 attribute_name);
1178 			}
1179 		}
1180 		return g_strdup (_("unknown"));
1181 	} else {
1182 		return g_strdup (inconsistent_value);
1183 	}
1184 }
1185 
1186 
1187 static gboolean
1188 file_list_all_directories (GList *file_list)
1189 {
1190 	GList *l;
1191 	for (l = file_list; l != NULL; l = l->next) {
1192 		if (!nautilus_file_is_directory (NAUTILUS_FILE (l->data))) {
1193 			return FALSE;
1194 		}
1195 	}
1196 	return TRUE;
1197 }
1198 
1199 static void
1200 value_field_update_internal (GtkLabel *label, 
1201 			     GList *file_list)
1202 {
1203 	const char *attribute_name;
1204 	char *attribute_value;
1205 	char *inconsistent_string;
1206 	char *mime_type, *tmp;
1207 
1208 	g_assert (GTK_IS_LABEL (label));
1209 
1210 	attribute_name = g_object_get_data (G_OBJECT (label), "file_attribute");
1211 	inconsistent_string = g_object_get_data (G_OBJECT (label), "inconsistent_string");
1212 	attribute_value = file_list_get_string_attribute (file_list, 
1213 							  attribute_name,
1214 							  inconsistent_string);
1215 	if (!strcmp (attribute_name, "detailed_type") && strcmp (attribute_value, inconsistent_string)) {
1216 		mime_type = file_list_get_string_attribute (file_list,
1217 							    "mime_type",
1218 							    inconsistent_string);
1219 		if (strcmp (mime_type, inconsistent_string)) {
1220 			tmp = attribute_value;
1221 			attribute_value = g_strdup_printf (C_("MIME type description (MIME type)", "%s (%s)"), attribute_value, mime_type);
1222 			g_free (tmp);
1223 		}
1224 		g_free (mime_type);
1225 	}
1226 
1227 	gtk_label_set_text (label, attribute_value);
1228 	g_free (attribute_value);
1229 }
1230 
1231 static void
1232 value_field_update (NautilusPropertiesWindow *window, GtkLabel *label)
1233 {
1234 	gboolean use_original;
1235 
1236 	use_original = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (label), "show_original"));
1237 
1238 	value_field_update_internal (label, 
1239 				     (use_original ?
1240 				      window->details->original_files : 
1241 				      window->details->target_files));
1242 }
1243 
1244 static GtkLabel *
1245 attach_label (GtkGrid *grid,
1246 	      GtkWidget *sibling,
1247 	      const char *initial_text,
1248 	      gboolean ellipsize_text,
1249 	      gboolean selectable,
1250 	      gboolean mnemonic)
1251 {
1252 	GtkWidget *label_field;
1253 
1254 	if (ellipsize_text) {
1255 		label_field = gtk_label_new (initial_text);
1256                 gtk_label_set_ellipsize (GTK_LABEL (label_field),
1257 					 PANGO_ELLIPSIZE_END);
1258 	} else if (mnemonic) {
1259 		label_field = gtk_label_new_with_mnemonic (initial_text);
1260 	} else {
1261 		label_field = gtk_label_new (initial_text);
1262 	}
1263 
1264 	if (selectable) {
1265 		gtk_label_set_selectable (GTK_LABEL (label_field), TRUE);
1266 	}
1267 
1268 	gtk_misc_set_alignment (GTK_MISC (label_field), 0, 0.5);
1269 	gtk_widget_show (label_field);
1270 
1271 	if (ellipsize_text) {
1272 		gtk_widget_set_hexpand (label_field, TRUE);
1273 	}
1274 
1275 	if (sibling != NULL) {
1276 		gtk_grid_attach_next_to (grid, label_field, sibling,
1277 					 GTK_POS_RIGHT, 1, 1);
1278 	} else {
1279 		gtk_container_add (GTK_CONTAINER (grid), label_field);
1280 	}
1281 
1282 	return GTK_LABEL (label_field);
1283 }	      
1284 
1285 static GtkLabel *
1286 attach_value_label (GtkGrid *grid,
1287 		    GtkWidget *sibling,
1288 		    const char *initial_text)
1289 {
1290 	return attach_label (grid, sibling, initial_text, FALSE, TRUE, FALSE);
1291 }
1292 
1293 static GtkLabel *
1294 attach_ellipsizing_value_label (GtkGrid *grid,
1295 				GtkWidget *sibling,
1296 				const char *initial_text)
1297 {
1298 	return attach_label (grid, sibling, initial_text, TRUE, TRUE, FALSE);
1299 }
1300 
1301 static GtkWidget*
1302 attach_value_field_internal (NautilusPropertiesWindow *window,
1303 			     GtkGrid *grid,
1304 			     GtkWidget *sibling,
1305 			     const char *file_attribute_name,
1306 			     const char *inconsistent_string,
1307 			     gboolean show_original,
1308 			     gboolean ellipsize_text)
1309 {
1310 	GtkLabel *value_field;
1311 
1312 	if (ellipsize_text) {
1313 		value_field = attach_ellipsizing_value_label (grid, sibling, "");
1314 	} else {
1315 		value_field = attach_value_label (grid, sibling, "");
1316 	}
1317 
1318   	/* Stash a copy of the file attribute name in this field for the callback's sake. */
1319 	g_object_set_data_full (G_OBJECT (value_field), "file_attribute",
1320 				g_strdup (file_attribute_name), g_free);
1321 
1322 	g_object_set_data_full (G_OBJECT (value_field), "inconsistent_string",
1323 				g_strdup (inconsistent_string), g_free);
1324 
1325 	g_object_set_data (G_OBJECT (value_field), "show_original", GINT_TO_POINTER (show_original));
1326 
1327 	window->details->value_fields = g_list_prepend (window->details->value_fields,
1328 							value_field);
1329 	return GTK_WIDGET(value_field);
1330 }			     
1331 
1332 static GtkWidget*
1333 attach_value_field (NautilusPropertiesWindow *window,
1334 		    GtkGrid *grid,
1335 		    GtkWidget *sibling,
1336 		    const char *file_attribute_name,
1337 		    const char *inconsistent_string,
1338 		    gboolean show_original)
1339 {
1340 	return attach_value_field_internal (window, 
1341 					    grid, sibling,
1342 					    file_attribute_name, 
1343 					    inconsistent_string,
1344 					    show_original,
1345 					    FALSE);
1346 }
1347 
1348 static GtkWidget*
1349 attach_ellipsizing_value_field (NautilusPropertiesWindow *window,
1350 				GtkGrid *grid,
1351 				GtkWidget *sibling,
1352 		    		const char *file_attribute_name,
1353 				const char *inconsistent_string,
1354 				gboolean show_original)
1355 {
1356 	return attach_value_field_internal (window,
1357 					    grid, sibling, 
1358 					    file_attribute_name, 
1359 					    inconsistent_string, 
1360 					    show_original,
1361 					    TRUE);
1362 }
1363 
1364 static void
1365 group_change_callback (NautilusFile *file,
1366 		       GFile *res_loc,
1367 		       GError *error,
1368 		       NautilusPropertiesWindow *window)
1369 {
1370 	char *group;
1371 
1372 	g_assert (NAUTILUS_IS_PROPERTIES_WINDOW (window));
1373 	g_assert (window->details->group_change_file == file);
1374 
1375 	group = window->details->group_change_group;
1376 	g_assert (group != NULL);
1377 
1378 	/* Report the error if it's an error. */
1379 	eel_timed_wait_stop ((EelCancelCallback) cancel_group_change_callback, window);
1380 	nautilus_report_error_setting_group (file, error, GTK_WINDOW (window));
1381 
1382 	nautilus_file_unref (file);
1383 	g_free (group);
1384 
1385 	window->details->group_change_file = NULL;
1386 	window->details->group_change_group = NULL;
1387 	g_object_unref (G_OBJECT (window));
1388 }
1389 
1390 static void
1391 cancel_group_change_callback (NautilusPropertiesWindow *window)
1392 {
1393 	NautilusFile *file;
1394 	char *group;
1395 
1396 	file = window->details->group_change_file;
1397 	g_assert (NAUTILUS_IS_FILE (file));
1398 
1399 	group = window->details->group_change_group;
1400 	g_assert (group != NULL);
1401 
1402 	nautilus_file_cancel (file, (NautilusFileOperationCallback) group_change_callback, window);
1403 
1404 	g_free (group);
1405 	nautilus_file_unref (file);
1406 
1407 	window->details->group_change_file = NULL;
1408 	window->details->group_change_group = NULL;
1409 	g_object_unref (window);
1410 }
1411 
1412 static gboolean
1413 schedule_group_change_timeout (NautilusPropertiesWindow *window)
1414 {
1415 	NautilusFile *file;
1416 	char *group;
1417 
1418 	g_assert (NAUTILUS_IS_PROPERTIES_WINDOW (window));
1419 
1420 	file = window->details->group_change_file;
1421 	g_assert (NAUTILUS_IS_FILE (file));
1422 
1423 	group = window->details->group_change_group;
1424 	g_assert (group != NULL);
1425 
1426 	eel_timed_wait_start
1427 		((EelCancelCallback) cancel_group_change_callback,
1428 		 window,
1429 		 _("Cancel Group Change?"),
1430 		 GTK_WINDOW (window));
1431 
1432 	nautilus_file_set_group
1433 		(file,  group,
1434 		 (NautilusFileOperationCallback) group_change_callback, window);
1435 
1436 	window->details->group_change_timeout = 0;
1437 	return FALSE;
1438 }
1439 
1440 static void
1441 schedule_group_change (NautilusPropertiesWindow *window,
1442 		       NautilusFile       *file,
1443 		       const char         *group)
1444 {
1445 	g_assert (NAUTILUS_IS_PROPERTIES_WINDOW (window));
1446 	g_assert (window->details->group_change_group == NULL);
1447 	g_assert (window->details->group_change_file == NULL);
1448 	g_assert (NAUTILUS_IS_FILE (file));
1449 
1450 	window->details->group_change_file = nautilus_file_ref (file);
1451 	window->details->group_change_group = g_strdup (group);
1452 	g_object_ref (G_OBJECT (window));
1453 	window->details->group_change_timeout =
1454 		g_timeout_add (CHOWN_CHGRP_TIMEOUT,
1455 			       (GSourceFunc) schedule_group_change_timeout,
1456 			       window);
1457 }
1458 
1459 static void
1460 unschedule_or_cancel_group_change (NautilusPropertiesWindow *window)
1461 {
1462 	NautilusFile *file;
1463 	char *group;
1464 
1465 	g_assert (NAUTILUS_IS_PROPERTIES_WINDOW (window));
1466 
1467 	file = window->details->group_change_file;
1468 	group = window->details->group_change_group;
1469 
1470 	g_assert ((file == NULL && group == NULL) ||
1471 		  (file != NULL && group != NULL));
1472 
1473 	if (file != NULL) {
1474 		g_assert (NAUTILUS_IS_FILE (file));
1475 
1476 		if (window->details->group_change_timeout == 0) {
1477 			nautilus_file_cancel (file,
1478 					      (NautilusFileOperationCallback) group_change_callback, window);
1479 			eel_timed_wait_stop ((EelCancelCallback) cancel_group_change_callback, window);
1480 		}
1481 
1482 		nautilus_file_unref (file);
1483 		g_free (group);
1484 
1485 		window->details->group_change_file = NULL;
1486 		window->details->group_change_group = NULL;
1487 		g_object_unref (G_OBJECT (window));
1488 	}
1489 
1490 	if (window->details->group_change_timeout > 0) {
1491 		g_assert (file != NULL);
1492 		g_source_remove (window->details->group_change_timeout);
1493 		window->details->group_change_timeout = 0;
1494 	}
1495 }
1496 
1497 static void
1498 changed_group_callback (GtkComboBox *combo_box, NautilusFile *file)
1499 {
1500 	NautilusPropertiesWindow *window;
1501 	char *group;
1502 	char *cur_group;
1503 
1504 	g_assert (GTK_IS_COMBO_BOX (combo_box));
1505 	g_assert (NAUTILUS_IS_FILE (file));
1506 
1507 	group = gtk_combo_box_text_get_active_text (GTK_COMBO_BOX_TEXT (combo_box));
1508 	cur_group = nautilus_file_get_group_name (file);
1509 
1510 	if (group != NULL && strcmp (group, cur_group) != 0) {
1511 		/* Try to change file group. If this fails, complain to user. */
1512 		window = NAUTILUS_PROPERTIES_WINDOW (gtk_widget_get_ancestor (GTK_WIDGET (combo_box), GTK_TYPE_WINDOW));
1513 
1514 		unschedule_or_cancel_group_change (window);
1515 		schedule_group_change (window, file, group);
1516 	}
1517 	g_free (group);
1518 	g_free (cur_group);
1519 }
1520 
1521 /* checks whether the given column at the first level
1522  * of model has the specified entries in the given order. */
1523 static gboolean
1524 tree_model_entries_equal (GtkTreeModel *model,
1525 			  unsigned int  column,
1526 			  GList        *entries)
1527 {
1528 	GtkTreeIter iter;
1529 	gboolean empty_model;
1530 
1531 	g_assert (GTK_IS_TREE_MODEL (model));
1532 	g_assert (gtk_tree_model_get_column_type (model, column) == G_TYPE_STRING);
1533 
1534 	empty_model = !gtk_tree_model_get_iter_first (model, &iter);
1535 
1536 	if (!empty_model && entries != NULL) {
1537 		GList *l;
1538 
1539 		l = entries;
1540 
1541 		do {
1542 			char *val;
1543 
1544 			gtk_tree_model_get (model, &iter,
1545 					    column, &val,
1546 					    -1);
1547 			if ((val == NULL && l->data != NULL) ||
1548 			    (val != NULL && l->data == NULL) ||
1549 			    (val != NULL && strcmp (val, l->data))) {
1550 				g_free (val);
1551 				return FALSE;
1552 			}
1553 
1554 			g_free (val);
1555 			l = l->next;
1556 		} while (gtk_tree_model_iter_next (model, &iter));
1557 
1558 		return l == NULL;
1559 	} else {
1560 		return (empty_model && entries == NULL) ||
1561 		       (!empty_model && entries != NULL);
1562 	}
1563 }
1564 
1565 static char *
1566 combo_box_get_active_entry (GtkComboBox *combo_box,
1567 			    unsigned int column)
1568 {
1569 	GtkTreeModel *model;
1570 	GtkTreeIter iter;
1571 	char *val;
1572 
1573 	g_assert (GTK_IS_COMBO_BOX (combo_box));
1574 
1575 	if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (combo_box), &iter)) {
1576 		model = gtk_combo_box_get_model (combo_box);
1577 		g_assert (GTK_IS_TREE_MODEL (model));
1578 
1579 		gtk_tree_model_get (model, &iter,
1580 				    column, &val,
1581 				    -1);
1582 		return val;
1583 	}
1584 
1585 	return NULL;
1586 }
1587 
1588 /* returns the index of the given entry in the the given column
1589  * at the first level of model. Returns -1 if entry can't be found
1590  * or entry is NULL.
1591  * */
1592 static int
1593 tree_model_get_entry_index (GtkTreeModel *model,
1594 			    unsigned int  column,
1595 			    const char   *entry)
1596 {
1597 	GtkTreeIter iter;
1598 	int index;
1599 	gboolean empty_model;
1600 
1601 	g_assert (GTK_IS_TREE_MODEL (model));
1602 	g_assert (gtk_tree_model_get_column_type (model, column) == G_TYPE_STRING);
1603 
1604 	empty_model = !gtk_tree_model_get_iter_first (model, &iter);
1605 	if (!empty_model && entry != NULL) {
1606 		index = 0;
1607 
1608 		do {
1609 			char *val;
1610 
1611 			gtk_tree_model_get (model, &iter,
1612 					    column, &val,
1613 					    -1);
1614 			if (val != NULL && !strcmp (val, entry)) {
1615 				g_free (val);
1616 				return index;
1617 			}
1618 
1619 			g_free (val);
1620 			index++;
1621 		} while (gtk_tree_model_iter_next (model, &iter));
1622 	}
1623 
1624 	return -1;
1625 }
1626 
1627 
1628 static void
1629 synch_groups_combo_box (GtkComboBox *combo_box, NautilusFile *file)
1630 {
1631 	GList *groups;
1632 	GList *node;
1633 	GtkTreeModel *model;
1634 	GtkListStore *store;
1635 	const char *group_name;
1636 	char *current_group_name;
1637 	int group_index;
1638 	int current_group_index;
1639 
1640 	g_assert (GTK_IS_COMBO_BOX (combo_box));
1641 	g_assert (NAUTILUS_IS_FILE (file));
1642 
1643 	if (nautilus_file_is_gone (file)) {
1644 		return;
1645 	}
1646 
1647 	groups = nautilus_file_get_settable_group_names (file);
1648 
1649 	model = gtk_combo_box_get_model (combo_box);
1650 	store = GTK_LIST_STORE (model);
1651 	g_assert (GTK_IS_LIST_STORE (model));
1652 
1653 	if (!tree_model_entries_equal (model, 0, groups)) {
1654 		/* Clear the contents of ComboBox in a wacky way because there
1655 		 * is no function to clear all items and also no function to obtain
1656 		 * the number of items in a combobox.
1657 		 */
1658 		gtk_list_store_clear (store);
1659 
1660 		for (node = groups, group_index = 0; node != NULL; node = node->next, ++group_index) {
1661 			group_name = (const char *)node->data;
1662 			gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_box), group_name);
1663 		}
1664 	}
1665 
1666 	current_group_name = nautilus_file_get_group_name (file);
1667 	current_group_index = tree_model_get_entry_index (model, 0, current_group_name);
1668 
1669 	/* If current group wasn't in list, we prepend it (with a separator). 
1670 	 * This can happen if the current group is an id with no matching
1671 	 * group in the groups file.
1672 	 */
1673 	if (current_group_index < 0 && current_group_name != NULL) {
1674 		if (groups != NULL) {
1675 			/* add separator */
1676 			gtk_combo_box_text_prepend_text (GTK_COMBO_BOX_TEXT (combo_box), "-");
1677 		}
1678 
1679 		gtk_combo_box_text_prepend_text (GTK_COMBO_BOX_TEXT (combo_box), current_group_name);
1680 		current_group_index = 0;
1681 	}
1682 	gtk_combo_box_set_active (combo_box, current_group_index);
1683 
1684 	g_free (current_group_name);
1685 	g_list_free_full (groups, g_free);
1686 }
1687 
1688 static gboolean
1689 combo_box_row_separator_func (GtkTreeModel *model,
1690 			      GtkTreeIter  *iter,
1691 			      gpointer      data)
1692 {
1693   	gchar *text;
1694 	gboolean ret;
1695 
1696   	gtk_tree_model_get (model, iter, 0, &text, -1);
1697 
1698 	if (text == NULL) {
1699 		return FALSE;
1700 	}
1701 
1702   	if (strcmp (text, "-") == 0) {
1703     		ret = TRUE;
1704 	} else {
1705 		ret = FALSE;
1706 	}
1707 	
1708   	g_free (text);
1709   	return ret;
1710 }
1711 
1712 static GtkComboBox *
1713 attach_combo_box (GtkGrid *grid,
1714 		  GtkWidget *sibling,
1715 		  gboolean two_columns)
1716 {
1717 	GtkWidget *combo_box;
1718 	GtkWidget *aligner;
1719 
1720 	if (!two_columns) {
1721 		combo_box = gtk_combo_box_text_new ();
1722 	} else {
1723 		GtkTreeModel *model;
1724 		GtkCellRenderer *renderer;
1725 
1726 		model = GTK_TREE_MODEL (gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_STRING));
1727 		combo_box = gtk_combo_box_new_with_model (model);
1728 		g_object_unref (G_OBJECT (model));
1729 
1730 		renderer = gtk_cell_renderer_text_new ();
1731 		gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo_box), renderer, TRUE);
1732 		gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (combo_box), renderer,
1733 					       "text", 0);
1734 		
1735 	}
1736 	gtk_widget_show (combo_box);
1737 
1738   	gtk_combo_box_set_row_separator_func (GTK_COMBO_BOX (combo_box),
1739 					      combo_box_row_separator_func,
1740 					      NULL,
1741 					      NULL);
1742 
1743 	/* Put combo box in alignment to make it left-justified
1744 	 * but minimally sized.
1745 	 */
1746 	aligner = gtk_alignment_new (0, 0.5, 0, 0);
1747 	gtk_widget_show (aligner);
1748 
1749 	gtk_container_add (GTK_CONTAINER (aligner), combo_box);
1750 	gtk_grid_attach_next_to (grid, aligner, sibling,
1751 				 GTK_POS_RIGHT, 1, 1);
1752 
1753 	return GTK_COMBO_BOX (combo_box);
1754 }		    	
1755 
1756 static GtkComboBox*
1757 attach_group_combo_box (GtkGrid *grid,
1758 			GtkWidget *sibling,
1759 		        NautilusFile *file)
1760 {
1761 	GtkComboBox *combo_box;
1762 
1763 	combo_box = attach_combo_box (grid, sibling, FALSE);
1764 
1765 	synch_groups_combo_box (combo_box, file);
1766 
1767 	/* Connect to signal to update menu when file changes. */
1768 	g_signal_connect_object (file, "changed",
1769 				 G_CALLBACK (synch_groups_combo_box),
1770 				 combo_box, G_CONNECT_SWAPPED);
1771 	g_signal_connect_data (combo_box, "changed",
1772 			       G_CALLBACK (changed_group_callback),
1773 			       nautilus_file_ref (file),
1774 			       (GClosureNotify)nautilus_file_unref, 0);
1775 
1776 	return combo_box;
1777 }	
1778 
1779 static void
1780 owner_change_callback (NautilusFile *file,
1781                        GFile 	    *result_location,
1782 		       GError        *error,
1783 		       NautilusPropertiesWindow *window)
1784 {
1785 	char *owner;
1786 
1787 	g_assert (NAUTILUS_IS_PROPERTIES_WINDOW (window));
1788 	g_assert (window->details->owner_change_file == file);
1789 
1790 	owner = window->details->owner_change_owner;
1791 	g_assert (owner != NULL);
1792 
1793 	/* Report the error if it's an error. */
1794 	eel_timed_wait_stop ((EelCancelCallback) cancel_owner_change_callback, window);
1795 	nautilus_report_error_setting_owner (file, error, GTK_WINDOW (window));
1796 
1797 	nautilus_file_unref (file);
1798 	g_free (owner);
1799 
1800 	window->details->owner_change_file = NULL;
1801 	window->details->owner_change_owner = NULL;
1802 	g_object_unref (G_OBJECT (window));
1803 }
1804 
1805 static void
1806 cancel_owner_change_callback (NautilusPropertiesWindow *window)
1807 {
1808 	NautilusFile *file;
1809 	char *owner;
1810 
1811 	file = window->details->owner_change_file;
1812 	g_assert (NAUTILUS_IS_FILE (file));
1813 
1814 	owner = window->details->owner_change_owner;
1815 	g_assert (owner != NULL);
1816 
1817 	nautilus_file_cancel (file, (NautilusFileOperationCallback) owner_change_callback, window);
1818 
1819 	nautilus_file_unref (file);
1820 	g_free (owner);
1821 
1822 	window->details->owner_change_file = NULL;
1823 	window->details->owner_change_owner = NULL;
1824 	g_object_unref (window);
1825 }
1826 
1827 static gboolean
1828 schedule_owner_change_timeout (NautilusPropertiesWindow *window)
1829 {
1830 	NautilusFile *file;
1831 	char *owner;
1832 
1833 	g_assert (NAUTILUS_IS_PROPERTIES_WINDOW (window));
1834 
1835 	file = window->details->owner_change_file;
1836 	g_assert (NAUTILUS_IS_FILE (file));
1837 
1838 	owner = window->details->owner_change_owner;
1839 	g_assert (owner != NULL);
1840 
1841 	eel_timed_wait_start
1842 		((EelCancelCallback) cancel_owner_change_callback,
1843 		 window,
1844 		 _("Cancel Owner Change?"),
1845 		 GTK_WINDOW (window));
1846 
1847 	nautilus_file_set_owner
1848 		(file,  owner,
1849 		 (NautilusFileOperationCallback) owner_change_callback, window);
1850 
1851 	window->details->owner_change_timeout = 0;
1852 	return FALSE;
1853 }
1854 
1855 static void
1856 schedule_owner_change (NautilusPropertiesWindow *window,
1857 		       NautilusFile       *file,
1858 		       const char         *owner)
1859 {
1860 	g_assert (NAUTILUS_IS_PROPERTIES_WINDOW (window));
1861 	g_assert (window->details->owner_change_owner == NULL);
1862 	g_assert (window->details->owner_change_file == NULL);
1863 	g_assert (NAUTILUS_IS_FILE (file));
1864 
1865 	window->details->owner_change_file = nautilus_file_ref (file);
1866 	window->details->owner_change_owner = g_strdup (owner);
1867 	g_object_ref (G_OBJECT (window));
1868 	window->details->owner_change_timeout =
1869 		g_timeout_add (CHOWN_CHGRP_TIMEOUT,
1870 			       (GSourceFunc) schedule_owner_change_timeout,
1871 			       window);
1872 }
1873 
1874 static void
1875 unschedule_or_cancel_owner_change (NautilusPropertiesWindow *window)
1876 {
1877 	NautilusFile *file;
1878 	char *owner;
1879 
1880 	g_assert (NAUTILUS_IS_PROPERTIES_WINDOW (window));
1881 
1882 	file = window->details->owner_change_file;
1883 	owner = window->details->owner_change_owner;
1884 
1885 	g_assert ((file == NULL && owner == NULL) ||
1886 		  (file != NULL && owner != NULL));
1887 
1888 	if (file != NULL) {
1889 		g_assert (NAUTILUS_IS_FILE (file));
1890 
1891 		if (window->details->owner_change_timeout == 0) {
1892 			nautilus_file_cancel (file,
1893 					      (NautilusFileOperationCallback) owner_change_callback, window);
1894 			eel_timed_wait_stop ((EelCancelCallback) cancel_owner_change_callback, window);
1895 		}
1896 
1897 		nautilus_file_unref (file);
1898 		g_free (owner);
1899 
1900 		window->details->owner_change_file = NULL;
1901 		window->details->owner_change_owner = NULL;
1902 		g_object_unref (G_OBJECT (window));
1903 	}
1904 
1905 	if (window->details->owner_change_timeout > 0) {
1906 		g_assert (file != NULL);
1907 		g_source_remove (window->details->owner_change_timeout);
1908 		window->details->owner_change_timeout = 0;
1909 	}
1910 }
1911 
1912 static void
1913 changed_owner_callback (GtkComboBox *combo_box, NautilusFile* file)
1914 {
1915 	NautilusPropertiesWindow *window;
1916 	char *owner_text;
1917 	char **name_array;
1918 	char *new_owner;
1919 	char *cur_owner;
1920 
1921 	g_assert (GTK_IS_COMBO_BOX (combo_box));
1922 	g_assert (NAUTILUS_IS_FILE (file));
1923 
1924 	owner_text = combo_box_get_active_entry (combo_box, 0);
1925         if (! owner_text)
1926 	    return;
1927     	name_array = g_strsplit (owner_text, " - ", 2);
1928 	new_owner = name_array[0];
1929 	g_free (owner_text);
1930 	cur_owner = nautilus_file_get_owner_name (file);
1931 
1932 	if (strcmp (new_owner, cur_owner) != 0) {
1933 		/* Try to change file owner. If this fails, complain to user. */
1934 		window = NAUTILUS_PROPERTIES_WINDOW (gtk_widget_get_ancestor (GTK_WIDGET (combo_box), GTK_TYPE_WINDOW));
1935 
1936 		unschedule_or_cancel_owner_change (window);
1937 		schedule_owner_change (window, file, new_owner);
1938 	}
1939 	g_strfreev (name_array);
1940 	g_free (cur_owner);
1941 }
1942 
1943 static void
1944 synch_user_menu (GtkComboBox *combo_box, NautilusFile *file)
1945 {
1946 	GList *users;
1947 	GList *node;
1948 	GtkTreeModel *model;
1949 	GtkListStore *store;
1950 	GtkTreeIter iter;
1951 	char *user_name;
1952 	char *owner_name;
1953 	int user_index;
1954 	int owner_index;
1955 	char **name_array;
1956 	char *combo_text;
1957 
1958 	g_assert (GTK_IS_COMBO_BOX (combo_box));
1959 	g_assert (NAUTILUS_IS_FILE (file));
1960 
1961 	if (nautilus_file_is_gone (file)) {
1962 		return;
1963 	}
1964 
1965 	users = nautilus_get_user_names ();
1966 
1967 	model = gtk_combo_box_get_model (combo_box);
1968 	store = GTK_LIST_STORE (model);
1969 	g_assert (GTK_IS_LIST_STORE (model));
1970 
1971 	if (!tree_model_entries_equal (model, 1, users)) {
1972 		/* Clear the contents of ComboBox in a wacky way because there
1973 		 * is no function to clear all items and also no function to obtain
1974 		 * the number of items in a combobox.
1975 		 */
1976 		gtk_list_store_clear (store);
1977 
1978 		for (node = users, user_index = 0; node != NULL; node = node->next, ++user_index) {
1979 			user_name = (char *)node->data;
1980 
1981 			name_array = g_strsplit (user_name, "\n", 2);
1982 			if (name_array[1] != NULL) {
1983 				combo_text = g_strdup_printf ("%s - %s", name_array[0], name_array[1]);
1984 			} else {
1985 				combo_text = g_strdup (name_array[0]);
1986 			}
1987 
1988 			gtk_list_store_append (store, &iter);
1989 			gtk_list_store_set (store, &iter,
1990 					    0, combo_text,
1991 					    1, user_name,
1992 					    -1);
1993 
1994 			g_strfreev (name_array);
1995 			g_free (combo_text);
1996 		}
1997 	}
1998 
1999 	owner_name = nautilus_file_get_string_attribute (file, "owner");
2000 	owner_index = tree_model_get_entry_index (model, 0, owner_name);
2001 
2002 	/* If owner wasn't in list, we prepend it (with a separator). 
2003 	 * This can happen if the owner is an id with no matching
2004 	 * identifier in the passwords file.
2005 	 */
2006 	if (owner_index < 0 && owner_name != NULL) {
2007 		if (users != NULL) {
2008 			/* add separator */
2009 			gtk_list_store_prepend (store, &iter);
2010 			gtk_list_store_set (store, &iter,
2011 					    0, "-",
2012 					    1, NULL,
2013 					    -1);
2014 		}
2015 
2016 		name_array = g_strsplit (owner_name, " - ", 2);
2017 		if (name_array[1] != NULL) {
2018 			user_name = g_strdup_printf ("%s\n%s", name_array[0], name_array[1]);
2019 		} else {
2020 			user_name = g_strdup (name_array[0]);
2021 		}
2022 		owner_index = 0;
2023 
2024 		gtk_list_store_prepend (store, &iter);
2025 		gtk_list_store_set (store, &iter,
2026 				    0, owner_name,
2027 				    1, user_name,
2028 				    -1);
2029 
2030 		g_free (user_name);
2031 		g_strfreev (name_array);
2032 	}
2033 
2034 	gtk_combo_box_set_active (combo_box, owner_index);
2035 
2036 	g_free (owner_name);
2037 	g_list_free_full (users, g_free);
2038 }	
2039 
2040 static GtkComboBox*
2041 attach_owner_combo_box (GtkGrid *grid,
2042 		        GtkWidget *sibling,
2043 		        NautilusFile *file)
2044 {
2045 	GtkComboBox *combo_box;
2046 
2047 	combo_box = attach_combo_box (grid, sibling, TRUE);
2048 
2049 	synch_user_menu (combo_box, file);
2050 
2051 	/* Connect to signal to update menu when file changes. */
2052 	g_signal_connect_object (file, "changed",
2053 				 G_CALLBACK (synch_user_menu),
2054 				 combo_box, G_CONNECT_SWAPPED);	
2055 	g_signal_connect_data (combo_box, "changed",
2056 			       G_CALLBACK (changed_owner_callback),
2057 			       nautilus_file_ref (file),
2058 			       (GClosureNotify)nautilus_file_unref, 0);
2059 
2060 	return combo_box;
2061 }
2062 
2063 static gboolean
2064 file_has_prefix (NautilusFile *file,
2065 		 GList *prefix_candidates)
2066 {
2067 	GList *p;
2068 	GFile *location, *candidate_location;
2069 
2070 	location = nautilus_file_get_location (file);
2071 
2072 	for (p = prefix_candidates; p != NULL; p = p->next) {
2073 		if (file == p->data) {
2074 			continue;
2075 		}
2076 
2077 		candidate_location = nautilus_file_get_location (NAUTILUS_FILE (p->data));
2078 		if (g_file_has_prefix (location, candidate_location)) {
2079 			g_object_unref (location);
2080 			g_object_unref (candidate_location);
2081 			return TRUE;
2082 		}
2083 		g_object_unref (candidate_location);
2084 	}
2085 
2086 	g_object_unref (location);
2087 
2088 	return FALSE;
2089 }
2090 
2091 static void
2092 directory_contents_value_field_update (NautilusPropertiesWindow *window)
2093 {
2094 	NautilusRequestStatus file_status;
2095 	char *text, *temp;
2096 	guint directory_count;
2097 	guint file_count;
2098 	guint total_count;
2099 	guint unreadable_directory_count;
2100 	goffset total_size;
2101 	gboolean used_two_lines;
2102 	NautilusFile *file;
2103 	GList *l;
2104 	guint file_unreadable;
2105 	goffset file_size;
2106 	gboolean deep_count_active;
2107 
2108 	g_assert (NAUTILUS_IS_PROPERTIES_WINDOW (window));
2109 
2110 	total_count = window->details->total_count;
2111 	total_size = window->details->total_size;
2112 	unreadable_directory_count = FALSE;
2113 
2114 	for (l = window->details->target_files; l; l = l->next) {
2115 		file = NAUTILUS_FILE (l->data);
2116 
2117 		if (file_has_prefix (file, window->details->target_files)) {
2118 			/* don't count nested files twice */
2119 			continue;
2120 		}
2121 
2122 		if (nautilus_file_is_directory (file)) {
2123 			file_status = nautilus_file_get_deep_counts (file,
2124 								     &directory_count,
2125 								     &file_count,
2126 								     &file_unreadable,
2127 								     &file_size,
2128 								     TRUE);
2129 			total_count += (file_count + directory_count);
2130 			total_size += file_size;
2131 
2132 			if (file_unreadable) {
2133 				unreadable_directory_count = TRUE;
2134 			}
2135 
2136 			if (file_status == NAUTILUS_REQUEST_DONE) {
2137 				stop_deep_count_for_file (window, file);
2138 			}
2139 		} else {
2140 			++total_count;
2141 			total_size += nautilus_file_get_size (file);
2142 		}
2143 	}
2144 
2145 	deep_count_active = (g_list_length (window->details->deep_count_files) > 0);
2146 	/* If we've already displayed the total once, don't do another visible
2147 	 * count-up if the deep_count happens to get invalidated.
2148 	 * But still display the new total, since it might have changed.
2149 	 */
2150 	if (window->details->deep_count_finished && deep_count_active) {
2151 		return;
2152 	}
2153 
2154 	text = NULL;
2155 	used_two_lines = FALSE;
2156 	
2157 	if (total_count == 0) {
2158 		if (!deep_count_active) {
2159 			if (unreadable_directory_count == 0) {
2160 				text = g_strdup (_("nothing"));
2161 			} else {
2162 				text = g_strdup (_("unreadable"));
2163 			}
2164 		} else {
2165 			text = g_strdup ("...");
2166 		}
2167 	} else {
2168 		char *size_str;
2169 		size_str = g_format_size (total_size);
2170 		text = g_strdup_printf (ngettext("%'d item, with size %s",
2171 						 "%'d items, totalling %s",
2172 						 total_count),
2173 					total_count, size_str);
2174 		g_free (size_str);
2175 
2176 		if (unreadable_directory_count != 0) {
2177 			temp = text;
2178 			text = g_strconcat (temp, "\n",
2179 					    _("(some contents unreadable)"),
2180 					    NULL);
2181 			g_free (temp);
2182 			used_two_lines = TRUE;
2183 		}
2184 	}
2185 
2186 	gtk_label_set_text (window->details->directory_contents_value_field,
2187 			    text);
2188 	g_free (text);
2189 
2190 	/* Also set the title field here, with a trailing carriage return &
2191 	 * space if the value field has two lines. This is a hack to get the
2192 	 * "Contents:" title to line up with the first line of the
2193 	 * 2-line value. Maybe there's a better way to do this, but I
2194 	 * couldn't think of one.
2195 	 */
2196 	text = g_strdup (_("Contents:"));
2197 	if (used_two_lines) {
2198 		temp = text;
2199 		text = g_strconcat (temp, "\n ", NULL);
2200 		g_free (temp);
2201 	}
2202 	gtk_label_set_text (window->details->directory_contents_title_field,
2203 			    text);
2204 	g_free (text);
2205 
2206 	if (!deep_count_active) {
2207 		window->details->deep_count_finished = TRUE;
2208 		stop_spinner (window);
2209 	}
2210 }
2211 
2212 static gboolean
2213 update_directory_contents_callback (gpointer data)
2214 {
2215 	NautilusPropertiesWindow *window;
2216 
2217 	window = NAUTILUS_PROPERTIES_WINDOW (data);
2218 
2219 	window->details->update_directory_contents_timeout_id = 0;
2220 	directory_contents_value_field_update (window);
2221 
2222 	return FALSE;
2223 }
2224 
2225 static void
2226 schedule_directory_contents_update (NautilusPropertiesWindow *window)
2227 {
2228 	g_assert (NAUTILUS_IS_PROPERTIES_WINDOW (window));
2229 
2230 	if (window->details->update_directory_contents_timeout_id == 0) {
2231 		window->details->update_directory_contents_timeout_id
2232 			= g_timeout_add (DIRECTORY_CONTENTS_UPDATE_INTERVAL,
2233 					 update_directory_contents_callback,
2234 					 window);
2235 	}
2236 }
2237 
2238 static GtkLabel *
2239 attach_directory_contents_value_field (NautilusPropertiesWindow *window,
2240 				       GtkGrid *grid,
2241 				       GtkWidget *sibling)
2242 {
2243 	GtkLabel *value_field;
2244 
2245 	value_field = attach_value_label (grid, sibling, "");
2246 
2247 	g_assert (window->details->directory_contents_value_field == NULL);
2248 	window->details->directory_contents_value_field = value_field;
2249 
2250 	gtk_label_set_line_wrap (value_field, TRUE);
2251 
2252 	return value_field;
2253 }
2254 
2255 static GtkLabel *
2256 attach_title_field (GtkGrid *grid,
2257 		    const char *title)
2258 {
2259 	return attach_label (grid, NULL, title, FALSE, FALSE, TRUE);
2260 }		      
2261 
2262 #define INCONSISTENT_STATE_STRING \
2263 	"\xE2\x80\x92"
2264 
2265 static void
2266 append_title_value_pair (NautilusPropertiesWindow *window,
2267 			 GtkGrid *grid,
2268 			 const char *title, 
2269  			 const char *file_attribute_name,
2270 			 const char *inconsistent_state,
2271 			 gboolean show_original)
2272 {
2273 	GtkLabel *title_label;
2274 	GtkWidget *value;
2275 
2276 	title_label = attach_title_field (grid, title);
2277 	value = attach_value_field (window, grid, GTK_WIDGET (title_label),
2278 				    file_attribute_name,
2279 				    inconsistent_state,
2280 				    show_original); 
2281 	gtk_label_set_mnemonic_widget (title_label, value);
2282 }
2283 
2284 static void
2285 append_title_and_ellipsizing_value (NautilusPropertiesWindow *window,
2286 				    GtkGrid *grid,
2287 				    const char *title,
2288 				    const char *file_attribute_name,
2289 				    const char *inconsistent_state,
2290 				    gboolean show_original)
2291 {
2292 	GtkLabel *title_label;
2293 	GtkWidget *value;
2294 
2295 	title_label = attach_title_field (grid, title);
2296 	value = attach_ellipsizing_value_field (window, grid,
2297 						GTK_WIDGET (title_label),
2298 						file_attribute_name,
2299 						inconsistent_state,
2300 						show_original);
2301 	gtk_label_set_mnemonic_widget (title_label, value);
2302 }
2303 
2304 static void
2305 append_directory_contents_fields (NautilusPropertiesWindow *window,
2306 				  GtkGrid *grid)
2307 {
2308 	GtkLabel *title_field, *value_field;
2309 	GList *l;
2310 
2311 	title_field = attach_title_field (grid, "");
2312 	window->details->directory_contents_title_field = title_field;
2313 	gtk_label_set_line_wrap (title_field, TRUE);
2314 
2315 	value_field = attach_directory_contents_value_field (window, grid, GTK_WIDGET (title_field));
2316 
2317 	window->details->directory_contents_spinner = gtk_spinner_new ();
2318 
2319 	gtk_grid_attach_next_to (grid,
2320 				 window->details->directory_contents_spinner,
2321 				 GTK_WIDGET (value_field),
2322 				 GTK_POS_RIGHT,
2323 				 1, 1);
2324 
2325 	for (l = window->details->target_files; l; l = l->next) {
2326 		NautilusFile *file;
2327 
2328 		file = NAUTILUS_FILE (l->data);
2329 		start_deep_count_for_file (window, file);
2330 	}
2331 
2332 	/* Fill in the initial value. */
2333 	directory_contents_value_field_update (window);
2334 
2335 	gtk_label_set_mnemonic_widget (title_field, GTK_WIDGET(value_field));
2336 }
2337 
2338 static GtkWidget *
2339 create_page_with_hbox (GtkNotebook *notebook,
2340 		       const char *title,
2341 		       const char *help_uri)
2342 {
2343 	GtkWidget *hbox;
2344 
2345 	g_assert (GTK_IS_NOTEBOOK (notebook));
2346 	g_assert (title != NULL);
2347 
2348 	hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
2349 	gtk_widget_show (hbox);
2350 	gtk_container_set_border_width (GTK_CONTAINER (hbox), 12);
2351 	gtk_box_set_spacing (GTK_BOX (hbox), 12);
2352 	gtk_notebook_append_page (notebook, hbox, gtk_label_new (title));
2353 	g_object_set_data_full (G_OBJECT (hbox), "help-uri", g_strdup (help_uri), g_free);
2354 
2355 	return hbox;
2356 }
2357 
2358 static GtkWidget *
2359 create_page_with_vbox (GtkNotebook *notebook,
2360 		       const char *title,
2361 		       const char *help_uri)
2362 {
2363 	GtkWidget *vbox;
2364 
2365 	g_assert (GTK_IS_NOTEBOOK (notebook));
2366 	g_assert (title != NULL);
2367 
2368 	vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
2369 	gtk_widget_show (vbox);
2370 	gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
2371 	gtk_notebook_append_page (notebook, vbox, gtk_label_new (title));
2372 	g_object_set_data_full (G_OBJECT (vbox), "help-uri", g_strdup (help_uri), g_free);
2373 
2374 	return vbox;
2375 }		       
2376 
2377 static GtkWidget *
2378 append_blank_row (GtkGrid *grid)
2379 {
2380 	return GTK_WIDGET (attach_title_field (grid, ""));
2381 }
2382 
2383 static void
2384 append_blank_slim_row (GtkGrid *grid)
2385 {
2386 	GtkWidget *w;
2387 	PangoAttribute *attribute;
2388 	PangoAttrList *attr_list;
2389 
2390 	attr_list = pango_attr_list_new ();
2391 	attribute = pango_attr_scale_new (0.30);
2392 	pango_attr_list_insert (attr_list, attribute);
2393 
2394 	w = gtk_label_new (NULL);
2395 	gtk_label_set_attributes (GTK_LABEL (w), attr_list);
2396 	gtk_widget_show (w);
2397 
2398 	pango_attr_list_unref (attr_list);
2399 
2400 	gtk_container_add (GTK_CONTAINER (grid), w);
2401 }
2402 
2403 static GtkWidget *
2404 create_grid_with_standard_properties (void)
2405 {
2406 	GtkWidget *grid;
2407 
2408 	grid = gtk_grid_new ();
2409 	gtk_container_set_border_width (GTK_CONTAINER (grid), 6);
2410 	gtk_grid_set_row_spacing (GTK_GRID (grid), ROW_PAD);
2411 	gtk_grid_set_column_spacing (GTK_GRID (grid), 12);	
2412 	gtk_orientable_set_orientation (GTK_ORIENTABLE (grid), GTK_ORIENTATION_VERTICAL);
2413 	gtk_widget_show (grid);
2414 
2415 	return grid;
2416 }
2417 
2418 static gboolean
2419 is_merged_trash_directory (NautilusFile *file) 
2420 {
2421 	char *file_uri;
2422 	gboolean result;
2423 
2424 	file_uri = nautilus_file_get_uri (file);
2425 	result = strcmp (file_uri, "trash:///") == 0;
2426 	g_free (file_uri);
2427 
2428 	return result;
2429 }
2430 
2431 static gboolean
2432 is_computer_directory (NautilusFile *file)
2433 {
2434 	char *file_uri;
2435 	gboolean result;
2436 	
2437 	file_uri = nautilus_file_get_uri (file);
2438 	result = strcmp (file_uri, "computer:///") == 0;
2439 	g_free (file_uri);
2440 	
2441 	return result;
2442 }
2443 
2444 static gboolean
2445 is_network_directory (NautilusFile *file)
2446 {
2447 	char *file_uri;
2448 	gboolean result;
2449 	
2450 	file_uri = nautilus_file_get_uri (file);
2451 	result = strcmp (file_uri, "network:///") == 0;
2452 	g_free (file_uri);
2453 	
2454 	return result;
2455 }
2456 
2457 static gboolean
2458 is_burn_directory (NautilusFile *file)
2459 {
2460 	char *file_uri;
2461 	gboolean result;
2462 	
2463 	file_uri = nautilus_file_get_uri (file);
2464 	result = strcmp (file_uri, "burn:///") == 0;
2465 	g_free (file_uri);
2466 	
2467 	return result;
2468 }
2469 
2470 static gboolean
2471 is_recent_directory (NautilusFile *file)
2472 {
2473 	char *file_uri;
2474 	gboolean result;
2475 
2476 	file_uri = nautilus_file_get_uri (file);
2477 	result = strcmp (file_uri, "recent:///") == 0;
2478 	g_free (file_uri);
2479 
2480 	return result;
2481 }
2482 
2483 static gboolean
2484 should_show_custom_icon_buttons (NautilusPropertiesWindow *window) 
2485 {
2486 	if (is_multi_file_window (window)) {
2487 		return FALSE;
2488 	}
2489 
2490 	return TRUE;
2491 }
2492 
2493 static gboolean
2494 should_show_file_type (NautilusPropertiesWindow *window) 
2495 {
2496 	if (!is_multi_file_window (window) 
2497 	    && (is_merged_trash_directory (get_target_file (window)) ||
2498 		is_computer_directory (get_target_file (window)) ||
2499 		is_network_directory (get_target_file (window)) ||
2500 		is_burn_directory (get_target_file (window)))) {
2501 		return FALSE;
2502 	}
2503 
2504 
2505 	return TRUE;
2506 }
2507 
2508 static gboolean
2509 should_show_location_info (NautilusPropertiesWindow *window) 
2510 {
2511 	if (!is_multi_file_window (window) 
2512 	    && (is_merged_trash_directory (get_target_file (window)) ||
2513 		is_computer_directory (get_target_file (window)) ||
2514 		is_network_directory (get_target_file (window)) ||
2515 		is_burn_directory (get_target_file (window)))) {
2516 		return FALSE;
2517 	}
2518 
2519 	return TRUE;
2520 }
2521 
2522 static gboolean
2523 should_show_accessed_date (NautilusPropertiesWindow *window) 
2524 {
2525 	/* Accessed date for directory seems useless. If we some
2526 	 * day decide that it is useful, we should separately
2527 	 * consider whether it's useful for "trash:".
2528 	 */
2529 	if (file_list_all_directories (window->details->target_files)) {
2530 		return FALSE;
2531 	}
2532 
2533 	return TRUE;
2534 }
2535 
2536 static gboolean
2537 should_show_link_target (NautilusPropertiesWindow *window)
2538 {
2539 	if (!is_multi_file_window (window)
2540 	    && nautilus_file_is_symbolic_link (get_target_file (window))) {
2541 		return TRUE;
2542 	}
2543 
2544 	return FALSE;
2545 }
2546 
2547 static gboolean
2548 location_show_original (NautilusPropertiesWindow *window)
2549 {
2550 	NautilusFile *file;
2551 
2552 	/* there is no way a recent item will be mixed with
2553 	   other items so just pick the first file to check */
2554 	file = NAUTILUS_FILE (g_list_nth_data (window->details->original_files, 0));
2555 	return (file != NULL && !nautilus_file_is_in_recent (file));
2556 }
2557 
2558 static gboolean
2559 should_show_free_space (NautilusPropertiesWindow *window)
2560 {
2561 
2562 	if (!is_multi_file_window (window)
2563 	    && (is_merged_trash_directory (get_target_file (window)) ||
2564 		is_computer_directory (get_target_file (window)) ||
2565 		is_network_directory (get_target_file (window)) ||
2566 		is_recent_directory (get_target_file (window)) ||
2567 		is_burn_directory (get_target_file (window)))) {
2568 		return FALSE;
2569 	}
2570 
2571 	if (file_list_all_directories (window->details->target_files)) {
2572 		return TRUE;
2573 	}
2574 
2575 	return FALSE;
2576 }
2577 
2578 static gboolean
2579 should_show_volume_usage (NautilusPropertiesWindow *window)
2580 {
2581 	NautilusFile 		*file;
2582 	gboolean 		success = FALSE;
2583 	
2584 	if (is_multi_file_window (window)) {
2585 		return FALSE;
2586 	}
2587 
2588 	file = get_original_file (window);
2589 
2590 	if (file == NULL) {
2591 		return FALSE;
2592 	}
2593 
2594 	if (nautilus_file_can_unmount (file)) {
2595 		return TRUE;
2596 	}
2597 
2598 #ifdef TODO_GIO
2599 	/* Look at is_mountpoint for activation uri */
2600 #endif
2601 	return success;
2602 }
2603 
2604 static void
2605 paint_used_legend (GtkWidget *widget,
2606 		   cairo_t *cr,
2607 		   gpointer data)
2608 {
2609 	NautilusPropertiesWindow *window;
2610 	gint width, height;
2611 	GtkAllocation allocation;
2612 
2613 	gtk_widget_get_allocation (widget, &allocation);
2614 	
2615   	width  = allocation.width;
2616   	height = allocation.height;
2617   	
2618 	window = NAUTILUS_PROPERTIES_WINDOW (data);
2619 
2620 	cairo_rectangle  (cr,
2621 			  2,
2622 			  2,
2623 			  width - 4,
2624 			  height - 4);
2625 
2626 	gdk_cairo_set_source_rgba (cr, &window->details->used_color);
2627 	cairo_fill_preserve (cr);
2628 
2629 	gdk_cairo_set_source_rgba (cr, &window->details->used_stroke_color);
2630 	cairo_stroke (cr);
2631 }
2632 
2633 static void
2634 paint_free_legend (GtkWidget *widget,
2635 		   cairo_t *cr, gpointer data)
2636 {
2637 	NautilusPropertiesWindow *window;
2638 	gint width, height;
2639 	GtkAllocation allocation;
2640 
2641 	window = NAUTILUS_PROPERTIES_WINDOW (data);
2642 	gtk_widget_get_allocation (widget, &allocation);
2643 	
2644   	width  = allocation.width;
2645   	height = allocation.height;
2646   
2647 	cairo_rectangle (cr,
2648 			 2,
2649 			 2,
2650 			 width - 4,
2651 			 height - 4);
2652 
2653 	gdk_cairo_set_source_rgba (cr, &window->details->free_color);
2654 	cairo_fill_preserve(cr);
2655 
2656 	gdk_cairo_set_source_rgba (cr, &window->details->free_stroke_color);
2657 	cairo_stroke (cr);
2658 }
2659 
2660 static void
2661 paint_slice (cairo_t       *cr,
2662 	     double         x,
2663 	     double         y,
2664 	     double         radius,
2665 	     double         percent_start,
2666 	     double         percent_width,
2667 	     const GdkRGBA *fill,
2668 	     const GdkRGBA *stroke)
2669 {
2670 	double angle1;
2671 	double angle2;
2672 	gboolean full;
2673 	double offset = G_PI / 2.0;
2674 
2675 	if (percent_width < .01) {
2676 		return;
2677 	}
2678 
2679 	angle1 = (percent_start * 2 * G_PI) - offset;
2680 	angle2 = angle1 + (percent_width * 2 * G_PI);
2681 
2682 	full = (percent_width > .99);
2683 
2684 	if (!full) {
2685 		cairo_move_to (cr, x, y);
2686 	}
2687 	cairo_arc (cr, x, y, radius, angle1, angle2);
2688 
2689 	if (!full) {
2690 		cairo_line_to (cr, x, y);
2691 	}
2692 
2693 	gdk_cairo_set_source_rgba (cr, fill);
2694 	cairo_fill_preserve (cr);
2695 
2696 	gdk_cairo_set_source_rgba (cr, stroke);
2697 	cairo_stroke (cr);
2698 }
2699 
2700 static void
2701 paint_pie_chart (GtkWidget *widget,
2702 		 cairo_t *cr,
2703 		 gpointer data)
2704 {
2705 	NautilusPropertiesWindow *window;
2706 	gint width, height;
2707 	double free, used, reserved;
2708 	double xc, yc, radius;
2709 	GtkAllocation allocation;
2710 	GtkStyleContext *notebook_ctx;
2711 	GdkRGBA bg_color;
2712 
2713 	window = NAUTILUS_PROPERTIES_WINDOW (data);
2714 	gtk_widget_get_allocation (widget, &allocation);
2715 
2716 	width  = allocation.width;
2717 	height = allocation.height;
2718 
2719 	notebook_ctx = gtk_widget_get_style_context (GTK_WIDGET (window->details->notebook));
2720 	gtk_style_context_get_background_color (notebook_ctx,
2721 						gtk_widget_get_state_flags (GTK_WIDGET (window->details->notebook)),
2722 						&bg_color);
2723 
2724 	cairo_save (cr);
2725 	gdk_cairo_set_source_rgba (cr, &bg_color);
2726 	cairo_paint (cr);
2727 	cairo_restore (cr);
2728 
2729 	free = (double)window->details->volume_free / (double)window->details->volume_capacity;
2730 	used = (double)window->details->volume_used / (double)window->details->volume_capacity;
2731 	reserved = 1.0 - (used + free);
2732 
2733 	xc = width / 2;
2734 	yc = height / 2;
2735 
2736 	if (width < height) {
2737 		radius = width / 2 - 8;
2738 	} else {
2739 		radius = height / 2 - 8;
2740 	}
2741 
2742 	paint_slice (cr, xc, yc, radius,
2743 		     0, free,
2744 		     &window->details->free_color, &window->details->free_stroke_color);
2745 	paint_slice (cr, xc, yc, radius,
2746 		     free + used, reserved,
2747 		     &window->details->unknown_color, &window->details->unknown_stroke_color);
2748 	/* paint the used last so its slice strokes are on top */
2749 	paint_slice (cr, xc, yc, radius,
2750 		     free, used,
2751 		     &window->details->used_color, &window->details->used_stroke_color);
2752 }
2753 
2754 
2755 /* Copied from gtk/gtkstyle.c */
2756 
2757 static void
2758 rgb_to_hls (gdouble *r,
2759             gdouble *g,
2760             gdouble *b)
2761 {
2762   gdouble min;
2763   gdouble max;
2764   gdouble red;
2765   gdouble green;
2766   gdouble blue;
2767   gdouble h, l, s;
2768   gdouble delta;
2769   
2770   red = *r;
2771   green = *g;
2772   blue = *b;
2773   
2774   if (red > green)
2775     {
2776       if (red > blue)
2777         max = red;
2778       else
2779         max = blue;
2780       
2781       if (green < blue)
2782         min = green;
2783       else
2784         min = blue;
2785     }
2786   else
2787     {
2788       if (green > blue)
2789         max = green;
2790       else
2791         max = blue;
2792       
2793       if (red < blue)
2794         min = red;
2795       else
2796         min = blue;
2797     }
2798   
2799   l = (max + min) / 2;
2800   s = 0;
2801   h = 0;
2802   
2803   if (max != min)
2804     {
2805       if (l <= 0.5)
2806         s = (max - min) / (max + min);
2807       else
2808         s = (max - min) / (2 - max - min);
2809       
2810       delta = max -min;
2811       if (red == max)
2812         h = (green - blue) / delta;
2813       else if (green == max)
2814         h = 2 + (blue - red) / delta;
2815       else if (blue == max)
2816         h = 4 + (red - green) / delta;
2817       
2818       h *= 60;
2819       if (h < 0.0)
2820         h += 360;
2821     }
2822   
2823   *r = h;
2824   *g = l;
2825   *b = s;
2826 }
2827 
2828 static void
2829 hls_to_rgb (gdouble *h,
2830             gdouble *l,
2831             gdouble *s)
2832 {
2833   gdouble hue;
2834   gdouble lightness;
2835   gdouble saturation;
2836   gdouble m1, m2;
2837   gdouble r, g, b;
2838   
2839   lightness = *l;
2840   saturation = *s;
2841   
2842   if (lightness <= 0.5)
2843     m2 = lightness * (1 + saturation);
2844   else
2845     m2 = lightness + saturation - lightness * saturation;
2846   m1 = 2 * lightness - m2;
2847   
2848   if (saturation == 0)
2849     {
2850       *h = lightness;
2851       *l = lightness;
2852       *s = lightness;
2853     }
2854   else
2855     {
2856       hue = *h + 120;
2857       while (hue > 360)
2858         hue -= 360;
2859       while (hue < 0)
2860         hue += 360;
2861       
2862       if (hue < 60)
2863         r = m1 + (m2 - m1) * hue / 60;
2864       else if (hue < 180)
2865         r = m2;
2866       else if (hue < 240)
2867         r = m1 + (m2 - m1) * (240 - hue) / 60;
2868       else
2869         r = m1;
2870       
2871       hue = *h;
2872       while (hue > 360)
2873         hue -= 360;
2874       while (hue < 0)
2875         hue += 360;
2876       
2877       if (hue < 60)
2878         g = m1 + (m2 - m1) * hue / 60;
2879       else if (hue < 180)
2880         g = m2;
2881       else if (hue < 240)
2882         g = m1 + (m2 - m1) * (240 - hue) / 60;
2883       else
2884         g = m1;
2885       
2886       hue = *h - 120;
2887       while (hue > 360)
2888         hue -= 360;
2889       while (hue < 0)
2890         hue += 360;
2891       
2892       if (hue < 60)
2893         b = m1 + (m2 - m1) * hue / 60;
2894       else if (hue < 180)
2895         b = m2;
2896       else if (hue < 240)
2897         b = m1 + (m2 - m1) * (240 - hue) / 60;
2898       else
2899         b = m1;
2900       
2901       *h = r;
2902       *l = g;
2903       *s = b;
2904     }
2905 }
2906 static void
2907 _pie_style_shade (GdkRGBA *a,
2908                   GdkRGBA *b,
2909                   gdouble   k)
2910 {
2911   gdouble red;
2912   gdouble green;
2913   gdouble blue;
2914   
2915   red = a->red;
2916   green = a->green;
2917   blue = a->blue;
2918   
2919   rgb_to_hls (&red, &green, &blue);
2920 
2921   green *= k;
2922   if (green > 1.0)
2923     green = 1.0;
2924   else if (green < 0.0)
2925     green = 0.0;
2926   
2927   blue *= k;
2928   if (blue > 1.0)
2929     blue = 1.0;
2930   else if (blue < 0.0)
2931     blue = 0.0;
2932   
2933   hls_to_rgb (&red, &green, &blue);
2934   
2935   b->red = red;
2936   b->green = green;
2937   b->blue = blue;
2938   b->alpha = a->alpha;
2939 }
2940 
2941 
2942 static GtkWidget* 
2943 create_pie_widget (NautilusPropertiesWindow *window)
2944 {
2945 	NautilusFile		*file;
2946 	GtkGrid                 *grid;
2947 	GtkStyleContext		*style;
2948 	GtkWidget 		*pie_canvas;
2949 	GtkWidget 		*used_canvas;
2950 	GtkWidget 		*used_label;
2951 	GtkWidget 		*used_type_label;
2952 	GtkWidget 		*free_canvas;
2953 	GtkWidget 		*free_label;
2954 	GtkWidget 		*free_type_label;
2955 	GtkWidget 		*capacity_label;
2956 	GtkWidget 		*capacity_value_label;
2957 	GtkWidget 		*fstype_label;
2958 	GtkWidget 		*fstype_value_label;
2959 	GtkWidget 		*spacer_label;
2960 	gchar			*capacity;
2961 	gchar 			*used;
2962 	gchar 			*free;
2963 	const char		*fs_type;
2964 	gchar			*uri;
2965 	GFile *location;
2966 	GFileInfo *info;
2967 	
2968 	capacity = g_format_size (window->details->volume_capacity);
2969 	free 	 = g_format_size (window->details->volume_free);
2970 	used 	 = g_format_size (window->details->volume_used);
2971 	
2972 	file = get_original_file (window);
2973 	
2974 	uri = nautilus_file_get_activation_uri (file);
2975 	
2976 	grid = GTK_GRID (gtk_grid_new ());
2977 	gtk_widget_set_hexpand (GTK_WIDGET (grid), FALSE);
2978 	gtk_container_set_border_width (GTK_CONTAINER (grid), 5);
2979 	gtk_grid_set_row_spacing (GTK_GRID (grid), 10);
2980 	gtk_grid_set_column_spacing (GTK_GRID (grid), 10);
2981 	style = gtk_widget_get_style_context (GTK_WIDGET (grid));
2982 
2983 	if (!gtk_style_context_lookup_color (style, "chart_rgba_0", &window->details->unknown_color)) {
2984 		window->details->unknown_color.red = UNKNOWN_FILL_R;
2985 		window->details->unknown_color.green = UNKNOWN_FILL_G;
2986 		window->details->unknown_color.blue = UNKNOWN_FILL_B;
2987 		window->details->unknown_color.alpha = 1;
2988 	}
2989 	if (!gtk_style_context_lookup_color (style, "chart_rgba_1", &window->details->used_color)) {
2990 		window->details->used_color.red = USED_FILL_R;
2991 		window->details->used_color.green = USED_FILL_G;
2992 		window->details->used_color.blue = USED_FILL_B;
2993 		window->details->used_color.alpha = 1;
2994 	}
2995 
2996 	if (!gtk_style_context_lookup_color (style, "chart_rgba_2", &window->details->free_color)) {
2997 		window->details->free_color.red = FREE_FILL_R;
2998 		window->details->free_color.green = FREE_FILL_G;
2999 		window->details->free_color.blue = FREE_FILL_B;
3000 		window->details->free_color.alpha = 1;
3001 	}
3002 
3003 	_pie_style_shade (&window->details->used_color, &window->details->used_stroke_color, 0.7);
3004 	_pie_style_shade (&window->details->free_color, &window->details->free_stroke_color, 0.7);
3005 	_pie_style_shade (&window->details->unknown_color, &window->details->unknown_stroke_color, 0.7);
3006 
3007 	pie_canvas = gtk_drawing_area_new ();
3008 	gtk_widget_set_size_request (pie_canvas, 200, 200);
3009 
3010 	used_canvas = gtk_drawing_area_new ();
3011 	gtk_widget_set_size_request (used_canvas, 20, 20);
3012 	used_label = gtk_label_new (used);
3013 	/* Translators: "used" refers to the capacity of the filesystem */
3014 	used_type_label = gtk_label_new (_("used"));
3015 
3016 	free_canvas = gtk_drawing_area_new ();
3017 	gtk_widget_set_size_request (free_canvas, 20, 20);
3018 	free_label = gtk_label_new (free);
3019 	/* Translators: "free" refers to the capacity of the filesystem */
3020 	free_type_label = gtk_label_new (_("free"));
3021 
3022 	capacity_label = gtk_label_new (_("Total capacity:"));
3023 	capacity_value_label = gtk_label_new (capacity);
3024 
3025 	fstype_label = gtk_label_new (_("Filesystem type:"));
3026 	fstype_value_label = gtk_label_new (NULL);
3027 
3028 	spacer_label = gtk_label_new ("");
3029 
3030 	location = g_file_new_for_uri (uri);
3031 	info = g_file_query_filesystem_info (location, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE,
3032 					     NULL, NULL);
3033 	if (info) {
3034 		fs_type = g_file_info_get_attribute_string (info, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE);
3035 		if (fs_type != NULL) {
3036 			gtk_label_set_text (GTK_LABEL (fstype_value_label), fs_type);
3037 		}
3038 
3039 		g_object_unref (info);
3040 	}
3041 	g_object_unref (location);
3042 	
3043 	g_free (uri);
3044 	g_free (capacity);
3045 	g_free (used);
3046 	g_free (free);
3047 
3048 	gtk_container_add_with_properties (GTK_CONTAINER (grid), pie_canvas,
3049 					   "height", 5,
3050 					   NULL);
3051 
3052 	gtk_widget_set_vexpand (spacer_label, TRUE);
3053 	gtk_grid_attach_next_to (grid, spacer_label, pie_canvas,
3054 				 GTK_POS_RIGHT, 1, 1);
3055 
3056 	gtk_widget_set_halign (used_canvas, GTK_ALIGN_END);
3057 	gtk_widget_set_vexpand (used_canvas, FALSE);
3058 	gtk_grid_attach_next_to (grid, used_canvas, spacer_label,
3059 				 GTK_POS_BOTTOM, 1, 1);
3060 	gtk_widget_set_halign (used_label, GTK_ALIGN_END);
3061 	gtk_widget_set_vexpand (used_label, FALSE);
3062 	gtk_grid_attach_next_to (grid, used_label, used_canvas,
3063 				 GTK_POS_RIGHT, 1, 1);
3064 	gtk_widget_set_halign (used_type_label, GTK_ALIGN_START);
3065 	gtk_widget_set_vexpand (used_type_label, FALSE);
3066 	gtk_grid_attach_next_to (grid, used_type_label, used_label,
3067 				 GTK_POS_RIGHT, 1, 1);
3068 
3069 	gtk_widget_set_halign (free_canvas, GTK_ALIGN_END);
3070 	gtk_widget_set_vexpand (free_canvas, FALSE);
3071 	gtk_grid_attach_next_to (grid, free_canvas, used_canvas,
3072 				 GTK_POS_BOTTOM, 1, 1);
3073 	gtk_widget_set_halign (free_label, GTK_ALIGN_END);
3074 	gtk_widget_set_vexpand (free_label, FALSE);
3075 	gtk_grid_attach_next_to (grid, free_label, free_canvas,
3076 				 GTK_POS_RIGHT, 1, 1);
3077 	gtk_widget_set_halign (free_type_label, GTK_ALIGN_START);
3078 	gtk_widget_set_vexpand (free_type_label, FALSE);
3079 	gtk_grid_attach_next_to (grid, free_type_label, free_label,
3080 				 GTK_POS_RIGHT, 1, 1);
3081 
3082 	gtk_widget_set_halign (capacity_label, GTK_ALIGN_END);
3083 	gtk_widget_set_vexpand (capacity_label, FALSE);
3084 	gtk_grid_attach_next_to (grid, capacity_label, free_canvas,
3085 				 GTK_POS_BOTTOM, 1, 1);
3086 	gtk_widget_set_halign (capacity_value_label, GTK_ALIGN_START);
3087 	gtk_widget_set_vexpand (capacity_value_label, FALSE);
3088 	gtk_grid_attach_next_to (grid, capacity_value_label, capacity_label,
3089 				 GTK_POS_RIGHT, 1, 1);
3090 
3091 	gtk_widget_set_halign (fstype_label, GTK_ALIGN_END);
3092 	gtk_widget_set_vexpand (fstype_label, FALSE);
3093 	gtk_grid_attach_next_to (grid, fstype_label, capacity_label,
3094 				 GTK_POS_BOTTOM, 1, 1);
3095 	gtk_widget_set_halign (fstype_value_label, GTK_ALIGN_START);
3096 	gtk_widget_set_vexpand (fstype_value_label, FALSE);
3097 	gtk_grid_attach_next_to (grid, fstype_value_label, fstype_label,
3098 				 GTK_POS_RIGHT, 1, 1);
3099 
3100 	g_signal_connect (pie_canvas, "draw",
3101 			  G_CALLBACK (paint_pie_chart), window);
3102 	g_signal_connect (used_canvas, "draw",
3103 			  G_CALLBACK (paint_used_legend), window);
3104 	g_signal_connect (free_canvas, "draw",
3105 			  G_CALLBACK (paint_free_legend), window);
3106 	        
3107 	return GTK_WIDGET (grid);
3108 }
3109 
3110 static GtkWidget*
3111 create_volume_usage_widget (NautilusPropertiesWindow *window)
3112 {
3113 	GtkWidget *piewidget = NULL;
3114 	gchar *uri;
3115 	NautilusFile *file;
3116 	GFile *location;
3117 	GFileInfo *info;
3118 
3119 	file = get_original_file (window);
3120 
3121 	uri = nautilus_file_get_activation_uri (file);
3122 
3123 	location = g_file_new_for_uri (uri);
3124 	info = g_file_query_filesystem_info (location, "filesystem::*", NULL, NULL);
3125 
3126 	if (info) {
3127 		window->details->volume_capacity = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_FILESYSTEM_SIZE);
3128 		window->details->volume_free = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_FILESYSTEM_FREE);
3129 		if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_FILESYSTEM_USED)) {
3130 			window->details->volume_used = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_FILESYSTEM_USED);
3131 		} else {
3132 			window->details->volume_used = window->details->volume_capacity - window->details->volume_free;
3133 		}
3134 
3135 		g_object_unref (info);
3136 	} else {
3137 		window->details->volume_capacity = 0;
3138 		window->details->volume_free = 0;
3139 		window->details->volume_used = 0;
3140 	}
3141 
3142 	g_object_unref (location);
3143 
3144 	if (window->details->volume_capacity > 0) {
3145 		piewidget = create_pie_widget (window);
3146 		gtk_widget_show_all (piewidget);
3147 	}
3148 
3149 	return piewidget;
3150 }
3151 
3152 static void
3153 create_basic_page (NautilusPropertiesWindow *window)
3154 {
3155 	GtkGrid *grid;
3156 	GtkWidget *icon_aligner;
3157 	GtkWidget *icon_pixmap_widget;
3158 	GtkWidget *volume_usage;
3159 	GtkWidget *hbox, *vbox;
3160 
3161 	hbox = create_page_with_hbox (window->details->notebook, _("Basic"),
3162 				      "help:gnome-help/nautilus-file-properties-basic");
3163 	
3164 	/* Icon pixmap */
3165 
3166 	icon_pixmap_widget = create_image_widget (
3167 		window, should_show_custom_icon_buttons (window));
3168 	gtk_widget_show (icon_pixmap_widget);
3169 
3170 	icon_aligner = gtk_alignment_new (1, 0, 0, 0);
3171 	gtk_widget_show (icon_aligner);
3172 	
3173 	gtk_container_add (GTK_CONTAINER (icon_aligner), icon_pixmap_widget);
3174 	gtk_box_pack_start (GTK_BOX (hbox), icon_aligner, FALSE, FALSE, 0);
3175 
3176 	window->details->icon_chooser = NULL;
3177 
3178 	/* Grid */
3179 
3180 	vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
3181 	gtk_widget_show (vbox);
3182 	gtk_container_add (GTK_CONTAINER (hbox), vbox);
3183 
3184 	grid = GTK_GRID (create_grid_with_standard_properties ());
3185 	gtk_box_pack_start (GTK_BOX (vbox), GTK_WIDGET (grid), FALSE, FALSE, 0);
3186 	window->details->basic_grid = grid;
3187 
3188 	/* Name label.  The text will be determined in update_name_field */
3189 	window->details->name_label = attach_title_field (grid, NULL);
3190 
3191 	/* Name field */
3192 	window->details->name_field = NULL;
3193 	update_name_field (window);
3194 
3195 	/* Start with name field selected, if it's an entry. */
3196 	if (NAUTILUS_IS_ENTRY (window->details->name_field)) {
3197 		nautilus_entry_select_all (NAUTILUS_ENTRY (window->details->name_field));
3198 		gtk_widget_grab_focus (GTK_WIDGET (window->details->name_field));
3199 	}
3200 
3201 	if (nautilus_desktop_item_properties_should_show (window->details->target_files)) {
3202 		GtkSizeGroup *label_size_group;
3203 		GtkWidget *box;
3204 
3205 		label_size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
3206 		gtk_size_group_add_widget (label_size_group,
3207 					   GTK_WIDGET (window->details->name_label));
3208 		box = nautilus_desktop_item_properties_make_box (label_size_group,
3209 								 window->details->target_files);
3210 
3211 		gtk_grid_attach_next_to (window->details->basic_grid, box, 
3212 					 GTK_WIDGET (window->details->name_label),
3213 					 GTK_POS_BOTTOM, 2, 1);
3214 	}
3215 
3216 	if (should_show_file_type (window)) {
3217 		append_title_and_ellipsizing_value (window, grid,
3218 						    _("Type:"), 
3219 						    "detailed_type",
3220 						    INCONSISTENT_STATE_STRING,
3221 						    FALSE);
3222 	}
3223 
3224 	if (should_show_link_target (window)) {
3225 		append_title_and_ellipsizing_value (window, grid, 
3226 						    _("Link target:"), 
3227 						    "link_target",
3228 						    INCONSISTENT_STATE_STRING,
3229 						    FALSE);
3230 	}
3231 
3232 	if (is_multi_file_window (window) ||
3233 	    nautilus_file_is_directory (get_target_file (window))) {
3234 		append_directory_contents_fields (window, grid);
3235 	} else {
3236 		append_title_value_pair (window, grid, _("Size:"), 
3237 					 "size_detail",
3238 					 INCONSISTENT_STATE_STRING,
3239 					 FALSE);
3240 	}
3241 
3242 	append_blank_row (grid);
3243 
3244 	if (should_show_location_info (window)) {
3245 		append_title_and_ellipsizing_value (window, grid, _("Location:"), 
3246 						    "where",
3247 						    INCONSISTENT_STATE_STRING,
3248 						    location_show_original (window));
3249 		
3250 		append_title_and_ellipsizing_value (window, grid, 
3251 						    _("Volume:"), 
3252 						    "volume",
3253 						    INCONSISTENT_STATE_STRING,
3254 						    FALSE);
3255 	}
3256 
3257 	if (should_show_accessed_date (window)) {
3258 		append_blank_row (grid);
3259 
3260 		append_title_value_pair (window, grid, _("Accessed:"), 
3261 					 "date_accessed_full",
3262 					 INCONSISTENT_STATE_STRING,
3263 					 FALSE);
3264 		append_title_value_pair (window, grid, _("Modified:"), 
3265 					 "date_modified_full",
3266 					 INCONSISTENT_STATE_STRING,
3267 					 FALSE);
3268 	}
3269 
3270 	if (should_show_free_space (window)
3271 	    && ! should_show_volume_usage (window)) {
3272 		append_blank_row (grid);
3273 
3274 		append_title_value_pair (window, grid, _("Free space:"), 
3275 					 "free_space",
3276 					 INCONSISTENT_STATE_STRING,
3277 					 FALSE);
3278 	}
3279 
3280 	if (should_show_volume_usage (window)) {
3281 		volume_usage = create_volume_usage_widget (window);
3282 		if (volume_usage != NULL) {
3283 			gtk_container_add_with_properties (GTK_CONTAINER (grid),
3284 							   volume_usage,
3285 							   "width", 3,
3286 							   NULL);
3287 		}
3288 	}
3289 }
3290 
3291 static gboolean 
3292 files_has_directory (NautilusPropertiesWindow *window)
3293 {
3294 	GList *l;
3295 
3296 	for (l = window->details->target_files; l != NULL; l = l->next) {
3297 		NautilusFile *file;
3298 		file = NAUTILUS_FILE (l->data);
3299 		if (nautilus_file_is_directory (file)) {
3300 			return TRUE;
3301 		}
3302 		
3303 	}
3304 
3305 	return FALSE;
3306 }
3307 
3308 static gboolean
3309 files_has_changable_permissions_directory (NautilusPropertiesWindow *window)
3310 {
3311 	GList *l;
3312 	gboolean changable = FALSE;
3313 
3314 	for (l = window->details->target_files; l != NULL; l = l->next) {
3315 		NautilusFile *file;
3316 		file = NAUTILUS_FILE (l->data);
3317 		if (nautilus_file_is_directory (file) &&
3318 		    nautilus_file_can_get_permissions (file) &&
3319 		    nautilus_file_can_set_permissions (file)) {
3320 			changable = TRUE;
3321 		} else {
3322 			changable = FALSE;
3323 			break;
3324 		}
3325 	}
3326 
3327 	return changable;
3328 }
3329 
3330 static gboolean
3331 files_has_file (NautilusPropertiesWindow *window)
3332 {
3333 	GList *l;
3334 
3335 	for (l = window->details->target_files; l != NULL; l = l->next) {
3336 		NautilusFile *file;
3337 		file = NAUTILUS_FILE (l->data);
3338 		if (!nautilus_file_is_directory (file)) {
3339 			return TRUE;
3340 		}
3341 	}
3342 
3343 	return FALSE;
3344 }
3345 
3346 static void
3347 start_long_operation (NautilusPropertiesWindow *window)
3348 {
3349 	if (window->details->long_operation_underway == 0) {
3350 		/* start long operation */
3351 		GdkCursor * cursor;
3352 		
3353 		cursor = gdk_cursor_new (GDK_WATCH);
3354 		gdk_window_set_cursor (gtk_widget_get_window (GTK_WIDGET (window)), cursor);
3355 		g_object_unref (cursor);
3356 	}
3357 	window->details->long_operation_underway ++;
3358 }
3359 
3360 static void
3361 end_long_operation (NautilusPropertiesWindow *window)
3362 {
3363 	if (gtk_widget_get_window (GTK_WIDGET (window)) != NULL &&
3364 	    window->details->long_operation_underway == 1) {
3365 		/* finished !! */
3366 		gdk_window_set_cursor (gtk_widget_get_window (GTK_WIDGET (window)), NULL);
3367 	}
3368 	window->details->long_operation_underway--;
3369 }
3370 
3371 static void
3372 permission_change_callback (NautilusFile *file,
3373 			    GFile *res_loc,
3374 			    GError *error,
3375 			    gpointer callback_data)
3376 {
3377 	NautilusPropertiesWindow *window;
3378 	g_assert (callback_data != NULL);
3379 
3380 	window = NAUTILUS_PROPERTIES_WINDOW (callback_data);
3381 	end_long_operation (window);
3382 	
3383 	/* Report the error if it's an error. */
3384 	nautilus_report_error_setting_permissions (file, error, NULL);
3385 
3386 	g_object_unref (window);
3387 }
3388 
3389 static void
3390 update_permissions (NautilusPropertiesWindow *window,
3391 		    guint32 vfs_new_perm,
3392 		    guint32 vfs_mask,
3393 		    gboolean is_folder,
3394 		    gboolean apply_to_both_folder_and_dir,
3395 		    gboolean use_original)
3396 {
3397 	GList *l;
3398 	
3399 	for (l = window->details->target_files; l != NULL; l = l->next) {
3400 		NautilusFile *file;
3401 		guint32 permissions;
3402 
3403 		file = NAUTILUS_FILE (l->data);
3404 
3405 		if (!nautilus_file_can_get_permissions (file)) {
3406 			continue;
3407 		}
3408 	
3409 		if (!apply_to_both_folder_and_dir &&
3410 		    ((nautilus_file_is_directory (file) && !is_folder) ||
3411 		     (!nautilus_file_is_directory (file) && is_folder))) {
3412 			continue;
3413 		}
3414 
3415 		permissions = nautilus_file_get_permissions (file);
3416 		if (use_original) {
3417 			gpointer ptr;
3418 			if (g_hash_table_lookup_extended (window->details->initial_permissions,
3419 							  file, NULL, &ptr)) {
3420 				permissions = (permissions & ~vfs_mask) | (GPOINTER_TO_INT (ptr) & vfs_mask);
3421 			}
3422 		} else {
3423 			permissions = (permissions & ~vfs_mask) | vfs_new_perm;
3424 		}
3425 
3426 		start_long_operation (window);
3427 		g_object_ref (window);
3428 		nautilus_file_set_permissions
3429 			(file, permissions,
3430 			 permission_change_callback,
3431 			 window);
3432 	}	
3433 }
3434 
3435 static gboolean
3436 initial_permission_state_consistent (NautilusPropertiesWindow *window,
3437 				     guint32 mask,
3438 				     gboolean is_folder,
3439 				     gboolean both_folder_and_dir)
3440 {
3441 	GList *l;
3442 	gboolean first;
3443 	guint32 first_permissions;
3444 
3445 	first = TRUE;
3446 	first_permissions = 0;
3447 	for (l = window->details->target_files; l != NULL; l = l->next) {
3448 		NautilusFile *file;
3449 		guint32 permissions;
3450 
3451 		file = l->data;
3452 		
3453 		if (!both_folder_and_dir &&
3454 		    ((nautilus_file_is_directory (file) && !is_folder) ||
3455 		     (!nautilus_file_is_directory (file) && is_folder))) {
3456 			continue;
3457 		}
3458 		
3459 		permissions = GPOINTER_TO_INT (g_hash_table_lookup (window->details->initial_permissions,
3460 								    file));
3461 
3462 		if (first) {
3463 			if ((permissions & mask) != mask &&
3464 			    (permissions & mask) != 0) {
3465 				/* Not fully on or off -> inconsistent */
3466 				return FALSE;
3467 			}
3468 				
3469 			first_permissions = permissions;
3470 			first = FALSE;
3471 				
3472 		} else if ((permissions & mask) != first_permissions) {
3473 			/* Not same permissions as first -> inconsistent */
3474 			return FALSE;
3475 		}
3476 	}
3477 	return TRUE;
3478 }
3479 
3480 static void
3481 permission_button_toggled (GtkToggleButton *button, 
3482 			   NautilusPropertiesWindow *window)
3483 {
3484 	gboolean is_folder, is_special;
3485 	guint32 permission_mask;
3486 	gboolean inconsistent;
3487 	gboolean on;
3488 	
3489 	permission_mask = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (button),
3490 							      "permission"));
3491 	is_folder = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (button),
3492 							"is-folder"));
3493 	is_special = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (button),
3494 							"is-special"));
3495 
3496 	if (gtk_toggle_button_get_active (button)
3497 	    && !gtk_toggle_button_get_inconsistent (button)) {
3498 		/* Go to the initial state unless the initial state was 
3499 		   consistent, or we support recursive apply */
3500 		inconsistent = TRUE;
3501 		on = TRUE;
3502 
3503 		if (initial_permission_state_consistent (window, permission_mask, is_folder, is_special)) {
3504 			inconsistent = FALSE;
3505 			on = TRUE;
3506 		}
3507 	} else if (gtk_toggle_button_get_inconsistent (button)
3508 		   && !gtk_toggle_button_get_active (button)) {
3509 		inconsistent = FALSE;
3510 		on = TRUE;
3511 	} else {
3512 		inconsistent = FALSE;
3513 		on = FALSE;
3514 	}
3515 	
3516 	g_signal_handlers_block_by_func (G_OBJECT (button), 
3517 					 G_CALLBACK (permission_button_toggled),
3518 					 window);
3519 
3520 	gtk_toggle_button_set_active (button, on);
3521 	gtk_toggle_button_set_inconsistent (button, inconsistent);
3522 
3523 	g_signal_handlers_unblock_by_func (G_OBJECT (button), 
3524 					   G_CALLBACK (permission_button_toggled),
3525 					   window);
3526 
3527 	update_permissions (window,
3528 			    on?permission_mask:0,
3529 			    permission_mask,
3530 			    is_folder,
3531 			    is_special,
3532 			    inconsistent);
3533 }
3534 
3535 static void
3536 permission_button_update (NautilusPropertiesWindow *window,
3537 			  GtkToggleButton *button)
3538 {
3539 	GList *l;
3540 	gboolean all_set;
3541 	gboolean all_unset;
3542 	gboolean all_cannot_set;
3543 	gboolean is_folder, is_special;
3544 	gboolean no_match;
3545 	gboolean sensitive;
3546 	guint32 button_permission;
3547 
3548 	button_permission = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (button),
3549 								"permission"));
3550 	is_folder = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (button),
3551 							"is-folder"));
3552 	is_special = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (button),
3553 							 "is-special"));
3554 	
3555 	all_set = TRUE;
3556 	all_unset = TRUE;
3557 	all_cannot_set = TRUE;
3558 	no_match = TRUE;
3559 	for (l = window->details->target_files; l != NULL; l = l->next) {
3560 		NautilusFile *file;
3561 		guint32 file_permissions;
3562 
3563 		file = NAUTILUS_FILE (l->data);
3564 
3565 		if (!nautilus_file_can_get_permissions (file)) {
3566 			continue;
3567 		}
3568 
3569 		if (!is_special &&
3570 		    ((nautilus_file_is_directory (file) && !is_folder) ||
3571 		     (!nautilus_file_is_directory (file) && is_folder))) {
3572 			continue;
3573 		}
3574 
3575 		no_match = FALSE;
3576 		
3577 		file_permissions = nautilus_file_get_permissions (file);
3578 
3579 		if ((file_permissions & button_permission) == button_permission) {
3580 			all_unset = FALSE;
3581 		} else if ((file_permissions & button_permission) == 0) {
3582 			all_set = FALSE;
3583 		} else {
3584 			all_unset = FALSE;
3585 			all_set = FALSE;
3586 		}
3587 
3588 		if (nautilus_file_can_set_permissions (file)) {
3589 			all_cannot_set = FALSE;
3590 		}
3591 	}
3592 
3593 	sensitive = !all_cannot_set;
3594 
3595 	g_signal_handlers_block_by_func (G_OBJECT (button), 
3596 					 G_CALLBACK (permission_button_toggled),
3597 					 window);
3598 
3599 	gtk_toggle_button_set_active (button, !all_unset);
3600 	/* if actually inconsistent, or default value for file buttons
3601 	   if no files are selected. (useful for recursive apply) */
3602 	gtk_toggle_button_set_inconsistent (button,
3603 					    (!all_unset && !all_set) ||
3604 					    (!is_folder && no_match));
3605 	gtk_widget_set_sensitive (GTK_WIDGET (button), sensitive);
3606 
3607 	g_signal_handlers_unblock_by_func (G_OBJECT (button), 
3608 					   G_CALLBACK (permission_button_toggled),
3609 					   window);
3610 }
3611 
3612 static void
3613 set_up_permissions_checkbox (NautilusPropertiesWindow *window,
3614 			     GtkWidget *check_button, 
3615 			     guint32 permission,
3616 			     gboolean is_folder)
3617 {
3618 	/* Load up the check_button with data we'll need when updating its state. */
3619         g_object_set_data (G_OBJECT (check_button), "permission", 
3620 			   GINT_TO_POINTER (permission));
3621         g_object_set_data (G_OBJECT (check_button), "properties_window", 
3622 			   window);
3623 	g_object_set_data (G_OBJECT (check_button), "is-folder",
3624 			   GINT_TO_POINTER (is_folder));
3625 	
3626 	window->details->permission_buttons = 
3627 		g_list_prepend (window->details->permission_buttons,
3628 				check_button);
3629 
3630 	g_signal_connect_object (check_button, "toggled",
3631 				 G_CALLBACK (permission_button_toggled),
3632 				 window,
3633 				 0);
3634 }
3635 
3636 static GtkWidget *
3637 add_execute_checkbox_with_label (NautilusPropertiesWindow *window,
3638 				 GtkGrid *grid,
3639 				 GtkWidget *sibling,
3640 				 const char *label,
3641 				 guint32 permission_to_check,
3642 				 GtkLabel *label_for,
3643 				 gboolean is_folder)
3644 {
3645 	GtkWidget *check_button;
3646 	gboolean a11y_enabled;
3647 	
3648 	check_button = gtk_check_button_new_with_mnemonic (label);
3649 	gtk_widget_show (check_button);
3650 
3651 	if (sibling) {
3652 		gtk_grid_attach_next_to (grid, check_button, sibling,
3653 					 GTK_POS_RIGHT, 1, 1);
3654 	} else {
3655 		gtk_container_add (GTK_CONTAINER (grid), check_button);
3656 	}
3657 
3658 	set_up_permissions_checkbox (window, 
3659 				     check_button, 
3660 				     permission_to_check,
3661 				     is_folder);
3662 
3663 	a11y_enabled = GTK_IS_ACCESSIBLE (gtk_widget_get_accessible (check_button));
3664 	if (a11y_enabled && label_for != NULL) {
3665 		eel_accessibility_set_up_label_widget_relation (GTK_WIDGET (label_for),
3666 								check_button);
3667 	}
3668 
3669 	return check_button;
3670 }
3671 
3672 enum {
3673 	UNIX_PERM_SUID = S_ISUID,
3674 	UNIX_PERM_SGID = S_ISGID,	
3675 	UNIX_PERM_STICKY = 01000,	/* S_ISVTX not defined on all systems */
3676 	UNIX_PERM_USER_READ = S_IRUSR,
3677 	UNIX_PERM_USER_WRITE = S_IWUSR,
3678 	UNIX_PERM_USER_EXEC = S_IXUSR,
3679 	UNIX_PERM_USER_ALL = S_IRUSR | S_IWUSR | S_IXUSR,
3680 	UNIX_PERM_GROUP_READ = S_IRGRP,
3681 	UNIX_PERM_GROUP_WRITE = S_IWGRP,
3682 	UNIX_PERM_GROUP_EXEC = S_IXGRP,
3683 	UNIX_PERM_GROUP_ALL = S_IRGRP | S_IWGRP | S_IXGRP,
3684 	UNIX_PERM_OTHER_READ = S_IROTH,
3685 	UNIX_PERM_OTHER_WRITE = S_IWOTH,
3686 	UNIX_PERM_OTHER_EXEC = S_IXOTH,
3687 	UNIX_PERM_OTHER_ALL = S_IROTH | S_IWOTH | S_IXOTH
3688 };
3689 
3690 typedef enum {
3691 	PERMISSION_READ  = (1<<0),
3692 	PERMISSION_WRITE = (1<<1),
3693 	PERMISSION_EXEC  = (1<<2)
3694 } PermissionValue;
3695 
3696 typedef enum {
3697 	PERMISSION_USER,
3698 	PERMISSION_GROUP,
3699 	PERMISSION_OTHER
3700 } PermissionType;
3701 
3702 static guint32 vfs_perms[3][3] = {
3703 	{UNIX_PERM_USER_READ, UNIX_PERM_USER_WRITE, UNIX_PERM_USER_EXEC},
3704 	{UNIX_PERM_GROUP_READ, UNIX_PERM_GROUP_WRITE, UNIX_PERM_GROUP_EXEC},
3705 	{UNIX_PERM_OTHER_READ, UNIX_PERM_OTHER_WRITE, UNIX_PERM_OTHER_EXEC},
3706 };
3707 
3708 static guint32 
3709 permission_to_vfs (PermissionType type, PermissionValue perm)
3710 {
3711 	guint32 vfs_perm;
3712 	g_assert (type >= 0 && type < 3);
3713 
3714 	vfs_perm = 0;
3715 	if (perm & PERMISSION_READ) {
3716 		vfs_perm |= vfs_perms[type][0];
3717 	}
3718 	if (perm & PERMISSION_WRITE) {
3719 		vfs_perm |= vfs_perms[type][1];
3720 	}
3721 	if (perm & PERMISSION_EXEC) {
3722 		vfs_perm |= vfs_perms[type][2];
3723 	}
3724 	
3725 	return vfs_perm;
3726 }
3727 
3728 
3729 static PermissionValue
3730 permission_from_vfs (PermissionType type, guint32 vfs_perm)
3731 {
3732 	PermissionValue perm;
3733 	g_assert (type >= 0 && type < 3);
3734 
3735 	perm = 0;
3736 	if (vfs_perm & vfs_perms[type][0]) {
3737 		perm |= PERMISSION_READ;
3738 	}
3739 	if (vfs_perm & vfs_perms[type][1]) {
3740 		perm |= PERMISSION_WRITE;
3741 	}
3742 	if (vfs_perm & vfs_perms[type][2]) {
3743 		perm |= PERMISSION_EXEC;
3744 	}
3745 	
3746 	return perm;
3747 }
3748 
3749 static void
3750 permission_combo_changed (GtkWidget *combo, NautilusPropertiesWindow *window)
3751 {
3752 	GtkTreeIter iter;
3753 	GtkTreeModel *model;
3754 	gboolean is_folder, use_original;
3755 	PermissionType type;
3756 	int new_perm, mask;
3757 	guint32 vfs_new_perm, vfs_mask;
3758 
3759 	is_folder = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (combo), "is-folder"));
3760 	type = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (combo), "permission-type"));
3761 
3762 	if (is_folder) {
3763 		mask = PERMISSION_READ|PERMISSION_WRITE|PERMISSION_EXEC;
3764 	} else {
3765 		mask = PERMISSION_READ|PERMISSION_WRITE;
3766 	}
3767 
3768 	vfs_mask = permission_to_vfs (type, mask);
3769 	
3770 	model = gtk_combo_box_get_model (GTK_COMBO_BOX (combo));
3771 	
3772 	if (!gtk_combo_box_get_active_iter (GTK_COMBO_BOX (combo),  &iter)) {
3773 		return;
3774 	}
3775 	gtk_tree_model_get (model, &iter, COLUMN_VALUE, &new_perm,
3776 			    COLUMN_USE_ORIGINAL, &use_original, -1);
3777 	vfs_new_perm = permission_to_vfs (type, new_perm);
3778 
3779 	update_permissions (window, vfs_new_perm, vfs_mask,
3780 			    is_folder, FALSE, use_original);
3781 }
3782 
3783 static void
3784 permission_combo_add_multiple_choice (GtkComboBox *combo, GtkTreeIter *iter)
3785 {
3786 	GtkTreeModel *model;
3787 	GtkListStore *store;
3788 	gboolean found;
3789 
3790 	model = gtk_combo_box_get_model (combo);
3791 	store = GTK_LIST_STORE (model);
3792 
3793 	found = FALSE;
3794 	gtk_tree_model_get_iter_first (model, iter);
3795 	do {
3796 		gboolean multi;
3797 		gtk_tree_model_get (model, iter, COLUMN_USE_ORIGINAL, &multi, -1);
3798 		
3799 		if (multi) {
3800 			found = TRUE;
3801 			break;
3802 		}
3803 	} while (gtk_tree_model_iter_next (model, iter));
3804 	
3805 	if (!found) {
3806 		gtk_list_store_append (store, iter);
3807 		gtk_list_store_set (store, iter,
3808 				    COLUMN_NAME, "---",
3809 				    COLUMN_VALUE, 0,
3810 				    COLUMN_USE_ORIGINAL, TRUE, -1);
3811 	}
3812 }
3813 
3814 static void
3815 permission_combo_update (NautilusPropertiesWindow *window,
3816 			 GtkComboBox *combo)
3817 {
3818 	PermissionType type;
3819 	PermissionValue perm, all_dir_perm, all_file_perm, all_perm;
3820 	gboolean is_folder, no_files, no_dirs, all_file_same, all_dir_same, all_same;
3821 	gboolean all_dir_cannot_set, all_file_cannot_set, sensitive;
3822 	GtkTreeIter iter;
3823 	int mask;
3824 	GtkTreeModel *model;
3825 	GtkListStore *store;
3826 	GList *l;
3827 	gboolean is_multi;
3828 
3829 	model = gtk_combo_box_get_model (combo);
3830 	
3831 	is_folder = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (combo), "is-folder"));
3832 	type = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (combo), "permission-type"));
3833 
3834 	is_multi = FALSE;
3835 	if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (combo),  &iter)) {
3836 		gtk_tree_model_get (model, &iter, COLUMN_USE_ORIGINAL, &is_multi, -1);
3837 	}
3838 
3839 	no_files = TRUE;
3840 	no_dirs = TRUE;
3841 	all_dir_same = TRUE;
3842 	all_file_same = TRUE;
3843 	all_dir_perm = 0;
3844 	all_file_perm = 0;
3845 	all_dir_cannot_set = TRUE;
3846 	all_file_cannot_set = TRUE;
3847 	
3848 	for (l = window->details->target_files; l != NULL; l = l->next) {
3849 		NautilusFile *file;
3850 		guint32 file_permissions;
3851 
3852 		file = NAUTILUS_FILE (l->data);
3853 
3854 		if (!nautilus_file_can_get_permissions (file)) {
3855 			continue;
3856 		}
3857 
3858 		if (nautilus_file_is_directory (file)) {
3859 			mask = PERMISSION_READ|PERMISSION_WRITE|PERMISSION_EXEC;
3860 		} else {
3861 			mask = PERMISSION_READ|PERMISSION_WRITE;
3862 		}
3863 		
3864 		file_permissions = nautilus_file_get_permissions (file);
3865 
3866 		perm = permission_from_vfs (type, file_permissions) & mask;
3867 
3868 		if (nautilus_file_is_directory (file)) {
3869 			if (no_dirs) {
3870 				all_dir_perm = perm;
3871 				no_dirs = FALSE;
3872 			} else if (perm != all_dir_perm) {
3873 				all_dir_same = FALSE;
3874 			}
3875 			
3876 			if (nautilus_file_can_set_permissions (file)) {
3877 				all_dir_cannot_set = FALSE;
3878 			}
3879 		} else {
3880 			if (no_files) {
3881 				all_file_perm = perm;
3882 				no_files = FALSE;
3883 			} else if (perm != all_file_perm) {
3884 				all_file_same = FALSE;
3885 			}
3886 			
3887 			if (nautilus_file_can_set_permissions (file)) {
3888 				all_file_cannot_set = FALSE;
3889 			}
3890 		}
3891 	}
3892 
3893 	if (is_folder) {
3894 		all_same = all_dir_same;
3895 		all_perm = all_dir_perm;
3896 	} else {
3897 		all_same = all_file_same && !no_files;
3898 		all_perm = all_file_perm;
3899 	}
3900 
3901 	store = GTK_LIST_STORE (model);
3902 	if (all_same) {
3903 		gboolean found;
3904 
3905 		found = FALSE;
3906 		gtk_tree_model_get_iter_first (model, &iter);
3907 		do {
3908 			int current_perm;
3909 			gtk_tree_model_get (model, &iter, 1, &current_perm, -1);
3910 
3911 			if (current_perm == all_perm) {
3912 				found = TRUE;
3913 				break;
3914 			}
3915 		} while (gtk_tree_model_iter_next (model, &iter));
3916 
3917 		if (!found) {
3918 			GString *str;
3919 			str = g_string_new ("");
3920 			
3921 			if (!(all_perm & PERMISSION_READ)) {
3922 				/* translators: this gets concatenated to "no read",
3923 				 * "no access", etc. (see following strings)
3924 				 */
3925 				g_string_append (str, _("no "));
3926 			}
3927 			if (is_folder) {
3928 				g_string_append (str, _("list"));
3929 			} else {
3930 				g_string_append (str, _("read"));
3931 			}
3932 			
3933 			g_string_append (str, ", ");
3934 			
3935 			if (!(all_perm & PERMISSION_WRITE)) {
3936 				g_string_append (str, _("no "));
3937 			}
3938 			if (is_folder) {
3939 				g_string_append (str, _("create/delete"));
3940 			} else {
3941 				g_string_append (str, _("write"));
3942 			}
3943 
3944 			if (is_folder) {
3945 				g_string_append (str, ", ");
3946 
3947 				if (!(all_perm & PERMISSION_EXEC)) {
3948 					g_string_append (str, _("no "));
3949 				}
3950 				g_string_append (str, _("access"));
3951 			}
3952 			
3953 			gtk_list_store_append (store, &iter);
3954 			gtk_list_store_set (store, &iter,
3955 					    0, str->str,
3956 					    1, all_perm, -1);
3957 			
3958 			g_string_free (str, TRUE);
3959 		}
3960 	} else {
3961 		permission_combo_add_multiple_choice (combo, &iter);
3962 	}
3963 
3964 	g_signal_handlers_block_by_func (G_OBJECT (combo), 
3965 					 G_CALLBACK (permission_combo_changed),
3966 					 window);
3967 	
3968 	gtk_combo_box_set_active_iter (combo, &iter);
3969 
3970 	/* Also enable if no files found (for recursive
3971 	   file changes when only selecting folders) */
3972 	if (is_folder) {
3973 		sensitive = !all_dir_cannot_set;
3974 	} else {
3975 		sensitive = !all_file_cannot_set;
3976 	}
3977 	gtk_widget_set_sensitive (GTK_WIDGET (combo), sensitive);
3978 
3979 	g_signal_handlers_unblock_by_func (G_OBJECT (combo), 
3980 					   G_CALLBACK (permission_combo_changed),
3981 					   window);
3982 
3983 }
3984 
3985 static GtkWidget *
3986 create_permissions_combo_box (PermissionType type,
3987 			      gboolean is_folder)
3988 {
3989 	GtkWidget *combo;
3990 	GtkListStore *store;
3991 	GtkCellRenderer *cell;
3992 	GtkTreeIter iter;
3993 
3994 	store = gtk_list_store_new (NUM_COLUMNS, G_TYPE_STRING, G_TYPE_INT, G_TYPE_BOOLEAN, G_TYPE_STRING);
3995 	combo = gtk_combo_box_new_with_model (GTK_TREE_MODEL (store));
3996 	gtk_combo_box_set_id_column (GTK_COMBO_BOX (combo), COLUMN_ID);
3997 
3998 	g_object_set_data (G_OBJECT (combo), "is-folder", GINT_TO_POINTER (is_folder));
3999 	g_object_set_data (G_OBJECT (combo), "permission-type", GINT_TO_POINTER (type));
4000 
4001 	if (is_folder) {
4002 		if (type != PERMISSION_USER) {
4003 			gtk_list_store_append (store, &iter);
4004 			/* Translators: this is referred to the permissions
4005 			 * the user has in a directory.
4006 			 */
4007 			gtk_list_store_set (store, &iter,
4008 					    COLUMN_NAME, _("None"),
4009 					    COLUMN_VALUE, 0,
4010 					    COLUMN_ID, "none",
4011 					    -1);
4012 		}
4013 		gtk_list_store_append (store, &iter);
4014 		gtk_list_store_set (store, &iter,
4015 				    COLUMN_NAME, _("List files only"),
4016 				    COLUMN_VALUE, PERMISSION_READ,
4017 				    COLUMN_ID, "r",
4018 				    -1);
4019 		gtk_list_store_append (store, &iter);
4020 		gtk_list_store_set (store, &iter,
4021 				    COLUMN_NAME, _("Access files"),
4022 				    COLUMN_VALUE, PERMISSION_READ|PERMISSION_EXEC,
4023 				    COLUMN_ID, "rx",
4024 				    -1);
4025 		gtk_list_store_append (store, &iter);
4026 		gtk_list_store_set (store, &iter,
4027 				    COLUMN_NAME, _("Create and delete files"),
4028 				    COLUMN_VALUE, PERMISSION_READ|PERMISSION_EXEC|PERMISSION_WRITE,
4029 				    COLUMN_ID, "rwx",
4030 				    -1);
4031 	} else {
4032 		if (type != PERMISSION_USER) {
4033 			gtk_list_store_append (store, &iter);
4034 			gtk_list_store_set (store, &iter,
4035 					    COLUMN_NAME, _("None"),
4036 					    COLUMN_VALUE, 0,
4037 					    COLUMN_ID, "none",
4038 					    -1);
4039 		}
4040 		gtk_list_store_append (store, &iter);
4041 		gtk_list_store_set (store, &iter,
4042 				    COLUMN_NAME, _("Read-only"),
4043 				    COLUMN_VALUE, PERMISSION_READ,
4044 				    COLUMN_ID, "r",
4045 				    -1);
4046 		gtk_list_store_append (store, &iter);
4047 		gtk_list_store_set (store, &iter,
4048 				    COLUMN_NAME, _("Read and write"),
4049 				    COLUMN_VALUE, PERMISSION_READ|PERMISSION_WRITE,
4050 				    COLUMN_ID, "rw",
4051 				    -1);
4052 	}
4053 	g_object_unref (store);
4054 
4055 	cell = gtk_cell_renderer_text_new ();
4056 	gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), cell, TRUE);
4057 	gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo), cell,
4058 					"text", COLUMN_NAME,
4059 					NULL);
4060 
4061 	return combo;
4062 }
4063 
4064 static void
4065 add_permissions_combo_box (NautilusPropertiesWindow *window,
4066 			   GtkGrid *grid,
4067 			   PermissionType type,
4068 			   gboolean is_folder,
4069 			   gboolean short_label)
4070 {
4071 	GtkWidget *combo;
4072 	GtkLabel *label;
4073 
4074 	if (short_label) {
4075 		label = attach_title_field (grid, _("Access:"));
4076 	} else if (is_folder) {
4077 		label = attach_title_field (grid, _("Folder access:"));
4078 	} else {
4079 		label = attach_title_field (grid, _("File access:"));
4080 	}
4081 
4082 	combo = create_permissions_combo_box (type, is_folder);
4083 
4084 	window->details->permission_combos = g_list_prepend (window->details->permission_combos,
4085 							     combo);
4086 
4087 	g_signal_connect (combo, "changed", G_CALLBACK (permission_combo_changed), window);
4088 
4089 	gtk_label_set_mnemonic_widget (label, combo);
4090 	gtk_widget_show (combo);
4091 
4092 	gtk_grid_attach_next_to (grid, combo, GTK_WIDGET (label),
4093 				 GTK_POS_RIGHT, 1, 1);
4094 }
4095 
4096 static gboolean
4097 all_can_get_permissions (GList *file_list)
4098 {
4099 	GList *l;
4100 	for (l = file_list; l != NULL; l = l->next) {
4101 		NautilusFile *file;
4102 		
4103 		file = NAUTILUS_FILE (l->data);
4104 		
4105 		if (!nautilus_file_can_get_permissions (file)) {
4106 			return FALSE;
4107 		}
4108 	}
4109 
4110 	return TRUE;
4111 }
4112 
4113 static gboolean
4114 all_can_set_permissions (GList *file_list)
4115 {
4116 	GList *l;
4117 	for (l = file_list; l != NULL; l = l->next) {
4118 		NautilusFile *file;
4119 		
4120 		file = NAUTILUS_FILE (l->data);
4121 
4122 		if (!nautilus_file_can_set_permissions (file)) {
4123 			return FALSE;
4124 		}
4125 	}
4126 
4127 	return TRUE;
4128 }
4129 
4130 static GHashTable *
4131 get_initial_permissions (GList *file_list)
4132 {
4133 	GHashTable *ret;
4134 	GList *l;
4135 
4136 	ret = g_hash_table_new (g_direct_hash,
4137 				g_direct_equal);
4138 	
4139 	for (l = file_list; l != NULL; l = l->next) {
4140 		guint32 permissions;
4141 		NautilusFile *file;
4142 		
4143 		file = NAUTILUS_FILE (l->data);
4144 		
4145 		permissions = nautilus_file_get_permissions (file);
4146 		g_hash_table_insert (ret, file,
4147 				     GINT_TO_POINTER (permissions));
4148 	}
4149 
4150 	return ret;
4151 }
4152 
4153 static void
4154 create_simple_permissions (NautilusPropertiesWindow *window, GtkGrid *page_grid)
4155 {
4156 	gboolean has_directory;
4157 	gboolean has_file;
4158 	GtkLabel *group_label;
4159 	GtkLabel *owner_label;
4160 	GtkWidget *value;
4161 	GtkComboBox *group_combo_box;
4162 	GtkComboBox *owner_combo_box;
4163 
4164 	has_directory = files_has_directory (window);
4165 	has_file = files_has_file (window);
4166 
4167 	if (!is_multi_file_window (window) && nautilus_file_can_set_owner (get_target_file (window))) {
4168 		owner_label = attach_title_field (page_grid, _("_Owner:"));
4169 		/* Combo box in this case. */
4170 		owner_combo_box = attach_owner_combo_box (page_grid,
4171 							  GTK_WIDGET (owner_label),
4172 							  get_target_file (window));
4173 		gtk_label_set_mnemonic_widget (owner_label,
4174 					       GTK_WIDGET (owner_combo_box));
4175 	} else {
4176 		owner_label = attach_title_field (page_grid, _("Owner:"));
4177 		/* Static text in this case. */
4178 		value = attach_value_field (window, 
4179 					    page_grid, GTK_WIDGET (owner_label),
4180 					    "owner",
4181 					    INCONSISTENT_STATE_STRING,
4182 					    FALSE); 
4183 		gtk_label_set_mnemonic_widget (owner_label, value);
4184 	}
4185 	if (has_directory && has_file) {
4186 		add_permissions_combo_box (window, page_grid,
4187 					   PERMISSION_USER, TRUE, FALSE);
4188 		add_permissions_combo_box (window, page_grid,
4189 					   PERMISSION_USER, FALSE, FALSE);
4190 	} else {
4191 		add_permissions_combo_box (window, page_grid,
4192 					   PERMISSION_USER, has_directory, TRUE);
4193 	}
4194 
4195 	append_blank_slim_row (page_grid);
4196 
4197 	if (!is_multi_file_window (window) && nautilus_file_can_set_group (get_target_file (window))) {
4198 		group_label = attach_title_field (page_grid, _("_Group:"));
4199 
4200 		/* Combo box in this case. */
4201 		group_combo_box = attach_group_combo_box (page_grid, GTK_WIDGET (group_label),
4202 							  get_target_file (window));
4203 		gtk_label_set_mnemonic_widget (group_label,
4204 					       GTK_WIDGET (group_combo_box));
4205 	} else {
4206 		group_label = attach_title_field (page_grid, _("Group:"));
4207 
4208 		/* Static text in this case. */
4209 		value = attach_value_field (window, page_grid, 
4210 					    GTK_WIDGET (group_label), 
4211 					    "group",
4212 					    INCONSISTENT_STATE_STRING,
4213 					    FALSE); 
4214 		gtk_label_set_mnemonic_widget (group_label, value);
4215 	}
4216 	if (has_directory && has_file) {
4217 		add_permissions_combo_box (window, page_grid,
4218 					   PERMISSION_GROUP, TRUE, FALSE);
4219 		add_permissions_combo_box (window, page_grid,
4220 					   PERMISSION_GROUP, FALSE, FALSE);
4221 	} else {
4222 		add_permissions_combo_box (window, page_grid,
4223 					   PERMISSION_GROUP, has_directory, TRUE);
4224 	}
4225 
4226 	append_blank_slim_row (page_grid);
4227 	attach_title_field (page_grid, _("Others"));
4228 	if (has_directory && has_file) {
4229 		add_permissions_combo_box (window, page_grid,
4230 					   PERMISSION_OTHER, TRUE, FALSE);
4231 		add_permissions_combo_box (window, page_grid,
4232 					   PERMISSION_OTHER, FALSE, FALSE);
4233 	} else {
4234 		add_permissions_combo_box (window, page_grid,
4235 					   PERMISSION_OTHER, has_directory, TRUE);
4236 	}
4237 
4238 	if (!has_directory) {
4239 		GtkLabel *execute_label;
4240 		append_blank_slim_row (page_grid);
4241 
4242 		execute_label = attach_title_field (page_grid, _("Execute:"));
4243 		add_execute_checkbox_with_label (window, page_grid,
4244 						 GTK_WIDGET (execute_label),
4245 						 _("Allow _executing file as program"),
4246 						 UNIX_PERM_USER_EXEC|UNIX_PERM_GROUP_EXEC|UNIX_PERM_OTHER_EXEC,
4247 						 execute_label, FALSE);
4248 	}
4249 }
4250 
4251 static void
4252 set_recursive_permissions_done (gboolean success,
4253 				gpointer callback_data)
4254 {
4255 	NautilusPropertiesWindow *window;
4256 
4257 	window = NAUTILUS_PROPERTIES_WINDOW (callback_data);
4258 	end_long_operation (window);
4259 
4260 	g_object_unref (window);
4261 }
4262 
4263 static void
4264 on_change_permissions_response (GtkDialog                *dialog,
4265 			       int                       response,
4266 			       NautilusPropertiesWindow *window)
4267 {
4268 	if (response != GTK_RESPONSE_OK) {
4269 		gtk_widget_destroy (GTK_WIDGET (dialog));
4270 		return;
4271 	}
4272 	guint32 file_permission, file_permission_mask;
4273 	guint32 dir_permission, dir_permission_mask;
4274 	guint32 vfs_mask, vfs_new_perm;
4275 	GtkWidget *combo;
4276 	gboolean is_folder, use_original;
4277 	GList *l;
4278 	GtkTreeModel *model;
4279 	GtkTreeIter iter;
4280 	PermissionType type;
4281 	int new_perm, mask;
4282 
4283 	file_permission = 0;
4284 	file_permission_mask = 0;
4285 	dir_permission = 0;
4286 	dir_permission_mask = 0;
4287 
4288 	/* Simple mode, minus exec checkbox */
4289 	for (l = window->details->change_permission_combos; l != NULL; l = l->next) {
4290 		combo = l->data;
4291 
4292 		if (!gtk_combo_box_get_active_iter (GTK_COMBO_BOX (combo),  &iter)) {
4293 			continue;
4294 		}
4295 
4296 		type = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (combo), "permission-type"));
4297 		is_folder = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (combo), "is-folder"));
4298 
4299 		model = gtk_combo_box_get_model (GTK_COMBO_BOX (combo));
4300 		gtk_tree_model_get (model, &iter,
4301 				    COLUMN_VALUE, &new_perm,
4302 				    COLUMN_USE_ORIGINAL, &use_original, -1);
4303 		if (use_original) {
4304 			continue;
4305 		}
4306 		vfs_new_perm = permission_to_vfs (type, new_perm);
4307 		
4308 		if (is_folder) {
4309 			mask = PERMISSION_READ|PERMISSION_WRITE|PERMISSION_EXEC;
4310 		} else {
4311 			mask = PERMISSION_READ|PERMISSION_WRITE;
4312 		}
4313 		vfs_mask = permission_to_vfs (type, mask);
4314 		
4315 		if (is_folder) {
4316 			dir_permission_mask |= vfs_mask;
4317 			dir_permission |= vfs_new_perm;
4318 		} else {
4319 			file_permission_mask |= vfs_mask;
4320 			file_permission |= vfs_new_perm;
4321 		}
4322 	}
4323 
4324 	for (l = window->details->target_files; l != NULL; l = l->next) {
4325 		NautilusFile *file;
4326 		char *uri;
4327 
4328 		file = NAUTILUS_FILE (l->data);
4329 
4330 		if (nautilus_file_is_directory (file) &&
4331 		    nautilus_file_can_set_permissions (file)) {
4332 			uri = nautilus_file_get_uri (file);
4333 			start_long_operation (window);
4334 			g_object_ref (window);
4335 			nautilus_file_set_permissions_recursive (uri,
4336 								 file_permission,
4337 								 file_permission_mask,
4338 								 dir_permission,
4339 								 dir_permission_mask,
4340 								 set_recursive_permissions_done,
4341 								 window);
4342 			g_free (uri);
4343 		}
4344 	}
4345 	gtk_widget_destroy (GTK_WIDGET (dialog));
4346 }
4347 
4348 static void
4349 set_active_from_umask (GtkWidget     *combo,
4350 		       PermissionType type,
4351 		       gboolean       is_folder)
4352 {
4353 	mode_t initial;
4354 	mode_t mask;
4355 	mode_t p;
4356 	const char *id;
4357 
4358 	if (is_folder) {
4359 		initial = (S_IRWXU | S_IRWXG | S_IRWXO);
4360 	} else {
4361 		initial = (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
4362 	}
4363 
4364 	umask (mask = umask (0));
4365 
4366 	p = ~mask & initial;
4367 
4368 	if (type == PERMISSION_USER) {
4369 		p &= ~(S_IRWXG | S_IRWXO);
4370 		if ((p & S_IRWXU) == S_IRWXU) {
4371 			id = "rwx";
4372 		} else if ((p & (S_IRUSR | S_IWUSR)) == (S_IRUSR | S_IWUSR)) {
4373 			id = "rw";
4374 		} else if ((p & (S_IRUSR | S_IXUSR)) == (S_IRUSR | S_IXUSR)) {
4375 			id = "rx";
4376 		} else if ((p & S_IRUSR) == S_IRUSR) {
4377 			id = "r";
4378 		} else {
4379 			id = "none";
4380 		}
4381 	} else if (type == PERMISSION_GROUP) {
4382 		p &= ~(S_IRWXU | S_IRWXO);
4383 		if ((p & S_IRWXG) == S_IRWXG) {
4384 			id = "rwx";
4385 		} else if ((p & (S_IRGRP | S_IWGRP)) == (S_IRGRP | S_IWGRP)) {
4386 			id = "rw";
4387 		} else if ((p & (S_IRGRP | S_IXGRP)) == (S_IRGRP | S_IXGRP)) {
4388 			id = "rx";
4389 		} else if ((p & S_IRGRP) == S_IRGRP) {
4390 			id = "r";
4391 		} else {
4392 			id = "none";
4393 		}
4394 	} else {
4395 		p &= ~(S_IRWXU | S_IRWXG);
4396 		if ((p & S_IRWXO) == S_IRWXO) {
4397 			id = "rwx";
4398 		} else if ((p & (S_IROTH | S_IWOTH)) == (S_IROTH | S_IWOTH)) {
4399 			id = "rw";
4400 		} else if ((p & (S_IROTH | S_IXOTH)) == (S_IROTH | S_IXOTH)) {
4401 			id = "rx";
4402 		} else if ((p & S_IROTH) == S_IROTH) {
4403 			id = "r";
4404 		} else {
4405 			id = "none";
4406 		}
4407 	}
4408 
4409 	gtk_combo_box_set_active_id (GTK_COMBO_BOX (combo), id);
4410 }
4411 
4412 static void
4413 on_change_permissions_clicked (GtkWidget                *button,
4414 			       NautilusPropertiesWindow *window)
4415 {
4416 	GtkWidget *dialog;
4417 	GtkWidget *label;
4418 	GtkWidget *combo;
4419 	GtkGrid *grid;
4420 
4421 	dialog = gtk_dialog_new_with_buttons (_("Change Permissions for Enclosed Files"),
4422 					       GTK_WINDOW (window),
4423 					       GTK_DIALOG_MODAL,
4424 					       GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
4425 					       _("Change"), GTK_RESPONSE_OK,
4426 					       NULL);
4427 
4428 	grid = GTK_GRID (create_grid_with_standard_properties ());
4429 	gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
4430 			    GTK_WIDGET (grid),
4431 			    TRUE, TRUE, 0);
4432 
4433 	label = gtk_label_new (_("Files"));
4434 	gtk_misc_set_alignment (GTK_MISC (label), 0.5, 0.5);
4435 	gtk_grid_attach (grid, label, 1, 0, 1, 1);
4436 	label = gtk_label_new (_("Folders"));
4437 	gtk_misc_set_alignment (GTK_MISC (label), 0.5, 0.5);
4438 	gtk_grid_attach (grid, label, 2, 0, 1, 1);
4439 
4440 	label = gtk_label_new (_("Owner:"));
4441 	gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
4442 	gtk_grid_attach (grid, label, 0, 1, 1, 1);
4443 	combo = create_permissions_combo_box (PERMISSION_USER, FALSE);
4444 	window->details->change_permission_combos = g_list_prepend (window->details->change_permission_combos,
4445 								    combo);
4446 	set_active_from_umask (combo, PERMISSION_USER, FALSE);
4447 	gtk_grid_attach (grid, combo, 1, 1, 1, 1);
4448 	combo = create_permissions_combo_box (PERMISSION_USER, TRUE);
4449 	window->details->change_permission_combos = g_list_prepend (window->details->change_permission_combos,
4450 								    combo);
4451 	set_active_from_umask (combo, PERMISSION_USER, TRUE);
4452 	gtk_grid_attach (grid, combo, 2, 1, 1, 1);
4453 
4454 	label = gtk_label_new (_("Group:"));
4455 	gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
4456 	gtk_grid_attach (grid, label, 0, 2, 1, 1);
4457 	combo = create_permissions_combo_box (PERMISSION_GROUP, FALSE);
4458 	window->details->change_permission_combos = g_list_prepend (window->details->change_permission_combos,
4459 								    combo);
4460 	set_active_from_umask (combo, PERMISSION_GROUP, FALSE);
4461 	gtk_grid_attach (grid, combo, 1, 2, 1, 1);
4462 	combo = create_permissions_combo_box (PERMISSION_GROUP, TRUE);
4463 	window->details->change_permission_combos = g_list_prepend (window->details->change_permission_combos,
4464 								    combo);
4465 	set_active_from_umask (combo, PERMISSION_GROUP, TRUE);
4466 	gtk_grid_attach (grid, combo, 2, 2, 1, 1);
4467 
4468 	label = gtk_label_new (_("Others:"));
4469 	gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
4470 	gtk_grid_attach (grid, label, 0, 3, 1, 1);
4471 	combo = create_permissions_combo_box (PERMISSION_OTHER, FALSE);
4472 	window->details->change_permission_combos = g_list_prepend (window->details->change_permission_combos,
4473 								    combo);
4474 	set_active_from_umask (combo, PERMISSION_OTHER, FALSE);
4475 	gtk_grid_attach (grid, combo, 1, 3, 1, 1);
4476 	combo = create_permissions_combo_box (PERMISSION_OTHER, TRUE);
4477 	window->details->change_permission_combos = g_list_prepend (window->details->change_permission_combos,
4478 								    combo);
4479 	set_active_from_umask (combo, PERMISSION_OTHER, TRUE);
4480 	gtk_grid_attach (grid, combo, 2, 3, 1, 1);
4481 
4482 	g_signal_connect (dialog, "response", G_CALLBACK (on_change_permissions_response), window);
4483 	gtk_widget_show_all (dialog);
4484 }
4485 
4486 static void
4487 create_permissions_page (NautilusPropertiesWindow *window)
4488 {
4489 	GtkWidget *vbox, *button, *hbox;
4490 	GtkGrid *page_grid;
4491 	char *file_name, *prompt_text;
4492 	GList *file_list;
4493 
4494 	vbox = create_page_with_vbox (window->details->notebook,
4495 				      _("Permissions"),
4496 				      "help:gnome-help/nautilus-file-properties-permissions");
4497 
4498 	file_list = window->details->original_files;
4499 
4500 	window->details->initial_permissions = NULL;
4501 	
4502 	if (all_can_get_permissions (file_list) && all_can_get_permissions (window->details->target_files)) {
4503 		window->details->initial_permissions = get_initial_permissions (window->details->target_files);
4504 		window->details->has_recursive_apply = files_has_changable_permissions_directory (window);
4505 		
4506 		if (!all_can_set_permissions (file_list)) {
4507 			add_prompt_and_separator (
4508 				vbox, 
4509 				_("You are not the owner, so you cannot change these permissions."));
4510 		}
4511 
4512 		page_grid = GTK_GRID (create_grid_with_standard_properties ());
4513 
4514 		gtk_widget_show (GTK_WIDGET (page_grid));
4515 		gtk_box_pack_start (GTK_BOX (vbox), 
4516 				    GTK_WIDGET (page_grid), 
4517 				    TRUE, TRUE, 0);
4518 
4519 		create_simple_permissions (window, page_grid);
4520 
4521 #ifdef HAVE_SELINUX
4522 		append_blank_slim_row (page_grid);
4523 		append_title_value_pair
4524 			(window, page_grid, _("Security context:"), 
4525 			 "selinux_context", INCONSISTENT_STATE_STRING,
4526 			 FALSE);
4527 #endif
4528 
4529 		append_blank_row (page_grid);
4530 
4531 		if (window->details->has_recursive_apply) {
4532 			hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
4533 			gtk_widget_show (hbox);
4534 
4535 			gtk_container_add_with_properties (GTK_CONTAINER (page_grid), hbox,
4536 							   "width", 2,
4537 							   NULL);
4538 
4539 			button = gtk_button_new_with_mnemonic (_("Change Permissions for Enclosed Files..."));
4540 			gtk_widget_show (button);
4541 			gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
4542 			g_signal_connect (button, "clicked",
4543 					  G_CALLBACK (on_change_permissions_clicked),
4544 					  window);
4545 		}
4546 	} else {
4547 		if (!is_multi_file_window (window)) {
4548 			file_name = nautilus_file_get_display_name (get_target_file (window));
4549 			prompt_text = g_strdup_printf (_("The permissions of “%s” could not be determined."), file_name);
4550 			g_free (file_name);
4551 		} else {
4552 			prompt_text = g_strdup (_("The permissions of the selected file could not be determined."));
4553 		}
4554 		
4555 		add_prompt (vbox, prompt_text, TRUE);
4556 		g_free (prompt_text);
4557 	}
4558 }
4559 
4560 static void
4561 append_extension_pages (NautilusPropertiesWindow *window)
4562 {
4563 	GList *providers;
4564 	GList *p;
4565 	
4566  	providers = nautilus_module_get_extensions_for_type (NAUTILUS_TYPE_PROPERTY_PAGE_PROVIDER);
4567 	
4568 	for (p = providers; p != NULL; p = p->next) {
4569 		NautilusPropertyPageProvider *provider;
4570 		GList *pages;
4571 		GList *l;
4572 
4573 		provider = NAUTILUS_PROPERTY_PAGE_PROVIDER (p->data);
4574 		
4575 		pages = nautilus_property_page_provider_get_pages 
4576 			(provider, window->details->original_files);
4577 		
4578 		for (l = pages; l != NULL; l = l->next) {
4579 			NautilusPropertyPage *page;
4580 			GtkWidget *page_widget;
4581 			GtkWidget *label;
4582 			
4583 			page = NAUTILUS_PROPERTY_PAGE (l->data);
4584 
4585 			g_object_get (G_OBJECT (page), 
4586 				      "page", &page_widget, "label", &label, 
4587 				      NULL);
4588 			
4589 			gtk_notebook_append_page (window->details->notebook, 
4590 						  page_widget, label);
4591 
4592 			g_object_set_data (G_OBJECT (page_widget), 
4593 					   "is-extension-page",
4594 					   page);
4595 
4596 			g_object_unref (page_widget);
4597 			g_object_unref (label);
4598 
4599 			g_object_unref (page);
4600 		}
4601 
4602 		g_list_free (pages);
4603 	}
4604 
4605 	nautilus_module_extension_list_free (providers);
4606 }
4607 
4608 static gboolean
4609 should_show_permissions (NautilusPropertiesWindow *window) 
4610 {
4611 	NautilusFile *file;
4612 
4613 	file = get_target_file (window);
4614 
4615 	/* Don't show permissions for Trash and Computer since they're not
4616 	 * really file system objects.
4617 	 */
4618 	if (!is_multi_file_window (window)
4619 	    && (is_merged_trash_directory (file) ||
4620 		is_recent_directory (file) ||
4621 		is_computer_directory (file))) {
4622 		return FALSE;
4623 	}
4624 
4625 	return TRUE;
4626 }
4627 
4628 static char *
4629 get_pending_key (GList *file_list)
4630 {
4631 	GList *l;
4632 	GList *uris;
4633 	GString *key;
4634 	char *ret;
4635 	
4636 	uris = NULL;
4637 	for (l = file_list; l != NULL; l = l->next) {
4638 		uris = g_list_prepend (uris, nautilus_file_get_uri (NAUTILUS_FILE (l->data)));
4639 	}
4640 	uris = g_list_sort (uris, (GCompareFunc)strcmp);
4641 
4642 	key = g_string_new ("");
4643 	for (l = uris; l != NULL; l = l->next) {
4644 		g_string_append (key, l->data);
4645 		g_string_append (key, ";");
4646 	}
4647 
4648 	g_list_free_full (uris, g_free);
4649 
4650 	ret = key->str;
4651 	g_string_free (key, FALSE);
4652 
4653 	return ret;
4654 }
4655 
4656 static StartupData *
4657 startup_data_new (GList *original_files, 
4658 		  GList *target_files,
4659 		  const char *pending_key,
4660 		  GtkWidget *parent_widget,
4661 		  const char *startup_id)
4662 {
4663 	StartupData *data;
4664 	GList *l;
4665 
4666 	data = g_new0 (StartupData, 1);
4667 	data->original_files = nautilus_file_list_copy (original_files);
4668 	data->target_files = nautilus_file_list_copy (target_files);
4669 	data->parent_widget = parent_widget;
4670 	data->startup_id = g_strdup (startup_id);
4671 	data->pending_key = g_strdup (pending_key);
4672 	data->pending_files = g_hash_table_new (g_direct_hash,
4673 						g_direct_equal);
4674 
4675 	for (l = data->target_files; l != NULL; l = l->next) {
4676 		g_hash_table_insert (data->pending_files, l->data, l->data);
4677 	}
4678 
4679 	return data;
4680 }
4681 
4682 static void
4683 startup_data_free (StartupData *data)
4684 {
4685 	nautilus_file_list_free (data->original_files);
4686 	nautilus_file_list_free (data->target_files);
4687 	g_hash_table_destroy (data->pending_files);
4688 	g_free (data->pending_key);
4689 	g_free (data->startup_id);
4690 	g_free (data);
4691 }
4692 
4693 static void
4694 file_changed_callback (NautilusFile *file, gpointer user_data)
4695 {
4696 	NautilusPropertiesWindow *window = NAUTILUS_PROPERTIES_WINDOW (user_data);
4697 
4698 	if (!g_list_find (window->details->changed_files, file)) {
4699 		nautilus_file_ref (file);
4700 		window->details->changed_files = g_list_prepend (window->details->changed_files, file);
4701 		schedule_files_update (window);
4702 	}
4703 }
4704 
4705 static gboolean
4706 is_a_special_file (NautilusFile *file)
4707 {
4708 	if (file == NULL ||
4709 	    NAUTILUS_IS_DESKTOP_ICON_FILE (file) ||
4710 	    nautilus_file_is_nautilus_link (file) ||
4711 	    is_merged_trash_directory (file) ||
4712 	    is_computer_directory (file)) {
4713 		return TRUE;
4714 	}
4715 	return FALSE;
4716 }
4717 
4718 static gboolean
4719 should_show_open_with (NautilusPropertiesWindow *window)
4720 {
4721 	NautilusFile *file;
4722 	char *mime_type;
4723 	char *extension;
4724 	gboolean hide;
4725 
4726 	/* Don't show open with tab for desktop special icons (trash, etc)
4727 	 * or desktop files. We don't get the open-with menu for these anyway.
4728 	 *
4729 	 * Also don't show it for folders. Changing the default app for folders
4730 	 * leads to all sort of hard to understand errors.
4731 	 */
4732 
4733 	if (is_multi_file_window (window)) {
4734 		GList *l;
4735 
4736 		if (!file_list_attributes_identical (window->details->target_files,
4737 						     "mime_type")) {
4738 			return FALSE;
4739 		}
4740 
4741 		for (l = window->details->target_files; l; l = l->next) {
4742 			file = NAUTILUS_FILE (l->data);
4743 			if (nautilus_file_is_directory (file) || is_a_special_file (file)) {
4744 				return FALSE;
4745 			}
4746 		}
4747 
4748 		/* since we just confirmed all the mime types are the
4749 		   same we only need to test one file */
4750 		file = window->details->target_files->data;
4751 	} else {
4752 		file = get_target_file (window);
4753 
4754 		if (nautilus_file_is_directory (file) || is_a_special_file (file)) {
4755 			return FALSE;
4756 		}
4757 	}
4758 
4759 	mime_type = nautilus_file_get_mime_type (file);
4760 	extension = nautilus_file_get_extension (file);
4761 	hide = (g_content_type_is_unknown (mime_type) && extension == NULL);
4762 	g_free (mime_type);
4763 	g_free (extension);
4764 
4765 	return !hide;
4766 }
4767 
4768 static void
4769 create_open_with_page (NautilusPropertiesWindow *window)
4770 {
4771 	GtkWidget *vbox;
4772 	char *mime_type;
4773 	GList *files = NULL;
4774 	NautilusFile *target_file;
4775 
4776 	target_file = get_target_file (window);
4777 	mime_type = nautilus_file_get_mime_type (target_file);
4778 
4779 	if (!is_multi_file_window (window)) {
4780 		files = g_list_prepend (NULL, target_file);
4781 	} else {
4782 		files = g_list_copy (window->details->original_files);
4783 		if (files == NULL) {
4784 			return;
4785 		}
4786 	}
4787 
4788 	vbox = nautilus_mime_application_chooser_new (files, mime_type);
4789 
4790 	gtk_widget_show (vbox);
4791 	g_free (mime_type);
4792 	g_list_free (files);
4793 
4794 	g_object_set_data_full (G_OBJECT (vbox), "help-uri", g_strdup ("help:gnome-help/files-open"), g_free);
4795 	gtk_notebook_append_page (window->details->notebook, 
4796 				  vbox, gtk_label_new (_("Open With")));
4797 }
4798 
4799 
4800 static NautilusPropertiesWindow *
4801 create_properties_window (StartupData *startup_data)
4802 {
4803 	NautilusPropertiesWindow *window;
4804 	GList *l;
4805 
4806 	window = NAUTILUS_PROPERTIES_WINDOW (gtk_widget_new (NAUTILUS_TYPE_PROPERTIES_WINDOW, NULL));
4807 
4808 	window->details->original_files = nautilus_file_list_copy (startup_data->original_files);
4809 	
4810 	window->details->target_files = nautilus_file_list_copy (startup_data->target_files);
4811 
4812 	gtk_window_set_wmclass (GTK_WINDOW (window), "file_properties", "Nautilus");
4813 
4814 	if (startup_data->parent_widget) {
4815 		gtk_window_set_screen (GTK_WINDOW (window),
4816 				       gtk_widget_get_screen (startup_data->parent_widget));
4817 	}
4818 
4819 	if (startup_data->startup_id) {
4820 		gtk_window_set_startup_id (GTK_WINDOW (window), startup_data->startup_id);
4821 	}
4822 
4823 	gtk_window_set_type_hint (GTK_WINDOW (window), GDK_WINDOW_TYPE_HINT_DIALOG);
4824 
4825 	/* Set initial window title */
4826 	update_properties_window_title (window);
4827 
4828 	/* Start monitoring the file attributes we display. Note that some
4829 	 * of the attributes are for the original file, and some for the
4830 	 * target files.
4831 	 */
4832 
4833 	for (l = window->details->original_files; l != NULL; l = l->next) {
4834 		NautilusFile *file;
4835 		NautilusFileAttributes attributes;
4836 
4837 		file = NAUTILUS_FILE (l->data);
4838 
4839 		attributes =
4840 			NAUTILUS_FILE_ATTRIBUTES_FOR_ICON |
4841 			NAUTILUS_FILE_ATTRIBUTE_INFO |
4842 			NAUTILUS_FILE_ATTRIBUTE_LINK_INFO;
4843 
4844 		nautilus_file_monitor_add (file,
4845 					   &window->details->original_files, 
4846 					   attributes);	
4847 	}
4848 	
4849 	for (l = window->details->target_files; l != NULL; l = l->next) {
4850 		NautilusFile *file;
4851 		NautilusFileAttributes attributes;
4852 
4853 		file = NAUTILUS_FILE (l->data);
4854 		
4855 		attributes = 0;
4856 		if (nautilus_file_is_directory (file)) {
4857 			attributes |= NAUTILUS_FILE_ATTRIBUTE_DEEP_COUNTS;
4858 		}
4859 		
4860 		attributes |= NAUTILUS_FILE_ATTRIBUTE_INFO;
4861 		nautilus_file_monitor_add (file, &window->details->target_files, attributes);
4862 	}	
4863 		
4864 	for (l = window->details->target_files; l != NULL; l = l->next) {
4865 		g_signal_connect_object (NAUTILUS_FILE (l->data),
4866 					 "changed",
4867 					 G_CALLBACK (file_changed_callback),
4868 					 G_OBJECT (window),
4869 					 0);
4870 	}
4871 
4872 	for (l = window->details->original_files; l != NULL; l = l->next) {
4873 		g_signal_connect_object (NAUTILUS_FILE (l->data),
4874 					 "changed",
4875 					 G_CALLBACK (file_changed_callback),
4876 					 G_OBJECT (window),
4877 					 0);
4878 	}
4879 
4880 	/* Create the notebook tabs. */
4881 	window->details->notebook = GTK_NOTEBOOK (gtk_notebook_new ());
4882 	gtk_widget_show (GTK_WIDGET (window->details->notebook));
4883 	gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (window))),
4884 			    GTK_WIDGET (window->details->notebook),
4885 			    TRUE, TRUE, 0);
4886 
4887 	/* Create the pages. */
4888 	create_basic_page (window);
4889 
4890 	if (should_show_permissions (window)) {
4891 		create_permissions_page (window);
4892 	}
4893 
4894 	if (should_show_open_with (window)) {
4895 		create_open_with_page (window);
4896 	}
4897 
4898 	/* append pages from available views */
4899 	append_extension_pages (window);
4900 
4901 	gtk_dialog_add_buttons (GTK_DIALOG (window),
4902 				GTK_STOCK_HELP, GTK_RESPONSE_HELP,
4903 				GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
4904 				NULL);
4905 
4906 	/* FIXME - HIGificiation, should be done inside GTK+ */
4907 	gtk_container_set_border_width (GTK_CONTAINER (window), 5);
4908 	gtk_container_set_border_width (GTK_CONTAINER (window->details->notebook), 5);
4909 	gtk_container_set_border_width (GTK_CONTAINER (gtk_dialog_get_action_area (GTK_DIALOG (window))), 0);
4910 
4911 	/* Update from initial state */
4912 	properties_window_update (window, NULL);
4913 
4914 	return window;
4915 }
4916 
4917 static GList *
4918 get_target_file_list (GList *original_files)
4919 {
4920 	GList *ret;
4921 	GList *l;
4922 	
4923 	ret = NULL;
4924 	
4925 	for (l = original_files; l != NULL; l = l->next) {
4926 		NautilusFile *target;
4927 		
4928 		target = get_target_file_for_original_file (NAUTILUS_FILE (l->data));
4929 		
4930 		ret = g_list_prepend (ret, target);
4931 	}
4932 
4933 	ret = g_list_reverse (ret);
4934 
4935 	return ret;
4936 }
4937 
4938 static void
4939 add_window (NautilusPropertiesWindow *window)
4940 {
4941 	if (!is_multi_file_window (window)) {
4942 		g_hash_table_insert (windows,
4943 				     get_original_file (window), 
4944 				     window);
4945 		g_object_set_data (G_OBJECT (window), "window_key", 
4946 				   get_original_file (window));
4947 	}
4948 }
4949 
4950 static void
4951 remove_window (NautilusPropertiesWindow *window)
4952 {
4953 	gpointer key;
4954 
4955 	key = g_object_get_data (G_OBJECT (window), "window_key");
4956 	if (key) {
4957 		g_hash_table_remove (windows, key);
4958 	}
4959 }
4960 
4961 static GtkWindow *
4962 get_existing_window (GList *file_list)
4963 {
4964 	if (!file_list->next) {
4965 		return g_hash_table_lookup (windows, file_list->data);
4966 	}	
4967 
4968 	return NULL;
4969 }
4970 
4971 static void
4972 cancel_create_properties_window_callback (gpointer callback_data)
4973 {
4974 	remove_pending ((StartupData *)callback_data, TRUE, FALSE, TRUE);
4975 }
4976 
4977 static void
4978 parent_widget_destroyed_callback (GtkWidget *widget, gpointer callback_data)
4979 {
4980 	g_assert (widget == ((StartupData *)callback_data)->parent_widget);
4981 	
4982 	remove_pending ((StartupData *)callback_data, TRUE, TRUE, FALSE);
4983 }
4984 
4985 static void
4986 cancel_call_when_ready_callback (gpointer key,
4987 				 gpointer value,
4988 				 gpointer user_data)
4989 {
4990 	nautilus_file_cancel_call_when_ready 
4991 		(NAUTILUS_FILE (key), 
4992 		 is_directory_ready_callback, 
4993 		 user_data);
4994 }
4995 
4996 static void
4997 remove_pending (StartupData *startup_data,
4998 		gboolean cancel_call_when_ready,
4999 		gboolean cancel_timed_wait,
5000 		gboolean cancel_destroy_handler)
5001 {
5002 	if (cancel_call_when_ready) {
5003 		g_hash_table_foreach (startup_data->pending_files,
5004 				      cancel_call_when_ready_callback,
5005 				      startup_data);
5006 				      
5007 	}
5008 	if (cancel_timed_wait) {
5009 		eel_timed_wait_stop 
5010 			(cancel_create_properties_window_callback, startup_data);
5011 	}
5012 	if (cancel_destroy_handler && startup_data->parent_widget) {
5013 		g_signal_handlers_disconnect_by_func (startup_data->parent_widget,
5014 						      G_CALLBACK (parent_widget_destroyed_callback),
5015 						      startup_data);
5016 	}
5017 
5018 	g_hash_table_remove (pending_lists, startup_data->pending_key);
5019 
5020 	startup_data_free (startup_data);
5021 }
5022 
5023 static void
5024 is_directory_ready_callback (NautilusFile *file,
5025 			     gpointer data)
5026 {
5027 	StartupData *startup_data;
5028 	
5029 	startup_data = data;
5030 	
5031 	g_hash_table_remove (startup_data->pending_files, file);
5032 
5033 	if (g_hash_table_size (startup_data->pending_files) == 0) {
5034 		NautilusPropertiesWindow *new_window;
5035 		
5036 		new_window = create_properties_window (startup_data);
5037 		
5038 		add_window (new_window);
5039 		
5040 		remove_pending (startup_data, FALSE, TRUE, TRUE);
5041 		
5042 		gtk_window_present (GTK_WINDOW (new_window));
5043 	}
5044 }
5045 
5046 
5047 void
5048 nautilus_properties_window_present (GList       *original_files,
5049 				    GtkWidget   *parent_widget,
5050 				    const gchar *startup_id) 
5051 {
5052 	GList *l, *next;
5053 	GtkWidget *parent_window;
5054 	StartupData *startup_data;
5055 	GList *target_files;
5056 	GtkWindow *existing_window;
5057 	char *pending_key;
5058 
5059 	g_return_if_fail (original_files != NULL);
5060 	g_return_if_fail (parent_widget == NULL || GTK_IS_WIDGET (parent_widget));
5061 
5062 	/* Create the hash tables first time through. */
5063 	if (windows == NULL) {
5064 		windows = g_hash_table_new (NULL, NULL);
5065 	}
5066 	
5067 	if (pending_lists == NULL) {
5068 		pending_lists = g_hash_table_new (g_str_hash, g_str_equal);
5069 	}
5070 	
5071 	/* Look to see if there's already a window for this file. */
5072 	existing_window = get_existing_window (original_files);
5073 	if (existing_window != NULL) {
5074 		if (parent_widget)
5075 			gtk_window_set_screen (existing_window,
5076 					       gtk_widget_get_screen (parent_widget));
5077 		else if (startup_id)
5078 			gtk_window_set_startup_id (existing_window, startup_id);
5079 
5080 		gtk_window_present (existing_window);
5081 		return;
5082 	}
5083 
5084 
5085 	pending_key = get_pending_key (original_files);
5086 	
5087 	/* Look to see if we're already waiting for a window for this file. */
5088 	if (g_hash_table_lookup (pending_lists, pending_key) != NULL) {
5089 		return;
5090 	}
5091 
5092 	target_files = get_target_file_list (original_files);
5093 
5094 	startup_data = startup_data_new (original_files, 
5095 					 target_files,
5096 					 pending_key,
5097 					 parent_widget,
5098 					 startup_id);
5099 
5100 	nautilus_file_list_free (target_files);
5101 	g_free(pending_key);
5102 
5103 	/* Wait until we can tell whether it's a directory before showing, since
5104 	 * some one-time layout decisions depend on that info. 
5105 	 */
5106 	
5107 	g_hash_table_insert (pending_lists, startup_data->pending_key, startup_data->pending_key);
5108 	if (parent_widget) {
5109 		g_signal_connect (parent_widget, "destroy",
5110 				  G_CALLBACK (parent_widget_destroyed_callback), startup_data);
5111 
5112 		parent_window = gtk_widget_get_ancestor (parent_widget, GTK_TYPE_WINDOW);
5113 	} else
5114 		parent_window = NULL;
5115 
5116 	eel_timed_wait_start
5117 		(cancel_create_properties_window_callback,
5118 		 startup_data,
5119 		 _("Creating Properties window."),
5120 		 parent_window == NULL ? NULL : GTK_WINDOW (parent_window));
5121 
5122 	for (l = startup_data->target_files; l != NULL; l = next) {
5123 		next = l->next;
5124 		nautilus_file_call_when_ready
5125 			(NAUTILUS_FILE (l->data),
5126 			 NAUTILUS_FILE_ATTRIBUTE_INFO,
5127 			 is_directory_ready_callback,
5128 			 startup_data);
5129 	}
5130 }
5131 
5132 static void
5133 real_response (GtkDialog *dialog,
5134 	       int        response)
5135 {
5136 	GError *error = NULL;
5137 	NautilusPropertiesWindow *window = NAUTILUS_PROPERTIES_WINDOW (dialog);
5138 	GtkWidget *curpage;
5139 	const char *helpuri;
5140 
5141 	switch (response) {
5142 	case GTK_RESPONSE_HELP:
5143 		curpage = gtk_notebook_get_nth_page (window->details->notebook,
5144 						     gtk_notebook_get_current_page (window->details->notebook));
5145 		helpuri = g_object_get_data (G_OBJECT (curpage), "help-uri");
5146 		gtk_show_uri (gtk_window_get_screen (GTK_WINDOW (dialog)),
5147 			      helpuri ? helpuri : "help:gnome-help/files",
5148 			      gtk_get_current_event_time (),
5149 			      &error);
5150 		if (error != NULL) {
5151 			eel_show_error_dialog (_("There was an error displaying help."), error->message,
5152 					       GTK_WINDOW (dialog));
5153 			g_error_free (error);
5154 		}
5155 		break;
5156 
5157 	case GTK_RESPONSE_NONE:
5158 	case GTK_RESPONSE_CLOSE:
5159 	case GTK_RESPONSE_DELETE_EVENT:
5160 		gtk_widget_destroy (GTK_WIDGET (dialog));
5161 		break;
5162 
5163 	default:
5164 		g_assert_not_reached ();
5165 		break;
5166 	}
5167 }
5168 
5169 static void
5170 real_destroy (GtkWidget *object)
5171 {
5172 	NautilusPropertiesWindow *window;
5173 	GList *l;
5174 
5175 	window = NAUTILUS_PROPERTIES_WINDOW (object);
5176 
5177 	remove_window (window);
5178 
5179 	for (l = window->details->original_files; l != NULL; l = l->next) {
5180 		nautilus_file_monitor_remove (NAUTILUS_FILE (l->data), &window->details->original_files);
5181 	}
5182 	nautilus_file_list_free (window->details->original_files);
5183 	window->details->original_files = NULL;
5184 	
5185 	for (l = window->details->target_files; l != NULL; l = l->next) {
5186 		nautilus_file_monitor_remove (NAUTILUS_FILE (l->data), &window->details->target_files);
5187 	}
5188 	nautilus_file_list_free (window->details->target_files);
5189 	window->details->target_files = NULL;
5190 
5191 	nautilus_file_list_free (window->details->changed_files);
5192 	window->details->changed_files = NULL;
5193 
5194 	if (window->details->deep_count_spinner_timeout_id > 0) {
5195 		g_source_remove (window->details->deep_count_spinner_timeout_id);
5196 	}
5197 
5198 	for (l = window->details->deep_count_files; l != NULL; l = l->next) {
5199 		stop_deep_count_for_file (window, l->data);
5200 	}
5201 	g_list_free (window->details->deep_count_files);
5202 	window->details->deep_count_files = NULL;
5203 
5204 	window->details->name_field = NULL;
5205 
5206 	g_list_free (window->details->permission_buttons);
5207 	window->details->permission_buttons = NULL;
5208 
5209 	g_list_free (window->details->permission_combos);
5210 	window->details->permission_combos = NULL;
5211 
5212 	g_list_free (window->details->change_permission_combos);
5213 	window->details->change_permission_combos = NULL;
5214 
5215 	if (window->details->initial_permissions) {
5216 		g_hash_table_destroy (window->details->initial_permissions);
5217 		window->details->initial_permissions = NULL;
5218 	}
5219 
5220 	g_list_free (window->details->value_fields);
5221 	window->details->value_fields = NULL;
5222 
5223 	if (window->details->update_directory_contents_timeout_id != 0) {
5224 		g_source_remove (window->details->update_directory_contents_timeout_id);
5225 		window->details->update_directory_contents_timeout_id = 0;
5226 	}
5227 
5228 	if (window->details->update_files_timeout_id != 0) {
5229 		g_source_remove (window->details->update_files_timeout_id);
5230 		window->details->update_files_timeout_id = 0;
5231 	}
5232 
5233 	GTK_WIDGET_CLASS (nautilus_properties_window_parent_class)->destroy (object);
5234 }
5235 
5236 static void
5237 real_finalize (GObject *object)
5238 {
5239 	NautilusPropertiesWindow *window;
5240 
5241 	window = NAUTILUS_PROPERTIES_WINDOW (object);
5242 
5243 	g_list_free_full (window->details->mime_list, g_free);
5244 
5245 	g_free (window->details->pending_name);
5246 
5247 	G_OBJECT_CLASS (nautilus_properties_window_parent_class)->finalize (object);
5248 }
5249 
5250 /* converts
5251  *  file://foo/foobar/foofoo/bar
5252  * to
5253  *  foofoo/bar
5254  * if
5255  *  file://foo/foobar
5256  * is the parent
5257  *
5258  * It does not resolve any symlinks.
5259  * */
5260 static char *
5261 make_relative_uri_from_full (const char *uri,
5262 			     const char *base_uri)
5263 {
5264 	g_assert (uri != NULL);
5265 	g_assert (base_uri != NULL);
5266 
5267 	if (g_str_has_prefix (uri, base_uri)) {
5268 		uri += strlen (base_uri);
5269 		if (*uri != '/') {
5270 			return NULL;
5271 		}
5272 
5273 		while (*uri == '/') {
5274 			uri++;
5275 		}
5276 
5277 		if (*uri != '\0') {
5278 			return g_strdup (uri);
5279 		}
5280 	}
5281 
5282 	return NULL;
5283 }
5284 
5285 /* icon selection callback to set the image of the file object to the selected file */
5286 static void
5287 set_icon (const char* icon_uri, NautilusPropertiesWindow *properties_window)
5288 {
5289 	NautilusFile *file;
5290 	char *file_uri;
5291 	char *icon_path;
5292 	char *real_icon_uri;
5293 
5294 	g_assert (icon_uri != NULL);
5295 	g_assert (NAUTILUS_IS_PROPERTIES_WINDOW (properties_window));
5296 
5297 	icon_path = g_filename_from_uri (icon_uri, NULL, NULL);
5298 	/* we don't allow remote URIs */
5299 	if (icon_path != NULL) {
5300 		GList *l;
5301 
5302 		for (l = properties_window->details->original_files; l != NULL; l = l->next) {
5303 			file = NAUTILUS_FILE (l->data);
5304 
5305 			file_uri = nautilus_file_get_uri (file);
5306 
5307 			if (nautilus_file_is_mime_type (file, "application/x-desktop")) {
5308 				if (nautilus_link_local_set_icon (file_uri, icon_path)) {
5309 					nautilus_file_invalidate_attributes (file,
5310 									     NAUTILUS_FILE_ATTRIBUTE_INFO |
5311 									     NAUTILUS_FILE_ATTRIBUTE_LINK_INFO);
5312 				}
5313 			} else {
5314 				real_icon_uri = make_relative_uri_from_full (icon_uri, file_uri);
5315 				if (real_icon_uri == NULL) {
5316 					real_icon_uri = g_strdup (icon_uri);
5317 				}
5318 			
5319 				nautilus_file_set_metadata (file, NAUTILUS_METADATA_KEY_CUSTOM_ICON, NULL, real_icon_uri);
5320 				nautilus_file_set_metadata (file, NAUTILUS_METADATA_KEY_ICON_SCALE, NULL, NULL);
5321 
5322 				g_free (real_icon_uri);
5323 			}
5324 
5325 			g_free (file_uri);
5326 		}
5327 
5328 		g_free (icon_path);
5329 	}
5330 }
5331 
5332 static void
5333 update_preview_callback (GtkFileChooser *icon_chooser,
5334 			 NautilusPropertiesWindow *window)
5335 {
5336 	GtkWidget *preview_widget;
5337 	GdkPixbuf *pixbuf, *scaled_pixbuf;
5338 	char *filename;
5339 	double scale;
5340 
5341 	pixbuf = NULL;
5342 
5343 	filename = gtk_file_chooser_get_filename (icon_chooser);
5344 	if (filename != NULL) {
5345 		pixbuf = gdk_pixbuf_new_from_file (filename, NULL);
5346 	}
5347 
5348 	if (pixbuf != NULL) {
5349 		preview_widget = gtk_file_chooser_get_preview_widget (icon_chooser);
5350 		gtk_file_chooser_set_preview_widget_active (icon_chooser, TRUE);
5351 
5352 		if (gdk_pixbuf_get_width (pixbuf) > PREVIEW_IMAGE_WIDTH) {
5353 			scale = (double)gdk_pixbuf_get_height (pixbuf) /
5354 				gdk_pixbuf_get_width (pixbuf);
5355 
5356 			scaled_pixbuf = gnome_desktop_thumbnail_scale_down_pixbuf
5357 				(pixbuf,
5358 				 PREVIEW_IMAGE_WIDTH,
5359 				 scale * PREVIEW_IMAGE_WIDTH);
5360 			g_object_unref (pixbuf);
5361 			pixbuf = scaled_pixbuf;
5362 		}
5363 
5364 		gtk_image_set_from_pixbuf (GTK_IMAGE (preview_widget), pixbuf);
5365 	} else {
5366 		gtk_file_chooser_set_preview_widget_active (icon_chooser, FALSE);
5367 	}
5368 
5369 	g_free (filename);
5370 
5371 	if (pixbuf != NULL) {
5372 		g_object_unref (pixbuf);
5373 	}
5374 }
5375 
5376 static void
5377 custom_icon_file_chooser_response_cb (GtkDialog *dialog,
5378 				      gint response,
5379 				      NautilusPropertiesWindow *window)
5380 {
5381 	char *uri;
5382 
5383 	switch (response) {
5384 	case GTK_RESPONSE_NO:
5385 		reset_icon (window);
5386 		break;
5387 
5388 	case GTK_RESPONSE_OK:
5389 		uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
5390 		set_icon (uri, window);
5391 		g_free (uri);
5392 		break;
5393 
5394 	default:
5395 		break;
5396 	}
5397 
5398 	gtk_widget_hide (GTK_WIDGET (dialog));
5399 }
5400 
5401 static void
5402 select_image_button_callback (GtkWidget *widget,
5403 			      NautilusPropertiesWindow *window)
5404 {
5405 	GtkWidget *dialog, *preview;
5406 	GtkFileFilter *filter;
5407 	GList *l;
5408 	NautilusFile *file;
5409 	char *uri;
5410 	char *image_path;
5411 	gboolean revert_is_sensitive;
5412 
5413 	g_assert (NAUTILUS_IS_PROPERTIES_WINDOW (window));
5414 
5415 	dialog = window->details->icon_chooser;
5416 
5417 	if (dialog == NULL) {
5418 		dialog = gtk_file_chooser_dialog_new (_("Select Custom Icon"), GTK_WINDOW (window),
5419 						      GTK_FILE_CHOOSER_ACTION_OPEN,
5420 						      GTK_STOCK_REVERT_TO_SAVED, GTK_RESPONSE_NO,
5421 						      GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
5422 						      GTK_STOCK_OPEN, GTK_RESPONSE_OK,
5423 						      NULL);
5424 		gtk_file_chooser_add_shortcut_folder (GTK_FILE_CHOOSER (dialog),
5425 						      g_get_user_special_dir (G_USER_DIRECTORY_PICTURES),
5426 						      NULL);
5427 		gtk_window_set_destroy_with_parent (GTK_WINDOW (dialog), TRUE);
5428 
5429 		filter = gtk_file_filter_new ();
5430 		gtk_file_filter_add_pixbuf_formats (filter);
5431 		gtk_file_chooser_set_filter (GTK_FILE_CHOOSER (dialog), filter);
5432 
5433 		preview = gtk_image_new ();
5434 		gtk_widget_set_size_request (preview, PREVIEW_IMAGE_WIDTH, -1);
5435 		gtk_file_chooser_set_preview_widget (GTK_FILE_CHOOSER (dialog), preview);
5436 		gtk_file_chooser_set_use_preview_label (GTK_FILE_CHOOSER (dialog), FALSE);
5437 		gtk_file_chooser_set_preview_widget_active (GTK_FILE_CHOOSER (dialog), FALSE);
5438 
5439 		g_signal_connect (dialog, "update-preview",
5440 				  G_CALLBACK (update_preview_callback), window);
5441 
5442 		window->details->icon_chooser = dialog;
5443 
5444 		g_object_add_weak_pointer (G_OBJECT (dialog),
5445 					   (gpointer *) &window->details->icon_chooser);
5446 	}
5447 
5448 	/* it's likely that the user wants to pick an icon that is inside a local directory */
5449 	if (g_list_length (window->details->original_files) == 1) {
5450 		file = NAUTILUS_FILE (window->details->original_files->data);
5451 
5452 		if (nautilus_file_is_directory (file)) {
5453 			uri = nautilus_file_get_uri (file);
5454 
5455 			image_path = g_filename_from_uri (uri, NULL, NULL);
5456 			if (image_path != NULL) {
5457 				gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog), image_path);
5458 				g_free (image_path);
5459 			}
5460 
5461 			g_free (uri);
5462 		}
5463 	}
5464 
5465 	revert_is_sensitive = FALSE;
5466 	for (l = window->details->original_files; l != NULL; l = l->next) {
5467 		file = NAUTILUS_FILE (l->data);
5468 		image_path = nautilus_file_get_metadata (file, NAUTILUS_METADATA_KEY_CUSTOM_ICON, NULL);
5469 		revert_is_sensitive = (image_path != NULL);
5470 		g_free (image_path);
5471 
5472 		if (revert_is_sensitive) {
5473 			break;
5474 		}
5475 	}
5476 	gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog), GTK_RESPONSE_NO, revert_is_sensitive);
5477 
5478 	g_signal_connect (dialog, "response",
5479 			  G_CALLBACK (custom_icon_file_chooser_response_cb), window);
5480 	gtk_widget_show (dialog);
5481 }
5482 
5483 static void
5484 nautilus_properties_window_class_init (NautilusPropertiesWindowClass *class)
5485 {
5486 	GtkBindingSet *binding_set;
5487 
5488 	G_OBJECT_CLASS (class)->finalize = real_finalize;
5489 	GTK_WIDGET_CLASS (class)->destroy = real_destroy;
5490 	GTK_DIALOG_CLASS (class)->response = real_response;
5491 
5492 	binding_set = gtk_binding_set_by_class (class);
5493 	gtk_binding_entry_add_signal (binding_set, GDK_KEY_Escape, 0,
5494 				      "close", 0);
5495 
5496 	g_type_class_add_private (class, sizeof (NautilusPropertiesWindowDetails));
5497 }
5498 
5499 static void
5500 nautilus_properties_window_init (NautilusPropertiesWindow *window)
5501 {
5502 	window->details = G_TYPE_INSTANCE_GET_PRIVATE (window, NAUTILUS_TYPE_PROPERTIES_WINDOW,
5503 						       NautilusPropertiesWindowDetails);
5504 }