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
/* fm-directory-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 34 35 36 37 38 39
#include "nautilus-view.h"

#include "file-manager/fm-list-view.h"
#include "file-manager/fm-desktop-icon-view.h"
#include "file-manager/fm-actions.h"
#include "file-manager/fm-error-reporting.h"
#include "file-manager/fm-properties-window.h"

40
#include "nautilus-mime-actions.h"
41
#include "nautilus-src-marshal.h"
42 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>
Alexander Larsson's avatar
Alexander Larsson committed
49

Ramiro Estrugo's avatar
Ramiro Estrugo committed
50 51 52 53 54 55 56
#include <eel/eel-glib-extensions.h>
#include <eel/eel-gnome-extensions.h>
#include <eel/eel-gtk-extensions.h>
#include <eel/eel-gtk-macros.h>
#include <eel/eel-stock-dialogs.h>
#include <eel/eel-string.h>
#include <eel/eel-vfs-extensions.h>
57

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

83 84 85
#define DEBUG_FLAG NAUTILUS_DEBUG_DIRECTORY_VIEW
#include <libnautilus-private/nautilus-debug.h>

86 87 88 89 90 91 92 93 94 95 96
/* 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

97
#define SILENT_WINDOW_OPEN_LIMIT 5
98

99 100 101
#define DUPLICATE_HORIZONTAL_ICON_OFFSET 70
#define DUPLICATE_VERTICAL_ICON_OFFSET   30

102 103
#define MAX_QUEUED_UPDATES 500

Alexander Larsson's avatar
Alexander Larsson committed
104 105 106 107 108
#define FM_DIRECTORY_VIEW_MENU_PATH_APPLICATIONS_SUBMENU_PLACEHOLDER   	"/MenuBar/File/Open Placeholder/Open With/Applications Placeholder"
#define FM_DIRECTORY_VIEW_MENU_PATH_APPLICATIONS_PLACEHOLDER    	"/MenuBar/File/Open Placeholder/Applications Placeholder"
#define FM_DIRECTORY_VIEW_MENU_PATH_SCRIPTS_PLACEHOLDER    		"/MenuBar/File/Open Placeholder/Scripts/Scripts Placeholder"
#define FM_DIRECTORY_VIEW_MENU_PATH_EXTENSION_ACTIONS_PLACEHOLDER       "/MenuBar/Edit/Extension Actions"
#define FM_DIRECTORY_VIEW_MENU_PATH_NEW_DOCUMENTS_PLACEHOLDER  		"/MenuBar/File/New Items Placeholder/New Documents/New Documents Placeholder"
109
#define FM_DIRECTORY_VIEW_MENU_PATH_OPEN				"/MenuBar/File/Open Placeholder/Open"
110

Alexander Larsson's avatar
Alexander Larsson committed
111 112 113 114 115
#define FM_DIRECTORY_VIEW_POPUP_PATH_SELECTION				"/selection"
#define FM_DIRECTORY_VIEW_POPUP_PATH_APPLICATIONS_SUBMENU_PLACEHOLDER  	"/selection/Open Placeholder/Open With/Applications Placeholder"
#define FM_DIRECTORY_VIEW_POPUP_PATH_APPLICATIONS_PLACEHOLDER    	"/selection/Open Placeholder/Applications Placeholder"
#define FM_DIRECTORY_VIEW_POPUP_PATH_SCRIPTS_PLACEHOLDER    		"/selection/Open Placeholder/Scripts/Scripts Placeholder"
#define FM_DIRECTORY_VIEW_POPUP_PATH_EXTENSION_ACTIONS			"/selection/Extension Actions"
116
#define FM_DIRECTORY_VIEW_POPUP_PATH_OPEN				"/selection/Open Placeholder/Open"
Alexander Larsson's avatar
Alexander Larsson committed
117 118 119 120

#define FM_DIRECTORY_VIEW_POPUP_PATH_BACKGROUND				"/background"
#define FM_DIRECTORY_VIEW_POPUP_PATH_BACKGROUND_SCRIPTS_PLACEHOLDER	"/background/Before Zoom Items/New Object Items/Scripts/Scripts Placeholder"
#define FM_DIRECTORY_VIEW_POPUP_PATH_BACKGROUND_NEW_DOCUMENTS_PLACEHOLDER "/background/Before Zoom Items/New Object Items/New Documents/New Documents Placeholder"
121

122 123
#define FM_DIRECTORY_VIEW_POPUP_PATH_LOCATION				"/location"

124
#define MAX_MENU_LEVELS 5
125
#define TEMPLATE_LIMIT 30
126

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

Alexander Larsson's avatar
Alexander Larsson committed
145 146 147
enum 
{
  PROP_0,
148
  PROP_WINDOW_SLOT
Alexander Larsson's avatar
Alexander Larsson committed
149 150 151
};


152
static guint signals[LAST_SIGNAL];
Ettore Perazzoli's avatar
Ettore Perazzoli committed
153

154 155
static GdkAtom copied_files_atom;

156 157 158
static char *scripts_directory_uri;
static int scripts_directory_uri_length;

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

170
	GList *scripts_directory_list;
Alexander Larsson's avatar
Alexander Larsson committed
171 172 173
	GtkActionGroup *scripts_action_group;
	guint scripts_merge_id;
	
174
	GList *templates_directory_list;
Alexander Larsson's avatar
Alexander Larsson committed
175 176
	GtkActionGroup *templates_action_group;
	guint templates_merge_id;
177

Alexander Larsson's avatar
Alexander Larsson committed
178 179 180
	GtkActionGroup *extensions_menu_action_group;
	guint extensions_menu_merge_id;
	
181
	guint display_selection_idle_id;
182
	guint update_menus_timeout_id;
183
	guint update_status_idle_id;
184 185
	guint reveal_selection_idle_id;

186 187 188 189 190
	guint display_pending_source_id;
	guint changes_timeout_id;

	guint update_interval;
 	guint64 last_queued;
191
	
192 193
	guint files_added_handler_id;
	guint files_changed_handler_id;
194
	guint load_error_handler_id;
195
	guint done_loading_handler_id;
196
	guint file_changed_handler_id;
197

198 199
	guint delayed_rename_file_id;

200 201 202 203 204 205 206 207
	GList *new_added_files;
	GList *new_changed_files;

	GHashTable *non_ready_files;

	GList *old_added_files;
	GList *old_changed_files;

208
	GList *pending_selection;
209

210 211 212
	/* whether we are in the active slot */
	gboolean active;

