~ubuntu-branches/ubuntu/vivid/nautilus/vivid

« back to all changes in this revision

Viewing changes to .pc/git_properties_window_set_max_width_for_value_labels.patch/src/nautilus-properties-window.c

  • Committer: Package Import Robot
  • Author(s): Sebastien Bacher, Lars Uebernickel
  • Date: 2014-10-20 09:05:18 UTC
  • Revision ID: package-import@ubuntu.com-20141020090518-ykbdbqn3cb98gyh5
Tags: 1:3.10.1-0ubuntu15
* debian/patches/git_name_column.patch:
  - "nautilus-list-view: Avoid unreadable names" (lp: #1243806)

[ Lars Uebernickel ]
* debian/patches/properties_window_set_max_width_for_value_labels.patch:
  - avoid very wide properties window (GNOME: #732117)

Show diffs side-by-side

added added

removed removed

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