gtkfilechooserdefault.c 303 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 Jardon's avatar
Javier Jardon 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
#include "config.h"
21

22 23
#include "gtkfilechooserdefault.h"

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

74
#include <errno.h>
75
#include <string.h>
76
#include <time.h>
77 78
#include <sys/stat.h>
#include <sys/types.h>
79
#include <locale.h>
80

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

88 89 90 91
/* Values for GtkSelection-related "info" fields */
#define SELECTION_TEXT 0
#define SELECTION_URI  1

Matthias Clasen's avatar
Matthias Clasen committed
92
/* Profiling stuff */
Matthias Clasen's avatar
Matthias Clasen committed
93
#undef PROFILE_FILE_CHOOSER
Matthias Clasen's avatar
Matthias Clasen committed
94 95 96
#ifdef PROFILE_FILE_CHOOSER


97 98 99
#ifndef F_OK 
#define F_OK 0
#endif
100 101

#define PROFILE_INDENT 4
102

103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
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)
122
    str = g_strdup_printf ("MARK: %s %s %s", func ? func : "", msg1 ? msg1 : "", msg2 ? msg2 : "");
123
  else
124
    str = g_strdup_printf ("MARK: %*c %s %s %s", profile_indent - 1, ' ', func ? func : "", msg1 ? msg1 : "", msg2 ? msg2 : "");
125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141

  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

142

143

144
typedef struct _GtkFileChooserDefaultClass GtkFileChooserDefaultClass;
Owen Taylor's avatar
Owen Taylor committed
145

146 147 148
#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
149

150
#define MAX_LOADING_TIME 500
151

152 153
#define DEFAULT_NEW_FOLDER_NAME _("Type name of new folder")

154
struct _GtkFileChooserDefaultClass
Owen Taylor's avatar
Owen Taylor committed
155
{
156
  GtkBoxClass parent_class;
Owen Taylor's avatar
Owen Taylor committed
157 158
};

159 160 161
/* Signal IDs */
enum {
  LOCATION_POPUP,
162
  LOCATION_POPUP_ON_PASTE,
163
  UP_FOLDER,
164
  DOWN_FOLDER,
165
  HOME_FOLDER,
166
  DESKTOP_FOLDER,
167
  QUICK_BOOKMARK,
168
  LOCATION_TOGGLE_POPUP,
169
  SHOW_HIDDEN,
170
  SEARCH_SHORTCUT,
171
  RECENT_SHORTCUT,
172

173 174 175 176 177
  LAST_SIGNAL
};

static guint signals[LAST_SIGNAL] = { 0 };

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

190
typedef enum {
191
  SHORTCUT_TYPE_FILE,
192 193
  SHORTCUT_TYPE_VOLUME,
  SHORTCUT_TYPE_SEPARATOR,
194 195
  SHORTCUT_TYPE_SEARCH,
  SHORTCUT_TYPE_RECENT
196 197
} ShortcutType;

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

217 218 219 220 221 222 223 224 225
/* 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 */	\
226
	G_TYPE_BOOLEAN,		  /* MODEL_COL_IS_SENSITIVE */	\
227 228 229 230 231
	GDK_TYPE_PIXBUF,	  /* MODEL_COL_PIXBUF */	\
	G_TYPE_STRING,		  /* MODEL_COL_SIZE_TEXT */	\
	G_TYPE_STRING,		  /* MODEL_COL_MTIME_TEXT */	\
	PANGO_TYPE_ELLIPSIZE_MODE /* MODEL_COL_ELLIPSIZE */

232 233
/* Identifiers for target types */
enum {
234
  GTK_TREE_MODEL_ROW,
235 236
};

237 238
/* Interesting places in the shortcuts bar */
typedef enum {
239
  SHORTCUTS_SEARCH,
240 241
  SHORTCUTS_RECENT,
  SHORTCUTS_RECENT_SEPARATOR,
242 243 244 245
  SHORTCUTS_HOME,
  SHORTCUTS_DESKTOP,
  SHORTCUTS_VOLUMES,
  SHORTCUTS_SHORTCUTS,
246 247 248 249
  SHORTCUTS_BOOKMARKS_SEPARATOR,
  SHORTCUTS_BOOKMARKS,
  SHORTCUTS_CURRENT_FOLDER_SEPARATOR,
  SHORTCUTS_CURRENT_FOLDER
250 251
} ShortcutsIndex;

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

