nautilus-view.c 215 KB
Newer Older
1
/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
Darin Adler's avatar
Darin Adler committed
2

3
/* nautilus-view.c
Ettore Perazzoli's avatar
Ettore Perazzoli committed
4
 *
5
 * Copyright (C) 1999, 2000  Free Software Foundation
6
 * Copyright (C) 2000, 2001  Eazel, Inc.
Ettore Perazzoli's avatar
Ettore Perazzoli committed
7
8
9
10
11
12
13
14
15
16
17
18
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public
19
 * License along with this program; if not, see <http://www.gnu.org/licenses/>.
Ettore Perazzoli's avatar
Ettore Perazzoli committed
20
 *
21
22
 * Authors: Ettore Perazzoli,
 *          John Sullivan <sullivan@eazel.com>,
23
 *          Darin Adler <darin@bentspoon.com>,
24
25
 *          Pavel Cisler <pavel@eazel.com>,
 *          David Emory Watson <dwatson@cs.ucr.edu>
Ettore Perazzoli's avatar
Ettore Perazzoli committed
26
27
 */

28
#include <config.h>
29

30
31
#include "nautilus-view.h"

32
#include "nautilus-application.h"
33
#include "nautilus-desktop-canvas-view.h"
34
#include "nautilus-error-reporting.h"
35
#include "nautilus-list-view.h"
36
#include "nautilus-mime-actions.h"
37
#include "nautilus-previewer.h"
38
#include "nautilus-properties-window.h"
39
40
#include "nautilus-window.h"
#include "nautilus-toolbar.h"
41

42
43
44
45
#if ENABLE_EMPTY_VIEW
#include "nautilus-empty-view.h"
#endif

46
#include <gdk/gdkx.h>
47
48
49
50
51
52
#include <gdk/gdkkeysyms.h>
#include <gtk/gtk.h>
#include <glib/gi18n.h>
#include <glib/gstdio.h>
#include <gio/gio.h>
#include <math.h>
53
#include <string.h>
54
55
56
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
Alexander Larsson's avatar
Alexander Larsson committed
57

Ramiro Estrugo's avatar
Ramiro Estrugo committed
58
59
60
61
62
63
#include <eel/eel-glib-extensions.h>
#include <eel/eel-gnome-extensions.h>
#include <eel/eel-gtk-extensions.h>
#include <eel/eel-stock-dialogs.h>
#include <eel/eel-string.h>
#include <eel/eel-vfs-extensions.h>
64

65
#include <libnautilus-extension/nautilus-menu-provider.h>
66
#include <libnautilus-private/nautilus-clipboard.h>
67
#include <libnautilus-private/nautilus-clipboard-monitor.h>
Alexander Larsson's avatar
Alexander Larsson committed
68
69
#include <libnautilus-private/nautilus-desktop-icon-file.h>
#include <libnautilus-private/nautilus-desktop-directory.h>
70
#include <libnautilus-private/nautilus-search-directory.h>
71
#include <libnautilus-private/nautilus-directory.h>
72
#include <libnautilus-private/nautilus-dnd.h>
73
#include <libnautilus-private/nautilus-file-attributes.h>
74
#include <libnautilus-private/nautilus-file-changes-queue.h>
75
76
77
#include <libnautilus-private/nautilus-file-dnd.h>
#include <libnautilus-private/nautilus-file-operations.h>
#include <libnautilus-private/nautilus-file-utilities.h>
78
#include <libnautilus-private/nautilus-file-private.h>
79
80
81
#include <libnautilus-private/nautilus-global-preferences.h>
#include <libnautilus-private/nautilus-link.h>
#include <libnautilus-private/nautilus-metadata.h>
82
#include <libnautilus-private/nautilus-recent.h>
83
#include <libnautilus-private/nautilus-module.h>
84
#include <libnautilus-private/nautilus-profile.h>
85
86
#include <libnautilus-private/nautilus-program-choosing.h>
#include <libnautilus-private/nautilus-trash-monitor.h>
Alexander Larsson's avatar
Alexander Larsson committed
87
#include <libnautilus-private/nautilus-ui-utilities.h>
Alexander Larsson's avatar
Alexander Larsson committed
88
#include <libnautilus-private/nautilus-signaller.h>
Luca Ferretti's avatar
Luca Ferretti committed
89
#include <libnautilus-private/nautilus-icon-names.h>
90

