gtkfilechooserdefault.c 160 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
  GSource *edited_idle;
  char *edited_new_text;

Federico Mena Quintero's avatar
Federico Mena Quintero committed
154
155
156
  guint settings_signal_id;
  int icon_size;

157
#if 0
158
159
  GdkDragContext *shortcuts_drag_context;
  GSource *shortcuts_drag_outside_idle;
160
#endif
161

162
163
  /* Flags */

164
165
  guint local_only : 1;
  guint preview_widget_active : 1;
166
  guint use_preview_label : 1;
167
168
  guint select_multiple : 1;
  guint show_hidden : 1;
169
  guint list_sort_ascending : 1;
170
  guint changing_folder : 1;
171
  guint shortcuts_current_folder_active : 1;
172
  guint shortcuts_current_folder_is_volume : 1;
173
174

#if 0
175
  guint shortcuts_drag_outside : 1;
176
#endif
177
178
};

179
180
181
182
/* Signal IDs */
enum {
  LOCATION_POPUP,
  UP_FOLDER,
183
  DOWN_FOLDER,
184
185
186
187
188
189
  HOME_FOLDER,
  LAST_SIGNAL
};

static guint signals[LAST_SIGNAL] = { 0 };

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

200
201
202
203
204
205
206
207
/* Column numbers for the file list */
enum {
  FILE_LIST_COL_NAME,
  FILE_LIST_COL_SIZE,
  FILE_LIST_COL_MTIME,
  FILE_LIST_COL_NUM_COLUMNS
};

208
209
/* Identifiers for target types */
enum {
210
  GTK_TREE_MODEL_ROW,
211
212
213
  TEXT_URI_LIST
};

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

228
229
230
231
232
233
234
235
236
237
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]));
238

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

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

254
255
256
#define PREVIEW_HBOX_SPACING 12
#define NUM_LINES 40
#define NUM_CHARS 60
257

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

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);
275
static void     gtk_file_chooser_default_dispose      (GObject               *object);
276
277
278
279
280
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);
281

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

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

320
321
static void location_popup_handler (GtkFileChooserDefault *impl);
static void up_folder_handler      (GtkFileChooserDefault *impl);
322
static void down_folder_handler    (GtkFileChooserDefault *impl);
323
static void home_folder_handler    (GtkFileChooserDefault *impl);
324
static void update_appearance      (GtkFileChooserDefault *impl);
325

326
327
328
329
static void set_current_filter   (GtkFileChooserDefault *impl,
				  GtkFileFilter         *filter);
static void check_preview_change (GtkFileChooserDefault *impl);

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

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

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

353
354
355
356
357
358
static void list_selection_changed     (GtkTreeSelection      *tree_selection,
					GtkFileChooserDefault *impl);
static void list_row_activated         (GtkTreeView           *tree_view,
					GtkTreePath           *path,
					GtkTreeViewColumn     *column,
					GtkFileChooserDefault *impl);
359

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

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

Owen Taylor's avatar
Owen Taylor committed
393
static GObjectClass *parent_class;
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
423
424
425


/* 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
426
GType
427
_gtk_file_chooser_default_get_type (void)
Owen Taylor's avatar
Owen Taylor committed
428
{
429
  static GType file_chooser_default_type = 0;
Owen Taylor's avatar
Owen Taylor committed
430

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

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

453
454
455
456
457
458
459
      static const GInterfaceInfo file_chooser_embed_info =
      {
	(GInterfaceInitFunc) gtk_file_chooser_embed_default_iface_init, /* interface_init */
	NULL,			                                       /* interface_finalize */
	NULL			                                       /* interface_data */
      };

460
461
      file_chooser_default_type = g_type_register_static (GTK_TYPE_VBOX, "GtkFileChooserDefault",
							 &file_chooser_default_info, 0);
462

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

471
  return file_chooser_default_type;
