gtkfilechooserdefault.c 340 KB
Newer Older
1
/* -*- Mode: C; c-file-style: "gnu"; tab-width: 8 -*- */
Cody Russell's avatar
Cody Russell committed
2
/* GTK - The GIMP Toolkit
3
 * gtkfilechooserdefault.c: Default implementation of GtkFileChooser
Owen Taylor's avatar
Owen Taylor committed
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
 * 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.
 */

22
#include "config.h"
23

24
#include "gdk/gdkkeysyms.h"
25
#include "gtkalignment.h"
26
#include "gtkbindings.h"
27
#include "gtkcelllayout.h"
28
#include "gtkcellrendererpixbuf.h"
29
#include "gtkcellrenderertext.h"
Federico Mena Quintero's avatar
Federico Mena Quintero committed
30
#include "gtkcheckmenuitem.h"
31
#include "gtkclipboard.h"
32
#include "gtkcombobox.h"
33
#include "gtkentry.h"
34
#include "gtkexpander.h"
35
#include "gtkfilechooserprivate.h"
36
#include "gtkfilechooserdefault.h"
37
#include "gtkfilechooserdialog.h"
38
#include "gtkfilechooserembed.h"
39
#include "gtkfilechooserentry.h"
40
#include "gtkfilechoosersettings.h"
Owen Taylor's avatar
Owen Taylor committed
41 42
#include "gtkfilechooserutils.h"
#include "gtkfilechooser.h"
43
#include "gtkfilesystem.h"
Owen Taylor's avatar
Owen Taylor committed
44
#include "gtkfilesystemmodel.h"
45 46 47
#include "gtkframe.h"
#include "gtkhbox.h"
#include "gtkhpaned.h"
Federico Mena Quintero's avatar
Federico Mena Quintero committed
48
#include "gtkiconfactory.h"
49 50
#include "gtkicontheme.h"
#include "gtkimage.h"
51
#include "gtkimagemenuitem.h"
52
#include "gtklabel.h"
53
#include "gtkmarshalers.h"
54
#include "gtkmessagedialog.h"
55
#include "gtkmountoperation.h"
56
#include "gtkpathbar.h"
57
#include "gtkprivate.h"
58
#include "gtkradiobutton.h"
59 60
#include "gtkrecentfilter.h"
#include "gtkrecentmanager.h"
61
#include "gtkscrolledwindow.h"
62
#include "gtkseparatormenuitem.h"
63
#include "gtksizegroup.h"
64 65
#include "gtkstock.h"
#include "gtktable.h"
66
#include "gtktooltip.h"
67 68
#include "gtktreednd.h"
#include "gtktreeprivate.h"
69 70
#include "gtktreeselection.h"
#include "gtkvbox.h"
71
#include "gtkintl.h"
Owen Taylor's avatar
Owen Taylor committed
72

73 74
#include "gtkalias.h"

75
#include <errno.h>
76
#include <string.h>
Federico Mena Quintero's avatar
Federico Mena Quintero committed
77
#include <time.h>
78 79
#include <sys/stat.h>
#include <sys/types.h>
80
#include <locale.h>
81

82
#ifdef HAVE_UNISTD_H
83
#include <unistd.h>
84 85 86 87 88
#endif
#ifdef G_OS_WIN32
#include <io.h>
#endif

Matthias Clasen's avatar
Matthias Clasen committed
89
/* Profiling stuff */
Matthias Clasen's avatar
Matthias Clasen committed
90
#undef PROFILE_FILE_CHOOSER
Matthias Clasen's avatar
Matthias Clasen committed
91 92 93
#ifdef PROFILE_FILE_CHOOSER


94 95 96
#ifndef F_OK 
#define F_OK 0
#endif
97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117

#define PROFILE_INDENT 4
static int profile_indent;

static void
profile_add_indent (int indent)
{
  profile_indent += indent;
  if (profile_indent < 0)
    g_error ("You screwed up your indentation");
}

void
_gtk_file_chooser_profile_log (const char *func, int indent, const char *msg1, const char *msg2)
{
  char *str;

  if (indent < 0)
    profile_add_indent (indent);

  if (profile_indent == 0)
118
    str = g_strdup_printf ("MARK: %s %s %s", func ? func : "", msg1 ? msg1 : "", msg2 ? msg2 : "");
119
  else
120
    str = g_strdup_printf ("MARK: %*c %s %s %s", profile_indent - 1, ' ', func ? func : "", msg1 ? msg1 : "", msg2 ? msg2 : "");
121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137

  access (str, F_OK);
  g_free (str);

  if (indent > 0)
    profile_add_indent (indent);
}

#define profile_start(x, y) _gtk_file_chooser_profile_log (G_STRFUNC, PROFILE_INDENT, x, y)
#define profile_end(x, y) _gtk_file_chooser_profile_log (G_STRFUNC, -PROFILE_INDENT, x, y)
#define profile_msg(x, y) _gtk_file_chooser_profile_log (NULL, 0, x, y)
#else
#define profile_start(x, y)
#define profile_end(x, y)
#define profile_msg(x, y)
#endif

