gtkfilechooserdefault.c 183 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
typedef enum {
Federico Mena Quintero's avatar
Federico Mena Quintero committed
88 89 90 91
  LOAD_EMPTY,			/* There is no model */
  LOAD_PRELOAD,			/* Model is loading and a timer is running; model isn't inserted into the tree yet */
  LOAD_LOADING,			/* Timeout expired, model is inserted into the tree, but not fully loaded yet */
  LOAD_FINISHED			/* Model is fully loaded and inserted into the tree */
Federico Mena Quintero's avatar
Federico Mena Quintero committed
92 93 94
} LoadState;

#define MAX_LOADING_TIME 500
95

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

101
struct _GtkFileChooserDefault
Owen Taylor's avatar
Owen Taylor committed
102 103 104
{
  GtkVBox parent_instance;

105 106
  GtkFileChooserAction action;

Owen Taylor's avatar
Owen Taylor committed
107
  GtkFileSystem *file_system;
108

109 110
  /* Save mode widgets */
  GtkWidget *save_widgets;
111

112 113 114 115 116 117 118 119 120 121 122
  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
123
  GtkWidget *browse_files_popup_menu;
124
  GtkWidget *browse_files_popup_menu_add_shortcut_item;
Federico Mena Quintero's avatar
Federico Mena Quintero committed
125
  GtkWidget *browse_files_popup_menu_hidden_files_item;
126 127
  GtkWidget *browse_new_folder_button;
  GtkWidget *browse_path_bar;
128

129
  GtkFileSystemModel *browse_files_model;
130

131
  GtkWidget *filter_combo_hbox;
132
  GtkWidget *filter_combo;
133 134
  GtkWidget *preview_box;
  GtkWidget *preview_label;
135
  GtkWidget *preview_widget;
Federico Mena Quintero's avatar
Federico Mena Quintero committed
136
  GtkWidget *extra_align;
137
  GtkWidget *extra_widget;
138

139
  GtkListStore *shortcuts_model;
140 141
  GtkTreeModel *shortcuts_filter_model;

Owen Taylor's avatar
Owen Taylor committed
142
  GtkTreeModelSort *sort_model;
Owen Taylor's avatar
Owen Taylor committed
143

Federico Mena Quintero's avatar
Federico Mena Quintero committed
144 145 146
  LoadState load_state;
  guint load_timeout_id;

Federico Mena Quintero's avatar
Federico Mena Quintero committed
147
  GSList *pending_select_paths;
Federico Mena Quintero's avatar
Federico Mena Quintero committed
148

Owen Taylor's avatar
Owen Taylor committed
149 150
  GtkFileFilter *current_filter;
  GSList *filters;
151

Matthias Clasen's avatar
Matthias Clasen committed
152 153
  GtkTooltips *tooltips;

154 155
  gboolean has_home;
  gboolean has_desktop;
156 157

  int num_volumes;
158 159
  int num_shortcuts;
  int num_bookmarks;
160

161 162
  gulong volumes_changed_id;
  gulong bookmarks_changed_id;
163

164
  GtkFilePath *current_volume_path;
165
  GtkFilePath *current_folder;
166
  GtkFilePath *preview_path;
167
  char *preview_display_name;
168

169 170 171
  GtkTreeViewColumn *list_name_column;
  GtkCellRenderer *list_name_renderer;

Federico Mena Quintero's avatar
Federico Mena Quintero committed
172 173 174
  GSource *edited_idle;
  char *edited_new_text;

175
  gulong settings_signal_id;
Federico Mena Quintero's avatar
Federico Mena Quintero committed
176 177
  int icon_size;

178 179 180
  gulong toplevel_set_focus_id;
  GtkWidget *toplevel_last_focus_widget;

181
#if 0
182 183
  GdkDragContext *shortcuts_drag_context;
  GSource *shortcuts_drag_outside_idle;
184
#endif
185

186 187
  /* Flags */

188 189
  guint local_only : 1;
  guint preview_widget_active : 1;
190
  guint use_preview_label : 1;
191 192
  guint select_multiple : 1;
  guint show_hidden : 1;
193
  guint list_sort_ascending : 1;
194
  guint changing_folder : 1;
195
  guint shortcuts_current_folder_active : 1;
196 197

#if 0
198
  guint shortcuts_drag_outside : 1;
199
#endif
200 201
};

