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