213 214 215 216 217
	/* loading indicates whether this view has begun loading a directory.
	 * This flag should need not be set inside subclasses. FMDirectoryView automatically
	 * sets 'loading' to TRUE before it begins loading a directory's contents and to FALSE
	 * after it finishes loading the directory and its view.
	 */
218
	gboolean loading;
219
	gboolean menu_states_untrustworthy;
220
	gboolean scripts_invalid;
221
	gboolean templates_invalid;
222
	gboolean reported_load_error;
223

224 225 226 227 228 229 230 231
	/* 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;

232 233
	gboolean sort_directories_first;

234
	gboolean show_foreign_files;
235
	gboolean show_hidden_files;
236
	gboolean ignore_hidden_file_preferences;
237 238 239

	gboolean batching_selection_level;
	gboolean selection_changed_while_batched;
240

241 242
	gboolean selection_was_removed;

243 244 245
	gboolean metadata_for_directory_as_file_pending;
	gboolean metadata_for_files_in_directory_pending;

246 247 248
	gboolean selection_change_is_due_to_shell;
	gboolean send_selection_change_to_shell;

Alexander Larsson's avatar
Alexander Larsson committed
249 250
	GtkActionGroup *open_with_action_group;
	guint open_with_merge_id;
Alexander Larsson's avatar
Alexander Larsson committed
251 252

	GList *subdirectory_list;
253 254

	GdkPoint context_menu_position;
255
};
Ettore Perazzoli's avatar
Ettore Perazzoli committed
256

257 258 259 260 261
typedef struct {
	NautilusFile *file;
	NautilusDirectory *directory;
} FileAndDirectory;

262
/* forward declarations */
263

264 265 266 267 268 269 270
static gboolean display_selection_info_idle_callback           (gpointer              data);
static void     fm_directory_view_duplicate_selection          (FMDirectoryView      *view,
								GList                *files,
								GArray               *item_locations);
static void     fm_directory_view_create_links_for_files       (FMDirectoryView      *view,
								GList                *files,
								GArray               *item_locations);
Alexander Larsson's avatar
Alexander Larsson committed
271 272
static void     trash_or_delete_files                          (GtkWindow            *parent_window,
								const GList          *files,
273 274
								gboolean              delete_if_all_already_in_trash,
								FMDirectoryView      *view);
275 276 277
static void     load_directory                                 (FMDirectoryView      *view,
								NautilusDirectory    *directory);
static void     fm_directory_view_merge_menus                  (FMDirectoryView      *view);
278
static void     fm_directory_view_unmerge_menus                (FMDirectoryView      *view);
279
static void     fm_directory_view_init_show_hidden_files       (FMDirectoryView      *view);
280 281
static void     clipboard_changed_callback                     (NautilusClipboardMonitor *monitor,
								FMDirectoryView      *view);
282 283
static void     open_one_in_new_window                         (gpointer              data,
								gpointer              callback_data);