255
#define PREVIEW_HBOX_SPACING 12
256
#define NUM_LINES 45
257
#define NUM_CHARS 60
258

259
#define SETTINGS_KEY_LAST_FOLDER_URI     "last-folder-uri"
260 261 262 263 264 265 266 267
#define SETTINGS_KEY_LOCATION_MODE       "location-mode"
#define SETTINGS_KEY_SHOW_HIDDEN         "show-hidden"
#define SETTINGS_KEY_SHOW_SIZE_COLUMN    "show-size-column"
#define SETTINGS_KEY_SORT_COLUMN         "sort-column"
#define SETTINGS_KEY_SORT_ORDER          "sort-order"
#define SETTINGS_KEY_WINDOW_POSITION     "window-position"
#define SETTINGS_KEY_WINDOW_SIZE         "window-size"

268 269
static void gtk_file_chooser_default_iface_init       (GtkFileChooserIface        *iface);
static void gtk_file_chooser_embed_default_iface_init (GtkFileChooserEmbedIface   *iface);
270 271 272 273 274 275 276 277 278 279 280 281 282

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);
283
static void     gtk_file_chooser_default_dispose      (GObject               *object);
284
static void     gtk_file_chooser_default_show_all       (GtkWidget             *widget);
285
static void     gtk_file_chooser_default_realize        (GtkWidget             *widget);
286
static void     gtk_file_chooser_default_map            (GtkWidget             *widget);
287
static void     gtk_file_chooser_default_unmap          (GtkWidget             *widget);
288 289
static void     gtk_file_chooser_default_hierarchy_changed (GtkWidget          *widget,
							    GtkWidget          *previous_toplevel);
290
static void     gtk_file_chooser_default_style_updated  (GtkWidget             *widget);
291 292
static void     gtk_file_chooser_default_screen_changed (GtkWidget             *widget,
							 GdkScreen             *previous_screen);
293 294
static void     gtk_file_chooser_default_size_allocate  (GtkWidget             *widget,
							 GtkAllocation         *allocation);
295

296
static gboolean       gtk_file_chooser_default_set_current_folder 	   (GtkFileChooser    *chooser,
297
									    GFile             *folder,
298
									    GError           **error);
299
static gboolean       gtk_file_chooser_default_update_current_folder 	   (GtkFileChooser    *chooser,
300
									    GFile             *folder,
301
									    gboolean           keep_trail,
302
									    gboolean           clear_entry,
303
									    GError           **error);
304
static GFile *        gtk_file_chooser_default_get_current_folder 	   (GtkFileChooser    *chooser);
305
static void           gtk_file_chooser_default_set_current_name   	   (GtkFileChooser    *chooser,
306
									    const gchar       *name);
307 308
static gboolean       gtk_file_chooser_default_select_file        	   (GtkFileChooser    *chooser,
									    GFile             *file,
309
									    GError           **error);
310 311
static void           gtk_file_chooser_default_unselect_file      	   (GtkFileChooser    *chooser,
									    GFile             *file);
312 313
static void           gtk_file_chooser_default_select_all         	   (GtkFileChooser    *chooser);
static void           gtk_file_chooser_default_unselect_all       	   (GtkFileChooser    *chooser);
314 315
static GSList *       gtk_file_chooser_default_get_files          	   (GtkFileChooser    *chooser);
static GFile *        gtk_file_chooser_default_get_preview_file   	   (GtkFileChooser    *chooser);
316 317
static GtkFileSystem *gtk_file_chooser_default_get_file_system    	   (GtkFileChooser    *chooser);
static void           gtk_file_chooser_default_add_filter         	   (GtkFileChooser    *chooser,
318
									    GtkFileFilter     *filter);
319
static void           gtk_file_chooser_default_remove_filter      	   (GtkFileChooser    *chooser,
320
									    GtkFileFilter     *filter);
321 322
static GSList *       gtk_file_chooser_default_list_filters       	   (GtkFileChooser    *chooser);
static gboolean       gtk_file_chooser_default_add_shortcut_folder    (GtkFileChooser    *chooser,
323
								       GFile             *file,
324 325
								       GError           **error);
static gboolean       gtk_file_chooser_default_remove_shortcut_folder (GtkFileChooser    *chooser,
326
								       GFile             *file,
327 328
								       GError           **error);
