1
/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
3
/* fm-properties-window.c - window that lets user modify file properties
5
Copyright (C) 2000 Eazel, Inc.
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.
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.
17
You should have received a copy of the GNU Library General Public
18
License along with the Gnome Library; see the file COPYING.LIB. If not,
19
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20
Boston, MA 02111-1307, USA.
22
Authors: Darin Adler <darin@bentspoon.com>
27
#include "nautilus-properties-window.h"
29
#include "nautilus-desktop-item-properties.h"
30
#include "nautilus-error-reporting.h"
31
#include "nautilus-mime-actions.h"
34
#include <gdk/gdkkeysyms.h>
35
#include <glib/gi18n.h>
40
#define GNOME_DESKTOP_USE_UNSTABLE_API
41
#include <libgnome-desktop/gnome-desktop-thumbnail.h>
43
#include <eel/eel-accessibility.h>
44
#include <eel/eel-glib-extensions.h>
45
#include <eel/eel-gnome-extensions.h>
46
#include <eel/eel-gtk-extensions.h>
47
#include <eel/eel-stock-dialogs.h>
48
#include <eel/eel-string.h>
49
#include <eel/eel-vfs-extensions.h>
51
#include <libnautilus-extension/nautilus-property-page-provider.h>
52
#include <libnautilus-private/nautilus-entry.h>
53
#include <libnautilus-private/nautilus-file-attributes.h>
54
#include <libnautilus-private/nautilus-file-operations.h>
55
#include <libnautilus-private/nautilus-file-utilities.h>
56
#include <libnautilus-private/nautilus-desktop-icon-file.h>
57
#include <libnautilus-private/nautilus-global-preferences.h>
58
#include <libnautilus-private/nautilus-link.h>
59
#include <libnautilus-private/nautilus-metadata.h>
60
#include <libnautilus-private/nautilus-mime-application-chooser.h>
61
#include <libnautilus-private/nautilus-module.h>
65
#elif HAVE_SYS_MOUNT_H
67
#include <sys/param.h>
69
#include <sys/mount.h>
72
#define UNKNOWN_FILL_R 0.5333333333333333
73
#define UNKNOWN_FILL_G 0.5411764705882353
74
#define UNKNOWN_FILL_B 0.5215686274509804
76
#define USED_FILL_R 0.4470588235294118
77
#define USED_FILL_G 0.6235294117647059
78
#define USED_FILL_B 0.8117647058823529
80
#define FREE_FILL_R 0.9333333333333333
81
#define FREE_FILL_G 0.9333333333333333
82
#define FREE_FILL_B 0.9254901960784314
85
#define PREVIEW_IMAGE_WIDTH 96
89
static GHashTable *windows;
90
static GHashTable *pending_lists;
92
struct NautilusPropertiesWindowDetails {
93
GList *original_files;
96
GtkNotebook *notebook;
100
GtkWidget *icon_button;
101
GtkWidget *icon_image;
102
GtkWidget *icon_chooser;
104
GtkLabel *name_label;
105
GtkWidget *name_field;
106
unsigned int name_row;
109
GtkLabel *directory_contents_title_field;
110
GtkLabel *directory_contents_value_field;
111
GtkWidget *directory_contents_spinner;
112
guint update_directory_contents_timeout_id;
113
guint update_files_timeout_id;
115
NautilusFile *group_change_file;
116
char *group_change_group;
117
unsigned int group_change_timeout;
118
NautilusFile *owner_change_file;
119
char *owner_change_owner;
120
unsigned int owner_change_timeout;
122
GList *permission_buttons;
123
GList *permission_combos;
124
GList *change_permission_combos;
125
GHashTable *initial_permissions;
126
gboolean has_recursive_apply;
132
gboolean deep_count_finished;
133
GList *deep_count_files;
134
guint deep_count_spinner_timeout_id;
139
guint long_operation_underway;
141
GList *changed_files;
143
guint64 volume_capacity;
149
GdkRGBA unknown_color;
150
GdkRGBA used_stroke_color;
151
GdkRGBA free_stroke_color;
152
GdkRGBA unknown_stroke_color;
164
GList *original_files;
166
GtkWidget *parent_widget;
169
GHashTable *pending_files;
172
/* drag and drop definitions */
176
TARGET_GNOME_URI_LIST,
179
static const GtkTargetEntry target_table[] = {
180
{ "text/uri-list", 0, TARGET_URI_LIST },
181
{ "x-special/gnome-icon-list", 0, TARGET_GNOME_URI_LIST },
184
#define DIRECTORY_CONTENTS_UPDATE_INTERVAL 200 /* milliseconds */
185
#define FILES_UPDATE_INTERVAL 200 /* milliseconds */
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.
192
* Both combos react on changes by scheduling a new change and unscheduling
193
* or cancelling old pending changes.
195
#define CHOWN_CHGRP_TIMEOUT 300 /* milliseconds */
197
static void schedule_directory_contents_update (NautilusPropertiesWindow *window);
198
static void directory_contents_value_field_update (NautilusPropertiesWindow *window);
199
static void file_changed_callback (NautilusFile *file,
201
static void permission_button_update (NautilusPropertiesWindow *window,
202
GtkToggleButton *button);
203
static void permission_combo_update (NautilusPropertiesWindow *window,
205
static void value_field_update (NautilusPropertiesWindow *window,
207
static void properties_window_update (NautilusPropertiesWindow *window,
209
static void is_directory_ready_callback (NautilusFile *file,
211
static void cancel_group_change_callback (NautilusPropertiesWindow *window);
212
static void cancel_owner_change_callback (NautilusPropertiesWindow *window);
213
static void parent_widget_destroyed_callback (GtkWidget *widget,
214
gpointer callback_data);
215
static void select_image_button_callback (GtkWidget *widget,
216
NautilusPropertiesWindow *properties_window);
217
static void set_icon (const char *icon_path,
218
NautilusPropertiesWindow *properties_window);
219
static void remove_pending (StartupData *data,
220
gboolean cancel_call_when_ready,
221
gboolean cancel_timed_wait,
222
gboolean cancel_destroy_handler);
223
static void append_extension_pages (NautilusPropertiesWindow *window);
225
static gboolean name_field_focus_out (NautilusEntry *name_field,
226
GdkEventFocus *event,
227
gpointer callback_data);
228
static void name_field_activate (NautilusEntry *name_field,
229
gpointer callback_data);
230
static GtkLabel *attach_ellipsizing_value_label (GtkGrid *grid,
232
const char *initial_text);
234
static GtkWidget* create_pie_widget (NautilusPropertiesWindow *window);
236
G_DEFINE_TYPE (NautilusPropertiesWindow, nautilus_properties_window, GTK_TYPE_DIALOG);
239
is_multi_file_window (NautilusPropertiesWindow *window)
246
for (l = window->details->original_files; l != NULL; l = l->next) {
247
if (!nautilus_file_is_gone (NAUTILUS_FILE (l->data))) {
259
get_not_gone_original_file_count (NautilusPropertiesWindow *window)
266
for (l = window->details->original_files; l != NULL; l = l->next) {
267
if (!nautilus_file_is_gone (NAUTILUS_FILE (l->data))) {
275
static NautilusFile *
276
get_original_file (NautilusPropertiesWindow *window)
278
g_return_val_if_fail (!is_multi_file_window (window), NULL);
280
if (window->details->original_files == NULL) {
284
return NAUTILUS_FILE (window->details->original_files->data);
287
static NautilusFile *
288
get_target_file_for_original_file (NautilusFile *file)
290
NautilusFile *target_file;
292
char *uri_to_display;
293
NautilusDesktopLink *link;
296
if (NAUTILUS_IS_DESKTOP_ICON_FILE (file)) {
297
link = nautilus_desktop_icon_file_get_link (NAUTILUS_DESKTOP_ICON_FILE (file));
300
/* map to linked URI for these types of links */
301
location = nautilus_desktop_link_get_activation_location (link);
303
target_file = nautilus_file_get (location);
304
g_object_unref (location);
307
g_object_unref (link);
310
uri_to_display = nautilus_file_get_activation_uri (file);
311
if (uri_to_display != NULL) {
312
target_file = nautilus_file_get_by_uri (uri_to_display);
313
g_free (uri_to_display);
317
if (target_file != NULL) {
321
/* Ref passed-in file here since we've decided to use it. */
322
nautilus_file_ref (file);
326
static NautilusFile *
327
get_target_file (NautilusPropertiesWindow *window)
329
return NAUTILUS_FILE (window->details->target_files->data);
333
add_prompt (GtkWidget *vbox, const char *prompt_text, gboolean pack_at_start)
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);
342
gtk_box_pack_start (GTK_BOX (vbox), prompt, FALSE, FALSE, 0);
344
gtk_box_pack_end (GTK_BOX (vbox), prompt, FALSE, FALSE, 0);
349
add_prompt_and_separator (GtkWidget *vbox, const char *prompt_text)
351
GtkWidget *separator_line;
353
add_prompt (vbox, prompt_text, FALSE);
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);
361
get_image_for_properties_window (NautilusPropertiesWindow *window,
363
GdkPixbuf **icon_pixbuf)
365
NautilusIconInfo *icon, *new_icon;
370
icon_scale = gtk_widget_get_scale_factor (GTK_WIDGET (window->details->notebook));
372
for (l = window->details->original_files; l != NULL; l = l->next) {
375
file = NAUTILUS_FILE (l->data);
378
icon = nautilus_file_get_icon (file, NAUTILUS_ICON_SIZE_STANDARD, icon_scale,
379
NAUTILUS_FILE_ICON_FLAGS_USE_THUMBNAILS |
380
NAUTILUS_FILE_ICON_FLAGS_IGNORE_VISITING);
382
new_icon = nautilus_file_get_icon (file, NAUTILUS_ICON_SIZE_STANDARD, icon_scale,
383
NAUTILUS_FILE_ICON_FLAGS_USE_THUMBNAILS |
384
NAUTILUS_FILE_ICON_FLAGS_IGNORE_VISITING);
385
if (!new_icon || new_icon != icon) {
386
g_object_unref (icon);
387
g_object_unref (new_icon);
391
g_object_unref (new_icon);
396
icon = nautilus_icon_info_lookup_from_name ("text-x-generic",
397
NAUTILUS_ICON_SIZE_STANDARD,
401
if (icon_name != NULL) {
402
*icon_name = g_strdup (nautilus_icon_info_get_used_name (icon));
405
if (icon_pixbuf != NULL) {
406
*icon_pixbuf = nautilus_icon_info_get_pixbuf_at_size (icon, NAUTILUS_ICON_SIZE_STANDARD);
409
g_object_unref (icon);
414
update_properties_window_icon (NautilusPropertiesWindow *window)
417
cairo_surface_t *surface;
420
get_image_for_properties_window (window, &name, &pixbuf);
423
gtk_window_set_icon_name (GTK_WINDOW (window), name);
425
gtk_window_set_icon (GTK_WINDOW (window), pixbuf);
428
surface = gdk_cairo_surface_create_from_pixbuf (pixbuf, gtk_widget_get_scale_factor (GTK_WIDGET (window)),
429
gtk_widget_get_window (GTK_WIDGET (window)));
430
gtk_image_set_from_surface (GTK_IMAGE (window->details->icon_image), surface);
433
g_object_unref (pixbuf);
434
cairo_surface_destroy (surface);
437
/* utility to test if a uri refers to a local image */
439
uri_is_local_image (const char *uri)
444
image_path = g_filename_from_uri (uri, NULL, NULL);
445
if (image_path == NULL) {
449
pixbuf = gdk_pixbuf_new_from_file (image_path, NULL);
452
if (pixbuf == NULL) {
455
g_object_unref (pixbuf);
461
reset_icon (NautilusPropertiesWindow *properties_window)
465
for (l = properties_window->details->original_files; l != NULL; l = l->next) {
468
file = NAUTILUS_FILE (l->data);
470
nautilus_file_set_metadata (file,
471
NAUTILUS_METADATA_KEY_ICON_SCALE,
473
nautilus_file_set_metadata (file,
474
NAUTILUS_METADATA_KEY_CUSTOM_ICON,
481
nautilus_properties_window_drag_data_received (GtkWidget *widget, GdkDragContext *context,
483
GtkSelectionData *selection_data,
484
guint info, guint time)
487
gboolean exactly_one;
491
image = GTK_IMAGE (widget);
492
window = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (image)));
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');
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."),
504
if (uri_is_local_image (uris[0])) {
505
set_icon (uris[0], NAUTILUS_PROPERTIES_WINDOW (window));
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."),
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."),
529
create_image_widget (NautilusPropertiesWindow *window,
530
gboolean is_customizable)
535
image = gtk_image_new ();
536
window->details->icon_image = image;
538
update_properties_window_icon (window);
539
gtk_widget_show (image);
542
if (is_customizable) {
543
button = gtk_button_new ();
544
gtk_container_add (GTK_CONTAINER (button), image);
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);
552
g_signal_connect (image, "drag-data-received",
553
G_CALLBACK (nautilus_properties_window_drag_data_received), NULL);
554
g_signal_connect (button, "clicked",
555
G_CALLBACK (select_image_button_callback), window);
558
window->details->icon_button = button;
560
return button != NULL ? button : image;
564
set_name_field (NautilusPropertiesWindow *window,
565
const gchar *original_name,
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)
577
use_label = is_multi_file_window (window) || !nautilus_file_can_rename (get_original_file (window));
578
new_widget = !window->details->name_field || (use_label ? NAUTILUS_IS_ENTRY (window->details->name_field) : GTK_IS_LABEL (window->details->name_field));
581
if (window->details->name_field) {
582
gtk_widget_destroy (window->details->name_field);
586
window->details->name_field = GTK_WIDGET
587
(attach_ellipsizing_value_label (window->details->basic_grid,
588
GTK_WIDGET (window->details->name_label),
591
window->details->name_field = nautilus_entry_new ();
592
gtk_entry_set_text (GTK_ENTRY (window->details->name_field), name);
593
gtk_widget_show (window->details->name_field);
595
gtk_grid_attach_next_to (window->details->basic_grid, window->details->name_field,
596
GTK_WIDGET (window->details->name_label),
597
GTK_POS_RIGHT, 1, 1);
598
gtk_label_set_mnemonic_widget (GTK_LABEL (window->details->name_label), window->details->name_field);
600
g_signal_connect_object (window->details->name_field, "focus-out-event",
601
G_CALLBACK (name_field_focus_out), window, 0);
602
g_signal_connect_object (window->details->name_field, "activate",
603
G_CALLBACK (name_field_activate), window, 0);
606
gtk_widget_show (window->details->name_field);
608
/* Only replace text if the file's name has changed. */
609
else if (original_name == NULL || strcmp (original_name, name) != 0) {
612
gtk_label_set_text (GTK_LABEL (window->details->name_field), name);
614
/* Only reset the text if it's different from what is
615
* currently showing. This causes minimal ripples (e.g.
618
gchar *displayed_name = gtk_editable_get_chars (GTK_EDITABLE (window->details->name_field), 0, -1);
619
if (strcmp (displayed_name, name) != 0) {
620
gtk_entry_set_text (GTK_ENTRY (window->details->name_field), name);
622
g_free (displayed_name);
628
update_name_field (NautilusPropertiesWindow *window)
632
gtk_label_set_text_with_mnemonic (window->details->name_label,
633
ngettext ("_Name:", "_Names:",
634
get_not_gone_original_file_count (window)));
636
if (is_multi_file_window (window)) {
637
/* Multifile property dialog, show all names */
643
str = g_string_new ("");
647
for (l = window->details->target_files; l != NULL; l = l->next) {
648
file = NAUTILUS_FILE (l->data);
650
if (!nautilus_file_is_gone (file)) {
652
g_string_append (str, ", ");
656
name = nautilus_file_get_display_name (file);
657
g_string_append (str, name);
661
set_name_field (window, NULL, str->str);
662
g_string_free (str, TRUE);
664
const char *original_name = NULL;
667
file = get_original_file (window);
669
if (file == NULL || nautilus_file_is_gone (file)) {
670
current_name = g_strdup ("");
672
current_name = nautilus_file_get_display_name (file);
675
/* If the file name has changed since the original name was stored,
676
* update the text in the text field, possibly (deliberately) clobbering
677
* an edit in progress. If the name hasn't changed (but some other
678
* aspect of the file might have), then don't clobber changes.
680
if (window->details->name_field) {
681
original_name = (const char *) g_object_get_data (G_OBJECT (window->details->name_field), "original_name");
684
set_name_field (window, original_name, current_name);
686
if (original_name == NULL ||
687
g_strcmp0 (original_name, current_name) != 0) {
688
g_object_set_data_full (G_OBJECT (window->details->name_field),
693
g_free (current_name);
699
name_field_restore_original_name (NautilusEntry *name_field)
701
const char *original_name;
702
char *displayed_name;
704
original_name = (const char *) g_object_get_data (G_OBJECT (name_field),
707
if (!original_name) {
711
displayed_name = gtk_editable_get_chars (GTK_EDITABLE (name_field), 0, -1);
713
if (strcmp (original_name, displayed_name) != 0) {
714
gtk_entry_set_text (GTK_ENTRY (name_field), original_name);
716
nautilus_entry_select_all (name_field);
718
g_free (displayed_name);
722
rename_callback (NautilusFile *file, GFile *res_loc, GError *error, gpointer callback_data)
724
NautilusPropertiesWindow *window;
726
window = NAUTILUS_PROPERTIES_WINDOW (callback_data);
728
/* Complain to user if rename failed. */
730
nautilus_report_error_renaming_file (file,
731
window->details->pending_name,
733
GTK_WINDOW (window));
734
if (window->details->name_field != NULL) {
735
name_field_restore_original_name (NAUTILUS_ENTRY (window->details->name_field));
739
g_object_unref (window);
743
set_pending_name (NautilusPropertiesWindow *window, const char *name)
745
g_free (window->details->pending_name);
746
window->details->pending_name = g_strdup (name);
750
name_field_done_editing (NautilusEntry *name_field, NautilusPropertiesWindow *window)
754
const char *original_name;
756
g_return_if_fail (NAUTILUS_IS_ENTRY (name_field));
758
/* Don't apply if the dialog has more than one file */
759
if (is_multi_file_window (window)) {
763
file = get_original_file (window);
765
/* This gets called when the window is closed, which might be
766
* caused by the file having been deleted.
768
if (file == NULL || nautilus_file_is_gone (file)) {
772
new_name = gtk_editable_get_chars (GTK_EDITABLE (name_field), 0, -1);
774
/* Special case: silently revert text if new text is empty. */
775
if (strlen (new_name) == 0) {
776
name_field_restore_original_name (NAUTILUS_ENTRY (name_field));
778
original_name = (const char *) g_object_get_data (G_OBJECT (window->details->name_field),
780
/* Don't rename if not changed since we read the display name.
781
This is needed so that we don't save the display name to the
782
file when nothing is changed */
783
if (strcmp (new_name, original_name) != 0) {
784
set_pending_name (window, new_name);
785
g_object_ref (window);
786
nautilus_file_rename (file, new_name,
787
rename_callback, window);
795
name_field_focus_out (NautilusEntry *name_field,
796
GdkEventFocus *event,
797
gpointer callback_data)
799
g_assert (NAUTILUS_IS_PROPERTIES_WINDOW (callback_data));
801
if (gtk_widget_get_sensitive (GTK_WIDGET (name_field))) {
802
name_field_done_editing (name_field, NAUTILUS_PROPERTIES_WINDOW (callback_data));
809
name_field_activate (NautilusEntry *name_field, gpointer callback_data)
811
g_assert (NAUTILUS_IS_ENTRY (name_field));
812
g_assert (NAUTILUS_IS_PROPERTIES_WINDOW (callback_data));
814
/* Accept changes. */
815
name_field_done_editing (name_field, NAUTILUS_PROPERTIES_WINDOW (callback_data));
817
nautilus_entry_select_all_at_idle (name_field);
821
update_properties_window_title (NautilusPropertiesWindow *window)
826
g_return_if_fail (GTK_IS_WINDOW (window));
828
title = g_strdup_printf (_("Properties"));
830
if (!is_multi_file_window (window)) {
831
file = get_original_file (window);
835
name = nautilus_file_get_display_name (file);
836
title = g_strdup_printf (_("%s Properties"), name);
841
gtk_window_set_title (GTK_WINDOW (window), title);
847
clear_extension_pages (NautilusPropertiesWindow *window)
853
num_pages = gtk_notebook_get_n_pages
854
(GTK_NOTEBOOK (window->details->notebook));
856
for (i = 0; i < num_pages; i++) {
857
page = gtk_notebook_get_nth_page
858
(GTK_NOTEBOOK (window->details->notebook), i);
860
if (g_object_get_data (G_OBJECT (page), "is-extension-page")) {
861
gtk_notebook_remove_page
862
(GTK_NOTEBOOK (window->details->notebook), i);
870
refresh_extension_pages (NautilusPropertiesWindow *window)
872
clear_extension_pages (window);
873
append_extension_pages (window);
877
remove_from_dialog (NautilusPropertiesWindow *window,
881
GList *original_link;
883
NautilusFile *original_file;
884
NautilusFile *target_file;
886
index = g_list_index (window->details->target_files, file);
888
index = g_list_index (window->details->original_files, file);
889
g_return_if_fail (index != -1);
892
original_link = g_list_nth (window->details->original_files, index);
893
target_link = g_list_nth (window->details->target_files, index);
895
g_return_if_fail (original_link && target_link);
897
original_file = NAUTILUS_FILE (original_link->data);
898
target_file = NAUTILUS_FILE (target_link->data);
900
window->details->original_files = g_list_remove_link (window->details->original_files, original_link);
901
g_list_free (original_link);
903
window->details->target_files = g_list_remove_link (window->details->target_files, target_link);
904
g_list_free (target_link);
906
g_hash_table_remove (window->details->initial_permissions, target_file);
908
g_signal_handlers_disconnect_by_func (original_file,
909
G_CALLBACK (file_changed_callback),
911
g_signal_handlers_disconnect_by_func (target_file,
912
G_CALLBACK (file_changed_callback),
915
nautilus_file_monitor_remove (original_file, &window->details->original_files);
916
nautilus_file_monitor_remove (target_file, &window->details->target_files);
918
nautilus_file_unref (original_file);
919
nautilus_file_unref (target_file);
924
mime_list_equal (GList *a, GList *b)
927
if (strcmp (a->data, b->data)) {
938
get_mime_list (NautilusPropertiesWindow *window)
944
for (l = window->details->target_files; l != NULL; l = l->next) {
945
ret = g_list_append (ret, nautilus_file_get_mime_type (NAUTILUS_FILE (l->data)));
947
ret = g_list_reverse (ret);
952
start_spinner_callback (NautilusPropertiesWindow *window)
954
gtk_widget_show (window->details->directory_contents_spinner);
955
gtk_spinner_start (GTK_SPINNER (window->details->directory_contents_spinner));
956
window->details->deep_count_spinner_timeout_id = 0;
962
schedule_start_spinner (NautilusPropertiesWindow *window)
964
if (window->details->deep_count_spinner_timeout_id == 0) {
965
window->details->deep_count_spinner_timeout_id
966
= g_timeout_add_seconds (1,
967
(GSourceFunc)start_spinner_callback,
973
stop_spinner (NautilusPropertiesWindow *window)
975
gtk_spinner_stop (GTK_SPINNER (window->details->directory_contents_spinner));
976
gtk_widget_hide (window->details->directory_contents_spinner);
977
if (window->details->deep_count_spinner_timeout_id > 0) {
978
g_source_remove (window->details->deep_count_spinner_timeout_id);
979
window->details->deep_count_spinner_timeout_id = 0;
984
stop_deep_count_for_file (NautilusPropertiesWindow *window,
987
if (g_list_find (window->details->deep_count_files, file)) {
988
g_signal_handlers_disconnect_by_func (file,
989
G_CALLBACK (schedule_directory_contents_update),
991
nautilus_file_unref (file);
992
window->details->deep_count_files = g_list_remove (window->details->deep_count_files, file);
997
start_deep_count_for_file (NautilusPropertiesWindow *window,
1000
if (!g_list_find (window->details->deep_count_files, file)) {
1001
nautilus_file_ref (file);
1002
window->details->deep_count_files = g_list_prepend (window->details->deep_count_files, file);
1004
nautilus_file_recompute_deep_counts (file);
1005
if (!window->details->deep_count_finished) {
1006
g_signal_connect_object (file,
1007
"updated-deep-count-in-progress",
1008
G_CALLBACK (schedule_directory_contents_update),
1009
window, G_CONNECT_SWAPPED);
1010
schedule_start_spinner (window);
1016
properties_window_update (NautilusPropertiesWindow *window,
1022
NautilusFile *changed_file;
1023
gboolean dirty_original = FALSE;
1024
gboolean dirty_target = FALSE;
1026
if (files == NULL) {
1027
dirty_original = TRUE;
1028
dirty_target = TRUE;
1031
for (tmp = files; tmp != NULL; tmp = tmp->next) {
1032
changed_file = NAUTILUS_FILE (tmp->data);
1034
if (changed_file && nautilus_file_is_gone (changed_file)) {
1035
/* Remove the file from the property dialog */
1036
remove_from_dialog (window, changed_file);
1037
changed_file = NULL;
1039
if (window->details->original_files == NULL) {
1043
if (changed_file == NULL ||
1044
g_list_find (window->details->original_files, changed_file)) {
1045
dirty_original = TRUE;
1047
if (changed_file == NULL ||
1048
g_list_find (window->details->target_files, changed_file)) {
1049
dirty_target = TRUE;
1051
if (changed_file != NULL) {
1052
start_deep_count_for_file (window, changed_file);
1056
if (dirty_original) {
1057
update_properties_window_title (window);
1058
update_properties_window_icon (window);
1059
update_name_field (window);
1061
/* If any of the value fields start to depend on the original
1062
* value, value_field_updates should be added here */
1066
for (l = window->details->permission_buttons; l != NULL; l = l->next) {
1067
permission_button_update (window, GTK_TOGGLE_BUTTON (l->data));
1070
for (l = window->details->permission_combos; l != NULL; l = l->next) {
1071
permission_combo_update (window, GTK_COMBO_BOX (l->data));
1074
for (l = window->details->value_fields; l != NULL; l = l->next) {
1075
value_field_update (window, GTK_LABEL (l->data));
1079
mime_list = get_mime_list (window);
1081
if (!window->details->mime_list) {
1082
window->details->mime_list = mime_list;
1084
if (!mime_list_equal (window->details->mime_list, mime_list)) {
1085
refresh_extension_pages (window);
1088
g_list_free_full (window->details->mime_list, g_free);
1089
window->details->mime_list = mime_list;
1094
update_files_callback (gpointer data)
1096
NautilusPropertiesWindow *window;
1098
window = NAUTILUS_PROPERTIES_WINDOW (data);
1100
window->details->update_files_timeout_id = 0;
1102
properties_window_update (window, window->details->changed_files);
1104
if (window->details->original_files == NULL) {
1105
/* Close the window if no files are left */
1106
gtk_widget_destroy (GTK_WIDGET (window));
1108
nautilus_file_list_free (window->details->changed_files);
1109
window->details->changed_files = NULL;
1116
schedule_files_update (NautilusPropertiesWindow *window)
1118
g_assert (NAUTILUS_IS_PROPERTIES_WINDOW (window));
1120
if (window->details->update_files_timeout_id == 0) {
1121
window->details->update_files_timeout_id
1122
= g_timeout_add (FILES_UPDATE_INTERVAL,
1123
update_files_callback,
1129
file_list_attributes_identical (GList *file_list, const char *attribute_name)
1138
for (l = file_list; l != NULL; l = l->next) {
1141
file = NAUTILUS_FILE (l->data);
1143
if (nautilus_file_is_gone (file)) {
1147
if (first_attr == NULL) {
1148
first_attr = nautilus_file_get_string_attribute_with_default (file, attribute_name);
1151
attr = nautilus_file_get_string_attribute_with_default (file, attribute_name);
1152
if (strcmp (attr, first_attr)) {
1161
g_free (first_attr);
1166
file_list_get_string_attribute (GList *file_list,
1167
const char *attribute_name,
1168
const char *inconsistent_value)
1170
if (file_list_attributes_identical (file_list, attribute_name)) {
1173
for (l = file_list; l != NULL; l = l->next) {
1176
file = NAUTILUS_FILE (l->data);
1177
if (!nautilus_file_is_gone (file)) {
1178
return nautilus_file_get_string_attribute_with_default
1183
return g_strdup (_("unknown"));
1185
return g_strdup (inconsistent_value);
1191
file_list_all_directories (GList *file_list)
1194
for (l = file_list; l != NULL; l = l->next) {
1195
if (!nautilus_file_is_directory (NAUTILUS_FILE (l->data))) {
1203
value_field_update_internal (GtkLabel *label,
1206
const char *attribute_name;
1207
char *attribute_value;
1208
char *inconsistent_string;
1209
char *mime_type, *tmp;
1211
g_assert (GTK_IS_LABEL (label));
1213
attribute_name = g_object_get_data (G_OBJECT (label), "file_attribute");
1214
inconsistent_string = g_object_get_data (G_OBJECT (label), "inconsistent_string");
1215
attribute_value = file_list_get_string_attribute (file_list,
1217
inconsistent_string);
1218
if (!strcmp (attribute_name, "detailed_type") && strcmp (attribute_value, inconsistent_string)) {
1219
mime_type = file_list_get_string_attribute (file_list,
1221
inconsistent_string);
1222
if (strcmp (mime_type, inconsistent_string)) {
1223
tmp = attribute_value;
1224
attribute_value = g_strdup_printf (C_("MIME type description (MIME type)", "%s (%s)"), attribute_value, mime_type);
1230
gtk_label_set_text (label, attribute_value);
1231
g_free (attribute_value);
1235
value_field_update (NautilusPropertiesWindow *window, GtkLabel *label)
1237
gboolean use_original;
1239
use_original = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (label), "show_original"));
1241
value_field_update_internal (label,
1243
window->details->original_files :
1244
window->details->target_files));
1248
attach_label (GtkGrid *grid,
1250
const char *initial_text,
1251
gboolean ellipsize_text,
1252
gboolean selectable,
1255
GtkWidget *label_field;
1257
if (ellipsize_text) {
1258
label_field = gtk_label_new (initial_text);
1259
gtk_label_set_ellipsize (GTK_LABEL (label_field),
1260
PANGO_ELLIPSIZE_END);
1261
} else if (mnemonic) {
1262
label_field = gtk_label_new_with_mnemonic (initial_text);
1264
label_field = gtk_label_new (initial_text);
1268
gtk_label_set_selectable (GTK_LABEL (label_field), TRUE);
1271
gtk_misc_set_alignment (GTK_MISC (label_field), 0, 0.5);
1272
gtk_widget_show (label_field);
1274
if (ellipsize_text) {
1275
gtk_widget_set_hexpand (label_field, TRUE);
1278
if (sibling != NULL) {
1279
gtk_grid_attach_next_to (grid, label_field, sibling,
1280
GTK_POS_RIGHT, 1, 1);
1282
gtk_container_add (GTK_CONTAINER (grid), label_field);
1285
return GTK_LABEL (label_field);
1289
attach_value_label (GtkGrid *grid,
1291
const char *initial_text)
1293
return attach_label (grid, sibling, initial_text, FALSE, TRUE, FALSE);
1297
attach_ellipsizing_value_label (GtkGrid *grid,
1299
const char *initial_text)
1301
return attach_label (grid, sibling, initial_text, TRUE, TRUE, FALSE);
1305
attach_value_field_internal (NautilusPropertiesWindow *window,
1308
const char *file_attribute_name,
1309
const char *inconsistent_string,
1310
gboolean show_original,
1311
gboolean ellipsize_text)
1313
GtkLabel *value_field;
1315
if (ellipsize_text) {
1316
value_field = attach_ellipsizing_value_label (grid, sibling, "");
1318
value_field = attach_value_label (grid, sibling, "");
1321
/* Stash a copy of the file attribute name in this field for the callback's sake. */
1322
g_object_set_data_full (G_OBJECT (value_field), "file_attribute",
1323
g_strdup (file_attribute_name), g_free);
1325
g_object_set_data_full (G_OBJECT (value_field), "inconsistent_string",
1326
g_strdup (inconsistent_string), g_free);
1328
g_object_set_data (G_OBJECT (value_field), "show_original", GINT_TO_POINTER (show_original));
1330
window->details->value_fields = g_list_prepend (window->details->value_fields,
1332
return GTK_WIDGET(value_field);
1336
attach_value_field (NautilusPropertiesWindow *window,
1339
const char *file_attribute_name,
1340
const char *inconsistent_string,
1341
gboolean show_original)
1343
return attach_value_field_internal (window,
1345
file_attribute_name,
1346
inconsistent_string,
1352
attach_ellipsizing_value_field (NautilusPropertiesWindow *window,
1355
const char *file_attribute_name,
1356
const char *inconsistent_string,
1357
gboolean show_original)
1359
return attach_value_field_internal (window,
1361
file_attribute_name,
1362
inconsistent_string,
1368
group_change_callback (NautilusFile *file,
1371
NautilusPropertiesWindow *window)
1375
g_assert (NAUTILUS_IS_PROPERTIES_WINDOW (window));
1376
g_assert (window->details->group_change_file == file);
1378
group = window->details->group_change_group;
1379
g_assert (group != NULL);
1381
/* Report the error if it's an error. */
1382
eel_timed_wait_stop ((EelCancelCallback) cancel_group_change_callback, window);
1383
nautilus_report_error_setting_group (file, error, GTK_WINDOW (window));
1385
nautilus_file_unref (file);
1388
window->details->group_change_file = NULL;
1389
window->details->group_change_group = NULL;
1390
g_object_unref (G_OBJECT (window));
1394
cancel_group_change_callback (NautilusPropertiesWindow *window)
1399
file = window->details->group_change_file;
1400
g_assert (NAUTILUS_IS_FILE (file));
1402
group = window->details->group_change_group;
1403
g_assert (group != NULL);
1405
nautilus_file_cancel (file, (NautilusFileOperationCallback) group_change_callback, window);
1408
nautilus_file_unref (file);
1410
window->details->group_change_file = NULL;
1411
window->details->group_change_group = NULL;
1412
g_object_unref (window);
1416
schedule_group_change_timeout (NautilusPropertiesWindow *window)
1421
g_assert (NAUTILUS_IS_PROPERTIES_WINDOW (window));
1423
file = window->details->group_change_file;
1424
g_assert (NAUTILUS_IS_FILE (file));
1426
group = window->details->group_change_group;
1427
g_assert (group != NULL);
1429
eel_timed_wait_start
1430
((EelCancelCallback) cancel_group_change_callback,
1432
_("Cancel Group Change?"),
1433
GTK_WINDOW (window));
1435
nautilus_file_set_group
1437
(NautilusFileOperationCallback) group_change_callback, window);
1439
window->details->group_change_timeout = 0;
1444
schedule_group_change (NautilusPropertiesWindow *window,
1448
g_assert (NAUTILUS_IS_PROPERTIES_WINDOW (window));
1449
g_assert (window->details->group_change_group == NULL);
1450
g_assert (window->details->group_change_file == NULL);
1451
g_assert (NAUTILUS_IS_FILE (file));
1453
window->details->group_change_file = nautilus_file_ref (file);
1454
window->details->group_change_group = g_strdup (group);
1455
g_object_ref (G_OBJECT (window));
1456
window->details->group_change_timeout =
1457
g_timeout_add (CHOWN_CHGRP_TIMEOUT,
1458
(GSourceFunc) schedule_group_change_timeout,
1463
unschedule_or_cancel_group_change (NautilusPropertiesWindow *window)
1468
g_assert (NAUTILUS_IS_PROPERTIES_WINDOW (window));
1470
file = window->details->group_change_file;
1471
group = window->details->group_change_group;
1473
g_assert ((file == NULL && group == NULL) ||
1474
(file != NULL && group != NULL));
1477
g_assert (NAUTILUS_IS_FILE (file));
1479
if (window->details->group_change_timeout == 0) {
1480
nautilus_file_cancel (file,
1481
(NautilusFileOperationCallback) group_change_callback, window);
1482
eel_timed_wait_stop ((EelCancelCallback) cancel_group_change_callback, window);
1485
nautilus_file_unref (file);
1488
window->details->group_change_file = NULL;
1489
window->details->group_change_group = NULL;
1490
g_object_unref (G_OBJECT (window));
1493
if (window->details->group_change_timeout > 0) {
1494
g_assert (file != NULL);
1495
g_source_remove (window->details->group_change_timeout);
1496
window->details->group_change_timeout = 0;
1501
changed_group_callback (GtkComboBox *combo_box, NautilusFile *file)
1503
NautilusPropertiesWindow *window;
1507
g_assert (GTK_IS_COMBO_BOX (combo_box));
1508
g_assert (NAUTILUS_IS_FILE (file));
1510
group = gtk_combo_box_text_get_active_text (GTK_COMBO_BOX_TEXT (combo_box));
1511
cur_group = nautilus_file_get_group_name (file);
1513
if (group != NULL && strcmp (group, cur_group) != 0) {
1514
/* Try to change file group. If this fails, complain to user. */
1515
window = NAUTILUS_PROPERTIES_WINDOW (gtk_widget_get_ancestor (GTK_WIDGET (combo_box), GTK_TYPE_WINDOW));
1517
unschedule_or_cancel_group_change (window);
1518
schedule_group_change (window, file, group);
1524
/* checks whether the given column at the first level
1525
* of model has the specified entries in the given order. */
1527
tree_model_entries_equal (GtkTreeModel *model,
1528
unsigned int column,
1532
gboolean empty_model;
1534
g_assert (GTK_IS_TREE_MODEL (model));
1535
g_assert (gtk_tree_model_get_column_type (model, column) == G_TYPE_STRING);
1537
empty_model = !gtk_tree_model_get_iter_first (model, &iter);
1539
if (!empty_model && entries != NULL) {
1547
gtk_tree_model_get (model, &iter,
1550
if ((val == NULL && l->data != NULL) ||
1551
(val != NULL && l->data == NULL) ||
1552
(val != NULL && strcmp (val, l->data))) {
1559
} while (gtk_tree_model_iter_next (model, &iter));
1563
return (empty_model && entries == NULL) ||
1564
(!empty_model && entries != NULL);
1569
combo_box_get_active_entry (GtkComboBox *combo_box,
1570
unsigned int column)
1572
GtkTreeModel *model;
1576
g_assert (GTK_IS_COMBO_BOX (combo_box));
1578
if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (combo_box), &iter)) {
1579
model = gtk_combo_box_get_model (combo_box);
1580
g_assert (GTK_IS_TREE_MODEL (model));
1582
gtk_tree_model_get (model, &iter,
1591
/* returns the index of the given entry in the the given column
1592
* at the first level of model. Returns -1 if entry can't be found
1596
tree_model_get_entry_index (GtkTreeModel *model,
1597
unsigned int column,
1602
gboolean empty_model;
1604
g_assert (GTK_IS_TREE_MODEL (model));
1605
g_assert (gtk_tree_model_get_column_type (model, column) == G_TYPE_STRING);
1607
empty_model = !gtk_tree_model_get_iter_first (model, &iter);
1608
if (!empty_model && entry != NULL) {
1614
gtk_tree_model_get (model, &iter,
1617
if (val != NULL && !strcmp (val, entry)) {
1624
} while (gtk_tree_model_iter_next (model, &iter));
1632
synch_groups_combo_box (GtkComboBox *combo_box, NautilusFile *file)
1636
GtkTreeModel *model;
1637
GtkListStore *store;
1638
const char *group_name;
1639
char *current_group_name;
1641
int current_group_index;
1643
g_assert (GTK_IS_COMBO_BOX (combo_box));
1644
g_assert (NAUTILUS_IS_FILE (file));
1646
if (nautilus_file_is_gone (file)) {
1650
groups = nautilus_file_get_settable_group_names (file);
1652
model = gtk_combo_box_get_model (combo_box);
1653
store = GTK_LIST_STORE (model);
1654
g_assert (GTK_IS_LIST_STORE (model));
1656
if (!tree_model_entries_equal (model, 0, groups)) {
1657
/* Clear the contents of ComboBox in a wacky way because there
1658
* is no function to clear all items and also no function to obtain
1659
* the number of items in a combobox.
1661
gtk_list_store_clear (store);
1663
for (node = groups, group_index = 0; node != NULL; node = node->next, ++group_index) {
1664
group_name = (const char *)node->data;
1665
gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_box), group_name);
1669
current_group_name = nautilus_file_get_group_name (file);
1670
current_group_index = tree_model_get_entry_index (model, 0, current_group_name);
1672
/* If current group wasn't in list, we prepend it (with a separator).
1673
* This can happen if the current group is an id with no matching
1674
* group in the groups file.
1676
if (current_group_index < 0 && current_group_name != NULL) {
1677
if (groups != NULL) {
1679
gtk_combo_box_text_prepend_text (GTK_COMBO_BOX_TEXT (combo_box), "-");
1682
gtk_combo_box_text_prepend_text (GTK_COMBO_BOX_TEXT (combo_box), current_group_name);
1683
current_group_index = 0;
1685
gtk_combo_box_set_active (combo_box, current_group_index);
1687
g_free (current_group_name);
1688
g_list_free_full (groups, g_free);
1692
combo_box_row_separator_func (GtkTreeModel *model,
1699
gtk_tree_model_get (model, iter, 0, &text, -1);
1705
if (strcmp (text, "-") == 0) {
1715
static GtkComboBox *
1716
attach_combo_box (GtkGrid *grid,
1718
gboolean two_columns)
1720
GtkWidget *combo_box;
1724
combo_box = gtk_combo_box_text_new ();
1726
GtkTreeModel *model;
1727
GtkCellRenderer *renderer;
1729
model = GTK_TREE_MODEL (gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_STRING));
1730
combo_box = gtk_combo_box_new_with_model (model);
1731
g_object_unref (G_OBJECT (model));
1733
renderer = gtk_cell_renderer_text_new ();
1734
gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo_box), renderer, TRUE);
1735
gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (combo_box), renderer,
1739
gtk_widget_show (combo_box);
1741
gtk_combo_box_set_row_separator_func (GTK_COMBO_BOX (combo_box),
1742
combo_box_row_separator_func,
1746
/* Put combo box in alignment to make it left-justified
1747
* but minimally sized.
1749
aligner = gtk_alignment_new (0, 0.5, 0, 0);
1750
gtk_widget_show (aligner);
1752
gtk_container_add (GTK_CONTAINER (aligner), combo_box);
1753
gtk_grid_attach_next_to (grid, aligner, sibling,
1754
GTK_POS_RIGHT, 1, 1);
1756
return GTK_COMBO_BOX (combo_box);
1760
attach_group_combo_box (GtkGrid *grid,
1764
GtkComboBox *combo_box;
1766
combo_box = attach_combo_box (grid, sibling, FALSE);
1768
synch_groups_combo_box (combo_box, file);
1770
/* Connect to signal to update menu when file changes. */
1771
g_signal_connect_object (file, "changed",
1772
G_CALLBACK (synch_groups_combo_box),
1773
combo_box, G_CONNECT_SWAPPED);
1774
g_signal_connect_data (combo_box, "changed",
1775
G_CALLBACK (changed_group_callback),
1776
nautilus_file_ref (file),
1777
(GClosureNotify)nautilus_file_unref, 0);
1783
owner_change_callback (NautilusFile *file,
1784
GFile *result_location,
1786
NautilusPropertiesWindow *window)
1790
g_assert (NAUTILUS_IS_PROPERTIES_WINDOW (window));
1791
g_assert (window->details->owner_change_file == file);
1793
owner = window->details->owner_change_owner;
1794
g_assert (owner != NULL);
1796
/* Report the error if it's an error. */
1797
eel_timed_wait_stop ((EelCancelCallback) cancel_owner_change_callback, window);
1798
nautilus_report_error_setting_owner (file, error, GTK_WINDOW (window));
1800
nautilus_file_unref (file);
1803
window->details->owner_change_file = NULL;
1804
window->details->owner_change_owner = NULL;
1805
g_object_unref (G_OBJECT (window));
1809
cancel_owner_change_callback (NautilusPropertiesWindow *window)
1814
file = window->details->owner_change_file;
1815
g_assert (NAUTILUS_IS_FILE (file));
1817
owner = window->details->owner_change_owner;
1818
g_assert (owner != NULL);
1820
nautilus_file_cancel (file, (NautilusFileOperationCallback) owner_change_callback, window);
1822
nautilus_file_unref (file);
1825
window->details->owner_change_file = NULL;
1826
window->details->owner_change_owner = NULL;
1827
g_object_unref (window);
1831
schedule_owner_change_timeout (NautilusPropertiesWindow *window)
1836
g_assert (NAUTILUS_IS_PROPERTIES_WINDOW (window));
1838
file = window->details->owner_change_file;
1839
g_assert (NAUTILUS_IS_FILE (file));
1841
owner = window->details->owner_change_owner;
1842
g_assert (owner != NULL);
1844
eel_timed_wait_start
1845
((EelCancelCallback) cancel_owner_change_callback,
1847
_("Cancel Owner Change?"),
1848
GTK_WINDOW (window));
1850
nautilus_file_set_owner
1852
(NautilusFileOperationCallback) owner_change_callback, window);
1854
window->details->owner_change_timeout = 0;
1859
schedule_owner_change (NautilusPropertiesWindow *window,
1863
g_assert (NAUTILUS_IS_PROPERTIES_WINDOW (window));
1864
g_assert (window->details->owner_change_owner == NULL);
1865
g_assert (window->details->owner_change_file == NULL);
1866
g_assert (NAUTILUS_IS_FILE (file));
1868
window->details->owner_change_file = nautilus_file_ref (file);
1869
window->details->owner_change_owner = g_strdup (owner);
1870
g_object_ref (G_OBJECT (window));
1871
window->details->owner_change_timeout =
1872
g_timeout_add (CHOWN_CHGRP_TIMEOUT,
1873
(GSourceFunc) schedule_owner_change_timeout,
1878
unschedule_or_cancel_owner_change (NautilusPropertiesWindow *window)
1883
g_assert (NAUTILUS_IS_PROPERTIES_WINDOW (window));
1885
file = window->details->owner_change_file;
1886
owner = window->details->owner_change_owner;
1888
g_assert ((file == NULL && owner == NULL) ||
1889
(file != NULL && owner != NULL));
1892
g_assert (NAUTILUS_IS_FILE (file));
1894
if (window->details->owner_change_timeout == 0) {
1895
nautilus_file_cancel (file,
1896
(NautilusFileOperationCallback) owner_change_callback, window);
1897
eel_timed_wait_stop ((EelCancelCallback) cancel_owner_change_callback, window);
1900
nautilus_file_unref (file);
1903
window->details->owner_change_file = NULL;
1904
window->details->owner_change_owner = NULL;
1905
g_object_unref (G_OBJECT (window));
1908
if (window->details->owner_change_timeout > 0) {
1909
g_assert (file != NULL);
1910
g_source_remove (window->details->owner_change_timeout);
1911
window->details->owner_change_timeout = 0;
1916
changed_owner_callback (GtkComboBox *combo_box, NautilusFile* file)
1918
NautilusPropertiesWindow *window;
1924
g_assert (GTK_IS_COMBO_BOX (combo_box));
1925
g_assert (NAUTILUS_IS_FILE (file));
1927
owner_text = combo_box_get_active_entry (combo_box, 0);
1930
name_array = g_strsplit (owner_text, " - ", 2);
1931
new_owner = name_array[0];
1932
g_free (owner_text);
1933
cur_owner = nautilus_file_get_owner_name (file);
1935
if (strcmp (new_owner, cur_owner) != 0) {
1936
/* Try to change file owner. If this fails, complain to user. */
1937
window = NAUTILUS_PROPERTIES_WINDOW (gtk_widget_get_ancestor (GTK_WIDGET (combo_box), GTK_TYPE_WINDOW));
1939
unschedule_or_cancel_owner_change (window);
1940
schedule_owner_change (window, file, new_owner);
1942
g_strfreev (name_array);
1947
synch_user_menu (GtkComboBox *combo_box, NautilusFile *file)
1951
GtkTreeModel *model;
1952
GtkListStore *store;
1961
g_assert (GTK_IS_COMBO_BOX (combo_box));
1962
g_assert (NAUTILUS_IS_FILE (file));
1964
if (nautilus_file_is_gone (file)) {
1968
users = nautilus_get_user_names ();
1970
model = gtk_combo_box_get_model (combo_box);
1971
store = GTK_LIST_STORE (model);
1972
g_assert (GTK_IS_LIST_STORE (model));
1974
if (!tree_model_entries_equal (model, 1, users)) {
1975
/* Clear the contents of ComboBox in a wacky way because there
1976
* is no function to clear all items and also no function to obtain
1977
* the number of items in a combobox.
1979
gtk_list_store_clear (store);
1981
for (node = users, user_index = 0; node != NULL; node = node->next, ++user_index) {
1982
user_name = (char *)node->data;
1984
name_array = g_strsplit (user_name, "\n", 2);
1985
if (name_array[1] != NULL) {
1986
combo_text = g_strdup_printf ("%s - %s", name_array[0], name_array[1]);
1988
combo_text = g_strdup (name_array[0]);
1991
gtk_list_store_append (store, &iter);
1992
gtk_list_store_set (store, &iter,
1997
g_strfreev (name_array);
1998
g_free (combo_text);
2002
owner_name = nautilus_file_get_string_attribute (file, "owner");
2003
owner_index = tree_model_get_entry_index (model, 0, owner_name);
2005
/* If owner wasn't in list, we prepend it (with a separator).
2006
* This can happen if the owner is an id with no matching
2007
* identifier in the passwords file.
2009
if (owner_index < 0 && owner_name != NULL) {
2010
if (users != NULL) {
2012
gtk_list_store_prepend (store, &iter);
2013
gtk_list_store_set (store, &iter,
2019
name_array = g_strsplit (owner_name, " - ", 2);
2020
if (name_array[1] != NULL) {
2021
user_name = g_strdup_printf ("%s\n%s", name_array[0], name_array[1]);
2023
user_name = g_strdup (name_array[0]);
2027
gtk_list_store_prepend (store, &iter);
2028
gtk_list_store_set (store, &iter,
2034
g_strfreev (name_array);
2037
gtk_combo_box_set_active (combo_box, owner_index);
2039
g_free (owner_name);
2040
g_list_free_full (users, g_free);
2044
attach_owner_combo_box (GtkGrid *grid,
2048
GtkComboBox *combo_box;
2050
combo_box = attach_combo_box (grid, sibling, TRUE);
2052
synch_user_menu (combo_box, file);
2054
/* Connect to signal to update menu when file changes. */
2055
g_signal_connect_object (file, "changed",
2056
G_CALLBACK (synch_user_menu),
2057
combo_box, G_CONNECT_SWAPPED);
2058
g_signal_connect_data (combo_box, "changed",
2059
G_CALLBACK (changed_owner_callback),
2060
nautilus_file_ref (file),
2061
(GClosureNotify)nautilus_file_unref, 0);
2067
file_has_prefix (NautilusFile *file,
2068
GList *prefix_candidates)
2071
GFile *location, *candidate_location;
2073
location = nautilus_file_get_location (file);
2075
for (p = prefix_candidates; p != NULL; p = p->next) {
2076
if (file == p->data) {
2080
candidate_location = nautilus_file_get_location (NAUTILUS_FILE (p->data));
2081
if (g_file_has_prefix (location, candidate_location)) {
2082
g_object_unref (location);
2083
g_object_unref (candidate_location);
2086
g_object_unref (candidate_location);
2089
g_object_unref (location);
2095
directory_contents_value_field_update (NautilusPropertiesWindow *window)
2097
NautilusRequestStatus file_status;
2099
guint directory_count;
2102
guint unreadable_directory_count;
2104
gboolean used_two_lines;
2107
guint file_unreadable;
2109
gboolean deep_count_active;
2111
g_assert (NAUTILUS_IS_PROPERTIES_WINDOW (window));
2113
total_count = window->details->total_count;
2114
total_size = window->details->total_size;
2115
unreadable_directory_count = FALSE;
2117
for (l = window->details->target_files; l; l = l->next) {
2118
file = NAUTILUS_FILE (l->data);
2120
if (file_has_prefix (file, window->details->target_files)) {
2121
/* don't count nested files twice */
2125
if (nautilus_file_is_directory (file)) {
2126
file_status = nautilus_file_get_deep_counts (file,
2132
total_count += (file_count + directory_count);
2133
total_size += file_size;
2135
if (file_unreadable) {
2136
unreadable_directory_count = TRUE;
2139
if (file_status == NAUTILUS_REQUEST_DONE) {
2140
stop_deep_count_for_file (window, file);
2144
total_size += nautilus_file_get_size (file);
2148
deep_count_active = (g_list_length (window->details->deep_count_files) > 0);
2149
/* If we've already displayed the total once, don't do another visible
2150
* count-up if the deep_count happens to get invalidated.
2151
* But still display the new total, since it might have changed.
2153
if (window->details->deep_count_finished && deep_count_active) {
2158
used_two_lines = FALSE;
2160
if (total_count == 0) {
2161
if (!deep_count_active) {
2162
if (unreadable_directory_count == 0) {
2163
text = g_strdup (_("nothing"));
2165
text = g_strdup (_("unreadable"));
2168
text = g_strdup ("…");
2172
size_str = g_format_size (total_size);
2173
text = g_strdup_printf (ngettext("%'d item, with size %s",
2174
"%'d items, totalling %s",
2176
total_count, size_str);
2179
if (unreadable_directory_count != 0) {
2181
text = g_strconcat (temp, "\n",
2182
_("(some contents unreadable)"),
2185
used_two_lines = TRUE;
2189
gtk_label_set_text (window->details->directory_contents_value_field,
2193
/* Also set the title field here, with a trailing carriage return &
2194
* space if the value field has two lines. This is a hack to get the
2195
* "Contents:" title to line up with the first line of the
2196
* 2-line value. Maybe there's a better way to do this, but I
2197
* couldn't think of one.
2199
text = g_strdup (_("Contents:"));
2200
if (used_two_lines) {
2202
text = g_strconcat (temp, "\n ", NULL);
2205
gtk_label_set_text (window->details->directory_contents_title_field,
2209
if (!deep_count_active) {
2210
window->details->deep_count_finished = TRUE;
2211
stop_spinner (window);
2216
update_directory_contents_callback (gpointer data)
2218
NautilusPropertiesWindow *window;
2220
window = NAUTILUS_PROPERTIES_WINDOW (data);
2222
window->details->update_directory_contents_timeout_id = 0;
2223
directory_contents_value_field_update (window);
2229
schedule_directory_contents_update (NautilusPropertiesWindow *window)
2231
g_assert (NAUTILUS_IS_PROPERTIES_WINDOW (window));
2233
if (window->details->update_directory_contents_timeout_id == 0) {
2234
window->details->update_directory_contents_timeout_id
2235
= g_timeout_add (DIRECTORY_CONTENTS_UPDATE_INTERVAL,
2236
update_directory_contents_callback,
2242
attach_directory_contents_value_field (NautilusPropertiesWindow *window,
2246
GtkLabel *value_field;
2248
value_field = attach_value_label (grid, sibling, "");
2250
g_assert (window->details->directory_contents_value_field == NULL);
2251
window->details->directory_contents_value_field = value_field;
2253
gtk_label_set_line_wrap (value_field, TRUE);
2259
attach_title_field (GtkGrid *grid,
2262
return attach_label (grid, NULL, title, FALSE, FALSE, TRUE);
2265
#define INCONSISTENT_STATE_STRING \
2269
append_title_value_pair (NautilusPropertiesWindow *window,
2272
const char *file_attribute_name,
2273
const char *inconsistent_state,
2274
gboolean show_original)
2276
GtkLabel *title_label;
2279
title_label = attach_title_field (grid, title);
2280
value = attach_value_field (window, grid, GTK_WIDGET (title_label),
2281
file_attribute_name,
2284
gtk_label_set_mnemonic_widget (title_label, value);
2288
append_title_and_ellipsizing_value (NautilusPropertiesWindow *window,
2291
const char *file_attribute_name,
2292
const char *inconsistent_state,
2293
gboolean show_original)
2295
GtkLabel *title_label;
2298
title_label = attach_title_field (grid, title);
2299
value = attach_ellipsizing_value_field (window, grid,
2300
GTK_WIDGET (title_label),
2301
file_attribute_name,
2304
gtk_label_set_mnemonic_widget (title_label, value);
2308
append_directory_contents_fields (NautilusPropertiesWindow *window,
2311
GtkLabel *title_field, *value_field;
2314
title_field = attach_title_field (grid, "");
2315
window->details->directory_contents_title_field = title_field;
2316
gtk_label_set_line_wrap (title_field, TRUE);
2318
value_field = attach_directory_contents_value_field (window, grid, GTK_WIDGET (title_field));
2320
window->details->directory_contents_spinner = gtk_spinner_new ();
2322
gtk_grid_attach_next_to (grid,
2323
window->details->directory_contents_spinner,
2324
GTK_WIDGET (value_field),
2328
for (l = window->details->target_files; l; l = l->next) {
2331
file = NAUTILUS_FILE (l->data);
2332
start_deep_count_for_file (window, file);
2335
/* Fill in the initial value. */
2336
directory_contents_value_field_update (window);
2338
gtk_label_set_mnemonic_widget (title_field, GTK_WIDGET(value_field));
2342
create_page_with_hbox (GtkNotebook *notebook,
2344
const char *help_uri)
2348
g_assert (GTK_IS_NOTEBOOK (notebook));
2349
g_assert (title != NULL);
2351
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
2352
gtk_widget_show (hbox);
2353
gtk_container_set_border_width (GTK_CONTAINER (hbox), 12);
2354
gtk_box_set_spacing (GTK_BOX (hbox), 12);
2355
gtk_notebook_append_page (notebook, hbox, gtk_label_new (title));
2356
g_object_set_data_full (G_OBJECT (hbox), "help-uri", g_strdup (help_uri), g_free);
2362
create_page_with_vbox (GtkNotebook *notebook,
2364
const char *help_uri)
2368
g_assert (GTK_IS_NOTEBOOK (notebook));
2369
g_assert (title != NULL);
2371
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
2372
gtk_widget_show (vbox);
2373
gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
2374
gtk_notebook_append_page (notebook, vbox, gtk_label_new (title));
2375
g_object_set_data_full (G_OBJECT (vbox), "help-uri", g_strdup (help_uri), g_free);
2381
append_blank_row (GtkGrid *grid)
2383
return GTK_WIDGET (attach_title_field (grid, ""));
2387
append_blank_slim_row (GtkGrid *grid)
2390
PangoAttribute *attribute;
2391
PangoAttrList *attr_list;
2393
attr_list = pango_attr_list_new ();
2394
attribute = pango_attr_scale_new (0.30);
2395
pango_attr_list_insert (attr_list, attribute);
2397
w = gtk_label_new (NULL);
2398
gtk_label_set_attributes (GTK_LABEL (w), attr_list);
2399
gtk_widget_show (w);
2401
pango_attr_list_unref (attr_list);
2403
gtk_container_add (GTK_CONTAINER (grid), w);
2407
create_grid_with_standard_properties (void)
2411
grid = gtk_grid_new ();
2412
gtk_container_set_border_width (GTK_CONTAINER (grid), 6);
2413
gtk_grid_set_row_spacing (GTK_GRID (grid), ROW_PAD);
2414
gtk_grid_set_column_spacing (GTK_GRID (grid), 12);
2415
gtk_orientable_set_orientation (GTK_ORIENTABLE (grid), GTK_ORIENTATION_VERTICAL);
2416
gtk_widget_show (grid);
2422
is_merged_trash_directory (NautilusFile *file)
2427
file_uri = nautilus_file_get_uri (file);
2428
result = strcmp (file_uri, "trash:///") == 0;
2435
is_computer_directory (NautilusFile *file)
2440
file_uri = nautilus_file_get_uri (file);
2441
result = strcmp (file_uri, "computer:///") == 0;
2448
is_root_directory (NautilusFile *file)
2453
location = nautilus_file_get_location (file);
2454
result = nautilus_is_root_directory (location);
2455
g_object_unref (location);
2461
is_network_directory (NautilusFile *file)
2466
file_uri = nautilus_file_get_uri (file);
2467
result = strcmp (file_uri, "network:///") == 0;
2474
is_burn_directory (NautilusFile *file)
2479
file_uri = nautilus_file_get_uri (file);
2480
result = strcmp (file_uri, "burn:///") == 0;
2487
is_recent_directory (NautilusFile *file)
2492
file_uri = nautilus_file_get_uri (file);
2493
result = strcmp (file_uri, "recent:///") == 0;
2500
should_show_custom_icon_buttons (NautilusPropertiesWindow *window)
2502
if (is_multi_file_window (window)) {
2510
should_show_file_type (NautilusPropertiesWindow *window)
2512
if (!is_multi_file_window (window)
2513
&& (is_merged_trash_directory (get_target_file (window)) ||
2514
is_computer_directory (get_target_file (window)) ||
2515
is_network_directory (get_target_file (window)) ||
2516
is_burn_directory (get_target_file (window)))) {
2525
should_show_location_info (NautilusPropertiesWindow *window)
2527
if (!is_multi_file_window (window)
2528
&& (is_merged_trash_directory (get_target_file (window)) ||
2529
is_root_directory (get_target_file (window)) ||
2530
is_computer_directory (get_target_file (window)) ||
2531
is_network_directory (get_target_file (window)) ||
2532
is_burn_directory (get_target_file (window)))) {
2540
should_show_accessed_date (NautilusPropertiesWindow *window)
2542
/* Accessed date for directory seems useless. If we some
2543
* day decide that it is useful, we should separately
2544
* consider whether it's useful for "trash:".
2546
if (file_list_all_directories (window->details->target_files)) {
2554
should_show_link_target (NautilusPropertiesWindow *window)
2556
if (!is_multi_file_window (window)
2557
&& nautilus_file_is_symbolic_link (get_target_file (window))) {
2565
location_show_original (NautilusPropertiesWindow *window)
2569
/* there is no way a recent item will be mixed with
2570
other items so just pick the first file to check */
2571
file = NAUTILUS_FILE (g_list_nth_data (window->details->original_files, 0));
2572
return (file != NULL && !nautilus_file_is_in_recent (file));
2576
should_show_free_space (NautilusPropertiesWindow *window)
2578
if (!is_multi_file_window (window)
2579
&& (is_merged_trash_directory (get_target_file (window)) ||
2580
is_computer_directory (get_target_file (window)) ||
2581
is_network_directory (get_target_file (window)) ||
2582
is_recent_directory (get_target_file (window)) ||
2583
is_burn_directory (get_target_file (window)))) {
2587
if (file_list_all_directories (window->details->target_files)) {
2595
should_show_volume_info (NautilusPropertiesWindow *window)
2599
if (is_multi_file_window (window)) {
2603
file = get_original_file (window);
2609
if (nautilus_file_can_unmount (file)) {
2617
should_show_volume_usage (NautilusPropertiesWindow *window)
2620
gboolean success = FALSE;
2622
if (is_multi_file_window (window)) {
2626
file = get_original_file (window);
2632
if (nautilus_file_can_unmount (file)) {
2636
success = is_root_directory (file);
2639
/* Look at is_mountpoint for activation uri */
2646
paint_used_legend (GtkWidget *widget,
2650
NautilusPropertiesWindow *window;
2652
GtkAllocation allocation;
2654
gtk_widget_get_allocation (widget, &allocation);
2656
width = allocation.width;
2657
height = allocation.height;
2659
window = NAUTILUS_PROPERTIES_WINDOW (data);
2661
cairo_rectangle (cr,
2667
gdk_cairo_set_source_rgba (cr, &window->details->used_color);
2668
cairo_fill_preserve (cr);
2670
gdk_cairo_set_source_rgba (cr, &window->details->used_stroke_color);
2675
paint_free_legend (GtkWidget *widget,
2676
cairo_t *cr, gpointer data)
2678
NautilusPropertiesWindow *window;
2680
GtkAllocation allocation;
2682
window = NAUTILUS_PROPERTIES_WINDOW (data);
2683
gtk_widget_get_allocation (widget, &allocation);
2685
width = allocation.width;
2686
height = allocation.height;
2688
cairo_rectangle (cr,
2694
gdk_cairo_set_source_rgba (cr, &window->details->free_color);
2695
cairo_fill_preserve(cr);
2697
gdk_cairo_set_source_rgba (cr, &window->details->free_stroke_color);
2702
paint_slice (cairo_t *cr,
2706
double percent_start,
2707
double percent_width,
2708
const GdkRGBA *fill,
2709
const GdkRGBA *stroke)
2714
double offset = G_PI / 2.0;
2716
if (percent_width < .01) {
2720
angle1 = (percent_start * 2 * G_PI) - offset;
2721
angle2 = angle1 + (percent_width * 2 * G_PI);
2723
full = (percent_width > .99);
2726
cairo_move_to (cr, x, y);
2728
cairo_arc (cr, x, y, radius, angle1, angle2);
2731
cairo_line_to (cr, x, y);
2734
gdk_cairo_set_source_rgba (cr, fill);
2735
cairo_fill_preserve (cr);
2737
gdk_cairo_set_source_rgba (cr, stroke);
2742
paint_pie_chart (GtkWidget *widget,
2746
NautilusPropertiesWindow *window;
2748
double free, used, reserved;
2749
double xc, yc, radius;
2750
GtkAllocation allocation;
2751
GtkStyleContext *notebook_ctx;
2754
window = NAUTILUS_PROPERTIES_WINDOW (data);
2755
gtk_widget_get_allocation (widget, &allocation);
2757
width = allocation.width;
2758
height = allocation.height;
2760
notebook_ctx = gtk_widget_get_style_context (GTK_WIDGET (window->details->notebook));
2761
gtk_style_context_get_background_color (notebook_ctx,
2762
gtk_widget_get_state_flags (GTK_WIDGET (window->details->notebook)),
2766
gdk_cairo_set_source_rgba (cr, &bg_color);
2770
free = (double)window->details->volume_free / (double)window->details->volume_capacity;
2771
used = (double)window->details->volume_used / (double)window->details->volume_capacity;
2772
reserved = 1.0 - (used + free);
2777
if (width < height) {
2778
radius = width / 2 - 8;
2780
radius = height / 2 - 8;
2783
paint_slice (cr, xc, yc, radius,
2785
&window->details->free_color, &window->details->free_stroke_color);
2786
paint_slice (cr, xc, yc, radius,
2787
free + used, reserved,
2788
&window->details->unknown_color, &window->details->unknown_stroke_color);
2789
/* paint the used last so its slice strokes are on top */
2790
paint_slice (cr, xc, yc, radius,
2792
&window->details->used_color, &window->details->used_stroke_color);
2796
/* Copied from gtk/gtkstyle.c */
2799
rgb_to_hls (gdouble *r,
2840
l = (max + min) / 2;
2847
s = (max - min) / (max + min);
2849
s = (max - min) / (2 - max - min);
2853
h = (green - blue) / delta;
2854
else if (green == max)
2855
h = 2 + (blue - red) / delta;
2856
else if (blue == max)
2857
h = 4 + (red - green) / delta;
2870
hls_to_rgb (gdouble *h,
2883
if (lightness <= 0.5)
2884
m2 = lightness * (1 + saturation);
2886
m2 = lightness + saturation - lightness * saturation;
2887
m1 = 2 * lightness - m2;
2889
if (saturation == 0)
2904
r = m1 + (m2 - m1) * hue / 60;
2908
r = m1 + (m2 - m1) * (240 - hue) / 60;
2919
g = m1 + (m2 - m1) * hue / 60;
2923
g = m1 + (m2 - m1) * (240 - hue) / 60;
2934
b = m1 + (m2 - m1) * hue / 60;
2938
b = m1 + (m2 - m1) * (240 - hue) / 60;
2948
_pie_style_shade (GdkRGBA *a,
2960
rgb_to_hls (&red, &green, &blue);
2965
else if (green < 0.0)
2971
else if (blue < 0.0)
2974
hls_to_rgb (&red, &green, &blue);
2979
b->alpha = a->alpha;
2984
create_pie_widget (NautilusPropertiesWindow *window)
2988
GtkStyleContext *style;
2989
GtkWidget *pie_canvas;
2990
GtkWidget *used_canvas;
2991
GtkWidget *used_label;
2992
GtkWidget *used_type_label;
2993
GtkWidget *free_canvas;
2994
GtkWidget *free_label;
2995
GtkWidget *free_type_label;
2996
GtkWidget *capacity_label;
2997
GtkWidget *capacity_value_label;
2998
GtkWidget *fstype_label;
2999
GtkWidget *fstype_value_label;
3000
GtkWidget *spacer_label;
3004
const char *fs_type;
3009
capacity = g_format_size (window->details->volume_capacity);
3010
free = g_format_size (window->details->volume_free);
3011
used = g_format_size (window->details->volume_used);
3013
file = get_original_file (window);
3015
uri = nautilus_file_get_activation_uri (file);
3017
grid = GTK_GRID (gtk_grid_new ());
3018
gtk_widget_set_hexpand (GTK_WIDGET (grid), FALSE);
3019
gtk_container_set_border_width (GTK_CONTAINER (grid), 5);
3020
gtk_grid_set_row_spacing (GTK_GRID (grid), 10);
3021
gtk_grid_set_column_spacing (GTK_GRID (grid), 10);
3022
style = gtk_widget_get_style_context (GTK_WIDGET (grid));
3024
if (!gtk_style_context_lookup_color (style, "chart_rgba_0", &window->details->unknown_color)) {
3025
window->details->unknown_color.red = UNKNOWN_FILL_R;
3026
window->details->unknown_color.green = UNKNOWN_FILL_G;
3027
window->details->unknown_color.blue = UNKNOWN_FILL_B;
3028
window->details->unknown_color.alpha = 1;
3030
if (!gtk_style_context_lookup_color (style, "chart_rgba_1", &window->details->used_color)) {
3031
window->details->used_color.red = USED_FILL_R;
3032
window->details->used_color.green = USED_FILL_G;
3033
window->details->used_color.blue = USED_FILL_B;
3034
window->details->used_color.alpha = 1;
3037
if (!gtk_style_context_lookup_color (style, "chart_rgba_2", &window->details->free_color)) {
3038
window->details->free_color.red = FREE_FILL_R;
3039
window->details->free_color.green = FREE_FILL_G;
3040
window->details->free_color.blue = FREE_FILL_B;
3041
window->details->free_color.alpha = 1;
3044
_pie_style_shade (&window->details->used_color, &window->details->used_stroke_color, 0.7);
3045
_pie_style_shade (&window->details->free_color, &window->details->free_stroke_color, 0.7);
3046
_pie_style_shade (&window->details->unknown_color, &window->details->unknown_stroke_color, 0.7);
3048
pie_canvas = gtk_drawing_area_new ();
3049
gtk_widget_set_size_request (pie_canvas, 200, 200);
3051
used_canvas = gtk_drawing_area_new ();
3052
gtk_widget_set_size_request (used_canvas, 20, 20);
3053
used_label = gtk_label_new (used);
3054
/* Translators: "used" refers to the capacity of the filesystem */
3055
used_type_label = gtk_label_new (_("used"));
3057
free_canvas = gtk_drawing_area_new ();
3058
gtk_widget_set_size_request (free_canvas, 20, 20);
3059
free_label = gtk_label_new (free);
3060
/* Translators: "free" refers to the capacity of the filesystem */
3061
free_type_label = gtk_label_new (_("free"));
3063
capacity_label = gtk_label_new (_("Total capacity:"));
3064
capacity_value_label = gtk_label_new (capacity);
3066
fstype_label = gtk_label_new (_("Filesystem type:"));
3067
fstype_value_label = gtk_label_new (NULL);
3069
spacer_label = gtk_label_new ("");
3071
location = g_file_new_for_uri (uri);
3072
info = g_file_query_filesystem_info (location, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE,
3075
fs_type = g_file_info_get_attribute_string (info, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE);
3076
if (fs_type != NULL) {
3077
gtk_label_set_text (GTK_LABEL (fstype_value_label), fs_type);
3080
g_object_unref (info);
3082
g_object_unref (location);
3089
gtk_container_add_with_properties (GTK_CONTAINER (grid), pie_canvas,
3093
gtk_widget_set_vexpand (spacer_label, TRUE);
3094
gtk_grid_attach_next_to (grid, spacer_label, pie_canvas,
3095
GTK_POS_RIGHT, 1, 1);
3097
gtk_widget_set_halign (used_canvas, GTK_ALIGN_END);
3098
gtk_widget_set_vexpand (used_canvas, FALSE);
3099
gtk_grid_attach_next_to (grid, used_canvas, spacer_label,
3100
GTK_POS_BOTTOM, 1, 1);
3101
gtk_widget_set_halign (used_label, GTK_ALIGN_END);
3102
gtk_widget_set_vexpand (used_label, FALSE);
3103
gtk_grid_attach_next_to (grid, used_label, used_canvas,
3104
GTK_POS_RIGHT, 1, 1);
3105
gtk_widget_set_halign (used_type_label, GTK_ALIGN_START);
3106
gtk_widget_set_vexpand (used_type_label, FALSE);
3107
gtk_grid_attach_next_to (grid, used_type_label, used_label,
3108
GTK_POS_RIGHT, 1, 1);
3110
gtk_widget_set_halign (free_canvas, GTK_ALIGN_END);
3111
gtk_widget_set_vexpand (free_canvas, FALSE);
3112
gtk_grid_attach_next_to (grid, free_canvas, used_canvas,
3113
GTK_POS_BOTTOM, 1, 1);
3114
gtk_widget_set_halign (free_label, GTK_ALIGN_END);
3115
gtk_widget_set_vexpand (free_label, FALSE);
3116
gtk_grid_attach_next_to (grid, free_label, free_canvas,
3117
GTK_POS_RIGHT, 1, 1);
3118
gtk_widget_set_halign (free_type_label, GTK_ALIGN_START);
3119
gtk_widget_set_vexpand (free_type_label, FALSE);
3120
gtk_grid_attach_next_to (grid, free_type_label, free_label,
3121
GTK_POS_RIGHT, 1, 1);
3123
gtk_widget_set_halign (capacity_label, GTK_ALIGN_END);
3124
gtk_widget_set_vexpand (capacity_label, FALSE);
3125
gtk_grid_attach_next_to (grid, capacity_label, free_canvas,
3126
GTK_POS_BOTTOM, 1, 1);
3127
gtk_widget_set_halign (capacity_value_label, GTK_ALIGN_START);
3128
gtk_widget_set_vexpand (capacity_value_label, FALSE);
3129
gtk_grid_attach_next_to (grid, capacity_value_label, capacity_label,
3130
GTK_POS_RIGHT, 1, 1);
3132
gtk_widget_set_halign (fstype_label, GTK_ALIGN_END);
3133
gtk_widget_set_vexpand (fstype_label, FALSE);
3134
gtk_grid_attach_next_to (grid, fstype_label, capacity_label,
3135
GTK_POS_BOTTOM, 1, 1);
3136
gtk_widget_set_halign (fstype_value_label, GTK_ALIGN_START);
3137
gtk_widget_set_vexpand (fstype_value_label, FALSE);
3138
gtk_grid_attach_next_to (grid, fstype_value_label, fstype_label,
3139
GTK_POS_RIGHT, 1, 1);
3141
g_signal_connect (pie_canvas, "draw",
3142
G_CALLBACK (paint_pie_chart), window);
3143
g_signal_connect (used_canvas, "draw",
3144
G_CALLBACK (paint_used_legend), window);
3145
g_signal_connect (free_canvas, "draw",
3146
G_CALLBACK (paint_free_legend), window);
3148
return GTK_WIDGET (grid);
3152
create_volume_usage_widget (NautilusPropertiesWindow *window)
3154
GtkWidget *piewidget = NULL;
3160
file = get_original_file (window);
3162
uri = nautilus_file_get_activation_uri (file);
3164
location = g_file_new_for_uri (uri);
3165
info = g_file_query_filesystem_info (location, "filesystem::*", NULL, NULL);
3168
window->details->volume_capacity = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_FILESYSTEM_SIZE);
3169
window->details->volume_free = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_FILESYSTEM_FREE);
3170
if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_FILESYSTEM_USED)) {
3171
window->details->volume_used = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_FILESYSTEM_USED);
3173
window->details->volume_used = window->details->volume_capacity - window->details->volume_free;
3176
g_object_unref (info);
3178
window->details->volume_capacity = 0;
3179
window->details->volume_free = 0;
3180
window->details->volume_used = 0;
3183
g_object_unref (location);
3185
if (window->details->volume_capacity > 0) {
3186
piewidget = create_pie_widget (window);
3187
gtk_widget_show_all (piewidget);
3194
create_basic_page (NautilusPropertiesWindow *window)
3197
GtkWidget *icon_aligner;
3198
GtkWidget *icon_pixmap_widget;
3199
GtkWidget *volume_usage;
3200
GtkWidget *hbox, *vbox;
3202
if (!g_strcmp0(g_getenv("XDG_CURRENT_DESKTOP"), "Unity"))
3203
hbox = create_page_with_hbox (window->details->notebook, _("Basic"),
3204
"help:ubuntu-help/nautilus-file-properties-basic");
3206
hbox = create_page_with_hbox (window->details->notebook, _("Basic"),
3207
"help:gnome-help/nautilus-file-properties-basic");
3211
icon_pixmap_widget = create_image_widget (
3212
window, should_show_custom_icon_buttons (window));
3213
gtk_widget_show (icon_pixmap_widget);
3215
icon_aligner = gtk_alignment_new (1, 0, 0, 0);
3216
gtk_widget_show (icon_aligner);
3218
gtk_container_add (GTK_CONTAINER (icon_aligner), icon_pixmap_widget);
3219
gtk_box_pack_start (GTK_BOX (hbox), icon_aligner, FALSE, FALSE, 0);
3221
window->details->icon_chooser = NULL;
3225
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
3226
gtk_widget_show (vbox);
3227
gtk_container_add (GTK_CONTAINER (hbox), vbox);
3229
grid = GTK_GRID (create_grid_with_standard_properties ());
3230
gtk_box_pack_start (GTK_BOX (vbox), GTK_WIDGET (grid), FALSE, FALSE, 0);
3231
window->details->basic_grid = grid;
3233
/* Name label. The text will be determined in update_name_field */
3234
window->details->name_label = attach_title_field (grid, NULL);
3237
window->details->name_field = NULL;
3238
update_name_field (window);
3240
/* Start with name field selected, if it's an entry. */
3241
if (NAUTILUS_IS_ENTRY (window->details->name_field)) {
3242
nautilus_entry_select_all (NAUTILUS_ENTRY (window->details->name_field));
3243
gtk_widget_grab_focus (GTK_WIDGET (window->details->name_field));
3246
if (nautilus_desktop_item_properties_should_show (window->details->target_files)) {
3247
GtkSizeGroup *label_size_group;
3250
label_size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
3251
gtk_size_group_add_widget (label_size_group,
3252
GTK_WIDGET (window->details->name_label));
3253
box = nautilus_desktop_item_properties_make_box (label_size_group,
3254
window->details->target_files);
3256
gtk_grid_attach_next_to (window->details->basic_grid, box,
3257
GTK_WIDGET (window->details->name_label),
3258
GTK_POS_BOTTOM, 2, 1);
3261
if (should_show_file_type (window)) {
3262
append_title_and_ellipsizing_value (window, grid,
3265
INCONSISTENT_STATE_STRING,
3269
if (should_show_link_target (window)) {
3270
append_title_and_ellipsizing_value (window, grid,
3273
INCONSISTENT_STATE_STRING,
3277
if (is_multi_file_window (window) ||
3278
nautilus_file_is_directory (get_target_file (window))) {
3279
append_directory_contents_fields (window, grid);
3281
append_title_value_pair (window, grid, _("Size:"),
3283
INCONSISTENT_STATE_STRING,
3287
append_blank_row (grid);
3289
if (should_show_location_info (window)) {
3290
append_title_and_ellipsizing_value (window, grid, _("Location:"),
3292
INCONSISTENT_STATE_STRING,
3293
location_show_original (window));
3296
if (should_show_volume_info (window)) {
3297
append_title_and_ellipsizing_value (window, grid,
3300
INCONSISTENT_STATE_STRING,
3304
if (should_show_accessed_date (window)) {
3305
append_blank_row (grid);
3307
append_title_value_pair (window, grid, _("Accessed:"),
3308
"date_accessed_full",
3309
INCONSISTENT_STATE_STRING,
3311
append_title_value_pair (window, grid, _("Modified:"),
3312
"date_modified_full",
3313
INCONSISTENT_STATE_STRING,
3317
if (should_show_free_space (window)
3318
&& ! should_show_volume_usage (window)) {
3319
append_blank_row (grid);
3321
append_title_value_pair (window, grid, _("Free space:"),
3323
INCONSISTENT_STATE_STRING,
3327
if (should_show_volume_usage (window)) {
3328
volume_usage = create_volume_usage_widget (window);
3329
if (volume_usage != NULL) {
3330
gtk_container_add_with_properties (GTK_CONTAINER (grid),
3339
files_has_directory (NautilusPropertiesWindow *window)
3343
for (l = window->details->target_files; l != NULL; l = l->next) {
3345
file = NAUTILUS_FILE (l->data);
3346
if (nautilus_file_is_directory (file)) {
3356
files_has_changable_permissions_directory (NautilusPropertiesWindow *window)
3359
gboolean changable = FALSE;
3361
for (l = window->details->target_files; l != NULL; l = l->next) {
3363
file = NAUTILUS_FILE (l->data);
3364
if (nautilus_file_is_directory (file) &&
3365
nautilus_file_can_get_permissions (file) &&
3366
nautilus_file_can_set_permissions (file)) {
3378
files_has_file (NautilusPropertiesWindow *window)
3382
for (l = window->details->target_files; l != NULL; l = l->next) {
3384
file = NAUTILUS_FILE (l->data);
3385
if (!nautilus_file_is_directory (file)) {
3394
start_long_operation (NautilusPropertiesWindow *window)
3396
if (window->details->long_operation_underway == 0) {
3397
/* start long operation */
3400
cursor = gdk_cursor_new (GDK_WATCH);
3401
gdk_window_set_cursor (gtk_widget_get_window (GTK_WIDGET (window)), cursor);
3402
g_object_unref (cursor);
3404
window->details->long_operation_underway ++;
3408
end_long_operation (NautilusPropertiesWindow *window)
3410
if (gtk_widget_get_window (GTK_WIDGET (window)) != NULL &&
3411
window->details->long_operation_underway == 1) {
3413
gdk_window_set_cursor (gtk_widget_get_window (GTK_WIDGET (window)), NULL);
3415
window->details->long_operation_underway--;
3419
permission_change_callback (NautilusFile *file,
3422
gpointer callback_data)
3424
NautilusPropertiesWindow *window;
3425
g_assert (callback_data != NULL);
3427
window = NAUTILUS_PROPERTIES_WINDOW (callback_data);
3428
end_long_operation (window);
3430
/* Report the error if it's an error. */
3431
nautilus_report_error_setting_permissions (file, error, NULL);
3433
g_object_unref (window);
3437
update_permissions (NautilusPropertiesWindow *window,
3438
guint32 vfs_new_perm,
3441
gboolean apply_to_both_folder_and_dir,
3442
gboolean use_original)
3446
for (l = window->details->target_files; l != NULL; l = l->next) {
3448
guint32 permissions;
3450
file = NAUTILUS_FILE (l->data);
3452
if (!nautilus_file_can_get_permissions (file)) {
3456
if (!apply_to_both_folder_and_dir &&
3457
((nautilus_file_is_directory (file) && !is_folder) ||
3458
(!nautilus_file_is_directory (file) && is_folder))) {
3462
permissions = nautilus_file_get_permissions (file);
3465
if (g_hash_table_lookup_extended (window->details->initial_permissions,
3466
file, NULL, &ptr)) {
3467
permissions = (permissions & ~vfs_mask) | (GPOINTER_TO_INT (ptr) & vfs_mask);
3470
permissions = (permissions & ~vfs_mask) | vfs_new_perm;
3473
start_long_operation (window);
3474
g_object_ref (window);
3475
nautilus_file_set_permissions
3477
permission_change_callback,
3483
initial_permission_state_consistent (NautilusPropertiesWindow *window,
3486
gboolean both_folder_and_dir)
3490
guint32 first_permissions;
3493
first_permissions = 0;
3494
for (l = window->details->target_files; l != NULL; l = l->next) {
3496
guint32 permissions;
3500
if (!both_folder_and_dir &&
3501
((nautilus_file_is_directory (file) && !is_folder) ||
3502
(!nautilus_file_is_directory (file) && is_folder))) {
3506
permissions = GPOINTER_TO_INT (g_hash_table_lookup (window->details->initial_permissions,
3510
if ((permissions & mask) != mask &&
3511
(permissions & mask) != 0) {
3512
/* Not fully on or off -> inconsistent */
3516
first_permissions = permissions;
3519
} else if ((permissions & mask) != first_permissions) {
3520
/* Not same permissions as first -> inconsistent */
3528
permission_button_toggled (GtkToggleButton *button,
3529
NautilusPropertiesWindow *window)
3531
gboolean is_folder, is_special;
3532
guint32 permission_mask;
3533
gboolean inconsistent;
3536
permission_mask = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (button),
3538
is_folder = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (button),
3540
is_special = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (button),
3543
if (gtk_toggle_button_get_active (button)
3544
&& !gtk_toggle_button_get_inconsistent (button)) {
3545
/* Go to the initial state unless the initial state was
3546
consistent, or we support recursive apply */
3547
inconsistent = TRUE;
3550
if (initial_permission_state_consistent (window, permission_mask, is_folder, is_special)) {
3551
inconsistent = FALSE;
3554
} else if (gtk_toggle_button_get_inconsistent (button)
3555
&& !gtk_toggle_button_get_active (button)) {
3556
inconsistent = FALSE;
3559
inconsistent = FALSE;
3563
g_signal_handlers_block_by_func (G_OBJECT (button),
3564
G_CALLBACK (permission_button_toggled),
3567
gtk_toggle_button_set_active (button, on);
3568
gtk_toggle_button_set_inconsistent (button, inconsistent);
3570
g_signal_handlers_unblock_by_func (G_OBJECT (button),
3571
G_CALLBACK (permission_button_toggled),
3574
update_permissions (window,
3575
on?permission_mask:0,
3583
permission_button_update (NautilusPropertiesWindow *window,
3584
GtkToggleButton *button)
3589
gboolean all_cannot_set;
3590
gboolean is_folder, is_special;
3593
guint32 button_permission;
3595
button_permission = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (button),
3597
is_folder = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (button),
3599
is_special = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (button),
3604
all_cannot_set = TRUE;
3606
for (l = window->details->target_files; l != NULL; l = l->next) {
3608
guint32 file_permissions;
3610
file = NAUTILUS_FILE (l->data);
3612
if (!nautilus_file_can_get_permissions (file)) {
3617
((nautilus_file_is_directory (file) && !is_folder) ||
3618
(!nautilus_file_is_directory (file) && is_folder))) {
3624
file_permissions = nautilus_file_get_permissions (file);
3626
if ((file_permissions & button_permission) == button_permission) {
3628
} else if ((file_permissions & button_permission) == 0) {
3635
if (nautilus_file_can_set_permissions (file)) {
3636
all_cannot_set = FALSE;
3640
sensitive = !all_cannot_set;
3642
g_signal_handlers_block_by_func (G_OBJECT (button),
3643
G_CALLBACK (permission_button_toggled),
3646
gtk_toggle_button_set_active (button, !all_unset);
3647
/* if actually inconsistent, or default value for file buttons
3648
if no files are selected. (useful for recursive apply) */
3649
gtk_toggle_button_set_inconsistent (button,
3650
(!all_unset && !all_set) ||
3651
(!is_folder && no_match));
3652
gtk_widget_set_sensitive (GTK_WIDGET (button), sensitive);
3654
g_signal_handlers_unblock_by_func (G_OBJECT (button),
3655
G_CALLBACK (permission_button_toggled),
3660
set_up_permissions_checkbox (NautilusPropertiesWindow *window,
3661
GtkWidget *check_button,
3665
/* Load up the check_button with data we'll need when updating its state. */
3666
g_object_set_data (G_OBJECT (check_button), "permission",
3667
GINT_TO_POINTER (permission));
3668
g_object_set_data (G_OBJECT (check_button), "properties_window",
3670
g_object_set_data (G_OBJECT (check_button), "is-folder",
3671
GINT_TO_POINTER (is_folder));
3673
window->details->permission_buttons =
3674
g_list_prepend (window->details->permission_buttons,
3677
g_signal_connect_object (check_button, "toggled",
3678
G_CALLBACK (permission_button_toggled),
3684
add_execute_checkbox_with_label (NautilusPropertiesWindow *window,
3688
guint32 permission_to_check,
3689
GtkLabel *label_for,
3692
GtkWidget *check_button;
3693
gboolean a11y_enabled;
3695
check_button = gtk_check_button_new_with_mnemonic (label);
3696
gtk_widget_show (check_button);
3699
gtk_grid_attach_next_to (grid, check_button, sibling,
3700
GTK_POS_RIGHT, 1, 1);
3702
gtk_container_add (GTK_CONTAINER (grid), check_button);
3705
set_up_permissions_checkbox (window,
3707
permission_to_check,
3710
a11y_enabled = GTK_IS_ACCESSIBLE (gtk_widget_get_accessible (check_button));
3711
if (a11y_enabled && label_for != NULL) {
3712
eel_accessibility_set_up_label_widget_relation (GTK_WIDGET (label_for),
3716
return check_button;
3720
UNIX_PERM_SUID = S_ISUID,
3721
UNIX_PERM_SGID = S_ISGID,
3722
UNIX_PERM_STICKY = 01000, /* S_ISVTX not defined on all systems */
3723
UNIX_PERM_USER_READ = S_IRUSR,
3724
UNIX_PERM_USER_WRITE = S_IWUSR,
3725
UNIX_PERM_USER_EXEC = S_IXUSR,
3726
UNIX_PERM_USER_ALL = S_IRUSR | S_IWUSR | S_IXUSR,
3727
UNIX_PERM_GROUP_READ = S_IRGRP,
3728
UNIX_PERM_GROUP_WRITE = S_IWGRP,
3729
UNIX_PERM_GROUP_EXEC = S_IXGRP,
3730
UNIX_PERM_GROUP_ALL = S_IRGRP | S_IWGRP | S_IXGRP,
3731
UNIX_PERM_OTHER_READ = S_IROTH,
3732
UNIX_PERM_OTHER_WRITE = S_IWOTH,
3733
UNIX_PERM_OTHER_EXEC = S_IXOTH,
3734
UNIX_PERM_OTHER_ALL = S_IROTH | S_IWOTH | S_IXOTH
3738
PERMISSION_READ = (1<<0),
3739
PERMISSION_WRITE = (1<<1),
3740
PERMISSION_EXEC = (1<<2)
3749
static guint32 vfs_perms[3][3] = {
3750
{UNIX_PERM_USER_READ, UNIX_PERM_USER_WRITE, UNIX_PERM_USER_EXEC},
3751
{UNIX_PERM_GROUP_READ, UNIX_PERM_GROUP_WRITE, UNIX_PERM_GROUP_EXEC},
3752
{UNIX_PERM_OTHER_READ, UNIX_PERM_OTHER_WRITE, UNIX_PERM_OTHER_EXEC},
3756
permission_to_vfs (PermissionType type, PermissionValue perm)
3759
g_assert (type >= 0 && type < 3);
3762
if (perm & PERMISSION_READ) {
3763
vfs_perm |= vfs_perms[type][0];
3765
if (perm & PERMISSION_WRITE) {
3766
vfs_perm |= vfs_perms[type][1];
3768
if (perm & PERMISSION_EXEC) {
3769
vfs_perm |= vfs_perms[type][2];
3776
static PermissionValue
3777
permission_from_vfs (PermissionType type, guint32 vfs_perm)
3779
PermissionValue perm;
3780
g_assert (type >= 0 && type < 3);
3783
if (vfs_perm & vfs_perms[type][0]) {
3784
perm |= PERMISSION_READ;
3786
if (vfs_perm & vfs_perms[type][1]) {
3787
perm |= PERMISSION_WRITE;
3789
if (vfs_perm & vfs_perms[type][2]) {
3790
perm |= PERMISSION_EXEC;
3797
permission_combo_changed (GtkWidget *combo, NautilusPropertiesWindow *window)
3800
GtkTreeModel *model;
3801
gboolean is_folder, use_original;
3802
PermissionType type;
3804
guint32 vfs_new_perm, vfs_mask;
3806
is_folder = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (combo), "is-folder"));
3807
type = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (combo), "permission-type"));
3810
mask = PERMISSION_READ|PERMISSION_WRITE|PERMISSION_EXEC;
3812
mask = PERMISSION_READ|PERMISSION_WRITE;
3815
vfs_mask = permission_to_vfs (type, mask);
3817
model = gtk_combo_box_get_model (GTK_COMBO_BOX (combo));
3819
if (!gtk_combo_box_get_active_iter (GTK_COMBO_BOX (combo), &iter)) {
3822
gtk_tree_model_get (model, &iter, COLUMN_VALUE, &new_perm,
3823
COLUMN_USE_ORIGINAL, &use_original, -1);
3824
vfs_new_perm = permission_to_vfs (type, new_perm);
3826
update_permissions (window, vfs_new_perm, vfs_mask,
3827
is_folder, FALSE, use_original);
3831
permission_combo_add_multiple_choice (GtkComboBox *combo, GtkTreeIter *iter)
3833
GtkTreeModel *model;
3834
GtkListStore *store;
3837
model = gtk_combo_box_get_model (combo);
3838
store = GTK_LIST_STORE (model);
3841
gtk_tree_model_get_iter_first (model, iter);
3844
gtk_tree_model_get (model, iter, COLUMN_USE_ORIGINAL, &multi, -1);
3850
} while (gtk_tree_model_iter_next (model, iter));
3853
gtk_list_store_append (store, iter);
3854
gtk_list_store_set (store, iter,
3857
COLUMN_USE_ORIGINAL, TRUE, -1);
3862
permission_combo_update (NautilusPropertiesWindow *window,
3865
PermissionType type;
3866
PermissionValue perm, all_dir_perm, all_file_perm, all_perm;
3867
gboolean is_folder, no_files, no_dirs, all_file_same, all_dir_same, all_same;
3868
gboolean all_dir_cannot_set, all_file_cannot_set, sensitive;
3871
GtkTreeModel *model;
3872
GtkListStore *store;
3876
model = gtk_combo_box_get_model (combo);
3878
is_folder = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (combo), "is-folder"));
3879
type = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (combo), "permission-type"));
3882
if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (combo), &iter)) {
3883
gtk_tree_model_get (model, &iter, COLUMN_USE_ORIGINAL, &is_multi, -1);
3888
all_dir_same = TRUE;
3889
all_file_same = TRUE;
3892
all_dir_cannot_set = TRUE;
3893
all_file_cannot_set = TRUE;
3895
for (l = window->details->target_files; l != NULL; l = l->next) {
3897
guint32 file_permissions;
3899
file = NAUTILUS_FILE (l->data);
3901
if (!nautilus_file_can_get_permissions (file)) {
3905
if (nautilus_file_is_directory (file)) {
3906
mask = PERMISSION_READ|PERMISSION_WRITE|PERMISSION_EXEC;
3908
mask = PERMISSION_READ|PERMISSION_WRITE;
3911
file_permissions = nautilus_file_get_permissions (file);
3913
perm = permission_from_vfs (type, file_permissions) & mask;
3915
if (nautilus_file_is_directory (file)) {
3917
all_dir_perm = perm;
3919
} else if (perm != all_dir_perm) {
3920
all_dir_same = FALSE;
3923
if (nautilus_file_can_set_permissions (file)) {
3924
all_dir_cannot_set = FALSE;
3928
all_file_perm = perm;
3930
} else if (perm != all_file_perm) {
3931
all_file_same = FALSE;
3934
if (nautilus_file_can_set_permissions (file)) {
3935
all_file_cannot_set = FALSE;
3941
all_same = all_dir_same;
3942
all_perm = all_dir_perm;
3944
all_same = all_file_same && !no_files;
3945
all_perm = all_file_perm;
3948
store = GTK_LIST_STORE (model);
3953
gtk_tree_model_get_iter_first (model, &iter);
3956
gtk_tree_model_get (model, &iter, 1, ¤t_perm, -1);
3958
if (current_perm == all_perm) {
3962
} while (gtk_tree_model_iter_next (model, &iter));
3966
str = g_string_new ("");
3968
if (!(all_perm & PERMISSION_READ)) {
3969
/* translators: this gets concatenated to "no read",
3970
* "no access", etc. (see following strings)
3972
g_string_append (str, _("no "));
3975
g_string_append (str, _("list"));
3977
g_string_append (str, _("read"));
3980
g_string_append (str, ", ");
3982
if (!(all_perm & PERMISSION_WRITE)) {
3983
g_string_append (str, _("no "));
3986
g_string_append (str, _("create/delete"));
3988
g_string_append (str, _("write"));
3992
g_string_append (str, ", ");
3994
if (!(all_perm & PERMISSION_EXEC)) {
3995
g_string_append (str, _("no "));
3997
g_string_append (str, _("access"));
4000
gtk_list_store_append (store, &iter);
4001
gtk_list_store_set (store, &iter,
4005
g_string_free (str, TRUE);
4008
permission_combo_add_multiple_choice (combo, &iter);
4011
g_signal_handlers_block_by_func (G_OBJECT (combo),
4012
G_CALLBACK (permission_combo_changed),
4015
gtk_combo_box_set_active_iter (combo, &iter);
4017
/* Also enable if no files found (for recursive
4018
file changes when only selecting folders) */
4020
sensitive = !all_dir_cannot_set;
4022
sensitive = !all_file_cannot_set;
4024
gtk_widget_set_sensitive (GTK_WIDGET (combo), sensitive);
4026
g_signal_handlers_unblock_by_func (G_OBJECT (combo),
4027
G_CALLBACK (permission_combo_changed),
4033
create_permissions_combo_box (PermissionType type,
4037
GtkListStore *store;
4038
GtkCellRenderer *cell;
4041
store = gtk_list_store_new (NUM_COLUMNS, G_TYPE_STRING, G_TYPE_INT, G_TYPE_BOOLEAN, G_TYPE_STRING);
4042
combo = gtk_combo_box_new_with_model (GTK_TREE_MODEL (store));
4043
gtk_combo_box_set_id_column (GTK_COMBO_BOX (combo), COLUMN_ID);
4045
g_object_set_data (G_OBJECT (combo), "is-folder", GINT_TO_POINTER (is_folder));
4046
g_object_set_data (G_OBJECT (combo), "permission-type", GINT_TO_POINTER (type));
4049
if (type != PERMISSION_USER) {
4050
gtk_list_store_append (store, &iter);
4051
/* Translators: this is referred to the permissions
4052
* the user has in a directory.
4054
gtk_list_store_set (store, &iter,
4055
COLUMN_NAME, _("None"),
4060
gtk_list_store_append (store, &iter);
4061
gtk_list_store_set (store, &iter,
4062
COLUMN_NAME, _("List files only"),
4063
COLUMN_VALUE, PERMISSION_READ,
4066
gtk_list_store_append (store, &iter);
4067
gtk_list_store_set (store, &iter,
4068
COLUMN_NAME, _("Access files"),
4069
COLUMN_VALUE, PERMISSION_READ|PERMISSION_EXEC,
4072
gtk_list_store_append (store, &iter);
4073
gtk_list_store_set (store, &iter,
4074
COLUMN_NAME, _("Create and delete files"),
4075
COLUMN_VALUE, PERMISSION_READ|PERMISSION_EXEC|PERMISSION_WRITE,
4079
if (type != PERMISSION_USER) {
4080
gtk_list_store_append (store, &iter);
4081
gtk_list_store_set (store, &iter,
4082
COLUMN_NAME, _("None"),
4087
gtk_list_store_append (store, &iter);
4088
gtk_list_store_set (store, &iter,
4089
COLUMN_NAME, _("Read-only"),
4090
COLUMN_VALUE, PERMISSION_READ,
4093
gtk_list_store_append (store, &iter);
4094
gtk_list_store_set (store, &iter,
4095
COLUMN_NAME, _("Read and write"),
4096
COLUMN_VALUE, PERMISSION_READ|PERMISSION_WRITE,
4100
g_object_unref (store);
4102
cell = gtk_cell_renderer_text_new ();
4103
gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), cell, TRUE);
4104
gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo), cell,
4105
"text", COLUMN_NAME,
4112
add_permissions_combo_box (NautilusPropertiesWindow *window,
4114
PermissionType type,
4116
gboolean short_label)
4122
label = attach_title_field (grid, _("Access:"));
4123
} else if (is_folder) {
4124
label = attach_title_field (grid, _("Folder access:"));
4126
label = attach_title_field (grid, _("File access:"));
4129
combo = create_permissions_combo_box (type, is_folder);
4131
window->details->permission_combos = g_list_prepend (window->details->permission_combos,
4134
g_signal_connect (combo, "changed", G_CALLBACK (permission_combo_changed), window);
4136
gtk_label_set_mnemonic_widget (label, combo);
4137
gtk_widget_show (combo);
4139
gtk_grid_attach_next_to (grid, combo, GTK_WIDGET (label),
4140
GTK_POS_RIGHT, 1, 1);
4144
all_can_get_permissions (GList *file_list)
4147
for (l = file_list; l != NULL; l = l->next) {
4150
file = NAUTILUS_FILE (l->data);
4152
if (!nautilus_file_can_get_permissions (file)) {
4161
all_can_set_permissions (GList *file_list)
4164
for (l = file_list; l != NULL; l = l->next) {
4167
file = NAUTILUS_FILE (l->data);
4169
if (!nautilus_file_can_set_permissions (file)) {
4178
get_initial_permissions (GList *file_list)
4183
ret = g_hash_table_new (g_direct_hash,
4186
for (l = file_list; l != NULL; l = l->next) {
4187
guint32 permissions;
4190
file = NAUTILUS_FILE (l->data);
4192
permissions = nautilus_file_get_permissions (file);
4193
g_hash_table_insert (ret, file,
4194
GINT_TO_POINTER (permissions));
4201
create_simple_permissions (NautilusPropertiesWindow *window, GtkGrid *page_grid)
4203
gboolean has_directory;
4205
GtkLabel *group_label;
4206
GtkLabel *owner_label;
4208
GtkComboBox *group_combo_box;
4209
GtkComboBox *owner_combo_box;
4211
has_directory = files_has_directory (window);
4212
has_file = files_has_file (window);
4214
if (!is_multi_file_window (window) && nautilus_file_can_set_owner (get_target_file (window))) {
4215
owner_label = attach_title_field (page_grid, _("_Owner:"));
4216
/* Combo box in this case. */
4217
owner_combo_box = attach_owner_combo_box (page_grid,
4218
GTK_WIDGET (owner_label),
4219
get_target_file (window));
4220
gtk_label_set_mnemonic_widget (owner_label,
4221
GTK_WIDGET (owner_combo_box));
4223
owner_label = attach_title_field (page_grid, _("Owner:"));
4224
/* Static text in this case. */
4225
value = attach_value_field (window,
4226
page_grid, GTK_WIDGET (owner_label),
4228
INCONSISTENT_STATE_STRING,
4230
gtk_label_set_mnemonic_widget (owner_label, value);
4232
if (has_directory && has_file) {
4233
add_permissions_combo_box (window, page_grid,
4234
PERMISSION_USER, TRUE, FALSE);
4235
add_permissions_combo_box (window, page_grid,
4236
PERMISSION_USER, FALSE, FALSE);
4238
add_permissions_combo_box (window, page_grid,
4239
PERMISSION_USER, has_directory, TRUE);
4242
append_blank_slim_row (page_grid);
4244
if (!is_multi_file_window (window) && nautilus_file_can_set_group (get_target_file (window))) {
4245
group_label = attach_title_field (page_grid, _("_Group:"));
4247
/* Combo box in this case. */
4248
group_combo_box = attach_group_combo_box (page_grid, GTK_WIDGET (group_label),
4249
get_target_file (window));
4250
gtk_label_set_mnemonic_widget (group_label,
4251
GTK_WIDGET (group_combo_box));
4253
group_label = attach_title_field (page_grid, _("Group:"));
4255
/* Static text in this case. */
4256
value = attach_value_field (window, page_grid,
4257
GTK_WIDGET (group_label),
4259
INCONSISTENT_STATE_STRING,
4261
gtk_label_set_mnemonic_widget (group_label, value);
4263
if (has_directory && has_file) {
4264
add_permissions_combo_box (window, page_grid,
4265
PERMISSION_GROUP, TRUE, FALSE);
4266
add_permissions_combo_box (window, page_grid,
4267
PERMISSION_GROUP, FALSE, FALSE);
4269
add_permissions_combo_box (window, page_grid,
4270
PERMISSION_GROUP, has_directory, TRUE);
4273
append_blank_slim_row (page_grid);
4274
attach_title_field (page_grid, _("Others"));
4275
if (has_directory && has_file) {
4276
add_permissions_combo_box (window, page_grid,
4277
PERMISSION_OTHER, TRUE, FALSE);
4278
add_permissions_combo_box (window, page_grid,
4279
PERMISSION_OTHER, FALSE, FALSE);
4281
add_permissions_combo_box (window, page_grid,
4282
PERMISSION_OTHER, has_directory, TRUE);
4285
if (!has_directory) {
4286
GtkLabel *execute_label;
4287
append_blank_slim_row (page_grid);
4289
execute_label = attach_title_field (page_grid, _("Execute:"));
4290
add_execute_checkbox_with_label (window, page_grid,
4291
GTK_WIDGET (execute_label),
4292
_("Allow _executing file as program"),
4293
UNIX_PERM_USER_EXEC|UNIX_PERM_GROUP_EXEC|UNIX_PERM_OTHER_EXEC,
4294
execute_label, FALSE);
4299
set_recursive_permissions_done (gboolean success,
4300
gpointer callback_data)
4302
NautilusPropertiesWindow *window;
4304
window = NAUTILUS_PROPERTIES_WINDOW (callback_data);
4305
end_long_operation (window);
4307
g_object_unref (window);
4311
on_change_permissions_response (GtkDialog *dialog,
4313
NautilusPropertiesWindow *window)
4315
if (response != GTK_RESPONSE_OK) {
4316
gtk_widget_destroy (GTK_WIDGET (dialog));
4319
guint32 file_permission, file_permission_mask;
4320
guint32 dir_permission, dir_permission_mask;
4321
guint32 vfs_mask, vfs_new_perm;
4323
gboolean is_folder, use_original;
4325
GtkTreeModel *model;
4327
PermissionType type;
4330
file_permission = 0;
4331
file_permission_mask = 0;
4333
dir_permission_mask = 0;
4335
/* Simple mode, minus exec checkbox */
4336
for (l = window->details->change_permission_combos; l != NULL; l = l->next) {
4339
if (!gtk_combo_box_get_active_iter (GTK_COMBO_BOX (combo), &iter)) {
4343
type = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (combo), "permission-type"));
4344
is_folder = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (combo), "is-folder"));
4346
model = gtk_combo_box_get_model (GTK_COMBO_BOX (combo));
4347
gtk_tree_model_get (model, &iter,
4348
COLUMN_VALUE, &new_perm,
4349
COLUMN_USE_ORIGINAL, &use_original, -1);
4353
vfs_new_perm = permission_to_vfs (type, new_perm);
4356
mask = PERMISSION_READ|PERMISSION_WRITE|PERMISSION_EXEC;
4358
mask = PERMISSION_READ|PERMISSION_WRITE;
4360
vfs_mask = permission_to_vfs (type, mask);
4363
dir_permission_mask |= vfs_mask;
4364
dir_permission |= vfs_new_perm;
4366
file_permission_mask |= vfs_mask;
4367
file_permission |= vfs_new_perm;
4371
for (l = window->details->target_files; l != NULL; l = l->next) {
4375
file = NAUTILUS_FILE (l->data);
4377
if (nautilus_file_is_directory (file) &&
4378
nautilus_file_can_set_permissions (file)) {
4379
uri = nautilus_file_get_uri (file);
4380
start_long_operation (window);
4381
g_object_ref (window);
4382
nautilus_file_set_permissions_recursive (uri,
4384
file_permission_mask,
4386
dir_permission_mask,
4387
set_recursive_permissions_done,
4392
gtk_widget_destroy (GTK_WIDGET (dialog));
4396
set_active_from_umask (GtkWidget *combo,
4397
PermissionType type,
4406
initial = (S_IRWXU | S_IRWXG | S_IRWXO);
4408
initial = (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
4411
umask (mask = umask (0));
4413
p = ~mask & initial;
4415
if (type == PERMISSION_USER) {
4416
p &= ~(S_IRWXG | S_IRWXO);
4417
if ((p & S_IRWXU) == S_IRWXU) {
4419
} else if ((p & (S_IRUSR | S_IWUSR)) == (S_IRUSR | S_IWUSR)) {
4421
} else if ((p & (S_IRUSR | S_IXUSR)) == (S_IRUSR | S_IXUSR)) {
4423
} else if ((p & S_IRUSR) == S_IRUSR) {
4428
} else if (type == PERMISSION_GROUP) {
4429
p &= ~(S_IRWXU | S_IRWXO);
4430
if ((p & S_IRWXG) == S_IRWXG) {
4432
} else if ((p & (S_IRGRP | S_IWGRP)) == (S_IRGRP | S_IWGRP)) {
4434
} else if ((p & (S_IRGRP | S_IXGRP)) == (S_IRGRP | S_IXGRP)) {
4436
} else if ((p & S_IRGRP) == S_IRGRP) {
4442
p &= ~(S_IRWXU | S_IRWXG);
4443
if ((p & S_IRWXO) == S_IRWXO) {
4445
} else if ((p & (S_IROTH | S_IWOTH)) == (S_IROTH | S_IWOTH)) {
4447
} else if ((p & (S_IROTH | S_IXOTH)) == (S_IROTH | S_IXOTH)) {
4449
} else if ((p & S_IROTH) == S_IROTH) {
4456
gtk_combo_box_set_active_id (GTK_COMBO_BOX (combo), id);
4460
on_change_permissions_clicked (GtkWidget *button,
4461
NautilusPropertiesWindow *window)
4468
dialog = gtk_dialog_new_with_buttons (_("Change Permissions for Enclosed Files"),
4469
GTK_WINDOW (window),
4471
_("_Cancel"), GTK_RESPONSE_CANCEL,
4472
_("Change"), GTK_RESPONSE_OK,
4475
grid = GTK_GRID (create_grid_with_standard_properties ());
4476
gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
4480
label = gtk_label_new (_("Files"));
4481
gtk_misc_set_alignment (GTK_MISC (label), 0.5, 0.5);
4482
gtk_grid_attach (grid, label, 1, 0, 1, 1);
4483
label = gtk_label_new (_("Folders"));
4484
gtk_misc_set_alignment (GTK_MISC (label), 0.5, 0.5);
4485
gtk_grid_attach (grid, label, 2, 0, 1, 1);
4487
label = gtk_label_new (_("Owner:"));
4488
gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
4489
gtk_grid_attach (grid, label, 0, 1, 1, 1);
4490
combo = create_permissions_combo_box (PERMISSION_USER, FALSE);
4491
window->details->change_permission_combos = g_list_prepend (window->details->change_permission_combos,
4493
set_active_from_umask (combo, PERMISSION_USER, FALSE);
4494
gtk_grid_attach (grid, combo, 1, 1, 1, 1);
4495
combo = create_permissions_combo_box (PERMISSION_USER, TRUE);
4496
window->details->change_permission_combos = g_list_prepend (window->details->change_permission_combos,
4498
set_active_from_umask (combo, PERMISSION_USER, TRUE);
4499
gtk_grid_attach (grid, combo, 2, 1, 1, 1);
4501
label = gtk_label_new (_("Group:"));
4502
gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
4503
gtk_grid_attach (grid, label, 0, 2, 1, 1);
4504
combo = create_permissions_combo_box (PERMISSION_GROUP, FALSE);
4505
window->details->change_permission_combos = g_list_prepend (window->details->change_permission_combos,
4507
set_active_from_umask (combo, PERMISSION_GROUP, FALSE);
4508
gtk_grid_attach (grid, combo, 1, 2, 1, 1);
4509
combo = create_permissions_combo_box (PERMISSION_GROUP, TRUE);
4510
window->details->change_permission_combos = g_list_prepend (window->details->change_permission_combos,
4512
set_active_from_umask (combo, PERMISSION_GROUP, TRUE);
4513
gtk_grid_attach (grid, combo, 2, 2, 1, 1);
4515
label = gtk_label_new (_("Others:"));
4516
gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
4517
gtk_grid_attach (grid, label, 0, 3, 1, 1);
4518
combo = create_permissions_combo_box (PERMISSION_OTHER, FALSE);
4519
window->details->change_permission_combos = g_list_prepend (window->details->change_permission_combos,
4521
set_active_from_umask (combo, PERMISSION_OTHER, FALSE);
4522
gtk_grid_attach (grid, combo, 1, 3, 1, 1);
4523
combo = create_permissions_combo_box (PERMISSION_OTHER, TRUE);
4524
window->details->change_permission_combos = g_list_prepend (window->details->change_permission_combos,
4526
set_active_from_umask (combo, PERMISSION_OTHER, TRUE);
4527
gtk_grid_attach (grid, combo, 2, 3, 1, 1);
4529
g_signal_connect (dialog, "response", G_CALLBACK (on_change_permissions_response), window);
4530
gtk_widget_show_all (dialog);
4534
create_permissions_page (NautilusPropertiesWindow *window)
4536
GtkWidget *vbox, *button, *hbox;
4538
char *file_name, *prompt_text;
4541
if (!g_strcmp0(g_getenv("XDG_CURRENT_DESKTOP"), "Unity"))
4542
vbox = create_page_with_vbox (window->details->notebook,
4544
"help:ubuntu-help/nautilus-file-properties-permissions");
4546
vbox = create_page_with_vbox (window->details->notebook,
4548
"help:gnome-help/nautilus-file-properties-permissions");
4551
file_list = window->details->original_files;
4553
window->details->initial_permissions = NULL;
4555
if (all_can_get_permissions (file_list) && all_can_get_permissions (window->details->target_files)) {
4556
window->details->initial_permissions = get_initial_permissions (window->details->target_files);
4557
window->details->has_recursive_apply = files_has_changable_permissions_directory (window);
4559
if (!all_can_set_permissions (file_list)) {
4560
add_prompt_and_separator (
4562
_("You are not the owner, so you cannot change these permissions."));
4565
page_grid = GTK_GRID (create_grid_with_standard_properties ());
4567
gtk_widget_show (GTK_WIDGET (page_grid));
4568
gtk_box_pack_start (GTK_BOX (vbox),
4569
GTK_WIDGET (page_grid),
4572
create_simple_permissions (window, page_grid);
4575
append_blank_slim_row (page_grid);
4576
append_title_value_pair
4577
(window, page_grid, _("Security context:"),
4578
"selinux_context", INCONSISTENT_STATE_STRING,
4582
append_blank_row (page_grid);
4584
if (window->details->has_recursive_apply) {
4585
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
4586
gtk_widget_show (hbox);
4588
gtk_container_add_with_properties (GTK_CONTAINER (page_grid), hbox,
4592
button = gtk_button_new_with_mnemonic (_("Change Permissions for Enclosed Files…"));
4593
gtk_widget_show (button);
4594
gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
4595
g_signal_connect (button, "clicked",
4596
G_CALLBACK (on_change_permissions_clicked),
4600
if (!is_multi_file_window (window)) {
4601
file_name = nautilus_file_get_display_name (get_target_file (window));
4602
prompt_text = g_strdup_printf (_("The permissions of “%s” could not be determined."), file_name);
4605
prompt_text = g_strdup (_("The permissions of the selected file could not be determined."));
4608
add_prompt (vbox, prompt_text, TRUE);
4609
g_free (prompt_text);
4614
append_extension_pages (NautilusPropertiesWindow *window)
4619
providers = nautilus_module_get_extensions_for_type (NAUTILUS_TYPE_PROPERTY_PAGE_PROVIDER);
4621
for (p = providers; p != NULL; p = p->next) {
4622
NautilusPropertyPageProvider *provider;
4626
provider = NAUTILUS_PROPERTY_PAGE_PROVIDER (p->data);
4628
pages = nautilus_property_page_provider_get_pages
4629
(provider, window->details->original_files);
4631
for (l = pages; l != NULL; l = l->next) {
4632
NautilusPropertyPage *page;
4633
GtkWidget *page_widget;
4636
page = NAUTILUS_PROPERTY_PAGE (l->data);
4638
g_object_get (G_OBJECT (page),
4639
"page", &page_widget, "label", &label,
4642
gtk_notebook_append_page (window->details->notebook,
4643
page_widget, label);
4645
g_object_set_data (G_OBJECT (page_widget),
4646
"is-extension-page",
4649
g_object_unref (page_widget);
4650
g_object_unref (label);
4652
g_object_unref (page);
4655
g_list_free (pages);
4658
nautilus_module_extension_list_free (providers);
4662
should_show_permissions (NautilusPropertiesWindow *window)
4666
file = get_target_file (window);
4668
/* Don't show permissions for Trash and Computer since they're not
4669
* really file system objects.
4671
if (!is_multi_file_window (window)
4672
&& (is_merged_trash_directory (file) ||
4673
is_recent_directory (file) ||
4674
is_computer_directory (file))) {
4682
get_pending_key (GList *file_list)
4690
for (l = file_list; l != NULL; l = l->next) {
4691
uris = g_list_prepend (uris, nautilus_file_get_uri (NAUTILUS_FILE (l->data)));
4693
uris = g_list_sort (uris, (GCompareFunc)strcmp);
4695
key = g_string_new ("");
4696
for (l = uris; l != NULL; l = l->next) {
4697
g_string_append (key, l->data);
4698
g_string_append (key, ";");
4701
g_list_free_full (uris, g_free);
4704
g_string_free (key, FALSE);
4709
static StartupData *
4710
startup_data_new (GList *original_files,
4711
GList *target_files,
4712
const char *pending_key,
4713
GtkWidget *parent_widget,
4714
const char *startup_id)
4719
data = g_new0 (StartupData, 1);
4720
data->original_files = nautilus_file_list_copy (original_files);
4721
data->target_files = nautilus_file_list_copy (target_files);
4722
data->parent_widget = parent_widget;
4723
data->startup_id = g_strdup (startup_id);
4724
data->pending_key = g_strdup (pending_key);
4725
data->pending_files = g_hash_table_new (g_direct_hash,
4728
for (l = data->target_files; l != NULL; l = l->next) {
4729
g_hash_table_insert (data->pending_files, l->data, l->data);
4736
startup_data_free (StartupData *data)
4738
nautilus_file_list_free (data->original_files);
4739
nautilus_file_list_free (data->target_files);
4740
g_hash_table_destroy (data->pending_files);
4741
g_free (data->pending_key);
4742
g_free (data->startup_id);
4747
file_changed_callback (NautilusFile *file, gpointer user_data)
4749
NautilusPropertiesWindow *window = NAUTILUS_PROPERTIES_WINDOW (user_data);
4751
if (!g_list_find (window->details->changed_files, file)) {
4752
nautilus_file_ref (file);
4753
window->details->changed_files = g_list_prepend (window->details->changed_files, file);
4754
schedule_files_update (window);
4759
is_a_special_file (NautilusFile *file)
4762
NAUTILUS_IS_DESKTOP_ICON_FILE (file) ||
4763
nautilus_file_is_nautilus_link (file) ||
4764
is_merged_trash_directory (file) ||
4765
is_computer_directory (file)) {
4772
should_show_open_with (NautilusPropertiesWindow *window)
4779
/* Don't show open with tab for desktop special icons (trash, etc)
4780
* or desktop files. We don't get the open-with menu for these anyway.
4782
* Also don't show it for folders. Changing the default app for folders
4783
* leads to all sort of hard to understand errors.
4786
if (is_multi_file_window (window)) {
4789
if (!file_list_attributes_identical (window->details->target_files,
4794
for (l = window->details->target_files; l; l = l->next) {
4795
file = NAUTILUS_FILE (l->data);
4796
if (nautilus_file_is_directory (file) || is_a_special_file (file)) {
4801
/* since we just confirmed all the mime types are the
4802
same we only need to test one file */
4803
file = window->details->target_files->data;
4805
file = get_target_file (window);
4807
if (nautilus_file_is_directory (file) || is_a_special_file (file)) {
4812
mime_type = nautilus_file_get_mime_type (file);
4813
extension = nautilus_file_get_extension (file);
4814
hide = (g_content_type_is_unknown (mime_type) && extension == NULL);
4822
create_open_with_page (NautilusPropertiesWindow *window)
4826
GList *files = NULL;
4827
NautilusFile *target_file;
4829
target_file = get_target_file (window);
4830
mime_type = nautilus_file_get_mime_type (target_file);
4832
if (!is_multi_file_window (window)) {
4833
files = g_list_prepend (NULL, target_file);
4835
files = g_list_copy (window->details->original_files);
4836
if (files == NULL) {
4841
vbox = nautilus_mime_application_chooser_new (files, mime_type);
4843
gtk_widget_show (vbox);
4845
g_list_free (files);
4847
if (!g_strcmp0(g_getenv("XDG_CURRENT_DESKTOP"), "Unity"))
4848
g_object_set_data_full (G_OBJECT (vbox), "help-uri", g_strdup ("help:ubuntu-help/files-open"), g_free);
4850
g_object_set_data_full (G_OBJECT (vbox), "help-uri", g_strdup ("help:gnome-help/files-open"), g_free);
4852
gtk_notebook_append_page (window->details->notebook,
4853
vbox, gtk_label_new (_("Open With")));
4857
static NautilusPropertiesWindow *
4858
create_properties_window (StartupData *startup_data)
4860
NautilusPropertiesWindow *window;
4863
window = NAUTILUS_PROPERTIES_WINDOW (gtk_widget_new (NAUTILUS_TYPE_PROPERTIES_WINDOW, NULL));
4865
window->details->original_files = nautilus_file_list_copy (startup_data->original_files);
4867
window->details->target_files = nautilus_file_list_copy (startup_data->target_files);
4869
gtk_window_set_wmclass (GTK_WINDOW (window), "file_properties", "Nautilus");
4871
if (startup_data->parent_widget) {
4872
gtk_window_set_screen (GTK_WINDOW (window),
4873
gtk_widget_get_screen (startup_data->parent_widget));
4876
if (startup_data->startup_id) {
4877
gtk_window_set_startup_id (GTK_WINDOW (window), startup_data->startup_id);
4880
gtk_window_set_type_hint (GTK_WINDOW (window), GDK_WINDOW_TYPE_HINT_DIALOG);
4882
/* Set initial window title */
4883
update_properties_window_title (window);
4885
/* Start monitoring the file attributes we display. Note that some
4886
* of the attributes are for the original file, and some for the
4890
for (l = window->details->original_files; l != NULL; l = l->next) {
4892
NautilusFileAttributes attributes;
4894
file = NAUTILUS_FILE (l->data);
4897
NAUTILUS_FILE_ATTRIBUTES_FOR_ICON |
4898
NAUTILUS_FILE_ATTRIBUTE_INFO |
4899
NAUTILUS_FILE_ATTRIBUTE_LINK_INFO;
4901
nautilus_file_monitor_add (file,
4902
&window->details->original_files,
4906
for (l = window->details->target_files; l != NULL; l = l->next) {
4908
NautilusFileAttributes attributes;
4910
file = NAUTILUS_FILE (l->data);
4913
if (nautilus_file_is_directory (file)) {
4914
attributes |= NAUTILUS_FILE_ATTRIBUTE_DEEP_COUNTS;
4917
attributes |= NAUTILUS_FILE_ATTRIBUTE_INFO;
4918
nautilus_file_monitor_add (file, &window->details->target_files, attributes);
4921
for (l = window->details->target_files; l != NULL; l = l->next) {
4922
g_signal_connect_object (NAUTILUS_FILE (l->data),
4924
G_CALLBACK (file_changed_callback),
4929
for (l = window->details->original_files; l != NULL; l = l->next) {
4930
g_signal_connect_object (NAUTILUS_FILE (l->data),
4932
G_CALLBACK (file_changed_callback),
4937
/* Create the notebook tabs. */
4938
window->details->notebook = GTK_NOTEBOOK (gtk_notebook_new ());
4939
gtk_widget_show (GTK_WIDGET (window->details->notebook));
4940
gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (window))),
4941
GTK_WIDGET (window->details->notebook),
4944
/* Create the pages. */
4945
create_basic_page (window);
4947
if (should_show_permissions (window)) {
4948
create_permissions_page (window);
4951
if (should_show_open_with (window)) {
4952
create_open_with_page (window);
4955
/* append pages from available views */
4956
append_extension_pages (window);
4958
gtk_dialog_add_buttons (GTK_DIALOG (window),
4959
_("_Help"), GTK_RESPONSE_HELP,
4960
_("_Close"), GTK_RESPONSE_CLOSE,
4963
/* FIXME - HIGificiation, should be done inside GTK+ */
4964
gtk_container_set_border_width (GTK_CONTAINER (window), 5);
4965
gtk_container_set_border_width (GTK_CONTAINER (window->details->notebook), 5);
4966
gtk_container_set_border_width (GTK_CONTAINER (gtk_dialog_get_action_area (GTK_DIALOG (window))), 0);
4968
/* Update from initial state */
4969
properties_window_update (window, NULL);
4975
get_target_file_list (GList *original_files)
4982
for (l = original_files; l != NULL; l = l->next) {
4983
NautilusFile *target;
4985
target = get_target_file_for_original_file (NAUTILUS_FILE (l->data));
4987
ret = g_list_prepend (ret, target);
4990
ret = g_list_reverse (ret);
4996
add_window (NautilusPropertiesWindow *window)
4998
if (!is_multi_file_window (window)) {
4999
g_hash_table_insert (windows,
5000
get_original_file (window),
5002
g_object_set_data (G_OBJECT (window), "window_key",
5003
get_original_file (window));
5008
remove_window (NautilusPropertiesWindow *window)
5012
key = g_object_get_data (G_OBJECT (window), "window_key");
5014
g_hash_table_remove (windows, key);
5019
get_existing_window (GList *file_list)
5021
if (!file_list->next) {
5022
return g_hash_table_lookup (windows, file_list->data);
5029
cancel_create_properties_window_callback (gpointer callback_data)
5031
remove_pending ((StartupData *)callback_data, TRUE, FALSE, TRUE);
5035
parent_widget_destroyed_callback (GtkWidget *widget, gpointer callback_data)
5037
g_assert (widget == ((StartupData *)callback_data)->parent_widget);
5039
remove_pending ((StartupData *)callback_data, TRUE, TRUE, FALSE);
5043
cancel_call_when_ready_callback (gpointer key,
5047
nautilus_file_cancel_call_when_ready
5048
(NAUTILUS_FILE (key),
5049
is_directory_ready_callback,
5054
remove_pending (StartupData *startup_data,
5055
gboolean cancel_call_when_ready,
5056
gboolean cancel_timed_wait,
5057
gboolean cancel_destroy_handler)
5059
if (cancel_call_when_ready) {
5060
g_hash_table_foreach (startup_data->pending_files,
5061
cancel_call_when_ready_callback,
5065
if (cancel_timed_wait) {
5067
(cancel_create_properties_window_callback, startup_data);
5069
if (cancel_destroy_handler && startup_data->parent_widget) {
5070
g_signal_handlers_disconnect_by_func (startup_data->parent_widget,
5071
G_CALLBACK (parent_widget_destroyed_callback),
5075
g_hash_table_remove (pending_lists, startup_data->pending_key);
5077
startup_data_free (startup_data);
5081
is_directory_ready_callback (NautilusFile *file,
5084
StartupData *startup_data;
5086
startup_data = data;
5088
g_hash_table_remove (startup_data->pending_files, file);
5090
if (g_hash_table_size (startup_data->pending_files) == 0) {
5091
NautilusPropertiesWindow *new_window;
5093
new_window = create_properties_window (startup_data);
5095
add_window (new_window);
5097
remove_pending (startup_data, FALSE, TRUE, TRUE);
5099
gtk_window_present (GTK_WINDOW (new_window));
5105
nautilus_properties_window_present (GList *original_files,
5106
GtkWidget *parent_widget,
5107
const gchar *startup_id)
5110
GtkWidget *parent_window;
5111
StartupData *startup_data;
5112
GList *target_files;
5113
GtkWindow *existing_window;
5116
g_return_if_fail (original_files != NULL);
5117
g_return_if_fail (parent_widget == NULL || GTK_IS_WIDGET (parent_widget));
5119
/* Create the hash tables first time through. */
5120
if (windows == NULL) {
5121
windows = g_hash_table_new (NULL, NULL);
5124
if (pending_lists == NULL) {
5125
pending_lists = g_hash_table_new (g_str_hash, g_str_equal);
5128
/* Look to see if there's already a window for this file. */
5129
existing_window = get_existing_window (original_files);
5130
if (existing_window != NULL) {
5132
gtk_window_set_screen (existing_window,
5133
gtk_widget_get_screen (parent_widget));
5134
else if (startup_id)
5135
gtk_window_set_startup_id (existing_window, startup_id);
5137
gtk_window_present (existing_window);
5142
pending_key = get_pending_key (original_files);
5144
/* Look to see if we're already waiting for a window for this file. */
5145
if (g_hash_table_lookup (pending_lists, pending_key) != NULL) {
5149
target_files = get_target_file_list (original_files);
5151
startup_data = startup_data_new (original_files,
5157
nautilus_file_list_free (target_files);
5158
g_free(pending_key);
5160
/* Wait until we can tell whether it's a directory before showing, since
5161
* some one-time layout decisions depend on that info.
5164
g_hash_table_insert (pending_lists, startup_data->pending_key, startup_data->pending_key);
5165
if (parent_widget) {
5166
g_signal_connect (parent_widget, "destroy",
5167
G_CALLBACK (parent_widget_destroyed_callback), startup_data);
5169
parent_window = gtk_widget_get_ancestor (parent_widget, GTK_TYPE_WINDOW);
5171
parent_window = NULL;
5173
eel_timed_wait_start
5174
(cancel_create_properties_window_callback,
5176
_("Creating Properties window."),
5177
parent_window == NULL ? NULL : GTK_WINDOW (parent_window));
5179
for (l = startup_data->target_files; l != NULL; l = next) {
5181
nautilus_file_call_when_ready
5182
(NAUTILUS_FILE (l->data),
5183
NAUTILUS_FILE_ATTRIBUTE_INFO,
5184
is_directory_ready_callback,
5190
real_response (GtkDialog *dialog,
5193
GError *error = NULL;
5194
NautilusPropertiesWindow *window = NAUTILUS_PROPERTIES_WINDOW (dialog);
5196
const char *helpuri;
5199
case GTK_RESPONSE_HELP:
5200
curpage = gtk_notebook_get_nth_page (window->details->notebook,
5201
gtk_notebook_get_current_page (window->details->notebook));
5202
helpuri = g_object_get_data (G_OBJECT (curpage), "help-uri");
5204
if (!g_strcmp0(g_getenv("XDG_CURRENT_DESKTOP"), "Unity"))
5205
gtk_show_uri (gtk_window_get_screen (GTK_WINDOW (dialog)),
5206
helpuri ? helpuri : "help:ubuntu-help/files",
5207
gtk_get_current_event_time (),
5210
gtk_show_uri (gtk_window_get_screen (GTK_WINDOW (dialog)),
5211
helpuri ? helpuri : "help:gnome-help/files",
5212
gtk_get_current_event_time (),
5215
if (error != NULL) {
5216
eel_show_error_dialog (_("There was an error displaying help."), error->message,
5217
GTK_WINDOW (dialog));
5218
g_error_free (error);
5222
case GTK_RESPONSE_NONE:
5223
case GTK_RESPONSE_CLOSE:
5224
case GTK_RESPONSE_DELETE_EVENT:
5225
gtk_widget_destroy (GTK_WIDGET (dialog));
5229
g_assert_not_reached ();
5235
real_destroy (GtkWidget *object)
5237
NautilusPropertiesWindow *window;
5240
window = NAUTILUS_PROPERTIES_WINDOW (object);
5242
remove_window (window);
5244
unschedule_or_cancel_group_change (window);
5245
unschedule_or_cancel_owner_change (window);
5247
for (l = window->details->original_files; l != NULL; l = l->next) {
5248
nautilus_file_monitor_remove (NAUTILUS_FILE (l->data), &window->details->original_files);
5250
nautilus_file_list_free (window->details->original_files);
5251
window->details->original_files = NULL;
5253
for (l = window->details->target_files; l != NULL; l = l->next) {
5254
nautilus_file_monitor_remove (NAUTILUS_FILE (l->data), &window->details->target_files);
5256
nautilus_file_list_free (window->details->target_files);
5257
window->details->target_files = NULL;
5259
nautilus_file_list_free (window->details->changed_files);
5260
window->details->changed_files = NULL;
5262
if (window->details->deep_count_spinner_timeout_id > 0) {
5263
g_source_remove (window->details->deep_count_spinner_timeout_id);
5266
while (window->details->deep_count_files) {
5267
stop_deep_count_for_file (window, window->details->deep_count_files->data);
5270
window->details->name_field = NULL;
5272
g_list_free (window->details->permission_buttons);
5273
window->details->permission_buttons = NULL;
5275
g_list_free (window->details->permission_combos);
5276
window->details->permission_combos = NULL;
5278
g_list_free (window->details->change_permission_combos);
5279
window->details->change_permission_combos = NULL;
5281
if (window->details->initial_permissions) {
5282
g_hash_table_destroy (window->details->initial_permissions);
5283
window->details->initial_permissions = NULL;
5286
g_list_free (window->details->value_fields);
5287
window->details->value_fields = NULL;
5289
if (window->details->update_directory_contents_timeout_id != 0) {
5290
g_source_remove (window->details->update_directory_contents_timeout_id);
5291
window->details->update_directory_contents_timeout_id = 0;
5294
if (window->details->update_files_timeout_id != 0) {
5295
g_source_remove (window->details->update_files_timeout_id);
5296
window->details->update_files_timeout_id = 0;
5299
GTK_WIDGET_CLASS (nautilus_properties_window_parent_class)->destroy (object);
5303
real_finalize (GObject *object)
5305
NautilusPropertiesWindow *window;
5307
window = NAUTILUS_PROPERTIES_WINDOW (object);
5309
g_list_free_full (window->details->mime_list, g_free);
5311
g_free (window->details->pending_name);
5313
G_OBJECT_CLASS (nautilus_properties_window_parent_class)->finalize (object);
5317
* file://foo/foobar/foofoo/bar
5324
* It does not resolve any symlinks.
5327
make_relative_uri_from_full (const char *uri,
5328
const char *base_uri)
5330
g_assert (uri != NULL);
5331
g_assert (base_uri != NULL);
5333
if (g_str_has_prefix (uri, base_uri)) {
5334
uri += strlen (base_uri);
5339
while (*uri == '/') {
5344
return g_strdup (uri);
5351
/* icon selection callback to set the image of the file object to the selected file */
5353
set_icon (const char* icon_uri, NautilusPropertiesWindow *properties_window)
5358
char *real_icon_uri;
5360
g_assert (icon_uri != NULL);
5361
g_assert (NAUTILUS_IS_PROPERTIES_WINDOW (properties_window));
5363
icon_path = g_filename_from_uri (icon_uri, NULL, NULL);
5364
/* we don't allow remote URIs */
5365
if (icon_path != NULL) {
5368
for (l = properties_window->details->original_files; l != NULL; l = l->next) {
5369
file = NAUTILUS_FILE (l->data);
5371
file_uri = nautilus_file_get_uri (file);
5373
if (nautilus_file_is_mime_type (file, "application/x-desktop")) {
5374
if (nautilus_link_local_set_icon (file_uri, icon_path)) {
5375
nautilus_file_invalidate_attributes (file,
5376
NAUTILUS_FILE_ATTRIBUTE_INFO |
5377
NAUTILUS_FILE_ATTRIBUTE_LINK_INFO);
5380
real_icon_uri = make_relative_uri_from_full (icon_uri, file_uri);
5381
if (real_icon_uri == NULL) {
5382
real_icon_uri = g_strdup (icon_uri);
5385
nautilus_file_set_metadata (file, NAUTILUS_METADATA_KEY_CUSTOM_ICON, NULL, real_icon_uri);
5386
nautilus_file_set_metadata (file, NAUTILUS_METADATA_KEY_ICON_SCALE, NULL, NULL);
5388
g_free (real_icon_uri);
5399
update_preview_callback (GtkFileChooser *icon_chooser,
5400
NautilusPropertiesWindow *window)
5402
GtkWidget *preview_widget;
5403
GdkPixbuf *pixbuf, *scaled_pixbuf;
5409
filename = gtk_file_chooser_get_filename (icon_chooser);
5410
if (filename != NULL) {
5411
pixbuf = gdk_pixbuf_new_from_file (filename, NULL);
5414
if (pixbuf != NULL) {
5415
preview_widget = gtk_file_chooser_get_preview_widget (icon_chooser);
5416
gtk_file_chooser_set_preview_widget_active (icon_chooser, TRUE);
5418
if (gdk_pixbuf_get_width (pixbuf) > PREVIEW_IMAGE_WIDTH) {
5419
scale = (double)gdk_pixbuf_get_height (pixbuf) /
5420
gdk_pixbuf_get_width (pixbuf);
5422
scaled_pixbuf = gnome_desktop_thumbnail_scale_down_pixbuf
5424
PREVIEW_IMAGE_WIDTH,
5425
scale * PREVIEW_IMAGE_WIDTH);
5426
g_object_unref (pixbuf);
5427
pixbuf = scaled_pixbuf;
5430
gtk_image_set_from_pixbuf (GTK_IMAGE (preview_widget), pixbuf);
5432
gtk_file_chooser_set_preview_widget_active (icon_chooser, FALSE);
5437
if (pixbuf != NULL) {
5438
g_object_unref (pixbuf);
5443
custom_icon_file_chooser_response_cb (GtkDialog *dialog,
5445
NautilusPropertiesWindow *window)
5450
case GTK_RESPONSE_NO:
5451
reset_icon (window);
5454
case GTK_RESPONSE_OK:
5455
uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
5457
set_icon (uri, window);
5459
reset_icon (window);
5468
gtk_widget_hide (GTK_WIDGET (dialog));
5472
select_image_button_callback (GtkWidget *widget,
5473
NautilusPropertiesWindow *window)
5475
GtkWidget *dialog, *preview;
5476
GtkFileFilter *filter;
5481
gboolean revert_is_sensitive;
5483
g_assert (NAUTILUS_IS_PROPERTIES_WINDOW (window));
5485
dialog = window->details->icon_chooser;
5487
if (dialog == NULL) {
5488
dialog = gtk_file_chooser_dialog_new (_("Select Custom Icon"), GTK_WINDOW (window),
5489
GTK_FILE_CHOOSER_ACTION_OPEN,
5490
_("_Revert"), GTK_RESPONSE_NO,
5491
_("_Cancel"), GTK_RESPONSE_CANCEL,
5492
_("_Open"), GTK_RESPONSE_OK,
5494
gtk_file_chooser_add_shortcut_folder (GTK_FILE_CHOOSER (dialog),
5495
g_get_user_special_dir (G_USER_DIRECTORY_PICTURES),
5497
gtk_window_set_destroy_with_parent (GTK_WINDOW (dialog), TRUE);
5499
filter = gtk_file_filter_new ();
5500
gtk_file_filter_add_pixbuf_formats (filter);
5501
gtk_file_chooser_set_filter (GTK_FILE_CHOOSER (dialog), filter);
5503
preview = gtk_image_new ();
5504
gtk_widget_set_size_request (preview, PREVIEW_IMAGE_WIDTH, -1);
5505
gtk_file_chooser_set_preview_widget (GTK_FILE_CHOOSER (dialog), preview);
5506
gtk_file_chooser_set_use_preview_label (GTK_FILE_CHOOSER (dialog), FALSE);
5507
gtk_file_chooser_set_preview_widget_active (GTK_FILE_CHOOSER (dialog), FALSE);
5509
g_signal_connect (dialog, "update-preview",
5510
G_CALLBACK (update_preview_callback), window);
5512
window->details->icon_chooser = dialog;
5514
g_object_add_weak_pointer (G_OBJECT (dialog),
5515
(gpointer *) &window->details->icon_chooser);
5518
/* it's likely that the user wants to pick an icon that is inside a local directory */
5519
if (g_list_length (window->details->original_files) == 1) {
5520
file = NAUTILUS_FILE (window->details->original_files->data);
5522
if (nautilus_file_is_directory (file)) {
5523
uri = nautilus_file_get_uri (file);
5525
image_path = g_filename_from_uri (uri, NULL, NULL);
5526
if (image_path != NULL) {
5527
gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog), image_path);
5528
g_free (image_path);
5535
revert_is_sensitive = FALSE;
5536
for (l = window->details->original_files; l != NULL; l = l->next) {
5537
file = NAUTILUS_FILE (l->data);
5538
image_path = nautilus_file_get_metadata (file, NAUTILUS_METADATA_KEY_CUSTOM_ICON, NULL);
5539
revert_is_sensitive = (image_path != NULL);
5540
g_free (image_path);
5542
if (revert_is_sensitive) {
5546
gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog), GTK_RESPONSE_NO, revert_is_sensitive);
5548
g_signal_connect (dialog, "response",
5549
G_CALLBACK (custom_icon_file_chooser_response_cb), window);
5550
gtk_widget_show (dialog);
5554
nautilus_properties_window_class_init (NautilusPropertiesWindowClass *class)
5556
GtkBindingSet *binding_set;
5558
G_OBJECT_CLASS (class)->finalize = real_finalize;
5559
GTK_WIDGET_CLASS (class)->destroy = real_destroy;
5560
GTK_DIALOG_CLASS (class)->response = real_response;
5562
binding_set = gtk_binding_set_by_class (class);
5563
gtk_binding_entry_add_signal (binding_set, GDK_KEY_Escape, 0,
5566
g_type_class_add_private (class, sizeof (NautilusPropertiesWindowDetails));
5570
nautilus_properties_window_init (NautilusPropertiesWindow *window)
5572
window->details = G_TYPE_INSTANCE_GET_PRIVATE (window, NAUTILUS_TYPE_PROPERTIES_WINDOW,
5573
NautilusPropertiesWindowDetails);