nautilus-view.c 284 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 19 20 21 22
 *
 * 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
 * License along with this program; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 *
23 24
 * Authors: Ettore Perazzoli,
 *          John Sullivan <sullivan@eazel.com>,
25
 *          Darin Adler <darin@bentspoon.com>,
26 27
 *          Pavel Cisler <pavel@eazel.com>,
 *          David Emory Watson <dwatson@cs.ucr.edu>
Ettore Perazzoli's avatar
Ettore Perazzoli committed
28 29
 */

30
#include <config.h>
31

32 33
#include "nautilus-view.h"

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

42
#include <gdk/gdkx.h>
43 44 45 46 47 48
#include <gdk/gdkkeysyms.h>
#include <gtk/gtk.h>
#include <glib/gi18n.h>
#include <glib/gstdio.h>
#include <gio/gio.h>
#include <math.h>
49 50 51
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
Alexander Larsson's avatar
Alexander Larsson committed
52

Ramiro Estrugo's avatar
Ramiro Estrugo committed
53 54 55 56 57 58
#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>
59

60
#include <libnautilus-extension/nautilus-menu-provider.h>
61
#include <libnautilus-private/nautilus-clipboard.h>
62
#include <libnautilus-private/nautilus-clipboard-monitor.h>
Alexander Larsson's avatar
Alexander Larsson committed
63 64
#include <libnautilus-private/nautilus-desktop-icon-file.h>
#include <libnautilus-private/nautilus-desktop-directory.h>
65
#include <libnautilus-private/nautilus-search-directory.h>
66
#include <libnautilus-private/nautilus-directory.h>
67
#include <libnautilus-private/nautilus-dnd.h>
68
#include <libnautilus-private/nautilus-file-attributes.h>
69
#include <libnautilus-private/nautilus-file-changes-queue.h>
70 71 72
#include <libnautilus-private/nautilus-file-dnd.h>
#include <libnautilus-private/nautilus-file-operations.h>
#include <libnautilus-private/nautilus-file-utilities.h>
73
#include <libnautilus-private/nautilus-file-private.h>
74 75 76
#include <libnautilus-private/nautilus-global-preferences.h>
#include <libnautilus-private/nautilus-link.h>
#include <libnautilus-private/nautilus-metadata.h>
77
#include <libnautilus-private/nautilus-recent.h>
78
#include <libnautilus-private/nautilus-module.h>
79 80
#include <libnautilus-private/nautilus-program-choosing.h>
#include <libnautilus-private/nautilus-trash-monitor.h>
Alexander Larsson's avatar
Alexander Larsson committed
81
#include <libnautilus-private/nautilus-ui-utilities.h>
Alexander Larsson's avatar
Alexander Larsson committed
82
#include <libnautilus-private/nautilus-signaller.h>
83
#include <libnautilus-private/nautilus-icon-names.h>
Amos Brocco's avatar
Amos Brocco committed
84
#include <libnautilus-private/nautilus-file-undo-manager.h>
85

86 87 88
#define DEBUG_FLAG NAUTILUS_DEBUG_DIRECTORY_VIEW
#include <libnautilus-private/nautilus-debug.h>