284 285
static void     open_one_in_folder_window                      (gpointer              data,
								gpointer              callback_data);
286 287 288
static void     schedule_update_menus                          (FMDirectoryView      *view);
static void     schedule_update_menus_callback                 (gpointer              callback_data);
static void     remove_update_menus_timeout_callback           (FMDirectoryView      *view);
289 290
static void     schedule_update_status                          (FMDirectoryView      *view);
static void     remove_update_status_idle_callback             (FMDirectoryView *view); 
291
static void     reset_update_interval                          (FMDirectoryView      *view);
292 293 294 295 296 297 298 299 300 301 302 303 304
static void     schedule_idle_display_of_pending_files         (FMDirectoryView      *view);
static void     unschedule_display_of_pending_files            (FMDirectoryView      *view);
static void     disconnect_model_handlers                      (FMDirectoryView      *view);
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);
static void     fm_directory_view_trash_state_changed_callback (NautilusTrashMonitor *trash,
								gboolean              state,
								gpointer              callback_data);
static void     fm_directory_view_select_file                  (FMDirectoryView      *view,
								NautilusFile         *file);
Alexander Larsson's avatar
Alexander Larsson committed
305

306 307
static void     update_templates_directory                     (FMDirectoryView *view);
static void     user_dirs_changed                              (FMDirectoryView *view);
308

309 310
static gboolean file_list_all_are_folders                      (GList *file_list);

311
static void unschedule_pop_up_location_context_menu (FMDirectoryView *view);
312

313 314
G_DEFINE_TYPE (FMDirectoryView, fm_directory_view, GTK_TYPE_SCROLLED_WINDOW);
#define parent_class fm_directory_view_parent_class
315

Ramiro Estrugo's avatar
Ramiro Estrugo committed
316 317 318 319 320 321 322
EEL_IMPLEMENT_MUST_OVERRIDE_SIGNAL (fm_directory_view, add_file)
EEL_IMPLEMENT_MUST_OVERRIDE_SIGNAL (fm_directory_view, bump_zoom_level)
EEL_IMPLEMENT_MUST_OVERRIDE_SIGNAL (fm_directory_view, can_zoom_in)
EEL_IMPLEMENT_MUST_OVERRIDE_SIGNAL (fm_directory_view, can_zoom_out)
EEL_IMPLEMENT_MUST_OVERRIDE_SIGNAL (fm_directory_view, clear)
EEL_IMPLEMENT_MUST_OVERRIDE_SIGNAL (fm_directory_view, file_changed)
EEL_IMPLEMENT_MUST_OVERRIDE_SIGNAL (fm_directory_view, get_selection)
323
EEL_IMPLEMENT_MUST_OVERRIDE_SIGNAL (fm_directory_view, get_selection_for_file_transfer)
324
EEL_IMPLEMENT_MUST_OVERRIDE_SIGNAL (fm_directory_view, get_item_count)
Ramiro Estrugo's avatar
Ramiro Estrugo committed
325
EEL_IMPLEMENT_MUST_OVERRIDE_SIGNAL (fm_directory_view, is_empty)
326
EEL_IMPLEMENT_MUST_OVERRIDE_SIGNAL (fm_directory_view, reset_to_defaults)
Darin Adler's avatar
Darin Adler committed
327
EEL_IMPLEMENT_MUST_OVERRIDE_SIGNAL (fm_directory_view, restore_default_zoom_level)
Ramiro Estrugo's avatar
Ramiro Estrugo committed
328 329
EEL_IMPLEMENT_MUST_OVERRIDE_SIGNAL (fm_directory_view, select_all)
EEL_IMPLEMENT_MUST_OVERRIDE_SIGNAL (fm_directory_view, set_selection)
Darin Adler's avatar
Darin Adler committed
330
EEL_IMPLEMENT_MUST_OVERRIDE_SIGNAL (fm_directory_view, zoom_to_level)
Alexander Larsson's avatar
Alexander Larsson committed
331
EEL_IMPLEMENT_MUST_OVERRIDE_SIGNAL (fm_directory_view, get_zoom_level)
332
EEL_IMPLEMENT_MUST_OVERRIDE_SIGNAL (fm_directory_view, invert_selection)
333

334
/* virtual methods (public and non-public) */
335

336 337 338 339 340 341 342 343 344 345
/**
 * fm_directory_view_merge_menus:
 * 
 * Add this view's menus to the window's menu bar.
 * @view: FMDirectoryView in question.
 */
static void
fm_directory_view_merge_menus (FMDirectoryView *view)
{
	g_return_if_fail (FM_IS_DIRECTORY_VIEW (view));
346

347 348 349 350
	EEL_CALL_METHOD
		(FM_DIRECTORY_VIEW_CLASS, view,
		 merge_menus, (view));
}
351

