nautilus-view.c 286 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_supports_properties (FMDirectoryView *view)
529
{
530
	g_return_val_if_fail (FM_IS_DIRECTORY_VIEW (view), FALSE);
531

532 533 534 535 536 537 538
	return EEL_CALL_METHOD_WITH_RETURN_VALUE
		(FM_DIRECTORY_VIEW_CLASS, view,
		 supports_properties, (view));
}

static gboolean
fm_directory_view_is_empty (FMDirectoryView *view)
539
{
540
	g_return_val_if_fail (FM_IS_DIRECTORY_VIEW (view), FALSE);
541

542 543 544
	return EEL_CALL_METHOD_WITH_RETURN_VALUE
		(FM_DIRECTORY_VIEW_CLASS, view,
		 is_empty, (view));
545 546
}

547 548 549 550 551 552 553 554 555
/**
 * 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)
556
{
557
	g_return_if_fail (FM_IS_DIRECTORY_VIEW (view));
558

559 560 561
	if (!nautilus_view_supports_zooming (view)) {
		return;
	}
Alexander Larsson's avatar
Alexander Larsson committed
562

563 564 565
	EEL_CALL_METHOD
		(FM_DIRECTORY_VIEW_CLASS, view,
		 bump_zoom_level, (view, zoom_increment));
Alexander Larsson's avatar
Alexander Larsson committed
566 567
}

568 569 570 571 572 573 574 575 576
/**
 * 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)
577
{
578
	g_return_if_fail (FM_IS_DIRECTORY_VIEW (view));
579

580 581 582 583 584 585 586
	if (!nautilus_view_supports_zooming (view)) {
		return;
	}

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

589 590
NautilusZoomLevel
nautilus_view_get_zoom_level (FMDirectoryView *view)
591
{
592
	g_return_val_if_fail (FM_IS_DIRECTORY_VIEW (view), NAUTILUS_ZOOM_LEVEL_STANDARD);
593

594 595
	if (!nautilus_view_supports_zooming (view)) {
		return NAUTILUS_ZOOM_LEVEL_STANDARD;
596 597
	}

598 599 600
	return EEL_CALL_METHOD_WITH_RETURN_VALUE
		(FM_DIRECTORY_VIEW_CLASS, view,
		 get_zoom_level, (view));
601 602
}

603 604 605 606 607 608 609 610 611 612 613
/**
 * 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)
614
{
615
	g_return_val_if_fail (FM_IS_DIRECTORY_VIEW (view), FALSE);
616

617 618
	if (!nautilus_view_supports_zooming (view)) {
		return FALSE;
619
	}
620

621 622 623
	return EEL_CALL_METHOD_WITH_RETURN_VALUE
		(FM_DIRECTORY_VIEW_CLASS, view,
		 can_zoom_in, (view));
624 625
}

626 627 628 629 630 631 632 633 634 635 636
/**
 * 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)
637
{
638
	g_return_val_if_fail (FM_IS_DIRECTORY_VIEW (view), FALSE);
639

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

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

649 650
gboolean
nautilus_view_supports_zooming (FMDirectoryView *view)
651
{
652
	g_return_val_if_fail (FM_IS_DIRECTORY_VIEW (view), FALSE);
653

654 655 656
	return EEL_CALL_METHOD_WITH_RETURN_VALUE
		(FM_DIRECTORY_VIEW_CLASS, view,
		 supports_zooming, (view));
657 658
}

659 660 661 662 663 664 665 666
/**
 * 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
667
{
668 669 670 671
	g_return_if_fail (FM_IS_DIRECTORY_VIEW (view));

	if (!nautilus_view_supports_zooming (view)) {
		return;
Alexander Larsson's avatar
Alexander Larsson committed
672
	}
673 674 675 676

	EEL_CALL_METHOD
		(FM_DIRECTORY_VIEW_CLASS, view,
		 restore_default_zoom_level, (view));
Alexander Larsson's avatar
Alexander Larsson committed
677 678
}

679 680
const char *
nautilus_view_get_view_id (NautilusView *view)
Alexander Larsson's avatar
Alexander Larsson committed
681
{
682 683 684 685
	return EEL_CALL_METHOD_WITH_RETURN_VALUE
		(FM_DIRECTORY_VIEW_CLASS, view,
		 get_view_id, (view));
}
Alexander Larsson's avatar
Alexander Larsson committed
686

687 688 689 690 691 692
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
693 694 695
}

void
696 697
nautilus_view_scroll_to_file (NautilusView *view,
			      const char *uri)
Alexander Larsson's avatar
Alexander Larsson committed
698
{
699 700 701 702
	EEL_CALL_METHOD
		(FM_DIRECTORY_VIEW_CLASS, view,
		 scroll_to_file, (view, uri));
}
Alexander Larsson's avatar
Alexander Larsson committed
703

704 705 706 707
char **
fm_directory_view_get_emblem_names_to_exclude (FMDirectoryView *view)
{
	g_return_val_if_fail (FM_IS_DIRECTORY_VIEW (view), NULL);
Alexander Larsson's avatar
Alexander Larsson committed
708

709 710 711
	return EEL_CALL_METHOD_WITH_RETURN_VALUE
		(FM_DIRECTORY_VIEW_CLASS, view,
		 get_emblem_names_to_exclude, (view));
Alexander Larsson's avatar
Alexander Larsson committed
712 713
}

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

720 721
	EEL_CALL_METHOD (FM_DIRECTORY_VIEW_CLASS, view,
			 set_is_active, (view, is_active));
722 723
}

724
/**
725
 * nautilus_view_get_selection:
726 727 728 729 730 731 732 733 734 735 736
 *
 * 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 *
737
nautilus_view_get_selection (NautilusView *view)
738
{
739
	g_return_val_if_fail (NAUTILUS_IS_VIEW (view), NULL);
740

741
	return EEL_CALL_METHOD_WITH_RETURN_VALUE
742
		(NAUTILUS_VIEW_CLASS, view,
743
		 get_selection, (view));
744 745 746
}


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

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


763 764 765
	EEL_CALL_METHOD
		(FM_DIRECTORY_VIEW_CLASS, view,
		 update_menus, (view));
766

767 768
	view->details->menu_states_untrustworthy = FALSE;
}
769

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

776 777 778 779
typedef struct {
	NautilusFile *file;
	FMDirectoryView *directory_view;
} ScriptLaunchParameters;
780

781 782 783 784
typedef struct {
	NautilusFile *file;
	FMDirectoryView *directory_view;
} CreateTemplateParameters;
785

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

793 794 795
	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
796

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

802
	return result;
803 804
}

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

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

815 816
	g_free (parameters);
}			      
817

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

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


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

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

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


858 859 860 861
static void
file_and_directory_list_free (GList *list)
{
	GList *l;
862

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

867
	g_list_free (list);
868 869
}

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

878 879
	return (fad1->file == fad2->file &&
		fad1->directory == fad2->directory);
880 881
}

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

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

891

Alexander Larsson's avatar
Alexander Larsson committed
892

893 894 895 896

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

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

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

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

929
	return result;
930 931
}

932
static void
933
create_templates_parameters_free (CreateTemplateParameters *parameters)
934
{
935 936 937 938 939 940 941 942 943 944 945
	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;
946 947
}

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

953 954
	return view->details->slot;
}
955

956 957 958 959 960 961 962 963
/* 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;
964

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

972
	return GTK_WINDOW (window);
973 974
}

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

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

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

1002 1003 1004 1005
	response = gtk_dialog_run (dialog);
	gtk_widget_destroy (GTK_WIDGET (dialog));

	return response == GTK_RESPONSE_YES;
1006 1007
}

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

1015 1016 1017 1018 1019 1020 1021
	/* 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));
1022
	}
1023

1024
	return FALSE;
1025 1026
}

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

1034 1035 1036 1037 1038 1039 1040 1041 1042
	/* 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;
1043 1044
}

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

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

1074 1075 1076 1077 1078 1079 1080 1081
	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);
1082

1083
	g_free (path);
1084 1085 1086
}

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

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

1102
	g_free (path);
1103