gtkfilechooserdefault.c 301 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 17 18 19 20 21
 * 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
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

22
#include "config.h"
23

24
#include "gdk/gdkkeysyms.h"
25
#include "gtkalignment.h"
26
#include "gtkbindings.h"
27
#include "gtkcelllayout.h"
28
#include "gtkcellrendererpixbuf.h"
29
#include "gtkcellrenderertext.h"
Federico Mena Quintero's avatar
Federico Mena Quintero committed
30
#include "gtkcheckmenuitem.h"
31
#include "gtkclipboard.h"
32
#include "gtkcombobox.h"
33
#include "gtkentry.h"
34
#include "gtkexpander.h"
35
#include "gtkfilechooserprivate.h"
36
#include "gtkfilechooserdefault.h"
37
#include "gtkfilechooserdialog.h"
38
#include "gtkfilechooserembed.h"
39
#include "gtkfilechooserentry.h"
40
#include "gtkfilechoosersettings.h"
Owen Taylor's avatar
Owen Taylor committed
41 42
#include "gtkfilechooserutils.h"
#include "gtkfilechooser.h"
43
#include "gtkfilesystem.h"
Owen Taylor's avatar
Owen Taylor committed
44
#include "gtkfilesystemmodel.h"
45 46 47
#include "gtkframe.h"
#include "gtkhbox.h"
#include "gtkhpaned.h"
Federico Mena Quintero's avatar
Federico Mena Quintero committed
48
#include "gtkiconfactory.h"
49 50
#include "gtkicontheme.h"
#include "gtkimage.h"
51
#include "gtkimagemenuitem.h"
52
#include "gtklabel.h"
53
#include "gtkmarshalers.h"
54
#include "gtkmessagedialog.h"
55
#include "gtkmountoperation.h"
56
#include "gtkpathbar.h"
57
#include "gtkprivate.h"
58
#include "gtkradiobutton.h"
59 60
#include "gtkrecentfilter.h"
#include "gtkrecentmanager.h"
61
#include "gtkscrolledwindow.h"
62
#include "gtkseparatormenuitem.h"
63
#include "gtksizegroup.h"
64 65
#include "gtkstock.h"
#include "gtktable.h"
66
#include "gtktooltip.h"
67 68
#include "gtktreednd.h"
#include "gtktreeprivate.h"
69 70
#include "gtktreeselection.h"
#include "gtkvbox.h"
71
#include "gtkintl.h"
Owen Taylor's avatar
Owen Taylor committed
72

73 74
#include "gtkalias.h"

75
#include <errno.h>
76
#include <string.h>
Federico Mena Quintero's avatar
Federico Mena Quintero committed
77
#include <time.h>
78 79
#include <sys/stat.h>
#include <sys/types.h>
80
#include <locale.h>
81

82
#ifdef HAVE_UNISTD_H
83
#include <unistd.h>
84 85 86 87 88
#endif
#ifdef G_OS_WIN32
#include <io.h>
#endif

Matthias Clasen's avatar
Matthias Clasen committed
89
/* Profiling stuff */
Matthias Clasen's avatar
Matthias Clasen committed
90
#undef PROFILE_FILE_CHOOSER
Matthias Clasen's avatar
Matthias Clasen committed
91 92 93
#ifdef PROFILE_FILE_CHOOSER


94 95 96
#ifndef F_OK 
#define F_OK 0
#endif
97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117

#define PROFILE_INDENT 4
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)
118
    str = g_strdup_printf ("MARK: %s %s %s", func ? func : "", msg1 ? msg1 : "", msg2 ? msg2 : "");
119
  else
120
    str = g_strdup_printf ("MARK: %*c %s %s %s", profile_indent - 1, ' ', func ? func : "", msg1 ? msg1 : "", msg2 ? msg2 : "");
121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137

  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

138

139

140
typedef struct _GtkFileChooserDefaultClass GtkFileChooserDefaultClass;
Owen Taylor's avatar
Owen Taylor committed
141

142 143 144
#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
145

Federico Mena Quintero's avatar
Federico Mena Quintero committed
146
#define MAX_LOADING_TIME 500
147

148 149
#define DEFAULT_NEW_FOLDER_NAME _("Type name of new folder")

150
struct _GtkFileChooserDefaultClass
Owen Taylor's avatar
Owen Taylor committed
151 152 153 154
{
  GtkVBoxClass parent_class;
};

155 156 157
/* Signal IDs */
enum {
  LOCATION_POPUP,
158
  LOCATION_POPUP_ON_PASTE,
159
  UP_FOLDER,
160
  DOWN_FOLDER,
161
  HOME_FOLDER,
162
  DESKTOP_FOLDER,
163
  QUICK_BOOKMARK,
164
  LOCATION_TOGGLE_POPUP,
165
  SHOW_HIDDEN,
166
  SEARCH_SHORTCUT,
167
  RECENT_SHORTCUT,
168

169 170 171 172 173
  LAST_SIGNAL
};

