gtkfilechooserdefault.c 177 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);
Federico Mena Quintero's avatar
Federico Mena Quintero committed
306
static void     gtk_file_chooser_default_map            (GtkWidget             *widget);
307 308
static void     gtk_file_chooser_default_hierarchy_changed (GtkWidget          *widget,
							    GtkWidget          *previous_toplevel);
309 310 311 312
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);
313

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

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

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

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

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

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

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

386 387
static void bookmarks_check_add_sensitivity (GtkFileChooserDefault *impl);

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

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

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

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

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

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

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

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

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

/* Drag and drop interface declarations */

typedef struct {
  GtkTreeModelFilter parent;

  GtkFileChooserDefault *impl;
} ShortcutsModelFilter;

typedef struct {
  GtkTreeModelFilterClass parent_class;
} ShortcutsModelFilterClass;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Owen Taylor's avatar
Owen Taylor committed
622
  _gtk_file_chooser_install_properties (gobject_class);
623

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

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

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

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

  impl->tooltips = gtk_tooltips_new ();
  g_object_ref (impl->tooltips);
  gtk_object_sink (GTK_OBJECT (impl->tooltips));
679 680
}

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

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

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

  shortcuts_free (impl);

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

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

  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
770 771 772 773 774 775
  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
776 777
  load_remove_timer (impl);

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

785 786
  g_free (impl->preview_display_name);

Federico Mena Quintero's avatar
Federico Mena Quintero committed
787 788
  g_free (impl->edited_new_text);

789 790
  g_object_unref (impl->tooltips);

791 792 793
  G_OBJECT_CLASS (parent_class)->finalize (object);
}

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

802
  dialog = gtk_message_dialog_new (parent,
803 804
				   GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
				   GTK_MESSAGE_ERROR,
805
				   GTK_BUTTONS_OK,
806 807
				   "%s",
				   msg);
808 809
  gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
					    "%s", detail);
810 811 812 813
  gtk_dialog_run (GTK_DIALOG (dialog));
  gtk_widget_destroy (dialog);
}

Federico Mena Quintero's avatar
Federico Mena Quintero committed
814 815 816
/* Returns a toplevel GtkWindow, or NULL if none */
static GtkWindow *
get_toplevel (GtkWidget *widget)
Federico Mena Quintero's avatar
Federico Mena Quintero committed
817
{
818 819
  GtkWidget *toplevel;

Federico Mena Quintero's avatar
Federico Mena Quintero committed
820
  toplevel = gtk_widget_get_toplevel (widget);
821
  if (!GTK_WIDGET_TOPLEVEL (toplevel))
Federico Mena Quintero's avatar
Federico Mena Quintero committed
822 823 824 825
    return NULL;
  else
    return GTK_WINDOW (toplevel);
}
826

Federico Mena Quintero's avatar
Federico Mena Quintero committed
827 828 829
/* Shows an error dialog for the file chooser */
static void
error_message (GtkFileChooserDefault *impl,
830 831
	       const char            *msg,
	       const char            *detail)
Federico Mena Quintero's avatar
Federico Mena Quintero committed
832
{
833
  error_message_with_parent (get_toplevel (GTK_WIDGET (impl)), msg, detail);
834 835
}

836 837 838 839 840 841 842
/* 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)
{
843 844
  if (error)
    {
845 846 847 848 849 850 851
      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);
852 853 854 855
      g_free (text);
      g_free (uri);
      g_error_free (error);
    }
856 857
}

858 859 860 861 862 863 864 865 866
/* 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,
867
		_("Could not retrieve information about the file"),
868 869 870 871 872
		path, error);
}

/* Shows an error dialog about not being able to add a bookmark */
static void
873 874 875
error_adding_bookmark_dialog (GtkFileChooserDefault *impl,
			      const GtkFilePath     *path,
			      GError                *error)
876 877
{
  error_dialog (impl,
878
		_("Could not add a bookmark"),
879 880 881
		path, error);
}

882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904
/* 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 */
905 906
static void
error_building_filename_dialog (GtkFileChooserDefault *impl,
907
				const GtkFilePath     *folder_part,
908 909 910
				const char            *file_part,
				GError                *error)
{
911 912
  error_dialog (impl, _("Invalid file name"), 
		NULL, error);
913 914
}

915 916 917 918 919 920
/* Shows an error dialog when we cannot switch to a folder */
static void
error_changing_folder_dialog (GtkFileChooserDefault *impl,
			      const GtkFilePath     *path,
			      GError                *error)
{
921 922
  error_dialog (impl, _("The folder contents could not be displayed"),
		path, error);
923 924 925 926 927 928 929 930 931
}

/* 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
932 933 934 935 936 937 938 939 940 941 942 943
  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);
944 945

  error = NULL;
Federico Mena Quintero's avatar
Federico Mena Quintero committed
946
  result = _gtk_file_chooser_set_current_folder_path (GTK_FILE_CHOOSER (impl), path_copy, &error);
947 948

  if (!result)
Federico Mena Quintero's avatar
Federico Mena Quintero committed
949 950 951
    error_changing_folder_dialog (impl, path_copy, error);

  gtk_file_path_free (path_copy);
952 953 954 955

  return result;
}