202 203 204 205
/* Signal IDs */
enum {
  LOCATION_POPUP,
  UP_FOLDER,
206
  DOWN_FOLDER,
207 208 209 210 211 212
  HOME_FOLDER,
  LAST_SIGNAL
};

static guint signals[LAST_SIGNAL] = { 0 };

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

224 225 226 227 228 229 230 231
/* Column numbers for the file list */
enum {
  FILE_LIST_COL_NAME,
  FILE_LIST_COL_SIZE,
  FILE_LIST_COL_MTIME,
  FILE_LIST_COL_NUM_COLUMNS
};

232 233
/* Identifiers for target types */
enum {
234
  GTK_TREE_MODEL_ROW,
235 236 237
  TEXT_URI_LIST
};

238 239 240 241 242 243 244 245 246 247 248
/* 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 },
249 250 251
  { "text/uri-list", 0, TEXT_URI_LIST }
};

252 253 254 255 256 257 258 259 260 261
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]));
262

263 264 265 266 267 268
/* Interesting places in the shortcuts bar */
typedef enum {
  SHORTCUTS_HOME,
  SHORTCUTS_DESKTOP,
  SHORTCUTS_VOLUMES,
  SHORTCUTS_SHORTCUTS,
269 270 271 272
  SHORTCUTS_BOOKMARKS_SEPARATOR,
  SHORTCUTS_BOOKMARKS,
  SHORTCUTS_CURRENT_FOLDER_SEPARATOR,
  SHORTCUTS_CURRENT_FOLDER
273 274
} ShortcutsIndex;

Federico Mena Quintero's avatar
Federico Mena Quintero committed
275
/* Icon size for if we can't get it from the theme */
Federico Mena Quintero's avatar
Federico Mena Quintero committed
276
#define FALLBACK_ICON_SIZE 16
Federico Mena Quintero's avatar
Federico Mena Quintero committed
277

278 279 280
#define PREVIEW_HBOX_SPACING 12
#define NUM_LINES 40
#define NUM_CHARS 60
281

282 283 284 285
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);
286 287 288 289 290 291 292 293 294 295 296 297 298

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);
299
static void     gtk_file_chooser_default_dispose      (GObject               *object);
300
static void     gtk_file_chooser_default_show_all       (GtkWidget             *widget);
Federico Mena Quintero's avatar
Federico Mena Quintero committed
301
static void     gtk_file_chooser_default_map            (GtkWidget             *widget);
302 303
static void     gtk_file_chooser_default_hierarchy_changed (GtkWidget          *widget,
							    GtkWidget          *previous_toplevel);
304 305 306 307
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);
308

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

338 339 340 341 342 343
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);
344
static gboolean       gtk_file_chooser_default_should_respond         (GtkFileChooserEmbed *chooser_embed);
Federico Mena Quintero's avatar
Federico Mena Quintero committed
345
static void           gtk_file_chooser_default_initial_focus          (GtkFileChooserEmbed *chooser_embed);
346

347 348
static void location_popup_handler (GtkFileChooserDefault *impl,
				    const gchar           *path);
349
static void up_folder_handler      (GtkFileChooserDefault *impl);
350
static void down_folder_handler    (GtkFileChooserDefault *impl);
351
static void home_folder_handler    (GtkFileChooserDefault *impl);
352
static void update_appearance      (GtkFileChooserDefault *impl);
353

354 355 356 357
static void set_current_filter   (GtkFileChooserDefault *impl,
				  GtkFileFilter         *filter);
static void check_preview_change (GtkFileChooserDefault *impl);

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

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

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

383 384
static void bookmarks_check_add_sensitivity (GtkFileChooserDefault *impl);

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

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

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

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

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

436 437 438
static const GtkFileInfo *get_list_file_info (GtkFileChooserDefault *impl,
					      GtkTreeIter           *iter);

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