138

139

140
typedef struct _GtkFileChooserDefaultClass GtkFileChooserDefaultClass;
Owen Taylor's avatar
Owen Taylor committed
141

142 143 144
#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
145

Federico Mena Quintero's avatar
Federico Mena Quintero committed
146
#define MAX_LOADING_TIME 500
147

148 149
#define DEFAULT_NEW_FOLDER_NAME _("Type name of new folder")

150
struct _GtkFileChooserDefaultClass
Owen Taylor's avatar
Owen Taylor committed
151 152 153 154
{
  GtkVBoxClass parent_class;
};

155 156 157
/* Signal IDs */
enum {
  LOCATION_POPUP,
158
  LOCATION_POPUP_ON_PASTE,
159
  UP_FOLDER,
160
  DOWN_FOLDER,
161
  HOME_FOLDER,
162
  DESKTOP_FOLDER,
163
  QUICK_BOOKMARK,
164
  LOCATION_TOGGLE_POPUP,
165
  SHOW_HIDDEN,
166
  SEARCH_SHORTCUT,
167
  RECENT_SHORTCUT,
168

169 170 171 172 173
  LAST_SIGNAL
};

static guint signals[LAST_SIGNAL] = { 0 };

174
/* Column numbers for the shortcuts tree.  Keep these in sync with shortcuts_model_create() */
175 176 177
enum {
  SHORTCUTS_COL_PIXBUF,
  SHORTCUTS_COL_NAME,
Federico Mena Quintero's avatar
Federico Mena Quintero committed
178
  SHORTCUTS_COL_DATA,
179
  SHORTCUTS_COL_TYPE,
180
  SHORTCUTS_COL_REMOVABLE,
181
  SHORTCUTS_COL_PIXBUF_VISIBLE,
182
  SHORTCUTS_COL_CANCELLABLE,
183
  SHORTCUTS_COL_NUM_COLUMNS
Owen Taylor's avatar
Owen Taylor committed
184 185
};

186
typedef enum {
187
  SHORTCUT_TYPE_FILE,
188 189
  SHORTCUT_TYPE_VOLUME,
  SHORTCUT_TYPE_SEPARATOR,
190 191
  SHORTCUT_TYPE_SEARCH,
  SHORTCUT_TYPE_RECENT
192 193 194 195 196 197
} ShortcutType;

/* Column numbers for the search model.  
 * Keep this in sync with search_setup_model() 
 */
enum {
198
  SEARCH_MODEL_COL_FILE,
199 200
  SEARCH_MODEL_COL_DISPLAY_NAME,
  SEARCH_MODEL_COL_COLLATION_KEY,
201 202
  SEARCH_MODEL_COL_MTIME,
  SEARCH_MODEL_COL_SIZE,
203
  SEARCH_MODEL_COL_CANCELLABLE,
204 205 206 207
  SEARCH_MODEL_COL_PIXBUF,
  SEARCH_MODEL_COL_MIME_TYPE,
  SEARCH_MODEL_COL_IS_FOLDER,
  SEARCH_MODEL_COL_NUM_COLUMNS
208 209
};

210
enum {
211
  RECENT_MODEL_COL_FILE,
212 213 214
  RECENT_MODEL_COL_DISPLAY_NAME,
  RECENT_MODEL_COL_INFO,
  RECENT_MODEL_COL_IS_FOLDER,
215
  RECENT_MODEL_COL_CANCELLABLE,
216 217 218
  RECENT_MODEL_COL_NUM_COLUMNS
};

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

224 225 226 227 228 229 230 231
static gboolean
search_is_possible (GtkFileChooserDefault *impl)
{
  if (impl->search_engine == NULL)
    impl->search_engine = _gtk_search_engine_new ();
  
  return impl->search_engine != NULL;
}
232

233 234
/* Interesting places in the shortcuts bar */
typedef enum {
235
  SHORTCUTS_SEARCH,
236 237
  SHORTCUTS_RECENT,
  SHORTCUTS_RECENT_SEPARATOR,
238 239 240 241
  SHORTCUTS_HOME,
  SHORTCUTS_DESKTOP,
  SHORTCUTS_VOLUMES,
  SHORTCUTS_SHORTCUTS,
242 243 244 245
  SHORTCUTS_BOOKMARKS_SEPARATOR,
  SHORTCUTS_BOOKMARKS,
  SHORTCUTS_CURRENT_FOLDER_SEPARATOR,
  SHORTCUTS_CURRENT_FOLDER
246 247
} ShortcutsIndex;

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

251
#define PREVIEW_HBOX_SPACING 12
252
#define NUM_LINES 45
253
#define NUM_CHARS 60
254