Owen Taylor's avatar
Owen Taylor committed
472
473
474
}

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

Owen Taylor's avatar
Owen Taylor committed
481
482
  parent_class = g_type_class_peek_parent (class);

483
484
485
486
  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;
487
  gobject_class->dispose = gtk_file_chooser_default_dispose;
Owen Taylor's avatar
Owen Taylor committed
488

489
  widget_class->show_all = gtk_file_chooser_default_show_all;
490
491
  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
492

493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
  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);
509
510
511
512
513
514
515
516
  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);
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
  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,
534
				GDK_Up, GDK_MOD1_MASK,
535
536
537
				"up-folder",
				0);
  gtk_binding_entry_add_signal (binding_set,
538
				GDK_KP_Up, GDK_MOD1_MASK,
539
540
541
				"up-folder",
				0);

542
543
544
545
546
547
548
549
550
  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);

551
  gtk_binding_entry_add_signal (binding_set,
552
553
				GDK_Home, GDK_MOD1_MASK,
				"home-folder",
554
555
				0);
  gtk_binding_entry_add_signal (binding_set,
556
557
				GDK_KP_Home, GDK_MOD1_MASK,
				"home-folder",
558
559
				0);

Owen Taylor's avatar
Owen Taylor committed
560
  _gtk_file_chooser_install_properties (gobject_class);
561

Owen Taylor's avatar
Owen Taylor committed
562
563
564
565
566
  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
567
568
569
}

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

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

608
  gtk_widget_set_redraw_on_allocate (GTK_WIDGET (impl), TRUE);
609
  gtk_box_set_spacing (GTK_BOX (impl), 12);
610
611
612
}

