nautilus-view.c 309 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
	END_FILE_CHANGES,
133
	FLUSH_ADDED_FILES,
134
	END_LOADING,
135
	FILE_CHANGED,
136
	LOAD_ERROR,
Pavel Cisler's avatar
Pavel Cisler committed
137
	MOVE_COPY_ITEMS,
138
	REMOVE_FILE,
139
	ZOOM_LEVEL_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_locations_selected;
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 255
	gboolean allow_moves;

256
	GdkPoint context_menu_position;
257
};
Ettore Perazzoli's avatar
Ettore Perazzoli committed
258

259 260 261 262 263
typedef struct {
	NautilusFile *file;
	NautilusDirectory *directory;
} FileAndDirectory;

264
/* forward declarations */
265

266
static gboolean display_selection_info_idle_callback           (gpointer              data);
Darin Adler's avatar
Darin Adler committed
267 268
static void     fm_directory_view_class_init                   (FMDirectoryViewClass *klass);
static void     fm_directory_view_init                         (FMDirectoryView      *view);
269 270 271 272 273 274
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
275 276
static void     trash_or_delete_files                          (GtkWindow            *parent_window,
								const GList          *files,
277 278
								gboolean              delete_if_all_already_in_trash,
								FMDirectoryView      *view);
279 280 281
static void     load_directory                                 (FMDirectoryView      *view,
								NautilusDirectory    *directory);
static void     fm_directory_view_merge_menus                  (FMDirectoryView      *view);
282
static void     fm_directory_view_unmerge_menus                (FMDirectoryView      *view);
283
static void     fm_directory_view_init_show_hidden_files       (FMDirectoryView      *view);
284 285
static void     clipboard_changed_callback                     (NautilusClipboardMonitor *monitor,
								FMDirectoryView      *view);
286 287
static void     open_one_in_new_window                         (gpointer              data,
								gpointer              callback_data);
288 289
static void     open_one_in_folder_window                      (gpointer              data,
								gpointer              callback_data);
290 291 292
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);
293 294
static void     schedule_update_status                          (FMDirectoryView      *view);
static void     remove_update_status_idle_callback             (FMDirectoryView *view); 
295
static void     reset_update_interval                          (FMDirectoryView      *view);
296 297 298 299 300 301 302 303 304 305 306 307 308
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
309

310
static GdkDragAction ask_link_action                           (FMDirectoryView      *view);
311 312
static void     update_templates_directory                     (FMDirectoryView *view);
static void     user_dirs_changed                              (FMDirectoryView *view);
313

314 315
static gboolean file_list_all_are_folders                      (GList *file_list);

Alexander Larsson's avatar
Alexander Larsson committed
316 317 318 319 320 321 322 323
static void action_open_scripts_folder_callback    (GtkAction *action,
						    gpointer   callback_data);
static void action_cut_files_callback              (GtkAction *action,
						    gpointer   callback_data);
static void action_copy_files_callback             (GtkAction *action,
						    gpointer   callback_data);
static void action_paste_files_callback            (GtkAction *action,
						    gpointer   callback_data);
324 325 326 327
static void action_copy_to_next_pane_callback      (GtkAction *action,
						    gpointer   callback_data);
static void action_move_to_next_pane_callback      (GtkAction *action,
						    gpointer   callback_data);
Alexander Larsson's avatar
Alexander Larsson committed
328 329
static void action_rename_callback                 (GtkAction *action,
						    gpointer   callback_data);
330 331
static void action_rename_select_all_callback      (GtkAction *action,
						    gpointer   callback_data);
Alexander Larsson's avatar
Alexander Larsson committed
332 333 334 335 336 337 338 339
static void action_paste_files_into_callback       (GtkAction *action,
						    gpointer   callback_data);
static void action_connect_to_server_link_callback (GtkAction *action,
						    gpointer   data);
static void action_mount_volume_callback           (GtkAction *action,
						    gpointer   data);
static void action_unmount_volume_callback         (GtkAction *action,
						    gpointer   data);
340 341
static void action_format_volume_callback          (GtkAction *action,
						    gpointer   data);
342 343 344 345
static void action_start_volume_callback           (GtkAction *action,
						    gpointer   data);