static GSList *       gtk_file_chooser_default_list_shortcut_folders  (GtkFileChooser    *chooser);
329

330 331 332
static void           gtk_file_chooser_default_get_default_size       (GtkFileChooserEmbed *chooser_embed,
								       gint                *default_width,
								       gint                *default_height);
333
static gboolean       gtk_file_chooser_default_should_respond         (GtkFileChooserEmbed *chooser_embed);
Federico Mena Quintero's avatar
Federico Mena Quintero committed
334
static void           gtk_file_chooser_default_initial_focus          (GtkFileChooserEmbed *chooser_embed);
335

336 337
static void add_selection_to_recent_list (GtkFileChooserDefault *impl);

338 339
static void location_popup_handler  (GtkFileChooserDefault *impl,
				     const gchar           *path);
340
static void location_popup_on_paste_handler (GtkFileChooserDefault *impl);
341
static void location_toggle_popup_handler   (GtkFileChooserDefault *impl);
342 343 344 345 346 347 348 349
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);
350
static void recent_shortcut_handler (GtkFileChooserDefault *impl);
351
static void update_appearance       (GtkFileChooserDefault *impl);
352

353 354 355 356
static void set_current_filter   (GtkFileChooserDefault *impl,
				  GtkFileFilter         *filter);
static void check_preview_change (GtkFileChooserDefault *impl);

357
static void filter_combo_changed       (GtkComboBox           *combo_box,
358
					GtkFileChooserDefault *impl);
Federico Mena Quintero's avatar
Federico Mena Quintero committed
359 360 361 362 363

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

364 365 366
static gboolean shortcuts_select_func   (GtkTreeSelection      *selection,
					 GtkTreeModel          *model,
					 GtkTreePath           *path,
367
					 gboolean               path_currently_selected,
368
					 gpointer               data);
369 370
static gboolean shortcuts_get_selected  (GtkFileChooserDefault *impl,
					 GtkTreeIter           *iter);
371 372
static void shortcuts_activate_iter (GtkFileChooserDefault *impl,
				     GtkTreeIter           *iter);
373 374
static int shortcuts_get_index (GtkFileChooserDefault *impl,
				ShortcutsIndex         where);
375
static int shortcut_find_position (GtkFileChooserDefault *impl,
376
				   GFile                 *file);
377

378 379
static void bookmarks_check_add_sensitivity (GtkFileChooserDefault *impl);

380 381 382 383 384 385
static gboolean list_select_func   (GtkTreeSelection      *selection,
				    GtkTreeModel          *model,
				    GtkTreePath           *path,
				    gboolean               path_currently_selected,
				    gpointer               data);

386 387 388 389 390 391
static void list_selection_changed     (GtkTreeSelection      *tree_selection,
					GtkFileChooserDefault *impl);
static void list_row_activated         (GtkTreeView           *tree_view,
					GtkTreePath           *path,
					GtkTreeViewColumn     *column,
					GtkFileChooserDefault *impl);
392

393
static void path_bar_clicked (GtkPathBar            *path_bar,
394 395
			      GFile                 *file,
			      GFile                 *child,
396 397
                              gboolean               child_is_hidden,
                              GtkFileChooserDefault *impl);
398

399 400 401 402 403
static void add_bookmark_button_clicked_cb    (GtkButton             *button,
					       GtkFileChooserDefault *impl);
static void remove_bookmark_button_clicked_cb (GtkButton             *button,
					       GtkFileChooserDefault *impl);

404
static void update_cell_renderer_attributes (GtkFileChooserDefault *impl);
405

406
static void load_remove_timer (GtkFileChooserDefault *impl);
407
static void browse_files_center_selected_row (GtkFileChooserDefault *impl);
408

409 410 411 412
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
413 414
static void stop_loading_and_clear_list_model (GtkFileChooserDefault *impl,
                                               gboolean remove_from_treeview);
415 416

static void     search_setup_widgets         (GtkFileChooserDefault *impl);
417 418
static void     search_stop_searching        (GtkFileChooserDefault *impl,
                                              gboolean               remove_query);
419 420 421
static void     search_clear_model           (GtkFileChooserDefault *impl, 
					      gboolean               remove_from_treeview);