91
92
93
#define GNOME_DESKTOP_USE_UNSTABLE_API
#include <gdesktop-enums.h>

94
95
96
#define DEBUG_FLAG NAUTILUS_DEBUG_DIRECTORY_VIEW
#include <libnautilus-private/nautilus-debug.h>

97
98
99
100
101
102
103
104
105
106
107
/* Minimum starting update inverval */
#define UPDATE_INTERVAL_MIN 100
/* Maximum update interval */
#define UPDATE_INTERVAL_MAX 2000
/* Amount of miliseconds the update interval is increased */
#define UPDATE_INTERVAL_INC 250
/* Interval at which the update interval is increased */
#define UPDATE_INTERVAL_TIMEOUT_INTERVAL 250
/* Milliseconds that have to pass without a change to reset the update interval */
#define UPDATE_INTERVAL_RESET 1000

108
#define SILENT_WINDOW_OPEN_LIMIT 5
109

110
111
112
#define DUPLICATE_HORIZONTAL_ICON_OFFSET 70
#define DUPLICATE_VERTICAL_ICON_OFFSET   30

113
114
#define MAX_QUEUED_UPDATES 500

115
#define MAX_MENU_LEVELS 5
116
#define TEMPLATE_LIMIT 30
117

118
/* Time to show the duplicated folder label */
Carlos Soriano Sánchez's avatar
Carlos Soriano Sánchez committed
119
#define DIALOG_DUPLICATED_NAME_ERROR_LABEL_TIMEOUT 500
120

Pavel Cisler's avatar
Pavel Cisler committed
121
enum {
122
	ADD_FILE,
123
	BEGIN_FILE_CHANGES,
124
	BEGIN_LOADING,
125
	CLEAR,
126
127
	END_FILE_CHANGES,
	END_LOADING,
128
	FILE_CHANGED,
Pavel Cisler's avatar
Pavel Cisler committed
129
	MOVE_COPY_ITEMS,
130
	REMOVE_FILE,
131
	SELECTION_CHANGED,
Alexander Larsson's avatar
Alexander Larsson committed
132
133
	TRASH,
	DELETE,
134
135
136
	LAST_SIGNAL
};

137
enum {
138
	PROP_WINDOW_SLOT = 1,
139
	PROP_SUPPORTS_ZOOMING,
140
	NUM_PROPERTIES
Alexander Larsson's avatar
Alexander Larsson committed
141
142
};

143
static guint signals[LAST_SIGNAL];
144
static GParamSpec *properties[NUM_PROPERTIES] = { NULL, };
Ettore Perazzoli's avatar
Ettore Perazzoli committed
145

146
147
static GdkAtom copied_files_atom;

148
static char *scripts_directory_uri = NULL;
149
150
static int scripts_directory_uri_length;

151
struct NautilusViewDetails
Ettore Perazzoli's avatar
Ettore Perazzoli committed
152
{
153
	NautilusWindowSlot *slot;
154
	NautilusDirectory *model;
Darin Adler's avatar
Darin Adler committed
155
	NautilusFile *directory_as_file;
156
157
	NautilusFile *pathbar_popup_directory_as_file;
	GdkEventButton *pathbar_popup_event;
Alexander Larsson's avatar
Alexander Larsson committed
158
	guint dir_merge_id;
159

Carlos Soriano Sánchez's avatar
Carlos Soriano Sánchez committed
160
	gint dialog_duplicated_name_label_timeout_id;
161

162
	gboolean supports_zooming;
163

164
	GList *scripts_directory_list;
165
	GList *templates_directory_list;
Alexander Larsson's avatar
Alexander Larsson committed
166
	
167
	guint display_selection_idle_id;
168
	guint update_context_menus_timeout_id;
169
	guint update_status_idle_id;
170
171
	guint reveal_selection_idle_id;

172
173
174
175
176
	guint display_pending_source_id;
	guint changes_timeout_id;

	guint update_interval;
 	guint64 last_queued;
177
	
178
179
	guint files_added_handler_id;
	guint files_changed_handler_id;
180
	guint load_error_handler_id;
181
	guint done_loading_handler_id;
182
	guint file_changed_handler_id;
183

184
185
186
187
188
189
190
191
	GList *new_added_files;
	GList *new_changed_files;

	GHashTable *non_ready_files;

	GList *old_added_files;
	GList *old_changed_files;

192
	GList *pending_selection;
193

194
195
196
	/* whether we are in the active slot */
	gboolean active;

197
	/* loading indicates whether this view has begun loading a directory.
198
	 * This flag should need not be set inside subclasses. NautilusView automatically
199
200
201
	 * sets 'loading' to TRUE before it begins loading a directory's contents and to FALSE
	 * after it finishes loading the directory and its view.
	 */
202
	gboolean loading;
203
	gboolean templates_present;
204
	gboolean scripts_present;
205

206
207
208
209
210
211
212
213
	/* flag to indicate that no file updates should be dispatched to subclasses.
	 * This is a workaround for bug #87701 that prevents the list view from
	 * losing focus when the underlying GtkTreeView is updated.
	 */
	gboolean updates_frozen;
	guint	 updates_queued;
	gboolean needs_reload;

214
215
	gboolean is_renaming;

216
217
	gboolean sort_directories_first;

218
	gboolean show_foreign_files;
219
	gboolean show_hidden_files;
220
	gboolean ignore_hidden_file_preferences;
221
222
223

	gboolean batching_selection_level;
	gboolean selection_changed_while_batched;
224

225
226
	gboolean selection_was_removed;

227
228
229
	gboolean metadata_for_directory_as_file_pending;
	gboolean metadata_for_files_in_directory_pending;

Alexander Larsson's avatar
Alexander Larsson committed
230
	GList *subdirectory_list;
231
232

	GdkPoint context_menu_position;
233
234
235
236
237
238

	GMenu *selection_menu;
	GMenu *background_menu;
	GMenu *pathbar_menu;

	GActionGroup *view_action_group;
239
};
Ettore Perazzoli's avatar
Ettore Perazzoli committed
240