static void action_stop_volume_callback            (GtkAction *action,
						    gpointer   data);
346 347
static void action_detect_media_callback           (GtkAction *action,
						    gpointer   data);
Alexander Larsson's avatar
Alexander Larsson committed
348

349 350 351 352
/* location popup-related actions */

static void action_location_open_alternate_callback (GtkAction *action,
						     gpointer   callback_data);
353 354
static void action_location_open_folder_window_callback (GtkAction *action,
							 gpointer   callback_data);
355 356 357 358 359 360 361 362 363

static void action_location_cut_callback            (GtkAction *action,
						     gpointer   callback_data);
static void action_location_copy_callback           (GtkAction *action,
						     gpointer   callback_data);
static void action_location_trash_callback          (GtkAction *action,
						     gpointer   callback_data);
static void action_location_delete_callback         (GtkAction *action,
						     gpointer   callback_data);
364 365 366 367
static void action_location_properties_callback     (GtkAction *action,
						     gpointer   callback_data);

static void unschedule_pop_up_location_context_menu (FMDirectoryView *view);
368

369 370 371 372 373
static inline void fm_directory_view_widget_to_file_operation_position (FMDirectoryView *view,
									GdkPoint *position); 
static void        fm_directory_view_widget_to_file_operation_position_xy (FMDirectoryView *view,
									   int *x, int *y);

374
EEL_CLASS_BOILERPLATE (FMDirectoryView, fm_directory_view, GTK_TYPE_SCROLLED_WINDOW)
375

Ramiro Estrugo's avatar
Ramiro Estrugo committed
376 377 378 379 380 381 382
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)
383
EEL_IMPLEMENT_MUST_OVERRIDE_SIGNAL (fm_directory_view, get_selection_for_file_transfer)
384
EEL_IMPLEMENT_MUST_OVERRIDE_SIGNAL (fm_directory_view, get_item_count)
Ramiro Estrugo's avatar
Ramiro Estrugo committed
385
EEL_IMPLEMENT_MUST_OVERRIDE_SIGNAL (fm_directory_view, is_empty)
386
EEL_IMPLEMENT_MUST_OVERRIDE_SIGNAL (fm_directory_view, reset_to_defaults)
Darin Adler's avatar
Darin Adler committed
387
EEL_IMPLEMENT_MUST_OVERRIDE_SIGNAL (fm_directory_view, restore_default_zoom_level)
Ramiro Estrugo's avatar
Ramiro Estrugo committed
388 389
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
390
EEL_IMPLEMENT_MUST_OVERRIDE_SIGNAL (fm_directory_view, zoom_to_level)
Alexander Larsson's avatar
Alexander Larsson committed
391
EEL_IMPLEMENT_MUST_OVERRIDE_SIGNAL (fm_directory_view, get_zoom_level)
392
EEL_IMPLEMENT_MUST_OVERRIDE_SIGNAL (fm_directory_view, invert_selection)
393

394
typedef struct {
Alexander Larsson's avatar
Alexander Larsson committed
395
	GAppInfo *application;
396
	GList *files;
397 398 399
	FMDirectoryView *directory_view;
} ApplicationLaunchParameters;

400 401 402 403 404
typedef struct {
	NautilusFile *file;
	FMDirectoryView *directory_view;
} ScriptLaunchParameters;

405 406 407 408 409
typedef struct {
	NautilusFile *file;
	FMDirectoryView *directory_view;
} CreateTemplateParameters;

Alexander Larsson's avatar
Alexander Larsson committed
410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437
static ApplicationLaunchParameters *
application_launch_parameters_new (GAppInfo *application,
			      	   GList *files,
			           FMDirectoryView *directory_view)
{
	ApplicationLaunchParameters *result;

	result = g_new0 (ApplicationLaunchParameters, 1);
	result->application = g_object_ref (application);
	result->files = nautilus_file_list_copy (files);

	if (directory_view != NULL) {
		g_object_ref (directory_view);
		result->directory_view = directory_view;
	}

	return result;
}

static void
application_launch_parameters_free (ApplicationLaunchParameters *parameters)
{
	g_object_unref (parameters->application);
	nautilus_file_list_free (parameters->files);

	if (parameters->directory_view != NULL) {
		g_object_unref (parameters->directory_view);
	}
438

Alexander Larsson's avatar
Alexander Larsson committed
439 440
	g_free (parameters);
}			      
441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489