89 90 91 92 93 94 95 96 97 98 99
/* 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

100
#define SILENT_WINDOW_OPEN_LIMIT 5
101

102 103 104
#define DUPLICATE_HORIZONTAL_ICON_OFFSET 70
#define DUPLICATE_VERTICAL_ICON_OFFSET   30

105 106
#define MAX_QUEUED_UPDATES 500

107 108 109 110 111 112
#define NAUTILUS_VIEW_MENU_PATH_APPLICATIONS_SUBMENU_PLACEHOLDER  "/ViewMenu/Open Placeholder/Open With/Applications Placeholder"
#define NAUTILUS_VIEW_MENU_PATH_APPLICATIONS_PLACEHOLDER    	  "/ViewMenu/Open Placeholder/Applications Placeholder"
#define NAUTILUS_VIEW_MENU_PATH_SCRIPTS_PLACEHOLDER               "/ViewMenu/Open Placeholder/Scripts/Scripts Placeholder"
#define NAUTILUS_VIEW_MENU_PATH_EXTENSION_ACTIONS_PLACEHOLDER     "/ViewMenu/Edit/Extension Actions"
#define NAUTILUS_VIEW_MENU_PATH_NEW_DOCUMENTS_PLACEHOLDER  	  "/ViewMenu/New Items Placeholder/New Documents/New Documents Placeholder"
#define NAUTILUS_VIEW_MENU_PATH_OPEN				  "/ViewMenu/Open Placeholder/Open"
113

114 115 116 117 118 119
#define NAUTILUS_VIEW_POPUP_PATH_SELECTION			  "/selection"
#define NAUTILUS_VIEW_POPUP_PATH_APPLICATIONS_SUBMENU_PLACEHOLDER "/selection/Open Placeholder/Open With/Applications Placeholder"
#define NAUTILUS_VIEW_POPUP_PATH_APPLICATIONS_PLACEHOLDER    	  "/selection/Open Placeholder/Applications Placeholder"
#define NAUTILUS_VIEW_POPUP_PATH_SCRIPTS_PLACEHOLDER    	  "/selection/Open Placeholder/Scripts/Scripts Placeholder"
#define NAUTILUS_VIEW_POPUP_PATH_EXTENSION_ACTIONS		  "/selection/Extension Actions"
#define NAUTILUS_VIEW_POPUP_PATH_OPEN				  "/selection/Open Placeholder/Open"
Alexander Larsson's avatar
Alexander Larsson committed
120

121 122 123
#define NAUTILUS_VIEW_POPUP_PATH_BACKGROUND			  "/background"
#define NAUTILUS_VIEW_POPUP_PATH_BACKGROUND_SCRIPTS_PLACEHOLDER	  "/background/Before Zoom Items/New Object Items/Scripts/Scripts Placeholder"
#define NAUTILUS_VIEW_POPUP_PATH_BACKGROUND_NEW_DOCUMENTS_PLACEHOLDER "/background/Before Zoom Items/New Object Items/New Documents/New Documents Placeholder"
124

125
#define NAUTILUS_VIEW_POPUP_PATH_LOCATION			  "/location"
126

127
#define MAX_MENU_LEVELS 5
128
#define TEMPLATE_LIMIT 30
129

Pavel Cisler's avatar
Pavel Cisler committed
130
enum {
131
	ADD_FILE,
132
	BEGIN_FILE_CHANGES,
133
	BEGIN_LOADING,
134
	CLEAR,
135 136
	END_FILE_CHANGES,
	END_LOADING,
137
	FILE_CHANGED,
138
	LOAD_ERROR,
139
	MOVE_COPY_ITEMS,
140
	REMOVE_FILE,
141
	ZOOM_LEVEL_CHANGED,
142
	SELECTION_CHANGED,
Alexander Larsson's avatar
Alexander Larsson committed
143 144
	TRASH,
	DELETE,
145 146 147
	LAST_SIGNAL
};

148
enum {
149
	PROP_WINDOW_SLOT = 1,
150
	PROP_SUPPORTS_ZOOMING,
151
	NUM_PROPERTIES
Alexander Larsson's avatar
Alexander Larsson committed
152 153
};

154
static guint signals[LAST_SIGNAL];
155
static GParamSpec *properties[NUM_PROPERTIES] = { NULL, };
Ettore Perazzoli's avatar
Ettore Perazzoli committed
156

157 158
static GdkAtom copied_files_atom;

159
static char *scripts_directory_uri = NULL;
160 161
static int scripts_directory_uri_length;

162
struct NautilusViewDetails
Ettore Perazzoli's avatar
Ettore Perazzoli committed
163
{
164 165
	NautilusWindow *window;
	NautilusWindowSlot *slot;
166
	NautilusDirectory *model;
Darin Adler's avatar
Darin Adler committed
167
	NautilusFile *directory_as_file;
168 169
	NautilusFile *location_popup_directory_as_file;
	GdkEventButton *location_popup_event;
Alexander Larsson's avatar
Alexander Larsson committed
170 171
	GtkActionGroup *dir_action_group;
	guint dir_merge_id;
172

173
	gboolean supports_zooming;
174

175
	GList *scripts_directory_list;
Alexander Larsson's avatar
Alexander Larsson committed
176 177 178
	GtkActionGroup *scripts_action_group;
	guint scripts_merge_id;
	
179
	GList *templates_directory_list;
Alexander Larsson's avatar
Alexander Larsson committed
180 181
	GtkActionGroup *templates_action_group;
	guint templates_merge_id;
182

Alexander Larsson's avatar
Alexander Larsson committed
183 184 185
	GtkActionGroup *extensions_menu_action_group;
	guint extensions_menu_merge_id;
	
186
	guint display_selection_idle_id;
187
	guint update_menus_timeout_id;
188
	guint update_status_idle_id;
189 190
	guint reveal_selection_idle_id;

191 192 193 194 195
	guint display_pending_source_id;
	guint changes_timeout_id;

	guint update_interval;
 	guint64 last_queued;
196
	
197 198
	guint files_added_handler_id;
	guint files_changed_handler_id;
199
	guint load_error_handler_id;
200
	guint done_loading_handler_id;
201
	guint file_changed_handler_id;
202

203 204
	guint delayed_rename_file_id;

205 206 207 208 209 210 211 212
	GList *new_added_files;
	GList *new_changed_files;

	GHashTable *non_ready_files;

	GList *old_added_files;
	GList *old_changed_files;

213
	GList *pending_selection;
214

215 216 217
	/* whether we are in the active slot */
	gboolean active;

