gtkfilechooserdefault.c 167 KB
Newer Older
Owen Taylor's avatar
Owen Taylor committed
1
/* GTK - The GIMP Toolkit
2
 * gtkfilechooserdefault.c: Default implementation of GtkFileChooser
Owen Taylor's avatar
Owen Taylor committed
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
 * 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.
 */

21
#include <config.h>
22
#include "gdk/gdkkeysyms.h"
23
#include "gtkalias.h"
24
#include "gtkalignment.h"
25
#include "gtkbindings.h"
26
#include "gtkbutton.h"
27
#include "gtkcelllayout.h"
28
#include "gtkcellrendererpixbuf.h"
29
#include "gtkcellrenderertext.h"
30
#include "gtkcellrenderertext.h"
Federico Mena Quintero's avatar
Federico Mena Quintero committed
31
#include "gtkcheckmenuitem.h"
32
#include "gtkcombobox.h"
33
#include "gtkentry.h"
34
#include "gtkexpander.h"
35
#include "gtkfilechooserdefault.h"
36
#include "gtkfilechooserembed.h"
37
#include "gtkfilechooserentry.h"
Owen Taylor's avatar
Owen Taylor committed
38
39
40
#include "gtkfilechooserutils.h"
#include "gtkfilechooser.h"
#include "gtkfilesystemmodel.h"
41
42
43
#include "gtkframe.h"
#include "gtkhbox.h"
#include "gtkhpaned.h"
Federico Mena Quintero's avatar
Federico Mena Quintero committed
44
#include "gtkiconfactory.h"
45
46
#include "gtkicontheme.h"
#include "gtkimage.h"
47
#include "gtkimagemenuitem.h"
48
#include "gtkintl.h"
49
#include "gtklabel.h"
50
#include "gtkmarshalers.h"
51
#include "gtkmenuitem.h"
52
#include "gtkmessagedialog.h"
53
#include "gtkpathbar.h"
54
55
#include "gtkprivate.h"
#include "gtkscrolledwindow.h"
56
#include "gtkseparatormenuitem.h"
57
#include "gtksizegroup.h"
58
59
#include "gtkstock.h"
#include "gtktable.h"
60
61
#include "gtktreednd.h"
#include "gtktreeprivate.h"
62
63
64
65
66
67
#include "gtktreeview.h"
#include "gtktreemodelsort.h"
#include "gtktreeselection.h"
#include "gtktreestore.h"
#include "gtktypebuiltins.h"
#include "gtkvbox.h"
Owen Taylor's avatar
Owen Taylor committed
68

69
70
71
72
73
74
#if defined (G_OS_UNIX)
#include "gtkfilesystemunix.h"
#elif defined (G_OS_WIN32)
#include "gtkfilesystemwin32.h"
#endif

75
#include <errno.h>
76
#include <string.h>
Federico Mena Quintero's avatar
Federico Mena Quintero committed
77
#include <time.h>
78

79
typedef struct _GtkFileChooserDefaultClass GtkFileChooserDefaultClass;
Owen Taylor's avatar
Owen Taylor committed
80

81
82
83
#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
84

85

86
struct _GtkFileChooserDefaultClass
Owen Taylor's avatar
Owen Taylor committed
87
88
89
90
{
  GtkVBoxClass parent_class;
};