static GList *
file_and_directory_list_to_files (GList *fad_list)
{
	GList *res, *l;
	FileAndDirectory *fad;

	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);
}


static GList *
file_and_directory_list_from_files (NautilusDirectory *directory, GList *files)
{
	GList *res, *l;
	FileAndDirectory *fad;

	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);
}

static void
file_and_directory_free (FileAndDirectory *fad)
{
	nautilus_directory_unref (fad->directory);
	nautilus_file_unref (fad->file);
	g_free (fad);
}


static void
file_and_directory_list_free (GList *list)
{
	GList *l;

	for (l = list; l != NULL; l = l->next) {
		file_and_directory_free (l->data);
	}
490 491

	g_list_free (list);
492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515
}

static gboolean
file_and_directory_equal (gconstpointer  v1,
			  gconstpointer  v2)
{
	const FileAndDirectory *fad1, *fad2;
	fad1 = v1;
	fad2 = v2;

	return (fad1->file == fad2->file &&
		fad1->directory == fad2->directory);
}

static guint
file_and_directory_hash  (gconstpointer  v)
{
	const FileAndDirectory *fad;

	fad = v;
	return GPOINTER_TO_UINT (fad->file) ^ GPOINTER_TO_UINT (fad->directory);
}


516 517


518 519 520 521 522 523 524
static ScriptLaunchParameters *
script_launch_parameters_new (NautilusFile *file,
			      FMDirectoryView *directory_view)
{
	ScriptLaunchParameters *result;

	result = g_new0 (ScriptLaunchParameters, 1);
Darin Adler's avatar
Darin Adler committed
525
	g_object_ref (directory_view);
526 527 528 529 530 531 532 533 534 535
	result->directory_view = directory_view;
	nautilus_file_ref (file);
	result->file = file;

	return result;
}

static void
script_launch_parameters_free (ScriptLaunchParameters *parameters)
{
Darin Adler's avatar
Darin Adler committed
536
	g_object_unref (parameters->directory_view);
537 538 539 540
	nautilus_file_unref (parameters->file);
	g_free (parameters);
}			      

541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563
static CreateTemplateParameters *
create_template_parameters_new (NautilusFile *file,
				FMDirectoryView *directory_view)
{
	CreateTemplateParameters *result;

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

	return result;
}

static void
create_templates_parameters_free (CreateTemplateParameters *parameters)
{
	g_object_unref (parameters->directory_view);
	nautilus_file_unref (parameters->file);
	g_free (parameters);
}			      

564
NautilusWindow *
Alexander Larsson's avatar
Alexander Larsson committed
565 566 567 568 569 570 571
fm_directory_view_get_nautilus_window (FMDirectoryView  *view)
{
	g_assert (view->details->window != NULL);

	return view->details->window;
}

572
NautilusWindowSlot *
573 574 575 576 577 578
fm_directory_view_get_nautilus_window_slot (FMDirectoryView  *view)
{
	g_assert (view->details->slot != NULL);

	return view->details->slot;
}
Alexander Larsson's avatar
Alexander Larsson committed
579

580 581 582 583 584 585
/* 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.
 */
GtkWindow *
fm_directory_view_get_containing_window (FMDirectoryView *view)
586
{
587 588
	GtkWidget *window;

589 590
	g_assert (FM_IS_DIRECTORY_VIEW (view));
	
591 592 593 594 595 596
	window = gtk_widget_get_ancestor (GTK_WIDGET (view), GTK_TYPE_WINDOW);
	if (window == NULL) {
		return NULL;
	}

	return GTK_WINDOW (window);
597 598
}

599 600 601 602
static gboolean
fm_directory_view_confirm_multiple (GtkWindow *parent_window,
				    int count,
				    gboolean tabs)
603
{
604
	GtkDialog *dialog;
605
	char *prompt;
606
	char *detail;
607
	int response;
608 609 610 611 612

	if (count <= SILENT_WINDOW_OPEN_LIMIT) {
		return TRUE;
	}

613
	prompt = _("Are you sure you want to open all files?");
614 615 616 617 618 619 620
	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);
	}