static guint signals[LAST_SIGNAL] = { 0 };

174
/* Column numbers for the shortcuts tree.  Keep these in sync with shortcuts_model_create() */
175 176 177
enum {
  SHORTCUTS_COL_PIXBUF,
  SHORTCUTS_COL_NAME,
Federico Mena Quintero's avatar
Federico Mena Quintero committed
178
  SHORTCUTS_COL_DATA,
179
  SHORTCUTS_COL_TYPE,
180
  SHORTCUTS_COL_REMOVABLE,
181
  SHORTCUTS_COL_PIXBUF_VISIBLE,
182
  SHORTCUTS_COL_CANCELLABLE,
183
  SHORTCUTS_COL_NUM_COLUMNS
Owen Taylor's avatar
Owen Taylor committed
184 185
};

186
typedef enum {
187
  SHORTCUT_TYPE_FILE,
188 189
  SHORTCUT_TYPE_VOLUME,
  SHORTCUT_TYPE_SEPARATOR,
190 191
  SHORTCUT_TYPE_SEARCH,
  SHORTCUT_TYPE_RECENT
192 193
} ShortcutType;

194 195 196
#define MODEL_ATTRIBUTES "standard::name,standard::type,standard::display-name," \
                         "standard::is-hidden,standard::is-backup,standard::size," \
                         "standard::content-type,time::modified"
197 198 199 200 201 202 203 204 205 206 207 208 209 210 211
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,
  MODEL_COL_PIXBUF,
  MODEL_COL_SIZE_TEXT,
  MODEL_COL_MTIME_TEXT,
  MODEL_COL_ELLIPSIZE,
  MODEL_COL_NUM_COLUMNS
};

212 213
/* Identifiers for target types */
enum {
214
  GTK_TREE_MODEL_ROW,
215 216
};

217 218 219 220 221 222 223 224
static gboolean
search_is_possible (GtkFileChooserDefault *impl)
{
  if (impl->search_engine == NULL)
    impl->search_engine = _gtk_search_engine_new ();
  
  return impl->search_engine != NULL;
}
225

226 227
/* Interesting places in the shortcuts bar */
typedef enum {
228
  SHORTCUTS_SEARCH,
229 230
  SHORTCUTS_RECENT,
  SHORTCUTS_RECENT_SEPARATOR,
231 232 233 234
  SHORTCUTS_HOME,
  SHORTCUTS_DESKTOP,
  SHORTCUTS_VOLUMES,
  SHORTCUTS_SHORTCUTS,
235 236 237 238
  SHORTCUTS_BOOKMARKS_SEPARATOR,
  SHORTCUTS_BOOKMARKS,
  SHORTCUTS_CURRENT_FOLDER_SEPARATOR,
  SHORTCUTS_CURRENT_FOLDER
239 240
} ShortcutsIndex;

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

244
#define PREVIEW_HBOX_SPACING 12
245
#define NUM_LINES 45
246
#define NUM_CHARS 60
247

248 249
static void gtk_file_chooser_default_iface_init       (GtkFileChooserIface        *iface);
static void gtk_file_chooser_embed_default_iface_init (GtkFileChooserEmbedIface   *iface);
250 251 252 253 254 255 256 257 258 259 260 261 262

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);
263
static void     gtk_file_chooser_default_dispose      (GObject               *object);
264
static void     gtk_file_chooser_default_show_all       (GtkWidget             *widget);
265
static void     gtk_file_chooser_default_realize        (GtkWidget             *widget);
Federico Mena Quintero's avatar
Federico Mena Quintero committed
266
static void     gtk_file_chooser_default_map            (GtkWidget             *widget);
267
static void     gtk_file_chooser_default_unmap          (GtkWidget             *widget);
268 269
static void     gtk_file_chooser_default_hierarchy_changed (GtkWidget          *widget,
							    GtkWidget          *previous_toplevel);
270 271 272 273
static void     gtk_file_chooser_default_style_set      (GtkWidget             *widget,
							 GtkStyle              *previous_style);
static void     gtk_file_chooser_default_screen_changed (GtkWidget             *widget,
							 GdkScreen             *previous_screen);
274 275
static void     gtk_file_chooser_default_size_allocate  (GtkWidget             *widget,
							 GtkAllocation         *allocation);
276

277
static gboolean       gtk_file_chooser_default_set_current_folder 	   (GtkFileChooser    *chooser,
278
									    GFile             *folder,
279
									    GError           **error);
280
static gboolean       gtk_file_chooser_default_update_current_folder 	   (GtkFileChooser    *chooser,
281
									    GFile             *folder,
282
									    gboolean           keep_trail,
283
									    gboolean           clear_entry,
284
									    GError           **error);
285
static GFile *        gtk_file_chooser_default_get_current_folder 	   (GtkFileChooser    *chooser);
286
static void           gtk_file_chooser_default_set_current_name   	   (GtkFileChooser    *chooser,
287
									    const gchar       *name);
