gtkfilechooserdefault.c 156 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 "gtkalignment.h"
24
#include "gtkbindings.h"
25
#include "gtkbutton.h"
26
#include "gtkcelllayout.h"
27
#include "gtkcellrendererpixbuf.h"
28
#include "gtkcellrendererseptext.h"
29
#include "gtkcellrenderertext.h"
Federico Mena Quintero's avatar
Federico Mena Quintero committed
30
#include "gtkcheckmenuitem.h"
31
#include "gtkcombobox.h"
32
#include "gtkentry.h"
33
#include "gtkexpander.h"
34
#include "gtkfilechooserdefault.h"
35
#include "gtkfilechooserembed.h"
36
#include "gtkfilechooserentry.h"
Owen Taylor's avatar
Owen Taylor committed
37
38
39
#include "gtkfilechooserutils.h"
#include "gtkfilechooser.h"
#include "gtkfilesystemmodel.h"
40
41
42
#include "gtkframe.h"
#include "gtkhbox.h"
#include "gtkhpaned.h"
Federico Mena Quintero's avatar
Federico Mena Quintero committed
43
#include "gtkiconfactory.h"
44
45
#include "gtkicontheme.h"
#include "gtkimage.h"
46
#include "gtkintl.h"
47
#include "gtklabel.h"
48
#include "gtkmarshalers.h"
49
#include "gtkmenuitem.h"
50
#include "gtkmessagedialog.h"
51
#include "gtkpathbar.h"
52
53
#include "gtkprivate.h"
#include "gtkscrolledwindow.h"
54
#include "gtksizegroup.h"
55
56
#include "gtkstock.h"
#include "gtktable.h"
57
58
#include "gtktreednd.h"
#include "gtktreeprivate.h"
59
60
61
62
63
64
#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
65

66
67
68
69
70
71
#if defined (G_OS_UNIX)
#include "gtkfilesystemunix.h"
#elif defined (G_OS_WIN32)
#include "gtkfilesystemwin32.h"
#endif

72
#include <string.h>
Federico Mena Quintero's avatar
Federico Mena Quintero committed
73
#include <time.h>
74

75
typedef struct _GtkFileChooserDefaultClass GtkFileChooserDefaultClass;
Owen Taylor's avatar
Owen Taylor committed
76

77
78
79
#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
80

81

82
struct _GtkFileChooserDefaultClass
Owen Taylor's avatar
Owen Taylor committed
83
84
85
86
{
  GtkVBoxClass parent_class;
};

87
struct _GtkFileChooserDefault
Owen Taylor's avatar
Owen Taylor committed
88
89
90
{
  GtkVBox parent_instance;

91
92
  GtkFileChooserAction action;

Owen Taylor's avatar
Owen Taylor committed
93
  GtkFileSystem *file_system;
94

95
96
  /* Save mode widgets */
  GtkWidget *save_widgets;
97

98
99
100
101
102
103
104
105
106
107
108
109
  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
110
111
  GtkWidget *browse_files_popup_menu;
  GtkWidget *browse_files_popup_menu_hidden_files_item;
112
113
114
  GtkWidget *browse_new_folder_button;
  GtkWidget *browse_path_bar;
  GtkWidget *browse_extra_align;
115

116
  GtkFileSystemModel *browse_files_model;
117

118
  GtkWidget *filter_combo_hbox;
119
  GtkWidget *filter_combo;
120
121
  GtkWidget *preview_box;
  GtkWidget *preview_label;
122
123
  GtkWidget *preview_widget;
  GtkWidget *extra_widget;
124

125
  GtkListStore *shortcuts_model;
126
127
  GtkTreeModel *shortcuts_filter_model;

Owen Taylor's avatar
Owen Taylor committed
128
  GtkTreeModelSort *sort_model;
Owen Taylor's avatar
Owen Taylor committed
129

Owen Taylor's avatar
Owen Taylor committed
130
131
  GtkFileFilter *current_filter;
  GSList *filters;
132

133
134
  gboolean has_home;
  gboolean has_desktop;
135
136

  int num_volumes;
137
138
  int num_shortcuts;
  int num_bookmarks;
139

140
  guint volumes_changed_id;
141
142
  guint bookmarks_changed_id;

143
  GtkFilePath *current_volume_path;
144
  GtkFilePath *current_folder;
145
  GtkFilePath *preview_path;
146
  char *preview_display_name;
147

148
149
150
  GtkTreeViewColumn *list_name_column;
  GtkCellRenderer *list_name_renderer;

Federico Mena Quintero's avatar
Federico Mena Quintero committed
151
152
153
  guint settings_signal_id;
  int icon_size;

154
#if 0
155
156
  GdkDragContext *shortcuts_drag_context;
  GSource *shortcuts_drag_outside_idle;
157
#endif
158

159
160
  /* Flags */

161
162
  guint local_only : 1;
  guint preview_widget_active : 1;
163
  guint use_preview_label : 1;
164
165
  guint select_multiple : 1;
  guint show_hidden : 1;
166
  guint list_sort_ascending : 1;
167
  guint changing_folder : 1;
168
  guint shortcuts_current_folder_active : 1;
169
  guint shortcuts_current_folder_is_volume : 1;
170
171

#if 0
172
  guint shortcuts_drag_outside : 1;
173
#endif
174
175
};