91
struct _GtkFileChooserDefault
Owen Taylor's avatar
Owen Taylor committed
92
93
94
{
  GtkVBox parent_instance;

95
96
  GtkFileChooserAction action;

Owen Taylor's avatar
Owen Taylor committed
97
  GtkFileSystem *file_system;
98

99
100
  /* Save mode widgets */
  GtkWidget *save_widgets;
101

102
103
104
105
106
107
108
109
110
111
112
113
  GtkWidget *save_file_name_entry;
  GtkWidget *save_folder_label;
  GtkWidget *save_folder_combo;
  GtkWidget *save_extra_align;
  GtkWidget *save_expander;

  /* The file browsing widgets */
  GtkWidget *browse_widgets;
  GtkWidget *browse_shortcuts_tree_view;
  GtkWidget *browse_shortcuts_add_button;
  GtkWidget *browse_shortcuts_remove_button;
  GtkWidget *browse_files_tree_view;
Federico Mena Quintero's avatar
Federico Mena Quintero committed
114
  GtkWidget *browse_files_popup_menu;
115
  GtkWidget *browse_files_popup_menu_add_shortcut_item;
Federico Mena Quintero's avatar
Federico Mena Quintero committed
116
  GtkWidget *browse_files_popup_menu_hidden_files_item;
117
118
119
  GtkWidget *browse_new_folder_button;
  GtkWidget *browse_path_bar;
  GtkWidget *browse_extra_align;
120

121
  GtkFileSystemModel *browse_files_model;
122

123
  GtkWidget *filter_combo_hbox;
124
  GtkWidget *filter_combo;
125
126
  GtkWidget *preview_box;
  GtkWidget *preview_label;
127
128
  GtkWidget *preview_widget;
  GtkWidget *extra_widget;
129

130
  GtkListStore *shortcuts_model;
131
132
  GtkTreeModel *shortcuts_filter_model;

Owen Taylor's avatar
Owen Taylor committed
133
  GtkTreeModelSort *sort_model;
Owen Taylor's avatar
Owen Taylor committed
134

Owen Taylor's avatar
Owen Taylor committed
135
136
  GtkFileFilter *current_filter;
  GSList *filters;
137

138
139
  gboolean has_home;
  gboolean has_desktop;
140
141

  int num_volumes;
142
143
  int num_shortcuts;
  int num_bookmarks;
144

145
146
  gulong volumes_changed_id;
  gulong bookmarks_changed_id;
147

148
  GtkFilePath *current_volume_path;
149
  GtkFilePath *current_folder;
150
  GtkFilePath *preview_path;
151
  char *preview_display_name;
152

153
154
155
  GtkTreeViewColumn *list_name_column;
  GtkCellRenderer *list_name_renderer;

Federico Mena Quintero's avatar
Federico Mena Quintero committed
156
157
158
  GSource *edited_idle;
  char *edited_new_text;

159
  gulong settings_signal_id;
Federico Mena Quintero's avatar
Federico Mena Quintero committed
160
161
  int icon_size;

162
163
164
  gulong toplevel_set_focus_id;
  GtkWidget *toplevel_last_focus_widget;

165
#if 0
166
167
  GdkDragContext *shortcuts_drag_context;
  GSource *shortcuts_drag_outside_idle;
168
#endif
169

170
171
  /* Flags */

172
173
  guint local_only : 1;
  guint preview_widget_active : 1;
174
  guint use_preview_label : 1;
175
176
  guint select_multiple : 1;
  guint show_hidden : 1;
177
  guint list_sort_ascending : 1;
178
  guint changing_folder : 1;
179
  guint shortcuts_current_folder_active : 1;
180
181

#if 0
182
  guint shortcuts_drag_outside : 1;
183
#endif
184
185
};

186
187
188
189
/* Signal IDs */
enum {
  LOCATION_POPUP,
  UP_FOLDER,
190
  DOWN_FOLDER,
191
192
193
194
195
196
  HOME_FOLDER,
  LAST_SIGNAL
};

static guint signals[LAST_SIGNAL] = { 0 };

197
/* Column numbers for the shortcuts tree.  Keep these in sync with shortcuts_model_create() */
198
199
200
enum {
  SHORTCUTS_COL_PIXBUF,
  SHORTCUTS_COL_NAME,
Federico Mena Quintero's avatar
Federico Mena Quintero committed
201
202
  SHORTCUTS_COL_DATA,
  SHORTCUTS_COL_IS_VOLUME,
203
  SHORTCUTS_COL_REMOVABLE,
204
  SHORTCUTS_COL_PIXBUF_VISIBLE,
205
  SHORTCUTS_COL_NUM_COLUMNS
Owen Taylor's avatar
Owen Taylor committed
206
207
};

208
209
210
211
212
213
214
215
/* Column numbers for the file list */
enum {
  FILE_LIST_COL_NAME,
  FILE_LIST_COL_SIZE,
  FILE_LIST_COL_MTIME,
  FILE_LIST_COL_NUM_COLUMNS
};

216
217
/* Identifiers for target types */
enum {
218
  GTK_TREE_MODEL_ROW,
219
220
221
  TEXT_URI_LIST
};

222
223
224
225
226
227
228
229
230
231
232
/* Target types for dragging from the shortcuts list */
static GtkTargetEntry shortcuts_source_targets[] = {
  { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, GTK_TREE_MODEL_ROW }
};

static const int num_shortcuts_source_targets = (sizeof (shortcuts_source_targets)
						 / sizeof (shortcuts_source_targets[0]));

/* Target types for dropping into the shortcuts list */
static GtkTargetEntry shortcuts_dest_targets[] = {
  { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, GTK_TREE_MODEL_ROW },
233
234
235
  { "text/uri-list", 0, TEXT_URI_LIST }
};

236
237
238
239
240
241
242
243
244
245
static const int num_shortcuts_dest_targets = (sizeof (shortcuts_dest_targets)
					       / sizeof (shortcuts_dest_targets[0]));

/* Target types for DnD from the file list */
static GtkTargetEntry file_list_source_targets[] = {
  { "text/uri-list", 0, TEXT_URI_LIST }
};

static const int num_file_list_source_targets = (sizeof (file_list_source_targets)
						 / sizeof (file_list_source_targets[0]));
246

