gtkfilechooserdefault.c 169 KB
Newer Older
Owen Taylor's avatar
Owen Taylor committed
1
/* GTK - The GIMP Toolkit
2
 * gtkfilechooserdefault.c: Default implementation of GtkFileChooser
Owen Taylor's avatar
Owen Taylor committed
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
 * Copyright (C) 2003, Red Hat, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

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

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

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

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

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

86

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

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

96
97
  GtkFileChooserAction action;

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

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

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

122
  GtkFileSystemModel *browse_files_model;
123

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

131
  GtkListStore *shortcuts_model;
132
133
  GtkTreeModel *shortcuts_filter_model;

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

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

Matthias Clasen's avatar
Matthias Clasen committed
139
140
  GtkTooltips *tooltips;

141
142
  gboolean has_home;
  gboolean has_desktop;
143
144

  int num_volumes;
145
146
  int num_shortcuts;
  int num_bookmarks;
147

148
149
  gulong volumes_changed_id;
  gulong bookmarks_changed_id;
150

151
  GtkFilePath *current_volume_path;
152
  GtkFilePath *current_folder;
153
  GtkFilePath *preview_path;
154
  char *preview_display_name;
155

156
157
158
  GtkTreeViewColumn *list_name_column;
  GtkCellRenderer *list_name_renderer;

Federico Mena Quintero's avatar
Federico Mena Quintero committed
159
160
161
  GSource *edited_idle;
  char *edited_new_text;

162
  gulong settings_signal_id;
Federico Mena Quintero's avatar
Federico Mena Quintero committed
163
164
  int icon_size;

165
166
167
  gulong toplevel_set_focus_id;
  GtkWidget *toplevel_last_focus_widget;

168
#if 0
169
170
  GdkDragContext *shortcuts_drag_context;
  GSource *shortcuts_drag_outside_idle;
171
#endif
172

173
174
  /* Flags */

175
176
  guint local_only : 1;
  guint preview_widget_active : 1;
177
  guint use_preview_label : 1;
178
179
  guint select_multiple : 1;
  guint show_hidden : 1;
180
  guint list_sort_ascending : 1;
181
  guint changing_folder : 1;
182
  guint shortcuts_current_folder_active : 1;
183
184

#if 0
185
  guint shortcuts_drag_outside : 1;
186
#endif
187
188
};

189
190
191
192
/* Signal IDs */
enum {
  LOCATION_POPUP,
  UP_FOLDER,
193
  DOWN_FOLDER,
194
195
196
197
198
199
  HOME_FOLDER,
  LAST_SIGNAL
};

static guint signals[LAST_SIGNAL] = { 0 };

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

211
212
213
214
215
216
217
218
/* Column numbers for the file list */
enum {
  FILE_LIST_COL_NAME,
  FILE_LIST_COL_SIZE,
  FILE_LIST_COL_MTIME,
  FILE_LIST_COL_NUM_COLUMNS
};

219
220
/* Identifiers for target types */
enum {
221
  GTK_TREE_MODEL_ROW,
222
223
224
  TEXT_URI_LIST
};

225
226
227
228
229
230
231
232
233
234
235
/* 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 },
236
237
238
  { "text/uri-list", 0, TEXT_URI_LIST }
};

239
240
241
242
243
244
245
246
247
248
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]));
249

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

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

265
266
267
#define PREVIEW_HBOX_SPACING 12
#define NUM_LINES 40
#define NUM_CHARS 60
268

269
270
271
272
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);
273
274
275
276
277
278
279
280
281
282
283
284
285

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);
286
static void     gtk_file_chooser_default_dispose      (GObject               *object);
287
static void     gtk_file_chooser_default_show_all       (GtkWidget             *widget);
288
289
static void     gtk_file_chooser_default_hierarchy_changed (GtkWidget          *widget,
							    GtkWidget          *previous_toplevel);
290
291
292
293
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);
294

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

324
325
326
327
328
329
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);
330
static gboolean       gtk_file_chooser_default_should_respond         (GtkFileChooserEmbed *chooser_embed);
Federico Mena Quintero's avatar
Federico Mena Quintero committed
331
static void           gtk_file_chooser_default_initial_focus          (GtkFileChooserEmbed *chooser_embed);
332

333
334
static void location_popup_handler (GtkFileChooserDefault *impl);
static void up_folder_handler      (GtkFileChooserDefault *impl);
335
static void down_folder_handler    (GtkFileChooserDefault *impl);
336
static void home_folder_handler    (GtkFileChooserDefault *impl);
337
static void update_appearance      (GtkFileChooserDefault *impl);
338

339
340
341
342
static void set_current_filter   (GtkFileChooserDefault *impl,
				  GtkFileFilter         *filter);
static void check_preview_change (GtkFileChooserDefault *impl);

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

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

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

366
367
static void bookmarks_check_add_sensitivity (GtkFileChooserDefault *impl);

368
369
370
371
372
373
static void list_selection_changed     (GtkTreeSelection      *tree_selection,
					GtkFileChooserDefault *impl);
static void list_row_activated         (GtkTreeView           *tree_view,
					GtkTreePath           *path,
					GtkTreeViewColumn     *column,
					GtkFileChooserDefault *impl);
374

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

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

408
409
410
static const GtkFileInfo *get_list_file_info (GtkFileChooserDefault *impl,
					      GtkTreeIter           *iter);

Owen Taylor's avatar
Owen Taylor committed
411
static GObjectClass *parent_class;
412

413
414
415
416
417
418
419
420
421
422
423
424
425
426


/* Drag and drop interface declarations */