static void
613
gtk_file_chooser_default_finalize (GObject *object)
614
{
615
  GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (object);
616
  GSList *l;
617

618
619
  g_signal_handler_disconnect (impl->file_system, impl->volumes_changed_id);
  impl->volumes_changed_id = 0;
620
621
  g_signal_handler_disconnect (impl->file_system, impl->bookmarks_changed_id);
  impl->bookmarks_changed_id = 0;
622
623
  g_object_unref (impl->file_system);

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

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

645
646
647
648
649
650
651
652
653
654
655
656
657
  /* 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);

658
659
  g_free (impl->preview_display_name);

Federico Mena Quintero's avatar
Federico Mena Quintero committed
660
661
  g_free (impl->edited_new_text);

662
663
664
  G_OBJECT_CLASS (parent_class)->finalize (object);
}

665
/* Shows an error dialog set as transient for the specified window */
666
static void
667
668
error_message_with_parent (GtkWindow  *parent,
			   const char *msg)
669
670
671
{
  GtkWidget *dialog;

672
  dialog = gtk_message_dialog_new (parent,
673
674
				   GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
				   GTK_MESSAGE_ERROR,
675
				   GTK_BUTTONS_OK,
676
677
				   "%s",
				   msg);
678
679
680
681
  gtk_dialog_run (GTK_DIALOG (dialog));
  gtk_widget_destroy (dialog);
}

Federico Mena Quintero's avatar
Federico Mena Quintero committed
682
683
684
/* Returns a toplevel GtkWindow, or NULL if none */
static GtkWindow *
get_toplevel (GtkWidget *widget)
Federico Mena Quintero's avatar
Federico Mena Quintero committed
685
{
686
687
  GtkWidget *toplevel;

Federico Mena Quintero's avatar
Federico Mena Quintero committed
688
  toplevel = gtk_widget_get_toplevel (widget);
689
  if (!GTK_WIDGET_TOPLEVEL (toplevel))
Federico Mena Quintero's avatar
Federico Mena Quintero committed
690
691
692
693
    return NULL;
  else
    return GTK_WINDOW (toplevel);
}
694

Federico Mena Quintero's avatar
Federico Mena Quintero committed
695
696
697
698
699
700
/* 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);
701
702
}

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

725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
/* 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);
}

749
750
751
752
753
754
755
/* 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)
{
756
  char *uri;
757
758
  char *msg;

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

769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
/* 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
788
789
790
791
792
793
794
795
796
797
798
799
  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);
800
801

  error = NULL;
Federico Mena Quintero's avatar
Federico Mena Quintero committed
802
  result = _gtk_file_chooser_set_current_folder_path (GTK_FILE_CHOOSER (impl), path_copy, &error);
803
804

  if (!result)
Federico Mena Quintero's avatar
Federico Mena Quintero committed
805
806
807
    error_changing_folder_dialog (impl, path_copy, error);

  gtk_file_path_free (path_copy);
808
809
810
811

  return result;
}

812
static void
813
update_preview_widget_visibility (GtkFileChooserDefault *impl)
814
{
815
816
817
818
819
820
821
822
823
824
825
826
827
  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)
828
829
830
831
	{
	  gtk_widget_destroy (impl->preview_label);
	  impl->preview_label = NULL;
	}
832
833
    }

834
  if (impl->preview_widget_active && impl->preview_widget)
835
    gtk_widget_show (impl->preview_box);
836
  else
837
    gtk_widget_hide (impl->preview_box);
838
839

  g_signal_emit_by_name (impl, "default-size-changed");
840
841
}

842
static void
843
844
set_preview_widget (GtkFileChooserDefault *impl,
		    GtkWidget             *preview_widget)
845
846
847
848
849
{
  if (preview_widget == impl->preview_widget)
    return;

  if (impl->preview_widget)
850
    gtk_container_remove (GTK_CONTAINER (impl->preview_box),
851
			  impl->preview_widget);
852
853
854
855

  impl->preview_widget = preview_widget;
  if (impl->preview_widget)
    {
856
      gtk_widget_show (impl->preview_widget);
857
858
859
860
      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);
861
    }
862
863

  update_preview_widget_visibility (impl);
864
865
}

Federico Mena Quintero's avatar
Federico Mena Quintero committed
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
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
/* 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));
}

924
/* If a shortcut corresponds to the current folder, selects it */
925
static void
926
shortcuts_find_current_folder (GtkFileChooserDefault *impl)
927
{
928
  GtkTreeSelection *selection;
929
930
  int pos;
  GtkTreePath *path;
931

932
  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view));
933
934
935
936
937
938
939
940
941
942
943

  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);
944
945
}

946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
/* 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;
}

962
963
/* Convenience function to get the display name and icon info for a path */
static GtkFileInfo *
964
get_file_info (GtkFileSystem *file_system, const GtkFilePath *path, gboolean name_only, GError **error)
965
966
967
968
969
{
  GtkFilePath *parent_path;
  GtkFileFolder *parent_folder;
  GtkFileInfo *info;

970
971
  info = NULL;

972
973
974
  if (!gtk_file_system_get_parent (file_system, path, &parent_path, error))
    return NULL;

975
  parent_folder = gtk_file_system_get_folder (file_system, parent_path ? parent_path : path,
976
					      GTK_FILE_INFO_DISPLAY_NAME
977
					      | (name_only ? 0 : GTK_FILE_INFO_IS_FOLDER),
978
					      error);
979
  if (!parent_folder)
980
    goto out;
981

982
  info = gtk_file_folder_get_info (parent_folder, parent_path ? path : NULL, error);
983
  g_object_unref (parent_folder);
984

985
986
987
 out:

  gtk_file_path_free (parent_path);
988
989
990
  return info;
}

991
992
/* 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.
993
 */
994
static gboolean
995
996
shortcuts_insert_path (GtkFileChooserDefault *impl,
		       int                    pos,
997
998
		       gboolean               is_volume,
		       GtkFileSystemVolume   *volume,
999
1000
		       const GtkFilePath     *path,
		       const char            *label,
For faster browsing, not all history is shown. View entire blame