176
177
178
179
/* Signal IDs */
enum {
  LOCATION_POPUP,
  UP_FOLDER,
180
  DOWN_FOLDER,
181
182
183
184
185
186
  HOME_FOLDER,
  LAST_SIGNAL
};

static guint signals[LAST_SIGNAL] = { 0 };

187
/* Column numbers for the shortcuts tree.  Keep these in sync with shortcuts_model_create() */
188
189
190
191
enum {
  SHORTCUTS_COL_PIXBUF,
  SHORTCUTS_COL_NAME,
  SHORTCUTS_COL_PATH,
192
  SHORTCUTS_COL_REMOVABLE,
193
  SHORTCUTS_COL_PIXBUF_VISIBLE,
194
  SHORTCUTS_COL_NUM_COLUMNS
Owen Taylor's avatar
Owen Taylor committed
195
196
};

197
198
199
200
201
202
203
204
/* Column numbers for the file list */
enum {
  FILE_LIST_COL_NAME,
  FILE_LIST_COL_SIZE,
  FILE_LIST_COL_MTIME,
  FILE_LIST_COL_NUM_COLUMNS
};

205
206
/* Identifiers for target types */
enum {
207
  GTK_TREE_MODEL_ROW,
208
209
210
  TEXT_URI_LIST
};