247
248
249
250
251
252
/* Interesting places in the shortcuts bar */
typedef enum {
  SHORTCUTS_HOME,
  SHORTCUTS_DESKTOP,
  SHORTCUTS_VOLUMES,
  SHORTCUTS_SHORTCUTS,
253
254
255
256
  SHORTCUTS_BOOKMARKS_SEPARATOR,
  SHORTCUTS_BOOKMARKS,
  SHORTCUTS_CURRENT_FOLDER_SEPARATOR,
  SHORTCUTS_CURRENT_FOLDER
257
258
} ShortcutsIndex;

Federico Mena Quintero's avatar
Federico Mena Quintero committed
259
260
261
/* Icon size for if we can't get it from the theme */
#define FALLBACK_ICON_SIZE 20

262
263
264
#define PREVIEW_HBOX_SPACING 12
#define NUM_LINES 40
#define NUM_CHARS 60
265

266
267
268
269
static void gtk_file_chooser_default_class_init       (GtkFileChooserDefaultClass *class);
static void gtk_file_chooser_default_iface_init       (GtkFileChooserIface        *iface);
static void gtk_file_chooser_embed_default_iface_init (GtkFileChooserEmbedIface   *iface);
static void gtk_file_chooser_default_init             (GtkFileChooserDefault      *impl);
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
286
static void     gtk_file_chooser_default_hierarchy_changed (GtkWidget          *widget,
							    GtkWidget          *previous_toplevel);
287
288
289
290
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);
291

292
293
294
static gboolean       gtk_file_chooser_default_set_current_folder 	   (GtkFileChooser    *chooser,
									    const GtkFilePath *path,
									    GError           **error);
295
296
static GtkFilePath *  gtk_file_chooser_default_get_current_folder 	   (GtkFileChooser    *chooser);
static void           gtk_file_chooser_default_set_current_name   	   (GtkFileChooser    *chooser,
297
									    const gchar       *name);
298
299
300
static gboolean       gtk_file_chooser_default_select_path        	   (GtkFileChooser    *chooser,
									    const GtkFilePath *path,
									    GError           **error);
301
static void           gtk_file_chooser_default_unselect_path      	   (GtkFileChooser    *chooser,
302
									    const GtkFilePath *path);
303
304
305
306
307
308
static void           gtk_file_chooser_default_select_all         	   (GtkFileChooser    *chooser);
static void           gtk_file_chooser_default_unselect_all       	   (GtkFileChooser    *chooser);
static GSList *       gtk_file_chooser_default_get_paths          	   (GtkFileChooser    *chooser);
static GtkFilePath *  gtk_file_chooser_default_get_preview_path   	   (GtkFileChooser    *chooser);
static GtkFileSystem *gtk_file_chooser_default_get_file_system    	   (GtkFileChooser    *chooser);
static void           gtk_file_chooser_default_add_filter         	   (GtkFileChooser    *chooser,
309
									    GtkFileFilter     *filter);
310
static void           gtk_file_chooser_default_remove_filter      	   (GtkFileChooser    *chooser,
311
									    GtkFileFilter     *filter);
312
313
314
315
316
317
318
319
static GSList *       gtk_file_chooser_default_list_filters       	   (GtkFileChooser    *chooser);
static gboolean       gtk_file_chooser_default_add_shortcut_folder    (GtkFileChooser    *chooser,
								       const GtkFilePath *path,
								       GError           **error);
static gboolean       gtk_file_chooser_default_remove_shortcut_folder (GtkFileChooser    *chooser,
								       const GtkFilePath *path,
								       GError           **error);
static GSList *       gtk_file_chooser_default_list_shortcut_folders  (GtkFileChooser    *chooser);
320

321
322
323
324
325
326
static void           gtk_file_chooser_default_get_default_size       (GtkFileChooserEmbed *chooser_embed,
								       gint                *default_width,
								       gint                *default_height);
static void           gtk_file_chooser_default_get_resizable_hints    (GtkFileChooserEmbed *chooser_embed,
								       gboolean            *resize_horizontally,
								       gboolean            *resize_vertically);
327
static gboolean       gtk_file_chooser_default_should_respond         (GtkFileChooserEmbed *chooser_embed);
Federico Mena Quintero's avatar
Federico Mena Quintero committed
328
static void           gtk_file_chooser_default_initial_focus          (GtkFileChooserEmbed *chooser_embed);
329

330
331
static void location_popup_handler (GtkFileChooserDefault *impl);
static void up_folder_handler      (GtkFileChooserDefault *impl);
332
static void down_folder_handler    (GtkFileChooserDefault *impl);
333
static void home_folder_handler    (GtkFileChooserDefault *impl);
334
static void update_appearance      (GtkFileChooserDefault *impl);
335

336
337
338
339
static void set_current_filter   (GtkFileChooserDefault *impl,
				  GtkFileFilter         *filter);
static void check_preview_change (GtkFileChooserDefault *impl);

340
static void filter_combo_changed       (GtkComboBox           *combo_box,
341
					GtkFileChooserDefault *impl);
342
343
344
345
static void     shortcuts_row_activated_cb (GtkTreeView           *tree_view,
					    GtkTreePath           *path,
					    GtkTreeViewColumn     *column,
					    GtkFileChooserDefault *impl);
