gtkfilechooserdefault.c 164 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 "gtkimagemenuitem.h"
47
#include "gtkintl.h"
48
#include "gtklabel.h"
49
#include "gtkmarshalers.h"
50
#include "gtkmenuitem.h"
51
#include "gtkmessagedialog.h"
52
#include "gtkpathbar.h"
53
54
#include "gtkprivate.h"
#include "gtkscrolledwindow.h"
55
#include "gtkseparatormenuitem.h"
56
#include "gtksizegroup.h"
57
58
#include "gtkstock.h"
#include "gtktable.h"
59
60
#include "gtktreednd.h"
#include "gtktreeprivate.h"
61
62
63
64
65
66
#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
67

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

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

77
typedef struct _GtkFileChooserDefaultClass GtkFileChooserDefaultClass;
Owen Taylor's avatar
Owen Taylor committed
78

79
80
81
#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
82

83

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

89
struct _GtkFileChooserDefault
Owen Taylor's avatar
Owen Taylor committed
90
91
92
{
  GtkVBox parent_instance;

93
94
  GtkFileChooserAction action;

Owen Taylor's avatar
Owen Taylor committed
95
  GtkFileSystem *file_system;
96

97
98
  /* Save mode widgets */
  GtkWidget *save_widgets;
99

100
101
102
103
104
105
106
107
108
109
110
111
  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
112
  GtkWidget *browse_files_popup_menu;
113
  GtkWidget *browse_files_popup_menu_add_shortcut_item;
Federico Mena Quintero's avatar
Federico Mena Quintero committed
114
  GtkWidget *browse_files_popup_menu_hidden_files_item;
115
116
117
  GtkWidget *browse_new_folder_button;
  GtkWidget *browse_path_bar;
  GtkWidget *browse_extra_align;
118

119
  GtkFileSystemModel *browse_files_model;
120

121
  GtkWidget *filter_combo_hbox;
122
  GtkWidget *filter_combo;
123
124
  GtkWidget *preview_box;
  GtkWidget *preview_label;
125
126
  GtkWidget *preview_widget;
  GtkWidget *extra_widget;
127

128
  GtkListStore *shortcuts_model;
129
130
  GtkTreeModel *shortcuts_filter_model;

Owen Taylor's avatar
Owen Taylor committed
131
  GtkTreeModelSort *sort_model;
Owen Taylor's avatar
Owen Taylor committed
132

Owen Taylor's avatar
Owen Taylor committed
133
134
  GtkFileFilter *current_filter;
  GSList *filters;
135

136
137
  gboolean has_home;
  gboolean has_desktop;
138
139

  int num_volumes;
140
141
  int num_shortcuts;
  int num_bookmarks;
142

143
144
  gulong volumes_changed_id;
  gulong bookmarks_changed_id;
145

146
  GtkFilePath *current_volume_path;
147
  GtkFilePath *current_folder;
148
  GtkFilePath *preview_path;
149
  char *preview_display_name;
150

151
152
153
  GtkTreeViewColumn *list_name_column;
  GtkCellRenderer *list_name_renderer;

Federico Mena Quintero's avatar
Federico Mena Quintero committed
154
155
156
  GSource *edited_idle;
  char *edited_new_text;

157
  gulong settings_signal_id;
Federico Mena Quintero's avatar
Federico Mena Quintero committed
158
159
  int icon_size;

160
161
162
  gulong toplevel_set_focus_id;
  GtkWidget *toplevel_last_focus_widget;

163
#if 0
164
165
  GdkDragContext *shortcuts_drag_context;
  GSource *shortcuts_drag_outside_idle;
166
#endif
167

168
169
  /* Flags */

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

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

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

static guint signals[LAST_SIGNAL] = { 0 };

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

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

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

220
221
222
223
224
225
226
227
228
229
230
/* 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 },
231
232
233
  { "text/uri-list", 0, TEXT_URI_LIST }
};

234
235
236
237
238
239
240
241
242
243
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]));
244

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

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

260
261
262
#define PREVIEW_HBOX_SPACING 12
#define NUM_LINES 40
#define NUM_CHARS 60
263

264
265
266
267
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);
268
269
270
271
272
273
274
275
276
277
278
279
280

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);
281
static void     gtk_file_chooser_default_dispose      (GObject               *object);
282
static void     gtk_file_chooser_default_show_all       (GtkWidget             *widget);
283
284
static void     gtk_file_chooser_default_hierarchy_changed (GtkWidget          *widget,
							    GtkWidget          *previous_toplevel);
285
286
287
288
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);
289

290
291
292
static gboolean       gtk_file_chooser_default_set_current_folder 	   (GtkFileChooser    *chooser,
									    const GtkFilePath *path,
									    GError           **error);
293
294
static GtkFilePath *  gtk_file_chooser_default_get_current_folder 	   (GtkFileChooser    *chooser);
static void           gtk_file_chooser_default_set_current_name   	   (GtkFileChooser    *chooser,
295
									    const gchar       *name);
296
297
298
static gboolean       gtk_file_chooser_default_select_path        	   (GtkFileChooser    *chooser,
									    const GtkFilePath *path,
									    GError           **error);
299
static void           gtk_file_chooser_default_unselect_path      	   (GtkFileChooser    *chooser,
300
									    const GtkFilePath *path);
301
302
303
304
305
306
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,
307
									    GtkFileFilter     *filter);
308
static void           gtk_file_chooser_default_remove_filter      	   (GtkFileChooser    *chooser,
309
									    GtkFileFilter     *filter);
310
311
312
313
314
315
316
317
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);
318

319
320
321
322
323
324
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);
325
static gboolean       gtk_file_chooser_default_should_respond         (GtkFileChooserEmbed *chooser_embed);
Federico Mena Quintero's avatar
Federico Mena Quintero committed
326
static void           gtk_file_chooser_default_initial_focus          (GtkFileChooserEmbed *chooser_embed);
327

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

334
335
336
337
static void set_current_filter   (GtkFileChooserDefault *impl,
				  GtkFileFilter         *filter);
static void check_preview_change (GtkFileChooserDefault *impl);

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

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

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

361
362
static void bookmarks_check_add_sensitivity (GtkFileChooserDefault *impl);

363
364
365
366
367
368
static void list_selection_changed     (GtkTreeSelection      *tree_selection,
					GtkFileChooserDefault *impl);
static void list_row_activated         (GtkTreeView           *tree_view,
					GtkTreePath           *path,
					GtkTreeViewColumn     *column,
					GtkFileChooserDefault *impl);
369

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

375
376
377
378
379
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
380
381
382
383
384
static void list_icon_data_func (GtkTreeViewColumn *tree_column,
				 GtkCellRenderer   *cell,
				 GtkTreeModel      *tree_model,
				 GtkTreeIter       *iter,
				 gpointer           data);
385
386
387
388
389
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
390
#if 0
391
392
393
394
395
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
396
397
398
399
400
401
#endif
static void list_mtime_data_func (GtkTreeViewColumn *tree_column,
				  GtkCellRenderer   *cell,
				  GtkTreeModel      *tree_model,
				  GtkTreeIter       *iter,
				  gpointer           data);
402

Owen Taylor's avatar
Owen Taylor committed
403
static GObjectClass *parent_class;
404

405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435


/* 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
436
GType
437
_gtk_file_chooser_default_get_type (void)
Owen Taylor's avatar
Owen Taylor committed
438
{
439
  static GType file_chooser_default_type = 0;
Owen Taylor's avatar
Owen Taylor committed
440

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

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

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

470
471
      file_chooser_default_type = g_type_register_static (GTK_TYPE_VBOX, "GtkFileChooserDefault",
							 &file_chooser_default_info, 0);
472

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

481
  return file_chooser_default_type;
Owen Taylor's avatar
Owen Taylor committed
482
483
484
}

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

Owen Taylor's avatar
Owen Taylor committed
491
492
  parent_class = g_type_class_peek_parent (class);

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

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

504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
  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);
520
521
522
523
524
525
526
527
  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);
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
  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,
545
				GDK_Up, GDK_MOD1_MASK,
546
547
548
				"up-folder",
				0);
  gtk_binding_entry_add_signal (binding_set,
549
				GDK_KP_Up, GDK_MOD1_MASK,
550
551
552
				"up-folder",
				0);

553
554
555
556
557
558
559
560
561
  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);

562
  gtk_binding_entry_add_signal (binding_set,
563
564
				GDK_Home, GDK_MOD1_MASK,
				"home-folder",
565
566
				0);
  gtk_binding_entry_add_signal (binding_set,
567
568
				GDK_KP_Home, GDK_MOD1_MASK,
				"home-folder",
569
570
				0);

Owen Taylor's avatar
Owen Taylor committed
571
  _gtk_file_chooser_install_properties (gobject_class);
572

Owen Taylor's avatar
Owen Taylor committed
573
574
575
576
577
  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
578
579
580
}

static void
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
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
599
600
}

601
602
603
604
605
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;
606
  iface->should_respond = gtk_file_chooser_default_should_respond;
Federico Mena Quintero's avatar
Federico Mena Quintero committed
607
  iface->initial_focus = gtk_file_chooser_default_initial_focus;
608
}
Owen Taylor's avatar
Owen Taylor committed
609
static void
610
gtk_file_chooser_default_init (GtkFileChooserDefault *impl)
Owen Taylor's avatar
Owen Taylor committed
611
{
612
613
  impl->local_only = TRUE;
  impl->preview_widget_active = TRUE;
614
  impl->use_preview_label = TRUE;
615
616
  impl->select_multiple = FALSE;
  impl->show_hidden = FALSE;
Federico Mena Quintero's avatar
Federico Mena Quintero committed
617
  impl->icon_size = FALLBACK_ICON_SIZE;
618

619
  gtk_widget_set_redraw_on_allocate (GTK_WIDGET (impl), TRUE);
620
  gtk_box_set_spacing (GTK_BOX (impl), 12);
621
622
623
}

static void
624
gtk_file_chooser_default_finalize (GObject *object)
625
{
626
  GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (object);
627
  GSList *l;
628

629
630
  g_signal_handler_disconnect (impl->file_system, impl->volumes_changed_id);
  impl->volumes_changed_id = 0;
631
632
  g_signal_handler_disconnect (impl->file_system, impl->bookmarks_changed_id);
  impl->bookmarks_changed_id = 0;
633
634
  g_object_unref (impl->file_system);

635
636
637
638
639
640
641
642
643
644
645
  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
646
647
648
649
650
651
652
653
654
655

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

656
657
658
659
660
661
662
663
664
665
666
667
668
  /* 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);

669
670
  g_free (impl->preview_display_name);

Federico Mena Quintero's avatar
Federico Mena Quintero committed
671
672
  g_free (impl->edited_new_text);

673
674
675
  G_OBJECT_CLASS (parent_class)->finalize (object);
}

676
/* Shows an error dialog set as transient for the specified window */
677
static void
678
679
error_message_with_parent (GtkWindow  *parent,
			   const char *msg)
680
681
682
{
  GtkWidget *dialog;

683
  dialog = gtk_message_dialog_new (parent,
684
685
				   GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
				   GTK_MESSAGE_ERROR,
686
				   GTK_BUTTONS_OK,
687
688
				   "%s",
				   msg);
689
690
691
692
  gtk_dialog_run (GTK_DIALOG (dialog));
  gtk_widget_destroy (dialog);
}

Federico Mena Quintero's avatar
Federico Mena Quintero committed
693
694
695
/* Returns a toplevel GtkWindow, or NULL if none */
static GtkWindow *
get_toplevel (GtkWidget *widget)
Federico Mena Quintero's avatar
Federico Mena Quintero committed
696
{
697
698
  GtkWidget *toplevel;

Federico Mena Quintero's avatar
Federico Mena Quintero committed
699
  toplevel = gtk_widget_get_toplevel (widget);
700
  if (!GTK_WIDGET_TOPLEVEL (toplevel))
Federico Mena Quintero's avatar
Federico Mena Quintero committed
701
702
703
704
    return NULL;
  else
    return GTK_WINDOW (toplevel);
}
705

Federico Mena Quintero's avatar
Federico Mena Quintero committed
706
707
708
709
710
711
/* 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);
712
713
}

714
715
716
717
718
719
720
/* 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)
{
721
722
723
724
725
  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
726
727
728
      char *text = g_strdup_printf (msg,
				    uri,
				    error->message);
729
730
731
732
733
      error_message (impl, text);
      g_free (text);
      g_free (uri);
      g_error_free (error);
    }
734
735
}

736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
/* 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);
}

760
761
762
763
764
765
766
/* 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)
{
767
  char *uri;
768
769
  char *msg;

770
  uri = gtk_file_system_path_to_uri (impl->file_system, base_path);
771
  msg = g_strdup_printf (_("Could not build file name from '%s' and '%s':\n%s"),
772
			 uri, file_part,
773
774
			 error->message);
  error_message (impl, msg);
775
  g_free (uri);
776
777
778
779
  g_free (msg);
  g_error_free (error);
}

780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
/* 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
799
800
801
802
803
804
805
806
807
808
809
810
  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);
811
812

  error = NULL;
Federico Mena Quintero's avatar
Federico Mena Quintero committed
813
  result = _gtk_file_chooser_set_current_folder_path (GTK_FILE_CHOOSER (impl), path_copy, &error);
814
815

  if (!result)
Federico Mena Quintero's avatar
Federico Mena Quintero committed
816
817
818
    error_changing_folder_dialog (impl, path_copy, error);

  gtk_file_path_free (path_copy);
819
820
821
822

  return result;
}

823
static void
824
update_preview_widget_visibility (GtkFileChooserDefault *impl)
825
{
826
827
828
829
830
831
832
833
834
835
836
837
838
  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)
839
840
841
842
	{
	  gtk_widget_destroy (impl->preview_label);
	  impl->preview_label = NULL;
	}
843
844
    }

845
  if (impl->preview_widget_active && impl->preview_widget)
846
    gtk_widget_show (impl->preview_box);
847
  else
848
    gtk_widget_hide (impl->preview_box);
849
850

  g_signal_emit_by_name (impl, "default-size-changed");
851
852
}

853
static void
854
855
set_preview_widget (GtkFileChooserDefault *impl,
		    GtkWidget             *preview_widget)
856
857
858
859
860
{
  if (preview_widget == impl->preview_widget)
    return;

  if (impl->preview_widget)
861
    gtk_container_remove (GTK_CONTAINER (impl->preview_box),
862
			  impl->preview_widget);
863
864
865
866

  impl->preview_widget = preview_widget;
  if (impl->preview_widget)
    {
867
      gtk_widget_show (impl->preview_widget);
868
869
870
871
      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);
872
    }
873
874

  update_preview_widget_visibility (impl);
875
876
}

Federico Mena Quintero's avatar
Federico Mena Quintero committed
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
924
925
926
927
928
929
930
931
932
933
934
/* 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));
}

935
/* If a shortcut corresponds to the current folder, selects it */
936
static void
937
shortcuts_find_current_folder (GtkFileChooserDefault *impl)
938
{
939
  GtkTreeSelection *selection;
940
941
  int pos;
  GtkTreePath *path;
942

943
  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view));
944
945
946
947
948
949
950
951
952
953
954

  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);
955
956
}

957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
/* 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;
}

973
974
/* Convenience function to get the display name and icon info for a path */
static GtkFileInfo *
975
get_file_info (GtkFileSystem *file_system, const GtkFilePath *path, gboolean name_only, GError **error)
976
977
978
979
980
{
  GtkFilePath *parent_path;
  GtkFileFolder *parent_folder;
  GtkFileInfo *info;

981
982
  info = NULL;

983
984
985
  if (!gtk_file_system_get_parent (file_system, path, &parent_path, error))
    return NULL;

986
  parent_folder = gtk_file_system_get_folder (file_system, parent_path ? parent_path : path,
987
					      GTK_FILE_INFO_DISPLAY_NAME
988
					      | (name_only ? 0 : GTK_FILE_INFO_IS_FOLDER),
989
					      error);
990
  if (!parent_folder)
991
    goto out;
992

993
  info = gtk_file_folder_get_info (parent_folder, parent_path ? path : NULL, error);
994
  g_object_unref (parent_folder);
995

996
997
998
 out:

  gtk_file_path_free (parent_path);
999
1000
  return info;
}
For faster browsing, not all history is shown. View entire blame