Owen Taylor's avatar
Owen Taylor committed
441
static GObjectClass *parent_class;
442

443 444 445 446 447 448 449 450 451 452 453 454 455 456


/* Drag and drop interface declarations */

typedef struct {
  GtkTreeModelFilter parent;

  GtkFileChooserDefault *impl;
} ShortcutsModelFilter;

typedef struct {
  GtkTreeModelFilterClass parent_class;
} ShortcutsModelFilterClass;

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

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

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

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

508 509
      file_chooser_default_type = g_type_register_static (GTK_TYPE_VBOX, "GtkFileChooserDefault",
							 &file_chooser_default_info, 0);
510

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

519
  return file_chooser_default_type;
Owen Taylor's avatar
Owen Taylor committed
520 521 522
}

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

Owen Taylor's avatar
Owen Taylor committed
529 530
  parent_class = g_type_class_peek_parent (class);

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

537
  widget_class->show_all = gtk_file_chooser_default_show_all;
Federico Mena Quintero's avatar
Federico Mena Quintero committed
538
  widget_class->map = gtk_file_chooser_default_map;
539
  widget_class->hierarchy_changed = gtk_file_chooser_default_hierarchy_changed;
540 541
  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
542

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

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

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

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

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

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

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

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

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

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

  impl->tooltips = gtk_tooltips_new ();
  g_object_ref (impl->tooltips);
  gtk_object_sink (GTK_OBJECT (impl->tooltips));
675 676
}

Federico Mena Quintero's avatar
Federico Mena Quintero committed
677 678 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
/* 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;
}

Federico Mena Quintero's avatar
Federico Mena Quintero committed
728 729 730 731 732 733 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 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782
static void
pending_select_paths_free (GtkFileChooserDefault *impl)
{
  GSList *l;

  for (l = impl->pending_select_paths; l; l = l->next)
    {
      GtkFilePath *path;

      path = l->data;
      gtk_file_path_free (path);
    }

  g_slist_free (impl->pending_select_paths);
  impl->pending_select_paths = NULL;
}

static void
pending_select_paths_add (GtkFileChooserDefault *impl,
			  const GtkFilePath     *path)
{
  impl->pending_select_paths = g_slist_prepend (impl->pending_select_paths, gtk_file_path_copy (path));
}

/* Used from gtk_tree_selection_selected_foreach() */
static void
store_selection_foreach (GtkTreeModel *model,
			 GtkTreePath  *path,
			 GtkTreeIter  *iter,
			 gpointer      data)
{
  GtkFileChooserDefault *impl;
  GtkTreeIter child_iter;
  const GtkFilePath *file_path;

  impl = GTK_FILE_CHOOSER_DEFAULT (data);

  gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model, &child_iter, iter);

  file_path = _gtk_file_system_model_get_path (impl->browse_files_model, &child_iter);
  pending_select_paths_add (impl, file_path);
}

/* Stores the current selection in the list of paths to select; this is used to
 * preserve the selection when reloading the current folder.
 */
static void
pending_select_paths_store_selection (GtkFileChooserDefault *impl)
{
  GtkTreeSelection *selection;

  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
  gtk_tree_selection_selected_foreach (selection, store_selection_foreach, impl);
}