352 353
static void
fm_directory_view_unmerge_menus (FMDirectoryView *view)
Alexander Larsson's avatar
Alexander Larsson committed
354
{
355
	g_return_if_fail (FM_IS_DIRECTORY_VIEW (view));
Alexander Larsson's avatar
Alexander Larsson committed
356

357 358 359 360
	EEL_CALL_METHOD
		(FM_DIRECTORY_VIEW_CLASS, view,
		 unmerge_menus, (view));
}
Alexander Larsson's avatar
Alexander Larsson committed
361

362 363 364 365 366 367 368 369 370 371
/**
 * fm_directory_view_select_all:
 *
 * select all the items in the view
 * 
 **/
static void
fm_directory_view_select_all (FMDirectoryView *view)
{
	g_return_if_fail (FM_IS_DIRECTORY_VIEW (view));
Alexander Larsson's avatar
Alexander Larsson committed
372

373 374 375
	EEL_CALL_METHOD
		(FM_DIRECTORY_VIEW_CLASS, view,
		 select_all, (view));
Alexander Larsson's avatar
Alexander Larsson committed
376 377
}

378 379 380 381 382 383 384
/**
 * fm_directory_view_set_selection:
 *
 * set the selection to the items identified in @selection. @selection
 * should be a list of NautilusFiles
 * 
 **/
Alexander Larsson's avatar
Alexander Larsson committed
385
static void
386
fm_directory_view_set_selection (FMDirectoryView *view, GList *selection)
Alexander Larsson's avatar
Alexander Larsson committed
387
{
388
	g_return_if_fail (FM_IS_DIRECTORY_VIEW (view));
389

390 391 392 393
	EEL_CALL_METHOD
		(FM_DIRECTORY_VIEW_CLASS, view,
		 set_selection, (view, selection));
}
394 395

static GList *
396
fm_directory_view_get_selection_for_file_transfer (FMDirectoryView *view)
397
{
398
	g_return_val_if_fail (FM_IS_DIRECTORY_VIEW (view), NULL);
399

400 401 402
	return EEL_CALL_METHOD_WITH_RETURN_VALUE
		(FM_DIRECTORY_VIEW_CLASS, view,
		 get_selection_for_file_transfer, (view));
403 404
}

405 406 407 408 409 410 411 412 413
/**
 * fm_directory_view_get_selected_icon_locations:
 *
 * return an array of locations of selected icons if available
 * Return value: GArray of GdkPoints
 * 
 **/
static GArray *
fm_directory_view_get_selected_icon_locations (FMDirectoryView *view)
414
{
415
	g_return_val_if_fail (FM_IS_DIRECTORY_VIEW (view), NULL);
416

417 418 419
	return EEL_CALL_METHOD_WITH_RETURN_VALUE
		(FM_DIRECTORY_VIEW_CLASS, view,
		 get_selected_icon_locations, (view));
420 421 422
}

static void
423
fm_directory_view_invert_selection (FMDirectoryView *view)
424
{
425 426 427 428 429
	g_return_if_fail (FM_IS_DIRECTORY_VIEW (view));

	EEL_CALL_METHOD
		(FM_DIRECTORY_VIEW_CLASS, view,
		 invert_selection, (view));
430 431
}

432 433 434 435 436 437 438 439 440 441 442 443 444 445
/**
 * fm_directory_view_reveal_selection:
 *
 * Scroll as necessary to reveal the selected items.
 **/
static void
fm_directory_view_reveal_selection (FMDirectoryView *view)
{
	g_return_if_fail (FM_IS_DIRECTORY_VIEW (view));

	EEL_CALL_METHOD
		(FM_DIRECTORY_VIEW_CLASS, view,
		 reveal_selection, (view));
}
446

447 448 449 450 451 452
/**
 * fm_directory_view_reset_to_defaults:
 *
 * set sorting order, zoom level, etc. to match defaults
 * 
 **/
453
static void
454
fm_directory_view_reset_to_defaults (FMDirectoryView *view)
455
{
456
	NautilusWindowShowHiddenFilesMode mode;
457

458 459 460 461 462 463 464 465 466
	g_return_if_fail (FM_IS_DIRECTORY_VIEW (view));
	
	EEL_CALL_METHOD
		(FM_DIRECTORY_VIEW_CLASS, view,
		 reset_to_defaults, (view));
	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,
							    NAUTILUS_WINDOW_SHOW_HIDDEN_FILES_DEFAULT);
467 468 469 470
	}
}

static gboolean
471
fm_directory_view_using_manual_layout (FMDirectoryView  *view)
472
{
473
	g_return_val_if_fail (FM_IS_DIRECTORY_VIEW (view), FALSE);
474

475 476 477
	return EEL_CALL_METHOD_WITH_RETURN_VALUE
		(FM_DIRECTORY_VIEW_CLASS, view,
		 using_manual_layout, (view));
478 479 480
}