241
242
243
244
245
typedef struct {
	NautilusFile *file;
	NautilusDirectory *directory;
} FileAndDirectory;

246
/* forward declarations */
247

248
static gboolean display_selection_info_idle_callback           (gpointer              data);
Alexander Larsson's avatar
Alexander Larsson committed
249
250
static void     trash_or_delete_files                          (GtkWindow            *parent_window,
								const GList          *files,
251
252
								NautilusView      *view);
static void     load_directory                                 (NautilusView      *view,
253
								NautilusDirectory    *directory);
254
static void     clipboard_changed_callback                     (NautilusClipboardMonitor *monitor,
255
								NautilusView      *view);
256
257
static void     open_one_in_new_window                         (gpointer              data,
								gpointer              callback_data);
258
259
static void     schedule_update_context_menus                  (NautilusView      *view);
static void     remove_update_context_menus_timeout_callback   (NautilusView      *view);
260
261
262
263
264
265
static void     schedule_update_status                          (NautilusView      *view);
static void     remove_update_status_idle_callback             (NautilusView *view); 
static void     reset_update_interval                          (NautilusView      *view);
static void     schedule_idle_display_of_pending_files         (NautilusView      *view);
static void     unschedule_display_of_pending_files            (NautilusView      *view);
static void     disconnect_model_handlers                      (NautilusView      *view);
266
267
268
269
270
static void     metadata_for_directory_as_file_ready_callback  (NautilusFile         *file,
								gpointer              callback_data);
static void     metadata_for_files_in_directory_ready_callback (NautilusDirectory    *directory,
								GList                *files,
								gpointer              callback_data);
271
272
273
274
275
static void     nautilus_view_trash_state_changed_callback     (NautilusTrashMonitor *trash,
							        gboolean              state,
							        gpointer              callback_data);
static void     nautilus_view_select_file                      (NautilusView      *view,
							        NautilusFile         *file);
Alexander Larsson's avatar
Alexander Larsson committed
276

277
static void     update_templates_directory                     (NautilusView *view);
278

279
static void unschedule_pop_up_pathbar_context_menu (NautilusView *view);
280

281
282
G_DEFINE_TYPE (NautilusView, nautilus_view, GTK_TYPE_SCROLLED_WINDOW);

283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
static char *
real_get_backing_uri (NautilusView *view)
{
	NautilusDirectory *directory;
	char *uri;
       
	g_return_val_if_fail (NAUTILUS_IS_VIEW (view), NULL);

	if (view->details->model == NULL) {
		return NULL;
	}
       
	directory = view->details->model;
       
	if (NAUTILUS_IS_DESKTOP_DIRECTORY (directory)) {
		directory = nautilus_desktop_directory_get_real_directory (NAUTILUS_DESKTOP_DIRECTORY (directory));
	} else {
		nautilus_directory_ref (directory);
	}
       
	uri = nautilus_directory_get_uri (directory);

	nautilus_directory_unref (directory);

	return uri;
}