218
	/* loading indicates whether this view has begun loading a directory.
219
	 * This flag should need not be set inside subclasses. NautilusView automatically
220 221 222
	 * sets 'loading' to TRUE before it begins loading a directory's contents and to FALSE
	 * after it finishes loading the directory and its view.
	 */
223
	gboolean loading;
224
	gboolean menu_states_untrustworthy;
225
	gboolean scripts_invalid;
226
	gboolean templates_invalid;
227
	gboolean templates_present;
228
	gboolean reported_load_error;
229

230 231 232 233 234 235 236 237
	/* 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;

238 239
	gboolean is_renaming;

240 241
	gboolean sort_directories_first;

242
	gboolean show_foreign_files;
243
	gboolean show_hidden_files;
244
	gboolean ignore_hidden_file_preferences;
245 246 247

	gboolean batching_selection_level;
	gboolean selection_changed_while_batched;
248

249 250
	gboolean selection_was_removed;

251 252 253
	gboolean metadata_for_directory_as_file_pending;
	gboolean metadata_for_files_in_directory_pending;

254 255 256
	gboolean selection_change_is_due_to_shell;
	gboolean send_selection_change_to_shell;

Alexander Larsson's avatar
Alexander Larsson committed
257 258
	GtkActionGroup *open_with_action_group;
	guint open_with_merge_id;
Alexander Larsson's avatar
Alexander Larsson committed
259 260

	GList *subdirectory_list;
261 262

	GdkPoint context_menu_position;
263
};
Ettore Perazzoli's avatar
Ettore Perazzoli committed
264

265 266 267 268 269
typedef struct {
	NautilusFile *file;
	NautilusDirectory *directory;
} FileAndDirectory;

270
/* forward declarations */
271

272
static gboolean display_selection_info_idle_callback           (gpointer              data);
273 274 275 276 277 278
static void     nautilus_view_duplicate_selection              (NautilusView      *view,
							        GList                *files,
							        GArray               *item_locations);
static void     nautilus_view_create_links_for_files           (NautilusView      *view,
							        GList                *files,
							        GArray               *item_locations);
Alexander Larsson's avatar
Alexander Larsson committed
279 280
static void     trash_or_delete_files                          (GtkWindow            *parent_window,
								const GList          *files,
281
								gboolean              delete_if_all_already_in_trash,
282 283
								NautilusView      *view);
static void     load_directory                                 (NautilusView      *view,
284
								NautilusDirectory    *directory);