288 289
static gboolean       gtk_file_chooser_default_select_file        	   (GtkFileChooser    *chooser,
									    GFile             *file,
290
									    GError           **error);
291 292
static void           gtk_file_chooser_default_unselect_file      	   (GtkFileChooser    *chooser,
									    GFile             *file);
293 294
static void           gtk_file_chooser_default_select_all         	   (GtkFileChooser    *chooser);
static void           gtk_file_chooser_default_unselect_all       	   (GtkFileChooser    *chooser);
295 296
static GSList *       gtk_file_chooser_default_get_files          	   (GtkFileChooser    *chooser);
static GFile *        gtk_file_chooser_default_get_preview_file   	   (GtkFileChooser    *chooser);
297 298
static GtkFileSystem *gtk_file_chooser_default_get_file_system    	   (GtkFileChooser    *chooser);
static void           gtk_file_chooser_default_add_filter         	   (GtkFileChooser    *chooser,
299
									    GtkFileFilter     *filter);
300
static void           gtk_file_chooser_default_remove_filter      	   (GtkFileChooser    *chooser,
301
									    GtkFileFilter     *filter);
302 303
static GSList *       gtk_file_chooser_default_list_filters       	   (GtkFileChooser    *chooser);
static gboolean       gtk_file_chooser_default_add_shortcut_folder    (GtkFileChooser    *chooser,
304
								       GFile             *file,
305 306
								       GError           **error);
static gboolean       gtk_file_chooser_default_remove_shortcut_folder (GtkFileChooser    *chooser,
307
								       GFile             *file,
308 309
								       GError           **error);
static GSList *       gtk_file_chooser_default_list_shortcut_folders  (GtkFileChooser    *chooser);
310

311 312 313
static void           gtk_file_chooser_default_get_default_size       (GtkFileChooserEmbed *chooser_embed,
								       gint                *default_width,
								       gint                *default_height);
314
static gboolean       gtk_file_chooser_default_should_respond         (GtkFileChooserEmbed *chooser_embed);
Federico Mena Quintero's avatar
Federico Mena Quintero committed
315
static void           gtk_file_chooser_default_initial_focus          (GtkFileChooserEmbed *chooser_embed);
316

317 318
static void location_popup_handler  (GtkFileChooserDefault *impl,
				     const gchar           *path);
319
static void location_popup_on_paste_handler (GtkFileChooserDefault *impl);
320
static void location_toggle_popup_handler   (GtkFileChooserDefault *impl);
321 322 323 324 325 326 327 328
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);
329
static void recent_shortcut_handler (GtkFileChooserDefault *impl);
330
static void update_appearance       (GtkFileChooserDefault *impl);
331

332 333 334 335
static void set_current_filter   (GtkFileChooserDefault *impl,
				  GtkFileFilter         *filter);
static void check_preview_change (GtkFileChooserDefault *impl);

336
static void filter_combo_changed       (GtkComboBox           *combo_box,
337
					GtkFileChooserDefault *impl);
Federico Mena Quintero's avatar
Federico Mena Quintero committed
338 339 340 341 342

static gboolean shortcuts_key_press_event_cb (GtkWidget             *widget,
					      GdkEventKey           *event,
					      GtkFileChooserDefault *impl);

343 344 345
static gboolean shortcuts_select_func   (GtkTreeSelection      *selection,
					 GtkTreeModel          *model,
					 GtkTreePath           *path,
346
					 gboolean               path_currently_selected,
347
					 gpointer               data);
348 349
static gboolean shortcuts_get_selected  (GtkFileChooserDefault *impl,
					 GtkTreeIter           *iter);
350 351
static void shortcuts_activate_iter (GtkFileChooserDefault *impl,
				     GtkTreeIter           *iter);
352 353
static int shortcuts_get_index (GtkFileChooserDefault *impl,
				ShortcutsIndex         where);
354
static int shortcut_find_position (GtkFileChooserDefault *impl,
355
				   GFile                 *file);
356

357 358
static void bookmarks_check_add_sensitivity (GtkFileChooserDefault *impl);

359 360 361 362 363 364
static gboolean list_select_func   (GtkTreeSelection      *selection,
				    GtkTreeModel          *model,
				    GtkTreePath           *path,
				    gboolean               path_currently_selected,
				    gpointer               data);

365 366 367 368 369 370
static void list_selection_changed     (GtkTreeSelection      *tree_selection,
					GtkFileChooserDefault *impl);
static void list_row_activated         (GtkTreeView           *tree_view,
					GtkTreePath           *path,
					GtkTreeViewColumn     *column,
					GtkFileChooserDefault *impl);
371

Federico Mena Quintero's avatar
Federico Mena Quintero committed
372 373 374 375 376
static void select_func (GtkFileSystemModel *model,
			 GtkTreePath        *path,
			 GtkTreeIter        *iter,
			 gpointer            user_data);