Federico Mena Quintero's avatar
Federico Mena Quintero committed
346
347
348
349
350

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

351
352
353
static gboolean shortcuts_select_func   (GtkTreeSelection      *selection,
					 GtkTreeModel          *model,
					 GtkTreePath           *path,
354
					 gboolean               path_currently_selected,
355
					 gpointer               data);
356
357
static void shortcuts_activate_iter (GtkFileChooserDefault *impl,
				     GtkTreeIter           *iter);
358
359
static int shortcuts_get_index (GtkFileChooserDefault *impl,
				ShortcutsIndex         where);
360
361
static int shortcut_find_position (GtkFileChooserDefault *impl,
				   const GtkFilePath     *path);
362

363
364
static void bookmarks_check_add_sensitivity (GtkFileChooserDefault *impl);

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

372
static void path_bar_clicked           (GtkPathBar            *path_bar,
373
					GtkFilePath           *file_path,
Federico Mena Quintero's avatar
Federico Mena Quintero committed
374
					gboolean               child_is_hidden,
375
					GtkFileChooserDefault *impl);
376

377
378
379
380
381
static void add_bookmark_button_clicked_cb    (GtkButton             *button,
					       GtkFileChooserDefault *impl);
static void remove_bookmark_button_clicked_cb (GtkButton             *button,
					       GtkFileChooserDefault *impl);

Owen Taylor's avatar
Owen Taylor committed
382
383
384
385
386
static void list_icon_data_func (GtkTreeViewColumn *tree_column,
				 GtkCellRenderer   *cell,
				 GtkTreeModel      *tree_model,
				 GtkTreeIter       *iter,
				 gpointer           data);
387
388
389
390
391
static void list_name_data_func (GtkTreeViewColumn *tree_column,
				 GtkCellRenderer   *cell,
				 GtkTreeModel      *tree_model,
				 GtkTreeIter       *iter,
				 gpointer           data);
Federico Mena Quintero's avatar
Federico Mena Quintero committed
392
#if 0
393
394
395
396
397
static void list_size_data_func (GtkTreeViewColumn *tree_column,
				 GtkCellRenderer   *cell,
				 GtkTreeModel      *tree_model,
				 GtkTreeIter       *iter,
				 gpointer           data);
Federico Mena Quintero's avatar
Federico Mena Quintero committed
398
399
400
401
402
403
#endif
static void list_mtime_data_func (GtkTreeViewColumn *tree_column,
				  GtkCellRenderer   *cell,
				  GtkTreeModel      *tree_model,
				  GtkTreeIter       *iter,
				  gpointer           data);
404

405
406
407
static const GtkFileInfo *get_list_file_info (GtkFileChooserDefault *impl,
					      GtkTreeIter           *iter);

Owen Taylor's avatar
Owen Taylor committed
408
static GObjectClass *parent_class;
409

410
411
412
413
414
415
416
417
418
419
420
421
422
423


/* Drag and drop interface declarations */

typedef struct {
  GtkTreeModelFilter parent;

  GtkFileChooserDefault *impl;
} ShortcutsModelFilter;

typedef struct {
  GtkTreeModelFilterClass parent_class;
} ShortcutsModelFilterClass;

424
#define SHORTCUTS_MODEL_FILTER_TYPE (_shortcuts_model_filter_get_type ())
425
426
427
428
429
#define SHORTCUTS_MODEL_FILTER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SHORTCUTS_MODEL_FILTER_TYPE, ShortcutsModelFilter))

static void shortcuts_model_filter_drag_source_iface_init (GtkTreeDragSourceIface *iface);

G_DEFINE_TYPE_WITH_CODE (ShortcutsModelFilter,
430
			 _shortcuts_model_filter,
431
432
433
434
435
436
437
438
439
440
			 GTK_TYPE_TREE_MODEL_FILTER,
			 G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_DRAG_SOURCE,
						shortcuts_model_filter_drag_source_iface_init));

static GtkTreeModel *shortcuts_model_filter_new (GtkFileChooserDefault *impl,
						 GtkTreeModel          *child_model,
						 GtkTreePath           *root);