static gboolean search_should_respond        (GtkFileChooserDefault *impl);
422
static GSList  *search_get_selected_files    (GtkFileChooserDefault *impl);
423 424
static void     search_entry_activate_cb     (GtkEntry              *entry, 
					      gpointer               data);
425
static void     settings_load                (GtkFileChooserDefault *impl);
426

427
static void     recent_start_loading         (GtkFileChooserDefault *impl);
428 429 430 431
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);
432 433
static GSList * recent_get_selected_files    (GtkFileChooserDefault *impl);
static void     set_file_system_backend      (GtkFileChooserDefault *impl);
434
static void     unset_file_system_backend    (GtkFileChooserDefault *impl);
435

436

437

438 439 440 441 442 443 444 445


/* Drag and drop interface declarations */

typedef struct {
  GtkTreeModelFilter parent;

  GtkFileChooserDefault *impl;
446
} ShortcutsPaneModelFilter;
447 448 449

typedef struct {
  GtkTreeModelFilterClass parent_class;
450
} ShortcutsPaneModelFilterClass;
451

452 453
#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))
454

455
static void shortcuts_pane_model_filter_drag_source_iface_init (GtkTreeDragSourceIface *iface);
456

457 458
G_DEFINE_TYPE_WITH_CODE (ShortcutsPaneModelFilter,
			 _shortcuts_pane_model_filter,
459 460
			 GTK_TYPE_TREE_MODEL_FILTER,
			 G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_DRAG_SOURCE,
461
						shortcuts_pane_model_filter_drag_source_iface_init))
462

463 464 465
static GtkTreeModel *shortcuts_pane_model_filter_new (GtkFileChooserDefault *impl,
						      GtkTreeModel          *child_model,
						      GtkTreePath           *root);
466

467

468

469
G_DEFINE_TYPE_WITH_CODE (GtkFileChooserDefault, _gtk_file_chooser_default, GTK_TYPE_BOX,
Matthias Clasen's avatar
Matthias Clasen committed
470 471 472 473
			 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
474

475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490

static void
add_normal_and_shifted_binding (GtkBindingSet  *binding_set,
				guint           keyval,
				GdkModifierType modifiers,
				const gchar    *signal_name)
{
  gtk_binding_entry_add_signal (binding_set,
				keyval, modifiers,
				signal_name, 0);

  gtk_binding_entry_add_signal (binding_set,
				keyval, modifiers | GDK_SHIFT_MASK,
				signal_name, 0);
}

Owen Taylor's avatar
Owen Taylor committed
491
static void
Matthias Clasen's avatar
Matthias Clasen committed
492
_gtk_file_chooser_default_class_init (GtkFileChooserDefaultClass *class)
Owen Taylor's avatar
Owen Taylor committed
493
{
494
  static const guint quick_bookmark_keyvals[10] = {
495
    GDK_KEY_1, GDK_KEY_2, GDK_KEY_3, GDK_KEY_4, GDK_KEY_5, GDK_KEY_6, GDK_KEY_7, GDK_KEY_8, GDK_KEY_9, GDK_KEY_0
496
  };
Owen Taylor's avatar
Owen Taylor committed
497
  GObjectClass *gobject_class = G_OBJECT_CLASS (class);
Owen Taylor's avatar
Owen Taylor committed
498
  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
499
  GtkBindingSet *binding_set;
500
  int i;
Owen Taylor's avatar
Owen Taylor committed
501

502 503 504 505
  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;
506
  gobject_class->dispose = gtk_file_chooser_default_dispose;
Owen Taylor's avatar
Owen Taylor committed
507

508
  widget_class->show_all = gtk_file_chooser_default_show_all;
509
  widget_class->realize = gtk_file_chooser_default_realize;
510
  widget_class->map = gtk_file_chooser_default_map;
511
  widget_class->unmap = gtk_file_chooser_default_unmap;
512
  widget_class->hierarchy_changed = gtk_file_chooser_default_hierarchy_changed;
513
  widget_class->style_updated = gtk_file_chooser_default_style_updated;
514
  widget_class->screen_changed = gtk_file_chooser_default_screen_changed;
515
  widget_class->size_allocate = gtk_file_chooser_default_size_allocate;
Owen Taylor's avatar
Owen Taylor committed
516

517
  signals[LOCATION_POPUP] =
518 519 520 521 522 523 524 525
    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);

526
  signals[LOCATION_POPUP_ON_PASTE] =
527 528 529 530 531 532 533 534
    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);