255 256
static void gtk_file_chooser_default_iface_init       (GtkFileChooserIface        *iface);
static void gtk_file_chooser_embed_default_iface_init (GtkFileChooserEmbedIface   *iface);
257 258 259 260 261 262 263 264 265 266 267 268 269

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);
270
static void     gtk_file_chooser_default_dispose      (GObject               *object);
271
static void     gtk_file_chooser_default_show_all       (GtkWidget             *widget);
272
static void     gtk_file_chooser_default_realize        (GtkWidget             *widget);
Federico Mena Quintero's avatar
Federico Mena Quintero committed
273
static void     gtk_file_chooser_default_map            (GtkWidget             *widget);
274
static void     gtk_file_chooser_default_unmap          (GtkWidget             *widget);
275 276
static void     gtk_file_chooser_default_hierarchy_changed (GtkWidget          *widget,
							    GtkWidget          *previous_toplevel);
277 278 279 280
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);
281 282
static void     gtk_file_chooser_default_size_allocate  (GtkWidget             *widget,
							 GtkAllocation         *allocation);
283

284
static gboolean       gtk_file_chooser_default_set_current_folder 	   (GtkFileChooser    *chooser,
285
									    GFile             *folder,
286
									    GError           **error);
287
static gboolean       gtk_file_chooser_default_update_current_folder 	   (GtkFileChooser    *chooser,
288
									    GFile             *folder,
289
									    gboolean           keep_trail,
290
									    gboolean           clear_entry,
291
									    GError           **error);
292
static GFile *        gtk_file_chooser_default_get_current_folder 	   (GtkFileChooser    *chooser);
293
static void           gtk_file_chooser_default_set_current_name   	   (GtkFileChooser    *chooser,
294
									    const gchar       *name);
295 296
static gboolean       gtk_file_chooser_default_select_file        	   (GtkFileChooser    *chooser,
									    GFile             *file,
297
									    GError           **error);
298 299
static void           gtk_file_chooser_default_unselect_file      	   (GtkFileChooser    *chooser,
									    GFile             *file);
300 301
static void           gtk_file_chooser_default_select_all         	   (GtkFileChooser    *chooser);
static void           gtk_file_chooser_default_unselect_all       	   (GtkFileChooser    *chooser);
302 303
static GSList *       gtk_file_chooser_default_get_files          	   (GtkFileChooser    *chooser);
static GFile *        gtk_file_chooser_default_get_preview_file   	   (GtkFileChooser    *chooser);
304 305
static GtkFileSystem *gtk_file_chooser_default_get_file_system    	   (GtkFileChooser    *chooser);
static void           gtk_file_chooser_default_add_filter         	   (GtkFileChooser    *chooser,
306
									    GtkFileFilter     *filter);
307
static void           gtk_file_chooser_default_remove_filter      	   (GtkFileChooser    *chooser,
308
									    GtkFileFilter     *filter);
309 310
static GSList *       gtk_file_chooser_default_list_filters       	   (GtkFileChooser    *chooser);
static gboolean       gtk_file_chooser_default_add_shortcut_folder    (GtkFileChooser    *chooser,
311
								       GFile             *file,
312 313
								       GError           **error);
static gboolean       gtk_file_chooser_default_remove_shortcut_folder (GtkFileChooser    *chooser,
314
								       GFile             *file,
315 316
								       GError           **error);
static GSList *       gtk_file_chooser_default_list_shortcut_folders  (GtkFileChooser    *chooser);
317

318 319 320
static void           gtk_file_chooser_default_get_default_size       (GtkFileChooserEmbed *chooser_embed,
								       gint                *default_width,
								       gint                *default_height);
321
static gboolean       gtk_file_chooser_default_should_respond         (GtkFileChooserEmbed *chooser_embed);
Federico Mena Quintero's avatar
Federico Mena Quintero committed
322
static void           gtk_file_chooser_default_initial_focus          (GtkFileChooserEmbed *chooser_embed);
323

324 325
static void location_popup_handler  (GtkFileChooserDefault *impl,
				     const gchar           *path);
326
static void location_popup_on_paste_handler (GtkFileChooserDefault *impl);
327
static void location_toggle_popup_handler   (GtkFileChooserDefault *impl);
328 329 330 331 332 333 334 335
static void up_folder_handler       (GtkFileChooserDefault *impl);
static void down_folder_handler     (GtkFileChooserDefault *impl);
static void home_folder_handler     (GtkFileChooserDefault *impl);
static void desktop_folder_handler  (GtkFileChooserDefault *impl);
static void quick_bookmark_handler  (GtkFileChooserDefault *impl,
				     gint                   bookmark_index);
static void show_hidden_handler     (GtkFileChooserDefault *impl);
static void search_shortcut_handler (GtkFileChooserDefault *impl);
336
static void recent_shortcut_handler (GtkFileChooserDefault *impl);
337
static void update_appearance       (GtkFileChooserDefault *impl);
338

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

343
static void filter_combo_changed       (GtkComboBox           *combo_box,
344
					GtkFileChooserDefault *impl);