211
212
213
214
215
216
217
218
219
220
221
/* 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 },
222
223
224
  { "text/uri-list", 0, TEXT_URI_LIST }
};

225
226
227
228
229
230
231
232
233
234
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]));
235

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

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

251
252
253
#define PREVIEW_HBOX_SPACING 12
#define NUM_LINES 40
#define NUM_CHARS 60
254

255
256
257
258
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);
259
260
261
262
263
264
265
266
267
268
269
270
271

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);
272
static void     gtk_file_chooser_default_dispose      (GObject               *object);
273
274
275
276
277
static void     gtk_file_chooser_default_show_all       (GtkWidget             *widget);
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);
278

279
280
281
static gboolean       gtk_file_chooser_default_set_current_folder 	   (GtkFileChooser    *chooser,
									    const GtkFilePath *path,
									    GError           **error);
282
283
static GtkFilePath *  gtk_file_chooser_default_get_current_folder 	   (GtkFileChooser    *chooser);
static void           gtk_file_chooser_default_set_current_name   	   (GtkFileChooser    *chooser,
284
									    const gchar       *name);
285
286
287
static gboolean       gtk_file_chooser_default_select_path        	   (GtkFileChooser    *chooser,
									    const GtkFilePath *path,
									    GError           **error);
288
static void           gtk_file_chooser_default_unselect_path      	   (GtkFileChooser    *chooser,
289
									    const GtkFilePath *path);
290
291
292
293
294
295
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,
296
									    GtkFileFilter     *filter);
297
static void           gtk_file_chooser_default_remove_filter      	   (GtkFileChooser    *chooser,
298
									    GtkFileFilter     *filter);
299
300
301
302
303
304
305
306
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);
307

308
309
310
311
312
313
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);
314
static gboolean       gtk_file_chooser_default_should_respond         (GtkFileChooserEmbed *chooser_embed);
Federico Mena Quintero's avatar
Federico Mena Quintero committed
315
static void           gtk_file_chooser_default_initial_focus          (GtkFileChooserEmbed *chooser_embed);
316

317
318
static void location_popup_handler (GtkFileChooserDefault *impl);
static void up_folder_handler      (GtkFileChooserDefault *impl);
319
static void down_folder_handler    (GtkFileChooserDefault *impl);
320
static void home_folder_handler    (GtkFileChooserDefault *impl);
321
static void update_appearance      (GtkFileChooserDefault *impl);
322

323
324
325
326
static void set_current_filter   (GtkFileChooserDefault *impl,
				  GtkFileFilter         *filter);
static void check_preview_change (GtkFileChooserDefault *impl);

327
static void filter_combo_changed       (GtkComboBox           *combo_box,
328
					GtkFileChooserDefault *impl);
329
330
331
332
static void     shortcuts_row_activated_cb (GtkTreeView           *tree_view,
					    GtkTreePath           *path,
					    GtkTreeViewColumn     *column,
					    GtkFileChooserDefault *impl);
Federico Mena Quintero's avatar
Federico Mena Quintero committed
333
334
335
336
337

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

338
339
340
static gboolean shortcuts_select_func   (GtkTreeSelection      *selection,
					 GtkTreeModel          *model,
					 GtkTreePath           *path,
341
					 gboolean               path_currently_selected,
342
					 gpointer               data);
343
344
345
346
static void shortcuts_activate_item (GtkFileChooserDefault *impl,
				     int                    item_num);
static int shortcuts_get_index (GtkFileChooserDefault *impl,
				ShortcutsIndex         where);
347
348
static int shortcut_find_position (GtkFileChooserDefault *impl,
				   const GtkFilePath     *path);
349

350
351
352
353
354
355
static void list_selection_changed     (GtkTreeSelection      *tree_selection,
					GtkFileChooserDefault *impl);
static void list_row_activated         (GtkTreeView           *tree_view,
					GtkTreePath           *path,
					GtkTreeViewColumn     *column,
					GtkFileChooserDefault *impl);
356

357
static void path_bar_clicked           (GtkPathBar            *path_bar,
358
					GtkFilePath           *file_path,
Federico Mena Quintero's avatar
Federico Mena Quintero committed
359
					gboolean               child_is_hidden,
360
					GtkFileChooserDefault *impl);
361

362
363
364
365
366
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
367
368
369
370
371
static void list_icon_data_func (GtkTreeViewColumn *tree_column,
				 GtkCellRenderer   *cell,
				 GtkTreeModel      *tree_model,
				 GtkTreeIter       *iter,
				 gpointer           data);
372
373
374
375
376
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
377
#if 0
378
379
380
381
382
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
383
384
385
386
387
388
#endif
static void list_mtime_data_func (GtkTreeViewColumn *tree_column,
				  GtkCellRenderer   *cell,
				  GtkTreeModel      *tree_model,
				  GtkTreeIter       *iter,
				  gpointer           data);
389

Owen Taylor's avatar
Owen Taylor committed
390
static GObjectClass *parent_class;
391

392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422


/* Drag and drop interface declarations */

typedef struct {
  GtkTreeModelFilter parent;

  GtkFileChooserDefault *impl;
} ShortcutsModelFilter;

typedef struct {
  GtkTreeModelFilterClass parent_class;
} ShortcutsModelFilterClass;

#define SHORTCUTS_MODEL_FILTER_TYPE (shortcuts_model_filter_get_type ())
#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,
			 shortcuts_model_filter,
			 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
423
GType
424
_gtk_file_chooser_default_get_type (void)
Owen Taylor's avatar
Owen Taylor committed
425
{
426
  static GType file_chooser_default_type = 0;
Owen Taylor's avatar
Owen Taylor committed
427

428
  if (!file_chooser_default_type)
Owen Taylor's avatar
Owen Taylor committed
429
    {
430
      static const GTypeInfo file_chooser_default_info =
Owen Taylor's avatar
Owen Taylor committed
431
      {
432
	sizeof (GtkFileChooserDefaultClass),
Owen Taylor's avatar
Owen Taylor committed
433
434
	NULL,		/* base_init */
	NULL,		/* base_finalize */
435
	(GClassInitFunc) gtk_file_chooser_default_class_init,
Owen Taylor's avatar
Owen Taylor committed
436
437
	NULL,		/* class_finalize */
	NULL,		/* class_data */
438
	sizeof (GtkFileChooserDefault),
Owen Taylor's avatar
Owen Taylor committed
439
	0,		/* n_preallocs */
440
	(GInstanceInitFunc) gtk_file_chooser_default_init,
Owen Taylor's avatar
Owen Taylor committed
441
      };
442

Owen Taylor's avatar
Owen Taylor committed
443
444
      static const GInterfaceInfo file_chooser_info =
      {
445
	(GInterfaceInitFunc) gtk_file_chooser_default_iface_init, /* interface_init */
Owen Taylor's avatar
Owen Taylor committed
446
447
448
449
	NULL,			                                       /* interface_finalize */
	NULL			                                       /* interface_data */
      };

450
451
452
453
454
455
456
      static const GInterfaceInfo file_chooser_embed_info =
      {
	(GInterfaceInitFunc) gtk_file_chooser_embed_default_iface_init, /* interface_init */
	NULL,			                                       /* interface_finalize */
	NULL			                                       /* interface_data */
      };

457
458
      file_chooser_default_type = g_type_register_static (GTK_TYPE_VBOX, "GtkFileChooserDefault",
							 &file_chooser_default_info, 0);
459

460
      g_type_add_interface_static (file_chooser_default_type,
Owen Taylor's avatar
Owen Taylor committed
461
462
				   GTK_TYPE_FILE_CHOOSER,
				   &file_chooser_info);
463
464
465
      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
466
467
    }

468
  return file_chooser_default_type;
Owen Taylor's avatar
Owen Taylor committed
469
470
471
}