535
  signals[LOCATION_TOGGLE_POPUP] =
536 537 538 539 540 541 542 543
    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);

544
  signals[UP_FOLDER] =
545 546 547 548 549 550 551 552
    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);

553
  signals[DOWN_FOLDER] =
554 555 556 557 558 559 560 561
    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);

562
  signals[HOME_FOLDER] =
563 564 565 566 567 568 569 570
    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);

571
  signals[DESKTOP_FOLDER] =
572 573 574 575 576 577 578 579
    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);

580
  signals[QUICK_BOOKMARK] =
581 582 583 584 585 586 587 588
    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);

589
  signals[SHOW_HIDDEN] =
590 591 592 593 594 595 596 597
    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);

598
  signals[SEARCH_SHORTCUT] =
599 600 601 602 603 604 605 606
    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);

607
  signals[RECENT_SHORTCUT] =
608 609 610 611 612 613 614
    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);
615 616 617 618

  binding_set = gtk_binding_set_by_class (class);

  gtk_binding_entry_add_signal (binding_set,
619
				GDK_KEY_l, GDK_CONTROL_MASK,
620 621
				"location-toggle-popup",
				0);
622

623
  gtk_binding_entry_add_signal (binding_set,
624
				GDK_KEY_slash, 0,
625
				"location-popup",
626
				1, G_TYPE_STRING, "/");
Federico Mena Quintero's avatar
Federico Mena Quintero committed
627
  gtk_binding_entry_add_signal (binding_set,
628
				GDK_KEY_KP_Divide, 0,
Federico Mena Quintero's avatar
Federico Mena Quintero committed
629 630
				"location-popup",
				1, G_TYPE_STRING, "/");
631

632 633
#ifdef G_OS_UNIX
  gtk_binding_entry_add_signal (binding_set,
634
				GDK_KEY_asciitilde, 0,
635 636 637 638
				"location-popup",
				1, G_TYPE_STRING, "~");
#endif

639
  gtk_binding_entry_add_signal (binding_set,
640
				GDK_KEY_v, GDK_CONTROL_MASK,
641 642
				"location-popup-on-paste",
				0);
643
  gtk_binding_entry_add_signal (binding_set,
644
		  		GDK_KEY_BackSpace, 0,
645 646
				"up-folder",
				0);
647

648 649 650 651 652 653 654 655 656 657 658 659 660 661
  add_normal_and_shifted_binding (binding_set,
				  GDK_KEY_Up, GDK_MOD1_MASK,
				  "up-folder");

  add_normal_and_shifted_binding (binding_set,
				  GDK_KEY_KP_Up, GDK_MOD1_MASK,
				  "up-folder");

  add_normal_and_shifted_binding (binding_set,
				  GDK_KEY_Down, GDK_MOD1_MASK,
				  "down-folder");
  add_normal_and_shifted_binding (binding_set,
				  GDK_KEY_KP_Down, GDK_MOD1_MASK,
				  "down-folder");
662

663
  gtk_binding_entry_add_signal (binding_set,
664
				GDK_KEY_Home, GDK_MOD1_MASK,
665
				"home-folder",
666 667
				0);
  gtk_binding_entry_add_signal (binding_set,
668
				GDK_KEY_KP_Home, GDK_MOD1_MASK,
669
				"home-folder",
670
				0);
671
  gtk_binding_entry_add_signal (binding_set,
672
				GDK_KEY_d, GDK_MOD1_MASK,
673 674
				"desktop-folder",
				0);
675
  gtk_binding_entry_add_signal (binding_set,
676
				GDK_KEY_h, GDK_CONTROL_MASK,
677 678
                                "show-hidden",
                                0);
679
  gtk_binding_entry_add_signal (binding_set,
680
                                GDK_KEY_s, GDK_MOD1_MASK,
681 682
                                "search-shortcut",
                                0);
683
  gtk_binding_entry_add_signal (binding_set,
684
                                GDK_KEY_r, GDK_MOD1_MASK,
685 686
                                "recent-shortcut",
                                0);
687

688 689 690 691 692 693
  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
694 695 696 697
  _gtk_file_chooser_install_properties (gobject_class);
}