285 286 287
static void     nautilus_view_merge_menus                      (NautilusView      *view);
static void     nautilus_view_unmerge_menus                    (NautilusView      *view);
static void     nautilus_view_init_show_hidden_files           (NautilusView      *view);
288
static void     clipboard_changed_callback                     (NautilusClipboardMonitor *monitor,
289
								NautilusView      *view);
290 291
static void     open_one_in_new_window                         (gpointer              data,
								gpointer              callback_data);
292 293 294 295 296 297 298 299
static void     schedule_update_menus                          (NautilusView      *view);
static void     remove_update_menus_timeout_callback           (NautilusView      *view);
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);
300 301 302 303 304
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);
305 306 307 308 309
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
310

311 312
static void     update_templates_directory                     (NautilusView *view);
static void     user_dirs_changed                              (NautilusView *view);
313

314 315
static gboolean file_list_all_are_folders                      (GList *file_list);

316 317 318 319 320
static void unschedule_pop_up_location_context_menu (NautilusView *view);

G_DEFINE_TYPE (NautilusView, nautilus_view, GTK_TYPE_SCROLLED_WINDOW);
#define parent_class nautilus_view_parent_class

321
/* virtual methods (public and non-public) */
322

323
/**
324
 * nautilus_view_merge_menus:
325 326
 * 
 * Add this view's menus to the window's menu bar.
327
 * @view: NautilusView in question.
328 329
 */
static void
330
nautilus_view_merge_menus (NautilusView *view)
331
{
332
	g_return_if_fail (NAUTILUS_IS_VIEW (view));
333

334
	NAUTILUS_VIEW_CLASS (G_OBJECT_GET_CLASS (view))->merge_menus (view);
335
}
336

337
static void
338
nautilus_view_unmerge_menus (NautilusView *view)
Alexander Larsson's avatar
Alexander Larsson committed
339
{
340
	g_return_if_fail (NAUTILUS_IS_VIEW (view));
Alexander Larsson's avatar
Alexander Larsson committed
341

342
	NAUTILUS_VIEW_CLASS (G_OBJECT_GET_CLASS (view))->unmerge_menus (view);}
Alexander Larsson's avatar
Alexander Larsson committed
343

344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375
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
376
 * link and paste operations.
377 378 379 380 381 382 383
 */

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

384
	return NAUTILUS_VIEW_CLASS (G_OBJECT_GET_CLASS (view))->get_backing_uri (view);
385 386
}

387
/**
388
 * nautilus_view_select_all:
389 390 391 392 393
 *
 * select all the items in the view
 * 
 **/
static void
394
nautilus_view_select_all (NautilusView *view)
395
{
396
	g_return_if_fail (NAUTILUS_IS_VIEW (view));
Alexander Larsson's avatar
Alexander Larsson committed
397

398
	NAUTILUS_VIEW_CLASS (G_OBJECT_GET_CLASS (view))->select_all (view);
Alexander Larsson's avatar
Alexander Larsson committed
399 400 401
}

static void
402
nautilus_view_call_set_selection (NautilusView *view, GList *selection)
Alexander Larsson's avatar
Alexander Larsson committed
403
{
404
	g_return_if_fail (NAUTILUS_IS_VIEW (view));
405

406
	NAUTILUS_VIEW_CLASS (G_OBJECT_GET_CLASS (view))->set_selection (view, selection);
407
}
408 409

static GList *
410
nautilus_view_get_selection_for_file_transfer (NautilusView *view)
411
{
412
	g_return_val_if_fail (NAUTILUS_IS_VIEW (view), NULL);
413

414
	return NAUTILUS_VIEW_CLASS (G_OBJECT_GET_CLASS (view))->get_selection_for_file_transfer (view);
415 416
}

417
/**
418
 * nautilus_view_get_selected_icon_locations:
419 420 421 422 423 424
 *
 * return an array of locations of selected icons if available
 * Return value: GArray of GdkPoints
 * 
 **/
static GArray *
425
nautilus_view_get_selected_icon_locations (NautilusView *view)
426
{
427
	g_return_val_if_fail (NAUTILUS_IS_VIEW (view), NULL);
428

429
	return NAUTILUS_VIEW_CLASS (G_OBJECT_GET_CLASS (view))->get_selected_icon_locations (view);
430 431 432
}