static void
472
gtk_file_chooser_default_class_init (GtkFileChooserDefaultClass *class)
Owen Taylor's avatar
Owen Taylor committed
473
474
{
  GObjectClass *gobject_class = G_OBJECT_CLASS (class);
Owen Taylor's avatar
Owen Taylor committed
475
  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
476
  GtkBindingSet *binding_set;
Owen Taylor's avatar
Owen Taylor committed
477

Owen Taylor's avatar
Owen Taylor committed
478
479
  parent_class = g_type_class_peek_parent (class);

480
481
482
483
  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;
484
  gobject_class->dispose = gtk_file_chooser_default_dispose;
Owen Taylor's avatar
Owen Taylor committed
485

486
  widget_class->show_all = gtk_file_chooser_default_show_all;
487
488
  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
489

490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
  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);
506
507
508
509
510
511
512
513
  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);
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
  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);

  gtk_binding_entry_add_signal (binding_set,
531
				GDK_Up, GDK_MOD1_MASK,
532
533
534
				"up-folder",
				0);
  gtk_binding_entry_add_signal (binding_set,
535
				GDK_KP_Up, GDK_MOD1_MASK,
536
537
538
				"up-folder",
				0);

539
540
541
542
543
544
545
546
547
  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);

548
  gtk_binding_entry_add_signal (binding_set,
549
550
				GDK_Home, GDK_MOD1_MASK,
				"home-folder",
551
552
				0);
  gtk_binding_entry_add_signal (binding_set,
553
554
				GDK_KP_Home, GDK_MOD1_MASK,
				"home-folder",
555
556
				0);

Owen Taylor's avatar
Owen Taylor committed
557
  _gtk_file_chooser_install_properties (gobject_class);
558

Owen Taylor's avatar
Owen Taylor committed
559
560
561
562
563
  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
564
565
566
}

static void
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
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
585
586
}

587
588
589
590
591
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;
592
  iface->should_respond = gtk_file_chooser_default_should_respond;
Federico Mena Quintero's avatar
Federico Mena Quintero committed
593
  iface->initial_focus = gtk_file_chooser_default_initial_focus;
594
}
Owen Taylor's avatar
Owen Taylor committed
595
static void
596
gtk_file_chooser_default_init (GtkFileChooserDefault *impl)
Owen Taylor's avatar
Owen Taylor committed
597
{
598
599
  impl->local_only = TRUE;
  impl->preview_widget_active = TRUE;
600
  impl->use_preview_label = TRUE;
601
602
  impl->select_multiple = FALSE;
  impl->show_hidden = FALSE;
Federico Mena Quintero's avatar
Federico Mena Quintero committed
603
  impl->icon_size = FALLBACK_ICON_SIZE;
604

605
  gtk_widget_set_redraw_on_allocate (GTK_WIDGET (impl), TRUE);
606
  gtk_box_set_spacing (GTK_BOX (impl), 12);
607
608
609
}

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

615
616
  g_signal_handler_disconnect (impl->file_system, impl->volumes_changed_id);
  impl->volumes_changed_id = 0;
617
618
  g_signal_handler_disconnect (impl->file_system, impl->bookmarks_changed_id);
  impl->bookmarks_changed_id = 0;
619
620
  g_object_unref (impl->file_system);

621
622
623
624
625
626
627
628
629
630
631
  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
632
633
634
635
636
637
638
639
640
641

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