Federico Mena Quintero's avatar
Federico Mena Quintero committed
345 346 347 348 349

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

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

364 365
static void bookmarks_check_add_sensitivity (GtkFileChooserDefault *impl);

366 367 368 369 370 371
static gboolean list_select_func   (GtkTreeSelection      *selection,
				    GtkTreeModel          *model,
				    GtkTreePath           *path,
				    gboolean               path_currently_selected,
				    gpointer               data);

372 373 374 375 376 377
static void list_selection_changed     (GtkTreeSelection      *tree_selection,
					GtkFileChooserDefault *impl);
static void list_row_activated         (GtkTreeView           *tree_view,
					GtkTreePath           *path,
					GtkTreeViewColumn     *column,
					GtkFileChooserDefault *impl);
378

Federico Mena Quintero's avatar
Federico Mena Quintero committed
379 380 381 382 383
static void select_func (GtkFileSystemModel *model,
			 GtkTreePath        *path,
			 GtkTreeIter        *iter,
			 gpointer            user_data);

384
static void path_bar_clicked (GtkPathBar            *path_bar,
385 386
			      GFile                 *file,
			      GFile                 *child,
387 388
                              gboolean               child_is_hidden,
                              GtkFileChooserDefault *impl);
389

390 391 392 393
static void add_bookmark_button_clicked_cb    (GtkButton             *button,
					       GtkFileChooserDefault *impl);
static void remove_bookmark_button_clicked_cb (GtkButton             *button,
					       GtkFileChooserDefault *impl);
394 395
static void save_folder_combo_changed_cb      (GtkComboBox           *combo,
					       GtkFileChooserDefault *impl);
396

Owen Taylor's avatar
Owen Taylor committed
397 398 399 400 401
static void list_icon_data_func (GtkTreeViewColumn *tree_column,
				 GtkCellRenderer   *cell,
				 GtkTreeModel      *tree_model,
				 GtkTreeIter       *iter,
				 gpointer           data);
402 403 404 405 406 407 408 409 410 411
static void list_name_data_func (GtkTreeViewColumn *tree_column,
				 GtkCellRenderer   *cell,
				 GtkTreeModel      *tree_model,
				 GtkTreeIter       *iter,
				 gpointer           data);
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
412 413 414 415 416
static void list_mtime_data_func (GtkTreeViewColumn *tree_column,
				  GtkCellRenderer   *cell,
				  GtkTreeModel      *tree_model,
				  GtkTreeIter       *iter,
				  gpointer           data);
417

418 419
static GFileInfo       *get_list_file_info (GtkFileChooserDefault *impl,
					    GtkTreeIter           *iter);
420

Federico Mena Quintero's avatar
Federico Mena Quintero committed
421
static void load_remove_timer (GtkFileChooserDefault *impl);
422
static void browse_files_center_selected_row (GtkFileChooserDefault *impl);
Federico Mena Quintero's avatar
Federico Mena Quintero committed
423

424 425 426 427
static void location_button_toggled_cb (GtkToggleButton *toggle,
					GtkFileChooserDefault *impl);
static void location_switch_to_path_bar (GtkFileChooserDefault *impl);

428 429
static void     search_stop_searching        (GtkFileChooserDefault *impl,
                                              gboolean               remove_query);
430 431
static void     search_clear_model_row       (GtkTreeModel          *model,
                                              GtkTreeIter           *iter);
432 433 434 435
static void     search_clear_model           (GtkFileChooserDefault *impl, 
					      gboolean               remove_from_treeview);
static gboolean search_should_respond        (GtkFileChooserDefault *impl);
static void     search_switch_to_browse_mode (GtkFileChooserDefault *impl);
436
static GSList  *search_get_selected_files    (GtkFileChooserDefault *impl);
437 438
static void     search_entry_activate_cb     (GtkEntry              *entry, 
					      gpointer               data);
439
static void     settings_load                (GtkFileChooserDefault *impl);
440 441 442
static void     search_get_valid_child_iter  (GtkFileChooserDefault *impl,
                                              GtkTreeIter           *child_iter,
                                              GtkTreeIter           *iter);
443

444 445 446 447 448
static void     recent_stop_loading          (GtkFileChooserDefault *impl);
static void     recent_clear_model           (GtkFileChooserDefault *impl,
                                              gboolean               remove_from_treeview);
static gboolean recent_should_respond        (GtkFileChooserDefault *impl);
static void     recent_switch_to_browse_mode (GtkFileChooserDefault *impl);
449
static GSList * recent_get_selected_files    (GtkFileChooserDefault *impl);
450 451 452
static void     recent_get_valid_child_iter  (GtkFileChooserDefault *impl,
                                              GtkTreeIter           *child_iter,
                                              GtkTreeIter           *iter);
453
static void     set_file_system_backend      (GtkFileChooserDefault *impl);
454
static void     unset_file_system_backend    (GtkFileChooserDefault *impl);
455

456