/**
 *
 * nautilus_view_get_backing_uri:
 *
 * Returns the URI for the target location of new directory, new file, new
315
 * link and paste operations.
316
317
318
319
320
321
322
 */

char *
nautilus_view_get_backing_uri (NautilusView *view)
{
	g_return_val_if_fail (NAUTILUS_IS_VIEW (view), NULL);

323
	return NAUTILUS_VIEW_CLASS (G_OBJECT_GET_CLASS (view))->get_backing_uri (view);
324
325
}

326
/**
327
 * nautilus_view_select_all:
328
329
330
331
332
 *
 * select all the items in the view
 * 
 **/
static void
333
nautilus_view_select_all (NautilusView *view)
334
{
335
	g_return_if_fail (NAUTILUS_IS_VIEW (view));
Alexander Larsson's avatar
Alexander Larsson committed
336

337
	NAUTILUS_VIEW_CLASS (G_OBJECT_GET_CLASS (view))->select_all (view);
Alexander Larsson's avatar
Alexander Larsson committed
338
339
}

340
341
342
343
344
345
346
347
static void
nautilus_view_select_first (NautilusView *view)
{
	g_return_if_fail (NAUTILUS_IS_VIEW (view));

	NAUTILUS_VIEW_CLASS (G_OBJECT_GET_CLASS (view))->select_first (view);
}

Alexander Larsson's avatar
Alexander Larsson committed
348
static void
349
nautilus_view_call_set_selection (NautilusView *view, GList *selection)
Alexander Larsson's avatar
Alexander Larsson committed
350
{
351
	g_return_if_fail (NAUTILUS_IS_VIEW (view));
352

353
	NAUTILUS_VIEW_CLASS (G_OBJECT_GET_CLASS (view))->set_selection (view, selection);
354
}
355
356

static GList *
357
nautilus_view_get_selection_for_file_transfer (NautilusView *view)
358
{
359
	g_return_val_if_fail (NAUTILUS_IS_VIEW (view), NULL);
360

361
	return NAUTILUS_VIEW_CLASS (G_OBJECT_GET_CLASS (view))->get_selection_for_file_transfer (view);
362
363
364
}

static void
365
nautilus_view_invert_selection (NautilusView *view)
366
{
367
	g_return_if_fail (NAUTILUS_IS_VIEW (view));
368

369
	NAUTILUS_VIEW_CLASS (G_OBJECT_GET_CLASS (view))->invert_selection (view);
370
371
}

372
/**
373
 * nautilus_view_reveal_selection:
374
375
376
377
 *
 * Scroll as necessary to reveal the selected items.
 **/
static void
378
nautilus_view_reveal_selection (NautilusView *view)
379
{
380
	g_return_if_fail (NAUTILUS_IS_VIEW (view));
381

382
	NAUTILUS_VIEW_CLASS (G_OBJECT_GET_CLASS (view))->reveal_selection (view);
383
}
384
385

static gboolean
386
nautilus_view_using_manual_layout (NautilusView  *view)
387
{
388
	g_return_val_if_fail (NAUTILUS_IS_VIEW (view), FALSE);
389

390
	return 	NAUTILUS_VIEW_CLASS (G_OBJECT_GET_CLASS (view))->using_manual_layout (view);
391
392
}

393
/**
394
 * nautilus_view_can_rename_file
395
396
397
398
399
400
401
402
 *
 * Determine whether a file can be renamed.
 * @file: A NautilusFile
 * 
 * Return value: TRUE if @file can be renamed, FALSE otherwise.
 * 
 **/
static gboolean
403
nautilus_view_can_rename_file (NautilusView *view, NautilusFile *file)
404
{
405
	return 	NAUTILUS_VIEW_CLASS (G_OBJECT_GET_CLASS (view))->can_rename_file (view, file);
406
}
407

408
static gboolean
409
nautilus_view_is_read_only (NautilusView *view)
410
{
411
	g_return_val_if_fail (NAUTILUS_IS_VIEW (view), FALSE);
412

413
	return 	NAUTILUS_VIEW_CLASS (G_OBJECT_GET_CLASS (view))->is_read_only (view);
414
}
415