static void
433
nautilus_view_invert_selection (NautilusView *view)
434
{
435
	g_return_if_fail (NAUTILUS_IS_VIEW (view));
436

437
	NAUTILUS_VIEW_CLASS (G_OBJECT_GET_CLASS (view))->invert_selection (view);
438 439
}

440
/**
441
 * nautilus_view_reveal_selection:
442 443 444 445
 *
 * Scroll as necessary to reveal the selected items.
 **/
static void
446
nautilus_view_reveal_selection (NautilusView *view)
447
{
448
	g_return_if_fail (NAUTILUS_IS_VIEW (view));
449

450
	NAUTILUS_VIEW_CLASS (G_OBJECT_GET_CLASS (view))->reveal_selection (view);
451
}
452

453
/**
454
 * nautilus_view_reset_to_defaults:
455 456 457 458
 *
 * set sorting order, zoom level, etc. to match defaults
 * 
 **/
459
static void
460
nautilus_view_reset_to_defaults (NautilusView *view)
461
{
462
	NautilusWindowShowHiddenFilesMode mode;
463

464
	g_return_if_fail (NAUTILUS_IS_VIEW (view));
465 466 467

	NAUTILUS_VIEW_CLASS (G_OBJECT_GET_CLASS (view))->reset_to_defaults (view);

468 469 470
	mode = nautilus_window_get_hidden_files_mode (view->details->window);
	if (mode != NAUTILUS_WINDOW_SHOW_HIDDEN_FILES_DEFAULT) {
		nautilus_window_set_hidden_files_mode (view->details->window,
471
						       NAUTILUS_WINDOW_SHOW_HIDDEN_FILES_DEFAULT);
472 473 474 475
	}
}

static gboolean
476
nautilus_view_using_manual_layout (NautilusView  *view)
477
{
478
	g_return_val_if_fail (NAUTILUS_IS_VIEW (view), FALSE);
479

480
	return 	NAUTILUS_VIEW_CLASS (G_OBJECT_GET_CLASS (view))->using_manual_layout (view);
481 482 483
}

static guint
484
nautilus_view_get_item_count (NautilusView *view)
485
{
486
	g_return_val_if_fail (NAUTILUS_IS_VIEW (view), 0);
487

488
	return 	NAUTILUS_VIEW_CLASS (G_OBJECT_GET_CLASS (view))->get_item_count (view);
489 490
}

491
/**
492
 * nautilus_view_can_rename_file
493 494 495 496 497 498 499 500
 *
 * Determine whether a file can be renamed.
 * @file: A NautilusFile
 * 
 * Return value: TRUE if @file can be renamed, FALSE otherwise.
 * 
 **/
static gboolean
501
nautilus_view_can_rename_file (NautilusView *view, NautilusFile *file)
502
{
503
	return 	NAUTILUS_VIEW_CLASS (G_OBJECT_GET_CLASS (view))->can_rename_file (view, file);
504
}
505

506
static gboolean
507
nautilus_view_is_read_only (NautilusView *view)
508
{
509
	g_return_val_if_fail (NAUTILUS_IS_VIEW (view), FALSE);
510

511
	return 	NAUTILUS_VIEW_CLASS (G_OBJECT_GET_CLASS (view))->is_read_only (view);
512
}
513

514
static gboolean
515
showing_trash_directory (NautilusView *view)
516 517 518
{
	NautilusFile *file;

519
	file = nautilus_view_get_directory_as_file (view);
520 521 522 523 524 525
	if (file != NULL) {
		return nautilus_file_is_in_trash (file);
	}
	return FALSE;
}

526 527 528 529 530 531 532 533 534 535 536 537
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;
}

538
static gboolean
539
nautilus_view_supports_creating_files (NautilusView *view)
540
{
541
	g_return_val_if_fail (NAUTILUS_IS_VIEW (view), FALSE);
542

543 544 545
	return !nautilus_view_is_read_only (view)
		&& !showing_trash_directory (view)
		&& !showing_recent_directory (view);
546 547
}