457

458 459 460 461 462 463 464 465


/* Drag and drop interface declarations */

typedef struct {
  GtkTreeModelFilter parent;

  GtkFileChooserDefault *impl;
466
} ShortcutsPaneModelFilter;
467 468 469

typedef struct {
  GtkTreeModelFilterClass parent_class;
470
} ShortcutsPaneModelFilterClass;
471

472 473
#define SHORTCUTS_PANE_MODEL_FILTER_TYPE (_shortcuts_pane_model_filter_get_type ())
#define SHORTCUTS_PANE_MODEL_FILTER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SHORTCUTS_PANE_MODEL_FILTER_TYPE, ShortcutsPaneModelFilter))
474

475
static void shortcuts_pane_model_filter_drag_source_iface_init (GtkTreeDragSourceIface *iface);
476

477 478
G_DEFINE_TYPE_WITH_CODE (ShortcutsPaneModelFilter,
			 _shortcuts_pane_model_filter,
479 480
			 GTK_TYPE_TREE_MODEL_FILTER,
			 G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_DRAG_SOURCE,
481
						shortcuts_pane_model_filter_drag_source_iface_init))
482

483 484 485
static GtkTreeModel *shortcuts_pane_model_filter_new (GtkFileChooserDefault *impl,
						      GtkTreeModel          *child_model,
						      GtkTreePath           *root);
486

487

488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537
typedef struct {
  GtkTreeModelSort parent;

  GtkFileChooserDefault *impl;
} RecentModelSort;

typedef struct {
  GtkTreeModelSortClass parent_class;
} RecentModelSortClass;

#define RECENT_MODEL_SORT_TYPE (_recent_model_sort_get_type ())
#define RECENT_MODEL_SORT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), RECENT_MODEL_SORT_TYPE, RecentModelSort))

static void recent_model_sort_drag_source_iface_init (GtkTreeDragSourceIface *iface);

G_DEFINE_TYPE_WITH_CODE (RecentModelSort,
                         _recent_model_sort,
                         GTK_TYPE_TREE_MODEL_SORT,
                         G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_DRAG_SOURCE,
                                                recent_model_sort_drag_source_iface_init));

static GtkTreeModel *recent_model_sort_new (GtkFileChooserDefault *impl,
                                            GtkTreeModel          *child_model);


typedef struct {
  GtkTreeModelSort parent;

  GtkFileChooserDefault *impl;
} SearchModelSort;

typedef struct {
  GtkTreeModelSortClass parent_class;
} SearchModelSortClass;

#define SEARCH_MODEL_SORT_TYPE (_search_model_sort_get_type ())
#define SEARCH_MODEL_SORT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SEARCH_MODEL_SORT_TYPE, SearchModelSort))

static void search_model_sort_drag_source_iface_init (GtkTreeDragSourceIface *iface);

G_DEFINE_TYPE_WITH_CODE (SearchModelSort,
                         _search_model_sort,
                         GTK_TYPE_TREE_MODEL_SORT,
                         G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_DRAG_SOURCE,
                                                search_model_sort_drag_source_iface_init));

static GtkTreeModel *search_model_sort_new (GtkFileChooserDefault *impl,
                                            GtkTreeModel          *child_model);


538

Matthias Clasen's avatar
Matthias Clasen committed
539 540 541 542 543
G_DEFINE_TYPE_WITH_CODE (GtkFileChooserDefault, _gtk_file_chooser_default, GTK_TYPE_VBOX,
			 G_IMPLEMENT_INTERFACE (GTK_TYPE_FILE_CHOOSER,
						gtk_file_chooser_default_iface_init)
			 G_IMPLEMENT_INTERFACE (GTK_TYPE_FILE_CHOOSER_EMBED,
						gtk_file_chooser_embed_default_iface_init));						
Owen Taylor's avatar
Owen Taylor committed
544 545

