gtkfilechooserdefault.c 174 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 "gtkeventbox.h"
35
#include "gtkexpander.h"
36
#include "gtkfilechooserdefault.h"
37
#include "gtkfilechooserembed.h"
38
#include "gtkfilechooserentry.h"
Owen Taylor's avatar
Owen Taylor committed
39 40 41
#include "gtkfilechooserutils.h"
#include "gtkfilechooser.h"
#include "gtkfilesystemmodel.h"
42 43 44
#include "gtkframe.h"
#include "gtkhbox.h"
#include "gtkhpaned.h"
Federico Mena Quintero's avatar
Federico Mena Quintero committed
45
#include "gtkiconfactory.h"
46 47
#include "gtkicontheme.h"
#include "gtkimage.h"
48
#include "gtkimagemenuitem.h"
49
#include "gtkintl.h"
50
#include "gtklabel.h"
51
#include "gtkmarshalers.h"
52
#include "gtkmenuitem.h"
53
#include "gtkmessagedialog.h"
54
#include "gtkpathbar.h"
55 56
#include "gtkprivate.h"
#include "gtkscrolledwindow.h"
57
#include "gtkseparatormenuitem.h"
58
#include "gtksizegroup.h"
59 60
#include "gtkstock.h"
#include "gtktable.h"
61 62
#include "gtktreednd.h"
#include "gtktreeprivate.h"
63 64 65 66
#include "gtktreeview.h"
#include "gtktreemodelsort.h"
#include "gtktreeselection.h"
#include "gtktreestore.h"
Matthias Clasen's avatar
Matthias Clasen committed
67
#include "gtktooltips.h"
68 69
#include "gtktypebuiltins.h"
#include "gtkvbox.h"
Owen Taylor's avatar
Owen Taylor committed
70

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

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

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

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

Federico Mena Quintero's avatar
Federico Mena Quintero committed
87 88 89 90 91 92
typedef enum {
	LOAD_LOADING,
	LOAD_FINISHED
} LoadState;

#define MAX_LOADING_TIME 500
93

94
struct _GtkFileChooserDefaultClass
Owen Taylor's avatar
Owen Taylor committed
95 96 97 98
{
  GtkVBoxClass parent_class;
};

Federico Mena Quintero's avatar
Federico Mena Quintero committed
99 100 101 102 103 104
typedef enum {
  PENDING_OP_NONE,
  PENDING_OP_SELECT_PATH,
  PENDING_OP_SELECT_FIRST
} PendingOp;

105
struct _GtkFileChooserDefault
Owen Taylor's avatar
Owen Taylor committed
106 107 108
{
  GtkVBox parent_instance;

109 110
  GtkFileChooserAction action;

Owen Taylor's avatar
Owen Taylor committed
111
  GtkFileSystem *file_system;
112

113 114
  /* Save mode widgets */
  GtkWidget *save_widgets;
115

116 117 118 119 120 121 122 123 124 125 126
  GtkWidget *save_file_name_entry;
  GtkWidget *save_folder_label;
  GtkWidget *save_folder_combo;
  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
127
  GtkWidget *browse_files_popup_menu;
128
  GtkWidget *browse_files_popup_menu_add_shortcut_item;
Federico Mena Quintero's avatar
Federico Mena Quintero committed
129
  GtkWidget *browse_files_popup_menu_hidden_files_item;
130 131
  GtkWidget *browse_new_folder_button;
  GtkWidget *browse_path_bar;
132

133
  GtkFileSystemModel *browse_files_model;
134

135
  GtkWidget *filter_combo_hbox;
136
  GtkWidget *filter_combo;
137 138
  GtkWidget *preview_box;
  GtkWidget *preview_label;
139
  GtkWidget *preview_widget;
Federico Mena Quintero's avatar
Federico Mena Quintero committed
140
  GtkWidget *extra_align;
141
  GtkWidget *extra_widget;
142

143
  GtkListStore *shortcuts_model;
144 145
  GtkTreeModel *shortcuts_filter_model;

Owen Taylor's avatar
Owen Taylor committed
146
  GtkTreeModelSort *sort_model;
Owen Taylor's avatar
Owen Taylor committed
147

Federico Mena Quintero's avatar
Federico Mena Quintero committed
148 149 150
  LoadState load_state;
  guint load_timeout_id;

Federico Mena Quintero's avatar
Federico Mena Quintero committed
151 152 153
  PendingOp pending_op;
  GtkFilePath *pending_select_path;

Owen Taylor's avatar
Owen Taylor committed
154 155
  GtkFileFilter *current_filter;
  GSList *filters;
156

Matthias Clasen's avatar
Matthias Clasen committed
157 158
  GtkTooltips *tooltips;

159 160
  gboolean has_home;
  gboolean has_desktop;
161 162

  int num_volumes;
163 164
  int num_shortcuts;
  int num_bookmarks;
165

166 167
  gulong volumes_changed_id;
  gulong bookmarks_changed_id;
168

169
  GtkFilePath *current_volume_path;
170
  GtkFilePath *current_folder;
171
  GtkFilePath *preview_path;
172
  char *preview_display_name;
173

174 175 176
  GtkTreeViewColumn *list_name_column;
  GtkCellRenderer *list_name_renderer;

Federico Mena Quintero's avatar
Federico Mena Quintero committed
177 178 179
  GSource *edited_idle;
  char *edited_new_text;

180
  gulong settings_signal_id;
Federico Mena Quintero's avatar
Federico Mena Quintero committed
181 182
  int icon_size;

183 184 185
  gulong toplevel_set_focus_id;
  GtkWidget *toplevel_last_focus_widget;

186
#if 0
187 188
  GdkDragContext *shortcuts_drag_context;
  GSource *shortcuts_drag_outside_idle;
189
#endif
190

191 192
  /* Flags */

193 194
  guint local_only : 1;
  guint preview_widget_active : 1;
195
  guint use_preview_label : 1;
196 197
  guint select_multiple : 1;
  guint show_hidden : 1;
198
  guint list_sort_ascending : 1;
199
  guint changing_folder : 1;
200
  guint shortcuts_current_folder_active : 1;
201 202

#if 0
203
  guint shortcuts_drag_outside : 1;
204
#endif
205 206
};