static guint
481
fm_directory_view_get_item_count (FMDirectoryView *view)
482
{
483
	g_return_val_if_fail (FM_IS_DIRECTORY_VIEW (view), 0);
484

485 486 487
	return EEL_CALL_METHOD_WITH_RETURN_VALUE
		(FM_DIRECTORY_VIEW_CLASS, view,
		 get_item_count, (view));
488 489
}

490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505
/**
 * fm_directory_view_can_rename_file
 *
 * Determine whether a file can be renamed.
 * @file: A NautilusFile
 * 
 * Return value: TRUE if @file can be renamed, FALSE otherwise.
 * 
 **/
static gboolean
fm_directory_view_can_rename_file (FMDirectoryView *view, NautilusFile *file)
{
	return EEL_CALL_METHOD_WITH_RETURN_VALUE
		(FM_DIRECTORY_VIEW_CLASS, view,
		 can_rename_file, (view, file));
}
506

507 508 509 510
static gboolean
fm_directory_view_is_read_only (FMDirectoryView *view)
{
	g_return_val_if_fail (FM_IS_DIRECTORY_VIEW (view), FALSE);
511

512 513 514 515
	return EEL_CALL_METHOD_WITH_RETURN_VALUE
		(FM_DIRECTORY_VIEW_CLASS, view,
		 is_read_only, (view));
}
516

517 518
static gboolean
fm_directory_view_supports_creating_files (FMDirectoryView *view)
519
{
520
	g_return_val_if_fail (FM_IS_DIRECTORY_VIEW (view), FALSE);
521

522 523 524
	return EEL_CALL_METHOD_WITH_RETURN_VALUE
		(FM_DIRECTORY_VIEW_CLASS, view,
		 supports_creating_files, (view));
525 526
}

527 528
static gboolean
fm_directory_view_is_empty (FMDirectoryView *view)
529
{
530
	g_return_val_if_fail (FM_IS_DIRECTORY_VIEW (view), FALSE);
531

532 533 534
	return EEL_CALL_METHOD_WITH_RETURN_VALUE
		(FM_DIRECTORY_VIEW_CLASS, view,
		 is_empty, (view));
535 536
}

537 538 539 540 541 542 543 544 545
/**
 * 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)
546
{
547
	g_return_if_fail (FM_IS_DIRECTORY_VIEW (view));
548

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

553 554 555
	EEL_CALL_METHOD
		(FM_DIRECTORY_VIEW_CLASS, view,
		 bump_zoom_level, (view, zoom_increment));
Alexander Larsson's avatar
Alexander Larsson committed
556 557
}

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

570 571 572 573 574 575 576
	if (!nautilus_view_supports_zooming (view)) {
		return;
	}

	EEL_CALL_METHOD
		(FM_DIRECTORY_VIEW_CLASS, view,
		 zoom_to_level, (view, zoom_level));
577
}
Alexander Larsson's avatar
Alexander Larsson committed
578

579 580
NautilusZoomLevel
nautilus_view_get_zoom_level (FMDirectoryView *view)
581
{
582
	g_return_val_if_fail (FM_IS_DIRECTORY_VIEW (view), NAUTILUS_ZOOM_LEVEL_STANDARD);
583

584 585
	if (!nautilus_view_supports_zooming (view)) {
		return NAUTILUS_ZOOM_LEVEL_STANDARD;
586 587
	}

588 589 590
	return EEL_CALL_METHOD_WITH_RETURN_VALUE
		(FM_DIRECTORY_VIEW_CLASS, view,
		 get_zoom_level, (view));
591 592
}

593 594 595 596 597 598 599 600 601 602 603
/**
 * nautilus_view_can_zoom_in:
 *
 * Determine whether the view can be zoomed any closer.
 * @view: The zoomable FMDirectoryView.
 * 
 * Return value: TRUE if @view can be zoomed any closer, FALSE otherwise.
 * 
 **/
gboolean
nautilus_view_can_zoom_in (NautilusView *view)
604
{
605
	g_return_val_if_fail (FM_IS_DIRECTORY_VIEW (view), FALSE);
606

607 608
	if (!nautilus_view_supports_zooming (view)) {
		return FALSE;
609
	}
610

611 612 613
	return EEL_CALL_METHOD_WITH_RETURN_VALUE
		(FM_DIRECTORY_VIEW_CLASS, view,
		 can_zoom_in, (view));
614 615
}

616 617 618 619 620 621 622 623 624 625 626
/**
 * nautilus_view_can_zoom_out:
 *
 * Determine whether the view can be zoomed any further away.
 * @view: The zoomable FMDirectoryView.
 * 
 * Return value: TRUE if @view can be zoomed any further away, FALSE otherwise.
 * 
 **/