377
static void path_bar_clicked (GtkPathBar            *path_bar,
378 379
			      GFile                 *file,
			      GFile                 *child,
380 381
                              gboolean               child_is_hidden,
                              GtkFileChooserDefault *impl);
382

383 384 385 386
static void add_bookmark_button_clicked_cb    (GtkButton             *button,
					       GtkFileChooserDefault *impl);
static void remove_bookmark_button_clicked_cb (GtkButton             *button,
					       GtkFileChooserDefault *impl);
387 388
static void save_folder_combo_changed_cb      (GtkComboBox           *combo,
					       GtkFileChooserDefault *impl);
389

390
static void update_cell_renderer_attributes (GtkFileChooserDefault *impl);
391

Federico Mena Quintero's avatar
Federico Mena Quintero committed
392
static void load_remove_timer (GtkFileChooserDefault *impl);
393
static void browse_files_center_selected_row (GtkFileChooserDefault *impl);
Federico Mena Quintero's avatar
Federico Mena Quintero committed
394

395 396 397 398
static void location_button_toggled_cb (GtkToggleButton *toggle,
					GtkFileChooserDefault *impl);
static void location_switch_to_path_bar (GtkFileChooserDefault *impl);

399 400
static void     search_stop_searching        (GtkFileChooserDefault *impl,
                                              gboolean               remove_query);
401 402 403 404
static void     search_clear_model           (GtkFileChooserDefault *impl, 
					      gboolean               remove_from_treeview);
static gboolean search_should_respond        (GtkFileChooserDefault *impl);
static void     search_switch_to_browse_mode (GtkFileChooserDefault *impl);
405
static GSList  *search_get_selected_files    (GtkFileChooserDefault *impl);
406 407
static void     search_entry_activate_cb     (GtkEntry              *entry, 
					      gpointer               data);
408
static void     settings_load                (GtkFileChooserDefault *impl);
409

410 411 412 413 414
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);
static void     recent_switch_to_browse_mode (GtkFileChooserDefault *impl);
415 416
static GSList * recent_get_selected_files    (GtkFileChooserDefault *impl);
static void     set_file_system_backend      (GtkFileChooserDefault *impl);
417
static void     unset_file_system_backend    (GtkFileChooserDefault *impl);
418

419

420

421 422 423 424 425 426 427 428


/* Drag and drop interface declarations */

typedef struct {
  GtkTreeModelFilter parent;

  GtkFileChooserDefault *impl;
429
} ShortcutsPaneModelFilter;
430 431 432

typedef struct {
  GtkTreeModelFilterClass parent_class;
433
} ShortcutsPaneModelFilterClass;
434

435 436
#define SHORTCUTS_PANE_MODEL_FILTER_TYPE (_shortcuts_pane_model_filter_get_type ())
#define SHORTCUTS_PANE_MODEL_FILTER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SHORTCUTS_PANE_MODEL_FILTER_TYPE, ShortcutsPaneModelFilter))
437

438
static void shortcuts_pane_model_filter_drag_source_iface_init (GtkTreeDragSourceIface *iface);
439

440 441
G_DEFINE_TYPE_WITH_CODE (ShortcutsPaneModelFilter,
			 _shortcuts_pane_model_filter,
442 443
			 GTK_TYPE_TREE_MODEL_FILTER,
			 G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_DRAG_SOURCE,
444
						shortcuts_pane_model_filter_drag_source_iface_init))
445

446 447 448
static GtkTreeModel *shortcuts_pane_model_filter_new (GtkFileChooserDefault *impl,
						      GtkTreeModel          *child_model,
						      GtkTreePath           *root);
449

450

451