207 208 209 210
/* Signal IDs */
enum {
  LOCATION_POPUP,
  UP_FOLDER,
211
  DOWN_FOLDER,
212 213 214 215 216 217
  HOME_FOLDER,
  LAST_SIGNAL
};

static guint signals[LAST_SIGNAL] = { 0 };

218
/* Column numbers for the shortcuts tree.  Keep these in sync with shortcuts_model_create() */
219 220 221
enum {
  SHORTCUTS_COL_PIXBUF,
  SHORTCUTS_COL_NAME,
Federico Mena Quintero's avatar
Federico Mena Quintero committed
222 223
  SHORTCUTS_COL_DATA,
  SHORTCUTS_COL_IS_VOLUME,
224
  SHORTCUTS_COL_REMOVABLE,
225
  SHORTCUTS_COL_PIXBUF_VISIBLE,
226
  SHORTCUTS_COL_NUM_COLUMNS
Owen Taylor's avatar
Owen Taylor committed
227 228
};

229 230 231 232 233 234 235 236
/* Column numbers for the file list */
enum {
  FILE_LIST_COL_NAME,
  FILE_LIST_COL_SIZE,
  FILE_LIST_COL_MTIME,
  FILE_LIST_COL_NUM_COLUMNS
};

237 238
/* Identifiers for target types */
enum {
239
  GTK_TREE_MODEL_ROW,
240 241 242
  TEXT_URI_LIST
};

243 244 245 246 247 248 249 250 251 252 253
/* 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 },
254 255 256
  { "text/uri-list", 0, TEXT_URI_LIST }
};

257 258 259 260 261 262 263 264 265 266
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]));
267

268 269 270 271 272 273
/* Interesting places in the shortcuts bar */
typedef enum {
  SHORTCUTS_HOME,
  SHORTCUTS_DESKTOP,
  SHORTCUTS_VOLUMES,
  SHORTCUTS_SHORTCUTS,
274 275 276 277
  SHORTCUTS_BOOKMARKS_SEPARATOR,
  SHORTCUTS_BOOKMARKS,
  SHORTCUTS_CURRENT_FOLDER_SEPARATOR,
  SHORTCUTS_CURRENT_FOLDER
278 279
} ShortcutsIndex;

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

283 284 285
#define PREVIEW_HBOX_SPACING 12
#define NUM_LINES 40
#define NUM_CHARS 60
286