gboolean
nautilus_view_can_zoom_out (NautilusView *view)
627
{
628
	g_return_val_if_fail (FM_IS_DIRECTORY_VIEW (view), FALSE);
629

630 631
	if (!nautilus_view_supports_zooming (view)) {
		return FALSE;
632 633
	}

634 635 636
	return EEL_CALL_METHOD_WITH_RETURN_VALUE
		(FM_DIRECTORY_VIEW_CLASS, view,
		 can_zoom_out, (view));
637 638
}

639 640
gboolean
nautilus_view_supports_zooming (FMDirectoryView *view)
641
{
642
	g_return_val_if_fail (FM_IS_DIRECTORY_VIEW (view), FALSE);
643

644 645 646
	return EEL_CALL_METHOD_WITH_RETURN_VALUE
		(FM_DIRECTORY_VIEW_CLASS, view,
		 supports_zooming, (view));
647 648
}

649 650 651 652 653 654 655 656
/**
 * 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
657
{
658 659 660 661
	g_return_if_fail (FM_IS_DIRECTORY_VIEW (view));

	if (!nautilus_view_supports_zooming (view)) {
		return;
Alexander Larsson's avatar
Alexander Larsson committed
662
	}
663 664 665 666

	EEL_CALL_METHOD
		(FM_DIRECTORY_VIEW_CLASS, view,
		 restore_default_zoom_level, (view));
Alexander Larsson's avatar
Alexander Larsson committed
667 668
}

669 670
const char *
nautilus_view_get_view_id (NautilusView *view)
Alexander Larsson's avatar
Alexander Larsson committed
671
{
672 673 674 675
	return EEL_CALL_METHOD_WITH_RETURN_VALUE
		(FM_DIRECTORY_VIEW_CLASS, view,
		 get_view_id, (view));
}
Alexander Larsson's avatar
Alexander Larsson committed
676

677 678 679 680 681 682
char *
nautilus_view_get_first_visible_file (NautilusView *view)
{
	return EEL_CALL_METHOD_WITH_RETURN_VALUE
		(FM_DIRECTORY_VIEW_CLASS, view,
		 get_first_visible_file, (view));
Alexander Larsson's avatar
Alexander Larsson committed
683 684 685
}

void
686 687
nautilus_view_scroll_to_file (NautilusView *view,
			      const char *uri)
Alexander Larsson's avatar
Alexander Larsson committed
688
{
689 690 691 692
	EEL_CALL_METHOD
		(FM_DIRECTORY_VIEW_CLASS, view,
		 scroll_to_file, (view, uri));
}
Alexander Larsson's avatar
Alexander Larsson committed
693

694
char **
695
nautilus_view_get_emblem_names_to_exclude (NautilusView *view)
696
{
697 698 699 700
	char **excludes;
	int i;
	
	g_assert (FM_IS_DIRECTORY_VIEW (view));
Alexander Larsson's avatar
Alexander Larsson committed
701

702 703 704 705 706 707 708 709 710 711 712 713
	excludes = g_new (char *, 3);
	
	i = 0;
	excludes[i++] = g_strdup (NAUTILUS_FILE_EMBLEM_NAME_TRASH);

	if (!nautilus_file_can_write (view->details->directory_as_file)) {
		excludes[i++] = g_strdup (NAUTILUS_FILE_EMBLEM_NAME_CANT_WRITE);
	}

	excludes[i++] = NULL;

	return excludes;
Alexander Larsson's avatar
Alexander Larsson committed
714 715
}

716 717 718
void
nautilus_view_set_is_active (FMDirectoryView *view,
			     gboolean is_active)
719
{
720
	g_return_if_fail (FM_IS_DIRECTORY_VIEW (view));
721

722 723
	EEL_CALL_METHOD (FM_DIRECTORY_VIEW_CLASS, view,
			 set_is_active, (view, is_active));
724 725
}

726
/**
727
 * nautilus_view_get_selection:
728 729 730 731 732 733 734 735 736 737 738
 *
 * 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).
 * @view: FMDirectoryView whose selected items are of interest.
 * 
 * Return value: GList of NautilusFile pointers representing the selection.
 * 
 **/
GList *
739
nautilus_view_get_selection (NautilusView *view)
740
{
741
	g_return_val_if_fail (NAUTILUS_IS_VIEW (view), NULL);
742

743
	return EEL_CALL_METHOD_WITH_RETURN_VALUE
744
		(NAUTILUS_VIEW_CLASS, view,
745
		 get_selection, (view));
746 747 748
}


