gtkfilechooserdefault.c 225 KB
Newer Older
1
/* -*- Mode: C; c-file-style: "gnu"; tab-width: 8 -*- */
Cody Russell's avatar
Cody Russell committed
2
/* GTK - The GIMP Toolkit
3
 * gtkfilechooserdefault.c: Default implementation of GtkFileChooser
Owen Taylor's avatar
Owen Taylor committed
4 5 6 7 8 9 10 11 12 13 14 15 16
 * Copyright (C) 2003, Red Hat, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
Javier Jardón's avatar
Javier Jardón committed
17
 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
Owen Taylor's avatar
Owen Taylor committed
18 19
 */

20 21 22 23 24
/* TODO:
 *
 * * Fix FIXME-places-sidebar
 */

25
#include "config.h"
26

27 28
#include "gtkfilechooserdefault.h"

29
#include "gtkbindings.h"
30
#include "gtkcelllayout.h"
31
#include "gtkcellrendererpixbuf.h"
32
#include "gtkcellrenderertext.h"
Federico Mena Quintero's avatar
Federico Mena Quintero committed
33
#include "gtkcheckmenuitem.h"
34
#include "gtkclipboard.h"
35
#include "gtkcomboboxtext.h"
36
#include "gtkentry.h"
37
#include "gtkexpander.h"
38
#include "gtkfilechooserprivate.h"
39
#include "gtkfilechooserdialog.h"
40
#include "gtkfilechooserembed.h"
41
#include "gtkfilechooserentry.h"
Owen Taylor's avatar
Owen Taylor committed
42 43
#include "gtkfilechooserutils.h"
#include "gtkfilechooser.h"
44
#include "gtkfilesystem.h"
Owen Taylor's avatar
Owen Taylor committed
45
#include "gtkfilesystemmodel.h"
46
#include "gtkframe.h"
47
#include "gtkgrid.h"
48
#include "deprecated/gtkiconfactory.h"
49 50
#include "gtkicontheme.h"
#include "gtkimage.h"
51
#include "deprecated/gtkimagemenuitem.h"
52
#include "gtkinfobar.h"
53
#include "gtklabel.h"
54
#include "gtkmarshalers.h"
55
#include "gtkmessagedialog.h"
56
#include "gtkmountoperation.h"
57
#include "gtkpaned.h"
58
#include "gtkpathbar.h"
59
#include "gtkplacessidebar.h"
60
#include "gtkprivate.h"
61
#include "gtkradiobutton.h"
62 63
#include "gtkrecentfilter.h"
#include "gtkrecentmanager.h"
64
#include "gtkscrolledwindow.h"
65
#include "gtkseparatormenuitem.h"
66
#include "gtksettings.h"
67
#include "gtksizegroup.h"
68
#include "gtksizerequest.h"
69 70
#include "gtktoolbar.h"
#include "gtktoolbutton.h"
71
#include "gtktooltip.h"
72 73
#include "gtktreednd.h"
#include "gtktreeprivate.h"
74
#include "gtktreeselection.h"
75 76
#include "gtkbox.h"
#include "gtkorientable.h"
77
#include "gtkintl.h"
Owen Taylor's avatar
Owen Taylor committed
78

79
#include <cairo-gobject.h>
80
#include <errno.h>
81
#include <string.h>
82
#include <time.h>
83 84
#include <sys/stat.h>
#include <sys/types.h>
85
#include <locale.h>
86

87
#ifdef HAVE_UNISTD_H
88
#include <unistd.h>
89 90 91 92 93
#endif
#ifdef G_OS_WIN32
#include <io.h>
#endif

94 95 96 97
/* Values for GtkSelection-related "info" fields */
#define SELECTION_TEXT 0
#define SELECTION_URI  1

Matthias Clasen's avatar
Matthias Clasen committed
98
/* Profiling stuff */
Matthias Clasen's avatar
Matthias Clasen committed
99
#undef PROFILE_FILE_CHOOSER
Matthias Clasen's avatar
Matthias Clasen committed
100 101 102
#ifdef PROFILE_FILE_CHOOSER


103 104 105
#ifndef F_OK 
#define F_OK 0
#endif
106 107

#define PROFILE_INDENT 4
108

109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
static int profile_indent;

static void
profile_add_indent (int indent)
{
  profile_indent += indent;
  if (profile_indent < 0)
    g_error ("You screwed up your indentation");
}

void
_gtk_file_chooser_profile_log (const char *func, int indent, const char *msg1, const char *msg2)
{
  char *str;

  if (indent < 0)
    profile_add_indent (indent);

  if (profile_indent == 0)
128
    str = g_strdup_printf ("MARK: %s %s %s", func ? func : "", msg1 ? msg1 : "", msg2 ? msg2 : "");
129
  else
130
    str = g_strdup_printf ("MARK: %*c %s %s %s", profile_indent - 1, ' ', func ? func : "", msg1 ? msg1 : "", msg2 ? msg2 : "");
131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147

  access (str, F_OK);
  g_free (str);

  if (indent > 0)
    profile_add_indent (indent);
}

#define profile_start(x, y) _gtk_file_chooser_profile_log (G_STRFUNC, PROFILE_INDENT, x, y)
#define profile_end(x, y) _gtk_file_chooser_profile_log (G_STRFUNC, -PROFILE_INDENT, x, y)
#define profile_msg(x, y) _gtk_file_chooser_profile_log (NULL, 0, x, y)
#else
#define profile_start(x, y)
#define profile_end(x, y)
#define profile_msg(x, y)
#endif

148

149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176
typedef enum {
  LOAD_EMPTY,			/* There is no model */
  LOAD_PRELOAD,			/* Model is loading and a timer is running; model isn't inserted into the tree yet */
  LOAD_LOADING,			/* Timeout expired, model is inserted into the tree, but not fully loaded yet */
  LOAD_FINISHED			/* Model is fully loaded and inserted into the tree */
} LoadState;

typedef enum {
  RELOAD_EMPTY,			/* No folder has been set */
  RELOAD_HAS_FOLDER		/* We have a folder, although it may not be completely loaded yet; no need to reload */
} ReloadState;

typedef enum {
  LOCATION_MODE_PATH_BAR,
  LOCATION_MODE_FILENAME_ENTRY
} LocationMode;

typedef enum {
  OPERATION_MODE_BROWSE,
  OPERATION_MODE_SEARCH,
  OPERATION_MODE_RECENT
} OperationMode;