287 288 289 290
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);
291 292 293 294 295 296 297 298 299 300 301 302 303

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);
304
static void     gtk_file_chooser_default_dispose      (GObject               *object);
305
static void     gtk_file_chooser_default_show_all       (GtkWidget             *widget);
306 307
static void     gtk_file_chooser_default_hierarchy_changed (GtkWidget          *widget,
							    GtkWidget          *previous_toplevel);
308 309 310 311
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);
312

313 314 315
static gboolean       gtk_file_chooser_default_set_current_folder 	   (GtkFileChooser    *chooser,
									    const GtkFilePath *path,
									    GError           **error);
316 317
static GtkFilePath *  gtk_file_chooser_default_get_current_folder 	   (GtkFileChooser    *chooser);
static void           gtk_file_chooser_default_set_current_name   	   (GtkFileChooser    *chooser,
318
									    const gchar       *name);
319 320 321
static gboolean       gtk_file_chooser_default_select_path        	   (GtkFileChooser    *chooser,
									    const GtkFilePath *path,
									    GError           **error);
322
static void           gtk_file_chooser_default_unselect_path      	   (GtkFileChooser    *chooser,
323
									    const GtkFilePath *path);
324 325 326 327 328 329
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,
330
									    GtkFileFilter     *filter);
331
static void           gtk_file_chooser_default_remove_filter      	   (GtkFileChooser    *chooser,
332
									    GtkFileFilter     *filter);
333 334 335 336 337 338 339 340
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);
341

342 343 344 345 346 347
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);
348
static gboolean       gtk_file_chooser_default_should_respond         (GtkFileChooserEmbed *chooser_embed);
Federico Mena Quintero's avatar
Federico Mena Quintero committed
349
static void           gtk_file_chooser_default_initial_focus          (GtkFileChooserEmbed *chooser_embed);
350

351 352
static void location_popup_handler (GtkFileChooserDefault *impl,
				    const gchar           *path);
353
static void up_folder_handler      (GtkFileChooserDefault *impl);
354
static void down_folder_handler    (GtkFileChooserDefault *impl);
355
static void home_folder_handler    (GtkFileChooserDefault *impl);
356
static void update_appearance      (GtkFileChooserDefault *impl);
357

358 359 360 361
static void set_current_filter   (GtkFileChooserDefault *impl,
				  GtkFileFilter         *filter);
static void check_preview_change (GtkFileChooserDefault *impl);

362
static void filter_combo_changed       (GtkComboBox           *combo_box,
363
					GtkFileChooserDefault *impl);
364 365 366 367
static void     shortcuts_row_activated_cb (GtkTreeView           *tree_view,
					    GtkTreePath           *path,
					    GtkTreeViewColumn     *column,
					    GtkFileChooserDefault *impl);
Federico Mena Quintero's avatar
Federico Mena Quintero committed
368 369 370 371 372

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

373 374 375
static gboolean shortcuts_select_func   (GtkTreeSelection      *selection,
					 GtkTreeModel          *model,
					 GtkTreePath           *path,
376
					 gboolean               path_currently_selected,
377
					 gpointer               data);
378 379
static void shortcuts_activate_iter (GtkFileChooserDefault *impl,
				     GtkTreeIter           *iter);
380 381
static int shortcuts_get_index (GtkFileChooserDefault *impl,
				ShortcutsIndex         where);
382 383
static int shortcut_find_position (GtkFileChooserDefault *impl,
				   const GtkFilePath     *path);
384

385 386
static void bookmarks_check_add_sensitivity (GtkFileChooserDefault *impl);

387 388 389 390 391 392
static gboolean list_select_func   (GtkTreeSelection      *selection,
				    GtkTreeModel          *model,
				    GtkTreePath           *path,
				    gboolean               path_currently_selected,
				    gpointer               data);

393 394 395 396 397 398
static void list_selection_changed     (GtkTreeSelection      *tree_selection,
					GtkFileChooserDefault *impl);
static void list_row_activated         (GtkTreeView           *tree_view,
					GtkTreePath           *path,
					GtkTreeViewColumn     *column,
					GtkFileChooserDefault *impl);
399

Federico Mena Quintero's avatar
Federico Mena Quintero committed
400 401 402 403 404
static void select_func (GtkFileSystemModel *model,
			 GtkTreePath        *path,
			 GtkTreeIter        *iter,
			 gpointer            user_data);