typedef struct {
  GtkTreeModelFilter parent;

  GtkFileChooserDefault *impl;
} ShortcutsModelFilter;

typedef struct {
  GtkTreeModelFilterClass parent_class;
} ShortcutsModelFilterClass;

427
#define SHORTCUTS_MODEL_FILTER_TYPE (_shortcuts_model_filter_get_type ())
428
429
430
431
432
#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,
433
			 _shortcuts_model_filter,
434
435
436
437
438
439
440
441
442
443
			 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
444
GType
445
_gtk_file_chooser_default_get_type (void)
Owen Taylor's avatar
Owen Taylor committed
446
{
447
  static GType file_chooser_default_type = 0;
Owen Taylor's avatar
Owen Taylor committed
448

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

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

471
472
473
474
475
476
477
      static const GInterfaceInfo file_chooser_embed_info =
      {
	(GInterfaceInitFunc) gtk_file_chooser_embed_default_iface_init, /* interface_init */
	NULL,			                                       /* interface_finalize */
	NULL			                                       /* interface_data */
      };

478
479
      file_chooser_default_type = g_type_register_static (GTK_TYPE_VBOX, "GtkFileChooserDefault",
							 &file_chooser_default_info, 0);
480

481
      g_type_add_interface_static (file_chooser_default_type,
Owen Taylor's avatar
Owen Taylor committed
482
483
				   GTK_TYPE_FILE_CHOOSER,
				   &file_chooser_info);
484
485
486
      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
487
488
    }

489
  return file_chooser_default_type;
Owen Taylor's avatar
Owen Taylor committed
490
491
492
}

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

Owen Taylor's avatar
Owen Taylor committed
499
500
  parent_class = g_type_class_peek_parent (class);

501
502
503
504
  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;
505
  gobject_class->dispose = gtk_file_chooser_default_dispose;
Owen Taylor's avatar
Owen Taylor committed
506

507
  widget_class->show_all = gtk_file_chooser_default_show_all;
508
  widget_class->hierarchy_changed = gtk_file_chooser_default_hierarchy_changed;
509
510
  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
511

512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
  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);
528
529
530
531
532
533
534
535
  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);
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
  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);

552
553
554
555
556
  gtk_binding_entry_add_signal (binding_set,
				GDK_slash, 0,
				"location-popup",
				0);

557
  gtk_binding_entry_add_signal (binding_set,
558
				GDK_Up, GDK_MOD1_MASK,
559
560
561
				"up-folder",
				0);
  gtk_binding_entry_add_signal (binding_set,
562
				GDK_KP_Up, GDK_MOD1_MASK,
563
564
565
				"up-folder",
				0);

566
567
568
569
570
571
572
573
574
  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);

575
  gtk_binding_entry_add_signal (binding_set,
576
577
				GDK_Home, GDK_MOD1_MASK,
				"home-folder",
578
579
				0);
  gtk_binding_entry_add_signal (binding_set,
580
581
				GDK_KP_Home, GDK_MOD1_MASK,
				"home-folder",
582
583
				0);

Owen Taylor's avatar
Owen Taylor committed
584
  _gtk_file_chooser_install_properties (gobject_class);
585

Owen Taylor's avatar
Owen Taylor committed
586
587
588
589
590
  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
591
592
593
}

static void
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
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
612
613
}

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

632
  gtk_widget_set_redraw_on_allocate (GTK_WIDGET (impl), TRUE);
633
  gtk_box_set_spacing (GTK_BOX (impl), 12);
Matthias Clasen's avatar
Matthias Clasen committed
634
635
636
637

  impl->tooltips = gtk_tooltips_new ();
  g_object_ref (impl->tooltips);
  gtk_object_sink (GTK_OBJECT (impl->tooltips));
638
639
}