static void
698 699
gtk_file_chooser_default_iface_init (GtkFileChooserIface *iface)
{
700 701
  iface->select_file = gtk_file_chooser_default_select_file;
  iface->unselect_file = gtk_file_chooser_default_unselect_file;
702 703
  iface->select_all = gtk_file_chooser_default_select_all;
  iface->unselect_all = gtk_file_chooser_default_unselect_all;
704 705
  iface->get_files = gtk_file_chooser_default_get_files;
  iface->get_preview_file = gtk_file_chooser_default_get_preview_file;
706 707 708 709 710 711 712 713 714 715
  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
716 717
}

718 719 720 721
static void
gtk_file_chooser_embed_default_iface_init (GtkFileChooserEmbedIface *iface)
{
  iface->get_default_size = gtk_file_chooser_default_get_default_size;
722
  iface->should_respond = gtk_file_chooser_default_should_respond;
Federico Mena Quintero's avatar
Federico Mena Quintero committed
723
  iface->initial_focus = gtk_file_chooser_default_initial_focus;
724
}
Matthias Clasen's avatar
Matthias Clasen committed
725

Owen Taylor's avatar
Owen Taylor committed
726
static void
Matthias Clasen's avatar
Matthias Clasen committed
727
_gtk_file_chooser_default_init (GtkFileChooserDefault *impl)
Owen Taylor's avatar
Owen Taylor committed
728
{
729
  profile_start ("start", NULL);
730 731 732
#ifdef PROFILE_FILE_CHOOSER
  access ("MARK: *** CREATE FILE CHOOSER", F_OK);
#endif
733 734
  impl->local_only = TRUE;
  impl->preview_widget_active = TRUE;
735
  impl->use_preview_label = TRUE;
736 737
  impl->select_multiple = FALSE;
  impl->show_hidden = FALSE;
738
  impl->show_size_column = TRUE;
Federico Mena Quintero's avatar
Federico Mena Quintero committed
739
  impl->icon_size = FALLBACK_ICON_SIZE;
740
  impl->load_state = LOAD_EMPTY;
741
  impl->reload_state = RELOAD_EMPTY;
742
  impl->pending_select_files = NULL;
743
  impl->location_mode = LOCATION_MODE_PATH_BAR;
744
  impl->operation_mode = OPERATION_MODE_BROWSE;
745
  impl->sort_column = MODEL_COL_NAME;
746
  impl->sort_order = GTK_SORT_ASCENDING;
747
  impl->recent_manager = gtk_recent_manager_get_default ();
748
  impl->create_folders = TRUE;
749

750 751
  gtk_orientable_set_orientation (GTK_ORIENTABLE (impl),
                                  GTK_ORIENTATION_VERTICAL);
752
  gtk_box_set_spacing (GTK_BOX (impl), 12);
Matthias Clasen's avatar
Matthias Clasen committed
753

754 755
  set_file_system_backend (impl);

756
  profile_end ("end", NULL);
757 758
}