Owen Taylor's avatar
Owen Taylor committed
441
GType
442
_gtk_file_chooser_default_get_type (void)
Owen Taylor's avatar
Owen Taylor committed
443
{
444
  static GType file_chooser_default_type = 0;
Owen Taylor's avatar
Owen Taylor committed
445

446
  if (!file_chooser_default_type)
Owen Taylor's avatar
Owen Taylor committed
447
    {
448
      static const GTypeInfo file_chooser_default_info =
Owen Taylor's avatar
Owen Taylor committed
449
      {
450
	sizeof (GtkFileChooserDefaultClass),
Owen Taylor's avatar
Owen Taylor committed
451
452
	NULL,		/* base_init */
	NULL,		/* base_finalize */
453
	(GClassInitFunc) gtk_file_chooser_default_class_init,
Owen Taylor's avatar
Owen Taylor committed
454
455
	NULL,		/* class_finalize */
	NULL,		/* class_data */
456
	sizeof (GtkFileChooserDefault),
Owen Taylor's avatar
Owen Taylor committed
457
	0,		/* n_preallocs */
458
	(GInstanceInitFunc) gtk_file_chooser_default_init,
Owen Taylor's avatar
Owen Taylor committed
459
      };
460

Owen Taylor's avatar
Owen Taylor committed
461
462
      static const GInterfaceInfo file_chooser_info =
      {
463
	(GInterfaceInitFunc) gtk_file_chooser_default_iface_init, /* interface_init */
Owen Taylor's avatar
Owen Taylor committed
464
465
466
467
	NULL,			                                       /* interface_finalize */
	NULL			                                       /* interface_data */
      };

468
469
470
471
472
473
474
      static const GInterfaceInfo file_chooser_embed_info =
      {
	(GInterfaceInitFunc) gtk_file_chooser_embed_default_iface_init, /* interface_init */
	NULL,			                                       /* interface_finalize */
	NULL			                                       /* interface_data */
      };

475
476
      file_chooser_default_type = g_type_register_static (GTK_TYPE_VBOX, "GtkFileChooserDefault",
							 &file_chooser_default_info, 0);
477

478
      g_type_add_interface_static (file_chooser_default_type,
Owen Taylor's avatar
Owen Taylor committed
479
480
				   GTK_TYPE_FILE_CHOOSER,
				   &file_chooser_info);
481
482
483
      g_type_add_interface_static (file_chooser_default_type,
				   GTK_TYPE_FILE_CHOOSER_EMBED,
				   &file_chooser_embed_info);
Owen Taylor's avatar
Owen Taylor committed
484
485
    }

486
  return file_chooser_default_type;
Owen Taylor's avatar
Owen Taylor committed
487
488
489
}