621
	dialog = eel_show_yes_no_dialog (prompt, detail, 
622
					 GTK_STOCK_OK, GTK_STOCK_CANCEL,
Alexander Larsson's avatar
Alexander Larsson committed
623
					 parent_window);
624
	g_free (detail);
625

626
	response = gtk_dialog_run (dialog);
Cosimo Cecchi's avatar
Cosimo Cecchi committed
627
	gtk_widget_destroy (GTK_WIDGET (dialog));
628 629

	return response == GTK_RESPONSE_YES;
630 631
}

632 633 634
static gboolean
selection_contains_one_item_in_menu_callback (FMDirectoryView *view, GList *selection)
{
Ramiro Estrugo's avatar
Ramiro Estrugo committed
635
	if (eel_g_list_exactly_one_item (selection)) {
636 637 638 639 640 641 642 643
		return TRUE;
	}

	/* 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) {
644
		g_warning ("Expected one selected item, found %'d. No action will be performed.", 	
645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662
			   g_list_length (selection));
	}

	return FALSE;
}

static gboolean
selection_not_empty_in_menu_callback (FMDirectoryView *view, GList *selection)
{
	if (selection != NULL) {
		return TRUE;
	}

	/* 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) {
663
		g_warning ("Empty selection found when selection was expected. No action will be performed.");
664 665 666 667 668
	}

	return FALSE;
}

Alexander Larsson's avatar
Alexander Larsson committed
669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692
static char *
get_view_directory (FMDirectoryView *view)
{
	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;
}

void
fm_directory_view_activate_files (FMDirectoryView *view,
				  GList *files,
				  NautilusWindowOpenMode mode,
693 694
				  NautilusWindowOpenFlags flags,
				  gboolean confirm_multiple)
Alexander Larsson's avatar
Alexander Larsson committed
695 696 697 698 699
{
	char *path;

	path = get_view_directory (view);
	nautilus_mime_activate_files (fm_directory_view_get_containing_window (view),
700
				      view->details->slot,
Alexander Larsson's avatar
Alexander Larsson committed
701 702 703
				      files,
				      path,
				      mode,
704 705
				      flags,
				      confirm_multiple);
Alexander Larsson's avatar
Alexander Larsson committed
706 707 708 709 710 711 712 713 714 715 716 717 718 719

	g_free (path);
}

void
fm_directory_view_activate_file (FMDirectoryView *view,
				 NautilusFile *file,
				 NautilusWindowOpenMode mode,
				 NautilusWindowOpenFlags flags)
{
	char *path;

	path = get_view_directory (view);
	nautilus_mime_activate_file (fm_directory_view_get_containing_window (view),
720
				     view->details->slot,
Alexander Larsson's avatar
Alexander Larsson committed
721 722 723 724 725 726 727 728
				     file,
				     path,
				     mode,
				     flags);

	g_free (path);
}

729
static void
Alexander Larsson's avatar
Alexander Larsson committed
730 731
action_open_callback (GtkAction *action,
		      gpointer callback_data)
732
{
733 734
	GList *selection;
	FMDirectoryView *view;
735

736
	view = FM_DIRECTORY_VIEW (callback_data);
737

738
	selection = fm_directory_view_get_selection (view);
Alexander Larsson's avatar
Alexander Larsson committed
739 740
	fm_directory_view_activate_files (view,
					  selection,
Alexander Larsson's avatar
Alexander Larsson committed
741
					  NAUTILUS_WINDOW_OPEN_ACCORDING_TO_MODE,
742 743
					  0,
					  TRUE);
744 745 746
	nautilus_file_list_free (selection);
}

747
static void
Alexander Larsson's avatar
Alexander Larsson committed
748 749
action_open_close_parent_callback (GtkAction *action,
				   gpointer callback_data)
750 751 752 753
{
	GList *selection;
	FMDirectoryView *view;

754
	view = FM_DIRECTORY_VIEW (callback_data);
755 756

	selection = fm_directory_view_get_selection (view);
Alexander Larsson's avatar
Alexander Larsson committed
757 758
	fm_directory_view_activate_files (view,
					  selection,
Alexander Larsson's avatar
Alexander Larsson committed
759
					  NAUTILUS_WINDOW_OPEN_ACCORDING_TO_MODE,
760 761
					  NAUTILUS_WINDOW_OPEN_FLAG_CLOSE_BEHIND,
					  TRUE);
762 763 764 765
	nautilus_file_list_free (selection);
}


766
static void
Alexander Larsson's avatar
Alexander Larsson committed
767 768
action_open_alternate_callback (GtkAction *action,
				gpointer callback_data)
769
{
770 771
	FMDirectoryView *view;
	GList *selection;
Alexander Larsson's avatar
Alexander Larsson committed
772
	GtkWindow *window;
773

774
	view = FM_DIRECTORY_VIEW (callback_data);
775
	selection = fm_directory_view_get_selection (view);
776

Alexander Larsson's avatar
Alexander Larsson committed
777 778
	window = fm_directory_view_get_containing_window (view);

779
	if (fm_directory_view_confirm_multiple (window, g_list_length (selection), FALSE)) {
780
		g_list_foreach (selection, open_one_in_new_window, view);
781
	}
782 783 784 785

	nautilus_file_list_free (selection);
}

786 787 788 789 790 791 792 793
static void
action_open_new_tab_callback (GtkAction *action,
			      gpointer callback_data)
{
	FMDirectoryView *view;
	GList *selection;
	GtkWindow *window;

794
	view = FM_DIRECTORY_VIEW (callback_data);
795 796 797 798 799 800 801 802 803 804 805 806 807 808 809
	selection = fm_directory_view_get_selection (view);

	window = fm_directory_view_get_containing_window (view);

	if (fm_directory_view_confirm_multiple (window, g_list_length (selection), TRUE)) {
		fm_directory_view_activate_files (view,
						  selection,
						  NAUTILUS_WINDOW_OPEN_ACCORDING_TO_MODE,
						  NAUTILUS_WINDOW_OPEN_FLAG_NEW_TAB,
						  FALSE);
	}

	nautilus_file_list_free (selection);
}

810 811
static void
action_open_folder_window_callback (GtkAction *action,
Alexander Larsson's avatar
Alexander Larsson committed
812
				gpointer callback_data)
813 814 815
{
	FMDirectoryView *view;
	GList *selection;
Alexander Larsson's avatar
Alexander Larsson committed
816
	GtkWindow *window;
817

818
	view = FM_DIRECTORY_VIEW (callback_data);
819 820
	selection = fm_directory_view_get_selection (view);

Alexander Larsson's avatar
Alexander Larsson committed
821 822
	window = fm_directory_view_get_containing_window (view);

823
	if (fm_directory_view_confirm_multiple (window, g_list_length (selection), FALSE)) {
824 825 826 827 828 829
		g_list_foreach (selection, open_one_in_folder_window, view);
	}

	nautilus_file_list_free (selection);
}

830
static void
831 832 833
app_chooser_dialog_response_cb (GtkDialog *dialog,
				gint response_id,
				gpointer user_data)
834
{
Alexander Larsson's avatar
Alexander Larsson committed
835
	GtkWindow *parent_window;
836
	NautilusFile *file;
837
	GAppInfo *info;
Alexander Larsson's avatar
Alexander Larsson committed
838
	GList files;
839

840 841 842 843 844 845 846 847
	parent_window = user_data;

	if (response_id != GTK_RESPONSE_OK) {
		gtk_widget_destroy (GTK_WIDGET (dialog));
		return;
	}

	info = gtk_app_chooser_get_app_info (GTK_APP_CHOOSER (dialog));
848
	file = g_object_get_data (G_OBJECT (dialog), "directory-view:file");
849

850 851
	g_signal_emit_by_name (nautilus_signaller_get_current (), "mime_data_changed");

Alexander Larsson's avatar
Alexander Larsson committed
852 853 854
	files.next = NULL;
	files.prev = NULL;
	files.data = file;
855 856 857 858
	nautilus_launch_application (info, &files, parent_window);

	gtk_widget_destroy (GTK_WIDGET (dialog));
	g_object_unref (info);
859 860 861
}

static void
862
choose_program (FMDirectoryView *view,
863
		NautilusFile *file)
864
{
865
	GtkWidget *dialog;
866 867
	GFile *location;
	GtkWindow *parent_window;
868

869 870 871 872
	g_assert (FM_IS_DIRECTORY_VIEW (view));
	g_assert (NAUTILUS_IS_FILE (file));

	nautilus_file_ref (file);
873 874
	location = nautilus_file_get_location (file);
	parent_window = fm_directory_view_get_containing_window (view);
875

876 877
	dialog = gtk_app_chooser_dialog_new (parent_window, 0,
					     location);
878 879 880 881 882
	g_object_set_data_full (G_OBJECT (dialog), 
				"directory-view:file",
				g_object_ref (file),
				(GDestroyNotify)g_object_unref);
	gtk_widget_show (dialog);
883

884 885 886 887 888
	g_signal_connect_object (dialog, "response", 
				 G_CALLBACK (app_chooser_dialog_response_cb),
				 parent_window, 0);

	g_object_unref (location);
889 890 891 892
	nautilus_file_unref (file);	
}

static void
893
open_with_other_program (FMDirectoryView *view)
894
{
895 896 897 898 899 900
        GList *selection;

	g_assert (FM_IS_DIRECTORY_VIEW (view));

       	selection = fm_directory_view_get_selection (view);

901
	if (selection_contains_one_item_in_menu_callback (view, selection)) {
902
		choose_program (view, NAUTILUS_FILE (selection->data));
903
	}
904 905

	nautilus_file_list_free (selection);
906 907 908
}

static void
Alexander Larsson's avatar
Alexander Larsson committed
909 910
action_other_application_callback (GtkAction *action,
				   gpointer callback_data)
911
{
912
	g_assert (FM_IS_DIRECTORY_VIEW (callback_data));
913

914
	open_with_other_program (FM_DIRECTORY_VIEW (callback_data));
915 916
}

917
static void
918
trash_or_delete_selected_files (FMDirectoryView *view)
919 920
{
        GList *selection;
921 922 923 924 925 926

	/* This might be rapidly called multiple times for the same selection
	 * when using keybindings. So we remember if the current selection
	 * was already removed (but the view doesn't know about it yet).
	 */
	if (!view->details->selection_was_removed) {
927
		selection = fm_directory_view_get_selection_for_file_transfer (view);
928 929 930
		trash_or_delete_files (fm_directory_view_get_containing_window (view),
				       selection, TRUE,
				       view);
931 932 933
		nautilus_file_list_free (selection);
		view->details->selection_was_removed = TRUE;
	}
934 935
}