typedef enum {
  STARTUP_MODE_RECENT,
  STARTUP_MODE_CWD
} StartupMode;

177
typedef struct {
178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277
  GtkFileChooserAction action;

  GtkFileSystem *file_system;

  /* Save mode widgets */
  GtkWidget *save_widgets;
  GtkWidget *save_widgets_table;

  GtkWidget *save_folder_label;

  /* The file browsing widgets */
  GtkWidget *browse_widgets_box;
  GtkWidget *browse_widgets_hpaned;
  GtkWidget *browse_header_box;
  GtkWidget *browse_files_tree_view;
  GtkWidget *browse_files_popup_menu;
  GtkWidget *browse_files_popup_menu_add_shortcut_item;
  GtkWidget *browse_files_popup_menu_hidden_files_item;
  GtkWidget *browse_files_popup_menu_size_column_item;
  GtkWidget *browse_files_popup_menu_copy_file_location_item;
  GtkWidget *browse_files_popup_menu_visit_file_item;
  GtkWidget *browse_new_folder_button;
  GtkWidget *browse_path_bar_hbox;
  GtkSizeGroup *browse_path_bar_size_group;
  GtkWidget *browse_path_bar;
  GtkWidget *browse_special_mode_icon;
  GtkWidget *browse_special_mode_label;
  GtkWidget *browse_select_a_folder_info_bar;
  GtkWidget *browse_select_a_folder_label;
  GtkWidget *browse_select_a_folder_icon;

  GtkFileSystemModel *browse_files_model;
  char *browse_files_last_selected_name;

  GtkWidget *places_sidebar;
  StartupMode startup_mode;

  /* OPERATION_MODE_SEARCH */
  GtkWidget *search_hbox;
  GtkWidget *search_entry;
  GtkSearchEngine *search_engine;
  GtkQuery *search_query;
  GtkFileSystemModel *search_model;

  /* OPERATION_MODE_RECENT */
  GtkRecentManager *recent_manager;
  GtkFileSystemModel *recent_model;
  guint load_recent_id;

  GtkWidget *filter_combo_hbox;
  GtkWidget *filter_combo;
  GtkWidget *preview_box;
  GtkWidget *preview_label;
  GtkWidget *preview_widget;
  GtkWidget *extra_align;
  GtkWidget *extra_widget;

  GtkWidget *location_button;
  GtkWidget *location_entry_box;
  GtkWidget *location_label;
  GtkWidget *location_entry;
  LocationMode location_mode;

  /* Handles */
  GSList *loading_shortcuts;
  GSList *reload_icon_cancellables;
  GCancellable *file_list_drag_data_received_cancellable;
  GCancellable *update_current_folder_cancellable;
  GCancellable *should_respond_get_info_cancellable;
  GCancellable *file_exists_get_info_cancellable;
  GCancellable *update_from_entry_cancellable;
  GCancellable *shortcuts_activate_iter_cancellable;

  LoadState load_state;
  ReloadState reload_state;
  guint load_timeout_id;

  OperationMode operation_mode;

  GSList *pending_select_files;

  GtkFileFilter *current_filter;
  GSList *filters;

  GtkBookmarksManager *bookmarks_manager;

  int num_volumes;
  int num_shortcuts;
  int num_bookmarks;

  gulong volumes_changed_id;
  gulong bookmarks_changed_id;

  GFile *current_volume_file;
  GFile *current_folder;
  GFile *preview_file;
  char *preview_display_name;

  GtkTreeViewColumn *list_name_column;
  GtkCellRenderer *list_name_renderer;
278
  GtkCellRenderer *list_pixbuf_renderer;
279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312
  GtkTreeViewColumn *list_mtime_column;
  GtkTreeViewColumn *list_size_column;

  GSource *edited_idle;
  char *edited_new_text;

  gulong settings_signal_id;
  int icon_size;

  GSource *focus_entry_idle;

  gulong toplevel_set_focus_id;
  GtkWidget *toplevel_last_focus_widget;

  gint sort_column;
  GtkSortType sort_order;

  /* Flags */

  guint local_only : 1;
  guint preview_widget_active : 1;
  guint use_preview_label : 1;
  guint select_multiple : 1;
  guint show_hidden : 1;
  guint do_overwrite_confirmation : 1;
  guint list_sort_ascending : 1;
  guint changing_folder : 1;
  guint shortcuts_current_folder_active : 1;
  guint has_home : 1;
  guint has_desktop : 1;
  guint has_search : 1;
  guint has_recent: 1;
  guint show_size_column : 1;
  guint create_folders : 1;
313
} GtkFileChooserDefaultPrivate;
Owen Taylor's avatar
Owen Taylor committed
314

315 316 317
#define GTK_FILE_CHOOSER_DEFAULT_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_FILE_CHOOSER_DEFAULT, GtkFileChooserDefaultClass))
#define GTK_IS_FILE_CHOOSER_DEFAULT_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_FILE_CHOOSER_DEFAULT))
#define GTK_FILE_CHOOSER_DEFAULT_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_FILE_CHOOSER_DEFAULT, GtkFileChooserDefaultClass))
Owen Taylor's avatar
Owen Taylor committed
318

319
#define MAX_LOADING_TIME 500
320

321 322
#define DEFAULT_NEW_FOLDER_NAME _("Type name of new folder")

323 324 325 326 327 328 329 330 331
typedef struct _GtkFileChooserDefaultClass GtkFileChooserDefaultClass;

struct _GtkFileChooserDefault
{
  GtkBox parent_instance;

  GtkFileChooserDefaultPrivate *priv;
};

332
struct _GtkFileChooserDefaultClass
Owen Taylor's avatar
Owen Taylor committed
333
{
334
  GtkBoxClass parent_class;
Owen Taylor's avatar
Owen Taylor committed
335 336
};

337 338 339
/* Signal IDs */
enum {
  LOCATION_POPUP,
340
  LOCATION_POPUP_ON_PASTE,
341
  UP_FOLDER,
342
  DOWN_FOLDER,
343
  HOME_FOLDER,
344
  DESKTOP_FOLDER,
345
  QUICK_BOOKMARK,
346
  LOCATION_TOGGLE_POPUP,
347
  SHOW_HIDDEN,
348
  SEARCH_SHORTCUT,
349
  RECENT_SHORTCUT,
350

351 352 353 354 355
  LAST_SIGNAL
};

static guint signals[LAST_SIGNAL] = { 0 };