416
static gboolean
417
showing_trash_directory (NautilusView *view)
418
419
420
{
	NautilusFile *file;

421
	file = nautilus_view_get_directory_as_file (view);
422
423
424
425
426
427
	if (file != NULL) {
		return nautilus_file_is_in_trash (file);
	}
	return FALSE;
}

428
429
430
431
432
433
434
435
436
437
438
439
static gboolean
showing_network_directory (NautilusView *view)
{
	NautilusFile *file;

	file = nautilus_view_get_directory_as_file (view);
	if (file != NULL) {
		return nautilus_file_is_in_network (file);
	}
	return FALSE;
}

William Jon McCann's avatar
William Jon McCann committed
440
441
442
443
444
445
446
447
448
449
450
451
static gboolean
showing_recent_directory (NautilusView *view)
{
	NautilusFile *file;

	file = nautilus_view_get_directory_as_file (view);
	if (file != NULL) {
		return nautilus_file_is_in_recent (file);
	}
	return FALSE;
}

452
static gboolean
453
nautilus_view_supports_creating_files (NautilusView *view)
454
{
455
	g_return_val_if_fail (NAUTILUS_IS_VIEW (view), FALSE);
456

William Jon McCann's avatar
William Jon McCann committed
457
458
459
	return !nautilus_view_is_read_only (view)
		&& !showing_trash_directory (view)
		&& !showing_recent_directory (view);
460
461
}

462
static gboolean
463
nautilus_view_is_empty (NautilusView *view)
464
{
465
	g_return_val_if_fail (NAUTILUS_IS_VIEW (view), FALSE);
466

467
	return 	NAUTILUS_VIEW_CLASS (G_OBJECT_GET_CLASS (view))->is_empty (view);
468
469
}

470
471
472
473
474
475
476
477
478
/**
 * nautilus_view_bump_zoom_level:
 *
 * bump the current zoom level by invoking the relevant subclass through the slot
 * 
 **/
void
nautilus_view_bump_zoom_level (NautilusView *view,
			       int zoom_increment)
479
{
480
	g_return_if_fail (NAUTILUS_IS_VIEW (view));
481

482
483
484
	if (!nautilus_view_supports_zooming (view)) {
		return;
	}
Alexander Larsson's avatar
Alexander Larsson committed
485

486
	NAUTILUS_VIEW_CLASS (G_OBJECT_GET_CLASS (view))->bump_zoom_level (view, zoom_increment);
Alexander Larsson's avatar
Alexander Larsson committed
487
488
}

489
490
491
492
/**
 * nautilus_view_can_zoom_in:
 *
 * Determine whether the view can be zoomed any closer.
493
 * @view: The zoomable NautilusView.
494
495
496
497
498
499
 * 
 * Return value: TRUE if @view can be zoomed any closer, FALSE otherwise.
 * 
 **/
gboolean
nautilus_view_can_zoom_in (NautilusView *view)
500
{
501
	g_return_val_if_fail (NAUTILUS_IS_VIEW (view), FALSE);
502

503
504
	if (!nautilus_view_supports_zooming (view)) {
		return FALSE;
505
	}
506

507
	return NAUTILUS_VIEW_CLASS (G_OBJECT_GET_CLASS (view))->can_zoom_in (view);
508
509
}

510
511
512
513
/**
 * nautilus_view_can_zoom_out:
 *
 * Determine whether the view can be zoomed any further away.
514
 * @view: The zoomable NautilusView.
515
516
517
518
519
520
 * 
 * Return value: TRUE if @view can be zoomed any further away, FALSE otherwise.
 * 
 **/
gboolean
nautilus_view_can_zoom_out (NautilusView *view)
521
{
522
	g_return_val_if_fail (NAUTILUS_IS_VIEW (view), FALSE);
523

524
525
	if (!nautilus_view_supports_zooming (view)) {
		return FALSE;
526
527
	}

528
	return NAUTILUS_VIEW_CLASS (G_OBJECT_GET_CLASS (view))->can_zoom_out (view);
529
530
}

531
gboolean
532
nautilus_view_supports_zooming (NautilusView *view)
533
{
534
	g_return_val_if_fail (NAUTILUS_IS_VIEW (view), FALSE);
535

536
	return view->details->supports_zooming;
537
538
}

539
540
541
542
543
544
545
546
/**
 * nautilus_view_restore_default_zoom_level:
 *
 * restore to the default zoom level by invoking the relevant subclass through the slot
 * 
 **/