749 750 751 752 753 754 755 756
/**
 * nautilus_view_update_menus:
 * 
 * Update the sensitivity and wording of dynamic menu items.
 * @view: FMDirectoryView in question.
 */
void
nautilus_view_update_menus (NautilusView *view)
757
{
758
	g_return_if_fail (FM_IS_DIRECTORY_VIEW (view));
Alexander Larsson's avatar
Alexander Larsson committed
759

760 761
	if (!view->details->active) {
		return;
762
	}
763 764


765 766 767
	EEL_CALL_METHOD
		(FM_DIRECTORY_VIEW_CLASS, view,
		 update_menus, (view));
768

769 770
	view->details->menu_states_untrustworthy = FALSE;
}
771

772 773 774 775 776
typedef struct {
	GAppInfo *application;
	GList *files;
	FMDirectoryView *directory_view;
} ApplicationLaunchParameters;
777

778 779 780 781
typedef struct {
	NautilusFile *file;
	FMDirectoryView *directory_view;
} ScriptLaunchParameters;
782

783 784 785 786
typedef struct {
	NautilusFile *file;
	FMDirectoryView *directory_view;
} CreateTemplateParameters;
787

788 789 790 791
static ApplicationLaunchParameters *
application_launch_parameters_new (GAppInfo *application,
			      	   GList *files,
			           FMDirectoryView *directory_view)
792
{
793
	ApplicationLaunchParameters *result;
794

795 796 797
	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
798

799 800 801
	if (directory_view != NULL) {
		g_object_ref (directory_view);
		result->directory_view = directory_view;
802 803
	}

804
	return result;
805 806
}

807
static void
808
application_launch_parameters_free (ApplicationLaunchParameters *parameters)
809
{
810 811
	g_object_unref (parameters->application);
	nautilus_file_list_free (parameters->files);
812

813 814
	if (parameters->directory_view != NULL) {
		g_object_unref (parameters->directory_view);
815 816
	}

817 818
	g_free (parameters);
}			      
819

820 821
static GList *
file_and_directory_list_to_files (GList *fad_list)
822
{
823 824
	GList *res, *l;
	FileAndDirectory *fad;
825

826 827 828 829 830 831 832
	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);
}
833 834


835 836 837 838 839
static GList *
file_and_directory_list_from_files (NautilusDirectory *directory, GList *files)
{
	GList *res, *l;
	FileAndDirectory *fad;
840

841 842 843 844 845 846 847 848
	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);
849 850 851
}

static void
852
file_and_directory_free (FileAndDirectory *fad)
853
{
854 855 856 857
	nautilus_directory_unref (fad->directory);
	nautilus_file_unref (fad->file);
	g_free (fad);
}
858 859


860 861 862 863
static void
file_and_directory_list_free (GList *list)
{
	GList *l;
864

865 866
	for (l = list; l != NULL; l = l->next) {
		file_and_directory_free (l->data);
867
	}
868

869
	g_list_free (list);
870 871
}

872 873 874
static gboolean
file_and_directory_equal (gconstpointer  v1,
			  gconstpointer  v2)
875
{
876 877 878
	const FileAndDirectory *fad1, *fad2;
	fad1 = v1;
	fad2 = v2;
879

880 881
	return (fad1->file == fad2->file &&
		fad1->directory == fad2->directory);
882 883
}

884 885
static guint
file_and_directory_hash  (gconstpointer  v)
886
{
887
	const FileAndDirectory *fad;
888

889 890
	fad = v;
	return GPOINTER_TO_UINT (fad->file) ^ GPOINTER_TO_UINT (fad->directory);
891 892
}

893

Alexander Larsson's avatar
Alexander Larsson committed
894

895 896 897 898

static ScriptLaunchParameters *
script_launch_parameters_new (NautilusFile *file,
			      FMDirectoryView *directory_view)
899
{
900 901 902 903 904 905 906 907 908
	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;
909 910
}

911
static void
912
script_launch_parameters_free (ScriptLaunchParameters *parameters)
913
{
914 915 916 917
	g_object_unref (parameters->directory_view);
	nautilus_file_unref (parameters->file);
	g_free (parameters);
}			      
918

919 920 921 922 923
static CreateTemplateParameters *
create_template_parameters_new (NautilusFile *file,
				FMDirectoryView *directory_view)
{
	CreateTemplateParameters *result;
924

925 926 927 928 929
	result = g_new0 (CreateTemplateParameters, 1);
	g_object_ref (directory_view);
	result->directory_view = directory_view;
	nautilus_file_ref (file);
	result->file = file;
930

931
	return result;
932 933
}

934
static void
935
create_templates_parameters_free (CreateTemplateParameters *parameters)
936
{
937 938 939 940 941 942 943 944 945 946 947
	g_object_unref (parameters->directory_view);
	nautilus_file_unref (parameters->file);
	g_free (parameters);
}			      