356 357 358
#define MODEL_ATTRIBUTES "standard::name,standard::type,standard::display-name," \
                         "standard::is-hidden,standard::is-backup,standard::size," \
                         "standard::content-type,time::modified"
359 360 361 362 363 364 365 366
enum {
  /* the first 3 must be these due to settings caching sort column */
  MODEL_COL_NAME,
  MODEL_COL_SIZE,
  MODEL_COL_MTIME,
  MODEL_COL_FILE,
  MODEL_COL_NAME_COLLATED,
  MODEL_COL_IS_FOLDER,
367
  MODEL_COL_IS_SENSITIVE,
368
  MODEL_COL_SURFACE,
369 370 371 372 373 374
  MODEL_COL_SIZE_TEXT,
  MODEL_COL_MTIME_TEXT,
  MODEL_COL_ELLIPSIZE,
  MODEL_COL_NUM_COLUMNS
};

375 376 377 378 379 380 381 382 383
/* This list of types is passed to _gtk_file_system_model_new*() */
#define MODEL_COLUMN_TYPES					\
	MODEL_COL_NUM_COLUMNS,					\
	G_TYPE_STRING,		  /* MODEL_COL_NAME */		\
	G_TYPE_INT64,		  /* MODEL_COL_SIZE */		\
	G_TYPE_LONG,		  /* MODEL_COL_MTIME */		\
	G_TYPE_FILE,		  /* MODEL_COL_FILE */		\
	G_TYPE_STRING,		  /* MODEL_COL_NAME_COLLATED */	\
	G_TYPE_BOOLEAN,		  /* MODEL_COL_IS_FOLDER */	\
384
	G_TYPE_BOOLEAN,		  /* MODEL_COL_IS_SENSITIVE */	\
385
	CAIRO_GOBJECT_TYPE_SURFACE,  /* MODEL_COL_SURFACE */	\
386 387 388 389
	G_TYPE_STRING,		  /* MODEL_COL_SIZE_TEXT */	\
	G_TYPE_STRING,		  /* MODEL_COL_MTIME_TEXT */	\
	PANGO_TYPE_ELLIPSIZE_MODE /* MODEL_COL_ELLIPSIZE */

390 391
/* Identifiers for target types */
enum {
392
  GTK_TREE_MODEL_ROW,
393 394
};

395 396
#define DEFAULT_RECENT_FILES_LIMIT 50

Federico Mena Quintero's avatar
Federico Mena Quintero committed
397
/* Icon size for if we can't get it from the theme */
398
#define FALLBACK_ICON_SIZE 16
Federico Mena Quintero's avatar
Federico Mena Quintero committed
399

400
#define PREVIEW_HBOX_SPACING 12
401
#define NUM_LINES 45
402
#define NUM_CHARS 60
403

404 405
static void gtk_file_chooser_default_iface_init       (GtkFileChooserIface        *iface);
static void gtk_file_chooser_embed_default_iface_init (GtkFileChooserEmbedIface   *iface);
406 407 408 409 410 411 412 413 414 415 416 417 418

static GObject* gtk_file_chooser_default_constructor  (GType                  type,
						       guint                  n_construct_properties,
						       GObjectConstructParam *construct_params);
static void     gtk_file_chooser_default_finalize     (GObject               *object);
static void     gtk_file_chooser_default_set_property (GObject               *object,
						       guint                  prop_id,
						       const GValue          *value,
						       GParamSpec            *pspec);
static void     gtk_file_chooser_default_get_property (GObject               *object,
						       guint                  prop_id,
						       GValue                *value,
						       GParamSpec            *pspec);
419
static void     gtk_file_chooser_default_dispose      (GObject               *object);
420
static void     gtk_file_chooser_default_show_all       (GtkWidget             *widget);
421
static void     gtk_file_chooser_default_realize        (GtkWidget             *widget);
422
static void     gtk_file_chooser_default_map            (GtkWidget             *widget);
423
static void     gtk_file_chooser_default_unmap          (GtkWidget             *widget);
424 425
static void     gtk_file_chooser_default_hierarchy_changed (GtkWidget          *widget,
							    GtkWidget          *previous_toplevel);
426
static void     gtk_file_chooser_default_style_updated  (GtkWidget             *widget);
427 428
static void     gtk_file_chooser_default_screen_changed (GtkWidget             *widget,
							 GdkScreen             *previous_screen);
429

430
static gboolean       gtk_file_chooser_default_set_current_folder 	   (GtkFileChooser    *chooser,
431
									    GFile             *folder,
432
									    GError           **error);
433
static gboolean       gtk_file_chooser_default_update_current_folder 	   (GtkFileChooser    *chooser,
434
									    GFile             *folder,
435
									    gboolean           keep_trail,
436
									    gboolean           clear_entry,
437
									    GError           **error);
438
static GFile *        gtk_file_chooser_default_get_current_folder 	   (GtkFileChooser    *chooser);
439
static void           gtk_file_chooser_default_set_current_name   	   (GtkFileChooser    *chooser,
440
									    const gchar       *name);
441
static gchar *        gtk_file_chooser_default_get_current_name   	   (GtkFileChooser    *chooser);
442 443
static gboolean       gtk_file_chooser_default_select_file        	   (GtkFileChooser    *chooser,
									    GFile             *file,
444
									    GError           **error);
445 446
static void           gtk_file_chooser_default_unselect_file      	   (GtkFileChooser    *chooser,
									    GFile             *file);
447 448
static void           gtk_file_chooser_default_select_all         	   (GtkFileChooser    *chooser);
static void           gtk_file_chooser_default_unselect_all       	   (GtkFileChooser    *chooser);
449 450
static GSList *       gtk_file_chooser_default_get_files          	   (GtkFileChooser    *chooser);
static GFile *        gtk_file_chooser_default_get_preview_file   	   (GtkFileChooser    *chooser);
451 452
static GtkFileSystem *gtk_file_chooser_default_get_file_system    	   (GtkFileChooser    *chooser);
static void           gtk_file_chooser_default_add_filter         	   (GtkFileChooser    *chooser,
453
									    GtkFileFilter     *filter);
454
static void           gtk_file_chooser_default_remove_filter      	   (GtkFileChooser    *chooser,
455
									    GtkFileFilter     *filter);
456 457
static GSList *       gtk_file_chooser_default_list_filters       	   (GtkFileChooser    *chooser);
static gboolean       gtk_file_chooser_default_add_shortcut_folder    (GtkFileChooser    *chooser,
458
								       GFile             *file,
459 460
								       GError           **error);