void
nautilus_view_restore_default_zoom_level (NautilusView *view)
Alexander Larsson's avatar
Alexander Larsson committed
547
{
548
	g_return_if_fail (NAUTILUS_IS_VIEW (view));
549
550
551

	if (!nautilus_view_supports_zooming (view)) {
		return;
Alexander Larsson's avatar
Alexander Larsson committed
552
	}
553

554
	NAUTILUS_VIEW_CLASS (G_OBJECT_GET_CLASS (view))->restore_default_zoom_level (view);
Alexander Larsson's avatar
Alexander Larsson committed
555
556
}

557
558
const char *
nautilus_view_get_view_id (NautilusView *view)
Alexander Larsson's avatar
Alexander Larsson committed
559
{
560
	return NAUTILUS_VIEW_CLASS (G_OBJECT_GET_CLASS (view))->get_view_id (view);
561
}
Alexander Larsson's avatar
Alexander Larsson committed
562

563
564
565
char *
nautilus_view_get_first_visible_file (NautilusView *view)
{
566
	return NAUTILUS_VIEW_CLASS (G_OBJECT_GET_CLASS (view))->get_first_visible_file (view);
Alexander Larsson's avatar
Alexander Larsson committed
567
568
569
}

void
570
571
nautilus_view_scroll_to_file (NautilusView *view,
			      const char *uri)
Alexander Larsson's avatar
Alexander Larsson committed
572
{
573
	NAUTILUS_VIEW_CLASS (G_OBJECT_GET_CLASS (view))->scroll_to_file (view, uri);
574
}
Alexander Larsson's avatar
Alexander Larsson committed
575

576
/**
577
 * nautilus_view_get_selection:
578
579
580
581
582
 *
 * Get a list of NautilusFile pointers that represents the
 * currently-selected items in this view. Subclasses must override
 * the signal handler for the 'get_selection' signal. Callers are
 * responsible for g_free-ing the list (but not its data).
583
 * @view: NautilusView whose selected items are of interest.
584
585
586
587
588
 * 
 * Return value: GList of NautilusFile pointers representing the selection.
 * 
 **/
GList *
589
nautilus_view_get_selection (NautilusView *view)
590
{
591
	g_return_val_if_fail (NAUTILUS_IS_VIEW (view), NULL);
592

593
	return NAUTILUS_VIEW_CLASS (G_OBJECT_GET_CLASS (view))->get_selection (view);
594
595
}

596
597
typedef struct {
	NautilusFile *file;
598
	NautilusView *directory_view;
599
} ScriptLaunchParameters;
600

601
602
typedef struct {
	NautilusFile *file;
603
	NautilusView *directory_view;
604
} CreateTemplateParameters;
605

606
607
static GList *
file_and_directory_list_to_files (GList *fad_list)
608
{
609
610
	GList *res, *l;
	FileAndDirectory *fad;
611

612
613
614
615
616
617
618
	res = NULL;
	for (l = fad_list; l != NULL; l = l->next) {
		fad = l->data;
		res = g_list_prepend (res, nautilus_file_ref (fad->file));
	}
	return g_list_reverse (res);
}
619
620


621
622
623
624
625
static GList *
file_and_directory_list_from_files (NautilusDirectory *directory, GList *files)
{
	GList *res, *l;
	FileAndDirectory *fad;
626

627
628
629
630
631
632
633
634
	res = NULL;
	for (l = files; l != NULL; l = l->next) {
		fad = g_new0 (FileAndDirectory, 1);
		fad->directory = nautilus_directory_ref (directory);
		fad->file = nautilus_file_ref (l->data);
		res = g_list_prepend (res, fad);
	}
	return g_list_reverse (res);
635
636
637
}

static void
638
file_and_directory_free (FileAndDirectory *fad)
639
{
640
641
642
643
	nautilus_directory_unref (fad->directory);
	nautilus_file_unref (fad->file);
	g_free (fad);
}
644
645


646
647
648
649
static void
file_and_directory_list_free (GList *list)
{
	GList *l;
650

651
652
	for (l = list; l != NULL; l = l->next) {
		file_and_directory_free (l->data);
653
	}
654

655
	g_list_free (list);
656
657
}

658
659
660
static gboolean
file_and_directory_equal (gconstpointer  v1,
			  gconstpointer  v2)
661
{
662
663
664
	const FileAndDirectory *fad1, *fad2;
	fad1 = v1;
	fad2 = v2;
665

666
667
	return (fad1->file == fad2->file &&
		fad1->directory == fad2->directory);
668
669
}