642
643
644
645
646
647
648
649
650
651
652
653
654
  /* Free all the Models we have */
  if (impl->browse_files_model)
    g_object_unref (impl->browse_files_model);

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

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

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

655
656
  g_free (impl->preview_display_name);

657
658
659
  G_OBJECT_CLASS (parent_class)->finalize (object);
}

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

667
  dialog = gtk_message_dialog_new (parent,
668
669
				   GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
				   GTK_MESSAGE_ERROR,
670
				   GTK_BUTTONS_OK,
671
672
				   "%s",
				   msg);
673
674
675
676
  gtk_dialog_run (GTK_DIALOG (dialog));
  gtk_widget_destroy (dialog);
}

Federico Mena Quintero's avatar
Federico Mena Quintero committed
677
678
679
/* Returns a toplevel GtkWindow, or NULL if none */
static GtkWindow *
get_toplevel (GtkWidget *widget)
Federico Mena Quintero's avatar
Federico Mena Quintero committed
680
{
681
682
  GtkWidget *toplevel;

Federico Mena Quintero's avatar
Federico Mena Quintero committed
683
  toplevel = gtk_widget_get_toplevel (widget);
684
  if (!GTK_WIDGET_TOPLEVEL (toplevel))
Federico Mena Quintero's avatar
Federico Mena Quintero committed
685
686
687
688
    return NULL;
  else
    return GTK_WINDOW (toplevel);
}
689

Federico Mena Quintero's avatar
Federico Mena Quintero committed
690
691
692
693
694
695
/* 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);
696
697
}

698
699
700
701
702
703
704
/* 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)
{
705
706
707
708
709
  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
710
711
712
      char *text = g_strdup_printf (msg,
				    uri,
				    error->message);
713
714
715
716
717
      error_message (impl, text);
      g_free (text);
      g_free (uri);
      g_error_free (error);
    }
718
719
}

720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
/* 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);
}

744
745
746
747
748
749
750
/* 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)
{
751
  char *uri;
752
753
  char *msg;

754
  uri = gtk_file_system_path_to_uri (impl->file_system, base_path);
755
  msg = g_strdup_printf (_("Could not build file name from '%s' and '%s':\n%s"),
756
			 uri, file_part,
757
758
			 error->message);
  error_message (impl, msg);
759
  g_free (uri);
760
761
762
763
  g_free (msg);
  g_error_free (error);
}

764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
/* 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;

  error = NULL;
  result = _gtk_file_chooser_set_current_folder_path (GTK_FILE_CHOOSER (impl), path, &error);

  if (!result)
    error_changing_folder_dialog (impl, path, error);

  return result;
}

793
static void
794
update_preview_widget_visibility (GtkFileChooserDefault *impl)
795
{
796
797
798
799
800
801
802
803
804
805
806
807
808
  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)
809
810
811
812
	{
	  gtk_widget_destroy (impl->preview_label);
	  impl->preview_label = NULL;
	}
813
814
    }

815
  if (impl->preview_widget_active && impl->preview_widget)
816
    gtk_widget_show (impl->preview_box);
817
  else
818
    gtk_widget_hide (impl->preview_box);
819
820

  g_signal_emit_by_name (impl, "default-size-changed");
821
822
}

823
static void
824
825
set_preview_widget (GtkFileChooserDefault *impl,
		    GtkWidget             *preview_widget)
826
827
828
829
830
{
  if (preview_widget == impl->preview_widget)
    return;

  if (impl->preview_widget)
831
    gtk_container_remove (GTK_CONTAINER (impl->preview_box),
832
			  impl->preview_widget);
833
834
835
836

  impl->preview_widget = preview_widget;
  if (impl->preview_widget)
    {
837
      gtk_widget_show (impl->preview_widget);
838
839
840
841
      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);
842
    }
843
844

  update_preview_widget_visibility (impl);
845
846
}

Federico Mena Quintero's avatar
Federico Mena Quintero committed
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
/* Re-reads all the icons for the shortcuts, used when the theme changes */
static void
shortcuts_reload_icons (GtkFileChooserDefault *impl)
{
  GtkTreeIter iter;
  int i;
  int bookmarks_separator_idx;
  int current_folder_separator_idx;
  int volumes_idx;

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

  bookmarks_separator_idx = shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS_SEPARATOR);
  current_folder_separator_idx = shortcuts_get_index (impl, SHORTCUTS_CURRENT_FOLDER_SEPARATOR);
  volumes_idx = shortcuts_get_index (impl, SHORTCUTS_VOLUMES);

  i = 0;

  do {
    gpointer data;
    gboolean pixbuf_visible;
    GdkPixbuf *pixbuf;

    gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
			SHORTCUTS_COL_PATH, &data,
			SHORTCUTS_COL_PIXBUF_VISIBLE, &pixbuf_visible,
			-1);

    if (!pixbuf_visible || !data)
      goto next_iter;

    if (i >= volumes_idx && i < volumes_idx + impl->num_volumes)
      {
	GtkFileSystemVolume *volume;

	volume = data;
	pixbuf = gtk_file_system_volume_render_icon (impl->file_system, volume, GTK_WIDGET (impl),
						     impl->icon_size, NULL);
      }
    else
      {
	const GtkFilePath *path;

	path = data;
	pixbuf = gtk_file_system_render_icon (impl->file_system, path, GTK_WIDGET (impl),
					      impl->icon_size, NULL);
      }

    gtk_list_store_set (impl->shortcuts_model, &iter,
			SHORTCUTS_COL_PIXBUF, pixbuf,
			-1);

  next_iter:
    i++;
  } while (gtk_tree_model_iter_next (GTK_TREE_MODEL (impl->shortcuts_model),&iter));
}