Matthias Clasen's avatar
Matthias Clasen committed
452 453 454 455 456
G_DEFINE_TYPE_WITH_CODE (GtkFileChooserDefault, _gtk_file_chooser_default, GTK_TYPE_VBOX,
			 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
457 458

static void
Matthias Clasen's avatar
Matthias Clasen committed
459
_gtk_file_chooser_default_class_init (GtkFileChooserDefaultClass *class)
Owen Taylor's avatar
Owen Taylor committed
460
{
461 462 463
  static const guint quick_bookmark_keyvals[10] = {
    GDK_1, GDK_2, GDK_3, GDK_4, GDK_5, GDK_6, GDK_7, GDK_8, GDK_9, GDK_0
  };
Owen Taylor's avatar
Owen Taylor committed
464
  GObjectClass *gobject_class = G_OBJECT_CLASS (class);
Owen Taylor's avatar
Owen Taylor committed
465
  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
466
  GtkBindingSet *binding_set;
467
  int i;
Owen Taylor's avatar
Owen Taylor committed
468

469 470 471 472
  gobject_class->finalize = gtk_file_chooser_default_finalize;
  gobject_class->constructor = gtk_file_chooser_default_constructor;
  gobject_class->set_property = gtk_file_chooser_default_set_property;
  gobject_class->get_property = gtk_file_chooser_default_get_property;
473
  gobject_class->dispose = gtk_file_chooser_default_dispose;
Owen Taylor's avatar
Owen Taylor committed
474

475
  widget_class->show_all = gtk_file_chooser_default_show_all;
476
  widget_class->realize = gtk_file_chooser_default_realize;
Federico Mena Quintero's avatar
Federico Mena Quintero committed
477
  widget_class->map = gtk_file_chooser_default_map;
478
  widget_class->unmap = gtk_file_chooser_default_unmap;
479
  widget_class->hierarchy_changed = gtk_file_chooser_default_hierarchy_changed;
480 481
  widget_class->style_set = gtk_file_chooser_default_style_set;
  widget_class->screen_changed = gtk_file_chooser_default_screen_changed;
482
  widget_class->size_allocate = gtk_file_chooser_default_size_allocate;
Owen Taylor's avatar
Owen Taylor committed
483

484
  signals[LOCATION_POPUP] =
485 486 487 488 489 490 491 492
    g_signal_new_class_handler (I_("location-popup"),
                                G_OBJECT_CLASS_TYPE (class),
                                G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
                                G_CALLBACK (location_popup_handler),
                                NULL, NULL,
                                _gtk_marshal_VOID__STRING,
                                G_TYPE_NONE, 1, G_TYPE_STRING);

493
  signals[LOCATION_POPUP_ON_PASTE] =
494 495 496 497 498 499 500 501
    g_signal_new_class_handler (I_("location-popup-on-paste"),
                                G_OBJECT_CLASS_TYPE (class),
                                G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
                                G_CALLBACK (location_popup_on_paste_handler),
                                NULL, NULL,
                                _gtk_marshal_VOID__VOID,
                                G_TYPE_NONE, 0);

502
  signals[LOCATION_TOGGLE_POPUP] =
503 504 505 506 507 508 509 510
    g_signal_new_class_handler (I_("location-toggle-popup"),
                                G_OBJECT_CLASS_TYPE (class),
                                G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
                                G_CALLBACK (location_toggle_popup_handler),
                                NULL, NULL,
                                _gtk_marshal_VOID__VOID,
                                G_TYPE_NONE, 0);

511
  signals[UP_FOLDER] =
512 513 514 515 516 517 518 519
    g_signal_new_class_handler (I_("up-folder"),
                                G_OBJECT_CLASS_TYPE (class),
                                G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
                                G_CALLBACK (up_folder_handler),
                                NULL, NULL,
                                _gtk_marshal_VOID__VOID,
                                G_TYPE_NONE, 0);

520
  signals[DOWN_FOLDER] =
521 522 523 524 525 526 527 528
    g_signal_new_class_handler (I_("down-folder"),
                                G_OBJECT_CLASS_TYPE (class),
                                G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
                                G_CALLBACK (down_folder_handler),
                                NULL, NULL,
                                _gtk_marshal_VOID__VOID,
                                G_TYPE_NONE, 0);

529
  signals[HOME_FOLDER] =
530 531 532 533 534 535 536 537
    g_signal_new_class_handler (I_("home-folder"),
                                G_OBJECT_CLASS_TYPE (class),
                                G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
                                G_CALLBACK (home_folder_handler),
                                NULL, NULL,
                                _gtk_marshal_VOID__VOID,
                                G_TYPE_NONE, 0);

538
  signals[DESKTOP_FOLDER] =
539 540 541 542 543 544 545 546
    g_signal_new_class_handler (I_("desktop-folder"),
                                G_OBJECT_CLASS_TYPE (class),
                                G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
                                G_CALLBACK (desktop_folder_handler),
                                NULL, NULL,
                                _gtk_marshal_VOID__VOID,
                                G_TYPE_NONE, 0);

547
  signals[QUICK_BOOKMARK] =
548 549 550 551 552 553 554 555
    g_signal_new_class_handler (I_("quick-bookmark"),
                                G_OBJECT_CLASS_TYPE (class),
                                G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
                                G_CALLBACK (quick_bookmark_handler),
                                NULL, NULL,
                                _gtk_marshal_VOID__INT,
                                G_TYPE_NONE, 1, G_TYPE_INT);

556
  signals[SHOW_HIDDEN] =
557 558 559 560 561 562 563 564
    g_signal_new_class_handler (I_("show-hidden"),
                                G_OBJECT_CLASS_TYPE (class),
                                G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
                                G_CALLBACK (show_hidden_handler),
                                NULL, NULL,
                                _gtk_marshal_VOID__VOID,
                                G_TYPE_NONE, 0);

565
  signals[SEARCH_SHORTCUT] =
566 567 568 569 570 571 572 573
    g_signal_new_class_handler (I_("search-shortcut"),
                                G_OBJECT_CLASS_TYPE (class),
                                G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
                                G_CALLBACK (search_shortcut_handler),
                                NULL, NULL,
                                _gtk_marshal_VOID__VOID,
                                G_TYPE_NONE, 0);

574
  signals[RECENT_SHORTCUT] =
575 576 577 578 579 580 581
    g_signal_new_class_handler (I_("recent-shortcut"),
                                G_OBJECT_CLASS_TYPE (class),
                                G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
                                G_CALLBACK (recent_shortcut_handler),
                                NULL, NULL,
                                _gtk_marshal_VOID__VOID,
                                G_TYPE_NONE, 0);
582 583 584 585 586

  binding_set = gtk_binding_set_by_class (class);

  gtk_binding_entry_add_signal (binding_set,
				GDK_l, GDK_CONTROL_MASK,
587 588
				"location-toggle-popup",
				0);
589

590 591 592
  gtk_binding_entry_add_signal (binding_set,
				GDK_slash, 0,
				"location-popup",
593
				1, G_TYPE_STRING, "/");
Federico Mena Quintero's avatar
Federico Mena Quintero committed
594 595 596 597
  gtk_binding_entry_add_signal (binding_set,
				GDK_KP_Divide, 0,
				"location-popup",
				1, G_TYPE_STRING, "/");
598

599 600 601 602 603 604 605
#ifdef G_OS_UNIX
  gtk_binding_entry_add_signal (binding_set,
				GDK_asciitilde, 0,
				"location-popup",
				1, G_TYPE_STRING, "~");
#endif

606 607 608 609
  gtk_binding_entry_add_signal (binding_set,
				GDK_v, GDK_CONTROL_MASK,
				"location-popup-on-paste",
				0);
610
  gtk_binding_entry_add_signal (binding_set,
611
				GDK_Up, GDK_MOD1_MASK,
612 613
				"up-folder",
				0);
614 615 616 617
  gtk_binding_entry_add_signal (binding_set,
		  		GDK_BackSpace, 0,
				"up-folder",
				0);
618
  gtk_binding_entry_add_signal (binding_set,
619
				GDK_KP_Up, GDK_MOD1_MASK,
620 621 622
				"up-folder",
				0);

623 624 625 626 627 628 629 630 631
  gtk_binding_entry_add_signal (binding_set,
				GDK_Down, GDK_MOD1_MASK,
				"down-folder",
				0);
  gtk_binding_entry_add_signal (binding_set,
				GDK_KP_Down, GDK_MOD1_MASK,
				"down-folder",
				0);

632
  gtk_binding_entry_add_signal (binding_set,
633 634
				GDK_Home, GDK_MOD1_MASK,
				"home-folder",
635 636
				0);
  gtk_binding_entry_add_signal (binding_set,
637 638
				GDK_KP_Home, GDK_MOD1_MASK,
				"home-folder",
639
				0);
640 641 642 643
  gtk_binding_entry_add_signal (binding_set,
				GDK_d, GDK_MOD1_MASK,
				"desktop-folder",
				0);
644 645 646 647
  gtk_binding_entry_add_signal (binding_set,
				GDK_h, GDK_CONTROL_MASK,
                                "show-hidden",
                                0);
648 649 650 651
  gtk_binding_entry_add_signal (binding_set,
                                GDK_s, GDK_MOD1_MASK,
                                "search-shortcut",
                                0);
652 653 654 655
  gtk_binding_entry_add_signal (binding_set,
                                GDK_r, GDK_MOD1_MASK,
                                "recent-shortcut",
                                0);
656

657 658 659 660 661 662
  for (i = 0; i < 10; i++)
    gtk_binding_entry_add_signal (binding_set,
				  quick_bookmark_keyvals[i], GDK_MOD1_MASK,
				  "quick-bookmark",
				  1, G_TYPE_INT, i);

Owen Taylor's avatar
Owen Taylor committed
663 664 665 666
  _gtk_file_chooser_install_properties (gobject_class);
}

static void
667 668
gtk_file_chooser_default_iface_init (GtkFileChooserIface *iface)
{
669 670
  iface->select_file = gtk_file_chooser_default_select_file;
  iface->unselect_file = gtk_file_chooser_default_unselect_file;
671 672
  iface->select_all = gtk_file_chooser_default_select_all;
  iface->unselect_all = gtk_file_chooser_default_unselect_all;
673 674
  iface->get_files = gtk_file_chooser_default_get_files;
  iface->get_preview_file = gtk_file_chooser_default_get_preview_file;
675 676 677 678 679 680 681 682 683 684
  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;
  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;
Owen Taylor's avatar
Owen Taylor committed
685 686
}

687 688 689 690
static void
gtk_file_chooser_embed_default_iface_init (GtkFileChooserEmbedIface *iface)
{
  iface->get_default_size = gtk_file_chooser_default_get_default_size;
691
  iface->should_respond = gtk_file_chooser_default_should_respond;
Federico Mena Quintero's avatar
Federico Mena Quintero committed
692
  iface->initial_focus = gtk_file_chooser_default_initial_focus;
693
}
Matthias Clasen's avatar
Matthias Clasen committed
694

Owen Taylor's avatar
Owen Taylor committed
695
static void
Matthias Clasen's avatar
Matthias Clasen committed
696
_gtk_file_chooser_default_init (GtkFileChooserDefault *impl)
Owen Taylor's avatar
Owen Taylor committed
697
{
698
  profile_start ("start", NULL);
699 700 701
#ifdef PROFILE_FILE_CHOOSER
  access ("MARK: *** CREATE FILE CHOOSER", F_OK);
#endif
702 703
  impl->local_only = TRUE;
  impl->preview_widget_active = TRUE;
704
  impl->use_preview_label = TRUE;
705 706
  impl->select_multiple = FALSE;
  impl->show_hidden = FALSE;
707
  impl->show_size_column = TRUE;
Federico Mena Quintero's avatar
Federico Mena Quintero committed
708
  impl->icon_size = FALLBACK_ICON_SIZE;
Federico Mena Quintero's avatar
Federico Mena Quintero committed
709
  impl->load_state = LOAD_EMPTY;
710
  impl->reload_state = RELOAD_EMPTY;
711
  impl->pending_select_files = NULL;
712
  impl->location_mode = LOCATION_MODE_PATH_BAR;
713
  impl->operation_mode = OPERATION_MODE_BROWSE;
714
  impl->sort_column = MODEL_COL_NAME;
715
  impl->sort_order = GTK_SORT_ASCENDING;
716
  impl->recent_manager = gtk_recent_manager_get_default ();
717
  impl->create_folders = TRUE;
718

719
  gtk_box_set_spacing (GTK_BOX (impl), 12);
Matthias Clasen's avatar
Matthias Clasen committed
720

721 722
  set_file_system_backend (impl);

723
  profile_end ("end", NULL);
724 725
}

Federico Mena Quintero's avatar
Federico Mena Quintero committed
726 727 728 729 730 731
/* Frees the data columns for the specified iter in the shortcuts model*/
static void
shortcuts_free_row_data (GtkFileChooserDefault *impl,
			 GtkTreeIter           *iter)
{
  gpointer col_data;
732
  ShortcutType shortcut_type;
733
  GCancellable *cancellable;
Federico Mena Quintero's avatar
Federico Mena Quintero committed
734 735 736

  gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), iter,
		      SHORTCUTS_COL_DATA, &col_data,
737
		      SHORTCUTS_COL_TYPE, &shortcut_type,
738
		      SHORTCUTS_COL_CANCELLABLE, &cancellable,
Federico Mena Quintero's avatar
Federico Mena Quintero committed
739
		      -1);