405
static void path_bar_clicked           (GtkPathBar            *path_bar,
406
					GtkFilePath           *file_path,
Federico Mena Quintero's avatar
Federico Mena Quintero committed
407
					gboolean               child_is_hidden,
408
					GtkFileChooserDefault *impl);
409

410 411 412 413 414
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
415 416 417 418 419
static void list_icon_data_func (GtkTreeViewColumn *tree_column,
				 GtkCellRenderer   *cell,
				 GtkTreeModel      *tree_model,
				 GtkTreeIter       *iter,
				 gpointer           data);
420 421 422 423 424
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
425
#if 0
426 427 428 429 430
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
431 432 433 434 435 436
#endif
static void list_mtime_data_func (GtkTreeViewColumn *tree_column,
				  GtkCellRenderer   *cell,
				  GtkTreeModel      *tree_model,
				  GtkTreeIter       *iter,
				  gpointer           data);
437

438 439 440
static const GtkFileInfo *get_list_file_info (GtkFileChooserDefault *impl,
					      GtkTreeIter           *iter);

Federico Mena Quintero's avatar
Federico Mena Quintero committed
441 442
static void load_remove_timer (GtkFileChooserDefault *impl);

Owen Taylor's avatar
Owen Taylor committed
443
static GObjectClass *parent_class;
444

445 446 447 448 449 450 451 452 453 454 455 456 457 458


/* Drag and drop interface declarations */

typedef struct {
  GtkTreeModelFilter parent;

  GtkFileChooserDefault *impl;
} ShortcutsModelFilter;

typedef struct {
  GtkTreeModelFilterClass parent_class;
} ShortcutsModelFilterClass;

459
#define SHORTCUTS_MODEL_FILTER_TYPE (_shortcuts_model_filter_get_type ())
460 461 462 463 464
#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,
465
			 _shortcuts_model_filter,
466 467 468 469 470 471 472 473 474 475
			 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
476
GType
477
_gtk_file_chooser_default_get_type (void)
Owen Taylor's avatar
Owen Taylor committed
478
{
479
  static GType file_chooser_default_type = 0;
Owen Taylor's avatar
Owen Taylor committed
480

481
  if (!file_chooser_default_type)
Owen Taylor's avatar
Owen Taylor committed
482
    {
483
      static const GTypeInfo file_chooser_default_info =
Owen Taylor's avatar
Owen Taylor committed
484
      {
485
	sizeof (GtkFileChooserDefaultClass),
Owen Taylor's avatar
Owen Taylor committed
486 487
	NULL,		/* base_init */
	NULL,		/* base_finalize */
488
	(GClassInitFunc) gtk_file_chooser_default_class_init,
Owen Taylor's avatar
Owen Taylor committed
489 490
	NULL,		/* class_finalize */
	NULL,		/* class_data */
491
	sizeof (GtkFileChooserDefault),
Owen Taylor's avatar
Owen Taylor committed
492
	0,		/* n_preallocs */
493
	(GInstanceInitFunc) gtk_file_chooser_default_init,
Owen Taylor's avatar
Owen Taylor committed
494
      };
495

Owen Taylor's avatar
Owen Taylor committed
496 497
      static const GInterfaceInfo file_chooser_info =
      {
498
	(GInterfaceInitFunc) gtk_file_chooser_default_iface_init, /* interface_init */
Owen Taylor's avatar
Owen Taylor committed
499 500 501 502
	NULL,			                                       /* interface_finalize */
	NULL			                                       /* interface_data */
      };

503 504 505 506 507 508 509
      static const GInterfaceInfo file_chooser_embed_info =
      {
	(GInterfaceInitFunc) gtk_file_chooser_embed_default_iface_init, /* interface_init */
	NULL,			                                       /* interface_finalize */
	NULL			                                       /* interface_data */
      };

510 511
      file_chooser_default_type = g_type_register_static (GTK_TYPE_VBOX, "GtkFileChooserDefault",
							 &file_chooser_default_info, 0);
512

513
      g_type_add_interface_static (file_chooser_default_type,
Owen Taylor's avatar
Owen Taylor committed
514 515
				   GTK_TYPE_FILE_CHOOSER,
				   &file_chooser_info);
516 517 518
      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
519 520
    }

521
  return file_chooser_default_type;
Owen Taylor's avatar
Owen Taylor committed
522 523 524
}