670
671
static guint
file_and_directory_hash  (gconstpointer  v)
672
{
673
	const FileAndDirectory *fad;
674

675
676
	fad = v;
	return GPOINTER_TO_UINT (fad->file) ^ GPOINTER_TO_UINT (fad->directory);
677
678
}

679

Alexander Larsson's avatar
Alexander Larsson committed
680

681
682
683

static ScriptLaunchParameters *
script_launch_parameters_new (NautilusFile *file,
684
			      NautilusView *directory_view)
685
{
686
687
688
689
690
691
692
693
694
	ScriptLaunchParameters *result;

	result = g_new0 (ScriptLaunchParameters, 1);
	g_object_ref (directory_view);
	result->directory_view = directory_view;
	nautilus_file_ref (file);
	result->file = file;

	return result;
695
696
}

697
static void
698
script_launch_parameters_free (ScriptLaunchParameters *parameters)
699
{
700
701
702
703
	g_object_unref (parameters->directory_view);
	nautilus_file_unref (parameters->file);
	g_free (parameters);
}			      
704

705
706
static CreateTemplateParameters *
create_template_parameters_new (NautilusFile *file,
707
				NautilusView *directory_view)
708
709
{
	CreateTemplateParameters *result;
710

711
712
713
714
715
	result = g_new0 (CreateTemplateParameters, 1);
	g_object_ref (directory_view);
	result->directory_view = directory_view;
	nautilus_file_ref (file);
	result->file = file;
716

717
	return result;
718
719
}

720
static void
721
create_templates_parameters_free (CreateTemplateParameters *parameters)
722
{
723
724
725
726
727
	g_object_unref (parameters->directory_view);
	nautilus_file_unref (parameters->file);
	g_free (parameters);
}			      

728
NautilusWindow *
729
nautilus_view_get_window (NautilusView  *view)
730
{
731
	return nautilus_window_slot_get_window (view->details->slot);
732
733
}

734
NautilusWindowSlot *
735
nautilus_view_get_nautilus_window_slot (NautilusView  *view)
736
{
737
	g_assert (view->details->slot != NULL);
738

739
740
	return view->details->slot;
}
741

742
743
744
745
746
/* Returns the GtkWindow that this directory view occupies, or NULL
 * if at the moment this directory view is not in a GtkWindow or the
 * GtkWindow cannot be determined. Primarily used for parenting dialogs.
 */
static GtkWindow *
747
nautilus_view_get_containing_window (NautilusView *view)
748
749
{
	GtkWidget *window;
750

751
	g_assert (NAUTILUS_IS_VIEW (view));
752
753
754
755
756
	
	window = gtk_widget_get_ancestor (GTK_WIDGET (view), GTK_TYPE_WINDOW);
	if (window == NULL) {
		return NULL;
	}
757

758
	return GTK_WINDOW (window);
759
760
}

Alexander Larsson's avatar
Alexander Larsson committed
761
static gboolean
762
763
764
nautilus_view_confirm_multiple (GtkWindow *parent_window,
				int count,
				gboolean tabs)
Alexander Larsson's avatar
Alexander Larsson committed
765
{
766
767
768
769
	GtkDialog *dialog;
	char *prompt;
	char *detail;
	int response;
770

771
	if (count <= SILENT_WINDOW_OPEN_LIMIT) {
772
		return TRUE;
Alexander Larsson's avatar
Alexander Larsson committed
773
774
	}

775
776
777
778
779
780
781
	prompt = _("Are you sure you want to open all files?");
	if (tabs) {
		detail = g_strdup_printf (ngettext("This will open %'d separate tab.",
						   "This will open %'d separate tabs.", count), count);
	} else {
		detail = g_strdup_printf (ngettext("This will open %'d separate window.",
						   "This will open %'d separate windows.", count), count);
782
	}
783
	dialog = eel_show_yes_no_dialog (prompt, detail, 
784
					 _("_OK"), _("_Cancel"),
785
786
					 parent_window);
	g_free (detail);
787

788
789
790
791
	response = gtk_dialog_run (dialog);
	gtk_widget_destroy (GTK_WIDGET (dialog));

	return response == GTK_RESPONSE_YES;
792
793
}

794
static gboolean
795
selection_contains_one_item_in_menu_callback (NautilusView *view, GList *selection)
796
{
Cosimo Cecchi's avatar
Cosimo Cecchi committed
797
	if (g_list_length (selection) == 1) {
798
799
		return TRUE;
	}
800

801
	return FALSE;
802
803
}