740

741 742
  if (cancellable)
    g_cancellable_cancel (cancellable);
743

744
  if (!(shortcut_type == SHORTCUT_TYPE_FILE || 
745 746
	shortcut_type == SHORTCUT_TYPE_VOLUME) ||
      !col_data)
Federico Mena Quintero's avatar
Federico Mena Quintero committed
747 748
    return;

749
  if (shortcut_type == SHORTCUT_TYPE_VOLUME)
Federico Mena Quintero's avatar
Federico Mena Quintero committed
750 751 752 753
    {
      GtkFileSystemVolume *volume;

      volume = col_data;
754
      _gtk_file_system_volume_free (volume);
Federico Mena Quintero's avatar
Federico Mena Quintero committed
755 756 757
    }
  else
    {
758
      GFile *file;
Federico Mena Quintero's avatar
Federico Mena Quintero committed
759

760
      g_assert (shortcut_type == SHORTCUT_TYPE_FILE);
761

762 763
      file = col_data;
      g_object_unref (file);
Federico Mena Quintero's avatar
Federico Mena Quintero committed
764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786
    }
}

/* Frees all the data columns in the shortcuts model */
static void
shortcuts_free (GtkFileChooserDefault *impl)
{
  GtkTreeIter iter;

  if (!impl->shortcuts_model)
    return;

  if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (impl->shortcuts_model), &iter))
    do
      {
	shortcuts_free_row_data (impl, &iter);
      }
    while (gtk_tree_model_iter_next (GTK_TREE_MODEL (impl->shortcuts_model), &iter));

  g_object_unref (impl->shortcuts_model);
  impl->shortcuts_model = NULL;
}