static void
525
gtk_file_chooser_default_class_init (GtkFileChooserDefaultClass *class)
Owen Taylor's avatar
Owen Taylor committed
526 527
{
  GObjectClass *gobject_class = G_OBJECT_CLASS (class);
Owen Taylor's avatar
Owen Taylor committed
528
  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
529
  GtkBindingSet *binding_set;
Owen Taylor's avatar
Owen Taylor committed
530

Owen Taylor's avatar
Owen Taylor committed
531 532
  parent_class = g_type_class_peek_parent (class);

533 534 535 536
  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;
537
  gobject_class->dispose = gtk_file_chooser_default_dispose;
Owen Taylor's avatar
Owen Taylor committed
538

539
  widget_class->show_all = gtk_file_chooser_default_show_all;
540
  widget_class->hierarchy_changed = gtk_file_chooser_default_hierarchy_changed;
541 542
  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
543

544 545 546 547 548 549
  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,
550 551
			     _gtk_marshal_VOID__STRING,
			     G_TYPE_NONE, 1, G_TYPE_STRING);
552 553 554 555 556 557 558 559
  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);
560 561 562 563 564 565 566 567
  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);
568 569 570 571 572 573 574 575 576 577 578 579 580 581
  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",
582
				1, G_TYPE_STRING, "");
583

584 585 586
  gtk_binding_entry_add_signal (binding_set,
				GDK_slash, 0,
				"location-popup",
587
				1, G_TYPE_STRING, "/");
588

589
  gtk_binding_entry_add_signal (binding_set,
590
				GDK_Up, GDK_MOD1_MASK,
591 592
				"up-folder",
				0);
593 594 595 596
  gtk_binding_entry_add_signal (binding_set,
		  		GDK_BackSpace, 0,
				"up-folder",
				0);
597
  gtk_binding_entry_add_signal (binding_set,
598
				GDK_KP_Up, GDK_MOD1_MASK,
599 600 601
				"up-folder",
				0);

602 603 604 605 606 607 608 609 610
  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);

611
  gtk_binding_entry_add_signal (binding_set,
612 613
				GDK_Home, GDK_MOD1_MASK,
				"home-folder",
614 615
				0);
  gtk_binding_entry_add_signal (binding_set,
616 617
				GDK_KP_Home, GDK_MOD1_MASK,
				"home-folder",
618 619
				0);

Owen Taylor's avatar
Owen Taylor committed
620
  _gtk_file_chooser_install_properties (gobject_class);
621

Owen Taylor's avatar
Owen Taylor committed
622 623 624 625 626
  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
627 628 629
}

static void
630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647
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
648 649
}

650 651 652 653 654
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;
655
  iface->should_respond = gtk_file_chooser_default_should_respond;
Federico Mena Quintero's avatar
Federico Mena Quintero committed
656
  iface->initial_focus = gtk_file_chooser_default_initial_focus;
657
}
Owen Taylor's avatar
Owen Taylor committed
658
static void
659
gtk_file_chooser_default_init (GtkFileChooserDefault *impl)
Owen Taylor's avatar
Owen Taylor committed
660
{
661 662
  impl->local_only = TRUE;
  impl->preview_widget_active = TRUE;
663
  impl->use_preview_label = TRUE;
664 665
  impl->select_multiple = FALSE;
  impl->show_hidden = FALSE;
Federico Mena Quintero's avatar
Federico Mena Quintero committed
666
  impl->icon_size = FALLBACK_ICON_SIZE;
Federico Mena Quintero's avatar
Federico Mena Quintero committed
667
  impl->load_state = LOAD_FINISHED;
Federico Mena Quintero's avatar
Federico Mena Quintero committed
668 669
  impl->pending_op = PENDING_OP_NONE;
  impl->pending_select_path = NULL;
670

671
  gtk_widget_set_redraw_on_allocate (GTK_WIDGET (impl), TRUE);
672
  gtk_box_set_spacing (GTK_BOX (impl), 12);
Matthias Clasen's avatar
Matthias Clasen committed
673 674 675 676

  impl->tooltips = gtk_tooltips_new ();
  g_object_ref (impl->tooltips);
  gtk_object_sink (GTK_OBJECT (impl->tooltips));
677 678
}