Alexander Larsson's avatar
Alexander Larsson committed
936 937 938
static gboolean
real_trash (FMDirectoryView *view)
{
939 940 941 942 943 944 945 946 947 948
	GtkAction *action;

	action = gtk_action_group_get_action (view->details->dir_action_group,
					      FM_ACTION_TRASH);
	if (gtk_action_get_sensitive (action) &&
	    gtk_action_get_visible (action)) {
		trash_or_delete_selected_files (view);
		return TRUE;
	}
	return FALSE;
Alexander Larsson's avatar
Alexander Larsson committed
949 950
}

951
static void
Alexander Larsson's avatar
Alexander Larsson committed
952 953
action_trash_callback (GtkAction *action,
		       gpointer callback_data)
954
{
955
        trash_or_delete_selected_files (FM_DIRECTORY_VIEW (callback_data));
956 957
}

958
static void
959
delete_selected_files (FMDirectoryView *view)
960 961 962
{
        GList *selection;
	GList *node;
Alexander Larsson's avatar
Alexander Larsson committed
963
	GList *locations;
964

965
	selection = fm_directory_view_get_selection_for_file_transfer (view);
966 967
	if (selection == NULL) {
		return;
968 969
	}

Alexander Larsson's avatar
Alexander Larsson committed
970
	locations = NULL;
971
	for (node = selection; node != NULL; node = node->next) {
Alexander Larsson's avatar
Alexander Larsson committed
972 973
		locations = g_list_prepend (locations,
					    nautilus_file_get_location ((NautilusFile *) node->data));
974
	}
Alexander Larsson's avatar
Alexander Larsson committed
975
	locations = g_list_reverse (locations);
976

Alexander Larsson's avatar
Alexander Larsson committed
977
	nautilus_file_operations_delete (locations, fm_directory_view_get_containing_window (view), NULL, NULL);
978 979

	g_list_free_full (locations, g_object_unref);
980 981 982
        nautilus_file_list_free (selection);
}