905
/* If a shortcut corresponds to the current folder, selects it */
906
static void
907
shortcuts_find_current_folder (GtkFileChooserDefault *impl)
908
{
909
  GtkTreeSelection *selection;
910
911
  int pos;
  GtkTreePath *path;
912

913
  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view));
914
915
916
917
918
919
920
921
922
923
924

  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);
  gtk_tree_selection_select_path (selection, path);
  gtk_tree_path_free (path);
925
926
}

927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
/* Returns whether a path is a folder */
static gboolean
check_is_folder (GtkFileSystem *file_system, const GtkFilePath *path, GError **error)
{
  GtkFileFolder *folder;

  folder = gtk_file_system_get_folder (file_system, path,
				       GTK_FILE_INFO_DISPLAY_NAME,
				       error);
  if (!folder)
    return FALSE;

  g_object_unref (folder);
  return TRUE;
}

943
944
/* Convenience function to get the display name and icon info for a path */
static GtkFileInfo *
945
get_file_info (GtkFileSystem *file_system, const GtkFilePath *path, gboolean name_only, GError **error)
946
947
948
949
950
{
  GtkFilePath *parent_path;
  GtkFileFolder *parent_folder;
  GtkFileInfo *info;

951
952
  info = NULL;

953
954
955
  if (!gtk_file_system_get_parent (file_system, path, &parent_path, error))
    return NULL;

956
  parent_folder = gtk_file_system_get_folder (file_system, parent_path ? parent_path : path,
957
					      GTK_FILE_INFO_DISPLAY_NAME
958
					      | (name_only ? 0 : GTK_FILE_INFO_IS_FOLDER),
959
					      error);
960
  if (!parent_folder)
961
    goto out;
962

963
  info = gtk_file_folder_get_info (parent_folder, parent_path ? path : NULL, error);
964
  g_object_unref (parent_folder);
965

966
967
968
 out:

  gtk_file_path_free (parent_path);
969
970
971
  return info;
}

972
973
/* Inserts a path in the shortcuts tree, making a copy of it; alternatively,
 * inserts a volume.  A position of -1 indicates the end of the tree.
974
 */
975
static gboolean
976
977
shortcuts_insert_path (GtkFileChooserDefault *impl,
		       int                    pos,
978
979
		       gboolean               is_volume,
		       GtkFileSystemVolume   *volume,
980
981
		       const GtkFilePath     *path,
		       const char            *label,
982
		       gboolean               removable,
983
		       GError               **error)
984
{
985
  char *label_copy;
986
  GdkPixbuf *pixbuf;
987
  gpointer data;
988
989
  GtkTreeIter iter;

990
991
992
993
  if (is_volume)
    {
      data = volume;
      label_copy = gtk_file_system_volume_get_display_name (impl->file_system, volume);
Federico Mena Quintero's avatar
Federico Mena Quintero committed
994
995
      pixbuf = gtk_file_system_volume_render_icon (impl->file_system, volume, GTK_WIDGET (impl),
						   impl->icon_size, NULL);
996
    }
997
  else
998
    {
999
      if (!check_is_folder (impl->file_system, path, error))
1000
	return FALSE;
For faster browsing, not all history is shown. View entire blame