Federico Mena Quintero's avatar
Federico Mena Quintero committed
679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729
/* 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;
}

730
static void
731
gtk_file_chooser_default_finalize (GObject *object)
732
{
733
  GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (object);
734
  GSList *l;
735

Federico Mena Quintero's avatar
Federico Mena Quintero committed
736 737 738 739 740
  if (impl->shortcuts_filter_model)
    g_object_unref (impl->shortcuts_filter_model);

  shortcuts_free (impl);

741 742
  g_signal_handler_disconnect (impl->file_system, impl->volumes_changed_id);
  impl->volumes_changed_id = 0;
743 744
  g_signal_handler_disconnect (impl->file_system, impl->bookmarks_changed_id);
  impl->bookmarks_changed_id = 0;
745 746
  g_object_unref (impl->file_system);

747 748 749 750 751 752 753 754 755 756 757
  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
758 759 760 761 762 763 764 765 766 767

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

Federico Mena Quintero's avatar
Federico Mena Quintero committed
768 769 770 771 772 773
  if (impl->pending_op == PENDING_OP_SELECT_PATH)
    {
      g_assert (impl->pending_select_path != NULL);
      gtk_file_path_free (impl->pending_select_path);
    }

Federico Mena Quintero's avatar
Federico Mena Quintero committed
774 775
  load_remove_timer (impl);

776 777 778 779 780 781 782
  /* 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);

783 784
  g_free (impl->preview_display_name);

Federico Mena Quintero's avatar
Federico Mena Quintero committed
785 786
  g_free (impl->edited_new_text);

787 788
  g_object_unref (impl->tooltips);

789 790 791
  G_OBJECT_CLASS (parent_class)->finalize (object);
}

792
/* Shows an error dialog set as transient for the specified window */
793
static void
794 795
error_message_with_parent (GtkWindow  *parent,
			   const char *msg)
796 797 798
{
  GtkWidget *dialog;

799
  dialog = gtk_message_dialog_new (parent,
800 801
				   GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
				   GTK_MESSAGE_ERROR,
802
				   GTK_BUTTONS_OK,
803 804
				   "%s",
				   msg);
805 806 807 808
  gtk_dialog_run (GTK_DIALOG (dialog));
  gtk_widget_destroy (dialog);
}

Federico Mena Quintero's avatar
Federico Mena Quintero committed
809 810 811
/* Returns a toplevel GtkWindow, or NULL if none */
static GtkWindow *
get_toplevel (GtkWidget *widget)
Federico Mena Quintero's avatar
Federico Mena Quintero committed
812
{
813 814
  GtkWidget *toplevel;

Federico Mena Quintero's avatar
Federico Mena Quintero committed
815
  toplevel = gtk_widget_get_toplevel (widget);
816
  if (!GTK_WIDGET_TOPLEVEL (toplevel))
Federico Mena Quintero's avatar
Federico Mena Quintero committed
817 818 819 820
    return NULL;
  else
    return GTK_WINDOW (toplevel);
}
821

Federico Mena Quintero's avatar
Federico Mena Quintero committed
822 823 824 825 826 827
/* 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);
828 829
}

830 831 832 833 834 835 836
/* 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)
{
837 838 839 840 841
  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
842 843 844
      char *text = g_strdup_printf (msg,
				    uri,
				    error->message);
845 846 847 848 849
      error_message (impl, text);
      g_free (text);
      g_free (uri);
      g_error_free (error);
    }
850 851
}

852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875
/* 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);
}

876 877 878 879 880 881 882
/* 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)
{
883
  char *uri;
884 885
  char *msg;

886
  uri = gtk_file_system_path_to_uri (impl->file_system, base_path);
887
  msg = g_strdup_printf (_("Could not build file name from '%s' and '%s':\n%s"),
888
			 uri, file_part,
889 890
			 error->message);
  error_message (impl, msg);
891
  g_free (uri);
892 893 894 895
  g_free (msg);
  g_error_free (error);
}

896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914
/* 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
915 916 917 918 919 920 921 922 923 924 925 926
  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);
927 928

  error = NULL;
Federico Mena Quintero's avatar
Federico Mena Quintero committed
929
  result = _gtk_file_chooser_set_current_folder_path (GTK_FILE_CHOOSER (impl), path_copy, &error);
930 931

  if (!result)
Federico Mena Quintero's avatar
Federico Mena Quintero committed
932 933 934
    error_changing_folder_dialog (impl, path_copy, error);

  gtk_file_path_free (path_copy);
935 936 937 938

  return result;
}

939
static void
940
update_preview_widget_visibility (GtkFileChooserDefault *impl)
941
{
942 943 944 945 946 947 948 949 950 951 952 953 954
  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);</