Federico Mena Quintero's avatar
Federico Mena Quintero committed
759 760 761 762 763 764
/* 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;
765
  ShortcutType shortcut_type;
766
  GCancellable *cancellable;
Federico Mena Quintero's avatar
Federico Mena Quintero committed
767 768 769

  gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), iter,
		      SHORTCUTS_COL_DATA, &col_data,
770
		      SHORTCUTS_COL_TYPE, &shortcut_type,
771
		      SHORTCUTS_COL_CANCELLABLE, &cancellable,
Federico Mena Quintero's avatar
Federico Mena Quintero committed
772
		      -1);
773

774
  if (cancellable)
775 776 777 778
    {
      g_cancellable_cancel (cancellable);
      g_object_unref (cancellable);
    }
779

780
  if (!(shortcut_type == SHORTCUT_TYPE_FILE || 
781 782
	shortcut_type == SHORTCUT_TYPE_VOLUME) ||
      !col_data)
Federico Mena Quintero's avatar
Federico Mena Quintero committed
783 784
    return;

785
  if (shortcut_type == SHORTCUT_TYPE_VOLUME)
Federico Mena Quintero's avatar
Federico Mena Quintero committed
786 787 788 789
    {
      GtkFileSystemVolume *volume;

      volume = col_data;
790
      _gtk_file_system_volume_unref (volume);
Federico Mena Quintero's avatar
Federico Mena Quintero committed
791
    }
792
  if (shortcut_type == SHORTCUT_TYPE_FILE)
Federico Mena Quintero's avatar
Federico Mena Quintero committed
793
    {
794
      GFile *file;
Federico Mena Quintero's avatar
Federico Mena Quintero committed
795

796 797
      file = col_data;
      g_object_unref (file);
Federico Mena Quintero's avatar
Federico Mena Quintero committed
798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820
    }
}

/* 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;
}

821
static void
822
pending_select_files_free (GtkFileChooserDefault *impl)
823
{
824
  g_slist_free_full (impl->pending_select_files, g_object_unref);
825
  impl->pending_select_files = NULL;
826 827 828
}

static void
829 830
pending_select_files_add (GtkFileChooserDefault *impl,
			  GFile                 *file)
831
{
832 833
  impl->pending_select_files =
    g_slist_prepend (impl->pending_select_files, g_object_ref (file));
834 835
}

836
static void
837
gtk_file_chooser_default_finalize (GObject *object)
838
{
839
  GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (object);
840
  GSList *l;
841

842 843
  unset_file_system_backend (impl);

844 845 846
  if (impl->shortcuts_pane_filter_model)
    g_object_unref (impl->shortcuts_pane_filter_model);

Federico Mena Quintero's avatar
Federico Mena Quintero committed
847 848
  shortcuts_free (impl);

849 850
  g_free (impl->browse_files_last_selected_name);

851 852 853 854 855 856 857 858 859 860 861
  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);
862

863 864
  if (impl->current_volume_file)
    g_object_unref (impl->current_volume_file);
865 866

  if (impl->current_folder)
867
    g_object_unref (impl->current_folder);
868

869 870
  if (impl->preview_file)
    g_object_unref (impl->preview_file);
871

872 873 874
  if (impl->browse_path_bar_size_group)
    g_object_unref (impl->browse_path_bar_size_group);

875
  /* Free all the Models we have */
Benjamin Otte's avatar
Benjamin Otte committed
876
  stop_loading_and_clear_list_model (impl, FALSE);
877 878
  search_clear_model (impl, FALSE);
  recent_clear_model (impl, FALSE);
879

Benjamin Otte's avatar
Benjamin Otte committed
880 881 882
  /* stopping the load above should have cleared this */
  g_assert (impl->load_timeout_id == 0);

883 884
  g_free (impl->preview_display_name);

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

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

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

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

907 908 909
  if (parent && gtk_window_has_group (parent))
    gtk_window_group_add_window (gtk_window_get_group (parent),
                                 GTK_WINDOW (dialog));
910

911 912 913 914
  gtk_dialog_run (GTK_DIALOG (dialog));
  gtk_widget_destroy (dialog);
}

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

Federico Mena Quintero's avatar
Federico Mena Quintero committed
921
  toplevel = gtk_widget_get_toplevel (widget);
922
  if (!gtk_widget_is_toplevel (toplevel))
Federico Mena Quintero's avatar
Federico Mena Quintero committed
923 924 925 926
    return NULL;
  else
    return GTK_WINDOW (toplevel);
}
927

Federico Mena Quintero's avatar
Federico Mena Quintero committed
928 929 930
/* Shows an error dialog for the file chooser */
static void
error_message (GtkFileChooserDefault *impl,
931 932
	       const char            *msg,
	       const char            *detail)
Federico Mena Quintero's avatar
Federico Mena Quintero committed
933
{
934
  error_message_with_parent (get_toplevel (GTK_WIDGET (impl)), msg, detail);
935 936
}

937 938 939 940
/* Shows a simple error dialog relative to a path.  Frees the GError as well. */
static void
error_dialog (GtkFileChooserDefault *impl,
	      const char            *msg,
941
	      GFile                 *file,
942 943
	      GError                *error)
{
944
  if (error)
945
    {
946 947 948
      char *uri = NULL;
      char *text;

949 950
      if (file)
	uri = g_file_get_uri (file);
951 952
      text = g_strdup_printf (msg, uri);
      error_message (impl, text, error->message);
953 954 955 956
      g_free (text);
      g_free (uri);
      g_error_free (error);
    }
957 958
}

959 960 961 962 963
/* Displays an error message about not being able to get information for a file.
 * Frees the GError as well.
 */
static void
error_getting_info_dialog (GtkFileChooserDefault *impl,