983
static void
Alexander Larsson's avatar
Alexander Larsson committed
984 985
action_delete_callback (GtkAction *action,
			gpointer callback_data)
986
{
987
        delete_selected_files (FM_DIRECTORY_VIEW (callback_data));
988 989
}

990 991 992 993 994 995 996
static void
action_restore_from_trash_callback (GtkAction *action,
				    gpointer callback_data)
{
	FMDirectoryView *view;
	GList *selection;

997
	view = FM_DIRECTORY_VIEW (callback_data);
998 999

	selection = fm_directory_view_get_selection_for_file_transfer (view);
1000 1001
	nautilus_restore_files_from_trash (selection,
					   fm_directory_view_get_containing_window (view));
1002 1003 1004 1005 1006

	nautilus_file_list_free (selection);

}

Alexander Larsson's avatar
Alexander Larsson committed
1007 1008 1009
static gboolean
real_delete (FMDirectoryView *view)
{
1010 1011 1012 1013 1014 1015 1016 1017
	GtkAction *action;

	action = gtk_action_group_get_action (view->details->dir_action_group,
					      FM_ACTION_DELETE);
	if (gtk_action_get_sensitive (action) &&
	    gtk_action_get_visible (action)) {
		delete_selected_files (view);
		return TRUE;
Alexander Larsson's avatar
Alexander Larsson committed
1018
	}
1019
	return FALSE;
Alexander Larsson's avatar
Alexander Larsson committed
1020 1021
}

