gtkfilechooserdefault.c 165 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

Owen Taylor's avatar
Owen Taylor committed
405
static GObjectClass *parent_class;
406

407
408
409
410
411
412
413
414
415
416
417
418
419
420


/* Drag and drop interface declarations */

typedef struct {
  GtkTreeModelFilter parent;

  GtkFileChooserDefault *impl;
} ShortcutsModelFilter;

typedef struct {
  GtkTreeModelFilterClass parent_class;
} ShortcutsModelFilterClass;

421
#define SHORTCUTS_MODEL_FILTER_TYPE (_shortcuts_model_filter_get_type ())
422
423
424
425
426
#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,
427
			 _shortcuts_model_filter,
428
429
430
431
432
433
434
435
436
437
			 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
438
GType
439
_gtk_file_chooser_default_get_type (void)
Owen Taylor's avatar
Owen Taylor committed
440
{
441
  static GType file_chooser_default_type = 0;
Owen Taylor's avatar
Owen Taylor committed
442

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

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

465
466
467
468
469
470
471
      static const GInterfaceInfo file_chooser_embed_info =
      {
	(GInterfaceInitFunc) gtk_file_chooser_embed_default_iface_init, /* interface_init */
	NULL,			                                       /* interface_finalize */
	NULL			                                       /* interface_data */
      };

472
473
      file_chooser_default_type = g_type_register_static (GTK_TYPE_VBOX, "GtkFileChooserDefault",
							 &file_chooser_default_info, 0);
474

475
      g_type_add_interface_static (file_chooser_default_type,
Owen Taylor's avatar
Owen Taylor committed
476
477
				   GTK_TYPE_FILE_CHOOSER,
				   &file_chooser_info);
478
479
480
      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
481
482
    }

483
  return file_chooser_default_type;
Owen Taylor's avatar
Owen Taylor committed
484
485
486
}

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

Owen Taylor's avatar
Owen Taylor committed
493
494
  parent_class = g_type_class_peek_parent (class);

495
496
497
498
  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;
499
  gobject_class->dispose = gtk_file_chooser_default_dispose;
Owen Taylor's avatar
Owen Taylor committed
500

501
  widget_class->show_all = gtk_file_chooser_default_show_all;
502
  widget_class->hierarchy_changed = gtk_file_chooser_default_hierarchy_changed;
503
504
  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
505

506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
  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);
522
523
524
525
526
527
528
529
  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);
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
  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);

546
547
548
549
550
  gtk_binding_entry_add_signal (binding_set,
				GDK_slash, 0,
				"location-popup",
				0);

551
  gtk_binding_entry_add_signal (binding_set,
552
				GDK_Up, GDK_MOD1_MASK,
553
554
555
				"up-folder",
				0);
  gtk_binding_entry_add_signal (binding_set,
556
				GDK_KP_Up, GDK_MOD1_MASK,
557
558
559
				"up-folder",
				0);

560
561
562
563
564
565
566
567
568
  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);

569
  gtk_binding_entry_add_signal (binding_set,
570
571
				GDK_Home, GDK_MOD1_MASK,
				"home-folder",
572
573
				0);
  gtk_binding_entry_add_signal (binding_set,
574
575
				GDK_KP_Home, GDK_MOD1_MASK,
				"home-folder",
576
577
				0);

Owen Taylor's avatar
Owen Taylor committed
578
  _gtk_file_chooser_install_properties (gobject_class);
579

Owen Taylor's avatar
Owen Taylor committed
580
581
582
583
584
  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
585
586
587
}

static void
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
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
606
607
}

608
609
610
611
612
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;
613
  iface->should_respond = gtk_file_chooser_default_should_respond;
Federico Mena Quintero's avatar
Federico Mena Quintero committed
614
  iface->initial_focus = gtk_file_chooser_default_initial_focus;
615
}
Owen Taylor's avatar
Owen Taylor committed
616
static void
617
gtk_file_chooser_default_init (GtkFileChooserDefault *impl)
Owen Taylor's avatar
Owen Taylor committed
618
{
619
620
  impl->local_only = TRUE;
  impl->preview_widget_active = TRUE;
621
  impl->use_preview_label = TRUE;
622
623
  impl->select_multiple = FALSE;
  impl->show_hidden = FALSE;
Federico Mena Quintero's avatar
Federico Mena Quintero committed
624
  impl->icon_size = FALLBACK_ICON_SIZE;
625

626
  gtk_widget_set_redraw_on_allocate (GTK_WIDGET (impl), TRUE);
627
  gtk_box_set_spacing (GTK_BOX (impl), 12);
628
629
}