static gboolean       gtk_file_chooser_default_remove_shortcut_folder (GtkFileChooser    *chooser,
461
								       GFile             *file,
462 463
								       GError           **error);
static GSList *       gtk_file_chooser_default_list_shortcut_folders  (GtkFileChooser    *chooser);
464

465 466 467
static void           gtk_file_chooser_default_get_default_size       (GtkFileChooserEmbed *chooser_embed,
								       gint                *default_width,
								       gint                *default_height);
468
static gboolean       gtk_file_chooser_default_should_respond         (GtkFileChooserEmbed *chooser_embed);
Federico Mena Quintero's avatar
Federico Mena Quintero committed
469
static void           gtk_file_chooser_default_initial_focus          (GtkFileChooserEmbed *chooser_embed);
470

471 472
static void add_selection_to_recent_list (GtkFileChooserDefault *impl);

473 474
static void location_popup_handler  (GtkFileChooserDefault *impl,
				     const gchar           *path);
475
static void location_popup_on_paste_handler (GtkFileChooserDefault *impl);
476
static void location_toggle_popup_handler   (GtkFileChooserDefault *impl);
477 478 479 480 481 482 483 484
static void up_folder_handler       (GtkFileChooserDefault *impl);
static void down_folder_handler     (GtkFileChooserDefault *impl);
static void home_folder_handler     (GtkFileChooserDefault *impl);
static void desktop_folder_handler  (GtkFileChooserDefault *impl);
static void quick_bookmark_handler  (GtkFileChooserDefault *impl,
				     gint                   bookmark_index);
static void show_hidden_handler     (GtkFileChooserDefault *impl);
static void search_shortcut_handler (GtkFileChooserDefault *impl);
485
static void recent_shortcut_handler (GtkFileChooserDefault *impl);
486
static void update_appearance       (GtkFileChooserDefault *impl);
487

488 489
static void operation_mode_set (GtkFileChooserDefault *impl, OperationMode mode);

490 491 492 493
static void set_current_filter   (GtkFileChooserDefault *impl,
				  GtkFileFilter         *filter);
static void check_preview_change (GtkFileChooserDefault *impl);

494
static void filter_combo_changed       (GtkComboBox           *combo_box,
495
					GtkFileChooserDefault *impl);
Federico Mena Quintero's avatar
Federico Mena Quintero committed
496

497 498 499 500 501 502
static gboolean list_select_func   (GtkTreeSelection      *selection,
				    GtkTreeModel          *model,
				    GtkTreePath           *path,
				    gboolean               path_currently_selected,
				    gpointer               data);

503 504 505 506 507 508
static void list_selection_changed     (GtkTreeSelection      *tree_selection,
					GtkFileChooserDefault *impl);
static void list_row_activated         (GtkTreeView           *tree_view,
					GtkTreePath           *path,
					GtkTreeViewColumn     *column,
					GtkFileChooserDefault *impl);
509

510
static void path_bar_clicked (GtkPathBar            *path_bar,
511 512
			      GFile                 *file,
			      GFile                 *child,
513 514
                              gboolean               child_is_hidden,
                              GtkFileChooserDefault *impl);
515

516
static void update_cell_renderer_attributes (GtkFileChooserDefault *impl);
517

518
static void load_remove_timer (GtkFileChooserDefault *impl, LoadState new_load_state);
519
static void browse_files_center_selected_row (GtkFileChooserDefault *impl);
520

521 522 523 524
static void location_button_toggled_cb (GtkToggleButton *toggle,
					GtkFileChooserDefault *impl);
static void location_switch_to_path_bar (GtkFileChooserDefault *impl);

Benjamin Otte's avatar
Benjamin Otte committed
525 526
static void stop_loading_and_clear_list_model (GtkFileChooserDefault *impl,
                                               gboolean remove_from_treeview);
527 528

static void     search_setup_widgets         (GtkFileChooserDefault *impl);
529 530
static void     search_stop_searching        (GtkFileChooserDefault *impl,
                                              gboolean               remove_query);
531 532 533
static void     search_clear_model           (GtkFileChooserDefault *impl, 
					      gboolean               remove_from_treeview);
static gboolean search_should_respond        (GtkFileChooserDefault *impl);
534
static GSList  *search_get_selected_files    (GtkFileChooserDefault *impl);
535 536
static void     search_entry_activate_cb     (GtkEntry              *entry, 
					      gpointer               data);
537
static void     settings_load                (GtkFileChooserDefault *impl);
538

539
static void     recent_start_loading         (GtkFileChooserDefault *impl);
540 541 542 543
static void     recent_stop_loading          (GtkFileChooserDefault *impl);
static void     recent_clear_model           (GtkFileChooserDefault *impl,
                                              gboolean               remove_from_treeview);
static gboolean recent_should_respond        (GtkFileChooserDefault *impl);
544 545
static GSList * recent_get_selected_files    (GtkFileChooserDefault *impl);
static void     set_file_system_backend      (GtkFileChooserDefault *impl);
546
static void     unset_file_system_backend    (GtkFileChooserDefault *impl);
547

548

549

550
G_DEFINE_TYPE_WITH_CODE (GtkFileChooserDefault, _gtk_file_chooser_default, GTK_TYPE_BOX,
551
                         G_ADD_PRIVATE (GtkFileChooserDefault)
Matthias Clasen's avatar
Matthias Clasen committed
552 553 554 555
			 G_IMPLEMENT_INTERFACE (GTK_TYPE_FILE_CHOOSER,
						gtk_file_chooser_default_iface_init)
			 G_IMPLEMENT_INTERFACE (GTK_TYPE_FILE_CHOOSER_EMBED,
						gtk_file_chooser_embed_default_iface_init));						
Owen Taylor's avatar
Owen Taylor committed
556