static void
490
gtk_file_chooser_default_class_init (GtkFileChooserDefaultClass *class)
Owen Taylor's avatar
Owen Taylor committed
491
492
{
  GObjectClass *gobject_class = G_OBJECT_CLASS (class);
Owen Taylor's avatar
Owen Taylor committed
493
  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
494
  GtkBindingSet *binding_set;
Owen Taylor's avatar
Owen Taylor committed
495

Owen Taylor's avatar
Owen Taylor committed
496
497
  parent_class = g_type_class_peek_parent (class);

498
499
500
501
  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;
502
  gobject_class->dispose = gtk_file_chooser_default_dispose;
Owen Taylor's avatar
Owen Taylor committed
503

504
  widget_class->show_all = gtk_file_chooser_default_show_all;
505
  widget_class->hierarchy_changed = gtk_file_chooser_default_hierarchy_changed;
506
507
  widget_class->style_set = gtk_file_chooser_default_style_set;
  widget_class->screen_changed = gtk_file_chooser_default_screen_changed;
Owen Taylor's avatar
Owen Taylor committed
508

509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
  signals[LOCATION_POPUP] =
    _gtk_binding_signal_new ("location-popup",
			     G_OBJECT_CLASS_TYPE (class),
			     G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
			     G_CALLBACK (location_popup_handler),
			     NULL, NULL,
			     _gtk_marshal_VOID__VOID,
			     G_TYPE_NONE, 0);
  signals[UP_FOLDER] =
    _gtk_binding_signal_new ("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);
525
526
527
528
529
530
531
532
  signals[DOWN_FOLDER] =
    _gtk_binding_signal_new ("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);
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
  signals[HOME_FOLDER] =
    _gtk_binding_signal_new ("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);

  binding_set = gtk_binding_set_by_class (class);

  gtk_binding_entry_add_signal (binding_set,
				GDK_l, GDK_CONTROL_MASK,
				"location-popup",
				0);

549
550
551
552
553
  gtk_binding_entry_add_signal (binding_set,
				GDK_slash, 0,
				"location-popup",
				0);

554
  gtk_binding_entry_add_signal (binding_set,
555
				GDK_Up, GDK_MOD1_MASK,
556
557
558
				"up-folder",
				0);
  gtk_binding_entry_add_signal (binding_set,
559
				GDK_KP_Up, GDK_MOD1_MASK,
560
561
562
				"up-folder",
				0);

563
564
565
566
567
568
569
570
571
  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);

572
  gtk_binding_entry_add_signal (binding_set,
573
574
				GDK_Home, GDK_MOD1_MASK,
				"home-folder",
575
576
				0);
  gtk_binding_entry_add_signal (binding_set,
577
578
				GDK_KP_Home, GDK_MOD1_MASK,
				"home-folder",
579
580
				0);

Owen Taylor's avatar
Owen Taylor committed
581
  _gtk_file_chooser_install_properties (gobject_class);
582

Owen Taylor's avatar
Owen Taylor committed
583
584
585
586
587
  gtk_settings_install_property (g_param_spec_string ("gtk-file-chooser-backend",
						      P_("Default file chooser backend"),
						      P_("Name of the GtkFileChooser backend to use by default"),
						      NULL,
						      G_PARAM_READWRITE));
Owen Taylor's avatar
Owen Taylor committed
588
589
590
}

static void
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
gtk_file_chooser_default_iface_init (GtkFileChooserIface *iface)
{
  iface->select_path = gtk_file_chooser_default_select_path;
  iface->unselect_path = gtk_file_chooser_default_unselect_path;
  iface->select_all = gtk_file_chooser_default_select_all;
  iface->unselect_all = gtk_file_chooser_default_unselect_all;
  iface->get_paths = gtk_file_chooser_default_get_paths;
  iface->get_preview_path = gtk_file_chooser_default_get_preview_path;
  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
609
610
}

611
612
613
614
615
static void
gtk_file_chooser_embed_default_iface_init (GtkFileChooserEmbedIface *iface)
{
  iface->get_default_size = gtk_file_chooser_default_get_default_size;
  iface->get_resizable_hints = gtk_file_chooser_default_get_resizable_hints;
616
  iface->should_respond = gtk_file_chooser_default_should_respond;
Federico Mena Quintero's avatar
Federico Mena Quintero committed
617
  iface->initial_focus = gtk_file_chooser_default_initial_focus;
618
}
Owen Taylor's avatar
Owen Taylor committed
619
static void
620
gtk_file_chooser_default_init (GtkFileChooserDefault *impl)
Owen Taylor's avatar
Owen Taylor committed
621
{
622
623
  impl->local_only = TRUE;
  impl->preview_widget_active = TRUE;
624
  impl->use_preview_label = TRUE;
625
626
  impl->select_multiple = FALSE;
  impl->show_hidden = FALSE;
Federico Mena Quintero's avatar
Federico Mena Quintero committed
627
  impl->icon_size = FALLBACK_ICON_SIZE;
628

629
  gtk_widget_set_redraw_on_allocate (GTK_WIDGET (impl), TRUE);
630
  gtk_box_set_spacing (GTK_BOX (impl), 12);
631
632
}

Federico Mena Quintero's avatar
Federico Mena Quintero committed
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
/* 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;
  gboolean is_volume;

  gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), iter,
		      SHORTCUTS_COL_DATA, &col_data,
		      SHORTCUTS_COL_IS_VOLUME, &is_volume,
		      -1);
  if (!col_data)
    return;

  if (is_volume)
    {
      GtkFileSystemVolume *volume;

      volume = col_data;
      gtk_file_system_volume_free (impl->file_system, volume);
    }
  else
    {
      GtkFilePath *path;

      path = col_data;
      gtk_file_path_free (path);
    }
}

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

684
static void
685
gtk_file_chooser_default_finalize (GObject *object)
686
{
687
  GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (object);
688
  GSList *l;
689

Federico Mena Quintero's avatar
Federico Mena Quintero committed
690
691
692
693
694
  if (impl->shortcuts_filter_model)
    g_object_unref (impl->shortcuts_filter_model);

  shortcuts_free (impl);

695
696
  g_signal_handler_disconnect (impl->file_system, impl->volumes_changed_id);
  impl->volumes_changed_id = 0;
697
698
  g_signal_handler_disconnect (impl->file_system, impl->bookmarks_changed_id);
  impl->bookmarks_changed_id = 0;
699
700
  g_object_unref (impl->file_system);

701
702
703
704
705
706
707
708
709
710
711
  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
712
713
714
715
716
717
718
719
720
721

  if (impl->current_volume_path)
    gtk_file_path_free (impl->current_volume_path);

  if (impl->current_folder)
    gtk_file_path_free (impl->current_folder);

  if (impl->preview_path)
    gtk_file_path_free (impl->preview_path);

722
723
724
725
726
727
728
  /* Free all the Models we have */
  if (impl->browse_files_model)
    g_object_unref (impl->browse_files_model);

  if (impl->sort_model)
    g_object_unref (impl->sort_model);

729
730
  g_free (impl->preview_display_name);

Federico Mena Quintero's avatar
Federico Mena Quintero committed
731
732
  g_free (impl->edited_new_text);

733
734
735
  G_OBJECT_CLASS (parent_class)->finalize (object);
}

736
/* Shows an error dialog set as transient for the specified window */
737
static void
738
739
error_message_with_parent (GtkWindow  *parent,
			   const char *msg)
740
741
742
{
  GtkWidget *dialog;

743
  dialog = gtk_message_dialog_new (parent,
744
745
				   GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
				   GTK_MESSAGE_ERROR,
746
				   GTK_BUTTONS_OK,
747
748
				   "%s",
				   msg);
749
750
751
752
  gtk_dialog_run (GTK_DIALOG (dialog));
  gtk_widget_destroy (dialog);
}

Federico Mena Quintero's avatar
Federico Mena Quintero committed
753
754
755
/* Returns a toplevel GtkWindow, or NULL if none */
static GtkWindow *
get_toplevel (GtkWidget *widget)
Federico Mena Quintero's avatar
Federico Mena Quintero committed
756
{
757
758
  GtkWidget *toplevel;

Federico Mena Quintero's avatar
Federico Mena Quintero committed
759
  toplevel = gtk_widget_get_toplevel (widget);
760
  if (!GTK_WIDGET_TOPLEVEL (toplevel))
Federico Mena Quintero's avatar
Federico Mena Quintero committed
761
762
763
764
    return NULL;
  else
    return GTK_WINDOW (toplevel);
}
765

Federico Mena Quintero's avatar
Federico Mena Quintero committed
766
767
768
769
770
771
/* Shows an error dialog for the file chooser */
static void
error_message (GtkFileChooserDefault *impl,
	       const char            *msg)
{
  error_message_with_parent (get_toplevel (GTK_WIDGET (impl)), msg);
772
773
}

774
775
776
777
778
779
780
/* Shows a simple error dialog relative to a path.  Frees the GError as well. */
static void
error_dialog (GtkFileChooserDefault *impl,
	      const char            *msg,
	      const GtkFilePath     *path,
	      GError                *error)
{
781
782
783
784
785
  g_return_if_fail (path != NULL);

  if (error)
    {
      char *uri = gtk_file_system_path_to_uri (impl->file_system, path);
Morten Welinder's avatar
Morten Welinder committed
786
787
788
      char *text = g_strdup_printf (msg,
				    uri,
				    error->message);
789
790
791
792
793
      error_message (impl, text);
      g_free (text);
      g_free (uri);
      g_error_free (error);
    }
794
795
}

796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
/* 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,
			   const GtkFilePath     *path,
			   GError                *error)
{
  error_dialog (impl,
		_("Could not retrieve information about %s:\n%s"),
		path, error);
}

/* Shows an error dialog about not being able to add a bookmark */
static void
error_could_not_add_bookmark_dialog (GtkFileChooserDefault *impl,
				     const GtkFilePath     *path,
				     GError                *error)
{
  error_dialog (impl,
		_("Could not add a bookmark for %s:\n%s"),
		path, error);
}

820
821
822
823
824
825
826
/* Shows an error dialog about not being able to compose a filename */
static void
error_building_filename_dialog (GtkFileChooserDefault *impl,
				const GtkFilePath     *base_path,
				const char            *file_part,
				GError                *error)
{
827
  char *uri;
828
829
  char *msg;

830
  uri = gtk_file_system_path_to_uri (impl->file_system, base_path);
831
  msg = g_strdup_printf (_("Could not build file name from '%s' and '%s':\n%s"),
832
			 uri, file_part,
833
834
			 error->message);
  error_message (impl, msg);
835
  g_free (uri);
836
837
838
839
  g_free (msg);
  g_error_free (error);
}

840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
/* Shows an error dialog when we cannot switch to a folder */
static void
error_changing_folder_dialog (GtkFileChooserDefault *impl,
			      const GtkFilePath     *path,
			      GError                *error)
{
  error_dialog (impl,
		_("Could not change the current folder to %s:\n%s"),
		path,
		error);
}

/* Changes folders, displaying an error dialog if this fails */
static gboolean
change_folder_and_display_error (GtkFileChooserDefault *impl,
				 const GtkFilePath     *path)
{
  GError *error;
  gboolean result;
Federico Mena Quintero's avatar
Federico Mena Quintero committed
859
860
861
862
863
864
865
866
867
868
869
870
  GtkFilePath *path_copy;

  /* 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()
   *     calls _gtk_file_chooser_set_current_folder_path()
   *       changing folders fails, sets model to NULL, thus freeing the path in (*)
   */

  path_copy = gtk_file_path_copy (path);
871
872

  error = NULL;
Federico Mena Quintero's avatar
Federico Mena Quintero committed
873
  result = _gtk_file_chooser_set_current_folder_path (GTK_FILE_CHOOSER (impl), path_copy, &error);
874
875

  if (!result)
Federico Mena Quintero's avatar
Federico Mena Quintero committed
876
877
878
    error_changing_folder_dialog (impl, path_copy, error);

  gtk_file_path_free (path_copy);
879
880
881
882

  return result;
}

883
static void
884
update_preview_widget_visibility (GtkFileChooserDefault *impl)
885
{
886
887
888
889
890
891
892
893
894
895
896
897
898
  if (impl->use_preview_label)
    {
      if (!impl->preview_label)
	{
	  impl->preview_label = gtk_label_new (impl->preview_display_name);
	  gtk_box_pack_start (GTK_BOX (impl->preview_box), impl->preview_label, FALSE, FALSE, 0);
	  gtk_box_reorder_child (GTK_BOX (impl->preview_box), impl->preview_label, 0);
	  gtk_widget_show (impl->preview_label);
	}
    }
  else
    {
      if (impl->preview_label)
899
900
901
902
	{
	  gtk_widget_destroy (impl->preview_label);
	  impl->preview_label = NULL;
	}
903
904
    }

905
  if (impl->preview_widget_active && impl->preview_widget)
906
    gtk_widget_show (impl->preview_box);
907
  else
908
    gtk_widget_hide (impl->preview_box);
909
910

  g_signal_emit_by_name (impl, "default-size-changed");
911
912
}

913
static void
914
915
set_preview_widget (GtkFileChooserDefault *impl,
		    GtkWidget             *preview_widget)
916
917
918
919
920
{
  if (preview_widget == impl->preview_widget)
    return;

  if (impl->preview_widget)
921
    gtk_container_remove (GTK_CONTAINER (impl->preview_box),
922
			  impl->preview_widget);
923
924
925
926

  impl->preview_widget = preview_widget;
  if (impl->preview_widget)
    {
927
      gtk_widget_show (impl->preview_widget);
928
929
930
931
      gtk_box_pack_start (GTK_BOX (impl->preview_box), impl->preview_widget, TRUE, TRUE, 0);
      gtk_box_reorder_child (GTK_BOX (impl->preview_box),
			     impl->preview_widget,
			     (impl->use_preview_label && impl->preview_label) ? 1 : 0);
932
    }
933
934

  update_preview_widget_visibility (impl);
935
936
}

Federico Mena Quintero's avatar
Federico Mena Quintero committed
937
938
939
940
941
942
943
944
945
946
947
/* Re-reads all the icons for the shortcuts, used when the theme changes */
static void
shortcuts_reload_icons (GtkFileChooserDefault *impl)
{
  GtkTreeIter iter;

  if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (impl->shortcuts_model), &iter))
    return;

  do {
    gpointer data;
Federico Mena Quintero's avatar
Federico Mena Quintero committed
948
    gboolean is_volume;
Federico Mena Quintero's avatar
Federico Mena Quintero committed
949
950
951
952
    gboolean pixbuf_visible;
    GdkPixbuf *pixbuf;

    gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
Federico Mena Quintero's avatar
Federico Mena Quintero committed
953
			SHORTCUTS_COL_DATA, &data,
954
			SHORTCUTS_COL_IS_VOLUME, &is_volume,
Federico Mena Quintero's avatar
Federico Mena Quintero committed
955
956
957
			SHORTCUTS_COL_PIXBUF_VISIBLE, &pixbuf_visible,
			-1);