static void
Matthias Clasen's avatar
Matthias Clasen committed
546
_gtk_file_chooser_default_class_init (GtkFileChooserDefaultClass *class)
Owen Taylor's avatar
Owen Taylor committed
547
{
548 549 550
  static const guint quick_bookmark_keyvals[10] = {
    GDK_1, GDK_2, GDK_3, GDK_4, GDK_5, GDK_6, GDK_7, GDK_8, GDK_9, GDK_0
  };
Owen Taylor's avatar
Owen Taylor committed
551
  GObjectClass *gobject_class = G_OBJECT_CLASS (class);
Owen Taylor's avatar
Owen Taylor committed
552
  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
553
  GtkBindingSet *binding_set;
554
  int i;
Owen Taylor's avatar
Owen Taylor committed
555

556 557 558 559
  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;
560
  gobject_class->dispose = gtk_file_chooser_default_dispose;
Owen Taylor's avatar
Owen Taylor committed
561

562
  widget_class->show_all = gtk_file_chooser_default_show_all;
563
  widget_class->realize = gtk_file_chooser_default_realize;
Federico Mena Quintero's avatar
Federico Mena Quintero committed
564
  widget_class->map = gtk_file_chooser_default_map;
565
  widget_class->unmap = gtk_file_chooser_default_unmap;
566
  widget_class->hierarchy_changed = gtk_file_chooser_default_hierarchy_changed;
567 568
  widget_class->style_set = gtk_file_chooser_default_style_set;
  widget_class->screen_changed = gtk_file_chooser_default_screen_changed;
569
  widget_class->size_allocate = gtk_file_chooser_default_size_allocate;
Owen Taylor's avatar
Owen Taylor committed
570

571
  signals[LOCATION_POPUP] =
572 573 574 575 576 577 578 579
    g_signal_new_class_handler (I_("location-popup"),
                                G_OBJECT_CLASS_TYPE (class),
                                G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
                                G_CALLBACK (location_popup_handler),
                                NULL, NULL,
                                _gtk_marshal_VOID__STRING,
                                G_TYPE_NONE, 1, G_TYPE_STRING);

580
  signals[LOCATION_POPUP_ON_PASTE] =
581 582 583 584 585 586 587 588
    g_signal_new_class_handler (I_("location-popup-on-paste"),
                                G_OBJECT_CLASS_TYPE (class),
                                G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
                                G_CALLBACK (location_popup_on_paste_handler),
                                NULL, NULL,
                                _gtk_marshal_VOID__VOID,
                                G_TYPE_NONE, 0);

589
  signals[LOCATION_TOGGLE_POPUP] =
590 591 592 593 594 595 596 597
    g_signal_new_class_handler (I_("location-toggle-popup"),
                                G_OBJECT_CLASS_TYPE (class),
                                G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
                                G_CALLBACK (location_toggle_popup_handler),
                                NULL, NULL,
                                _gtk_marshal_VOID__VOID,
                                G_TYPE_NONE, 0);

598
  signals[UP_FOLDER] =
599 600 601 602 603 604 605 606
    g_signal_new_class_handler (I_("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);

607
  signals[DOWN_FOLDER] =
608 609 610 611 612 613 614 615
    g_signal_new_class_handler (I_("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);

616
  signals[HOME_FOLDER] =
617 618 619 620 621 622 623 624
    g_signal_new_class_handler (I_("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);

625
  signals[DESKTOP_FOLDER] =
626 627 628 629 630 631 632 633
    g_signal_new_class_handler (I_("desktop-folder"),
                                G_OBJECT_CLASS_TYPE (class),
                                G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
                                G_CALLBACK (desktop_folder_handler),
                                NULL, NULL,
                                _gtk_marshal_VOID__VOID,
                                G_TYPE_NONE, 0);

634
  signals[QUICK_BOOKMARK] =
635 636 637 638 639 640 641 642
    g_signal_new_class_handler (I_("quick-bookmark"),
                                G_OBJECT_CLASS_TYPE (class),
                                G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
                                G_CALLBACK (quick_bookmark_handler),
                                NULL, NULL,
                                _gtk_marshal_VOID__INT,
                                G_TYPE_NONE, 1, G_TYPE_INT);

643
  signals[SHOW_HIDDEN] =
644 645 646 647 648 649 650 651
    g_signal_new_class_handler (I_("show-hidden"),
                                G_OBJECT_CLASS_TYPE (class),
                                G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
                                G_CALLBACK (show_hidden_handler),
                                NULL, NULL,
                                _gtk_marshal_VOID__VOID,
                                G_TYPE_NONE, 0);

652
  signals[SEARCH_SHORTCUT] =
653 654 655 656 657 658 659 660
    g_signal_new_class_handler (I_("search-shortcut"),
                                G_OBJECT_CLASS_TYPE (class),
                                G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
                                G_CALLBACK (search_shortcut_handler),
                                NULL, NULL,
                                _gtk_marshal_VOID__VOID,
                                G_TYPE_NONE, 0);

661
  signals[RECENT_SHORTCUT] =
662 663 664 665 666 667 668
    g_signal_new_class_handler (I_("recent-shortcut"),
                                G_OBJECT_CLASS_TYPE (class),
                                G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
                                G_CALLBACK (recent_shortcut_handler),
                                NULL, NULL,
                                _gtk_marshal_VOID__VOID,
                                G_TYPE_NONE, 0);
669 670 671 672 673

  binding_set = gtk_binding_set_by_class (class);

  gtk_binding_entry_add_signal (binding_set,
				GDK_l, GDK_CONTROL_MASK,
674 675
				"location-toggle-popup",
				0);
676

677 678 679
  gtk_binding_entry_add_signal (binding_set,
				GDK_slash, 0,
				"location-popup",
680
				1, G_TYPE_STRING, "/");
Federico Mena Quintero's avatar
Federico Mena Quintero committed
681 682 683 684
  gtk_binding_entry_add_signal (binding_set,
				GDK_KP_Divide, 0,
				"location-popup",
				1, G_TYPE_STRING, "/");
685

686 687 688 689 690 691 692
#ifdef G_OS_UNIX
  gtk_binding_entry_add_signal (binding_set,
				GDK_asciitilde, 0,
				"location-popup",
				1, G_TYPE_STRING, "~");
#endif

693 694 695 696
  gtk_binding_entry_add_signal (binding_set,
				GDK_v, GDK_CONTROL_MASK,
				"location-popup-on-paste",
				0);
697
  gtk_binding_entry_add_signal (binding_set,
698
				GDK_Up, GDK_MOD1_MASK,
699 700
				"up-folder",
				0);
701 702 703 704
  gtk_binding_entry_add_signal (binding_set,
		  		GDK_BackSpace, 0,
				"up-folder",
				0);
705
  gtk_binding_entry_add_signal (binding_set,
706
				GDK_KP_Up, GDK_MOD1_MASK,
707 708 709
				"up-folder",
				0);

710 711 712 713 714 715 716 717 718
  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);

719
  gtk_binding_entry_add_signal (binding_set,
720 721
				GDK_Home, GDK_MOD1_MASK,
				"home-folder",
722 723
				0);
  gtk_binding_entry_add_signal (binding_set,
724 725
				GDK_KP_Home, GDK_MOD1_MASK,
				"home-folder",
726
				0);
727 728 729 730
  gtk_binding_entry_add_signal (binding_set,
				GDK_d, GDK_MOD1_MASK,
				"desktop-folder",
				0);
731 732 733 734
  gtk_binding_entry_add_signal (binding_set,
				GDK_h, GDK_CONTROL_MASK,
                                "show-hidden",
                                0);
735 736 737 738
  gtk_binding_entry_add_signal (binding_set,
                                GDK_s, GDK_MOD1_MASK,
                                "search-shortcut",
                                0);
739 740 741 742
  gtk_binding_entry_add_signal (binding_set,
                                GDK_r, GDK_MOD1_MASK,
                                "recent-shortcut",
                                0);
743

744 745 746 747 748 749
  for (i = 0; i < 10; i++)
    gtk_binding_entry_add_signal (binding_set,
				  quick_bookmark_keyvals[i], GDK_MOD1_MASK,
				  "quick-bookmark",
				  1, G_TYPE_INT, i);

Owen Taylor's avatar
Owen Taylor committed
750 751 752 753
  _gtk_file_chooser_install_properties (gobject_class);
}

static void
754 755
gtk_file_chooser_default_iface_init (GtkFileChooserIface *iface)
{
756 757
  iface->select_file = gtk_file_chooser_default_select_file;
  iface->unselect_file = gtk_file_chooser_default_unselect_file;
758 759
  iface->select_all = gtk_file_chooser_default_select_all;
  iface->unselect_all = gtk_file_chooser_default_unselect_all;
760 761
  iface->get_files = gtk_file_chooser_default_get_files;
  iface->get_preview_file = gtk_file_chooser_default_get_preview_file;
762 763 764 765 766 767 768 769 770 771
  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
772 773
}

774 775 776 777
static void
gtk_file_chooser_embed_default_iface_init (GtkFileChooserEmbedIface *iface)
{
  iface->get_default_size = gtk_file_chooser_default_get_default_size;
778
  iface->should_respond = gtk_file_chooser_default_should_respond;
Federico Mena Quintero's avatar
Federico Mena Quintero committed
779
  iface->initial_focus = gtk_file_chooser_default_initial_focus;
780
}
Matthias Clasen's avatar
Matthias Clasen committed
781

Owen Taylor's avatar
Owen Taylor committed
782
static void
Matthias Clasen's avatar
Matthias Clasen committed
783
_gtk_file_chooser_default_init (GtkFileChooserDefault *impl)
Owen Taylor's avatar
Owen Taylor committed
784
{
785
  profile_start ("start", NULL);
786 787 788
#ifdef PROFILE_FILE_CHOOSER
  access ("MARK: *** CREATE FILE CHOOSER", F_OK);
#endif
789 790
  impl->local_only = TRUE;
  impl->preview_widget_active = TRUE;
791
  impl->use_preview_label = TRUE;
792 793
  impl->select_multiple = FALSE;
  impl->show_hidden = FALSE;
794
  impl->show_size_column = TRUE;
Federico Mena Quintero's avatar
Federico Mena Quintero committed
795
  impl->icon_size = FALLBACK_ICON_SIZE;
Federico Mena Quintero's avatar
Federico Mena Quintero committed
796
  impl->load_state = LOAD_EMPTY;
797
  impl->reload_state = RELOAD_EMPTY;
798
  impl->pending_select_files = NULL;
799
  impl->location_mode = LOCATION_MODE_PATH_BAR;
800
  impl->operation_mode = OPERATION_MODE_BROWSE;
801 802
  impl->sort_column = FILE_LIST_COL_NAME;
  impl->sort_order = GTK_SORT_ASCENDING;
803
  impl->recent_manager = gtk_recent_manager_get_default ();
804

805
  gtk_box_set_spacing (GTK_BOX (impl), 12);
Matthias Clasen's avatar
Matthias Clasen committed
806

807 808
  set_file_system_backend (impl);

809
  profile_end ("end", NULL);
810 811
}

Federico Mena Quintero's avatar
Federico Mena Quintero committed
812 813 814 815 816 817
/* 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;
818
  ShortcutType shortcut_type;
819
  GCancellable *cancellable;
Federico Mena Quintero's avatar
Federico Mena Quintero committed
820 821 822

  gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), iter,
		      SHORTCUTS_COL_DATA, &col_data,
823
		      SHORTCUTS_COL_TYPE, &shortcut_type,
824
		      SHORTCUTS_COL_CANCELLABLE, &cancellable,
Federico Mena Quintero's avatar
Federico Mena Quintero committed
825
		      -1);
826

827 828
  if (cancellable)
    g_cancellable_cancel (cancellable);
829

830
  if (!(shortcut_type == SHORTCUT_TYPE_FILE || 
831 832
	shortcut_type == SHORTCUT_TYPE_VOLUME) ||
      !col_data)
Federico Mena Quintero's avatar
Federico Mena Quintero committed
833 834
    return;

835
  if (shortcut_type == SHORTCUT_TYPE_VOLUME)
Federico Mena Quintero's avatar
Federico Mena Quintero committed
836 837 838 839
    {
      GtkFileSystemVolume *volume;

      volume = col_data;
840
      _gtk_file_system_volume_free (volume);
Federico Mena Quintero's avatar
Federico Mena Quintero committed
841 842 843
    }
  else
    {
844
      GFile *file;
Federico Mena Quintero's avatar
Federico Mena Quintero committed
845

846
      g_assert (shortcut_type == SHORTCUT_TYPE_FILE);
847

848 849
      file = col_data;
      g_object_unref (file);
Federico Mena Quintero's avatar
Federico Mena Quintero committed
850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872
    }
}

/* 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
873
static void
874
pending_select_files_free (GtkFileChooserDefault *impl)
Federico Mena Quintero's avatar
Federico Mena Quintero committed
875
{
876 877 878
  g_slist_foreach (impl->pending_select_files, (GFunc) g_object_unref, NULL);
  g_slist_free (impl->pending_select_files);
  impl->pending_select_files = NULL;
Federico Mena Quintero's avatar
Federico Mena Quintero committed
879 880 881
}

static void
882 883
pending_select_files_add (GtkFileChooserDefault *impl,
			  GFile                 *file)
Federico Mena Quintero's avatar
Federico Mena Quintero committed
884
{
885 886
  impl->pending_select_files =
    g_slist_prepend (impl->pending_select_files, g_object_ref (file));
Federico Mena Quintero's avatar
Federico Mena Quintero committed
887 888 889 890 891 892 893 894 895 896 897
}

/* 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;
898
  GFile *file;
Federico Mena Quintero's avatar
Federico Mena Quintero committed
899 900 901 902 903

  impl = GTK_FILE_CHOOSER_DEFAULT (data);

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

904 905
  file = _gtk_file_system_model_get_file (impl->browse_files_model, &child_iter);
  pending_select_files_add (impl, file);
Federico Mena Quintero's avatar
Federico Mena Quintero committed
906 907 908 909 910 911
}

/* 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
912
pending_select_files_store_selection (GtkFileChooserDefault *impl)
Federico Mena Quintero's avatar
Federico Mena Quintero committed
913 914 915 916 917 918 919
{
  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);
}

920
static void
921
gtk_file_chooser_default_finalize (GObject *object)
922
{
923
  GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (object);
924
  GSList *l;
925

926 927
  unset_file_system_backend (impl);

928 929 930 931 932
  if (impl->shortcuts_pane_filter_model)
    g_object_unref (impl->shortcuts_pane_filter_model);

  if (impl->shortcuts_combo_filter_model)
    g_object_unref (impl->shortcuts_combo_filter_model);
Federico Mena Quintero's avatar
Federico Mena Quintero committed
933 934 935

  shortcuts_free (impl);

936 937
  g_free (impl->browse_files_last_selected_name);

938 939 940 941 942 943 944 945 946 947 948
  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
949

950 951
  if (impl->current_volume_file)
    g_object_unref (impl->current_volume_file);
Federico Mena Quintero's avatar
Federico Mena Quintero committed
952 953

  if (impl->current_folder)
954
    g_object_unref (impl->current_folder);
Federico Mena Quintero's avatar
Federico Mena Quintero committed
955

956 957
  if (impl->preview_file)
    g_object_unref (impl->preview_file);
Federico Mena Quintero's avatar
Federico Mena Quintero committed
958

959 960 961
  if (impl->browse_path_bar_size_group)
    g_object_unref (impl->browse_path_bar_size_group);