1022
static void
Alexander Larsson's avatar
Alexander Larsson committed
1023 1024
action_duplicate_callback (GtkAction *action,
			   gpointer callback_data)
1025 1026 1027
{
        FMDirectoryView *view;
        GList *selection;
1028 1029
        GArray *selected_item_locations;
 
1030
        view = FM_DIRECTORY_VIEW (callback_data);
1031
	selection = fm_directory_view_get_selection_for_file_transfer (view);
1032
	if (selection_not_empty_in_menu_callback (view, selection)) {
1033
		/* FIXME bugzilla.gnome.org 45061:
1034 1035 1036 1037 1038
		 * should change things here so that we use a get_icon_locations (view, selection).
		 * Not a problem in this case but in other places the selection may change by
		 * the time we go and retrieve the icon positions, relying on the selection
		 * staying intact to ensure the right sequence and count of positions is fragile.
		 */
Darin Adler's avatar
Darin Adler committed
1039
		selected_item_locations = fm_directory_view_get_selected_icon_locations (view);
1040 1041
	        fm_directory_view_duplicate_selection (view, selection, selected_item_locations);
	        g_array_free (selected_item_locations, TRUE);
1042
	}
1043 1044 1045 1046

        nautilus_file_list_free (selection);
}

1047
static void
Alexander Larsson's avatar
Alexander Larsson committed
1048 1049
action_create_link_callback (GtkAction *action,
			     gpointer callback_data)
1050 1051 1052
{
        FMDirectoryView *view;
        GList *selection;
1053
        GArray *selected_item_locations;
1054
        
1055
        g_assert (FM_IS_DIRECTORY_VIEW (callback_data));
1056

1057
        view = FM_DIRECTORY_VIEW (callback_data);
1058
	selection = fm_directory_view_get_selection (view);
1059
	if (selection_not_empty_in_menu_callback (view, selection)) {
Darin Adler's avatar
Darin Adler committed
1060
		selected_item_locations = fm_directory_view_get_selected_icon_locations (view);
1061 1062
	        fm_directory_view_create_links_for_files (view, selection, selected_item_locations);
	        g_array_free (selected_item_locations, TRUE);
1063
	}
1064 1065 1066 1067

        nautilus_file_list_free (selection);
}

1068
static void
Alexander Larsson's avatar
Alexander Larsson committed
1069 1070
action_select_all_callback (GtkAction *action, 
			    gpointer callback_data)