Federico Mena Quintero's avatar
Federico Mena Quintero committed
787
static void
788
pending_select_files_free (GtkFileChooserDefault *impl)
Federico Mena Quintero's avatar
Federico Mena Quintero committed
789
{
790 791 792
  g_slist_foreach (impl->pending_select_files, (GFunc) g_object_unref, NULL);
  g_slist_free (impl->pending_select_files);
  impl->pending_select_files = NULL;
Federico Mena Quintero's avatar
Federico Mena Quintero committed
793 794 795
}

static void
796 797
pending_select_files_add (GtkFileChooserDefault *impl,
			  GFile                 *file)
Federico Mena Quintero's avatar
Federico Mena Quintero committed
798
{
799 800
  impl->pending_select_files =
    g_slist_prepend (impl->pending_select_files, g_object_ref (file));
Federico Mena Quintero's avatar
Federico Mena Quintero committed
801 802 803 804 805 806 807 808 809 810
}

/* Used from gtk_tree_selection_selected_foreach() */
static void
store_selection_foreach (GtkTreeModel *model,
			 GtkTreePath  *path,
			 GtkTreeIter  *iter,
			 gpointer      data)
{
  GtkFileChooserDefault *impl;
811
  GFile *file;
Federico Mena Quintero's avatar
Federico Mena Quintero committed
812 813 814

  impl = GTK_FILE_CHOOSER_DEFAULT (data);

815
  file = _gtk_file_system_model_get_file (GTK_FILE_SYSTEM_MODEL (model), iter);
816
  pending_select_files_add (impl, file);
Federico Mena Quintero's avatar
Federico Mena Quintero committed
817 818 819 820 821 822
}