Federico Mena Quintero's avatar
Federico Mena Quintero committed
958
    if (pixbuf_visible && data)
Federico Mena Quintero's avatar
Federico Mena Quintero committed
959
      {
Federico Mena Quintero's avatar
Federico Mena Quintero committed
960
961
962
	if (is_volume)
	  {
	    GtkFileSystemVolume *volume;
Federico Mena Quintero's avatar
Federico Mena Quintero committed
963

Federico Mena Quintero's avatar
Federico Mena Quintero committed
964
965
966
967
968
969
970
	    volume = data;
	    pixbuf = gtk_file_system_volume_render_icon (impl->file_system, volume, GTK_WIDGET (impl),
							 impl->icon_size, NULL);
	  }
	else
	  {
	    const GtkFilePath *path;
Federico Mena Quintero's avatar
Federico Mena Quintero committed
971

Federico Mena Quintero's avatar
Federico Mena Quintero committed
972
973
974
975
	    path = data;
	    pixbuf = gtk_file_system_render_icon (impl->file_system, path, GTK_WIDGET (impl),
						  impl->icon_size, NULL);
	  }
Federico Mena Quintero's avatar
Federico Mena Quintero committed
976

Federico Mena Quintero's avatar
Federico Mena Quintero committed
977
978
979
980
981
982
	gtk_list_store_set (impl->shortcuts_model, &iter,
			    SHORTCUTS_COL_PIXBUF, pixbuf,
			    -1);
	if (pixbuf)
	  g_object_unref (pixbuf);
      }
Federico Mena Quintero's avatar
Federico Mena Quintero committed
983
984
985
  } while (gtk_tree_model_iter_next (GTK_TREE_MODEL (impl->shortcuts_model),&iter));
}

986
/* If a shortcut corresponds to the current folder, selects it */
987
static void
988
shortcuts_find_current_folder (GtkFileChooserDefault *impl)
989
{
990
  GtkTreeSelection *selection;
991
992
  int pos;
  GtkTreePath *path;
993

994
  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view));
995
996
997
998
999
1000

  pos = shortcut_find_position (impl, impl->current_folder);
  if (pos == -1)
    {
      gtk_tree_selection_unselect_all (selection);
      return;
For faster browsing, not all history is shown. View entire blame