1071
{
1072
	g_assert (FM_IS_DIRECTORY_VIEW (callback_data));
1073

1074
	fm_directory_view_select_all (callback_data);
1075 1076
}

1077 1078 1079 1080
static void
action_invert_selection_callback (GtkAction *action,
				  gpointer callback_data)
{
1081
	g_assert (FM_IS_DIRECTORY_VIEW (callback_data));
1082

1083
	fm_directory_view_invert_selection (callback_data);
1084 1085 1086
}


1087 1088 1089 1090
static void
pattern_select_response_cb (GtkWidget *dialog, int response, gpointer user_data)
{
	FMDirectoryView *view;
1091
	NautilusDirectory *directory;
1092
	GtkWidget *entry;
1093 1094
	GList *selection;
	GError *error;
1095 1096 1097

	view = FM_DIRECTORY_VIEW (user_data);

1098 1099
	switch (response) {
	case GTK_RESPONSE_OK :
1100 1101 1102 1103 1104 1105
		entry = g_object_get_data (G_OBJECT (dialog), "entry");
		directory = fm_directory_view_get_model (view);
		selection = nautilus_directory_match_pattern (directory,
					gtk_entry_get_text (GTK_ENTRY (entry)));
			
		if (selection) {
Alexander Larsson's avatar
Alexander Larsson committed
1106
			fm_directory_view_set_selection (view, selection);
1107
			nautilus_file_list_free (selection);
1108 1109

			fm_directory_view_reveal_selection(view);
1110
		}
1111 1112 1113 1114 1115 1116 1117 1118
		/* fall through */
	case GTK_RESPONSE_NONE :
	case GTK_RESPONSE_DELETE_EVENT :
	case GTK_RESPONSE_CANCEL :
		gtk_widget_destroy (GTK_WIDGET (dialog));
		break;
	case GTK_RESPONSE_HELP :
		error = NULL;
1119 1120 1121
		gtk_show_uri (gtk_window_get_screen (GTK_WINDOW (dialog)),
			      "ghelp:user-guide#nautilus-select-pattern",
			      gtk_get_current_event_time (), &error);
1122 1123 1124 1125 1126 1127 1128 1129
		if (error) {
			eel_show_error_dialog (_("There was an error displaying help."), error->message,
					       GTK_WINDOW (dialog));
			g_error_free (error);
		}
		break;
	default :
		g_assert_not_reached ();
1130 1131 1132 1133 1134 1135 1136
	}
}

static void
select_pattern (FMDirectoryView *view)
{
	GtkWidget *dialog;
1137
	GtkWidget *label;
1138 1139
	GtkWidget *example;
	GtkWidget *table;
1140 1141
	GtkWidget *entry;
	GList *ret;
1142
	char *example_pattern;
1143 1144

	ret = NULL;
1145
	dialog = gtk_dialog_new_with_buttons (_("Select Items Matching"),
1146 1147
			fm_directory_view_get_containing_window (view),
			GTK_DIALOG_DESTROY_WITH_PARENT,
1148 1149
			GTK_STOCK_HELP,
			GTK_RESPONSE_HELP,
1150 1151 1152 1153 1154 1155 1156
			GTK_STOCK_CANCEL,
			GTK_RESPONSE_CANCEL,
			GTK_STOCK_OK,
			GTK_RESPONSE_OK,
			NULL);
	gtk_dialog_set_default_response (GTK_DIALOG (dialog),
					 GTK_RESPONSE_OK);
1157
	gtk_container_set_border_width (GTK_CONTAINER (dialog), 5);
1158
	gtk_box_set_spacing (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), 2);
1159

1160
	label = gtk_label_new_with_mnemonic (_("_Pattern:"));
1161 1162 1163
	example = gtk_label_new (NULL);
	example_pattern = g_strdup_printf ("<b>%s</b><i>%s</i>", 
					   _("Examples: "),
1164
					   "*.png, file\?\?.txt, pict*.\?\?\?");
1165 1166 1167
	gtk_label_set_markup (GTK_LABEL (example), example_pattern);
	g_free (example_pattern);
	gtk_misc_set_alignment (GTK_MISC (example), 0.0, 0.5);
1168 1169
	entry = gtk_entry_new ();
	gtk_entry_set_activates_default (GTK_ENTRY (entry), TRUE);