557 558 559 560 561 562 563 564 565 566 567 568 569
static void
gtk_file_chooser_default_iface_init (GtkFileChooserIface *iface)
{
  iface->select_file = gtk_file_chooser_default_select_file;
  iface->unselect_file = gtk_file_chooser_default_unselect_file;
  iface->select_all = gtk_file_chooser_default_select_all;
  iface->unselect_all = gtk_file_chooser_default_unselect_all;
  iface->get_files = gtk_file_chooser_default_get_files;
  iface->get_preview_file = gtk_file_chooser_default_get_preview_file;
  iface->get_file_system = gtk_file_chooser_default_get_file_system;
  iface->set_current_folder = gtk_file_chooser_default_set_current_folder;
  iface->get_current_folder = gtk_file_chooser_default_get_current_folder;
  iface->set_current_name = gtk_file_chooser_default_set_current_name;
570
  iface->get_current_name = gtk_file_chooser_default_get_current_name;
571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587
  iface->add_filter = gtk_file_chooser_default_add_filter;
  iface->remove_filter = gtk_file_chooser_default_remove_filter;
  iface->list_filters = gtk_file_chooser_default_list_filters;
  iface->add_shortcut_folder = gtk_file_chooser_default_add_shortcut_folder;
  iface->remove_shortcut_folder = gtk_file_chooser_default_remove_shortcut_folder;
  iface->list_shortcut_folders = gtk_file_chooser_default_list_shortcut_folders;
}

static void
gtk_file_chooser_embed_default_iface_init (GtkFileChooserEmbedIface *iface)
{
  iface->get_default_size = gtk_file_chooser_default_get_default_size;
  iface->should_respond = gtk_file_chooser_default_should_respond;
  iface->initial_focus = gtk_file_chooser_default_initial_focus;
}

static void
588
pending_select_files_free (GtkFileChooserDefault *impl)
589
{
590
  GtkFileChooserDefaultPrivate *priv = impl->priv;
591

592
  g_slist_free_full (priv->pending_select_files, g_object_unref);
593
  priv->pending_select_files = NULL;
594 595 596 597 598 599
}

static void
pending_select_files_add (GtkFileChooserDefault *impl,
			  GFile                 *file)
{
600 601 602 603
  GtkFileChooserDefaultPrivate *priv = impl->priv;

  priv->pending_select_files =
    g_slist_prepend (priv->pending_select_files, g_object_ref (file));
604 605 606 607 608 609
}

static void
gtk_file_chooser_default_finalize (GObject *object)
{
  GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (object);
610
  GtkFileChooserDefaultPrivate *priv = impl->priv;
611 612 613 614
  GSList *l;

  unset_file_system_backend (impl);

615
  g_free (priv->browse_files_last_selected_name);
616

617
  for (l = priv->filters; l; l = l->next)
618 619 620 621 622 623
    {
      GtkFileFilter *filter;

      filter = GTK_FILE_FILTER (l->data);
      g_object_unref (filter);
    }
624
  g_slist_free (priv->filters);
625

626 627
  if (priv->current_filter)
    g_object_unref (priv->current_filter);
628

629 630
  if (priv->current_volume_file)
    g_object_unref (priv->current_volume_file);
631

632 633
  if (priv->current_folder)
    g_object_unref (priv->current_folder);
634

635 636
  if (priv->preview_file)
    g_object_unref (priv->preview_file);
637

638 639
  if (priv->browse_path_bar_size_group)
    g_object_unref (priv->browse_path_bar_size_group);
640 641 642 643 644 645 646

  /* Free all the Models we have */
  stop_loading_and_clear_list_model (impl, FALSE);
  search_clear_model (impl, FALSE);
  recent_clear_model (impl, FALSE);

  /* stopping the load above should have cleared this */
647 648 649
  g_assert (priv->load_timeout_id == 0);

  g_free (priv->preview_display_name);
650

651
  g_free (priv->edited_new_text);
652

653
  impl->priv = NULL;
654 655 656 657 658 659 660 661 662 663

  G_OBJECT_CLASS (_gtk_file_chooser_default_parent_class)->finalize (object);
}

/* Shows an error dialog set as transient for the specified window */
static void
error_message_with_parent (GtkWindow  *parent,
			   const char *msg,
			   const char *detail)
{
664 665
  GtkWidget *dialog;

666
  dialog = gtk_message_dialog_new (parent,
667 668
				   GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
				   GTK_MESSAGE_ERROR,
669
				   GTK_BUTTONS_OK,
670 671
				   "%s",
				   msg);
672 673
  gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
					    "%s", detail);
674

675 676 677
  if (parent && gtk_window_has_group (parent))
    gtk_window_group_add_window (gtk_window_get_group (parent),
                                 GTK_WINDOW (dialog));
678

679 680 681 682
  gtk_dialog_run (GTK_DIALOG (dialog));
  gtk_widget_destroy (dialog);
}

Federico Mena Quintero's avatar
Federico Mena Quintero committed
683 684 685
/* Returns a toplevel GtkWindow, or NULL if none */
static GtkWindow *
get_toplevel (GtkWidget *widget)
Federico Mena Quintero's avatar
Federico Mena Quintero committed
686
{
687 688
  GtkWidget *toplevel;

Federico Mena Quintero's avatar
Federico Mena Quintero committed
689
  toplevel = gtk_widget_get_toplevel (widget);
690
  if (!gtk_widget_is_toplevel (toplevel))
Federico Mena Quintero's avatar
Federico Mena Quintero committed
691 692 693 694
    return NULL;
  else
    return GTK_WINDOW (toplevel);
}
695

Federico Mena Quintero's avatar
Federico Mena Quintero committed
696 697 698
/* Shows an error dialog for the file chooser */
static void
error_message (GtkFileChooserDefault *impl,
699 700
	       const char            *msg,
	       const char            *detail)
Federico Mena Quintero's avatar
Federico Mena Quintero committed
701
{
702
  error_message_with_parent (get_toplevel (GTK_WIDGET (impl)), msg, detail);
703 704
}

705 706 707 708
/* Shows a simple error dialog relative to a path.  Frees the GError as well. */
static void
error_dialog (GtkFileChooserDefault *impl,
	      const char            *msg,
709
	      GFile                 *file,
710 711
	      GError                *error)
{
712
  if (error)
713
    {
714 715 716
      char *uri = NULL;
      char *text;

717 718
      if (file)
	uri = g_file_get_uri (file);
719 720
      text = g_strdup_printf (msg, uri);
      error_message (impl, text, error->message);
721 722 723 724
      g_free (text);
      g_free (uri);
      g_error_free (error);
    }
725 726
}

727 728 729
/* Shows an error dialog about not being able to create a folder */
static void
error_creating_folder_dialog (GtkFileChooserDefault *impl,
730
			      GFile                 *file,
731 732 733 734
			      GError                *error)
{
  error_dialog (impl, 
		_("The folder could not be created"), 
735
		file, error);
736 737
}

738 739 740 741 742
/* Shows an error about not being able to create a folder because a file with
 * the same name is already there.
 */