Federico Mena Quintero's avatar
Federico Mena Quintero committed
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
/* Frees the data columns for the specified iter in the shortcuts model*/
static void
shortcuts_free_row_data (GtkFileChooserDefault *impl,
			 GtkTreeIter           *iter)
{
  gpointer col_data;
  gboolean is_volume;

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

  if (is_volume)
    {
      GtkFileSystemVolume *volume;

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

      path = col_data;
      gtk_file_path_free (path);
    }
}

/* Frees all the data columns in the shortcuts model */
static void
shortcuts_free (GtkFileChooserDefault *impl)
{
  GtkTreeIter iter;

  if (!impl->shortcuts_model)
    return;

  if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (impl->shortcuts_model), &iter))
    do
      {
	shortcuts_free_row_data (impl, &iter);
      }
    while (gtk_tree_model_iter_next (GTK_TREE_MODEL (impl->shortcuts_model), &iter));

  g_object_unref (impl->shortcuts_model);
  impl->shortcuts_model = NULL;
}

691
static void
692
gtk_file_chooser_default_finalize (GObject *object)
693
{
694
  GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (object);
695
  GSList *l;
696

Federico Mena Quintero's avatar
Federico Mena Quintero committed
697
698
699
700
701
  if (impl->shortcuts_filter_model)
    g_object_unref (impl->shortcuts_filter_model);

  shortcuts_free (impl);

702
703
  g_signal_handler_disconnect (impl->file_system, impl->volumes_changed_id);
  impl->volumes_changed_id = 0;
704
705
  g_signal_handler_disconnect (impl->file_system, impl->bookmarks_changed_id);
  impl->bookmarks_changed_id = 0;
706
707
  g_object_unref (impl->file_system);

708
709
710
711
712
713
714
715
716
717
718
  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
719
720
721
722
723
724
725
726
727
728

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

729
730
731
732
733
734
735
  /* Free all the Models we have */
  if (impl->browse_files_model)
    g_object_unref (impl->browse_files_model);

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

736
737
  g_free (impl->preview_display_name);

Federico Mena Quintero's avatar
Federico Mena Quintero committed
738
739
  g_free (impl->edited_new_text);

740
741
742
  G_OBJECT_CLASS (parent_class)->finalize (object);
}

743
/* Shows an error dialog set as transient for the specified window */
744
static void
745
746
error_message_with_parent (GtkWindow  *parent,
			   const char *msg)
747
748
749
{
  GtkWidget *dialog;

750
  dialog = gtk_message_dialog_new (parent,
751
752
				   GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
				   GTK_MESSAGE_ERROR,
753
				   GTK_BUTTONS_OK,
754
755
				   "%s",
				   msg);
756
757
758
759
  gtk_dialog_run (GTK_DIALOG (dialog));
  gtk_widget_destroy (dialog);
}

Federico Mena Quintero's avatar
Federico Mena Quintero committed
760
761
762
/* Returns a toplevel GtkWindow, or NULL if none */
static GtkWindow *
get_toplevel (GtkWidget *widget)
Federico Mena Quintero's avatar
Federico Mena Quintero committed
763
{
764
765
  GtkWidget *toplevel;

Federico Mena Quintero's avatar
Federico Mena Quintero committed
766
  toplevel = gtk_widget_get_toplevel (widget);
767
  if (!GTK_WIDGET_TOPLEVEL (toplevel))
Federico Mena Quintero's avatar
Federico Mena Quintero committed
768
769
770
771
    return NULL;
  else
    return GTK_WINDOW (toplevel);
}
772