548
static gboolean
549
nautilus_view_is_empty (NautilusView *view)
550
{
551
	g_return_val_if_fail (NAUTILUS_IS_VIEW (view), FALSE);
552

553
	return 	NAUTILUS_VIEW_CLASS (G_OBJECT_GET_CLASS (view))->is_empty (view);
554 555
}

556 557 558 559 560 561 562 563 564
/**
 * 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)
565
{
566
	g_return_if_fail (NAUTILUS_IS_VIEW (view));
567

568 569 570
	if (!nautilus_view_supports_zooming (view)) {
		return;
	}
Alexander Larsson's avatar
Alexander Larsson committed
571

572
	NAUTILUS_VIEW_CLASS (G_OBJECT_GET_CLASS (view))->bump_zoom_level (view, zoom_increment);
Alexander Larsson's avatar
Alexander Larsson committed
573 574
}

575 576 577 578 579 580 581 582 583
/**
 * nautilus_view_zoom_to_level:
 *
 * Set the current zoom level by invoking the relevant subclass through the slot
 * 
 **/
void
nautilus_view_zoom_to_level (NautilusView *view,
			     NautilusZoomLevel zoom_level)
584
{
585
	g_return_if_fail (NAUTILUS_IS_VIEW (view));
586

587 588 589 590
	if (!nautilus_view_supports_zooming (view)) {
		return;
	}

591
	NAUTILUS_VIEW_CLASS (G_OBJECT_GET_CLASS (view))->zoom_to_level (view, zoom_level);
592
}
Alexander Larsson's avatar
Alexander Larsson committed
593

594
NautilusZoomLevel
595
nautilus_view_get_zoom_level (NautilusView *view)
596
{
597
	g_return_val_if_fail (NAUTILUS_IS_VIEW (view), NAUTILUS_ZOOM_LEVEL_STANDARD);
598

599 600
	if (!nautilus_view_supports_zooming (view)) {
		return NAUTILUS_ZOOM_LEVEL_STANDARD;
601 602
	}

603
	return NAUTILUS_VIEW_CLASS (G_OBJECT_GET_CLASS (view))->get_zoom_level (view);
604 605
}

606 607 608 609
/**
 * nautilus_view_can_zoom_in:
 *
 * Determine whether the view can be zoomed any closer.
610
 * @view: The zoomable NautilusView.
611 612 613 614 615 616
 * 
 * Return value: TRUE if @view can be zoomed any closer, FALSE otherwise.
 * 
 **/
gboolean
nautilus_view_can_zoom_in (NautilusView *view)
617
{
618
	g_return_val_if_fail (NAUTILUS_IS_VIEW (view), FALSE);
619

620 621
	if (!nautilus_view_supports_zooming (view)) {
		return FALSE;
622
	}
623

624
	return NAUTILUS_VIEW_CLASS (G_OBJECT_GET_CLASS (view))->can_zoom_in (view);
625 626
}

627 628 629 630
/**
 * nautilus_view_can_zoom_out:
 *
 * Determine whether the view can be zoomed any further away.
631
 * @view: The zoomable NautilusView.
632 633 634 635 636 637
 * 
 * Return value: TRUE if @view can be zoomed any further away, FALSE otherwise.
 * 
 **/
gboolean
nautilus_view_can_zoom_out (NautilusView *view)
638
{
639
	g_return_val_if_fail (NAUTILUS_IS_VIEW (view), FALSE);
640

641 642
	if (!nautilus_view_supports_zooming (view)) {
		return FALSE;
643 644
	}

645
	return NAUTILUS_VIEW_CLASS (G_OBJECT_GET_CLASS (view))->can_zoom_out (view);
646 647
}

648
gboolean
649
nautilus_view_supports_zooming (NautilusView *view)
650
{
651
	g_return_val_if_fail (NAUTILUS_IS_VIEW (view), FALSE);
652

653
	return view->details->supports_zooming;
654 655
}