static void
error_creating_folder_over_existing_file_dialog (GtkFileChooserDefault *impl,
743
						 GFile                 *file,
744 745 746
						 GError                *error)
{
  error_dialog (impl,
747 748 749
		_("The folder could not be created, as a file with the same "
                  "name already exists.  Try using a different name for the "
                  "folder, or rename the file first."),
750
		file, error);
751 752
}

753 754 755 756 757 758 759 760 761 762 763 764 765 766 767
static void
error_with_file_under_nonfolder (GtkFileChooserDefault *impl,
				 GFile *parent_file)
{
  GError *error;

  error = NULL;
  g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_NOT_DIRECTORY,
		       _("You need to choose a valid filename."));

  error_dialog (impl,
		_("Cannot create a file under %s as it is not a folder"),
		parent_file, error);
}

768 769 770 771 772 773 774 775
static void
error_filename_to_long_dialog (GtkFileChooserDefault *impl)
{
  error_message (impl,
                 _("Cannot create file as the filename is to long"),
                 _("Try using a shorter name."));
}

776 777 778 779
/* Shows an error about not being able to select a folder because a file with
 * the same name is already there.
 */
static void
780
error_selecting_folder_over_existing_file_dialog (GtkFileChooserDefault *impl)
781
{
782 783 784
  error_message (impl,
                 _("You may only select folders"),
                 _("The item that you selected is not a folder try using a different item."));
785 786
}

787
/* Shows an error dialog about not being able to create a filename */
788 789 790 791
static void
error_building_filename_dialog (GtkFileChooserDefault *impl,
				GError                *error)
{
792 793
  error_dialog (impl, _("Invalid file name"), 
		NULL, error);
794 795
}

796 797 798
/* Shows an error dialog when we cannot switch to a folder */
static void
error_changing_folder_dialog (GtkFileChooserDefault *impl,
799
			      GFile                 *file,
800 801
			      GError                *error)
{
802
  error_dialog (impl, _("The folder contents could not be displayed"),
803
		file, error);
804 805 806 807 808
}

/* Changes folders, displaying an error dialog if this fails */
static gboolean
change_folder_and_display_error (GtkFileChooserDefault *impl,
809
				 GFile                 *file,
810
				 gboolean               clear_entry)
811 812 813
{
  GError *error;
  gboolean result;
Federico Mena Quintero's avatar
Federico Mena Quintero committed
814

815
  g_return_val_if_fail (G_IS_FILE (file), FALSE);
816

Federico Mena Quintero's avatar
Federico Mena Quintero committed
817 818 819 820 821
  /* We copy the path because of this case:
   *
   * list_row_activated()
   *   fetches path from model; path belongs to the model (*)
   *   calls change_folder_and_display_error()
822
   *     calls gtk_file_chooser_set_current_folder_file()
Federico Mena Quintero's avatar
Federico Mena Quintero committed
823 824 825
   *       changing folders fails, sets model to NULL, thus freeing the path in (*)
   */

826
  error = NULL;
827
  result = gtk_file_chooser_default_update_current_folder (GTK_FILE_CHOOSER (impl), file, TRUE, clear_entry, &error);
828 829

  if (!result)
830
    error_changing_folder_dialog (impl, file, error);
831

832 833 834
  return result;
}

835 836 837 838 839 840 841 842
static void
emit_default_size_changed (GtkFileChooserDefault *impl)
{
  profile_msg ("    emit default-size-changed start", NULL);
  g_signal_emit_by_name (impl, "default-size-changed");
  profile_msg ("    emit default-size-changed end", NULL);
}

843
static void
844
update_preview_widget_visibility (GtkFileChooserDefault *impl)
845
{
846 847 848
  GtkFileChooserDefaultPrivate *priv = impl->priv;

  if (priv->use_preview_label)
849
    {
850
      if (!priv->preview_label)
851
	{
852 853 854 855 856
	  priv->preview_label = gtk_label_new (priv->preview_display_name);
	  gtk_box_pack_start (GTK_BOX (priv->preview_box), priv->preview_label, FALSE, FALSE, 0);
	  gtk_box_reorder_child (GTK_BOX (priv->preview_box), priv->preview_label, 0);
	  gtk_label_set_ellipsize (GTK_LABEL (priv->preview_label), PANGO_ELLIPSIZE_MIDDLE);
	  gtk_widget_show (priv->preview_label);
857 858 859 860
	}
    }
  else
    {
861
      if (priv->preview_label)
862
	{
863 864
	  gtk_widget_destroy (priv->preview_label);
	  priv->preview_label = NULL;
865
	}
866 867
    }

868 869
  if (priv->preview_widget_active && priv->preview_widget)
    gtk_widget_show (priv->preview_box);
870
  else
871
    gtk_widget_hide (priv->preview_box);
872

873
  if (!gtk_widget_get_mapped (GTK_WIDGET (impl)))
874
    emit_default_size_changed (impl);
875 876
}

877
static void
878 879
set_preview_widget (GtkFileChooserDefault *impl,
		    GtkWidget             *preview_widget)
880
{
881 882 883
  GtkFileChooserDefaultPrivate *priv = impl->priv;

  if (preview_widget == priv->preview_widget)
884 885
    return;

886 887 888
  if (priv->preview_widget)
    gtk_container_remove (GTK_CONTAINER (priv->preview_box),
			  priv->preview_widget);
889

890 891
  priv->preview_widget = preview_widget;
  if (priv->preview_widget)
892
    {
893 894 895 896 897
      gtk_widget_show (priv->preview_widget);
      gtk_box_pack_start (GTK_BOX (priv->preview_box), priv->preview_widget, TRUE, TRUE, 0);
      gtk_box_reorder_child (GTK_BOX (priv->preview_box),
			     priv->preview_widget,
			     (priv->use_preview_label && priv->preview_label) ? 1 : 0);
898
    }
899 900

  update_preview_widget_visibility (impl);
901 902
}

903 904 905 906 907 908 909
/* FIXME: GtkFileSystem needs a function to split a remote path
 * into hostname and path components, or maybe just have a 
 * gtk_file_system_path_get_display_name().
 *
 * This function is also used in gtkfilechooserbutton.c
 */