NautilusWindow *
fm_directory_view_get_nautilus_window (FMDirectoryView  *view)
{
	g_assert (view->details->window != NULL);

	return view->details->window;
948 949
}

950 951
NautilusWindowSlot *
fm_directory_view_get_nautilus_window_slot (FMDirectoryView  *view)
952
{
953
	g_assert (view->details->slot != NULL);
954

955 956
	return view->details->slot;
}
957

958 959 960 961 962 963 964 965
/* 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 *
fm_directory_view_get_containing_window (FMDirectoryView *view)
{
	GtkWidget *window;
966

967 968 969 970 971 972
	g_assert (FM_IS_DIRECTORY_VIEW (view));
	
	window = gtk_widget_get_ancestor (GTK_WIDGET (view), GTK_TYPE_WINDOW);
	if (window == NULL) {
		return NULL;
	}
973

974
	return GTK_WINDOW (window);
975 976
}

Alexander Larsson's avatar
Alexander Larsson committed
977
static gboolean
978 979 980
fm_directory_view_confirm_multiple (GtkWindow *parent_window,
				    int count,
				    gboolean tabs)
Alexander Larsson's avatar
Alexander Larsson committed
981
{
982 983 984 985
	GtkDialog *dialog;
	char *prompt;
	char *detail;
	int response;
986

987
	if (count <= SILENT_WINDOW_OPEN_LIMIT) {
988
		return TRUE;
Alexander Larsson's avatar
Alexander Larsson committed
989 990
	}

991 992 993 994 995 996 997
	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);
998
	}
999 1000 1001 1002
	dialog = eel_show_yes_no_dialog (prompt, detail, 
					 GTK_STOCK_OK, GTK_STOCK_CANCEL,
					 parent_window);
	g_free (detail);
1003

1004 1005 1006 1007
	response = gtk_dialog_run (dialog);
	gtk_widget_destroy (GTK_WIDGET (dialog));

	return response == GTK_RESPONSE_YES;
1008 1009
}

1010 1011
static gboolean
selection_contains_one_item_in_menu_callback (FMDirectoryView *view, GList *selection)
1012
{
1013 1014 1015
	if (eel_g_list_exactly_one_item (selection)) {
		return TRUE;
	}
1016

1017 1018 1019 1020 1021 1022 1023
	/* If we've requested a menu update that hasn't yet occurred, then
	 * the mismatch here doesn't surprise us, and we won't complain.
	 * Otherwise, we will complain.
	 */
	if (!view->details->menu_states_untrustworthy) {
		g_warning ("Expected one selected item, found %'d. No action will be performed.", 	
			   g_list_length (selection));
1024
	}
1025

1026
	return FALSE;
1027 1028
}

1029 1030
static gboolean
selection_not_empty_in_menu_callback (FMDirectoryView *view, GList *selection)
1031
{
1032 1033 1034
	if (selection != NULL) {
		return TRUE;
	}
1035

1036 1037 1038 1039 1040 1041 1042 1043 1044
	/* If we've requested a menu update that hasn't yet occurred, then
	 * the mismatch here doesn't surprise us, and we won't complain.
	 * Otherwise, we will complain.
	 */
	if (!view->details->menu_states_untrustworthy) {
		g_warning ("Empty selection found when selection was expected. No action will be performed.");
	}

	return FALSE;
1045 1046
}

1047 1048
static char *
get_view_directory (FMDirectoryView *view)
1049
{
1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064
	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;
1065 1066
}

1067 1068 1069 1070 1071 1072
void
fm_directory_view_activate_files (FMDirectoryView *view,
				  GList *files,
				  NautilusWindowOpenMode mode,
				  NautilusWindowOpenFlags flags,
				  gboolean confirm_multiple)
1073
{
1074
	char *path;
1075

1076 1077 1078 1079 1080 1081 1082 1083
	path = get_view_directory (view);
	nautilus_mime_activate_files (fm_directory_view_get_containing_window (view),
				      view->details->slot,
				      files,
				      path,
				      mode,
				      flags,
				      confirm_multiple);
1084

1085
	g_free (path);
1086 1087 1088
}

static void
1089 1090 1091 1092
fm_directory_view_activate_file (FMDirectoryView *view,
				 NautilusFile *file,
				 NautilusWindowOpenMode mode,
				 NautilusWindowOpenFlags flags)
1093
{
1094
	char *path;
1095

1096 1097 1098 1099 1100 1101 1102
	path = get_view_directory (view);
	nautilus_mime_activate_file (fm_directory_view_get_containing_window (view),
				     view->details->slot,
				     file,
				     path,
				     mode,
				     flags);
1103

1104
	g_free (path);
1105 1106 1107
}

static void