/* Stores the current selection in the list of paths to select; this is used to
 * preserve the selection when reloading the current folder.
 */
static void
823
pending_select_files_store_selection (GtkFileChooserDefault *impl)
Federico Mena Quintero's avatar
Federico Mena Quintero committed
824 825 826 827 828 829 830
{
  GtkTreeSelection *selection;

  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
  gtk_tree_selection_selected_foreach (selection, store_selection_foreach, impl);
}

831
static void
832
gtk_file_chooser_default_finalize (GObject *object)
833
{
834
  GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (object);
835
  GSList *l;
836

837 838
  unset_file_system_backend (impl);

839 840 841 842 843
  if (impl->shortcuts_pane_filter_model)
    g_object_unref (impl->shortcuts_pane_filter_model);

  if (impl->shortcuts_combo_filter_model)
    g_object_unref (impl->shortcuts_combo_filter_model);
Federico Mena Quintero's avatar
Federico Mena Quintero committed
844 845 846

  shortcuts_free (impl);

847 848
  g_free (impl->browse_files_last_selected_name);

849 850 851 852 853 854 855 856 857 858 859
  for (l = impl->filters; l; l = l->next)
    {
      GtkFileFilter *filter;

      filter = GTK_FILE_FILTER (l->data);
      g_object_unref (filter);
    }
  g_slist_free (impl->filters);

  if (impl->current_filter)
    g_object_unref (impl->current_filter);
Federico Mena Quintero's avatar
Federico Mena Quintero committed
860

861 862
  if (impl->current_volume_file)
    g_object_unref (impl->current_volume_file);
Federico Mena Quintero's avatar
Federico Mena Quintero committed
863 864

  if (impl->current_folder)
865
    g_object_unref (impl->current_folder);
Federico Mena Quintero's avatar
Federico Mena Quintero committed
866

867 868
  if (impl->preview_file)
    g_object_unref (impl->preview_file);
Federico Mena Quintero's avatar
Federico Mena Quintero committed
869

870 871 872
  if (impl->browse_path_bar_size_group)
    g_object_unref (impl->browse_path_bar_size_group);

Federico Mena Quintero's avatar
Federico Mena Quintero committed
873 874
  load_remove_timer (impl);

875 876 877 878
  /* Free all the Models we have */
  if (impl->browse_files_model)
    g_object_unref (impl->browse_files_model);

879 880
  search_clear_model (impl, FALSE);
  recent_clear_model (impl, FALSE);
881

882 883
  g_free (impl->preview_display_name);

Federico Mena Quintero's avatar
Federico Mena Quintero committed
884 885
  g_free (impl->edited_new_text);

Matthias Clasen's avatar
Matthias Clasen committed
886
  G_OBJECT_CLASS (_gtk_file_chooser_default_parent_class)->finalize (object);
887 888
}

889
/* Shows an error dialog set as transient for the specified window */
890
static void
891
error_message_with_parent (GtkWindow  *parent,
892 893
			   const char *msg,
			   const char *detail)
894 895 896
{
  GtkWidget *dialog;

897
  dialog = gtk_message_dialog_new (parent,
898 899
				   GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
				   GTK_MESSAGE_ERROR,
900
				   GTK_BUTTONS_OK,
901 902
				   "%s",
				   msg);
903 904
  gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
					    "%s", detail);
905

906
  if (parent && parent->group)
907 908
    gtk_window_group_add_window (parent->group, GTK_WINDOW (dialog));

909 910 911 912
  gtk_dialog_run (GTK_DIALOG (dialog));
  gtk_widget_destroy (dialog);
}

Federico Mena Quintero's avatar
Federico Mena Quintero committed
913 914 915
/* Returns a toplevel GtkWindow, or NULL if none */
static GtkWindow *
get_toplevel (GtkWidget *widget)
Federico Mena Quintero's avatar
Federico Mena Quintero committed
916
{
917 918
  GtkWidget *toplevel;

Federico Mena Quintero's avatar
Federico Mena Quintero committed
919
  toplevel = gtk_widget_get_toplevel (widget);