gchar *
910
_gtk_file_chooser_label_for_file (GFile *file)
911 912
{
  const gchar *path, *start, *end, *p;
913 914 915 916
  gchar *uri, *host, *label;

  uri = g_file_get_uri (file);

917
  start = strstr (uri, "://");
918
  if (start)
919
    {
920 921 922 923 924 925 926 927 928
      start += 3;
      path = strchr (start, '/');
      if (path)
        end = path;
      else
        {
          end = uri + strlen (uri);
          path = "/";
        }
929

930 931 932 933
      /* strip username */
      p = strchr (start, '@');
      if (p && p < end)
        start = p + 1;
934
  
935 936 937
      p = strchr (start, ':');
      if (p && p < end)
        end = p;
938
  
939 940 941 942 943 944 945 946 947 948 949 950 951 952
      host = g_strndup (start, end - start);
  
      /* Translators: the first string is a path and the second string 
       * is a hostname. Nautilus and the panel contain the same string 
       * to translate. 
       */
      label = g_strdup_printf (_("%1$s on %2$s"), path, host);
  
      g_free (host);
    }
  else
    {
      label = g_strdup (uri);
    }
953
  
954
  g_free (uri);
955 956 957 958

  return label;
}

959
/* Callback used when the "New Folder" button is clicked */
960
static void
961 962
new_folder_button_clicked (GtkButton             *button,
			   GtkFileChooserDefault *impl)
963
{
964
  GtkFileChooserDefaultPrivate *priv = impl->priv;
965
  GtkTreeIter iter;
966
  GtkTreePath *path;
967

968
  if (!priv->browse_files_model)
969
    return; /* FIXME: this sucks.  Disable the New Folder button or something. */
970

971
  /* Prevent button from being clicked twice */
972
  gtk_widget_set_sensitive (priv->browse_new_folder_button, FALSE);
973

974
  _gtk_file_system_model_add_editable (priv->browse_files_model, &iter);
975

976 977 978
  path = gtk_tree_model_get_path (GTK_TREE_MODEL (priv->browse_files_model), &iter);
  gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (priv->browse_files_tree_view),
				path, priv->list_name_column,
979
				FALSE, 0.0, 0.0);
980

981 982
  g_object_set (priv->list_name_renderer, "editable", TRUE, NULL);
  gtk_tree_view_set_cursor (GTK_TREE_VIEW (priv->browse_files_tree_view),
983
			    path,
984
			    priv->list_name_column,
985
			    TRUE);
986

987
  gtk_tree_path_free (path);
988 989
}

990 991
static GSource *
add_idle_while_impl_is_alive (GtkFileChooserDefault *impl, GCallback callback)
992
{
993
  GSource *source;
994

995 996 997 998
  source = g_idle_source_new ();
  g_source_set_closure (source,
			g_cclosure_new_object (callback, G_OBJECT (impl)));
  g_source_attach (source, NULL);
Federico Mena Quintero's avatar
Federico Mena Quintero committed
999

1000
  return source;
1001 1002
}

1003 1004 1005
/* Idle handler for creating a new folder after editing its name cell, or for
 * canceling the editing.
 */
1006
static gboolean
1007
edited_idle_cb (GtkFileChooserDefault *impl)
1008
{
1009 1010
  GtkFileChooserDefaultPrivate *priv = impl->priv;

1011 1012
  gdk_threads_enter ();
  
1013 1014
  g_source_destroy (priv->edited_idle);
  priv->edited_idle = NULL;
1015

1016 1017
  _gtk_file_system_model_remove_editable (priv->browse_files_model);
  g_object_set (priv->list_name_renderer, "editable", FALSE, NULL);
1018

1019
  gtk_widget_set_sensitive (priv->browse_new_folder_button, TRUE);
1020

1021 1022 1023
  if (priv->edited_new_text /* not cancelled? */
      && (strlen (priv->edited_new_text) != 0)
      && (strcmp (priv->edited_new_text, DEFAULT_NEW_FOLDER_NAME) != 0)) /* Don't create folder if name is empty or has not been edited */
1024
    {
1025 1026
      GError *error = NULL;
      GFile *file;
1027

1028 1029
      file = g_file_get_child_for_display_name (priv->current_folder,
						priv->edited_new_text,
1030 1031 1032 1033
						&error);
      if (file)
	{
	  GError *error = NULL;
1034

1035 1036 1037 1038
	  if (g_file_make_directory (file, NULL, &error))
	    change_folder_and_display_error (impl, file, FALSE);
	  else
	    error_creating_folder_dialog (impl, file, error);
1039

1040
	  g_object_unref (file);
1041
	}
1042 1043
      else
	error_creating_folder_dialog (impl, file, error);
1044

1045 1046
      g_free (priv->edited_new_text);
      priv->edited_new_text = NULL;
1047 1048
    }

1049
  gdk_threads_leave ();
1050

1051
  return FALSE;
1052 1053
}

1054
static void
1055 1056
queue_edited_idle (GtkFileChooserDefault *impl,
		   const gchar           *new_text)
1057
{
1058 1059
  GtkFileChooserDefaultPrivate *priv = impl->priv;

1060 1061 1062
  /* We create the folder in an idle handler so that we don't modify the tree
   * just now.
   */
1063

1064 1065
  if (!priv->edited_idle)
    priv->edited_idle = add_idle_while_impl_is_alive (impl, G_CALLBACK (edited_idle_cb));
1066

1067 1068
  g_free (priv->edited_new_text);
  priv->edited_new_text = g_strdup (new_text);
1069 1070
}

1071
/* Callback used from the text cell renderer when the new folder is named */
1072
static void
1073 1074 1075 1076
renderer_edited_cb (GtkCellRendererText   *cell_renderer_text,
		    const gchar           *path,
		    const gchar           *new_text,
		    GtkFileChooserDefault *impl)
1077
{
1078 1079 1080 1081
  /* work around bug #154921 */
  g_object_set (cell_renderer_text, 
		"mode", GTK_CELL_RENDERER_MODE_INERT, NULL);
  queue_edited_idle (impl, new_text);
1082 1083
}

1084 1085 1086
/* Callback used from the text cell renderer when the new folder edition gets
 * canceled.
 */
1087
static void
1088 1089
renderer_editing_canceled_cb (GtkCellRendererText   *cell_renderer_text,
			      GtkFileChooserDefault *impl)
1090
{
1091 1092 1093 1094
  /* work around bug #154921 */
  g_object_set (cell_renderer_text, 
		"mode", GTK_CELL_RENDERER_MODE_INERT, NULL);
  queue_edited_idle (impl, NULL);
1095 1096 1097
}


1098 1099 1100 1101 1102 1103
struct selection_check_closure {
  GtkFileChooserDefault *impl;
  int num_selected;
  gboolean all_files;
  gboolean all_folders;
};
1104