783
static void
784
gtk_file_chooser_default_finalize (GObject *object)
785
{
786
  GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (object);
787
  GSList *l;
788

Federico Mena Quintero's avatar
Federico Mena Quintero committed
789 790 791 792 793
  if (impl->shortcuts_filter_model)
    g_object_unref (impl->shortcuts_filter_model);

  shortcuts_free (impl);

794 795
  g_signal_handler_disconnect (impl->file_system, impl->volumes_changed_id);
  impl->volumes_changed_id = 0;
796 797
  g_signal_handler_disconnect (impl->file_system, impl->bookmarks_changed_id);
  impl->bookmarks_changed_id = 0;
798 799
  g_object_unref (impl->file_system);

800 801 802 803 804 805 806 807 808 809 810
  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
811 812 813 814 815 816 817 818 819 820

  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
821
  pending_select_paths_free (impl);
Federico Mena Quintero's avatar
Federico Mena Quintero committed
822

Federico Mena Quintero's avatar
Federico Mena Quintero committed
823 824
  load_remove_timer (impl);

825 826 827 828 829 830 831
  /* 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);

832 833
  g_free (impl->preview_display_name);

Federico Mena Quintero's avatar
Federico Mena Quintero committed
834 835
  g_free (impl->edited_new_text);

836 837
  g_object_unref (impl->tooltips);

838 839 840
  G_OBJECT_CLASS (parent_class)->finalize (object);
}

841
/* Shows an error dialog set as transient for the specified window */
842
static void
843
error_message_with_parent (GtkWindow  *parent,
844 845
			   const char *msg,
			   const char *detail)
846 847 848
{
  GtkWidget *dialog;

849
  dialog = gtk_message_dialog_new (parent,
850 851
				   GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
				   GTK_MESSAGE_ERROR,
852
				   GTK_BUTTONS_OK,
853 854
				   "%s",
				   msg);
855 856
  gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
					    "%s", detail);
857 858 859 860
  gtk_dialog_run (GTK_DIALOG (dialog));
  gtk_widget_destroy (dialog);
}

Federico Mena Quintero's avatar
Federico Mena Quintero committed
861 862 863
/* Returns a toplevel GtkWindow, or NULL if none */
static GtkWindow *
get_toplevel (GtkWidget *widget)
Federico Mena Quintero's avatar
Federico Mena Quintero committed
864
{
865 866
  GtkWidget *toplevel;

Federico Mena Quintero's avatar
Federico Mena Quintero committed
867
  toplevel = gtk_widget_get_toplevel (widget);
868
  if (!GTK_WIDGET_TOPLEVEL (toplevel))
Federico Mena Quintero's avatar
Federico Mena Quintero committed
869 870 871 872
    return NULL;
  else
    return GTK_WINDOW (toplevel);
}
873

Federico Mena Quintero's avatar
Federico Mena Quintero committed
874 875 876
/* Shows an error dialog for the file chooser */
static void
error_message (GtkFileChooserDefault *impl,
877 878
	       const char            *msg,
	       const char            *detail)
Federico Mena Quintero's avatar
Federico Mena Quintero committed
879
{
880
  error_message_with_parent (get_toplevel (GTK_WIDGET (impl)), msg, detail);
881 882
}

883 884 885 886 887 888 889
/* 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)
{
890 891
  if (error)
    {
892 893 894 895 896 897 898
      char *uri = NULL;
      char *text;

      if (path)
	uri = gtk_file_system_path_to_uri (impl->file_system, path);
      text = g_strdup_printf (msg, uri);
      error_message (impl, text, error->message);
899 900 901 902
      g_free (text);
      g_free (uri);
      g_error_free (error);
    }
903 904
}

905 906 907 908 909 910 911 912 913
/* 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,
914
		_("Could not retrieve information about the file"),
915 916 917 918 919
		path, error);
}

/* Shows an error dialog about not being able to add a bookmark */
static void
920 921 922
error_adding_bookmark_dialog (GtkFileChooserDefault *impl,
			      const GtkFilePath     *path,
			      GError                *error)
923 924
{
  error_dialog (impl,
925
		_("Could not add a bookmark"),
926 927 928
		path, error);
}

929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951
/* Shows an error dialog about not being able to remove a bookmark */
static void
error_removing_bookmark_dialog (GtkFileChooserDefault *impl,
				const GtkFilePath     *path,
				GError                *error)
{
  error_dialog (impl,
		_("Could not remove bookmark"),
		path, error);
}

/* Shows an error dialog about not being able to create a folder */
static void
error_creating_folder_dialog (GtkFileChooserDefault *impl,
			      const GtkFilePath     *path,
			      GError                *error)
{
  error_dialog (impl, 
		_("The folder could not be created"), 
		path, error);
}

/* Shows an error dialog about not being able to create a filename */
952 953
static void
error_building_filename_dialog (GtkFileChooserDefault *impl,
954
<