Federico Mena Quintero's avatar
Federico Mena Quintero committed
630
631
632
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
/* 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;
}

681
static void
682
gtk_file_chooser_default_finalize (GObject *object)
683
{
684
  GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (object);
685
  GSList *l;
686

Federico Mena Quintero's avatar
Federico Mena Quintero committed
687
688
689
690
691
  if (impl->shortcuts_filter_model)
    g_object_unref (impl->shortcuts_filter_model);

  shortcuts_free (impl);

692
693
  g_signal_handler_disconnect (impl->file_system, impl->volumes_changed_id);
  impl->volumes_changed_id = 0;
694
695
  g_signal_handler_disconnect (impl->file_system, impl->bookmarks_changed_id);
  impl->bookmarks_changed_id = 0;
696
697
  g_object_unref (impl->file_system);

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

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

719
720
721
722
723
724
725
  /* 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);

726
727
  g_free (impl->preview_display_name);

Federico Mena Quintero's avatar
Federico Mena Quintero committed
728
729
  g_free (impl->edited_new_text);

730
731
732
  G_OBJECT_CLASS (parent_class)->finalize (object);
}

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

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

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

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

Federico Mena Quintero's avatar
Federico Mena Quintero committed
763
764
765
766
767
768
/* 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);
769
770
}

771
772
773
774
775
776
777
/* 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)
{
778
779
780
781
782
  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
783
784
785
      char *text = g_strdup_printf (msg,
				    uri,
				    error->message);
786
787
788
789
790
      error_message (impl, text);
      g_free (text);
      g_free (uri);
      g_error_free (error);
    }
791
792
}

793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
/* 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);
}

817
818
819
820
821
822
823
/* 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)
{
824
  char *uri;
825
826
  char *msg;

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

837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
/* 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
856
857
858
859
860
861
862
863
864
865
866
867
  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);
868
869

  error = NULL;
Federico Mena Quintero's avatar
Federico Mena Quintero committed
870
  result = _gtk_file_chooser_set_current_folder_path (GTK_FILE_CHOOSER (impl), path_copy, &error);
871
872

  if (!result)
Federico Mena Quintero's avatar
Federico Mena Quintero committed
873
874
875
    error_changing_folder_dialog (impl, path_copy, error);

  gtk_file_path_free (path_copy);
876
877
878
879

  return result;
}

880
static void
881
update_preview_widget_visibility (GtkFileChooserDefault *impl)
882
{
883
884
885
886
887
888
889
890
891
892
893
894
895
  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)
896
897
898
899
	{
	  gtk_widget_destroy (impl->preview_label);
	  impl->preview_label = NULL;
	}
900
901
    }

902
  if (impl->preview_widget_active && impl->preview_widget)
903
    gtk_widget_show (impl->preview_box);
904
  else
905
    gtk_widget_hide (impl->preview_box);
906
907

  g_signal_emit_by_name (impl, "default-size-changed");
908
909
}

910
static void
911
912
set_preview_widget (GtkFileChooserDefault *impl,
		    GtkWidget             *preview_widget)
913
914
915
916
917
{
  if (preview_widget == impl->preview_widget)
    return;

  if (impl->preview_widget)
918
    gtk_container_remove (GTK_CONTAINER (impl->preview_box),
919
			  impl->preview_widget);
920
921
922
923

  impl->preview_widget = preview_widget;
  if (impl->preview_widget)
    {
924
      gtk_widget_show (impl->preview_widget);
925
926
927
928
      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);
929
    }
930
931

  update_preview_widget_visibility (impl);
932
933
}

Federico Mena Quintero's avatar
Federico Mena Quintero committed
934
935
936
937
938
939
940
941
942
943
944
/* 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
945
    gboolean is_volume;
Federico Mena Quintero's avatar
Federico Mena Quintero committed
946
947
948
949
    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
950
			SHORTCUTS_COL_DATA, &data,
951
			SHORTCUTS_COL_IS_VOLUME, &is_volume,
Federico Mena Quintero's avatar
Federico Mena Quintero committed
952
953
954
			SHORTCUTS_COL_PIXBUF_VISIBLE, &pixbuf_visible,
			-1);

Federico Mena Quintero's avatar
Federico Mena Quintero committed
955
    if (pixbuf_visible && data)
Federico Mena Quintero's avatar
Federico Mena Quintero committed
956
      {
Federico Mena Quintero's avatar
Federico Mena Quintero committed
957
958
959
	if (is_volume)
	  {
	    GtkFileSystemVolume *volume;
Federico Mena Quintero's avatar
Federico Mena Quintero committed
960

Federico Mena Quintero's avatar
Federico Mena Quintero committed
961
962
963
964
965
966
967
	    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
968

Federico Mena Quintero's avatar
Federico Mena Quintero committed
969
970
971
972
	    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
973

Federico Mena Quintero's avatar
Federico Mena Quintero committed
974
975
976
977
978
979
	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
980
981
982
  } while (gtk_tree_model_iter_next (GTK_TREE_MODEL (impl->shortcuts_model),&iter));
}

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

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

  pos = shortcut_find_position (impl, impl->current_folder);
  if (pos == -1)
    {
      gtk_tree_selection_unselect_all (selection);
      return;
    }

  path = gtk_tree_path_new_from_indices (pos, -1);
For faster browsing, not all history is shown. View entire blame