656 657 658 659 660 661 662 663
/**
 * 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
664
{
665
	g_return_if_fail (NAUTILUS_IS_VIEW (view));
666 667 668

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

671
	NAUTILUS_VIEW_CLASS (G_OBJECT_GET_CLASS (view))->restore_default_zoom_level (view);
Alexander Larsson's avatar
Alexander Larsson committed
672 673
}

674 675
const char *
nautilus_view_get_view_id (NautilusView *view)
Alexander Larsson's avatar
Alexander Larsson committed
676
{
677
	return NAUTILUS_VIEW_CLASS (G_OBJECT_GET_CLASS (view))->get_view_id (view);
678
}
Alexander Larsson's avatar
Alexander Larsson committed
679

680 681 682
char *
nautilus_view_get_first_visible_file (NautilusView *view)
{
683
	return NAUTILUS_VIEW_CLASS (G_OBJECT_GET_CLASS (view))->get_first_visible_file (view);
Alexander Larsson's avatar
Alexander Larsson committed
684 685 686
}

void
687 688
nautilus_view_scroll_to_file (NautilusView *view,
			      const char *uri)
Alexander Larsson's avatar
Alexander Larsson committed
689
{
690
	NAUTILUS_VIEW_CLASS (G_OBJECT_GET_CLASS (view))->scroll_to_file (view, uri);
691
}
Alexander Larsson's avatar
Alexander Larsson committed
692

693
/**
694
 * nautilus_view_get_selection:
695 696 697 698 699
 *
 * 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).
700
 * @view: NautilusView whose selected items are of interest.
701 702 703 704 705
 * 
 * Return value: GList of NautilusFile pointers representing the selection.
 * 
 **/
GList *
706
nautilus_view_get_selection (NautilusView *view)
707
{
708
	g_return_val_if_fail (NAUTILUS_IS_VIEW (view), NULL);
709

710
	return NAUTILUS_VIEW_CLASS (G_OBJECT_GET_CLASS (view))->get_selection (view);
711 712
}

713 714 715 716
/**
 * nautilus_view_update_menus:
 * 
 * Update the sensitivity and wording of dynamic menu items.
717
 * @view: NautilusView in question.
718 719 720
 */
void
nautilus_view_update_menus (NautilusView *view)
721
{
722
	g_return_if_fail (NAUTILUS_IS_VIEW (view));
Alexander Larsson's avatar
Alexander Larsson committed
723

724 725
	if (!view->details->active) {
		return;
726
	}
727

728
	NAUTILUS_VIEW_CLASS (G_OBJECT_GET_CLASS (view))->update_menus (view);
729

730 731
	view->details->menu_states_untrustworthy = FALSE;
}
732

733 734 735
typedef struct {
	GAppInfo *application;
	GList *files;
736
	NautilusView *directory_view;
737
} ApplicationLaunchParameters;
738

739 740
typedef struct {
	NautilusFile *file;
741
	NautilusView *directory_view;
742
} ScriptLaunchParameters;
743

744 745
typedef struct {
	NautilusFile *file;
746
	NautilusView *directory_view;
747
} CreateTemplateParameters;
748

749 750 751
static ApplicationLaunchParameters *
application_launch_parameters_new (GAppInfo *application,
			      	   GList *files,
752
			           NautilusView *directory_view)
753
{
754
	ApplicationLaunchParameters *result;
755

756 757 758
	result = g_new0 (ApplicationLaunchParameters, 1);
	result->application = g_object_ref (application);
	result->files = nautilus_file_list_copy (files);
Alexander Larsson's avatar
Alexander Larsson committed
759

760 761 762
	if (directory_view != NULL) {
		g_object_ref (directory_view);
		result->directory_view = directory_view;
763 764
	}

765
	return result;
766 767
}

768
static void
769
application_launch_parameters_free (ApplicationLaunchParameters *parameters)
770
{
771 772
	g_object_unref (parameters->application);
	nautilus_file_list_free (parameters->files);
773

774 775
	if (parameters->directory_view != NULL) {
		g_object_unref (parameters->directory_view);
776 777
	}

778 779
	g_free (parameters);
}			      
780

781 782
static GList *
file_and_directory_list_to_files (GList *fad_list)
783
{
784 785
	GList *res, *l;
	FileAndDirectory *fad;
786

787 788 789 790 791 792 793
	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);
}
794 795