804
static gboolean
805
selection_not_empty_in_menu_callback (NautilusView *view, GList *selection)
806
{
807
808
809
	if (selection != NULL) {
		return TRUE;
	}
810

811
	return FALSE;
812
813
}

814
static char *
815
get_view_directory (NautilusView *view)
816
{
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
	char *uri, *path;
	GFile *f;
	
	uri = nautilus_directory_get_uri (view->details->model);
	if (eel_uri_is_desktop (uri)) {
		g_free (uri);
		uri = nautilus_get_desktop_directory_uri ();
		
	}
	f = g_file_new_for_uri (uri);
	path = g_file_get_path (f);
	g_object_unref (f);
	g_free (uri);
	
	return path;
832
833
}

834
835
836
837
838
839
void
nautilus_view_preview_files (NautilusView *view,
			     GList *files,
			     GArray *locations)
{
	gchar *uri;
840
	guint xid;
841
842
843
844
845
846
	GtkWidget *toplevel;

	uri = nautilus_file_get_uri (files->data);
	toplevel = gtk_widget_get_toplevel (GTK_WIDGET (view));

	xid = gdk_x11_window_get_xid (gtk_widget_get_window (toplevel));
847
	nautilus_previewer_call_show_file (uri, xid, TRUE);
848
849

	g_free (uri);
850
851
}

852
853
854
855
856
857
858
859
860
861
862
863
864
void
nautilus_view_activate_selection (NautilusView *view)
{
	GList *selection;

	selection = nautilus_view_get_selection (view);
	nautilus_view_activate_files (view,
				      selection,
				      0,
				      TRUE);
	nautilus_file_list_free (selection);
}

865
void
866
867
868
869
nautilus_view_activate_files (NautilusView *view,
			      GList *files,
			      NautilusWindowOpenFlags flags,
			      gboolean confirm_multiple)
870
{
871
	char *path;
872

873
	path = get_view_directory (view);
874
	nautilus_mime_activate_files (nautilus_view_get_containing_window (view),
875
876
877
878
879
				      view->details->slot,
				      files,
				      path,
				      flags,
				      confirm_multiple);
880

881
	g_free (path);
882
883
884
}

static void
885
886
887
nautilus_view_activate_file (NautilusView *view,
			     NautilusFile *file,
			     NautilusWindowOpenFlags flags)
888
{
889
	char *path;
890

891
	path = get_view_directory (view);
892
	nautilus_mime_activate_file (nautilus_view_get_containing_window (view),
893
894
895
896
				     view->details->slot,
				     file,
				     path,
				     flags);
897

898
	g_free (path);
899
900
901
}

static void
902
903
904
action_open_with_default_application (GSimpleAction *action,
				      GVariant      *state,
				      gpointer       user_data)
905
{
906
	NautilusView *view;
907

908
	view = NAUTILUS_VIEW (user_data);
909
	nautilus_view_activate_selection (view);
910
}
911
static void
912
913
914
action_open_file_and_close_window (GSimpleAction *action,
				   GVariant      *state,
				   gpointer       user_data)
915
{
916
	GList *selection;
917
	NautilusView *view;
918

919
	view = NAUTILUS_VIEW (user_data);
920

921
	selection = nautilus_view_get_selection (view);
922
923
924
925
	nautilus_view_activate_files (view,
				      selection,
				      NAUTILUS_WINDOW_OPEN_FLAG_CLOSE_BEHIND,
				      TRUE);
926
	nautilus_file_list_free (selection);
927
928
}

929
930
931
932
933
934
static void
got_it_clicked (GtkDialog *dialog,
                gint       response_id,
                gpointer   user_data)
{
	g_settings_set_boolean (nautilus_preferences,
935
                                NAUTILUS_PREFERENCES_SHOW_MOVE_TO_TRASH_SHORTCUT_CHANGED_DIALOG,
936
937
938
939
                                FALSE);
}

static void
940
941
942
action_show_move_to_trash_shortcut_changed_dialog (GSimpleAction *action,
                                                   GVariant      *state,
                                                   gpointer       user_data)
943
944
945
946
947
948
{
	NautilusView *view;
	GtkWindow *dialog;
	GtkBuilder *builder;
	gboolean show_dialog_preference;

949
	view = NAUTILUS_VIEW (user_data);
950
	show_dialog_preference