1105
/* Used from gtk_tree_selection_selected_foreach() */
1106
static void
1107 1108 1109 1110
selection_check_foreach_cb (GtkTreeModel *model,
			    GtkTreePath  *path,
			    GtkTreeIter  *iter,
			    gpointer      data)
1111
{
1112 1113 1114
  struct selection_check_closure *closure;
  gboolean is_folder;
  GFile *file;
Federico Mena Quintero's avatar
Federico Mena Quintero committed
1115

1116 1117 1118 1119
  gtk_tree_model_get (model, iter,
                      MODEL_COL_FILE, &file,
                      MODEL_COL_IS_FOLDER, &is_folder,
                      -1);
1120

1121 1122
  if (file == NULL)
    return;
1123

1124
  g_object_unref (file);
1125

1126 1127
  closure = data;
  closure->num_selected++;
1128

1129 1130
  closure->all_folders = closure->all_folders && is_folder;
  closure->all_files = closure->all_files && !is_folder;
1131
}
1132

1133 1134 1135 1136 1137 1138
/* Checks whether the selected items in the file list are all files or all folders */
static void
selection_check (GtkFileChooserDefault *impl,
		 gint                  *num_selected,
		 gboolean              *all_files,
		 gboolean              *all_folders)
1139
{
1140
  GtkFileChooserDefaultPrivate *priv = impl->priv;
1141
  struct selection_check_closure closure;
1142 1143
  GtkTreeSelection *selection;

1144 1145 1146 1147
  closure.impl = impl;
  closure.num_selected = 0;
  closure.all_files = TRUE;
  closure.all_folders = TRUE;
1148

1149
  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->browse_files_tree_view));
1150 1151 1152
  gtk_tree_selection_selected_foreach (selection,
				       selection_check_foreach_cb,
				       &closure);
1153

1154
  g_assert (closure.num_selected == 0 || !(closure.all_files && closure.all_folders));
1155

1156 1157
  if (num_selected)
    *num_selected = closure.num_selected;
1158

1159 1160
  if (all_files)
    *all_files = closure.all_files;
1161

1162 1163
  if (all_folders)
    *all_folders = closure.all_folders;
1164
}
1165

1166 1167 1168 1169 1170
static gboolean
file_is_recent_uri (GFile *file)
{
  GFile *recent;
  gboolean same;
1171

1172 1173 1174
  recent = g_file_new_for_uri ("recent:///");
  same = g_file_equal (file, recent);
  g_object_unref (recent);
1175

1176 1177
  return same;
}
1178

1179
static void
1180
places_sidebar_open_location_cb (GtkPlacesSidebar *sidebar, GFile *location, GtkPlacesOpenFlags open_flags, GtkFileChooserDefault *impl)
1181
{
1182
  GtkFileChooserDefaultPrivate *priv = impl->priv;
1183
  gboolean clear_entry;
1184

1185 1186 1187
  /* In the Save modes, we want to preserve what the uesr typed in the filename
   * entry, so that he may choose another folder without erasing his typed name.
   */
1188 1189 1190
  if (priv->location_entry
      && !(priv->action == GTK_FILE_CHOOSER_ACTION_SAVE
	   || priv->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER))
1191 1192 1193
    clear_entry = TRUE;
  else
    clear_entry = FALSE;
1194

1195 1196 1197 1198 1199
  /* FIXME-places-sidebar:
   *
   * GtkPlacesSidebar doesn't have a Search item anymore.  We should put that function in a toolbar-like button, like
   * in Nautilus, and do operation_mode_set (impl, OPERATION_MODE_SEARCH);
   */
1200

1201 1202 1203 1204
  if (file_is_recent_uri (location))
    operation_mode_set (impl, OPERATION_MODE_RECENT);
  else
    change_folder_and_display_error (impl, location, clear_entry);
1205
}
1206

1207 1208 1209 1210 1211 1212 1213 1214
/* Callback used when the places sidebar needs us to display an error message */
static void
places_sidebar_show_error_message_cb (GtkPlacesSidebar *sidebar,
				      const char       *primary,
				      const char       *secondary,
				      GtkFileChooserDefault *impl)
{
  error_message (impl, primary, secondary);
1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239
}

static gboolean
key_is_left_or_right (GdkEventKey *event)
{
  guint modifiers;

  modifiers = gtk_accelerator_get_default_mod_mask ();

  return ((event->keyval == GDK_KEY_Right
	   || event->keyval == GDK_KEY_KP_Right
	   || event->keyval == GDK_KEY_Left
	   || event->keyval == GDK_KEY_KP_Left)
	  && (event->state & modifiers) == 0);
}

/* Handles key press events on the file list, so that we can trap Enter to
 * activate the default button on our own.  Also, checks to see if '/' has been
 * pressed.
 */
static gboolean
browse_files_key_press_event_cb (GtkWidget   *widget,
				 GdkEventKey *event,
				 gpointer     data)
{
1240 1241
  GtkFileChooserDefault *impl = (GtkFileChooserDefault *) data;
  GtkFileChooserDefaultPrivate *priv = impl->priv;
1242
  GdkModifierType no_text_input_mask;
1243

1244 1245
  no_text_input_mask =
    gtk_widget_get_modifier_mask (widget, GDK_MODIFIER_INTENT_NO_TEXT_INPUT);
1246

1247 1248 1249 1250 1251 1252 1253 1254 1255 1256
  if ((event->keyval == GDK_KEY_slash
       || event->keyval == GDK_KEY_KP_Divide
#ifdef G_OS_UNIX
       || event->keyval == GDK_KEY_asciitilde
#endif
       ) && !(event->state & no_text_input_mask))
    {
      location_popup_handler (impl, event->string);
      return TRUE;
    }
1257

1258 1259
  if (key_is_left_or_right (event))
    {
1260
      gtk_widget_grab_focus (priv->places_sidebar);
1261 1262
      return TRUE;
    }
1263

1264 1265 1266 1267 1268
  if ((event->keyval == GDK_KEY_Return
       || event->keyval == GDK_KEY_ISO_Enter
       || event->keyval == GDK_KEY_KP_Enter
       || event->keyval == GDK_KEY_space
       || event->keyval == GDK_KEY_KP_Space)
1269
      && !(event->state & gtk_accelerator_get_default_mod_mask ())
1270 1271
      && !(priv->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ||
	   priv->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER))
1272
    {