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, ¤t_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