796 797 798 799 800
static GList *
file_and_directory_list_from_files (NautilusDirectory *directory, GList *files)
{
	GList *res, *l;
	FileAndDirectory *fad;
801

802 803 804 805 806 807 808 809
	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);
810 811 812
}

static void
813
file_and_directory_free (FileAndDirectory *fad)
814
{
815 816 817 818
	nautilus_directory_unref (fad->directory);
	nautilus_file_unref (fad->file);
	g_free (fad);
}
819 820


821 822 823 824
static void
file_and_directory_list_free (GList *list)
{
	GList *l;
825

826 827
	for (l = list; l != NULL; l = l->next) {
		file_and_directory_free (l->data);
828
	}
829

830
	g_list_free (list);
831 832
}

833 834 835
static gboolean
file_and_directory_equal (gconstpointer  v1,
			  gconstpointer  v2)
836
{
837 838 839
	const FileAndDirectory *fad1, *fad2;
	fad1 = v1;
	fad2 = v2;
840

841 842
	return (fad1->file == fad2->file &&
		fad1->directory == fad2->directory);
843 844
}

845 846
static guint
file_and_directory_hash  (gconstpointer  v)
847
{
848
	const FileAndDirectory *fad;
849

850 851
	fad = v;
	return GPOINTER_TO_UINT (fad->file) ^ GPOINTER_TO_UINT (fad->directory);
852 853
}

854

Alexander Larsson's avatar
Alexander Larsson committed
855

856 857 858

static ScriptLaunchParameters *
script_launch_parameters_new (NautilusFile *file,
859
			      NautilusView *directory_view)
860
{
861 862 863 864 865 866 867 868 869
	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;
870 871
}

872
static void
873
script_launch_parameters_free (ScriptLaunchParameters *parameters)
874
{
875 876 877 878
	g_object_unref (parameters->directory_view);
	nautilus_file_unref (parameters->file);
	g_free (parameters);
}			      
879

880 881
static CreateTemplateParameters *
create_template_parameters_new (NautilusFile *file,
882
				NautilusView *directory_view)
883 884
{
	CreateTemplateParameters *result;
885

886 887 888 889 890
	result = g_new0 (CreateTemplateParameters, 1);
	g_object_ref (directory_view);
	result->directory_view = directory_view;
	nautilus_file_ref (file);
	result->file = file;
891

892
	return result;
893 894
}

895
static void
896
create_templates_parameters_free (CreateTemplateParameters *parameters)
897
{
898 899 900 901 902 903
	g_object_unref (parameters->directory_view);
	nautilus_file_unref (parameters->file);
	g_free (parameters);
}			      

NautilusWindow *
904
nautilus_view_get_nautilus_window (NautilusView  *view)
905 906 907 908
{
	g_assert (view->details->window != NULL);

	return view->details->window;
909 910
}

911
NautilusWindowSlot *
912
nautilus_view_get_nautilus_window_slot (NautilusView  *view)
913
{
914
	g_assert (view->details->slot != NULL);
915

916 917
	return view->details->slot;
}
918

919 920 921 922 923
/* 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 *
924
nautilus_view_get_containing_window (NautilusView *view)
925 926
{
	GtkWidget *window;
927

928
	g_assert (NAUTILUS_IS_VIEW (view));
929 930 931 932 933
	
	window = gtk_widget_get_ancestor (GTK_WIDGET (view), GTK_TYPE_WINDOW);
	if (window == NULL) {
		return NULL;
	}
934

935
	return GTK_WINDOW (window);
936 937
}

Alexander Larsson's avatar
Alexander Larsson committed
938
static gboolean
939 940 941
nautilus_view_confirm_multiple (GtkWindow *parent_window,
				int count,
				gboolean tabs)
Alexander Larsson's avatar
Alexander Larsson committed
942
{
943 944 945 946
	GtkDialog *dialog;
	char *prompt;
	char *detail;
	int response;
947

948
	if (count <= SILENT_WINDOW_OPEN_LIMIT) {
949
		return TRUE;