Federico Mena Quintero's avatar
Federico Mena Quintero committed
773
774
775
776
777
778
/* 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);
779
780
}

781
782
783
784
785
786
787
/* 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)
{
788
789
790
791
792
  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
793
794
795
      char *text = g_strdup_printf (msg,
				    uri,
				    error->message);
796
797
798
799
800
      error_message (impl, text);
      g_free (text);
      g_free (uri);
      g_error_free (error);
    }
801
802
}

803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
/* 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);
}

827
828
829
830
831
832
833
/* 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)
{
834
  char *uri;
835
836
  char *msg;

837
  uri = gtk_file_system_path_to_uri (impl->file_system, base_path);
838
  msg = g_strdup_printf (_("Could not build file name from '%s' and '%s':\n%s"),
839
			 uri, file_part,
840
841
			 error->message);
  error_message (impl, msg);
842
  g_free (uri);
843
844
845
846
  g_free (msg);
  g_error_free (error);
}

847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
/* 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
866
867
868
869
870
871
872
873
874
875
876
877
  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);
878
879

  error = NULL;
Federico Mena Quintero's avatar
Federico Mena Quintero committed
880
  result = _gtk_file_chooser_set_current_folder_path (GTK_FILE_CHOOSER (impl), path_copy, &error);
881
882

  if (!result)
Federico Mena Quintero's avatar
Federico Mena Quintero committed
883
884
885
    error_changing_folder_dialog (impl, path_copy, error);

  gtk_file_path_free (path_copy);
886
887
888
889

  return result;
}

890
static void
891
update_preview_widget_visibility (GtkFileChooserDefault *impl)
892
{
893
894
895
896
897
898
899
900
901
902
903
904
905
  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)
906
907
908
909
	{
	  gtk_widget_destroy (impl->preview_label);
	  impl->preview_label = NULL;
	}
910
911
    }

912
  if (impl->preview_widget_active && impl->preview_widget)
913
    gtk_widget_show (impl->preview_box);
914
  else
915
    gtk_widget_hide (impl->preview_box);
916
917

  g_signal_emit_by_name (impl, "default-size-changed");
918
919
}

920
static void
921
922
set_preview_widget (GtkFileChooserDefault *impl,
		    GtkWidget             *preview_widget)
923
924
925
926
927
{
  if (preview_widget == impl->preview_widget)
    return;

  if (impl->preview_widget)
928
    gtk_container_remove (GTK_CONTAINER (impl->preview_box),
929
			  impl->preview_widget);
930
931
932
933

  impl->preview_widget = preview_widget;
  if (impl->preview_widget)
    {
934
      gtk_widget_show (impl->preview_widget);
935
936
937
938
      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);
939
    }
940
941

  update_preview_widget_visibility (impl);
942
943
}

Federico Mena Quintero's avatar
Federico Mena Quintero committed
944
945
946
947
948
949
950
951
952
953
954
/* Re-reads all the icons for the shortcuts, used when the theme changes */
static void
shortcuts_reload_icons (GtkFileChooserDefault *impl)
{
  GtkTreeIter iter;

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

  do {
    gpointer data;
Federico Mena Quintero's avatar
Federico Mena Quintero committed
955
    gboolean is_volume;
Federico Mena Quintero's avatar
Federico Mena Quintero committed
956
957
958
959
    gboolean pixbuf_visible;
    GdkPixbuf *pixbuf;

    gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
Federico Mena Quintero's avatar
Federico Mena Quintero committed
960
			SHORTCUTS_COL_DATA, &data,
961
			SHORTCUTS_COL_IS_VOLUME, &is_volume,
Federico Mena Quintero's avatar
Federico Mena Quintero committed
962
963
964
			SHORTCUTS_COL_PIXBUF_VISIBLE, &pixbuf_visible,
			-1);

Federico Mena Quintero's avatar
Federico Mena Quintero committed
965
    if (pixbuf_visible && data)
Federico Mena Quintero's avatar
Federico Mena Quintero committed
966
      {
Federico Mena Quintero's avatar
Federico Mena Quintero committed
967
968
969
	if (is_volume)
	  {
	    GtkFileSystemVolume *volume;
Federico Mena Quintero's avatar
Federico Mena Quintero committed
970

Federico Mena Quintero's avatar
Federico Mena Quintero committed
971
972
973
974
975
976
977
	    volume = data;
	    pixbuf = gtk_file_system_volume_render_icon (impl->file_system, volume, GTK_WIDGET (impl),
							 impl->icon_size, NULL);
	  }
	else
	  {
	    const GtkFilePath *path;
Federico Mena Quintero's avatar
Federico Mena Quintero committed
978

Federico Mena Quintero's avatar
Federico Mena Quintero committed
979
980
981
982
	    path = data;
	    pixbuf = gtk_file_system_render_icon (impl->file_system, path, GTK_WIDGET (impl),
						  impl->icon_size, NULL);
	  }
Federico Mena Quintero's avatar
Federico Mena Quintero committed
983

Federico Mena Quintero's avatar
Federico Mena Quintero committed
984
985
986
987
988
989
	gtk_list_store_set (impl->shortcuts_model, &iter,
			    SHORTCUTS_COL_PIXBUF, pixbuf,
			    -1);
	if (pixbuf)
	  g_object_unref (pixbuf);
      }
Federico Mena Quintero's avatar
Federico Mena Quintero committed
990
991
992
  } while (gtk_tree_model_iter_next (GTK_TREE_MODEL (impl->shortcuts_model),&iter));
}

993
/* If a shortcut corresponds to the current folder, selects it */
994
static void
995
shortcuts_find_current_folder (GtkFileChooserDefault *impl)
996
{
997
  GtkTreeSelection *selection;
998
999
  int pos;
  GtkTreePath *path;
1000

For faster browsing, not all history is shown. View entire blame