]> Pileus Git - ~andy/gtk/blob - gtk/gtkfilechooserdefault.c
Create the location_entry in a single function
[~andy/gtk] / gtk / gtkfilechooserdefault.c
1 /* -*- Mode: C; c-file-style: "gnu"; tab-width: 8 -*- */
2 /* GTK - The GIMP Toolkit
3  * gtkfilechooserdefault.c: Default implementation of GtkFileChooser
4  * Copyright (C) 2003, Red Hat, Inc.
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21
22 #include "config.h"
23
24 #include "gtkfilechooserdefault.h"
25
26 #include "gtkbindings.h"
27 #include "gtkcelllayout.h"
28 #include "gtkcellrendererpixbuf.h"
29 #include "gtkcellrenderertext.h"
30 #include "gtkcheckmenuitem.h"
31 #include "gtkclipboard.h"
32 #include "gtkcomboboxtext.h"
33 #include "gtkentry.h"
34 #include "gtkexpander.h"
35 #include "gtkfilechooserprivate.h"
36 #include "gtkfilechooserdialog.h"
37 #include "gtkfilechooserembed.h"
38 #include "gtkfilechooserentry.h"
39 #include "gtkfilechooserutils.h"
40 #include "gtkfilechooser.h"
41 #include "gtkfilesystem.h"
42 #include "gtkfilesystemmodel.h"
43 #include "gtkframe.h"
44 #include "gtkpaned.h"
45 #include "gtkiconfactory.h"
46 #include "gtkicontheme.h"
47 #include "gtkimage.h"
48 #include "gtkimagemenuitem.h"
49 #include "gtkinfobar.h"
50 #include "gtklabel.h"
51 #include "gtkmarshalers.h"
52 #include "gtkmessagedialog.h"
53 #include "gtkmountoperation.h"
54 #include "gtkpathbar.h"
55 #include "gtkprivate.h"
56 #include "gtkradiobutton.h"
57 #include "gtkrecentfilter.h"
58 #include "gtkrecentmanager.h"
59 #include "gtkscrolledwindow.h"
60 #include "gtkseparatormenuitem.h"
61 #include "gtksizegroup.h"
62 #include "gtksizerequest.h"
63 #include "gtkstock.h"
64 #include "gtktable.h"
65 #include "gtktoolbar.h"
66 #include "gtktoolbutton.h"
67 #include "gtktooltip.h"
68 #include "gtktreednd.h"
69 #include "gtktreeprivate.h"
70 #include "gtktreeselection.h"
71 #include "gtkbox.h"
72 #include "gtkorientable.h"
73 #include "gtkintl.h"
74
75 #include <errno.h>
76 #include <string.h>
77 #include <time.h>
78 #include <sys/stat.h>
79 #include <sys/types.h>
80 #include <locale.h>
81
82 #ifdef HAVE_UNISTD_H
83 #include <unistd.h>
84 #endif
85 #ifdef G_OS_WIN32
86 #include <io.h>
87 #endif
88
89 /* Profiling stuff */
90 #undef PROFILE_FILE_CHOOSER
91 #ifdef PROFILE_FILE_CHOOSER
92
93
94 #ifndef F_OK 
95 #define F_OK 0
96 #endif
97
98 #define PROFILE_INDENT 4
99 static int profile_indent;
100
101 static void
102 profile_add_indent (int indent)
103 {
104   profile_indent += indent;
105   if (profile_indent < 0)
106     g_error ("You screwed up your indentation");
107 }
108
109 void
110 _gtk_file_chooser_profile_log (const char *func, int indent, const char *msg1, const char *msg2)
111 {
112   char *str;
113
114   if (indent < 0)
115     profile_add_indent (indent);
116
117   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   access (str, F_OK);
123   g_free (str);
124
125   if (indent > 0)
126     profile_add_indent (indent);
127 }
128
129 #define profile_start(x, y) _gtk_file_chooser_profile_log (G_STRFUNC, PROFILE_INDENT, x, y)
130 #define profile_end(x, y) _gtk_file_chooser_profile_log (G_STRFUNC, -PROFILE_INDENT, x, y)
131 #define profile_msg(x, y) _gtk_file_chooser_profile_log (NULL, 0, x, y)
132 #else
133 #define profile_start(x, y)
134 #define profile_end(x, y)
135 #define profile_msg(x, y)
136 #endif
137
138
139
140 typedef struct _GtkFileChooserDefaultClass GtkFileChooserDefaultClass;
141
142 #define GTK_FILE_CHOOSER_DEFAULT_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_FILE_CHOOSER_DEFAULT, GtkFileChooserDefaultClass))
143 #define GTK_IS_FILE_CHOOSER_DEFAULT_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_FILE_CHOOSER_DEFAULT))
144 #define GTK_FILE_CHOOSER_DEFAULT_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_FILE_CHOOSER_DEFAULT, GtkFileChooserDefaultClass))
145
146 #define MAX_LOADING_TIME 500
147
148 #define DEFAULT_NEW_FOLDER_NAME _("Type name of new folder")
149
150 struct _GtkFileChooserDefaultClass
151 {
152   GtkBoxClass parent_class;
153 };
154
155 /* Signal IDs */
156 enum {
157   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   LAST_SIGNAL
170 };
171
172 static guint signals[LAST_SIGNAL] = { 0 };
173
174 /* Column numbers for the shortcuts tree.  Keep these in sync with shortcuts_model_create() */
175 enum {
176   SHORTCUTS_COL_PIXBUF,
177   SHORTCUTS_COL_NAME,
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
184 };
185
186 typedef enum {
187   SHORTCUT_TYPE_FILE,
188   SHORTCUT_TYPE_VOLUME,
189   SHORTCUT_TYPE_SEPARATOR,
190   SHORTCUT_TYPE_SEARCH,
191   SHORTCUT_TYPE_RECENT
192 } ShortcutType;
193
194 #define MODEL_ATTRIBUTES "standard::name,standard::type,standard::display-name," \
195                          "standard::is-hidden,standard::is-backup,standard::size," \
196                          "standard::content-type,time::modified"
197 enum {
198   /* the first 3 must be these due to settings caching sort column */
199   MODEL_COL_NAME,
200   MODEL_COL_SIZE,
201   MODEL_COL_MTIME,
202   MODEL_COL_FILE,
203   MODEL_COL_NAME_COLLATED,
204   MODEL_COL_IS_FOLDER,
205   MODEL_COL_PIXBUF,
206   MODEL_COL_SIZE_TEXT,
207   MODEL_COL_MTIME_TEXT,
208   MODEL_COL_ELLIPSIZE,
209   MODEL_COL_NUM_COLUMNS
210 };
211
212 /* This list of types is passed to _gtk_file_system_model_new*() */
213 #define MODEL_COLUMN_TYPES                                      \
214         MODEL_COL_NUM_COLUMNS,                                  \
215         G_TYPE_STRING,            /* MODEL_COL_NAME */          \
216         G_TYPE_INT64,             /* MODEL_COL_SIZE */          \
217         G_TYPE_LONG,              /* MODEL_COL_MTIME */         \
218         G_TYPE_FILE,              /* MODEL_COL_FILE */          \
219         G_TYPE_STRING,            /* MODEL_COL_NAME_COLLATED */ \
220         G_TYPE_BOOLEAN,           /* MODEL_COL_IS_FOLDER */     \
221         GDK_TYPE_PIXBUF,          /* MODEL_COL_PIXBUF */        \
222         G_TYPE_STRING,            /* MODEL_COL_SIZE_TEXT */     \
223         G_TYPE_STRING,            /* MODEL_COL_MTIME_TEXT */    \
224         PANGO_TYPE_ELLIPSIZE_MODE /* MODEL_COL_ELLIPSIZE */
225
226 /* Identifiers for target types */
227 enum {
228   GTK_TREE_MODEL_ROW,
229 };
230
231 /* Interesting places in the shortcuts bar */
232 typedef enum {
233   SHORTCUTS_SEARCH,
234   SHORTCUTS_RECENT,
235   SHORTCUTS_RECENT_SEPARATOR,
236   SHORTCUTS_HOME,
237   SHORTCUTS_DESKTOP,
238   SHORTCUTS_VOLUMES,
239   SHORTCUTS_SHORTCUTS,
240   SHORTCUTS_BOOKMARKS_SEPARATOR,
241   SHORTCUTS_BOOKMARKS,
242   SHORTCUTS_CURRENT_FOLDER_SEPARATOR,
243   SHORTCUTS_CURRENT_FOLDER
244 } ShortcutsIndex;
245
246 /* Icon size for if we can't get it from the theme */
247 #define FALLBACK_ICON_SIZE 16
248
249 #define PREVIEW_HBOX_SPACING 12
250 #define NUM_LINES 45
251 #define NUM_CHARS 60
252
253 #define SETTINGS_KEY_LAST_FOLDER_URI     "last-folder-uri"
254 #define SETTINGS_KEY_LOCATION_MODE       "location-mode"
255 #define SETTINGS_KEY_SHOW_HIDDEN         "show-hidden"
256 #define SETTINGS_KEY_SHOW_SIZE_COLUMN    "show-size-column"
257 #define SETTINGS_KEY_SORT_COLUMN         "sort-column"
258 #define SETTINGS_KEY_SORT_ORDER          "sort-order"
259 #define SETTINGS_KEY_WINDOW_POSITION     "window-position"
260 #define SETTINGS_KEY_WINDOW_SIZE         "window-size"
261
262 static void gtk_file_chooser_default_iface_init       (GtkFileChooserIface        *iface);
263 static void gtk_file_chooser_embed_default_iface_init (GtkFileChooserEmbedIface   *iface);
264
265 static GObject* gtk_file_chooser_default_constructor  (GType                  type,
266                                                        guint                  n_construct_properties,
267                                                        GObjectConstructParam *construct_params);
268 static void     gtk_file_chooser_default_finalize     (GObject               *object);
269 static void     gtk_file_chooser_default_set_property (GObject               *object,
270                                                        guint                  prop_id,
271                                                        const GValue          *value,
272                                                        GParamSpec            *pspec);
273 static void     gtk_file_chooser_default_get_property (GObject               *object,
274                                                        guint                  prop_id,
275                                                        GValue                *value,
276                                                        GParamSpec            *pspec);
277 static void     gtk_file_chooser_default_dispose      (GObject               *object);
278 static void     gtk_file_chooser_default_show_all       (GtkWidget             *widget);
279 static void     gtk_file_chooser_default_realize        (GtkWidget             *widget);
280 static void     gtk_file_chooser_default_map            (GtkWidget             *widget);
281 static void     gtk_file_chooser_default_unmap          (GtkWidget             *widget);
282 static void     gtk_file_chooser_default_hierarchy_changed (GtkWidget          *widget,
283                                                             GtkWidget          *previous_toplevel);
284 static void     gtk_file_chooser_default_style_updated  (GtkWidget             *widget);
285 static void     gtk_file_chooser_default_screen_changed (GtkWidget             *widget,
286                                                          GdkScreen             *previous_screen);
287 static void     gtk_file_chooser_default_size_allocate  (GtkWidget             *widget,
288                                                          GtkAllocation         *allocation);
289
290 static gboolean       gtk_file_chooser_default_set_current_folder          (GtkFileChooser    *chooser,
291                                                                             GFile             *folder,
292                                                                             GError           **error);
293 static gboolean       gtk_file_chooser_default_update_current_folder       (GtkFileChooser    *chooser,
294                                                                             GFile             *folder,
295                                                                             gboolean           keep_trail,
296                                                                             gboolean           clear_entry,
297                                                                             GError           **error);
298 static GFile *        gtk_file_chooser_default_get_current_folder          (GtkFileChooser    *chooser);
299 static void           gtk_file_chooser_default_set_current_name            (GtkFileChooser    *chooser,
300                                                                             const gchar       *name);
301 static gboolean       gtk_file_chooser_default_select_file                 (GtkFileChooser    *chooser,
302                                                                             GFile             *file,
303                                                                             GError           **error);
304 static void           gtk_file_chooser_default_unselect_file               (GtkFileChooser    *chooser,
305                                                                             GFile             *file);
306 static void           gtk_file_chooser_default_select_all                  (GtkFileChooser    *chooser);
307 static void           gtk_file_chooser_default_unselect_all                (GtkFileChooser    *chooser);
308 static GSList *       gtk_file_chooser_default_get_files                   (GtkFileChooser    *chooser);
309 static GFile *        gtk_file_chooser_default_get_preview_file            (GtkFileChooser    *chooser);
310 static GtkFileSystem *gtk_file_chooser_default_get_file_system             (GtkFileChooser    *chooser);
311 static void           gtk_file_chooser_default_add_filter                  (GtkFileChooser    *chooser,
312                                                                             GtkFileFilter     *filter);
313 static void           gtk_file_chooser_default_remove_filter               (GtkFileChooser    *chooser,
314                                                                             GtkFileFilter     *filter);
315 static GSList *       gtk_file_chooser_default_list_filters                (GtkFileChooser    *chooser);
316 static gboolean       gtk_file_chooser_default_add_shortcut_folder    (GtkFileChooser    *chooser,
317                                                                        GFile             *file,
318                                                                        GError           **error);
319 static gboolean       gtk_file_chooser_default_remove_shortcut_folder (GtkFileChooser    *chooser,
320                                                                        GFile             *file,
321                                                                        GError           **error);
322 static GSList *       gtk_file_chooser_default_list_shortcut_folders  (GtkFileChooser    *chooser);
323
324 static void           gtk_file_chooser_default_get_default_size       (GtkFileChooserEmbed *chooser_embed,
325                                                                        gint                *default_width,
326                                                                        gint                *default_height);
327 static gboolean       gtk_file_chooser_default_should_respond         (GtkFileChooserEmbed *chooser_embed);
328 static void           gtk_file_chooser_default_initial_focus          (GtkFileChooserEmbed *chooser_embed);
329
330 static void add_selection_to_recent_list (GtkFileChooserDefault *impl);
331
332 static void location_popup_handler  (GtkFileChooserDefault *impl,
333                                      const gchar           *path);
334 static void location_popup_on_paste_handler (GtkFileChooserDefault *impl);
335 static void location_toggle_popup_handler   (GtkFileChooserDefault *impl);
336 static void up_folder_handler       (GtkFileChooserDefault *impl);
337 static void down_folder_handler     (GtkFileChooserDefault *impl);
338 static void home_folder_handler     (GtkFileChooserDefault *impl);
339 static void desktop_folder_handler  (GtkFileChooserDefault *impl);
340 static void quick_bookmark_handler  (GtkFileChooserDefault *impl,
341                                      gint                   bookmark_index);
342 static void show_hidden_handler     (GtkFileChooserDefault *impl);
343 static void search_shortcut_handler (GtkFileChooserDefault *impl);
344 static void recent_shortcut_handler (GtkFileChooserDefault *impl);
345 static void update_appearance       (GtkFileChooserDefault *impl);
346
347 static void set_current_filter   (GtkFileChooserDefault *impl,
348                                   GtkFileFilter         *filter);
349 static void check_preview_change (GtkFileChooserDefault *impl);
350
351 static void filter_combo_changed       (GtkComboBox           *combo_box,
352                                         GtkFileChooserDefault *impl);
353
354 static gboolean shortcuts_key_press_event_cb (GtkWidget             *widget,
355                                               GdkEventKey           *event,
356                                               GtkFileChooserDefault *impl);
357
358 static gboolean shortcuts_select_func   (GtkTreeSelection      *selection,
359                                          GtkTreeModel          *model,
360                                          GtkTreePath           *path,
361                                          gboolean               path_currently_selected,
362                                          gpointer               data);
363 static gboolean shortcuts_get_selected  (GtkFileChooserDefault *impl,
364                                          GtkTreeIter           *iter);
365 static void shortcuts_activate_iter (GtkFileChooserDefault *impl,
366                                      GtkTreeIter           *iter);
367 static int shortcuts_get_index (GtkFileChooserDefault *impl,
368                                 ShortcutsIndex         where);
369 static int shortcut_find_position (GtkFileChooserDefault *impl,
370                                    GFile                 *file);
371
372 static void bookmarks_check_add_sensitivity (GtkFileChooserDefault *impl);
373
374 static gboolean list_select_func   (GtkTreeSelection      *selection,
375                                     GtkTreeModel          *model,
376                                     GtkTreePath           *path,
377                                     gboolean               path_currently_selected,
378                                     gpointer               data);
379
380 static void list_selection_changed     (GtkTreeSelection      *tree_selection,
381                                         GtkFileChooserDefault *impl);
382 static void list_row_activated         (GtkTreeView           *tree_view,
383                                         GtkTreePath           *path,
384                                         GtkTreeViewColumn     *column,
385                                         GtkFileChooserDefault *impl);
386
387 static void path_bar_clicked (GtkPathBar            *path_bar,
388                               GFile                 *file,
389                               GFile                 *child,
390                               gboolean               child_is_hidden,
391                               GtkFileChooserDefault *impl);
392
393 static void add_bookmark_button_clicked_cb    (GtkButton             *button,
394                                                GtkFileChooserDefault *impl);
395 static void remove_bookmark_button_clicked_cb (GtkButton             *button,
396                                                GtkFileChooserDefault *impl);
397
398 static void update_cell_renderer_attributes (GtkFileChooserDefault *impl);
399
400 static void load_remove_timer (GtkFileChooserDefault *impl);
401 static void browse_files_center_selected_row (GtkFileChooserDefault *impl);
402
403 static void location_button_toggled_cb (GtkToggleButton *toggle,
404                                         GtkFileChooserDefault *impl);
405 static void location_switch_to_path_bar (GtkFileChooserDefault *impl);
406
407 static void stop_loading_and_clear_list_model (GtkFileChooserDefault *impl,
408                                                gboolean remove_from_treeview);
409
410 static void     search_setup_widgets         (GtkFileChooserDefault *impl);
411 static void     search_stop_searching        (GtkFileChooserDefault *impl,
412                                               gboolean               remove_query);
413 static void     search_clear_model           (GtkFileChooserDefault *impl, 
414                                               gboolean               remove_from_treeview);
415 static gboolean search_should_respond        (GtkFileChooserDefault *impl);
416 static GSList  *search_get_selected_files    (GtkFileChooserDefault *impl);
417 static void     search_entry_activate_cb     (GtkEntry              *entry, 
418                                               gpointer               data);
419 static void     settings_load                (GtkFileChooserDefault *impl);
420
421 static void     recent_start_loading         (GtkFileChooserDefault *impl);
422 static void     recent_stop_loading          (GtkFileChooserDefault *impl);
423 static void     recent_clear_model           (GtkFileChooserDefault *impl,
424                                               gboolean               remove_from_treeview);
425 static gboolean recent_should_respond        (GtkFileChooserDefault *impl);
426 static GSList * recent_get_selected_files    (GtkFileChooserDefault *impl);
427 static void     set_file_system_backend      (GtkFileChooserDefault *impl);
428 static void     unset_file_system_backend    (GtkFileChooserDefault *impl);
429
430
431
432 \f
433
434 /* Drag and drop interface declarations */
435
436 typedef struct {
437   GtkTreeModelFilter parent;
438
439   GtkFileChooserDefault *impl;
440 } ShortcutsPaneModelFilter;
441
442 typedef struct {
443   GtkTreeModelFilterClass parent_class;
444 } ShortcutsPaneModelFilterClass;
445
446 #define SHORTCUTS_PANE_MODEL_FILTER_TYPE (_shortcuts_pane_model_filter_get_type ())
447 #define SHORTCUTS_PANE_MODEL_FILTER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SHORTCUTS_PANE_MODEL_FILTER_TYPE, ShortcutsPaneModelFilter))
448
449 static void shortcuts_pane_model_filter_drag_source_iface_init (GtkTreeDragSourceIface *iface);
450
451 G_DEFINE_TYPE_WITH_CODE (ShortcutsPaneModelFilter,
452                          _shortcuts_pane_model_filter,
453                          GTK_TYPE_TREE_MODEL_FILTER,
454                          G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_DRAG_SOURCE,
455                                                 shortcuts_pane_model_filter_drag_source_iface_init))
456
457 static GtkTreeModel *shortcuts_pane_model_filter_new (GtkFileChooserDefault *impl,
458                                                       GtkTreeModel          *child_model,
459                                                       GtkTreePath           *root);
460
461 \f
462
463 G_DEFINE_TYPE_WITH_CODE (GtkFileChooserDefault, _gtk_file_chooser_default, GTK_TYPE_BOX,
464                          G_IMPLEMENT_INTERFACE (GTK_TYPE_FILE_CHOOSER,
465                                                 gtk_file_chooser_default_iface_init)
466                          G_IMPLEMENT_INTERFACE (GTK_TYPE_FILE_CHOOSER_EMBED,
467                                                 gtk_file_chooser_embed_default_iface_init));                                            
468
469
470 static void
471 add_normal_and_shifted_binding (GtkBindingSet  *binding_set,
472                                 guint           keyval,
473                                 GdkModifierType modifiers,
474                                 const gchar    *signal_name)
475 {
476   gtk_binding_entry_add_signal (binding_set,
477                                 keyval, modifiers,
478                                 signal_name, 0);
479
480   gtk_binding_entry_add_signal (binding_set,
481                                 keyval, modifiers | GDK_SHIFT_MASK,
482                                 signal_name, 0);
483 }
484
485 static void
486 _gtk_file_chooser_default_class_init (GtkFileChooserDefaultClass *class)
487 {
488   static const guint quick_bookmark_keyvals[10] = {
489     GDK_KEY_1, GDK_KEY_2, GDK_KEY_3, GDK_KEY_4, GDK_KEY_5, GDK_KEY_6, GDK_KEY_7, GDK_KEY_8, GDK_KEY_9, GDK_KEY_0
490   };
491   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
492   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
493   GtkBindingSet *binding_set;
494   int i;
495
496   gobject_class->finalize = gtk_file_chooser_default_finalize;
497   gobject_class->constructor = gtk_file_chooser_default_constructor;
498   gobject_class->set_property = gtk_file_chooser_default_set_property;
499   gobject_class->get_property = gtk_file_chooser_default_get_property;
500   gobject_class->dispose = gtk_file_chooser_default_dispose;
501
502   widget_class->show_all = gtk_file_chooser_default_show_all;
503   widget_class->realize = gtk_file_chooser_default_realize;
504   widget_class->map = gtk_file_chooser_default_map;
505   widget_class->unmap = gtk_file_chooser_default_unmap;
506   widget_class->hierarchy_changed = gtk_file_chooser_default_hierarchy_changed;
507   widget_class->style_updated = gtk_file_chooser_default_style_updated;
508   widget_class->screen_changed = gtk_file_chooser_default_screen_changed;
509   widget_class->size_allocate = gtk_file_chooser_default_size_allocate;
510
511   signals[LOCATION_POPUP] =
512     g_signal_new_class_handler (I_("location-popup"),
513                                 G_OBJECT_CLASS_TYPE (class),
514                                 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
515                                 G_CALLBACK (location_popup_handler),
516                                 NULL, NULL,
517                                 _gtk_marshal_VOID__STRING,
518                                 G_TYPE_NONE, 1, G_TYPE_STRING);
519
520   signals[LOCATION_POPUP_ON_PASTE] =
521     g_signal_new_class_handler (I_("location-popup-on-paste"),
522                                 G_OBJECT_CLASS_TYPE (class),
523                                 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
524                                 G_CALLBACK (location_popup_on_paste_handler),
525                                 NULL, NULL,
526                                 _gtk_marshal_VOID__VOID,
527                                 G_TYPE_NONE, 0);
528
529   signals[LOCATION_TOGGLE_POPUP] =
530     g_signal_new_class_handler (I_("location-toggle-popup"),
531                                 G_OBJECT_CLASS_TYPE (class),
532                                 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
533                                 G_CALLBACK (location_toggle_popup_handler),
534                                 NULL, NULL,
535                                 _gtk_marshal_VOID__VOID,
536                                 G_TYPE_NONE, 0);
537
538   signals[UP_FOLDER] =
539     g_signal_new_class_handler (I_("up-folder"),
540                                 G_OBJECT_CLASS_TYPE (class),
541                                 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
542                                 G_CALLBACK (up_folder_handler),
543                                 NULL, NULL,
544                                 _gtk_marshal_VOID__VOID,
545                                 G_TYPE_NONE, 0);
546
547   signals[DOWN_FOLDER] =
548     g_signal_new_class_handler (I_("down-folder"),
549                                 G_OBJECT_CLASS_TYPE (class),
550                                 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
551                                 G_CALLBACK (down_folder_handler),
552                                 NULL, NULL,
553                                 _gtk_marshal_VOID__VOID,
554                                 G_TYPE_NONE, 0);
555
556   signals[HOME_FOLDER] =
557     g_signal_new_class_handler (I_("home-folder"),
558                                 G_OBJECT_CLASS_TYPE (class),
559                                 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
560                                 G_CALLBACK (home_folder_handler),
561                                 NULL, NULL,
562                                 _gtk_marshal_VOID__VOID,
563                                 G_TYPE_NONE, 0);
564
565   signals[DESKTOP_FOLDER] =
566     g_signal_new_class_handler (I_("desktop-folder"),
567                                 G_OBJECT_CLASS_TYPE (class),
568                                 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
569                                 G_CALLBACK (desktop_folder_handler),
570                                 NULL, NULL,
571                                 _gtk_marshal_VOID__VOID,
572                                 G_TYPE_NONE, 0);
573
574   signals[QUICK_BOOKMARK] =
575     g_signal_new_class_handler (I_("quick-bookmark"),
576                                 G_OBJECT_CLASS_TYPE (class),
577                                 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
578                                 G_CALLBACK (quick_bookmark_handler),
579                                 NULL, NULL,
580                                 _gtk_marshal_VOID__INT,
581                                 G_TYPE_NONE, 1, G_TYPE_INT);
582
583   signals[SHOW_HIDDEN] =
584     g_signal_new_class_handler (I_("show-hidden"),
585                                 G_OBJECT_CLASS_TYPE (class),
586                                 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
587                                 G_CALLBACK (show_hidden_handler),
588                                 NULL, NULL,
589                                 _gtk_marshal_VOID__VOID,
590                                 G_TYPE_NONE, 0);
591
592   signals[SEARCH_SHORTCUT] =
593     g_signal_new_class_handler (I_("search-shortcut"),
594                                 G_OBJECT_CLASS_TYPE (class),
595                                 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
596                                 G_CALLBACK (search_shortcut_handler),
597                                 NULL, NULL,
598                                 _gtk_marshal_VOID__VOID,
599                                 G_TYPE_NONE, 0);
600
601   signals[RECENT_SHORTCUT] =
602     g_signal_new_class_handler (I_("recent-shortcut"),
603                                 G_OBJECT_CLASS_TYPE (class),
604                                 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
605                                 G_CALLBACK (recent_shortcut_handler),
606                                 NULL, NULL,
607                                 _gtk_marshal_VOID__VOID,
608                                 G_TYPE_NONE, 0);
609
610   binding_set = gtk_binding_set_by_class (class);
611
612   gtk_binding_entry_add_signal (binding_set,
613                                 GDK_KEY_l, GDK_CONTROL_MASK,
614                                 "location-toggle-popup",
615                                 0);
616
617   gtk_binding_entry_add_signal (binding_set,
618                                 GDK_KEY_slash, 0,
619                                 "location-popup",
620                                 1, G_TYPE_STRING, "/");
621   gtk_binding_entry_add_signal (binding_set,
622                                 GDK_KEY_KP_Divide, 0,
623                                 "location-popup",
624                                 1, G_TYPE_STRING, "/");
625
626 #ifdef G_OS_UNIX
627   gtk_binding_entry_add_signal (binding_set,
628                                 GDK_KEY_asciitilde, 0,
629                                 "location-popup",
630                                 1, G_TYPE_STRING, "~");
631 #endif
632
633   gtk_binding_entry_add_signal (binding_set,
634                                 GDK_KEY_v, GDK_CONTROL_MASK,
635                                 "location-popup-on-paste",
636                                 0);
637   gtk_binding_entry_add_signal (binding_set,
638                                 GDK_KEY_BackSpace, 0,
639                                 "up-folder",
640                                 0);
641
642   add_normal_and_shifted_binding (binding_set,
643                                   GDK_KEY_Up, GDK_MOD1_MASK,
644                                   "up-folder");
645
646   add_normal_and_shifted_binding (binding_set,
647                                   GDK_KEY_KP_Up, GDK_MOD1_MASK,
648                                   "up-folder");
649
650   add_normal_and_shifted_binding (binding_set,
651                                   GDK_KEY_Down, GDK_MOD1_MASK,
652                                   "down-folder");
653   add_normal_and_shifted_binding (binding_set,
654                                   GDK_KEY_KP_Down, GDK_MOD1_MASK,
655                                   "down-folder");
656
657   gtk_binding_entry_add_signal (binding_set,
658                                 GDK_KEY_Home, GDK_MOD1_MASK,
659                                 "home-folder",
660                                 0);
661   gtk_binding_entry_add_signal (binding_set,
662                                 GDK_KEY_KP_Home, GDK_MOD1_MASK,
663                                 "home-folder",
664                                 0);
665   gtk_binding_entry_add_signal (binding_set,
666                                 GDK_KEY_d, GDK_MOD1_MASK,
667                                 "desktop-folder",
668                                 0);
669   gtk_binding_entry_add_signal (binding_set,
670                                 GDK_KEY_h, GDK_CONTROL_MASK,
671                                 "show-hidden",
672                                 0);
673   gtk_binding_entry_add_signal (binding_set,
674                                 GDK_KEY_s, GDK_MOD1_MASK,
675                                 "search-shortcut",
676                                 0);
677   gtk_binding_entry_add_signal (binding_set,
678                                 GDK_KEY_r, GDK_MOD1_MASK,
679                                 "recent-shortcut",
680                                 0);
681
682   for (i = 0; i < 10; i++)
683     gtk_binding_entry_add_signal (binding_set,
684                                   quick_bookmark_keyvals[i], GDK_MOD1_MASK,
685                                   "quick-bookmark",
686                                   1, G_TYPE_INT, i);
687
688   _gtk_file_chooser_install_properties (gobject_class);
689 }
690
691 static void
692 gtk_file_chooser_default_iface_init (GtkFileChooserIface *iface)
693 {
694   iface->select_file = gtk_file_chooser_default_select_file;
695   iface->unselect_file = gtk_file_chooser_default_unselect_file;
696   iface->select_all = gtk_file_chooser_default_select_all;
697   iface->unselect_all = gtk_file_chooser_default_unselect_all;
698   iface->get_files = gtk_file_chooser_default_get_files;
699   iface->get_preview_file = gtk_file_chooser_default_get_preview_file;
700   iface->get_file_system = gtk_file_chooser_default_get_file_system;
701   iface->set_current_folder = gtk_file_chooser_default_set_current_folder;
702   iface->get_current_folder = gtk_file_chooser_default_get_current_folder;
703   iface->set_current_name = gtk_file_chooser_default_set_current_name;
704   iface->add_filter = gtk_file_chooser_default_add_filter;
705   iface->remove_filter = gtk_file_chooser_default_remove_filter;
706   iface->list_filters = gtk_file_chooser_default_list_filters;
707   iface->add_shortcut_folder = gtk_file_chooser_default_add_shortcut_folder;
708   iface->remove_shortcut_folder = gtk_file_chooser_default_remove_shortcut_folder;
709   iface->list_shortcut_folders = gtk_file_chooser_default_list_shortcut_folders;
710 }
711
712 static void
713 gtk_file_chooser_embed_default_iface_init (GtkFileChooserEmbedIface *iface)
714 {
715   iface->get_default_size = gtk_file_chooser_default_get_default_size;
716   iface->should_respond = gtk_file_chooser_default_should_respond;
717   iface->initial_focus = gtk_file_chooser_default_initial_focus;
718 }
719
720 static void
721 _gtk_file_chooser_default_init (GtkFileChooserDefault *impl)
722 {
723   profile_start ("start", NULL);
724 #ifdef PROFILE_FILE_CHOOSER
725   access ("MARK: *** CREATE FILE CHOOSER", F_OK);
726 #endif
727   impl->local_only = TRUE;
728   impl->preview_widget_active = TRUE;
729   impl->use_preview_label = TRUE;
730   impl->select_multiple = FALSE;
731   impl->show_hidden = FALSE;
732   impl->show_size_column = TRUE;
733   impl->icon_size = FALLBACK_ICON_SIZE;
734   impl->load_state = LOAD_EMPTY;
735   impl->reload_state = RELOAD_EMPTY;
736   impl->pending_select_files = NULL;
737   impl->location_mode = LOCATION_MODE_PATH_BAR;
738   impl->operation_mode = OPERATION_MODE_BROWSE;
739   impl->sort_column = MODEL_COL_NAME;
740   impl->sort_order = GTK_SORT_ASCENDING;
741   impl->recent_manager = gtk_recent_manager_get_default ();
742   impl->create_folders = TRUE;
743
744   gtk_orientable_set_orientation (GTK_ORIENTABLE (impl),
745                                   GTK_ORIENTATION_VERTICAL);
746   gtk_box_set_spacing (GTK_BOX (impl), 12);
747
748   set_file_system_backend (impl);
749
750   profile_end ("end", NULL);
751 }
752
753 /* Frees the data columns for the specified iter in the shortcuts model*/
754 static void
755 shortcuts_free_row_data (GtkFileChooserDefault *impl,
756                          GtkTreeIter           *iter)
757 {
758   gpointer col_data;
759   ShortcutType shortcut_type;
760   GCancellable *cancellable;
761
762   gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), iter,
763                       SHORTCUTS_COL_DATA, &col_data,
764                       SHORTCUTS_COL_TYPE, &shortcut_type,
765                       SHORTCUTS_COL_CANCELLABLE, &cancellable,
766                       -1);
767
768   if (cancellable)
769     {
770       g_cancellable_cancel (cancellable);
771       g_object_unref (cancellable);
772     }
773
774   if (!(shortcut_type == SHORTCUT_TYPE_FILE || 
775         shortcut_type == SHORTCUT_TYPE_VOLUME) ||
776       !col_data)
777     return;
778
779   if (shortcut_type == SHORTCUT_TYPE_VOLUME)
780     {
781       GtkFileSystemVolume *volume;
782
783       volume = col_data;
784       _gtk_file_system_volume_unref (volume);
785     }
786   if (shortcut_type == SHORTCUT_TYPE_FILE)
787     {
788       GFile *file;
789
790       file = col_data;
791       g_object_unref (file);
792     }
793 }
794
795 /* Frees all the data columns in the shortcuts model */
796 static void
797 shortcuts_free (GtkFileChooserDefault *impl)
798 {
799   GtkTreeIter iter;
800
801   if (!impl->shortcuts_model)
802     return;
803
804   if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (impl->shortcuts_model), &iter))
805     do
806       {
807         shortcuts_free_row_data (impl, &iter);
808       }
809     while (gtk_tree_model_iter_next (GTK_TREE_MODEL (impl->shortcuts_model), &iter));
810
811   g_object_unref (impl->shortcuts_model);
812   impl->shortcuts_model = NULL;
813 }
814
815 static void
816 pending_select_files_free (GtkFileChooserDefault *impl)
817 {
818   g_slist_foreach (impl->pending_select_files, (GFunc) g_object_unref, NULL);
819   g_slist_free (impl->pending_select_files);
820   impl->pending_select_files = NULL;
821 }
822
823 static void
824 pending_select_files_add (GtkFileChooserDefault *impl,
825                           GFile                 *file)
826 {
827   impl->pending_select_files =
828     g_slist_prepend (impl->pending_select_files, g_object_ref (file));
829 }
830
831 static void
832 gtk_file_chooser_default_finalize (GObject *object)
833 {
834   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (object);
835   GSList *l;
836
837   unset_file_system_backend (impl);
838
839   if (impl->shortcuts_pane_filter_model)
840     g_object_unref (impl->shortcuts_pane_filter_model);
841
842   shortcuts_free (impl);
843
844   g_free (impl->browse_files_last_selected_name);
845
846   for (l = impl->filters; l; l = l->next)
847     {
848       GtkFileFilter *filter;
849
850       filter = GTK_FILE_FILTER (l->data);
851       g_object_unref (filter);
852     }
853   g_slist_free (impl->filters);
854
855   if (impl->current_filter)
856     g_object_unref (impl->current_filter);
857
858   if (impl->current_volume_file)
859     g_object_unref (impl->current_volume_file);
860
861   if (impl->current_folder)
862     g_object_unref (impl->current_folder);
863
864   if (impl->preview_file)
865     g_object_unref (impl->preview_file);
866
867   if (impl->browse_path_bar_size_group)
868     g_object_unref (impl->browse_path_bar_size_group);
869
870   /* Free all the Models we have */
871   stop_loading_and_clear_list_model (impl, FALSE);
872   search_clear_model (impl, FALSE);
873   recent_clear_model (impl, FALSE);
874
875   /* stopping the load above should have cleared this */
876   g_assert (impl->load_timeout_id == 0);
877
878   g_free (impl->preview_display_name);
879
880   g_free (impl->edited_new_text);
881
882   G_OBJECT_CLASS (_gtk_file_chooser_default_parent_class)->finalize (object);
883 }
884
885 /* Shows an error dialog set as transient for the specified window */
886 static void
887 error_message_with_parent (GtkWindow  *parent,
888                            const char *msg,
889                            const char *detail)
890 {
891   GtkWidget *dialog;
892
893   dialog = gtk_message_dialog_new (parent,
894                                    GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
895                                    GTK_MESSAGE_ERROR,
896                                    GTK_BUTTONS_OK,
897                                    "%s",
898                                    msg);
899   gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
900                                             "%s", detail);
901
902   if (parent && gtk_window_has_group (parent))
903     gtk_window_group_add_window (gtk_window_get_group (parent),
904                                  GTK_WINDOW (dialog));
905
906   gtk_dialog_run (GTK_DIALOG (dialog));
907   gtk_widget_destroy (dialog);
908 }
909
910 /* Returns a toplevel GtkWindow, or NULL if none */
911 static GtkWindow *
912 get_toplevel (GtkWidget *widget)
913 {
914   GtkWidget *toplevel;
915
916   toplevel = gtk_widget_get_toplevel (widget);
917   if (!gtk_widget_is_toplevel (toplevel))
918     return NULL;
919   else
920     return GTK_WINDOW (toplevel);
921 }
922
923 /* Shows an error dialog for the file chooser */
924 static void
925 error_message (GtkFileChooserDefault *impl,
926                const char            *msg,
927                const char            *detail)
928 {
929   error_message_with_parent (get_toplevel (GTK_WIDGET (impl)), msg, detail);
930 }
931
932 /* Shows a simple error dialog relative to a path.  Frees the GError as well. */
933 static void
934 error_dialog (GtkFileChooserDefault *impl,
935               const char            *msg,
936               GFile                 *file,
937               GError                *error)
938 {
939   if (error)
940     {
941       char *uri = NULL;
942       char *text;
943
944       if (file)
945         uri = g_file_get_uri (file);
946       text = g_strdup_printf (msg, uri);
947       error_message (impl, text, error->message);
948       g_free (text);
949       g_free (uri);
950       g_error_free (error);
951     }
952 }
953
954 /* Displays an error message about not being able to get information for a file.
955  * Frees the GError as well.
956  */
957 static void
958 error_getting_info_dialog (GtkFileChooserDefault *impl,
959                            GFile                 *file,
960                            GError                *error)
961 {
962   error_dialog (impl,
963                 _("Could not retrieve information about the file"),
964                 file, error);
965 }
966
967 /* Shows an error dialog about not being able to add a bookmark */
968 static void
969 error_adding_bookmark_dialog (GtkFileChooserDefault *impl,
970                               GFile                 *file,
971                               GError                *error)
972 {
973   error_dialog (impl,
974                 _("Could not add a bookmark"),
975                 file, error);
976 }
977
978 /* Shows an error dialog about not being able to remove a bookmark */
979 static void
980 error_removing_bookmark_dialog (GtkFileChooserDefault *impl,
981                                 GFile                 *file,
982                                 GError                *error)
983 {
984   error_dialog (impl,
985                 _("Could not remove bookmark"),
986                 file, error);
987 }
988
989 /* Shows an error dialog about not being able to create a folder */
990 static void
991 error_creating_folder_dialog (GtkFileChooserDefault *impl,
992                               GFile                 *file,
993                               GError                *error)
994 {
995   error_dialog (impl, 
996                 _("The folder could not be created"), 
997                 file, error);
998 }
999
1000 /* Shows an error about not being able to create a folder because a file with
1001  * the same name is already there.
1002  */
1003 static void
1004 error_creating_folder_over_existing_file_dialog (GtkFileChooserDefault *impl,
1005                                                  GFile                 *file,
1006                                                  GError                *error)
1007 {
1008   error_dialog (impl,
1009                 _("The folder could not be created, as a file with the same "
1010                   "name already exists.  Try using a different name for the "
1011                   "folder, or rename the file first."),
1012                 file, error);
1013 }
1014
1015 static void
1016 error_with_file_under_nonfolder (GtkFileChooserDefault *impl,
1017                                  GFile *parent_file)
1018 {
1019   GError *error;
1020
1021   error = NULL;
1022   g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_NOT_DIRECTORY,
1023                        _("You need to choose a valid filename."));
1024
1025   error_dialog (impl,
1026                 _("Cannot create a file under %s as it is not a folder"),
1027                 parent_file, error);
1028 }
1029
1030 /* Shows an error about not being able to select a folder because a file with
1031  * the same name is already there.
1032  */
1033 static void
1034 error_selecting_folder_over_existing_file_dialog (GtkFileChooserDefault *impl,
1035                                                   GFile                 *file)
1036 {
1037   error_dialog (impl,
1038                 _("You may only select folders.  The item that you selected is not a folder; "
1039                   "try using a different item."),
1040                 file, NULL);
1041 }
1042
1043 /* Shows an error dialog about not being able to create a filename */
1044 static void
1045 error_building_filename_dialog (GtkFileChooserDefault *impl,
1046                                 GError                *error)
1047 {
1048   error_dialog (impl, _("Invalid file name"), 
1049                 NULL, error);
1050 }
1051
1052 /* Shows an error dialog when we cannot switch to a folder */
1053 static void
1054 error_changing_folder_dialog (GtkFileChooserDefault *impl,
1055                               GFile                 *file,
1056                               GError                *error)
1057 {
1058   error_dialog (impl, _("The folder contents could not be displayed"),
1059                 file, error);
1060 }
1061
1062 /* Changes folders, displaying an error dialog if this fails */
1063 static gboolean
1064 change_folder_and_display_error (GtkFileChooserDefault *impl,
1065                                  GFile                 *file,
1066                                  gboolean               clear_entry)
1067 {
1068   GError *error;
1069   gboolean result;
1070
1071   g_return_val_if_fail (G_IS_FILE (file), FALSE);
1072
1073   /* We copy the path because of this case:
1074    *
1075    * list_row_activated()
1076    *   fetches path from model; path belongs to the model (*)
1077    *   calls change_folder_and_display_error()
1078    *     calls gtk_file_chooser_set_current_folder_file()
1079    *       changing folders fails, sets model to NULL, thus freeing the path in (*)
1080    */
1081
1082   error = NULL;
1083   result = gtk_file_chooser_default_update_current_folder (GTK_FILE_CHOOSER (impl), file, TRUE, clear_entry, &error);
1084
1085   if (!result)
1086     error_changing_folder_dialog (impl, file, error);
1087
1088   return result;
1089 }
1090
1091 static void
1092 emit_default_size_changed (GtkFileChooserDefault *impl)
1093 {
1094   profile_msg ("    emit default-size-changed start", NULL);
1095   g_signal_emit_by_name (impl, "default-size-changed");
1096   profile_msg ("    emit default-size-changed end", NULL);
1097 }
1098
1099 static void
1100 update_preview_widget_visibility (GtkFileChooserDefault *impl)
1101 {
1102   if (impl->use_preview_label)
1103     {
1104       if (!impl->preview_label)
1105         {
1106           impl->preview_label = gtk_label_new (impl->preview_display_name);
1107           gtk_box_pack_start (GTK_BOX (impl->preview_box), impl->preview_label, FALSE, FALSE, 0);
1108           gtk_box_reorder_child (GTK_BOX (impl->preview_box), impl->preview_label, 0);
1109           gtk_label_set_ellipsize (GTK_LABEL (impl->preview_label), PANGO_ELLIPSIZE_MIDDLE);
1110           gtk_widget_show (impl->preview_label);
1111         }
1112     }
1113   else
1114     {
1115       if (impl->preview_label)
1116         {
1117           gtk_widget_destroy (impl->preview_label);
1118           impl->preview_label = NULL;
1119         }
1120     }
1121
1122   if (impl->preview_widget_active && impl->preview_widget)
1123     gtk_widget_show (impl->preview_box);
1124   else
1125     gtk_widget_hide (impl->preview_box);
1126
1127   if (!gtk_widget_get_mapped (GTK_WIDGET (impl)))
1128     emit_default_size_changed (impl);
1129 }
1130
1131 static void
1132 set_preview_widget (GtkFileChooserDefault *impl,
1133                     GtkWidget             *preview_widget)
1134 {
1135   if (preview_widget == impl->preview_widget)
1136     return;
1137
1138   if (impl->preview_widget)
1139     gtk_container_remove (GTK_CONTAINER (impl->preview_box),
1140                           impl->preview_widget);
1141
1142   impl->preview_widget = preview_widget;
1143   if (impl->preview_widget)
1144     {
1145       gtk_widget_show (impl->preview_widget);
1146       gtk_box_pack_start (GTK_BOX (impl->preview_box), impl->preview_widget, TRUE, TRUE, 0);
1147       gtk_box_reorder_child (GTK_BOX (impl->preview_box),
1148                              impl->preview_widget,
1149                              (impl->use_preview_label && impl->preview_label) ? 1 : 0);
1150     }
1151
1152   update_preview_widget_visibility (impl);
1153 }
1154
1155 /* Renders a "Search" icon at an appropriate size for a tree view */
1156 static GdkPixbuf *
1157 render_search_icon (GtkFileChooserDefault *impl)
1158 {
1159   return gtk_widget_render_icon_pixbuf (GTK_WIDGET (impl), GTK_STOCK_FIND, GTK_ICON_SIZE_MENU);
1160 }
1161
1162 static GdkPixbuf *
1163 render_recent_icon (GtkFileChooserDefault *impl)
1164 {
1165   GtkIconTheme *theme;
1166   GdkPixbuf *retval;
1167
1168   if (gtk_widget_has_screen (GTK_WIDGET (impl)))
1169     theme = gtk_icon_theme_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (impl)));
1170   else
1171     theme = gtk_icon_theme_get_default ();
1172
1173   retval = gtk_icon_theme_load_icon (theme, "document-open-recent",
1174                                      impl->icon_size, 0,
1175                                      NULL);
1176
1177   /* fallback */
1178   if (!retval)
1179     retval = gtk_widget_render_icon_pixbuf (GTK_WIDGET (impl), GTK_STOCK_FILE, GTK_ICON_SIZE_MENU);
1180
1181   return retval;
1182 }
1183
1184
1185 /* Re-reads all the icons for the shortcuts, used when the theme changes */
1186 struct ReloadIconsData
1187 {
1188   GtkFileChooserDefault *impl;
1189   GtkTreeRowReference *row_ref;
1190 };
1191
1192 static void
1193 shortcuts_reload_icons_get_info_cb (GCancellable *cancellable,
1194                                     GFileInfo    *info,
1195                                     const GError *error,
1196                                     gpointer      user_data)
1197 {
1198   GdkPixbuf *pixbuf;
1199   GtkTreeIter iter;
1200   GtkTreePath *path;
1201   gboolean cancelled = g_cancellable_is_cancelled (cancellable);
1202   struct ReloadIconsData *data = user_data;
1203
1204   if (!g_slist_find (data->impl->reload_icon_cancellables, cancellable))
1205     goto out;
1206
1207   data->impl->reload_icon_cancellables = g_slist_remove (data->impl->reload_icon_cancellables, cancellable);
1208
1209   if (cancelled || error)
1210     goto out;
1211
1212   pixbuf = _gtk_file_info_render_icon (info, GTK_WIDGET (data->impl), data->impl->icon_size);
1213
1214   path = gtk_tree_row_reference_get_path (data->row_ref);
1215   if (path)
1216     {
1217       gtk_tree_model_get_iter (GTK_TREE_MODEL (data->impl->shortcuts_model), &iter, path);
1218       gtk_list_store_set (data->impl->shortcuts_model, &iter,
1219                           SHORTCUTS_COL_PIXBUF, pixbuf,
1220                           -1);
1221       gtk_tree_path_free (path);
1222     }
1223
1224   if (pixbuf)
1225     g_object_unref (pixbuf);
1226
1227 out:
1228   gtk_tree_row_reference_free (data->row_ref);
1229   g_object_unref (data->impl);
1230   g_free (data);
1231
1232   g_object_unref (cancellable);
1233 }
1234
1235 static void
1236 shortcuts_reload_icons (GtkFileChooserDefault *impl)
1237 {
1238   GSList *l;
1239   GtkTreeIter iter;
1240
1241   profile_start ("start", NULL);
1242
1243   if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (impl->shortcuts_model), &iter))
1244     goto out;
1245
1246   for (l = impl->reload_icon_cancellables; l; l = l->next)
1247     {
1248       GCancellable *cancellable = G_CANCELLABLE (l->data);
1249       g_cancellable_cancel (cancellable);
1250     }
1251   g_slist_free (impl->reload_icon_cancellables);
1252   impl->reload_icon_cancellables = NULL;
1253
1254   do
1255     {
1256       gpointer data;
1257       ShortcutType shortcut_type;
1258       gboolean pixbuf_visible;
1259       GdkPixbuf *pixbuf;
1260
1261       gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
1262                           SHORTCUTS_COL_DATA, &data,
1263                           SHORTCUTS_COL_TYPE, &shortcut_type,
1264                           SHORTCUTS_COL_PIXBUF_VISIBLE, &pixbuf_visible,
1265                           -1);
1266
1267       pixbuf = NULL;
1268       if (pixbuf_visible)
1269         {
1270           if (shortcut_type == SHORTCUT_TYPE_VOLUME)
1271             {
1272               GtkFileSystemVolume *volume;
1273
1274               volume = data;
1275               pixbuf = _gtk_file_system_volume_render_icon (volume, GTK_WIDGET (impl),
1276                                                             impl->icon_size, NULL);
1277             }
1278           else if (shortcut_type == SHORTCUT_TYPE_FILE)
1279             {
1280               if (g_file_is_native (G_FILE (data)))
1281                 {
1282                   GFile *file;
1283                   struct ReloadIconsData *info;
1284                   GtkTreePath *tree_path;
1285                   GCancellable *cancellable;
1286
1287                   file = data;
1288
1289                   info = g_new0 (struct ReloadIconsData, 1);
1290                   info->impl = g_object_ref (impl);
1291                   tree_path = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->shortcuts_model), &iter);
1292                   info->row_ref = gtk_tree_row_reference_new (GTK_TREE_MODEL (impl->shortcuts_model), tree_path);
1293                   gtk_tree_path_free (tree_path);
1294
1295                   cancellable = _gtk_file_system_get_info (impl->file_system, file,
1296                                                            "standard::icon",
1297                                                            shortcuts_reload_icons_get_info_cb,
1298                                                            info);
1299                   impl->reload_icon_cancellables = g_slist_append (impl->reload_icon_cancellables, cancellable);
1300                 }
1301               else
1302                 {
1303                   GtkIconTheme *icon_theme;
1304
1305                   /* Don't call get_info for remote paths to avoid latency and
1306                    * auth dialogs.
1307                    * If we switch to a better bookmarks file format (XBEL), we
1308                    * should use mime info to get a better icon.
1309                    */
1310                   icon_theme = gtk_icon_theme_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (impl)));
1311                   pixbuf = gtk_icon_theme_load_icon (icon_theme, "folder-remote", 
1312                                                      impl->icon_size, 0, NULL);
1313                 }
1314             }
1315           else if (shortcut_type == SHORTCUT_TYPE_SEARCH)
1316             {
1317               pixbuf = render_search_icon (impl);
1318             }
1319           else if (shortcut_type == SHORTCUT_TYPE_RECENT)
1320             {
1321               pixbuf = render_recent_icon (impl);
1322             }
1323
1324           gtk_list_store_set (impl->shortcuts_model, &iter,
1325                               SHORTCUTS_COL_PIXBUF, pixbuf,
1326                               -1);
1327
1328           if (pixbuf)
1329             g_object_unref (pixbuf);
1330
1331         }
1332     }
1333   while (gtk_tree_model_iter_next (GTK_TREE_MODEL (impl->shortcuts_model),&iter));
1334
1335  out:
1336
1337   profile_end ("end", NULL);
1338 }
1339
1340 static void 
1341 shortcuts_find_folder (GtkFileChooserDefault *impl,
1342                        GFile                 *folder)
1343 {
1344   GtkTreeSelection *selection;
1345   int pos;
1346   GtkTreePath *path;
1347
1348   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view));
1349
1350   g_assert (folder != NULL);
1351   pos = shortcut_find_position (impl, folder);
1352   if (pos == -1)
1353     {
1354       gtk_tree_selection_unselect_all (selection);
1355       return;
1356     }
1357
1358   path = gtk_tree_path_new_from_indices (pos, -1);
1359   gtk_tree_selection_select_path (selection, path);
1360   gtk_tree_path_free (path);
1361 }
1362
1363 /* If a shortcut corresponds to the current folder, selects it */
1364 static void
1365 shortcuts_find_current_folder (GtkFileChooserDefault *impl)
1366 {
1367   shortcuts_find_folder (impl, impl->current_folder);
1368 }
1369
1370 /* Removes the specified number of rows from the shortcuts list */
1371 static void
1372 shortcuts_remove_rows (GtkFileChooserDefault *impl,
1373                        int                    start_row,
1374                        int                    n_rows)
1375 {
1376   GtkTreePath *path;
1377
1378   path = gtk_tree_path_new_from_indices (start_row, -1);
1379
1380   for (; n_rows; n_rows--)
1381     {
1382       GtkTreeIter iter;
1383
1384       if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->shortcuts_model), &iter, path))
1385         g_assert_not_reached ();
1386
1387       shortcuts_free_row_data (impl, &iter);
1388       gtk_list_store_remove (impl->shortcuts_model, &iter);
1389     }
1390
1391   gtk_tree_path_free (path);
1392 }
1393
1394 static void
1395 shortcuts_update_count (GtkFileChooserDefault *impl,
1396                         ShortcutsIndex         type,
1397                         gint                   value)
1398 {
1399   switch (type)
1400     {
1401       case SHORTCUTS_HOME:
1402         if (value < 0)
1403           impl->has_home = FALSE;
1404         else
1405           impl->has_home = TRUE;
1406         break;
1407
1408       case SHORTCUTS_DESKTOP:
1409         if (value < 0)
1410           impl->has_desktop = FALSE;
1411         else
1412           impl->has_desktop = TRUE;
1413         break;
1414
1415       case SHORTCUTS_VOLUMES:
1416         impl->num_volumes += value;
1417         break;
1418
1419       case SHORTCUTS_SHORTCUTS:
1420         impl->num_shortcuts += value;
1421         break;
1422
1423       case SHORTCUTS_BOOKMARKS:
1424         impl->num_bookmarks += value;
1425         break;
1426
1427       case SHORTCUTS_CURRENT_FOLDER:
1428         if (value < 0)
1429           impl->shortcuts_current_folder_active = FALSE;
1430         else
1431           impl->shortcuts_current_folder_active = TRUE;
1432         break;
1433
1434       default:
1435         /* nothing */
1436         break;
1437     }
1438 }
1439
1440 struct ShortcutsInsertRequest
1441 {
1442   GtkFileChooserDefault *impl;
1443   GFile *file;
1444   int pos;
1445   char *label_copy;
1446   GtkTreeRowReference *row_ref;
1447   ShortcutsIndex type;
1448   gboolean name_only;
1449   gboolean removable;
1450 };
1451
1452 static void
1453 get_file_info_finished (GCancellable *cancellable,
1454                         GFileInfo    *info,
1455                         const GError *error,
1456                         gpointer      data)
1457 {
1458   gint pos = -1;
1459   gboolean cancelled = g_cancellable_is_cancelled (cancellable);
1460   GdkPixbuf *pixbuf;
1461   GtkTreePath *path;
1462   GtkTreeIter iter;
1463   GCancellable *model_cancellable = NULL;
1464   struct ShortcutsInsertRequest *request = data;
1465
1466   path = gtk_tree_row_reference_get_path (request->row_ref);
1467   if (!path)
1468     /* Handle doesn't exist anymore in the model */
1469     goto out;
1470
1471   pos = gtk_tree_path_get_indices (path)[0];
1472   gtk_tree_model_get_iter (GTK_TREE_MODEL (request->impl->shortcuts_model),
1473                            &iter, path);
1474   gtk_tree_path_free (path);
1475
1476   /* validate cancellable, else goto out */
1477   gtk_tree_model_get (GTK_TREE_MODEL (request->impl->shortcuts_model), &iter,
1478                       SHORTCUTS_COL_CANCELLABLE, &model_cancellable,
1479                       -1);
1480   if (cancellable != model_cancellable)
1481     goto out;
1482
1483   /* set the cancellable to NULL in the model (we unref later on) */
1484   gtk_list_store_set (request->impl->shortcuts_model, &iter,
1485                       SHORTCUTS_COL_CANCELLABLE, NULL,
1486                       -1);
1487
1488   if (cancelled)
1489     goto out;
1490
1491   if (!info)
1492     {
1493       gtk_list_store_remove (request->impl->shortcuts_model, &iter);
1494       shortcuts_update_count (request->impl, request->type, -1);
1495
1496       if (request->type == SHORTCUTS_HOME)
1497         {
1498           GFile *home;
1499
1500           home = g_file_new_for_path (g_get_home_dir ());
1501           error_getting_info_dialog (request->impl, home, g_error_copy (error));
1502           g_object_unref (home);
1503         }
1504       else if (request->type == SHORTCUTS_CURRENT_FOLDER)
1505         {
1506           /* Remove the current folder separator */
1507           gint separator_pos = shortcuts_get_index (request->impl, SHORTCUTS_CURRENT_FOLDER_SEPARATOR);
1508           shortcuts_remove_rows (request->impl, separator_pos, 1);
1509         }
1510
1511       goto out;
1512     }
1513   
1514   if (!request->label_copy)
1515     request->label_copy = g_strdup (g_file_info_get_display_name (info));
1516   pixbuf = _gtk_file_info_render_icon (info, GTK_WIDGET (request->impl),
1517                                        request->impl->icon_size);
1518
1519   gtk_list_store_set (request->impl->shortcuts_model, &iter,
1520                       SHORTCUTS_COL_PIXBUF, pixbuf,
1521                       SHORTCUTS_COL_PIXBUF_VISIBLE, TRUE,
1522                       SHORTCUTS_COL_NAME, request->label_copy,
1523                       SHORTCUTS_COL_TYPE, SHORTCUT_TYPE_FILE,
1524                       SHORTCUTS_COL_REMOVABLE, request->removable,
1525                       -1);
1526
1527   if (request->impl->shortcuts_pane_filter_model)
1528     gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (request->impl->shortcuts_pane_filter_model));
1529
1530   if (pixbuf)
1531     g_object_unref (pixbuf);
1532
1533 out:
1534   g_object_unref (request->impl);
1535   g_object_unref (request->file);
1536   gtk_tree_row_reference_free (request->row_ref);
1537   g_free (request->label_copy);
1538   g_free (request);
1539
1540   if (model_cancellable)
1541     g_object_unref (model_cancellable);
1542 }
1543
1544 /* FIXME: GtkFileSystem needs a function to split a remote path
1545  * into hostname and path components, or maybe just have a 
1546  * gtk_file_system_path_get_display_name().
1547  *
1548  * This function is also used in gtkfilechooserbutton.c
1549  */
1550 gchar *
1551 _gtk_file_chooser_label_for_file (GFile *file)
1552 {
1553   const gchar *path, *start, *end, *p;
1554   gchar *uri, *host, *label;
1555
1556   uri = g_file_get_uri (file);
1557
1558   start = strstr (uri, "://");
1559   if (start)
1560     {
1561       start += 3;
1562       path = strchr (start, '/');
1563       if (path)
1564         end = path;
1565       else
1566         {
1567           end = uri + strlen (uri);
1568           path = "/";
1569         }
1570
1571       /* strip username */
1572       p = strchr (start, '@');
1573       if (p && p < end)
1574         start = p + 1;
1575   
1576       p = strchr (start, ':');
1577       if (p && p < end)
1578         end = p;
1579   
1580       host = g_strndup (start, end - start);
1581   
1582       /* Translators: the first string is a path and the second string 
1583        * is a hostname. Nautilus and the panel contain the same string 
1584        * to translate. 
1585        */
1586       label = g_strdup_printf (_("%1$s on %2$s"), path, host);
1587   
1588       g_free (host);
1589     }
1590   else
1591     {
1592       label = g_strdup (uri);
1593     }
1594   
1595   g_free (uri);
1596
1597   return label;
1598 }
1599
1600 /* Inserts a path in the shortcuts tree, making a copy of it; alternatively,
1601  * inserts a volume.  A position of -1 indicates the end of the tree.
1602  */
1603 static void
1604 shortcuts_insert_file (GtkFileChooserDefault *impl,
1605                        int                    pos,
1606                        ShortcutType           shortcut_type,
1607                        GtkFileSystemVolume   *volume,
1608                        GFile                 *file,
1609                        const char            *label,
1610                        gboolean               removable,
1611                        ShortcutsIndex         type)
1612 {
1613   char *label_copy;
1614   GdkPixbuf *pixbuf = NULL;
1615   gpointer data = NULL;
1616   GtkTreeIter iter;
1617   GtkIconTheme *icon_theme;
1618
1619   profile_start ("start shortcut", NULL);
1620
1621   if (shortcut_type == SHORTCUT_TYPE_VOLUME)
1622     {
1623       data = volume;
1624       label_copy = _gtk_file_system_volume_get_display_name (volume);
1625       pixbuf = _gtk_file_system_volume_render_icon (volume, GTK_WIDGET (impl),
1626                                                     impl->icon_size, NULL);
1627     }
1628   else if (shortcut_type == SHORTCUT_TYPE_FILE)
1629     {
1630       if (g_file_is_native (file))
1631         {
1632           struct ShortcutsInsertRequest *request;
1633           GCancellable *cancellable;
1634           GtkTreePath *p;
1635
1636           request = g_new0 (struct ShortcutsInsertRequest, 1);
1637           request->impl = g_object_ref (impl);
1638           request->file = g_object_ref (file);
1639           request->name_only = TRUE;
1640           request->removable = removable;
1641           request->pos = pos;
1642           request->type = type;
1643           if (label)
1644             request->label_copy = g_strdup (label);
1645
1646           if (pos == -1)
1647             gtk_list_store_append (impl->shortcuts_model, &iter);
1648           else
1649             gtk_list_store_insert (impl->shortcuts_model, &iter, pos);
1650
1651           p = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->shortcuts_model), &iter);
1652           request->row_ref = gtk_tree_row_reference_new (GTK_TREE_MODEL (impl->shortcuts_model), p);
1653           gtk_tree_path_free (p);
1654
1655           cancellable = _gtk_file_system_get_info (request->impl->file_system, request->file,
1656                                                    "standard::is-hidden,standard::is-backup,standard::display-name,standard::icon",
1657                                                    get_file_info_finished, request);
1658
1659           gtk_list_store_set (impl->shortcuts_model, &iter,
1660                               SHORTCUTS_COL_DATA, g_object_ref (file),
1661                               SHORTCUTS_COL_TYPE, SHORTCUT_TYPE_FILE,
1662                               SHORTCUTS_COL_CANCELLABLE, cancellable,
1663                               -1);
1664
1665           shortcuts_update_count (impl, type, 1);
1666
1667           return;
1668         }
1669       else
1670         {
1671           /* Don't call get_info for remote paths to avoid latency and
1672            * auth dialogs.
1673            */
1674           data = g_object_ref (file);
1675           if (label)
1676             label_copy = g_strdup (label);
1677           else
1678             label_copy = _gtk_file_chooser_label_for_file (file);
1679
1680           /* If we switch to a better bookmarks file format (XBEL), we
1681            * should use mime info to get a better icon.
1682            */
1683           icon_theme = gtk_icon_theme_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (impl)));
1684           pixbuf = gtk_icon_theme_load_icon (icon_theme, "folder-remote", 
1685                                              impl->icon_size, 0, NULL);
1686         }
1687     }
1688    else
1689     {
1690       g_assert_not_reached ();
1691
1692       return;
1693     }
1694
1695   if (pos == -1)
1696     gtk_list_store_append (impl->shortcuts_model, &iter);
1697   else
1698     gtk_list_store_insert (impl->shortcuts_model, &iter, pos);
1699
1700   shortcuts_update_count (impl, type, 1);
1701
1702   gtk_list_store_set (impl->shortcuts_model, &iter,
1703                       SHORTCUTS_COL_PIXBUF, pixbuf,
1704                       SHORTCUTS_COL_PIXBUF_VISIBLE, TRUE,
1705                       SHORTCUTS_COL_NAME, label_copy,
1706                       SHORTCUTS_COL_DATA, data,
1707                       SHORTCUTS_COL_TYPE, shortcut_type,
1708                       SHORTCUTS_COL_REMOVABLE, removable,
1709                       SHORTCUTS_COL_CANCELLABLE, NULL,
1710                       -1);
1711
1712   if (impl->shortcuts_pane_filter_model)
1713     gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (impl->shortcuts_pane_filter_model));
1714
1715   g_free (label_copy);
1716
1717   if (pixbuf)
1718     g_object_unref (pixbuf);
1719
1720   profile_end ("end", NULL);
1721 }
1722
1723 static void
1724 shortcuts_append_search (GtkFileChooserDefault *impl)
1725 {
1726   GdkPixbuf *pixbuf;
1727   GtkTreeIter iter;
1728
1729   pixbuf = render_search_icon (impl);
1730
1731   gtk_list_store_append (impl->shortcuts_model, &iter);
1732   gtk_list_store_set (impl->shortcuts_model, &iter,
1733                       SHORTCUTS_COL_PIXBUF, pixbuf,
1734                       SHORTCUTS_COL_PIXBUF_VISIBLE, TRUE,
1735                       SHORTCUTS_COL_NAME, _("Search"),
1736                       SHORTCUTS_COL_DATA, NULL,
1737                       SHORTCUTS_COL_TYPE, SHORTCUT_TYPE_SEARCH,
1738                       SHORTCUTS_COL_REMOVABLE, FALSE,
1739                       -1);
1740
1741   if (pixbuf)
1742     g_object_unref (pixbuf);
1743
1744   impl->has_search = TRUE;
1745 }
1746
1747 static void
1748 shortcuts_append_recent (GtkFileChooserDefault *impl)
1749 {
1750   GdkPixbuf *pixbuf;
1751   GtkTreeIter iter;
1752
1753   pixbuf = render_recent_icon (impl);
1754
1755   gtk_list_store_append (impl->shortcuts_model, &iter);
1756   gtk_list_store_set (impl->shortcuts_model, &iter,
1757                       SHORTCUTS_COL_PIXBUF, pixbuf,
1758                       SHORTCUTS_COL_PIXBUF_VISIBLE, TRUE,
1759                       SHORTCUTS_COL_NAME, _("Recently Used"),
1760                       SHORTCUTS_COL_DATA, NULL,
1761                       SHORTCUTS_COL_TYPE, SHORTCUT_TYPE_RECENT,
1762                       SHORTCUTS_COL_REMOVABLE, FALSE,
1763                       -1);
1764   
1765   if (pixbuf)
1766     g_object_unref (pixbuf);
1767 }
1768
1769 /* Appends an item for the user's home directory to the shortcuts model */
1770 static void
1771 shortcuts_append_home (GtkFileChooserDefault *impl)
1772 {
1773   const char *home_path;
1774   GFile *home;
1775
1776   profile_start ("start", NULL);
1777
1778   home_path = g_get_home_dir ();
1779   if (home_path == NULL)
1780     {
1781       profile_end ("end - no home directory!?", NULL);
1782       return;
1783     }
1784
1785   home = g_file_new_for_path (home_path);
1786   shortcuts_insert_file (impl, -1, SHORTCUT_TYPE_FILE, NULL, home, NULL, FALSE, SHORTCUTS_HOME);
1787   impl->has_home = TRUE;
1788
1789   g_object_unref (home);
1790
1791   profile_end ("end", NULL);
1792 }
1793
1794 /* Appends the ~/Desktop directory to the shortcuts model */
1795 static void
1796 shortcuts_append_desktop (GtkFileChooserDefault *impl)
1797 {
1798   const char *name;
1799   GFile *file;
1800
1801   profile_start ("start", NULL);
1802
1803   name = g_get_user_special_dir (G_USER_DIRECTORY_DESKTOP);
1804   /* "To disable a directory, point it to the homedir."
1805    * See http://freedesktop.org/wiki/Software/xdg-user-dirs
1806    **/
1807   if (!g_strcmp0 (name, g_get_home_dir ()))
1808     {
1809       profile_end ("end", NULL);
1810       return;
1811     }
1812
1813   file = g_file_new_for_path (name);
1814   shortcuts_insert_file (impl, -1, SHORTCUT_TYPE_FILE, NULL, file, _("Desktop"), FALSE, SHORTCUTS_DESKTOP);
1815   impl->has_desktop = TRUE;
1816
1817   /* We do not actually pop up an error dialog if there is no desktop directory
1818    * because some people may really not want to have one.
1819    */
1820
1821   g_object_unref (file);
1822
1823   profile_end ("end", NULL);
1824 }
1825
1826 /* Appends a list of GFile to the shortcuts model; returns how many were inserted */
1827 static int
1828 shortcuts_append_bookmarks (GtkFileChooserDefault *impl,
1829                             GSList                *bookmarks)
1830 {
1831   int start_row;
1832   int num_inserted;
1833   gchar *label;
1834
1835   profile_start ("start", NULL);
1836
1837   start_row = shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS_SEPARATOR) + 1;
1838   num_inserted = 0;
1839
1840   for (; bookmarks; bookmarks = bookmarks->next)
1841     {
1842       GFile *file;
1843
1844       file = bookmarks->data;
1845
1846       if (impl->local_only && !g_file_is_native (file))
1847         continue;
1848
1849       if (shortcut_find_position (impl, file) != -1)
1850         continue;
1851
1852       label = _gtk_file_system_get_bookmark_label (impl->file_system, file);
1853
1854       shortcuts_insert_file (impl, start_row + num_inserted, SHORTCUT_TYPE_FILE, NULL, file, label, TRUE, SHORTCUTS_BOOKMARKS);
1855       g_free (label);
1856
1857       num_inserted++;
1858     }
1859
1860   profile_end ("end", NULL);
1861
1862   return num_inserted;
1863 }
1864
1865 /* Returns the index for the corresponding item in the shortcuts bar */
1866 static int
1867 shortcuts_get_index (GtkFileChooserDefault *impl,
1868                      ShortcutsIndex         where)
1869 {
1870   int n;
1871
1872   n = 0;
1873   
1874   if (where == SHORTCUTS_SEARCH)
1875     goto out;
1876
1877   n += impl->has_search ? 1 : 0;
1878
1879   if (where == SHORTCUTS_RECENT)
1880     goto out;
1881
1882   n += 1; /* we always have the recently-used item */
1883
1884   if (where == SHORTCUTS_RECENT_SEPARATOR)
1885     goto out;
1886
1887   n += 1; /* we always have the separator after the recently-used item */
1888
1889   if (where == SHORTCUTS_HOME)
1890     goto out;
1891
1892   n += impl->has_home ? 1 : 0;
1893
1894   if (where == SHORTCUTS_DESKTOP)
1895     goto out;
1896
1897   n += impl->has_desktop ? 1 : 0;
1898
1899   if (where == SHORTCUTS_VOLUMES)
1900     goto out;
1901
1902   n += impl->num_volumes;
1903
1904   if (where == SHORTCUTS_SHORTCUTS)
1905     goto out;
1906
1907   n += impl->num_shortcuts;
1908
1909   if (where == SHORTCUTS_BOOKMARKS_SEPARATOR)
1910     goto out;
1911
1912   /* If there are no bookmarks there won't be a separator */
1913   n += (impl->num_bookmarks > 0) ? 1 : 0;
1914
1915   if (where == SHORTCUTS_BOOKMARKS)
1916     goto out;
1917
1918   n += impl->num_bookmarks;
1919
1920   if (where == SHORTCUTS_CURRENT_FOLDER_SEPARATOR)
1921     goto out;
1922
1923   n += 1;
1924
1925   if (where == SHORTCUTS_CURRENT_FOLDER)
1926     goto out;
1927
1928   g_assert_not_reached ();
1929
1930  out:
1931
1932   return n;
1933 }
1934
1935 /* Adds all the file system volumes to the shortcuts model */
1936 static void
1937 shortcuts_add_volumes (GtkFileChooserDefault *impl)
1938 {
1939   int start_row;
1940   GSList *list, *l;
1941   int n;
1942   gboolean old_changing_folders;
1943
1944   profile_start ("start", NULL);
1945
1946   old_changing_folders = impl->changing_folder;
1947   impl->changing_folder = TRUE;
1948
1949   start_row = shortcuts_get_index (impl, SHORTCUTS_VOLUMES);
1950   shortcuts_remove_rows (impl, start_row, impl->num_volumes);
1951   impl->num_volumes = 0;
1952
1953   list = _gtk_file_system_list_volumes (impl->file_system);
1954
1955   n = 0;
1956
1957   for (l = list; l; l = l->next)
1958     {
1959       GtkFileSystemVolume *volume;
1960
1961       volume = l->data;
1962
1963       if (impl->local_only)
1964         {
1965           if (_gtk_file_system_volume_is_mounted (volume))
1966             {
1967               GFile *base_file;
1968               gboolean base_is_native = TRUE;
1969
1970               base_file = _gtk_file_system_volume_get_root (volume);
1971               if (base_file != NULL)
1972                 {
1973                   base_is_native = g_file_is_native (base_file);
1974                   g_object_unref (base_file);
1975                 }
1976
1977               if (!base_is_native)
1978                 continue;
1979             }
1980         }
1981
1982       shortcuts_insert_file (impl,
1983                              start_row + n,
1984                              SHORTCUT_TYPE_VOLUME,
1985                              _gtk_file_system_volume_ref (volume),
1986                              NULL,
1987                              NULL,
1988                              FALSE,
1989                              SHORTCUTS_VOLUMES);
1990       n++;
1991     }
1992
1993   impl->num_volumes = n;
1994   g_slist_free (list);
1995
1996   if (impl->shortcuts_pane_filter_model)
1997     gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (impl->shortcuts_pane_filter_model));
1998
1999   impl->changing_folder = old_changing_folders;
2000
2001   profile_end ("end", NULL);
2002 }
2003
2004 /* Inserts a separator node in the shortcuts list */
2005 static void
2006 shortcuts_insert_separator (GtkFileChooserDefault *impl,
2007                             ShortcutsIndex         where)
2008 {
2009   GtkTreeIter iter;
2010
2011   g_assert (where == SHORTCUTS_RECENT_SEPARATOR || 
2012             where == SHORTCUTS_BOOKMARKS_SEPARATOR || 
2013             where == SHORTCUTS_CURRENT_FOLDER_SEPARATOR);
2014
2015   gtk_list_store_insert (impl->shortcuts_model, &iter,
2016                          shortcuts_get_index (impl, where));
2017   gtk_list_store_set (impl->shortcuts_model, &iter,
2018                       SHORTCUTS_COL_PIXBUF, NULL,
2019                       SHORTCUTS_COL_PIXBUF_VISIBLE, FALSE,
2020                       SHORTCUTS_COL_NAME, NULL,
2021                       SHORTCUTS_COL_DATA, NULL,
2022                       SHORTCUTS_COL_TYPE, SHORTCUT_TYPE_SEPARATOR,
2023                       -1);
2024 }
2025
2026 /* Updates the list of bookmarks */
2027 static void
2028 shortcuts_add_bookmarks (GtkFileChooserDefault *impl)
2029 {
2030   GSList *bookmarks;
2031   gboolean old_changing_folders;
2032   GtkTreeIter iter;
2033   GFile *list_selected = NULL;
2034   ShortcutType shortcut_type;
2035   gpointer col_data;
2036
2037   profile_start ("start", NULL);
2038         
2039   old_changing_folders = impl->changing_folder;
2040   impl->changing_folder = TRUE;
2041
2042   if (shortcuts_get_selected (impl, &iter))
2043     {
2044       gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), 
2045                           &iter, 
2046                           SHORTCUTS_COL_DATA, &col_data,
2047                           SHORTCUTS_COL_TYPE, &shortcut_type,
2048                           -1);
2049
2050       if (col_data && shortcut_type == SHORTCUT_TYPE_FILE)
2051         list_selected = g_object_ref (col_data);
2052     }
2053
2054   if (impl->num_bookmarks > 0)
2055     shortcuts_remove_rows (impl,
2056                            shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS_SEPARATOR),
2057                            impl->num_bookmarks + 1);
2058
2059   impl->num_bookmarks = 0;
2060   shortcuts_insert_separator (impl, SHORTCUTS_BOOKMARKS_SEPARATOR);
2061
2062   bookmarks = _gtk_file_system_list_bookmarks (impl->file_system);
2063   shortcuts_append_bookmarks (impl, bookmarks);
2064   g_slist_foreach (bookmarks, (GFunc) g_object_unref, NULL);
2065   g_slist_free (bookmarks);
2066
2067   if (impl->num_bookmarks == 0)
2068     shortcuts_remove_rows (impl, shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS_SEPARATOR), 1);
2069
2070   if (impl->shortcuts_pane_filter_model)
2071     gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (impl->shortcuts_pane_filter_model));
2072
2073   if (list_selected)
2074     {
2075       shortcuts_find_folder (impl, list_selected);
2076       g_object_unref (list_selected);
2077     }
2078
2079   impl->changing_folder = old_changing_folders;
2080
2081   profile_end ("end", NULL);
2082 }
2083
2084 /* Appends a separator and a row to the shortcuts list for the current folder */
2085 static void
2086 shortcuts_add_current_folder (GtkFileChooserDefault *impl)
2087 {
2088   int pos;
2089
2090   g_assert (!impl->shortcuts_current_folder_active);
2091
2092   g_assert (impl->current_folder != NULL);
2093
2094   pos = shortcut_find_position (impl, impl->current_folder);
2095   if (pos == -1)
2096     {
2097       GtkFileSystemVolume *volume;
2098       GFile *base_file;
2099
2100       /* Separator */
2101       shortcuts_insert_separator (impl, SHORTCUTS_CURRENT_FOLDER_SEPARATOR);
2102
2103       /* Item */
2104       pos = shortcuts_get_index (impl, SHORTCUTS_CURRENT_FOLDER);
2105
2106       volume = _gtk_file_system_get_volume_for_file (impl->file_system, impl->current_folder);
2107       if (volume)
2108         base_file = _gtk_file_system_volume_get_root (volume);
2109       else
2110         base_file = NULL;
2111
2112       if (base_file && g_file_equal (base_file, impl->current_folder))
2113         shortcuts_insert_file (impl, pos, SHORTCUT_TYPE_VOLUME, volume, NULL, NULL, FALSE, SHORTCUTS_CURRENT_FOLDER);
2114       else
2115         shortcuts_insert_file (impl, pos, SHORTCUT_TYPE_FILE, NULL, impl->current_folder, NULL, FALSE, SHORTCUTS_CURRENT_FOLDER);
2116
2117       if (base_file)
2118         g_object_unref (base_file);
2119     }
2120 }
2121
2122 /* Updates the current folder row in the shortcuts model */
2123 static void
2124 shortcuts_update_current_folder (GtkFileChooserDefault *impl)
2125 {
2126   int pos;
2127
2128   pos = shortcuts_get_index (impl, SHORTCUTS_CURRENT_FOLDER_SEPARATOR);
2129
2130   if (impl->shortcuts_current_folder_active)
2131     {
2132       shortcuts_remove_rows (impl, pos, 2);
2133       impl->shortcuts_current_folder_active = FALSE;
2134     }
2135
2136   shortcuts_add_current_folder (impl);
2137 }
2138
2139 /* Filter function used for the shortcuts filter model */
2140 static gboolean
2141 shortcuts_pane_filter_cb (GtkTreeModel *model,
2142                           GtkTreeIter  *iter,
2143                           gpointer      data)
2144 {
2145   GtkFileChooserDefault *impl;
2146   GtkTreePath *path;
2147   int pos;
2148
2149   impl = GTK_FILE_CHOOSER_DEFAULT (data);
2150
2151   path = gtk_tree_model_get_path (model, iter);
2152   if (!path)
2153     return FALSE;
2154
2155   pos = *gtk_tree_path_get_indices (path);
2156   gtk_tree_path_free (path);
2157
2158   return (pos < shortcuts_get_index (impl, SHORTCUTS_CURRENT_FOLDER_SEPARATOR));
2159 }
2160
2161 /* Creates the list model for shortcuts */
2162 static void
2163 shortcuts_model_create (GtkFileChooserDefault *impl)
2164 {
2165   /* Keep this order in sync with the SHORCUTS_COL_* enum values */
2166   impl->shortcuts_model = gtk_list_store_new (SHORTCUTS_COL_NUM_COLUMNS,
2167                                               GDK_TYPE_PIXBUF,  /* pixbuf */
2168                                               G_TYPE_STRING,    /* name */
2169                                               G_TYPE_POINTER,   /* path or volume */
2170                                               G_TYPE_INT,       /* ShortcutType */
2171                                               G_TYPE_BOOLEAN,   /* removable */
2172                                               G_TYPE_BOOLEAN,   /* pixbuf cell visibility */
2173                                               G_TYPE_POINTER);  /* GCancellable */
2174
2175   shortcuts_append_search (impl);
2176
2177   if (impl->recent_manager)
2178     {
2179       shortcuts_append_recent (impl);
2180       shortcuts_insert_separator (impl, SHORTCUTS_RECENT_SEPARATOR);
2181     }
2182   
2183   if (impl->file_system)
2184     {
2185       shortcuts_append_home (impl);
2186       shortcuts_append_desktop (impl);
2187       shortcuts_add_volumes (impl);
2188     }
2189
2190   impl->shortcuts_pane_filter_model = shortcuts_pane_model_filter_new (impl,
2191                                                                        GTK_TREE_MODEL (impl->shortcuts_model),
2192                                                                        NULL);
2193
2194   gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (impl->shortcuts_pane_filter_model),
2195                                           shortcuts_pane_filter_cb,
2196                                           impl,
2197                                           NULL);
2198 }
2199
2200 /* Callback used when the "New Folder" button is clicked */
2201 static void
2202 new_folder_button_clicked (GtkButton             *button,
2203                            GtkFileChooserDefault *impl)
2204 {
2205   GtkTreeIter iter;
2206   GtkTreePath *path;
2207
2208   if (!impl->browse_files_model)
2209     return; /* FIXME: this sucks.  Disable the New Folder button or something. */
2210
2211   /* Prevent button from being clicked twice */
2212   gtk_widget_set_sensitive (impl->browse_new_folder_button, FALSE);
2213
2214   _gtk_file_system_model_add_editable (impl->browse_files_model, &iter);
2215
2216   path = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->browse_files_model), &iter);
2217   gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (impl->browse_files_tree_view),
2218                                 path, impl->list_name_column,
2219                                 FALSE, 0.0, 0.0);
2220
2221   g_object_set (impl->list_name_renderer, "editable", TRUE, NULL);
2222   gtk_tree_view_set_cursor (GTK_TREE_VIEW (impl->browse_files_tree_view),
2223                             path,
2224                             impl->list_name_column,
2225                             TRUE);
2226
2227   gtk_tree_path_free (path);
2228 }
2229
2230 static GSource *
2231 add_idle_while_impl_is_alive (GtkFileChooserDefault *impl, GCallback callback)
2232 {
2233   GSource *source;
2234
2235   source = g_idle_source_new ();
2236   g_source_set_closure (source,
2237                         g_cclosure_new_object (callback, G_OBJECT (impl)));
2238   g_source_attach (source, NULL);
2239
2240   return source;
2241 }
2242
2243 /* Idle handler for creating a new folder after editing its name cell, or for
2244  * canceling the editing.
2245  */
2246 static gboolean
2247 edited_idle_cb (GtkFileChooserDefault *impl)
2248 {
2249   GDK_THREADS_ENTER ();
2250   
2251   g_source_destroy (impl->edited_idle);
2252   impl->edited_idle = NULL;
2253
2254   _gtk_file_system_model_remove_editable (impl->browse_files_model);
2255   g_object_set (impl->list_name_renderer, "editable", FALSE, NULL);
2256
2257   gtk_widget_set_sensitive (impl->browse_new_folder_button, TRUE);
2258
2259   if (impl->edited_new_text /* not cancelled? */
2260       && (strlen (impl->edited_new_text) != 0)
2261       && (strcmp (impl->edited_new_text, DEFAULT_NEW_FOLDER_NAME) != 0)) /* Don't create folder if name is empty or has not been edited */
2262     {
2263       GError *error = NULL;
2264       GFile *file;
2265
2266       file = g_file_get_child_for_display_name (impl->current_folder,
2267                                                 impl->edited_new_text,
2268                                                 &error);
2269       if (file)
2270         {
2271           GError *error = NULL;
2272
2273           if (g_file_make_directory (file, NULL, &error))
2274             change_folder_and_display_error (impl, file, FALSE);
2275           else
2276             error_creating_folder_dialog (impl, file, error);
2277
2278           g_object_unref (file);
2279         }
2280       else
2281         error_creating_folder_dialog (impl, file, error);
2282
2283       g_free (impl->edited_new_text);
2284       impl->edited_new_text = NULL;
2285     }
2286
2287   GDK_THREADS_LEAVE ();
2288
2289   return FALSE;
2290 }
2291
2292 static void
2293 queue_edited_idle (GtkFileChooserDefault *impl,
2294                    const gchar           *new_text)
2295 {
2296   /* We create the folder in an idle handler so that we don't modify the tree
2297    * just now.
2298    */
2299
2300   if (!impl->edited_idle)
2301     impl->edited_idle = add_idle_while_impl_is_alive (impl, G_CALLBACK (edited_idle_cb));
2302
2303   g_free (impl->edited_new_text);
2304   impl->edited_new_text = g_strdup (new_text);
2305 }
2306
2307 /* Callback used from the text cell renderer when the new folder is named */
2308 static void
2309 renderer_edited_cb (GtkCellRendererText   *cell_renderer_text,
2310                     const gchar           *path,
2311                     const gchar           *new_text,
2312                     GtkFileChooserDefault *impl)
2313 {
2314   /* work around bug #154921 */
2315   g_object_set (cell_renderer_text, 
2316                 "mode", GTK_CELL_RENDERER_MODE_INERT, NULL);
2317   queue_edited_idle (impl, new_text);
2318 }
2319
2320 /* Callback used from the text cell renderer when the new folder edition gets
2321  * canceled.
2322  */
2323 static void
2324 renderer_editing_canceled_cb (GtkCellRendererText   *cell_renderer_text,
2325                               GtkFileChooserDefault *impl)
2326 {
2327   /* work around bug #154921 */
2328   g_object_set (cell_renderer_text, 
2329                 "mode", GTK_CELL_RENDERER_MODE_INERT, NULL);
2330   queue_edited_idle (impl, NULL);
2331 }
2332
2333 /* Creates the widgets for the filter combo box */
2334 static GtkWidget *
2335 filter_create (GtkFileChooserDefault *impl)
2336 {
2337   GtkCellRenderer *cell;
2338   GList           *cells;
2339
2340   impl->filter_combo = gtk_combo_box_text_new ();
2341   gtk_combo_box_set_focus_on_click (GTK_COMBO_BOX (impl->filter_combo), FALSE);
2342
2343   /* Get the combo's text renderer and set ellipsize parameters */
2344   cells = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (impl->filter_combo));
2345   g_assert (cells);
2346   cell = cells->data;
2347
2348   g_object_set (G_OBJECT (cell),
2349                 "ellipsize", PANGO_ELLIPSIZE_END,
2350                 NULL);
2351
2352   g_list_free (cells);
2353
2354   g_signal_connect (impl->filter_combo, "changed",
2355                     G_CALLBACK (filter_combo_changed), impl);
2356
2357   gtk_widget_set_tooltip_text (impl->filter_combo,
2358                                _("Select which types of files are shown"));
2359
2360   return impl->filter_combo;
2361 }
2362
2363 static GtkWidget *
2364 toolbutton_new (GtkFileChooserDefault *impl,
2365                 GIcon                 *icon,
2366                 gboolean               sensitive,
2367                 gboolean               show,
2368                 GCallback              callback)
2369 {
2370   GtkToolItem *item;
2371   GtkWidget *image;
2372
2373   item = gtk_tool_button_new (NULL, NULL);
2374   image = gtk_image_new_from_gicon (icon, GTK_ICON_SIZE_SMALL_TOOLBAR);
2375   gtk_widget_show (image);
2376   gtk_tool_button_set_icon_widget (GTK_TOOL_BUTTON (item), image);
2377
2378   gtk_widget_set_sensitive (GTK_WIDGET (item), sensitive);
2379   g_signal_connect (item, "clicked", callback, impl);
2380
2381   if (show)
2382     gtk_widget_show (GTK_WIDGET (item));
2383
2384   return GTK_WIDGET (item);
2385 }
2386
2387 /* Looks for a path among the shortcuts; returns its index or -1 if it doesn't exist */
2388 static int
2389 shortcut_find_position (GtkFileChooserDefault *impl,
2390                         GFile                 *file)
2391 {
2392   GtkTreeIter iter;
2393   int i;
2394   int current_folder_separator_idx;
2395
2396   if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (impl->shortcuts_model), &iter))
2397     return -1;
2398
2399   current_folder_separator_idx = shortcuts_get_index (impl, SHORTCUTS_CURRENT_FOLDER_SEPARATOR);
2400
2401 #if 0
2402   /* FIXME: is this still needed? */
2403   if (current_folder_separator_idx >= impl->shortcuts_model->length)
2404     return -1;
2405 #endif
2406
2407   for (i = 0; i < current_folder_separator_idx; i++)
2408     {
2409       gpointer col_data;
2410       ShortcutType shortcut_type;
2411
2412       gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
2413                           SHORTCUTS_COL_DATA, &col_data,
2414                           SHORTCUTS_COL_TYPE, &shortcut_type,
2415                           -1);
2416
2417       if (col_data)
2418         {
2419           if (shortcut_type == SHORTCUT_TYPE_VOLUME)
2420             {
2421               GtkFileSystemVolume *volume;
2422               GFile *base_file;
2423               gboolean exists;
2424
2425               volume = col_data;
2426               base_file = _gtk_file_system_volume_get_root (volume);
2427
2428               exists = base_file && g_file_equal (file, base_file);
2429
2430               if (base_file)
2431                 g_object_unref (base_file);
2432
2433               if (exists)
2434                 return i;
2435             }
2436           else if (shortcut_type == SHORTCUT_TYPE_FILE)
2437             {
2438               GFile *model_file;
2439
2440               model_file = col_data;
2441
2442               if (model_file && g_file_equal (model_file, file))
2443                 return i;
2444             }
2445         }
2446
2447       if (i < current_folder_separator_idx - 1)
2448         {
2449           if (!gtk_tree_model_iter_next (GTK_TREE_MODEL (impl->shortcuts_model), &iter))
2450             g_assert_not_reached ();
2451         }
2452     }
2453
2454   return -1;
2455 }
2456
2457 /* Tries to add a bookmark from a path name */
2458 static gboolean
2459 shortcuts_add_bookmark_from_file (GtkFileChooserDefault *impl,
2460                                   GFile                 *file,
2461                                   int                    pos)
2462 {
2463   GError *error;
2464
2465   g_return_val_if_fail (G_IS_FILE (file), FALSE);
2466
2467   if (shortcut_find_position (impl, file) != -1)
2468     return FALSE;
2469
2470   error = NULL;
2471   if (!_gtk_file_system_insert_bookmark (impl->file_system, file, pos, &error))
2472     {
2473       error_adding_bookmark_dialog (impl, file, error);
2474       return FALSE;
2475     }
2476
2477   return TRUE;
2478 }
2479
2480 static void
2481 add_bookmark_foreach_cb (GtkTreeModel *model,
2482                          GtkTreePath  *path,
2483                          GtkTreeIter  *iter,
2484                          gpointer      data)
2485 {
2486   GtkFileChooserDefault *impl;
2487   GFile *file;
2488
2489   impl = (GtkFileChooserDefault *) data;
2490
2491   gtk_tree_model_get (model, iter,
2492                       MODEL_COL_FILE, &file,
2493                       -1);
2494
2495   shortcuts_add_bookmark_from_file (impl, file, -1);
2496
2497   g_object_unref (file);
2498 }
2499
2500 /* Adds a bookmark from the currently selected item in the file list */
2501 static void
2502 bookmarks_add_selected_folder (GtkFileChooserDefault *impl)
2503 {
2504   GtkTreeSelection *selection;
2505
2506   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
2507
2508   if (gtk_tree_selection_count_selected_rows (selection) == 0)
2509     shortcuts_add_bookmark_from_file (impl, impl->current_folder, -1);
2510   else
2511     gtk_tree_selection_selected_foreach (selection,
2512                                          add_bookmark_foreach_cb,
2513                                          impl);
2514 }
2515
2516 /* Callback used when the "Add bookmark" button is clicked */
2517 static void
2518 add_bookmark_button_clicked_cb (GtkButton *button,
2519                                 GtkFileChooserDefault *impl)
2520 {
2521   bookmarks_add_selected_folder (impl);
2522 }
2523
2524 /* Returns TRUE plus an iter in the shortcuts_model if a row is selected;
2525  * returns FALSE if no shortcut is selected.
2526  */
2527 static gboolean
2528 shortcuts_get_selected (GtkFileChooserDefault *impl,
2529                         GtkTreeIter           *iter)
2530 {
2531   GtkTreeSelection *selection;
2532   GtkTreeIter parent_iter;
2533
2534   if (!impl->browse_shortcuts_tree_view)
2535     return FALSE;
2536
2537   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view));
2538
2539   if (!gtk_tree_selection_get_selected (selection, NULL, &parent_iter))
2540     return FALSE;
2541
2542   gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (impl->shortcuts_pane_filter_model),
2543                                                     iter,
2544                                                     &parent_iter);
2545   return TRUE;
2546 }
2547
2548 /* Removes the selected bookmarks */
2549 static void
2550 remove_selected_bookmarks (GtkFileChooserDefault *impl)
2551 {
2552   GtkTreeIter iter;
2553   gpointer col_data;
2554   GFile *file;
2555   gboolean removable;
2556   GError *error;
2557
2558   if (!shortcuts_get_selected (impl, &iter))
2559     return;
2560
2561   gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
2562                       SHORTCUTS_COL_DATA, &col_data,
2563                       SHORTCUTS_COL_REMOVABLE, &removable,
2564                       -1);
2565
2566   if (!removable)
2567     return;
2568
2569   g_assert (col_data != NULL);
2570
2571   file = col_data;
2572
2573   error = NULL;
2574   if (!_gtk_file_system_remove_bookmark (impl->file_system, file, &error))
2575     error_removing_bookmark_dialog (impl, file, error);
2576 }
2577
2578 /* Callback used when the "Remove bookmark" button is clicked */
2579 static void
2580 remove_bookmark_button_clicked_cb (GtkButton *button,
2581                                    GtkFileChooserDefault *impl)
2582 {
2583   remove_selected_bookmarks (impl);
2584 }
2585
2586 struct selection_check_closure {
2587   GtkFileChooserDefault *impl;
2588   int num_selected;
2589   gboolean all_files;
2590   gboolean all_folders;
2591 };
2592
2593 /* Used from gtk_tree_selection_selected_foreach() */
2594 static void
2595 selection_check_foreach_cb (GtkTreeModel *model,
2596                             GtkTreePath  *path,
2597                             GtkTreeIter  *iter,
2598                             gpointer      data)
2599 {
2600   struct selection_check_closure *closure;
2601   gboolean is_folder;
2602   GFile *file;
2603
2604   gtk_tree_model_get (model, iter,
2605                       MODEL_COL_FILE, &file,
2606                       MODEL_COL_IS_FOLDER, &is_folder,
2607                       -1);
2608
2609   if (file == NULL)
2610     return;
2611
2612   g_object_unref (file);
2613
2614   closure = data;
2615   closure->num_selected++;
2616
2617   closure->all_folders = closure->all_folders && is_folder;
2618   closure->all_files = closure->all_files && !is_folder;
2619 }
2620
2621 /* Checks whether the selected items in the file list are all files or all folders */
2622 static void
2623 selection_check (GtkFileChooserDefault *impl,
2624                  gint                  *num_selected,
2625                  gboolean              *all_files,
2626                  gboolean              *all_folders)
2627 {
2628   struct selection_check_closure closure;
2629   GtkTreeSelection *selection;
2630
2631   closure.impl = impl;
2632   closure.num_selected = 0;
2633   closure.all_files = TRUE;
2634   closure.all_folders = TRUE;
2635
2636   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
2637   gtk_tree_selection_selected_foreach (selection,
2638                                        selection_check_foreach_cb,
2639                                        &closure);
2640
2641   g_assert (closure.num_selected == 0 || !(closure.all_files && closure.all_folders));
2642
2643   if (num_selected)
2644     *num_selected = closure.num_selected;
2645
2646   if (all_files)
2647     *all_files = closure.all_files;
2648
2649   if (all_folders)
2650     *all_folders = closure.all_folders;
2651 }
2652
2653 struct get_selected_file_closure {
2654   GtkFileChooserDefault *impl;
2655   GFile *file;
2656 };
2657
2658 static void
2659 get_selected_file_foreach_cb (GtkTreeModel *model,
2660                               GtkTreePath  *path,
2661                               GtkTreeIter  *iter,
2662                               gpointer      data)
2663 {
2664   struct get_selected_file_closure *closure = data;
2665
2666   if (closure->file)
2667     {
2668       /* Just in case this function gets run more than once with a multiple selection; we only care about one file */
2669       g_object_unref (closure->file);
2670       closure->file = NULL;
2671     }
2672
2673   gtk_tree_model_get (model, iter,
2674                       MODEL_COL_FILE, &closure->file, /* this will give us a reffed file */
2675                       -1);
2676 }
2677
2678 /* Returns a selected path from the file list */
2679 static GFile *
2680 get_selected_file (GtkFileChooserDefault *impl)
2681 {
2682   struct get_selected_file_closure closure;
2683   GtkTreeSelection *selection;
2684
2685   closure.impl = impl;
2686   closure.file = NULL;
2687
2688   selection =  gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
2689   gtk_tree_selection_selected_foreach (selection,
2690                                        get_selected_file_foreach_cb,
2691                                        &closure);
2692
2693   return closure.file;
2694 }
2695
2696 typedef struct {
2697   GtkFileChooserDefault *impl;
2698   gchar *tip;
2699 } UpdateTooltipData;
2700
2701 static void 
2702 update_tooltip (GtkTreeModel      *model,
2703                 GtkTreePath       *path,
2704                 GtkTreeIter       *iter,
2705                 gpointer           data)
2706 {
2707   UpdateTooltipData *udata = data;
2708
2709   if (udata->tip == NULL)
2710     {
2711       gchar *display_name;
2712
2713       gtk_tree_model_get (model, iter,
2714                           MODEL_COL_NAME, &display_name,
2715                           -1);
2716
2717       udata->tip = g_strdup_printf (_("Add the folder '%s' to the bookmarks"),
2718                                     display_name);
2719       g_free (display_name);
2720     }
2721 }
2722
2723
2724 /* Sensitize the "add bookmark" button if all the selected items are folders, or
2725  * if there are no selected items *and* the current folder is not in the
2726  * bookmarks list.  De-sensitize the button otherwise.
2727  */
2728 static void
2729 bookmarks_check_add_sensitivity (GtkFileChooserDefault *impl)
2730 {
2731   gint num_selected;
2732   gboolean all_folders;
2733   gboolean active;
2734   gchar *tip;
2735
2736   selection_check (impl, &num_selected, NULL, &all_folders);
2737
2738   if (num_selected == 0)
2739     active = (impl->current_folder != NULL) && (shortcut_find_position (impl, impl->current_folder) == -1);
2740   else if (num_selected == 1)
2741     {
2742       GFile *file;
2743
2744       file = get_selected_file (impl);
2745       active = file && all_folders && (shortcut_find_position (impl, file) == -1);
2746       if (file)
2747         g_object_unref (file);
2748     }
2749   else
2750     active = all_folders;
2751
2752   gtk_widget_set_sensitive (impl->browse_shortcuts_add_button, active);
2753
2754   if (impl->browse_files_popup_menu_add_shortcut_item)
2755     gtk_widget_set_sensitive (impl->browse_files_popup_menu_add_shortcut_item,
2756                               (num_selected == 0) ? FALSE : active);
2757
2758   if (active)
2759     {
2760       if (num_selected == 0)
2761         tip = g_strdup_printf (_("Add the current folder to the bookmarks"));
2762       else if (num_selected > 1)
2763         tip = g_strdup_printf (_("Add the selected folders to the bookmarks"));
2764       else
2765         {
2766           GtkTreeSelection *selection;
2767           UpdateTooltipData data;
2768
2769           selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
2770           data.impl = impl;
2771           data.tip = NULL;
2772           gtk_tree_selection_selected_foreach (selection, update_tooltip, &data);
2773           tip = data.tip;
2774         }
2775
2776       gtk_widget_set_tooltip_text (impl->browse_shortcuts_add_button, tip);
2777       g_free (tip);
2778     }
2779 }
2780
2781 /* Sets the sensitivity of the "remove bookmark" button depending on whether a
2782  * bookmark row is selected in the shortcuts tree.
2783  */
2784 static void
2785 bookmarks_check_remove_sensitivity (GtkFileChooserDefault *impl)
2786 {
2787   GtkTreeIter iter;
2788   gboolean removable = FALSE;
2789   gchar *name = NULL;
2790   gchar *tip;
2791   
2792   if (shortcuts_get_selected (impl, &iter))
2793     {
2794       gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
2795                           SHORTCUTS_COL_REMOVABLE, &removable,
2796                           SHORTCUTS_COL_NAME, &name,
2797                           -1);
2798       gtk_widget_set_sensitive (impl->browse_shortcuts_remove_button, removable);
2799
2800       if (removable)
2801         tip = g_strdup_printf (_("Remove the bookmark '%s'"), name);
2802       else
2803         tip = g_strdup_printf (_("Bookmark '%s' cannot be removed"), name);
2804
2805       gtk_widget_set_tooltip_text (impl->browse_shortcuts_remove_button, tip);
2806       g_free (tip);
2807     }
2808   else
2809     gtk_widget_set_tooltip_text (impl->browse_shortcuts_remove_button,
2810                                  _("Remove the selected bookmark"));
2811   g_free (name);
2812 }
2813
2814 static void
2815 shortcuts_check_popup_sensitivity (GtkFileChooserDefault *impl)
2816 {
2817   GtkTreeIter iter;
2818   gboolean removable = FALSE;
2819
2820   if (impl->browse_shortcuts_popup_menu == NULL)
2821     return;
2822
2823   if (shortcuts_get_selected (impl, &iter))
2824     gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
2825                         SHORTCUTS_COL_REMOVABLE, &removable,
2826                         -1);
2827
2828   gtk_widget_set_sensitive (impl->browse_shortcuts_popup_menu_remove_item, removable);
2829   gtk_widget_set_sensitive (impl->browse_shortcuts_popup_menu_rename_item, removable);
2830 }
2831
2832 /* GtkWidget::drag-begin handler for the shortcuts list. */
2833 static void
2834 shortcuts_drag_begin_cb (GtkWidget             *widget,
2835                          GdkDragContext        *context,
2836                          GtkFileChooserDefault *impl)
2837 {
2838 #if 0
2839   impl->shortcuts_drag_context = g_object_ref (context);
2840 #endif
2841 }
2842
2843 #if 0
2844 /* Removes the idle handler for outside drags */
2845 static void
2846 shortcuts_cancel_drag_outside_idle (GtkFileChooserDefault *impl)
2847 {
2848   if (!impl->shortcuts_drag_outside_idle)
2849     return;
2850
2851   g_source_destroy (impl->shortcuts_drag_outside_idle);
2852   impl->shortcuts_drag_outside_idle = NULL;
2853 }
2854 #endif
2855
2856 /* GtkWidget::drag-end handler for the shortcuts list. */
2857 static void
2858 shortcuts_drag_end_cb (GtkWidget             *widget,
2859                        GdkDragContext        *context,
2860                        GtkFileChooserDefault *impl)
2861 {
2862 #if 0
2863   g_object_unref (impl->shortcuts_drag_context);
2864
2865   shortcuts_cancel_drag_outside_idle (impl);
2866
2867   if (!impl->shortcuts_drag_outside)
2868     return;
2869
2870   gtk_button_clicked (GTK_BUTTON (impl->browse_shortcuts_remove_button));
2871
2872   impl->shortcuts_drag_outside = FALSE;
2873 #endif
2874 }
2875
2876 /* GtkWidget::drag-data-delete handler for the shortcuts list. */
2877 static void
2878 shortcuts_drag_data_delete_cb (GtkWidget             *widget,
2879                                GdkDragContext        *context,
2880                                GtkFileChooserDefault *impl)
2881 {
2882   g_signal_stop_emission_by_name (widget, "drag-data-delete");
2883 }
2884
2885 /* GtkWidget::drag-leave handler for the shortcuts list.  We unhighlight the
2886  * drop position.
2887  */
2888 static void
2889 shortcuts_drag_leave_cb (GtkWidget             *widget,
2890                          GdkDragContext        *context,
2891                          guint                  time_,
2892                          GtkFileChooserDefault *impl)
2893 {
2894 #if 0
2895   if (gtk_drag_get_source_widget (context) == widget && !impl->shortcuts_drag_outside_idle)
2896     {
2897       impl->shortcuts_drag_outside_idle = add_idle_while_impl_is_alive (impl, G_CALLBACK (shortcuts_drag_outside_idle_cb));
2898     }
2899 #endif
2900
2901   gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view),
2902                                    NULL,
2903                                    GTK_TREE_VIEW_DROP_BEFORE);
2904
2905   g_signal_stop_emission_by_name (widget, "drag-leave");
2906 }
2907
2908 /* Computes the appropriate row and position for dropping */
2909 static void
2910 shortcuts_compute_drop_position (GtkFileChooserDefault   *impl,
2911                                  int                      x,
2912                                  int                      y,
2913                                  GtkTreePath            **path,
2914                                  GtkTreeViewDropPosition *pos)
2915 {
2916   GtkTreeView *tree_view;
2917   GtkTreeViewColumn *column;
2918   int cell_y;
2919   GdkRectangle cell;
2920   int row;
2921   int bookmarks_index;
2922   int header_height = 0;
2923
2924   tree_view = GTK_TREE_VIEW (impl->browse_shortcuts_tree_view);
2925
2926   if (gtk_tree_view_get_headers_visible (tree_view))
2927     header_height = _gtk_tree_view_get_header_height (tree_view);
2928
2929   bookmarks_index = shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS);
2930
2931   if (!gtk_tree_view_get_path_at_pos (tree_view,
2932                                       x,
2933                                       y - header_height,
2934                                       path,
2935                                       &column,
2936                                       NULL,
2937                                       &cell_y))
2938     {
2939       row = bookmarks_index + impl->num_bookmarks - 1;
2940       *path = gtk_tree_path_new_from_indices (row, -1);
2941       *pos = GTK_TREE_VIEW_DROP_AFTER;
2942       return;
2943     }
2944
2945   row = *gtk_tree_path_get_indices (*path);
2946   gtk_tree_view_get_background_area (tree_view, *path, column, &cell);
2947   gtk_tree_path_free (*path);
2948
2949   if (row < bookmarks_index)
2950     {
2951       row = bookmarks_index;
2952       *pos = GTK_TREE_VIEW_DROP_BEFORE;
2953     }
2954   else if (row > bookmarks_index + impl->num_bookmarks - 1)
2955     {
2956       row = bookmarks_index + impl->num_bookmarks - 1;
2957       *pos = GTK_TREE_VIEW_DROP_AFTER;
2958     }
2959   else
2960     {
2961       if (cell_y < cell.height / 2)
2962         *pos = GTK_TREE_VIEW_DROP_BEFORE;
2963       else
2964         *pos = GTK_TREE_VIEW_DROP_AFTER;
2965     }
2966
2967   *path = gtk_tree_path_new_from_indices (row, -1);
2968 }
2969
2970 /* GtkWidget::drag-motion handler for the shortcuts list.  We basically
2971  * implement the destination side of DnD by hand, due to limitations in
2972  * GtkTreeView's DnD API.
2973  */
2974 static gboolean
2975 shortcuts_drag_motion_cb (GtkWidget             *widget,
2976                           GdkDragContext        *context,
2977                           gint                   x,
2978                           gint                   y,
2979                           guint                  time_,
2980                           GtkFileChooserDefault *impl)
2981 {
2982   GtkTreePath *path;
2983   GtkTreeViewDropPosition pos;
2984   GdkDragAction action;
2985
2986 #if 0
2987   if (gtk_drag_get_source_widget (context) == widget)
2988     {
2989       shortcuts_cancel_drag_outside_idle (impl);
2990
2991       if (impl->shortcuts_drag_outside)
2992         {
2993           shortcuts_drag_set_delete_cursor (impl, FALSE);
2994           impl->shortcuts_drag_outside = FALSE;
2995         }
2996     }
2997 #endif
2998
2999   if (gdk_drag_context_get_suggested_action (context) == GDK_ACTION_COPY ||
3000       (gdk_drag_context_get_actions (context) & GDK_ACTION_COPY) != 0)
3001     action = GDK_ACTION_COPY;
3002   else if (gdk_drag_context_get_suggested_action (context) == GDK_ACTION_MOVE ||
3003            (gdk_drag_context_get_actions (context) & GDK_ACTION_MOVE) != 0)
3004     action = GDK_ACTION_MOVE;
3005   else
3006     {
3007       action = 0;
3008       goto out;
3009     }
3010
3011   shortcuts_compute_drop_position (impl, x, y, &path, &pos);
3012   gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view), path, pos);
3013   gtk_tree_path_free (path);
3014
3015  out:
3016
3017   g_signal_stop_emission_by_name (widget, "drag-motion");
3018
3019   if (action != 0)
3020     {
3021       gdk_drag_status (context, action, time_);
3022       return TRUE;
3023     }
3024   else
3025     return FALSE;
3026 }
3027
3028 /* GtkWidget::drag-drop handler for the shortcuts list. */
3029 static gboolean
3030 shortcuts_drag_drop_cb (GtkWidget             *widget,
3031                         GdkDragContext        *context,
3032                         gint                   x,
3033                         gint                   y,
3034                         guint                  time_,
3035                         GtkFileChooserDefault *impl)
3036 {
3037 #if 0
3038   shortcuts_cancel_drag_outside_idle (impl);
3039 #endif
3040
3041   g_signal_stop_emission_by_name (widget, "drag-drop");
3042   return TRUE;
3043 }
3044
3045 /* Parses a "text/uri-list" string and inserts its URIs as bookmarks */
3046 static void
3047 shortcuts_drop_uris (GtkFileChooserDefault *impl,
3048                      GtkSelectionData      *selection_data,
3049                      int                    position)
3050 {
3051   gchar **uris;
3052   gint i;
3053
3054   uris = gtk_selection_data_get_uris (selection_data);
3055   if (!uris)
3056     return;
3057
3058   for (i = 0; uris[i]; i++)
3059     {
3060       char *uri;
3061       GFile *file;
3062
3063       uri = uris[i];
3064       file = g_file_new_for_uri (uri);
3065
3066       if (shortcuts_add_bookmark_from_file (impl, file, position))
3067         position++;
3068
3069       g_object_unref (file);
3070     }
3071
3072   g_strfreev (uris);
3073 }
3074
3075 /* Reorders the selected bookmark to the specified position */
3076 static void
3077 shortcuts_reorder (GtkFileChooserDefault *impl,
3078                    int                    new_position)
3079 {
3080   GtkTreeIter iter;
3081   gpointer col_data;
3082   ShortcutType shortcut_type;
3083   GtkTreePath *path;
3084   int old_position;
3085   int bookmarks_index;
3086   GFile *file;
3087   GError *error;
3088   gchar *name = NULL;
3089
3090   /* Get the selected path */
3091
3092   if (!shortcuts_get_selected (impl, &iter))
3093     g_assert_not_reached ();
3094
3095   path = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->shortcuts_model), &iter);
3096   old_position = *gtk_tree_path_get_indices (path);
3097   gtk_tree_path_free (path);
3098
3099   bookmarks_index = shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS);
3100   old_position -= bookmarks_index;
3101   g_assert (old_position >= 0 && old_position < impl->num_bookmarks);
3102
3103   gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
3104                       SHORTCUTS_COL_NAME, &name,
3105                       SHORTCUTS_COL_DATA, &col_data,
3106                       SHORTCUTS_COL_TYPE, &shortcut_type,
3107                       -1);
3108   g_assert (col_data != NULL);
3109   g_assert (shortcut_type == SHORTCUT_TYPE_FILE);
3110   
3111   file = col_data;
3112   g_object_ref (file); /* removal below will free file, so we need a new ref */
3113
3114   /* Remove the path from the old position and insert it in the new one */
3115
3116   if (new_position > old_position)
3117     new_position--;
3118
3119   if (old_position == new_position)
3120     goto out;
3121
3122   error = NULL;
3123   if (_gtk_file_system_remove_bookmark (impl->file_system, file, &error))
3124     {
3125       shortcuts_add_bookmark_from_file (impl, file, new_position);
3126       _gtk_file_system_set_bookmark_label (impl->file_system, file, name);
3127     }
3128   else
3129     error_adding_bookmark_dialog (impl, file, error);
3130
3131  out:
3132
3133   g_object_unref (file);
3134   g_free (name);
3135 }
3136
3137 /* Callback used when we get the drag data for the bookmarks list.  We add the
3138  * received URIs as bookmarks if they are folders.
3139  */
3140 static void
3141 shortcuts_drag_data_received_cb (GtkWidget          *widget,
3142                                  GdkDragContext     *context,
3143                                  gint                x,
3144                                  gint                y,
3145                                  GtkSelectionData   *selection_data,
3146                                  guint               info,
3147                                  guint               time_,
3148                                  gpointer            data)
3149 {
3150   GtkFileChooserDefault *impl;
3151   GtkTreePath *tree_path;
3152   GtkTreeViewDropPosition tree_pos;
3153   GdkAtom target;
3154   int position;
3155   int bookmarks_index;
3156
3157   impl = GTK_FILE_CHOOSER_DEFAULT (data);
3158
3159   /* Compute position */
3160
3161   bookmarks_index = shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS);
3162
3163   shortcuts_compute_drop_position (impl, x, y, &tree_path, &tree_pos);
3164   position = *gtk_tree_path_get_indices (tree_path);
3165   gtk_tree_path_free (tree_path);
3166
3167   if (tree_pos == GTK_TREE_VIEW_DROP_AFTER)
3168     position++;
3169
3170   g_assert (position >= bookmarks_index);
3171   position -= bookmarks_index;
3172
3173   target = gtk_selection_data_get_target (selection_data);
3174
3175   if (gtk_targets_include_uri (&target, 1))
3176     shortcuts_drop_uris (impl, selection_data, position);
3177   else if (target == gdk_atom_intern_static_string ("GTK_TREE_MODEL_ROW"))
3178     shortcuts_reorder (impl, position);
3179
3180   g_signal_stop_emission_by_name (widget, "drag-data-received");
3181 }
3182
3183 /* Callback used to display a tooltip in the shortcuts tree */
3184 static gboolean
3185 shortcuts_query_tooltip_cb (GtkWidget             *widget,
3186                             gint                   x,
3187                             gint                   y,
3188                             gboolean               keyboard_mode,
3189                             GtkTooltip            *tooltip,
3190                             GtkFileChooserDefault *impl)
3191 {
3192   GtkTreeModel *model;
3193   GtkTreeIter iter;
3194
3195   if (gtk_tree_view_get_tooltip_context (GTK_TREE_VIEW (widget),
3196                                          &x, &y,
3197                                          keyboard_mode,
3198                                          &model,
3199                                          NULL,
3200                                          &iter))
3201     {
3202       gpointer col_data;
3203       ShortcutType shortcut_type;
3204
3205       gtk_tree_model_get (model, &iter,
3206                           SHORTCUTS_COL_DATA, &col_data,
3207                           SHORTCUTS_COL_TYPE, &shortcut_type,
3208                           -1);
3209
3210       if (shortcut_type == SHORTCUT_TYPE_SEPARATOR)
3211         return FALSE;
3212       else if (shortcut_type == SHORTCUT_TYPE_VOLUME)
3213         return FALSE;
3214       else if (shortcut_type == SHORTCUT_TYPE_FILE)
3215         {
3216           GFile *file;
3217           char *parse_name;
3218
3219           file = G_FILE (col_data);
3220           parse_name = g_file_get_parse_name (file);
3221
3222           gtk_tooltip_set_text (tooltip, parse_name);
3223
3224           g_free (parse_name);
3225
3226           return TRUE;
3227         }
3228       else if (shortcut_type == SHORTCUT_TYPE_SEARCH)
3229         {
3230           return FALSE;
3231         }
3232       else if (shortcut_type == SHORTCUT_TYPE_RECENT)
3233         {
3234           return FALSE;
3235         }
3236     }
3237
3238   return FALSE;
3239 }
3240
3241
3242 /* Callback used when the selection in the shortcuts tree changes */
3243 static void
3244 shortcuts_selection_changed_cb (GtkTreeSelection      *selection,
3245                                 GtkFileChooserDefault *impl)
3246 {
3247   GtkTreeIter iter;
3248   GtkTreeIter child_iter;
3249
3250   bookmarks_check_remove_sensitivity (impl);
3251   shortcuts_check_popup_sensitivity (impl);
3252
3253   if (impl->changing_folder)
3254     return;
3255
3256   if (gtk_tree_selection_get_selected(selection, NULL, &iter))
3257     {
3258       gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (impl->shortcuts_pane_filter_model),
3259                                                         &child_iter,
3260                                                         &iter);
3261       shortcuts_activate_iter (impl, &child_iter);
3262     }
3263 }
3264
3265 static gboolean
3266 shortcuts_row_separator_func (GtkTreeModel *model,
3267                               GtkTreeIter  *iter,
3268                               gpointer      data)
3269 {
3270   ShortcutType shortcut_type;
3271
3272   gtk_tree_model_get (model, iter, SHORTCUTS_COL_TYPE, &shortcut_type, -1);
3273   
3274   return shortcut_type == SHORTCUT_TYPE_SEPARATOR;
3275 }
3276
3277 static gboolean
3278 shortcuts_key_press_event_after_cb (GtkWidget             *tree_view,
3279                                     GdkEventKey           *event,
3280                                     GtkFileChooserDefault *impl)
3281 {
3282   GtkWidget *entry;
3283
3284   /* don't screw up focus switching with Tab */
3285   if (event->keyval == GDK_KEY_Tab
3286       || event->keyval == GDK_KEY_KP_Tab
3287       || event->keyval == GDK_KEY_ISO_Left_Tab
3288       || event->length < 1)
3289     return FALSE;
3290
3291   if (impl->location_entry)
3292     entry = impl->location_entry;
3293   else if (impl->search_entry)
3294     entry = impl->search_entry;
3295   else
3296     entry = NULL;
3297
3298   if (entry)
3299     {
3300       gtk_widget_grab_focus (entry);
3301       return gtk_widget_event (entry, (GdkEvent *) event);
3302     }
3303   else
3304     return FALSE;
3305 }
3306
3307 /* Callback used when the file list's popup menu is detached */
3308 static void
3309 shortcuts_popup_menu_detach_cb (GtkWidget *attach_widget,
3310                                 GtkMenu   *menu)
3311 {
3312   GtkFileChooserDefault *impl;
3313   
3314   impl = g_object_get_data (G_OBJECT (attach_widget), "GtkFileChooserDefault");
3315   g_assert (GTK_IS_FILE_CHOOSER_DEFAULT (impl));
3316
3317   impl->browse_shortcuts_popup_menu = NULL;
3318   impl->browse_shortcuts_popup_menu_remove_item = NULL;
3319   impl->browse_shortcuts_popup_menu_rename_item = NULL;
3320 }
3321
3322 static void
3323 remove_shortcut_cb (GtkMenuItem           *item,
3324                     GtkFileChooserDefault *impl)
3325 {
3326   remove_selected_bookmarks (impl);
3327 }
3328
3329 /* Rename the selected bookmark */
3330 static void
3331 rename_selected_bookmark (GtkFileChooserDefault *impl)
3332 {
3333   GtkTreeIter iter;
3334   GtkTreePath *path;
3335   GtkTreeViewColumn *column;
3336   GtkCellRenderer *cell;
3337   GList *renderers;
3338
3339   if (shortcuts_get_selected (impl, &iter))
3340     {
3341       path = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->shortcuts_model), &iter);
3342       column = gtk_tree_view_get_column (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view), 0);
3343       renderers = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
3344       cell = g_list_nth_data (renderers, 1);
3345       g_list_free (renderers);
3346       g_object_set (cell, "editable", TRUE, NULL);
3347       gtk_tree_view_set_cursor_on_cell (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view),
3348                                         path, column, cell, TRUE);
3349       gtk_tree_path_free (path);
3350     }
3351 }
3352
3353 static void
3354 rename_shortcut_cb (GtkMenuItem           *item,
3355                     GtkFileChooserDefault *impl)
3356 {
3357   rename_selected_bookmark (impl);
3358 }
3359
3360 /* Constructs the popup menu for the file list if needed */
3361 static void
3362 shortcuts_build_popup_menu (GtkFileChooserDefault *impl)
3363 {
3364   GtkWidget *item;
3365
3366   if (impl->browse_shortcuts_popup_menu)
3367     return;
3368
3369   impl->browse_shortcuts_popup_menu = gtk_menu_new ();
3370   gtk_menu_attach_to_widget (GTK_MENU (impl->browse_shortcuts_popup_menu),
3371                              impl->browse_shortcuts_tree_view,
3372                              shortcuts_popup_menu_detach_cb);
3373
3374   item = gtk_image_menu_item_new_with_label (_("Remove"));
3375   impl->browse_shortcuts_popup_menu_remove_item = item;
3376   gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item),
3377                                  gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU));
3378   g_signal_connect (item, "activate",
3379                     G_CALLBACK (remove_shortcut_cb), impl);
3380   gtk_widget_show (item);
3381   gtk_menu_shell_append (GTK_MENU_SHELL (impl->browse_shortcuts_popup_menu), item);
3382
3383   item = gtk_menu_item_new_with_label (_("Rename..."));
3384   impl->browse_shortcuts_popup_menu_rename_item = item;
3385   g_signal_connect (item, "activate",
3386                     G_CALLBACK (rename_shortcut_cb), impl);
3387   gtk_widget_show (item);
3388   gtk_menu_shell_append (GTK_MENU_SHELL (impl->browse_shortcuts_popup_menu), item);
3389 }
3390
3391 static void
3392 shortcuts_update_popup_menu (GtkFileChooserDefault *impl)
3393 {
3394   shortcuts_build_popup_menu (impl);  
3395   shortcuts_check_popup_sensitivity (impl);
3396 }
3397
3398 static void
3399 popup_position_func (GtkMenu   *menu,
3400                      gint      *x,
3401                      gint      *y,
3402                      gboolean  *push_in,
3403                      gpointer   user_data);
3404
3405 static void
3406 shortcuts_popup_menu (GtkFileChooserDefault *impl,
3407                       GdkEventButton        *event)
3408 {
3409   shortcuts_update_popup_menu (impl);
3410   if (event)
3411     gtk_menu_popup (GTK_MENU (impl->browse_shortcuts_popup_menu),
3412                     NULL, NULL, NULL, NULL,
3413                     event->button, event->time);
3414   else
3415     {
3416       gtk_menu_popup (GTK_MENU (impl->browse_shortcuts_popup_menu),
3417                       NULL, NULL,
3418                       popup_position_func, impl->browse_shortcuts_tree_view,
3419                       0, GDK_CURRENT_TIME);
3420       gtk_menu_shell_select_first (GTK_MENU_SHELL (impl->browse_shortcuts_popup_menu),
3421                                    FALSE);
3422     }
3423 }
3424
3425 /* Callback used for the GtkWidget::popup-menu signal of the shortcuts list */
3426 static gboolean
3427 shortcuts_popup_menu_cb (GtkWidget *widget,
3428                          GtkFileChooserDefault *impl)
3429 {
3430   shortcuts_popup_menu (impl, NULL);
3431   return TRUE;
3432 }
3433
3434 /* Callback used when a button is pressed on the shortcuts list.  
3435  * We trap button 3 to bring up a popup menu.
3436  */
3437 static gboolean
3438 shortcuts_button_press_event_cb (GtkWidget             *widget,
3439                                  GdkEventButton        *event,
3440                                  GtkFileChooserDefault *impl)
3441 {
3442   static gboolean in_press = FALSE;
3443   gboolean handled;
3444
3445   if (in_press)
3446     return FALSE;
3447
3448   if (event->button != 3)
3449     return FALSE;
3450
3451   in_press = TRUE;
3452   handled = gtk_widget_event (impl->browse_shortcuts_tree_view, (GdkEvent *) event);
3453   in_press = FALSE;
3454
3455   if (!handled)
3456     return FALSE;
3457
3458   shortcuts_popup_menu (impl, event);
3459   return TRUE;
3460 }
3461
3462 static void
3463 shortcuts_edited (GtkCellRenderer       *cell,
3464                   gchar                 *path_string,
3465                   gchar                 *new_text,
3466                   GtkFileChooserDefault *impl)
3467 {
3468   GtkTreePath *path;
3469   GtkTreeIter iter;
3470   GFile *shortcut;
3471
3472   g_object_set (cell, "editable", FALSE, NULL);
3473
3474   path = gtk_tree_path_new_from_string (path_string);
3475   if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->shortcuts_model), &iter, path))
3476     g_assert_not_reached ();
3477
3478   gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
3479                       SHORTCUTS_COL_DATA, &shortcut,
3480                       -1);
3481   gtk_tree_path_free (path);
3482   
3483   _gtk_file_system_set_bookmark_label (impl->file_system, shortcut, new_text);
3484 }
3485
3486 static void
3487 shortcuts_editing_canceled (GtkCellRenderer       *cell,
3488                             GtkFileChooserDefault *impl)
3489 {
3490   g_object_set (cell, "editable", FALSE, NULL);
3491 }
3492
3493 /* Creates the widgets for the shortcuts and bookmarks tree */
3494 static GtkWidget *
3495 shortcuts_list_create (GtkFileChooserDefault *impl)
3496 {
3497   GtkWidget *swin;
3498   GtkTreeSelection *selection;
3499   GtkTreeViewColumn *column;
3500   GtkCellRenderer *renderer;
3501
3502   /* Target types for dragging a row to/from the shortcuts list */
3503   const GtkTargetEntry tree_model_row_targets[] = {
3504     { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, GTK_TREE_MODEL_ROW }
3505   };
3506
3507   /* Scrolled window */
3508
3509   swin = gtk_scrolled_window_new (NULL, NULL);
3510   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (swin),
3511                                   GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
3512   gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (swin),
3513                                        GTK_SHADOW_IN);
3514   gtk_widget_show (swin);
3515
3516   /* Tree */
3517   impl->browse_shortcuts_tree_view = gtk_tree_view_new ();
3518   gtk_style_context_add_class (gtk_widget_get_style_context (impl->browse_shortcuts_tree_view),
3519                                GTK_STYLE_CLASS_SIDEBAR);
3520   gtk_tree_view_set_enable_search (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view), FALSE);
3521 #ifdef PROFILE_FILE_CHOOSER
3522   g_object_set_data (G_OBJECT (impl->browse_shortcuts_tree_view), "fmq-name", "shortcuts");
3523 #endif
3524
3525   /* Connect "after" to key-press-event on the shortcuts pane.  We want this action to be possible:
3526    *
3527    *   1. user brings up a SAVE dialog
3528    *   2. user clicks on a shortcut in the shortcuts pane
3529    *   3. user starts typing a filename
3530    *
3531    * Normally, the user's typing would be ignored, as the shortcuts treeview doesn't
3532    * support interactive search.  However, we'd rather focus the location entry
3533    * so that the user can type *there*.
3534    *
3535    * To preserve keyboard navigation in the shortcuts pane, we don't focus the
3536    * filename entry if one clicks on a shortcut; rather, we focus the entry only
3537    * if the user starts typing while the focus is in the shortcuts pane.
3538    */
3539   g_signal_connect_after (impl->browse_shortcuts_tree_view, "key-press-event",
3540                           G_CALLBACK (shortcuts_key_press_event_after_cb), impl);
3541
3542   g_signal_connect (impl->browse_shortcuts_tree_view, "popup-menu",
3543                     G_CALLBACK (shortcuts_popup_menu_cb), impl);
3544   g_signal_connect (impl->browse_shortcuts_tree_view, "button-press-event",
3545                     G_CALLBACK (shortcuts_button_press_event_cb), impl);
3546   /* Accessible object name for the file chooser's shortcuts pane */
3547   atk_object_set_name (gtk_widget_get_accessible (impl->browse_shortcuts_tree_view), _("Places"));
3548
3549   gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view), impl->shortcuts_pane_filter_model);
3550
3551   gtk_tree_view_enable_model_drag_source (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view),
3552                                           GDK_BUTTON1_MASK,
3553                                           tree_model_row_targets,
3554                                           G_N_ELEMENTS (tree_model_row_targets),
3555                                           GDK_ACTION_MOVE);
3556
3557   gtk_drag_dest_set (impl->browse_shortcuts_tree_view,
3558                      GTK_DEST_DEFAULT_ALL,
3559                      tree_model_row_targets,
3560                      G_N_ELEMENTS (tree_model_row_targets),
3561                      GDK_ACTION_COPY | GDK_ACTION_MOVE);
3562   gtk_drag_dest_add_uri_targets (impl->browse_shortcuts_tree_view);
3563
3564   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view));
3565   gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
3566   gtk_tree_selection_set_select_function (selection,
3567                                           shortcuts_select_func,
3568                                           impl, NULL);
3569
3570   g_signal_connect (selection, "changed",
3571                     G_CALLBACK (shortcuts_selection_changed_cb), impl);
3572
3573   g_signal_connect (impl->browse_shortcuts_tree_view, "key-press-event",
3574                     G_CALLBACK (shortcuts_key_press_event_cb), impl);
3575
3576   g_signal_connect (impl->browse_shortcuts_tree_view, "drag-begin",
3577                     G_CALLBACK (shortcuts_drag_begin_cb), impl);
3578   g_signal_connect (impl->browse_shortcuts_tree_view, "drag-end",
3579                     G_CALLBACK (shortcuts_drag_end_cb), impl);
3580   g_signal_connect (impl->browse_shortcuts_tree_view, "drag-data-delete",
3581                     G_CALLBACK (shortcuts_drag_data_delete_cb), impl);
3582
3583   g_signal_connect (impl->browse_shortcuts_tree_view, "drag-leave",
3584                     G_CALLBACK (shortcuts_drag_leave_cb), impl);
3585   g_signal_connect (impl->browse_shortcuts_tree_view, "drag-motion",
3586                     G_CALLBACK (shortcuts_drag_motion_cb), impl);
3587   g_signal_connect (impl->browse_shortcuts_tree_view, "drag-drop",
3588                     G_CALLBACK (shortcuts_drag_drop_cb), impl);
3589   g_signal_connect (impl->browse_shortcuts_tree_view, "drag-data-received",
3590                     G_CALLBACK (shortcuts_drag_data_received_cb), impl);
3591
3592   /* Support tooltips */
3593   gtk_widget_set_has_tooltip (impl->browse_shortcuts_tree_view, TRUE);
3594   g_signal_connect (impl->browse_shortcuts_tree_view, "query-tooltip",
3595                     G_CALLBACK (shortcuts_query_tooltip_cb), impl);
3596
3597   gtk_container_add (GTK_CONTAINER (swin), impl->browse_shortcuts_tree_view);
3598   gtk_widget_show (impl->browse_shortcuts_tree_view);
3599
3600   /* Column */
3601
3602   column = gtk_tree_view_column_new ();
3603   /* Column header for the file chooser's shortcuts pane */
3604   gtk_tree_view_column_set_title (column, _("_Places"));
3605
3606   renderer = gtk_cell_renderer_pixbuf_new ();
3607   gtk_tree_view_column_pack_start (column, renderer, FALSE);
3608   gtk_tree_view_column_set_attributes (column, renderer,
3609                                        "pixbuf", SHORTCUTS_COL_PIXBUF,
3610                                        "visible", SHORTCUTS_COL_PIXBUF_VISIBLE,
3611                                        NULL);
3612
3613   renderer = gtk_cell_renderer_text_new ();
3614   g_object_set (renderer,
3615                 "width-chars", 12,
3616                 "ellipsize", PANGO_ELLIPSIZE_END,
3617                 NULL);
3618   g_signal_connect (renderer, "edited",
3619                     G_CALLBACK (shortcuts_edited), impl);
3620   g_signal_connect (renderer, "editing-canceled",
3621                     G_CALLBACK (shortcuts_editing_canceled), impl);
3622   gtk_tree_view_column_pack_start (column, renderer, TRUE);
3623   gtk_tree_view_column_set_attributes (column, renderer,
3624                                        "text", SHORTCUTS_COL_NAME,
3625                                        NULL);
3626
3627   gtk_tree_view_set_row_separator_func (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view),
3628                                         shortcuts_row_separator_func,
3629                                         NULL, NULL);
3630
3631   gtk_tree_view_append_column (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view), column);
3632
3633   return swin;
3634 }
3635
3636 /* Creates the widgets for the shortcuts/bookmarks pane */
3637 static GtkWidget *
3638 shortcuts_pane_create (GtkFileChooserDefault *impl,
3639                        GtkSizeGroup          *size_group)
3640 {
3641   GtkWidget *vbox;
3642   GtkWidget *toolbar;
3643   GtkWidget *widget;
3644   GtkStyleContext *context;
3645   GIcon *icon;
3646
3647   vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
3648   gtk_widget_show (vbox);
3649
3650   /* Shortcuts tree */
3651
3652   widget = shortcuts_list_create (impl);
3653
3654   gtk_size_group_add_widget (size_group, widget);
3655   context = gtk_widget_get_style_context (widget);
3656   gtk_style_context_set_junction_sides (context, GTK_JUNCTION_BOTTOM);
3657
3658   gtk_box_pack_start (GTK_BOX (vbox), widget, TRUE, TRUE, 0);
3659
3660   /* Box for buttons */
3661
3662   toolbar = gtk_toolbar_new ();
3663   gtk_toolbar_set_icon_size (GTK_TOOLBAR (toolbar), GTK_ICON_SIZE_MENU);
3664
3665   context = gtk_widget_get_style_context (toolbar);
3666   gtk_style_context_set_junction_sides (context, GTK_JUNCTION_TOP);
3667   gtk_style_context_add_class (context, GTK_STYLE_CLASS_INLINE_TOOLBAR);
3668   
3669   gtk_box_pack_start (GTK_BOX (vbox), toolbar, FALSE, FALSE, 0);
3670   gtk_widget_show (toolbar);
3671
3672   /* Add bookmark button */
3673   icon = g_themed_icon_new_with_default_fallbacks ("list-add-symbolic");
3674   impl->browse_shortcuts_add_button = toolbutton_new (impl,
3675                                                       icon,
3676                                                       FALSE,
3677                                                       TRUE,
3678                                                       G_CALLBACK (add_bookmark_button_clicked_cb));
3679   g_object_unref (icon);
3680
3681   gtk_toolbar_insert (GTK_TOOLBAR (toolbar), GTK_TOOL_ITEM (impl->browse_shortcuts_add_button), 0);
3682   gtk_widget_set_tooltip_text (impl->browse_shortcuts_add_button,
3683                                _("Add the selected folder to the Bookmarks"));
3684
3685   /* Remove bookmark button */
3686   icon = g_themed_icon_new_with_default_fallbacks ("list-remove-symbolic");
3687   impl->browse_shortcuts_remove_button = toolbutton_new (impl,
3688                                                          icon,
3689                                                          FALSE,
3690                                                          TRUE,
3691                                                          G_CALLBACK (remove_bookmark_button_clicked_cb));
3692   g_object_unref (icon);
3693   gtk_toolbar_insert (GTK_TOOLBAR (toolbar), GTK_TOOL_ITEM (impl->browse_shortcuts_remove_button), 1);
3694   gtk_widget_set_tooltip_text (impl->browse_shortcuts_remove_button,
3695                                _("Remove the selected bookmark"));
3696
3697   return vbox;
3698 }
3699
3700 static gboolean
3701 key_is_left_or_right (GdkEventKey *event)
3702 {
3703   guint modifiers;
3704
3705   modifiers = gtk_accelerator_get_default_mod_mask ();
3706
3707   return ((event->keyval == GDK_KEY_Right
3708            || event->keyval == GDK_KEY_KP_Right
3709            || event->keyval == GDK_KEY_Left
3710            || event->keyval == GDK_KEY_KP_Left)
3711           && (event->state & modifiers) == 0);
3712 }
3713
3714 /* Handles key press events on the file list, so that we can trap Enter to
3715  * activate the default button on our own.  Also, checks to see if '/' has been
3716  * pressed.  See comment by tree_view_keybinding_cb() for more details.
3717  */
3718 static gboolean
3719 browse_files_key_press_event_cb (GtkWidget   *widget,
3720                                  GdkEventKey *event,
3721                                  gpointer     data)
3722 {
3723   GtkFileChooserDefault *impl;
3724   int modifiers;
3725
3726   impl = (GtkFileChooserDefault *) data;
3727
3728   modifiers = gtk_accelerator_get_default_mod_mask ();
3729
3730   if ((event->keyval == GDK_KEY_slash
3731        || event->keyval == GDK_KEY_KP_Divide
3732 #ifdef G_OS_UNIX
3733        || event->keyval == GDK_KEY_asciitilde
3734 #endif
3735        ) && ! (event->state & (~GDK_SHIFT_MASK & modifiers)))
3736     {
3737       location_popup_handler (impl, event->string);
3738       return TRUE;
3739     }
3740
3741   if (key_is_left_or_right (event))
3742     {
3743       gtk_widget_grab_focus (impl->browse_shortcuts_tree_view);
3744       return TRUE;
3745     }
3746
3747   if ((event->keyval == GDK_KEY_Return
3748        || event->keyval == GDK_KEY_ISO_Enter
3749        || event->keyval == GDK_KEY_KP_Enter
3750        || event->keyval == GDK_KEY_space
3751        || event->keyval == GDK_KEY_KP_Space)
3752       && ((event->state & modifiers) == 0)
3753       && !(impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ||
3754            impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER))
3755     {
3756       GtkWindow *window;
3757
3758       window = get_toplevel (widget);
3759       if (window)
3760         {
3761           GtkWidget *default_widget, *focus_widget;
3762
3763           default_widget = gtk_window_get_default_widget (window);
3764           focus_widget = gtk_window_get_focus (window);
3765
3766           if (widget != default_widget &&
3767               !(widget == focus_widget && (!default_widget || !gtk_widget_get_sensitive (default_widget))))
3768             {
3769               gtk_window_activate_default (window);
3770
3771               return TRUE;
3772             }
3773         }
3774     }
3775
3776   return FALSE;
3777 }
3778
3779 /* Callback used when the file list's popup menu is detached */
3780 static void
3781 popup_menu_detach_cb (GtkWidget *attach_widget,
3782                       GtkMenu   *menu)
3783 {
3784   GtkFileChooserDefault *impl;
3785
3786   impl = g_object_get_data (G_OBJECT (attach_widget), "GtkFileChooserDefault");
3787   g_assert (GTK_IS_FILE_CHOOSER_DEFAULT (impl));
3788
3789   impl->browse_files_popup_menu = NULL;
3790   impl->browse_files_popup_menu_add_shortcut_item = NULL;
3791   impl->browse_files_popup_menu_hidden_files_item = NULL;
3792 }
3793
3794 /* Callback used when the "Add to Bookmarks" menu item is activated */
3795 static void
3796 add_to_shortcuts_cb (GtkMenuItem           *item,
3797                      GtkFileChooserDefault *impl)
3798 {
3799   bookmarks_add_selected_folder (impl);
3800 }
3801
3802 /* Callback used when the "Show Hidden Files" menu item is toggled */
3803 static void
3804 show_hidden_toggled_cb (GtkCheckMenuItem      *item,
3805                         GtkFileChooserDefault *impl)
3806 {
3807   g_object_set (impl,
3808                 "show-hidden", gtk_check_menu_item_get_active (item),
3809                 NULL);
3810 }
3811
3812 /* Callback used when the "Show Size Column" menu item is toggled */
3813 static void
3814 show_size_column_toggled_cb (GtkCheckMenuItem *item,
3815                              GtkFileChooserDefault *impl)
3816 {
3817   impl->show_size_column = gtk_check_menu_item_get_active (item);
3818
3819   gtk_tree_view_column_set_visible (impl->list_size_column,
3820                                     impl->show_size_column);
3821 }
3822
3823 /* Shows an error dialog about not being able to select a dragged file */
3824 static void
3825 error_selecting_dragged_file_dialog (GtkFileChooserDefault *impl,
3826                                      GFile                 *file,
3827                                      GError                *error)
3828 {
3829   error_dialog (impl,
3830                 _("Could not select file"),
3831                 file, error);
3832 }
3833
3834 static void
3835 file_list_drag_data_select_uris (GtkFileChooserDefault  *impl,
3836                                  gchar                 **uris)
3837 {
3838   int i;
3839   char *uri;
3840   GtkFileChooser *chooser = GTK_FILE_CHOOSER (impl);
3841
3842   for (i = 1; uris[i]; i++)
3843     {
3844       GFile *file;
3845       GError *error = NULL;
3846
3847       uri = uris[i];
3848       file = g_file_new_for_uri (uri);
3849
3850       gtk_file_chooser_default_select_file (chooser, file, &error);
3851       if (error)
3852         error_selecting_dragged_file_dialog (impl, file, error);
3853
3854       g_object_unref (file);
3855     }
3856 }
3857
3858 struct FileListDragData
3859 {
3860   GtkFileChooserDefault *impl;
3861   gchar **uris;
3862   GFile *file;
3863 };
3864
3865 static void
3866 file_list_drag_data_received_get_info_cb (GCancellable *cancellable,
3867                                           GFileInfo    *info,
3868                                           const GError *error,
3869                                           gpointer      user_data)
3870 {
3871   gboolean cancelled = g_cancellable_is_cancelled (cancellable);
3872   struct FileListDragData *data = user_data;
3873   GtkFileChooser *chooser = GTK_FILE_CHOOSER (data->impl);
3874
3875   if (cancellable != data->impl->file_list_drag_data_received_cancellable)
3876     goto out;
3877
3878   data->impl->file_list_drag_data_received_cancellable = NULL;
3879
3880   if (cancelled || error)
3881     goto out;
3882
3883   if ((data->impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
3884        data->impl->action == GTK_FILE_CHOOSER_ACTION_SAVE) &&
3885       data->uris[1] == 0 && !error && _gtk_file_info_consider_as_directory (info))
3886     change_folder_and_display_error (data->impl, data->file, FALSE);
3887   else
3888     {
3889       GError *error = NULL;
3890
3891       gtk_file_chooser_default_unselect_all (chooser);
3892       gtk_file_chooser_default_select_file (chooser, data->file, &error);
3893       if (error)
3894         error_selecting_dragged_file_dialog (data->impl, data->file, error);
3895       else
3896         browse_files_center_selected_row (data->impl);
3897     }
3898
3899   if (data->impl->select_multiple)
3900     file_list_drag_data_select_uris (data->impl, data->uris);
3901
3902 out:
3903   g_object_unref (data->impl);
3904   g_strfreev (data->uris);
3905   g_object_unref (data->file);
3906   g_free (data);
3907
3908   g_object_unref (cancellable);
3909 }
3910
3911 static void
3912 file_list_drag_data_received_cb (GtkWidget        *widget,
3913                                  GdkDragContext   *context,
3914                                  gint              x,
3915                                  gint              y,
3916                                  GtkSelectionData *selection_data,
3917                                  guint             info,
3918                                  guint             time_,
3919                                  gpointer          data)
3920 {
3921   GtkFileChooserDefault *impl;
3922   gchar **uris;
3923   char *uri;
3924   GFile *file;
3925
3926   impl = GTK_FILE_CHOOSER_DEFAULT (data);
3927
3928   /* Allow only drags from other widgets; see bug #533891. */
3929   if (gtk_drag_get_source_widget (context) == widget)
3930     {
3931       g_signal_stop_emission_by_name (widget, "drag-data-received");
3932       return;
3933     }
3934
3935   /* Parse the text/uri-list string, navigate to the first one */
3936   uris = gtk_selection_data_get_uris (selection_data);
3937   if (uris && uris[0])
3938     {
3939       struct FileListDragData *data;
3940
3941       uri = uris[0];
3942       file = g_file_new_for_uri (uri);
3943
3944       data = g_new0 (struct FileListDragData, 1);
3945       data->impl = g_object_ref (impl);
3946       data->uris = uris;
3947       data->file = file;
3948
3949       if (impl->file_list_drag_data_received_cancellable)
3950         g_cancellable_cancel (impl->file_list_drag_data_received_cancellable);
3951
3952       impl->file_list_drag_data_received_cancellable =
3953         _gtk_file_system_get_info (impl->file_system, file,
3954                                    "standard::type",
3955                                    file_list_drag_data_received_get_info_cb,
3956                                    data);
3957     }
3958
3959   g_signal_stop_emission_by_name (widget, "drag-data-received");
3960 }
3961
3962 /* Don't do anything with the drag_drop signal */
3963 static gboolean
3964 file_list_drag_drop_cb (GtkWidget             *widget,
3965                         GdkDragContext        *context,
3966                         gint                   x,
3967                         gint                   y,
3968                         guint                  time_,
3969                         GtkFileChooserDefault *impl)
3970 {
3971   g_signal_stop_emission_by_name (widget, "drag-drop");
3972   return TRUE;
3973 }
3974
3975 /* Disable the normal tree drag motion handler, it makes it look like you're
3976    dropping the dragged item onto a tree item */
3977 static gboolean
3978 file_list_drag_motion_cb (GtkWidget             *widget,
3979                           GdkDragContext        *context,
3980                           gint                   x,
3981                           gint                   y,
3982                           guint                  time_,
3983                           GtkFileChooserDefault *impl)
3984 {
3985   g_signal_stop_emission_by_name (widget, "drag-motion");
3986   return TRUE;
3987 }
3988
3989 /* Constructs the popup menu for the file list if needed */
3990 static void
3991 file_list_build_popup_menu (GtkFileChooserDefault *impl)
3992 {
3993   GtkWidget *item;
3994
3995   if (impl->browse_files_popup_menu)
3996     return;
3997
3998   impl->browse_files_popup_menu = gtk_menu_new ();
3999   gtk_menu_attach_to_widget (GTK_MENU (impl->browse_files_popup_menu),
4000                              impl->browse_files_tree_view,
4001                              popup_menu_detach_cb);
4002
4003   item = gtk_image_menu_item_new_with_mnemonic (_("_Add to Bookmarks"));
4004   impl->browse_files_popup_menu_add_shortcut_item = item;
4005   gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item),
4006                                  gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU));
4007   g_signal_connect (item, "activate",
4008                     G_CALLBACK (add_to_shortcuts_cb), impl);
4009   gtk_widget_show (item);
4010   gtk_menu_shell_append (GTK_MENU_SHELL (impl->browse_files_popup_menu), item);
4011
4012   item = gtk_separator_menu_item_new ();
4013   gtk_widget_show (item);
4014   gtk_menu_shell_append (GTK_MENU_SHELL (impl->browse_files_popup_menu), item);
4015
4016   item = gtk_check_menu_item_new_with_mnemonic (_("Show _Hidden Files"));
4017   impl->browse_files_popup_menu_hidden_files_item = item;
4018   g_signal_connect (item, "toggled",
4019                     G_CALLBACK (show_hidden_toggled_cb), impl);
4020   gtk_widget_show (item);
4021   gtk_menu_shell_append (GTK_MENU_SHELL (impl->browse_files_popup_menu), item);
4022
4023   item = gtk_check_menu_item_new_with_mnemonic (_("Show _Size Column"));
4024   impl->browse_files_popup_menu_size_column_item = item;
4025   g_signal_connect (item, "toggled",
4026                     G_CALLBACK (show_size_column_toggled_cb), impl);
4027   gtk_widget_show (item);
4028   gtk_menu_shell_append (GTK_MENU_SHELL (impl->browse_files_popup_menu), item);
4029
4030   bookmarks_check_add_sensitivity (impl);
4031 }
4032
4033 /* Updates the popup menu for the file list, creating it if necessary */
4034 static void
4035 file_list_update_popup_menu (GtkFileChooserDefault *impl)
4036 {
4037   file_list_build_popup_menu (impl);
4038
4039   /* FIXME - handle OPERATION_MODE_SEARCH and OPERATION_MODE_RECENT */
4040
4041   /* The sensitivity of the Add to Bookmarks item is set in
4042    * bookmarks_check_add_sensitivity()
4043    */
4044
4045   /* 'Show Hidden Files' */
4046   g_signal_handlers_block_by_func (impl->browse_files_popup_menu_hidden_files_item,
4047                                    G_CALLBACK (show_hidden_toggled_cb), impl);
4048   gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (impl->browse_files_popup_menu_hidden_files_item),
4049                                   impl->show_hidden);
4050   g_signal_handlers_unblock_by_func (impl->browse_files_popup_menu_hidden_files_item,
4051                                      G_CALLBACK (show_hidden_toggled_cb), impl);
4052
4053   /* 'Show Size Column' */
4054   g_signal_handlers_block_by_func (impl->browse_files_popup_menu_size_column_item,
4055                                    G_CALLBACK (show_size_column_toggled_cb), impl);
4056   gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (impl->browse_files_popup_menu_size_column_item),
4057                                   impl->show_size_column);
4058   g_signal_handlers_unblock_by_func (impl->browse_files_popup_menu_size_column_item,
4059                                      G_CALLBACK (show_size_column_toggled_cb), impl);
4060 }
4061
4062 static void
4063 popup_position_func (GtkMenu   *menu,
4064                      gint      *x,
4065                      gint      *y,
4066                      gboolean  *push_in,
4067                      gpointer   user_data)
4068 {
4069   GtkAllocation allocation;
4070   GtkWidget *widget = GTK_WIDGET (user_data);
4071   GdkScreen *screen = gtk_widget_get_screen (widget);
4072   GtkRequisition req;
4073   gint monitor_num;
4074   GdkRectangle monitor;
4075
4076   g_return_if_fail (gtk_widget_get_realized (widget));
4077
4078   gdk_window_get_origin (gtk_widget_get_window (widget), x, y);
4079
4080   gtk_widget_get_preferred_size (GTK_WIDGET (menu),
4081                                  &req, NULL);
4082
4083   gtk_widget_get_allocation (widget, &allocation);
4084   *x += (allocation.width - req.width) / 2;
4085   *y += (allocation.height - req.height) / 2;
4086
4087   monitor_num = gdk_screen_get_monitor_at_point (screen, *x, *y);
4088   gtk_menu_set_monitor (menu, monitor_num);
4089   gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
4090
4091   *x = CLAMP (*x, monitor.x, monitor.x + MAX (0, monitor.width - req.width));
4092   *y = CLAMP (*y, monitor.y, monitor.y + MAX (0, monitor.height - req.height));
4093
4094   *push_in = FALSE;
4095 }
4096
4097 static void
4098 file_list_popup_menu (GtkFileChooserDefault *impl,
4099                       GdkEventButton        *event)
4100 {
4101   file_list_update_popup_menu (impl);
4102   if (event)
4103     gtk_menu_popup (GTK_MENU (impl->browse_files_popup_menu),
4104                     NULL, NULL, NULL, NULL,
4105                     event->button, event->time);
4106   else
4107     {
4108       gtk_menu_popup (GTK_MENU (impl->browse_files_popup_menu),
4109                       NULL, NULL,
4110                       popup_position_func, impl->browse_files_tree_view,
4111                       0, GDK_CURRENT_TIME);
4112       gtk_menu_shell_select_first (GTK_MENU_SHELL (impl->browse_files_popup_menu),
4113                                    FALSE);
4114     }
4115
4116 }
4117
4118 /* Callback used for the GtkWidget::popup-menu signal of the file list */
4119 static gboolean
4120 list_popup_menu_cb (GtkWidget *widget,
4121                     GtkFileChooserDefault *impl)
4122 {
4123   file_list_popup_menu (impl, NULL);
4124   return TRUE;
4125 }
4126
4127 /* Callback used when a button is pressed on the file list.  We trap button 3 to
4128  * bring up a popup menu.
4129  */
4130 static gboolean
4131 list_button_press_event_cb (GtkWidget             *widget,
4132                             GdkEventButton        *event,
4133                             GtkFileChooserDefault *impl)
4134 {
4135   static gboolean in_press = FALSE;
4136
4137   if (in_press)
4138     return FALSE;
4139
4140   if (event->button != 3)
4141     return FALSE;
4142
4143   in_press = TRUE;
4144   gtk_widget_event (impl->browse_files_tree_view, (GdkEvent *) event);
4145   in_press = FALSE;
4146
4147   file_list_popup_menu (impl, event);
4148   return TRUE;
4149 }
4150
4151 typedef struct {
4152   OperationMode operation_mode;
4153   gint general_column;
4154   gint model_column;
4155 } ColumnMap;
4156
4157 /* Sets the sort column IDs for the file list; needs to be done whenever we
4158  * change the model on the treeview.
4159  */
4160 static void
4161 file_list_set_sort_column_ids (GtkFileChooserDefault *impl)
4162 {
4163   gtk_tree_view_column_set_sort_column_id (impl->list_name_column, MODEL_COL_NAME);
4164   gtk_tree_view_column_set_sort_column_id (impl->list_mtime_column, MODEL_COL_MTIME);
4165   gtk_tree_view_column_set_sort_column_id (impl->list_size_column, MODEL_COL_SIZE);
4166 }
4167
4168 static gboolean
4169 file_list_query_tooltip_cb (GtkWidget  *widget,
4170                             gint        x,
4171                             gint        y,
4172                             gboolean    keyboard_tip,
4173                             GtkTooltip *tooltip,
4174                             gpointer    user_data)
4175 {
4176   GtkFileChooserDefault *impl = user_data;
4177   GtkTreeModel *model;
4178   GtkTreePath *path;
4179   GtkTreeIter iter;
4180   GFile *file;
4181   gchar *filename;
4182
4183   if (impl->operation_mode == OPERATION_MODE_BROWSE)
4184     return FALSE;
4185
4186
4187   if (!gtk_tree_view_get_tooltip_context (GTK_TREE_VIEW (impl->browse_files_tree_view),
4188                                           &x, &y,
4189                                           keyboard_tip,
4190                                           &model, &path, &iter))
4191     return FALSE;
4192                                        
4193   gtk_tree_model_get (model, &iter,
4194                       MODEL_COL_FILE, &file,
4195                       -1);
4196
4197   if (file == NULL)
4198     {
4199       gtk_tree_path_free (path);
4200       return FALSE;
4201     }
4202
4203   filename = g_file_get_path (file);
4204   gtk_tooltip_set_text (tooltip, filename);
4205   gtk_tree_view_set_tooltip_row (GTK_TREE_VIEW (impl->browse_files_tree_view),
4206                                  tooltip,
4207                                  path);
4208
4209   g_free (filename);
4210   g_object_unref (file);
4211   gtk_tree_path_free (path);
4212
4213   return TRUE;
4214 }
4215
4216 static void
4217 set_icon_cell_renderer_fixed_size (GtkFileChooserDefault *impl, GtkCellRenderer *renderer)
4218 {
4219   gint xpad, ypad;
4220
4221   gtk_cell_renderer_get_padding (renderer, &xpad, &ypad);
4222   gtk_cell_renderer_set_fixed_size (renderer, 
4223                                     xpad * 2 + impl->icon_size,
4224                                     ypad * 2 + impl->icon_size);
4225 }
4226
4227 /* Creates the widgets for the file list */
4228 static GtkWidget *
4229 create_file_list (GtkFileChooserDefault *impl)
4230 {
4231   GtkWidget *swin;
4232   GtkTreeSelection *selection;
4233   GtkTreeViewColumn *column;
4234   GtkCellRenderer *renderer;
4235
4236   /* Scrolled window */
4237   swin = gtk_scrolled_window_new (NULL, NULL);
4238   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (swin),
4239                                   GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
4240   gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (swin),
4241                                        GTK_SHADOW_IN);
4242
4243   /* Tree/list view */
4244
4245   impl->browse_files_tree_view = gtk_tree_view_new ();
4246 #ifdef PROFILE_FILE_CHOOSER
4247   g_object_set_data (G_OBJECT (impl->browse_files_tree_view), "fmq-name", "file_list");
4248 #endif
4249   g_object_set_data (G_OBJECT (impl->browse_files_tree_view), I_("GtkFileChooserDefault"), impl);
4250   atk_object_set_name (gtk_widget_get_accessible (impl->browse_files_tree_view), _("Files"));
4251
4252   gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (impl->browse_files_tree_view), TRUE);
4253   gtk_container_add (GTK_CONTAINER (swin), impl->browse_files_tree_view);
4254
4255   gtk_drag_dest_set (impl->browse_files_tree_view,
4256                      GTK_DEST_DEFAULT_ALL,
4257                      NULL, 0,
4258                      GDK_ACTION_COPY | GDK_ACTION_MOVE);
4259   gtk_drag_dest_add_uri_targets (impl->browse_files_tree_view);
4260   
4261   g_signal_connect (impl->browse_files_tree_view, "row-activated",
4262                     G_CALLBACK (list_row_activated), impl);
4263   g_signal_connect (impl->browse_files_tree_view, "key-press-event",
4264                     G_CALLBACK (browse_files_key_press_event_cb), impl);
4265   g_signal_connect (impl->browse_files_tree_view, "popup-menu",
4266                     G_CALLBACK (list_popup_menu_cb), impl);
4267   g_signal_connect (impl->browse_files_tree_view, "button-press-event",
4268                     G_CALLBACK (list_button_press_event_cb), impl);
4269
4270   g_signal_connect (impl->browse_files_tree_view, "drag-data-received",
4271                     G_CALLBACK (file_list_drag_data_received_cb), impl);
4272   g_signal_connect (impl->browse_files_tree_view, "drag-drop",
4273                     G_CALLBACK (file_list_drag_drop_cb), impl);
4274   g_signal_connect (impl->browse_files_tree_view, "drag-motion",
4275                     G_CALLBACK (file_list_drag_motion_cb), impl);
4276
4277   g_object_set (impl->browse_files_tree_view, "has-tooltip", TRUE, NULL);
4278   g_signal_connect (impl->browse_files_tree_view, "query-tooltip",
4279                     G_CALLBACK (file_list_query_tooltip_cb), impl);
4280
4281   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
4282   gtk_tree_selection_set_select_function (selection,
4283                                           list_select_func,
4284                                           impl, NULL);
4285   gtk_tree_view_enable_model_drag_source (GTK_TREE_VIEW (impl->browse_files_tree_view),
4286                                           GDK_BUTTON1_MASK,
4287                                           NULL, 0,
4288                                           GDK_ACTION_COPY | GDK_ACTION_MOVE);
4289   gtk_drag_source_add_uri_targets (impl->browse_files_tree_view);
4290
4291   g_signal_connect (selection, "changed",
4292                     G_CALLBACK (list_selection_changed), impl);
4293
4294   /* Keep the column order in sync with update_cell_renderer_attributes() */
4295
4296   /* Filename column */
4297
4298   impl->list_name_column = gtk_tree_view_column_new ();
4299   gtk_tree_view_column_set_expand (impl->list_name_column, TRUE);
4300   gtk_tree_view_column_set_resizable (impl->list_name_column, TRUE);
4301   gtk_tree_view_column_set_title (impl->list_name_column, _("Name"));
4302
4303   renderer = gtk_cell_renderer_pixbuf_new ();
4304   /* We set a fixed size so that we get an empty slot even if no icons are loaded yet */
4305   set_icon_cell_renderer_fixed_size (impl, renderer);
4306   gtk_tree_view_column_pack_start (impl->list_name_column, renderer, FALSE);
4307
4308   impl->list_name_renderer = gtk_cell_renderer_text_new ();
4309   g_object_set (impl->list_name_renderer,
4310                 "ellipsize", PANGO_ELLIPSIZE_END,
4311                 NULL);
4312   g_signal_connect (impl->list_name_renderer, "edited",
4313                     G_CALLBACK (renderer_edited_cb), impl);
4314   g_signal_connect (impl->list_name_renderer, "editing-canceled",
4315                     G_CALLBACK (renderer_editing_canceled_cb), impl);
4316   gtk_tree_view_column_pack_start (impl->list_name_column, impl->list_name_renderer, TRUE);
4317
4318   gtk_tree_view_append_column (GTK_TREE_VIEW (impl->browse_files_tree_view), impl->list_name_column);
4319
4320   /* Size column */
4321
4322   column = gtk_tree_view_column_new ();
4323   gtk_tree_view_column_set_resizable (column, TRUE);
4324   gtk_tree_view_column_set_title (column, _("Size"));
4325
4326   renderer = gtk_cell_renderer_text_new ();
4327   g_object_set (renderer, 
4328                 "alignment", PANGO_ALIGN_RIGHT,
4329                 NULL);
4330   gtk_tree_view_column_pack_start (column, renderer, TRUE); /* bug: it doesn't expand */
4331   gtk_tree_view_append_column (GTK_TREE_VIEW (impl->browse_files_tree_view), column);
4332   impl->list_size_column = column;
4333
4334   /* Modification time column */
4335
4336   column = gtk_tree_view_column_new ();
4337   gtk_tree_view_column_set_resizable (column, TRUE);
4338   gtk_tree_view_column_set_title (column, _("Modified"));
4339
4340   renderer = gtk_cell_renderer_text_new ();
4341   gtk_tree_view_column_pack_start (column, renderer, TRUE);
4342   gtk_tree_view_append_column (GTK_TREE_VIEW (impl->browse_files_tree_view), column);
4343   impl->list_mtime_column = column;
4344   
4345   file_list_set_sort_column_ids (impl);
4346   update_cell_renderer_attributes (impl);
4347
4348   gtk_widget_show_all (swin);
4349
4350   return swin;
4351 }
4352
4353 /* Creates the widgets for the files/folders pane */
4354 static GtkWidget *
4355 file_pane_create (GtkFileChooserDefault *impl,
4356                   GtkSizeGroup          *size_group)
4357 {
4358   GtkWidget *vbox;
4359   GtkWidget *hbox;
4360   GtkWidget *widget;
4361
4362   vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
4363   gtk_widget_show (vbox);
4364
4365   /* Box for lists and preview */
4366
4367   hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, PREVIEW_HBOX_SPACING);
4368   gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0);
4369   gtk_widget_show (hbox);
4370
4371   /* File list */
4372
4373   widget = create_file_list (impl);
4374   gtk_box_pack_start (GTK_BOX (hbox), widget, TRUE, TRUE, 0);
4375   gtk_size_group_add_widget (size_group, widget);
4376
4377   /* Preview */
4378
4379   impl->preview_box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
4380   gtk_box_pack_start (GTK_BOX (hbox), impl->preview_box, FALSE, FALSE, 0);
4381   /* Don't show preview box initially */
4382
4383   /* Filter combo */
4384
4385   impl->filter_combo_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
4386
4387   widget = filter_create (impl);
4388
4389   gtk_widget_show (widget);
4390   gtk_box_pack_end (GTK_BOX (impl->filter_combo_hbox), widget, FALSE, FALSE, 0);
4391
4392   gtk_box_pack_end (GTK_BOX (vbox), impl->filter_combo_hbox, FALSE, FALSE, 0);
4393
4394   return vbox;
4395 }
4396
4397 static void
4398 location_entry_create (GtkFileChooserDefault *impl)
4399 {
4400   if (!impl->location_entry)
4401     impl->location_entry = _gtk_file_chooser_entry_new (TRUE);
4402
4403   _gtk_file_chooser_entry_set_file_system (GTK_FILE_CHOOSER_ENTRY (impl->location_entry),
4404                                            impl->file_system);
4405   _gtk_file_chooser_entry_set_local_only (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), impl->local_only);
4406   _gtk_file_chooser_entry_set_action (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), impl->action);
4407   gtk_entry_set_width_chars (GTK_ENTRY (impl->location_entry), 45);
4408   gtk_entry_set_activates_default (GTK_ENTRY (impl->location_entry), TRUE);
4409 }
4410
4411 /* Creates the widgets specific to Save mode */
4412 static void
4413 save_widgets_create (GtkFileChooserDefault *impl)
4414 {
4415   GtkWidget *vbox;
4416   GtkWidget *widget;
4417
4418   if (impl->save_widgets != NULL)
4419     return;
4420
4421   location_switch_to_path_bar (impl);
4422
4423   vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
4424
4425   impl->save_widgets_table = gtk_table_new (2, 2, FALSE);
4426   gtk_box_pack_start (GTK_BOX (vbox), impl->save_widgets_table, FALSE, FALSE, 0);
4427   gtk_widget_show (impl->save_widgets_table);
4428   gtk_table_set_row_spacings (GTK_TABLE (impl->save_widgets_table), 12);
4429   gtk_table_set_col_spacings (GTK_TABLE (impl->save_widgets_table), 12);
4430
4431   /* Label */
4432
4433   widget = gtk_label_new_with_mnemonic (_("_Name:"));
4434   gtk_widget_set_halign (widget, GTK_ALIGN_START);
4435   gtk_widget_set_valign (widget, GTK_ALIGN_CENTER);
4436   gtk_table_attach (GTK_TABLE (impl->save_widgets_table), widget,
4437                     0, 1, 0, 1,
4438                     GTK_FILL, GTK_FILL,
4439                     0, 0);
4440   gtk_widget_show (widget);
4441
4442   /* Location entry */
4443
4444   location_entry_create (impl);
4445   gtk_table_attach (GTK_TABLE (impl->save_widgets_table), impl->location_entry,
4446                     1, 2, 0, 1,
4447                     GTK_EXPAND | GTK_FILL, 0,
4448                     0, 0);
4449   gtk_widget_show (impl->location_entry);
4450   gtk_label_set_mnemonic_widget (GTK_LABEL (widget), impl->location_entry);
4451
4452   /* Folder combo */
4453   impl->save_folder_label = gtk_label_new (NULL);
4454   gtk_widget_set_halign (impl->save_folder_label, GTK_ALIGN_START);
4455   gtk_widget_set_valign (impl->save_folder_label, GTK_ALIGN_CENTER);
4456   gtk_table_attach (GTK_TABLE (impl->save_widgets_table), impl->save_folder_label,
4457                     0, 1, 1, 2,
4458                     GTK_FILL, GTK_FILL,
4459                     0, 0);
4460   gtk_widget_show (impl->save_folder_label);
4461
4462   impl->save_widgets = vbox;
4463   gtk_box_pack_start (GTK_BOX (impl), impl->save_widgets, FALSE, FALSE, 0);
4464   gtk_box_reorder_child (GTK_BOX (impl), impl->save_widgets, 0);
4465   gtk_widget_show (impl->save_widgets);
4466 }
4467
4468 /* Destroys the widgets specific to Save mode */
4469 static void
4470 save_widgets_destroy (GtkFileChooserDefault *impl)
4471 {
4472   if (impl->save_widgets == NULL)
4473     return;
4474
4475   gtk_widget_destroy (impl->save_widgets);
4476   impl->save_widgets = NULL;
4477   impl->save_widgets_table = NULL;
4478   impl->location_entry = NULL;
4479   impl->save_folder_label = NULL;
4480 }
4481
4482 /* Turns on the path bar widget.  Can be called even if we are already in that
4483  * mode.
4484  */
4485 static void
4486 location_switch_to_path_bar (GtkFileChooserDefault *impl)
4487 {
4488   if (impl->location_entry)
4489     {
4490       gtk_widget_destroy (impl->location_entry);
4491       impl->location_entry = NULL;
4492     }
4493
4494   gtk_widget_hide (impl->location_entry_box);
4495 }
4496
4497 /* Sets the full path of the current folder as the text in the location entry. */
4498 static void
4499 location_entry_set_initial_text (GtkFileChooserDefault *impl)
4500 {
4501   gchar *text, *filename;
4502
4503   if (!impl->current_folder)
4504     return;
4505
4506   filename = g_file_get_path (impl->current_folder);
4507
4508   if (filename)
4509     {
4510       text = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
4511       g_free (filename);
4512     }
4513   else
4514     text = g_file_get_uri (impl->current_folder);
4515
4516   if (text)
4517     {
4518       gboolean need_slash;
4519       int len;
4520
4521       len = strlen (text);
4522       need_slash = (text[len - 1] != G_DIR_SEPARATOR);
4523
4524       if (need_slash)
4525         {
4526           char *slash_text;
4527
4528           slash_text = g_new (char, len + 2);
4529           strcpy (slash_text, text);
4530           slash_text[len] = G_DIR_SEPARATOR;
4531           slash_text[len + 1] = 0;
4532
4533           g_free (text);
4534           text = slash_text;
4535         }
4536
4537       _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), text);
4538       g_free (text);
4539     }
4540
4541   g_free (filename);
4542 }
4543
4544 /* Turns on the location entry.  Can be called even if we are already in that
4545  * mode.
4546  */
4547 static void
4548 location_switch_to_filename_entry (GtkFileChooserDefault *impl)
4549 {
4550   /* when in search or recent files mode, we are not showing the
4551    * location_entry_box container, so there's no point in switching
4552    * to it.
4553    */
4554   if (impl->operation_mode == OPERATION_MODE_SEARCH ||
4555       impl->operation_mode == OPERATION_MODE_RECENT)
4556     return;
4557
4558   if (impl->location_entry)
4559     {
4560       gtk_widget_destroy (impl->location_entry);
4561       impl->location_entry = NULL;
4562     }
4563
4564   /* Box */
4565
4566   gtk_widget_show (impl->location_entry_box);
4567
4568   /* Entry */
4569
4570   location_entry_create (impl);
4571   gtk_box_pack_start (GTK_BOX (impl->location_entry_box), impl->location_entry, TRUE, TRUE, 0);
4572   gtk_label_set_mnemonic_widget (GTK_LABEL (impl->location_label), impl->location_entry);
4573
4574   /* Configure the entry */
4575
4576   _gtk_file_chooser_entry_set_base_folder (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), impl->current_folder);
4577
4578   /* Done */
4579
4580   gtk_widget_show (impl->location_entry);
4581   gtk_widget_grab_focus (impl->location_entry);
4582 }
4583
4584 /* Sets a new location mode.  set_buttons determines whether the toggle button
4585  * for the mode will also be changed.
4586  */
4587 static void
4588 location_mode_set (GtkFileChooserDefault *impl,
4589                    LocationMode new_mode,
4590                    gboolean set_button)
4591 {
4592   if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
4593       impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
4594     {
4595       GtkWindow *toplevel;
4596       GtkWidget *current_focus;
4597       gboolean button_active;
4598       gboolean switch_to_file_list;
4599
4600       switch (new_mode)
4601         {
4602         case LOCATION_MODE_PATH_BAR:
4603           button_active = FALSE;
4604
4605           /* The location_entry will disappear when we switch to path bar mode.  So,
4606            * we'll focus the file list in that case, to avoid having a window with
4607            * no focused widget.
4608            */
4609           toplevel = get_toplevel (GTK_WIDGET (impl));
4610           switch_to_file_list = FALSE;
4611           if (toplevel)
4612             {
4613               current_focus = gtk_window_get_focus (toplevel);
4614               if (!current_focus || current_focus == impl->location_entry)
4615                 switch_to_file_list = TRUE;
4616             }
4617
4618           location_switch_to_path_bar (impl);
4619
4620           if (switch_to_file_list)
4621             gtk_widget_grab_focus (impl->browse_files_tree_view);
4622
4623           break;
4624
4625         case LOCATION_MODE_FILENAME_ENTRY:
4626           button_active = TRUE;
4627           location_switch_to_filename_entry (impl);
4628           break;
4629
4630         default:
4631           g_assert_not_reached ();
4632           return;
4633         }
4634
4635       if (set_button)
4636         {
4637           g_signal_handlers_block_by_func (impl->location_button,
4638                                            G_CALLBACK (location_button_toggled_cb), impl);
4639
4640           gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (impl->location_button), button_active);
4641
4642           g_signal_handlers_unblock_by_func (impl->location_button,
4643                                              G_CALLBACK (location_button_toggled_cb), impl);
4644         }
4645     }
4646
4647   impl->location_mode = new_mode;
4648 }
4649
4650 static void
4651 location_toggle_popup_handler (GtkFileChooserDefault *impl)
4652 {
4653   /* when in search or recent files mode, we are not showing the
4654    * location_entry_box container, so there's no point in switching
4655    * to it.
4656    */
4657   if (impl->operation_mode == OPERATION_MODE_SEARCH ||
4658       impl->operation_mode == OPERATION_MODE_RECENT)
4659     return;
4660
4661   /* If the file entry is not visible, show it.
4662    * If it is visible, turn it off only if it is focused.  Otherwise, switch to the entry.
4663    */
4664   if (impl->location_mode == LOCATION_MODE_PATH_BAR)
4665     {
4666       location_mode_set (impl, LOCATION_MODE_FILENAME_ENTRY, TRUE);
4667     }
4668   else if (impl->location_mode == LOCATION_MODE_FILENAME_ENTRY)
4669     {
4670       if (gtk_widget_has_focus (impl->location_entry))
4671         {
4672           location_mode_set (impl, LOCATION_MODE_PATH_BAR, TRUE);
4673         }
4674       else
4675         {
4676           gtk_widget_grab_focus (impl->location_entry);
4677         }
4678     }
4679 }
4680
4681 /* Callback used when one of the location mode buttons is toggled */
4682 static void
4683 location_button_toggled_cb (GtkToggleButton *toggle,
4684                             GtkFileChooserDefault *impl)
4685 {
4686   gboolean is_active;
4687   LocationMode new_mode;
4688
4689   is_active = gtk_toggle_button_get_active (toggle);
4690
4691   if (is_active)
4692     {
4693       g_assert (impl->location_mode == LOCATION_MODE_PATH_BAR);
4694       new_mode = LOCATION_MODE_FILENAME_ENTRY;
4695     }
4696   else
4697     {
4698       g_assert (impl->location_mode == LOCATION_MODE_FILENAME_ENTRY);
4699       new_mode = LOCATION_MODE_PATH_BAR;
4700     }
4701
4702   location_mode_set (impl, new_mode, FALSE);
4703 }
4704
4705 /* Creates a toggle button for the location entry. */
4706 static void
4707 location_button_create (GtkFileChooserDefault *impl)
4708 {
4709   GtkWidget *image;
4710   const char *str;
4711
4712   image = gtk_image_new_from_stock (GTK_STOCK_EDIT, GTK_ICON_SIZE_BUTTON);
4713   gtk_widget_show (image);
4714
4715   impl->location_button = g_object_new (GTK_TYPE_TOGGLE_BUTTON,
4716                                         "image", image,
4717                                         NULL);
4718
4719   g_signal_connect (impl->location_button, "toggled",
4720                     G_CALLBACK (location_button_toggled_cb), impl);
4721
4722   str = _("Type a file name");
4723
4724   gtk_widget_set_tooltip_text (impl->location_button, str);
4725   atk_object_set_name (gtk_widget_get_accessible (impl->location_button), str);
4726 }
4727
4728 typedef enum {
4729   PATH_BAR_FOLDER_PATH,
4730   PATH_BAR_SELECT_A_FOLDER,
4731   PATH_BAR_ERROR_NO_FILENAME,
4732   PATH_BAR_ERROR_NO_FOLDER,
4733   PATH_BAR_RECENTLY_USED,
4734   PATH_BAR_SEARCH
4735 } PathBarMode;
4736
4737 /* Creates the info bar for informational messages or warnings, with its icon and label */
4738 static void
4739 info_bar_create (GtkFileChooserDefault *impl)
4740 {
4741   GtkWidget *content_area;
4742
4743   impl->browse_select_a_folder_info_bar = gtk_info_bar_new ();
4744   impl->browse_select_a_folder_icon = gtk_image_new_from_stock (GTK_STOCK_DIRECTORY, GTK_ICON_SIZE_MENU);
4745   impl->browse_select_a_folder_label = gtk_label_new (NULL);
4746
4747   content_area = gtk_info_bar_get_content_area (GTK_INFO_BAR (impl->browse_select_a_folder_info_bar));
4748
4749   gtk_box_pack_start (GTK_BOX (content_area), impl->browse_select_a_folder_icon, FALSE, FALSE, 0);
4750   gtk_box_pack_start (GTK_BOX (content_area), impl->browse_select_a_folder_label, FALSE, FALSE, 0);
4751
4752   gtk_widget_show (impl->browse_select_a_folder_icon);
4753   gtk_widget_show (impl->browse_select_a_folder_label);
4754 }
4755
4756 /* Sets the info bar to show the appropriate informational or warning message */
4757 static void
4758 info_bar_set (GtkFileChooserDefault *impl, PathBarMode mode)
4759 {
4760   char *str;
4761   gboolean free_str;
4762   GtkMessageType message_type;
4763
4764   free_str = FALSE;
4765
4766   switch (mode)
4767     {
4768     case PATH_BAR_SELECT_A_FOLDER:
4769       str = g_strconcat ("<i>", _("Please select a folder below"), "</i>", NULL);
4770       free_str = TRUE;
4771       message_type = GTK_MESSAGE_OTHER;
4772       break;
4773
4774     case PATH_BAR_ERROR_NO_FILENAME:
4775       str = _("Please type a file name");
4776       message_type = GTK_MESSAGE_WARNING;
4777       break;
4778
4779     case PATH_BAR_ERROR_NO_FOLDER:
4780       str = _("Please select a folder below");
4781       message_type = GTK_MESSAGE_WARNING;
4782       break;
4783
4784     default:
4785       g_assert_not_reached ();
4786       return;
4787     }
4788
4789   gtk_info_bar_set_message_type (GTK_INFO_BAR (impl->browse_select_a_folder_info_bar), message_type);
4790   gtk_image_set_from_stock (GTK_IMAGE (impl->browse_select_a_folder_icon),
4791                             (message_type == GTK_MESSAGE_WARNING) ? GTK_STOCK_DIALOG_WARNING : GTK_STOCK_DIRECTORY,
4792                             GTK_ICON_SIZE_MENU);
4793   gtk_label_set_markup (GTK_LABEL (impl->browse_select_a_folder_label), str);
4794
4795   if (free_str)
4796     g_free (str);
4797 }
4798
4799 /* Creates the icon and label used to show that the file chooser is in Search or Recently-used mode */
4800 static void
4801 special_mode_widgets_create (GtkFileChooserDefault *impl)
4802 {
4803   impl->browse_special_mode_icon = gtk_image_new ();
4804   gtk_size_group_add_widget (impl->browse_path_bar_size_group, impl->browse_special_mode_icon);
4805   gtk_box_pack_start (GTK_BOX (impl->browse_path_bar_hbox), impl->browse_special_mode_icon, FALSE, FALSE, 0);
4806
4807   impl->browse_special_mode_label = gtk_label_new (NULL);
4808   gtk_size_group_add_widget (impl->browse_path_bar_size_group, impl->browse_special_mode_label);
4809   gtk_box_pack_start (GTK_BOX (impl->browse_path_bar_hbox), impl->browse_special_mode_label, FALSE, FALSE, 0);
4810 }
4811
4812 /* Creates the path bar's container and eveyrthing that goes in it: location button, pathbar, info bar, and Create Folder button */
4813 static void
4814 path_bar_widgets_create (GtkFileChooserDefault *impl)
4815 {
4816   /* Location widgets - note browse_path_bar_hbox is packed in the right place until switch_path_bar() */
4817   impl->browse_path_bar_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
4818   gtk_widget_show (impl->browse_path_bar_hbox);
4819
4820   /* Size group that allows the path bar to be the same size between modes */
4821   impl->browse_path_bar_size_group = gtk_size_group_new (GTK_SIZE_GROUP_VERTICAL);
4822   gtk_size_group_set_ignore_hidden (impl->browse_path_bar_size_group, FALSE);
4823
4824   /* Location button */
4825   location_button_create (impl);
4826   gtk_size_group_add_widget (impl->browse_path_bar_size_group, impl->location_button);
4827   gtk_box_pack_start (GTK_BOX (impl->browse_path_bar_hbox), impl->location_button, FALSE, FALSE, 0);
4828
4829   /* Path bar */
4830   impl->browse_path_bar = g_object_new (GTK_TYPE_PATH_BAR, NULL);
4831   _gtk_path_bar_set_file_system (GTK_PATH_BAR (impl->browse_path_bar), impl->file_system);
4832   g_signal_connect (impl->browse_path_bar, "path-clicked", G_CALLBACK (path_bar_clicked), impl);
4833
4834   gtk_size_group_add_widget (impl->browse_path_bar_size_group, impl->browse_path_bar);
4835   gtk_box_pack_start (GTK_BOX (impl->browse_path_bar_hbox), impl->browse_path_bar, TRUE, TRUE, 0);
4836
4837   /* Info bar */
4838   info_bar_create (impl);
4839   gtk_size_group_add_widget (impl->browse_path_bar_size_group, impl->browse_select_a_folder_info_bar);
4840   gtk_box_pack_start (GTK_BOX (impl->browse_path_bar_hbox), impl->browse_select_a_folder_info_bar, TRUE, TRUE, 0);
4841
4842   /* Widgets for special modes (recently-used in Open mode, Search mode) */
4843   special_mode_widgets_create (impl);
4844
4845   /* Create Folder */
4846   impl->browse_new_folder_button = gtk_button_new_with_mnemonic (_("Create Fo_lder"));
4847   g_signal_connect (impl->browse_new_folder_button, "clicked",
4848                     G_CALLBACK (new_folder_button_clicked), impl);
4849   gtk_size_group_add_widget (impl->browse_path_bar_size_group, impl->browse_new_folder_button);
4850   gtk_box_pack_end (GTK_BOX (impl->browse_path_bar_hbox), impl->browse_new_folder_button, FALSE, FALSE, 0);
4851 }
4852
4853 /* Sets the path bar's mode to show a label, the actual folder path, or a
4854  * warning message.  You may call this function with PATH_BAR_ERROR_* directly
4855  * if the pathbar is already showing the widgets you expect; otherwise, call
4856  * path_bar_update() instead to set the appropriate widgets automatically.
4857  */
4858 static void
4859 path_bar_set_mode (GtkFileChooserDefault *impl, PathBarMode mode)
4860 {
4861   gboolean path_bar_visible             = FALSE;
4862   gboolean special_mode_widgets_visible = FALSE;
4863   gboolean info_bar_visible             = FALSE;
4864   gboolean create_folder_visible        = FALSE;
4865
4866   char *tmp;
4867
4868   switch (mode)
4869     {
4870     case PATH_BAR_FOLDER_PATH:
4871       path_bar_visible = TRUE;
4872       break;
4873
4874     case PATH_BAR_SELECT_A_FOLDER:
4875     case PATH_BAR_ERROR_NO_FILENAME:
4876     case PATH_BAR_ERROR_NO_FOLDER:
4877       info_bar_set (impl, mode);
4878       info_bar_visible = TRUE;
4879       break;
4880
4881     case PATH_BAR_RECENTLY_USED:
4882       gtk_image_set_from_icon_name (GTK_IMAGE (impl->browse_special_mode_icon), "document-open-recent", GTK_ICON_SIZE_BUTTON);
4883
4884       tmp = g_strdup_printf ("<b>%s</b>", _("Recently Used"));
4885       gtk_label_set_markup (GTK_LABEL (impl->browse_special_mode_label), tmp);
4886       g_free (tmp);
4887
4888       special_mode_widgets_visible = TRUE;
4889       break;
4890
4891     case PATH_BAR_SEARCH:
4892       gtk_image_set_from_stock (GTK_IMAGE (impl->browse_special_mode_icon), GTK_STOCK_FIND, GTK_ICON_SIZE_BUTTON);
4893
4894       tmp = g_strdup_printf ("<b>%s</b>", _("Search:"));
4895       gtk_label_set_markup (GTK_LABEL (impl->browse_special_mode_label), tmp);
4896       g_free (tmp);
4897
4898       special_mode_widgets_visible = TRUE;
4899       break;
4900
4901     default:
4902       g_assert_not_reached ();
4903     }
4904
4905   gtk_widget_set_visible (impl->browse_path_bar,                        path_bar_visible);
4906   gtk_widget_set_visible (impl->browse_special_mode_icon,               special_mode_widgets_visible);
4907   gtk_widget_set_visible (impl->browse_special_mode_label,              special_mode_widgets_visible);
4908   gtk_widget_set_visible (impl->browse_select_a_folder_info_bar,        info_bar_visible);
4909
4910   if (path_bar_visible)
4911     {
4912       if (impl->create_folders
4913           && impl->action != GTK_FILE_CHOOSER_ACTION_OPEN
4914           && impl->operation_mode != OPERATION_MODE_RECENT)
4915         create_folder_visible = TRUE;
4916     }
4917
4918   gtk_widget_set_visible (impl->browse_new_folder_button,               create_folder_visible);
4919 }
4920
4921 /* Creates the main hpaned with the widgets shared by Open and Save mode */
4922 static void
4923 browse_widgets_create (GtkFileChooserDefault *impl)
4924 {
4925   GtkWidget *hpaned;
4926   GtkWidget *widget;
4927   GtkSizeGroup *size_group;
4928
4929   impl->browse_widgets_box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
4930   gtk_box_pack_start (GTK_BOX (impl), impl->browse_widgets_box, TRUE, TRUE, 0);
4931   gtk_widget_show (impl->browse_widgets_box);
4932
4933   impl->browse_header_box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
4934   gtk_box_pack_start (GTK_BOX (impl->browse_widgets_box), impl->browse_header_box, FALSE, FALSE, 0);
4935   gtk_widget_show (impl->browse_header_box);
4936
4937   /* Path bar, info bar, and their respective machinery - the browse_path_bar_hbox will get packed elsewhere */
4938   path_bar_widgets_create (impl);
4939
4940   /* Box for the location label and entry */
4941
4942   impl->location_entry_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
4943   gtk_box_pack_start (GTK_BOX (impl->browse_header_box), impl->location_entry_box, FALSE, FALSE, 0);
4944
4945   impl->location_label = gtk_label_new_with_mnemonic (_("_Location:"));
4946   gtk_widget_show (impl->location_label);
4947   gtk_box_pack_start (GTK_BOX (impl->location_entry_box), impl->location_label, FALSE, FALSE, 0);
4948
4949   /* size group is used by the scrolled windows of the panes */
4950   size_group = gtk_size_group_new (GTK_SIZE_GROUP_VERTICAL);
4951
4952   /* Paned widget */
4953
4954   hpaned = gtk_paned_new (GTK_ORIENTATION_HORIZONTAL);
4955   gtk_widget_show (hpaned);
4956   gtk_box_pack_start (GTK_BOX (impl->browse_widgets_box), hpaned, TRUE, TRUE, 0);
4957
4958   widget = shortcuts_pane_create (impl, size_group);
4959   gtk_paned_pack1 (GTK_PANED (hpaned), widget, FALSE, FALSE);
4960   widget = file_pane_create (impl, size_group);
4961   gtk_paned_pack2 (GTK_PANED (hpaned), widget, TRUE, FALSE);
4962   gtk_paned_set_position (GTK_PANED (hpaned), 148);
4963   g_object_unref (size_group);
4964 }
4965
4966 static GObject*
4967 gtk_file_chooser_default_constructor (GType                  type,
4968                                       guint                  n_construct_properties,
4969                                       GObjectConstructParam *construct_params)
4970 {
4971   GtkFileChooserDefault *impl;
4972   GObject *object;
4973
4974   profile_start ("start", NULL);
4975
4976   object = G_OBJECT_CLASS (_gtk_file_chooser_default_parent_class)->constructor (type,
4977                                                                                 n_construct_properties,
4978                                                                                 construct_params);
4979   impl = GTK_FILE_CHOOSER_DEFAULT (object);
4980
4981   g_assert (impl->file_system);
4982
4983   gtk_widget_push_composite_child ();
4984
4985   /* Shortcuts model */
4986   shortcuts_model_create (impl);
4987
4988   /* The browse widgets */
4989   browse_widgets_create (impl);
4990
4991   /* Alignment to hold extra widget */
4992   impl->extra_align = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
4993   gtk_widget_set_halign (impl->extra_align, GTK_ALIGN_START);
4994   gtk_widget_set_valign (impl->extra_align, GTK_ALIGN_CENTER);
4995   gtk_box_pack_start (GTK_BOX (impl), impl->extra_align, FALSE, FALSE, 0);
4996
4997   gtk_widget_pop_composite_child ();
4998   update_appearance (impl);
4999
5000   profile_end ("end", NULL);
5001
5002   return object;
5003 }
5004
5005 /* Sets the extra_widget by packing it in the appropriate place */
5006 static void
5007 set_extra_widget (GtkFileChooserDefault *impl,
5008                   GtkWidget             *extra_widget)
5009 {
5010   if (extra_widget)
5011     {
5012       g_object_ref (extra_widget);
5013       /* FIXME: is this right ? */
5014       gtk_widget_show (extra_widget);
5015     }
5016
5017   if (impl->extra_widget)
5018     {
5019       gtk_container_remove (GTK_CONTAINER (impl->extra_align), impl->extra_widget);
5020       g_object_unref (impl->extra_widget);
5021     }
5022
5023   impl->extra_widget = extra_widget;
5024   if (impl->extra_widget)
5025     {
5026       gtk_container_add (GTK_CONTAINER (impl->extra_align), impl->extra_widget);
5027       gtk_widget_show (impl->extra_align);
5028     }
5029   else
5030     gtk_widget_hide (impl->extra_align);
5031 }
5032
5033 static void
5034 set_local_only (GtkFileChooserDefault *impl,
5035                 gboolean               local_only)
5036 {
5037   if (local_only != impl->local_only)
5038     {
5039       impl->local_only = local_only;
5040
5041       if (impl->location_entry)
5042         _gtk_file_chooser_entry_set_local_only (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), local_only);
5043
5044       if (impl->shortcuts_model && impl->file_system)
5045         {
5046           shortcuts_add_volumes (impl);
5047           shortcuts_add_bookmarks (impl);
5048         }
5049
5050       if (local_only && impl->current_folder &&
5051            !g_file_is_native (impl->current_folder))
5052         {
5053           /* If we are pointing to a non-local folder, make an effort to change
5054            * back to a local folder, but it's really up to the app to not cause
5055            * such a situation, so we ignore errors.
5056            */
5057           const gchar *home = g_get_home_dir ();
5058           GFile *home_file;
5059
5060           if (home == NULL)
5061             return;
5062
5063           home_file = g_file_new_for_path (home);
5064
5065           gtk_file_chooser_set_current_folder_file (GTK_FILE_CHOOSER (impl), home_file, NULL);
5066
5067           g_object_unref (home_file);
5068         }
5069     }
5070 }
5071
5072 static void
5073 volumes_bookmarks_changed_cb (GtkFileSystem         *file_system,
5074                               GtkFileChooserDefault *impl)
5075 {
5076   shortcuts_add_volumes (impl);
5077   shortcuts_add_bookmarks (impl);
5078
5079   bookmarks_check_add_sensitivity (impl);
5080   bookmarks_check_remove_sensitivity (impl);
5081   shortcuts_check_popup_sensitivity (impl);
5082 }
5083
5084 /* Sets the file chooser to multiple selection mode */
5085 static void
5086 set_select_multiple (GtkFileChooserDefault *impl,
5087                      gboolean               select_multiple,
5088                      gboolean               property_notify)
5089 {
5090   GtkTreeSelection *selection;
5091   GtkSelectionMode mode;
5092
5093   if (select_multiple == impl->select_multiple)
5094     return;
5095
5096   mode = select_multiple ? GTK_SELECTION_MULTIPLE : GTK_SELECTION_BROWSE;
5097
5098   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
5099   gtk_tree_selection_set_mode (selection, mode);
5100
5101   gtk_tree_view_set_rubber_banding (GTK_TREE_VIEW (impl->browse_files_tree_view), select_multiple);
5102
5103   impl->select_multiple = select_multiple;
5104   g_object_notify (G_OBJECT (impl), "select-multiple");
5105
5106   check_preview_change (impl);
5107 }
5108
5109 static void
5110 set_file_system_backend (GtkFileChooserDefault *impl)
5111 {
5112   profile_start ("start for backend", "default");
5113
5114   impl->file_system = _gtk_file_system_new ();
5115
5116   g_signal_connect (impl->file_system, "volumes-changed",
5117                     G_CALLBACK (volumes_bookmarks_changed_cb), impl);
5118   g_signal_connect (impl->file_system, "bookmarks-changed",
5119                     G_CALLBACK (volumes_bookmarks_changed_cb), impl);
5120
5121   profile_end ("end", NULL);
5122 }
5123
5124 static void
5125 unset_file_system_backend (GtkFileChooserDefault *impl)
5126 {
5127   g_signal_handlers_disconnect_by_func (impl->file_system,
5128                                         G_CALLBACK (volumes_bookmarks_changed_cb), impl);
5129
5130   g_object_unref (impl->file_system);
5131
5132   impl->file_system = NULL;
5133 }
5134
5135 /* Saves the widgets around the pathbar so they can be reparented later
5136  * in the correct place.  This function must be called paired with
5137  * restore_path_bar().
5138  */
5139 static void
5140 save_path_bar (GtkFileChooserDefault *impl)
5141 {
5142   GtkWidget *parent;
5143
5144   g_object_ref (impl->browse_path_bar_hbox);
5145
5146   parent = gtk_widget_get_parent (impl->browse_path_bar_hbox);
5147   if (parent)
5148     gtk_container_remove (GTK_CONTAINER (parent), impl->browse_path_bar_hbox);
5149 }
5150
5151 /* Reparents the path bar and the "Create folder" button to the right place:
5152  * Above the file list in Open mode, or to the right of the "Save in folder:"
5153  * label in Save mode.  The save_path_bar() function must be called before this
5154  * one.
5155  */
5156 static void
5157 restore_path_bar (GtkFileChooserDefault *impl)
5158 {
5159   if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN
5160       || impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
5161     {
5162       gtk_box_pack_start (GTK_BOX (impl->browse_header_box), impl->browse_path_bar_hbox, FALSE, FALSE, 0);
5163       gtk_box_reorder_child (GTK_BOX (impl->browse_header_box), impl->browse_path_bar_hbox, 0);
5164     }
5165   else if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
5166            || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
5167     {
5168       gtk_table_attach (GTK_TABLE (impl->save_widgets_table), impl->browse_path_bar_hbox,
5169                         1, 2, 1, 2,
5170                         GTK_EXPAND | GTK_FILL, GTK_FILL,
5171                         0, 0);
5172       gtk_label_set_mnemonic_widget (GTK_LABEL (impl->save_folder_label), impl->browse_path_bar);
5173     }
5174   else
5175     g_assert_not_reached ();
5176
5177   g_object_unref (impl->browse_path_bar_hbox);
5178 }
5179
5180 /* Takes the folder stored in a row in the recent_model, and puts it in the pathbar */
5181 static void
5182 put_recent_folder_in_pathbar (GtkFileChooserDefault *impl, GtkTreeIter *iter)
5183 {
5184   GFile *file;
5185
5186   gtk_tree_model_get (GTK_TREE_MODEL (impl->recent_model), iter,
5187                       MODEL_COL_FILE, &file,
5188                       -1);
5189   _gtk_path_bar_set_file (GTK_PATH_BAR (impl->browse_path_bar), file, FALSE, NULL); /* NULL-GError */
5190   g_object_unref (file);
5191 }
5192
5193 /* Sets the pathbar in the appropriate mode according to the current operation mode and action.  This is the central function for
5194  * dealing with the pathbar's widgets; as long as impl->action and impl->operation_mode are set correctly, then calling this
5195  * function will update all the pathbar's widgets.
5196  */
5197 static void
5198 path_bar_update (GtkFileChooserDefault *impl)
5199 {
5200   PathBarMode mode;
5201
5202   switch (impl->operation_mode)
5203     {
5204     case OPERATION_MODE_BROWSE:
5205       mode = PATH_BAR_FOLDER_PATH;
5206       break;
5207
5208     case OPERATION_MODE_RECENT:
5209       if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
5210         {
5211           GtkTreeSelection *selection;
5212           gboolean have_selected;
5213           GtkTreeIter iter;
5214
5215           selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
5216
5217           /* Save mode means single-selection mode, so the following is valid */
5218           have_selected = gtk_tree_selection_get_selected (selection, NULL, &iter);
5219
5220           if (have_selected)
5221             {
5222               mode = PATH_BAR_FOLDER_PATH;
5223               put_recent_folder_in_pathbar (impl, &iter);
5224             }
5225           else
5226             mode = PATH_BAR_SELECT_A_FOLDER;
5227         }
5228       else
5229         mode = PATH_BAR_RECENTLY_USED;
5230
5231       break;
5232
5233     case OPERATION_MODE_SEARCH:
5234       mode = PATH_BAR_SEARCH;
5235       break;
5236
5237     default:
5238       g_assert_not_reached ();
5239       return;
5240     }
5241
5242   path_bar_set_mode (impl, mode);
5243 }
5244
5245 static void
5246 operation_mode_discard_search_widgets (GtkFileChooserDefault *impl)
5247 {
5248   if (impl->search_hbox)
5249     {
5250       gtk_widget_destroy (impl->search_hbox);
5251
5252       impl->search_hbox = NULL;
5253       impl->search_entry = NULL;
5254     }
5255 }
5256
5257 /* Stops running operations like populating the browse model, searches, and the recent-files model */
5258 static void
5259 operation_mode_stop (GtkFileChooserDefault *impl, OperationMode mode)
5260 {
5261   switch (mode)
5262     {
5263     case OPERATION_MODE_BROWSE:
5264       stop_loading_and_clear_list_model (impl, TRUE);
5265       break;
5266
5267     case OPERATION_MODE_SEARCH:
5268       search_stop_searching (impl, FALSE);
5269       search_clear_model (impl, TRUE);
5270
5271       operation_mode_discard_search_widgets (impl);
5272       break;
5273
5274     case OPERATION_MODE_RECENT:
5275       recent_stop_loading (impl);
5276       recent_clear_model (impl, TRUE);
5277       break;
5278
5279     default:
5280       g_assert_not_reached ();
5281     }
5282 }
5283
5284 static void
5285 operation_mode_set_browse (GtkFileChooserDefault *impl)
5286 {
5287   path_bar_update (impl);
5288
5289   if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
5290       impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
5291     {
5292       gtk_widget_show (impl->location_button);
5293
5294       if (impl->location_mode == LOCATION_MODE_FILENAME_ENTRY)
5295         gtk_widget_show (impl->location_entry_box);
5296     }
5297 }
5298
5299 static void
5300 operation_mode_set_search (GtkFileChooserDefault *impl)
5301 {
5302   g_assert (impl->search_hbox == NULL);
5303   g_assert (impl->search_entry == NULL);
5304   g_assert (impl->search_model == NULL);
5305
5306   search_setup_widgets (impl);
5307 }
5308
5309 static void
5310 operation_mode_set_recent (GtkFileChooserDefault *impl)
5311 {
5312   path_bar_update (impl);
5313
5314   /* Hide the location widgets temporarily */
5315   if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
5316       impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
5317     {
5318       gtk_widget_hide (impl->location_button);
5319       gtk_widget_hide (impl->location_entry_box);
5320     }
5321
5322   recent_start_loading (impl);
5323 }
5324
5325 /* Sometimes we need to frob the selection in the shortcuts list manually */
5326 static void
5327 shortcuts_select_item_without_activating (GtkFileChooserDefault *impl, int pos)
5328 {
5329   GtkTreeSelection *selection;
5330   GtkTreePath *path;
5331
5332   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view));
5333
5334   g_signal_handlers_block_by_func (selection, G_CALLBACK (shortcuts_selection_changed_cb), impl);
5335
5336   path = gtk_tree_path_new_from_indices (pos, -1);
5337   gtk_tree_selection_select_path (selection, path);
5338   gtk_tree_path_free (path);
5339
5340   g_signal_handlers_unblock_by_func (selection, G_CALLBACK (shortcuts_selection_changed_cb), impl);
5341 }
5342
5343 static void
5344 operation_mode_set (GtkFileChooserDefault *impl, OperationMode mode)
5345 {
5346   ShortcutsIndex shortcut_to_select;
5347
5348   operation_mode_stop (impl, impl->operation_mode);
5349
5350   impl->operation_mode = mode;
5351
5352   switch (impl->operation_mode)
5353     {
5354     case OPERATION_MODE_BROWSE:
5355       operation_mode_set_browse (impl);
5356       shortcut_to_select = SHORTCUTS_CURRENT_FOLDER;
5357       break;
5358
5359     case OPERATION_MODE_SEARCH:
5360       operation_mode_set_search (impl);
5361       shortcut_to_select = SHORTCUTS_SEARCH;
5362       break;
5363
5364     case OPERATION_MODE_RECENT:
5365       operation_mode_set_recent (impl);
5366       shortcut_to_select = SHORTCUTS_RECENT;
5367       break;
5368
5369     default:
5370       g_assert_not_reached ();
5371       return;
5372     }
5373
5374   if (shortcut_to_select != SHORTCUTS_CURRENT_FOLDER)
5375     shortcuts_select_item_without_activating (impl, shortcuts_get_index (impl, shortcut_to_select));
5376 }
5377
5378 /* This function is basically a do_all function.
5379  *
5380  * It sets the visibility on all the widgets based on the current state, and
5381  * moves the custom_widget if needed.
5382  */
5383 static void
5384 update_appearance (GtkFileChooserDefault *impl)
5385 {
5386   save_path_bar (impl);
5387
5388   if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE ||
5389       impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
5390     {
5391       const char *text;
5392
5393       gtk_widget_hide (impl->location_button);
5394       save_widgets_create (impl);
5395
5396       if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
5397         text = _("Save in _folder:");
5398       else
5399         text = _("Create in _folder:");
5400
5401       gtk_label_set_text_with_mnemonic (GTK_LABEL (impl->save_folder_label), text);
5402
5403       if (impl->select_multiple)
5404         {
5405           g_warning ("Save mode cannot be set in conjunction with multiple selection mode.  "
5406                      "Re-setting to single selection mode.");
5407           set_select_multiple (impl, FALSE, TRUE);
5408         }
5409     }
5410   else if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
5411            impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
5412     {
5413       gtk_widget_show (impl->location_button);
5414       save_widgets_destroy (impl);
5415       location_mode_set (impl, impl->location_mode, TRUE);
5416     }
5417
5418   if (impl->location_entry)
5419     _gtk_file_chooser_entry_set_action (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), impl->action);
5420
5421   restore_path_bar (impl);
5422   path_bar_update (impl);
5423
5424   /* This *is* needed; we need to redraw the file list because the "sensitivity"
5425    * of files may change depending whether we are in a file or folder-only mode.
5426    */
5427   gtk_widget_queue_draw (impl->browse_files_tree_view);
5428
5429   emit_default_size_changed (impl);
5430 }
5431
5432 static void
5433 gtk_file_chooser_default_set_property (GObject      *object,
5434                                        guint         prop_id,
5435                                        const GValue *value,
5436                                        GParamSpec   *pspec)
5437
5438 {
5439   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (object);
5440
5441   switch (prop_id)
5442     {
5443     case GTK_FILE_CHOOSER_PROP_ACTION:
5444       {
5445         GtkFileChooserAction action = g_value_get_enum (value);
5446
5447         if (action != impl->action)
5448           {
5449             gtk_file_chooser_default_unselect_all (GTK_FILE_CHOOSER (impl));
5450             
5451             if ((action == GTK_FILE_CHOOSER_ACTION_SAVE ||
5452                  action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
5453                 && impl->select_multiple)
5454               {
5455                 g_warning ("Tried to change the file chooser action to SAVE or CREATE_FOLDER, but "
5456                            "this is not allowed in multiple selection mode.  Resetting the file chooser "
5457                            "to single selection mode.");
5458                 set_select_multiple (impl, FALSE, TRUE);
5459               }
5460             impl->action = action;
5461             update_cell_renderer_attributes (impl);
5462             update_appearance (impl);
5463             settings_load (impl);
5464           }
5465       }
5466       break;
5467
5468     case GTK_FILE_CHOOSER_PROP_FILTER:
5469       set_current_filter (impl, g_value_get_object (value));
5470       break;
5471
5472     case GTK_FILE_CHOOSER_PROP_LOCAL_ONLY:
5473       set_local_only (impl, g_value_get_boolean (value));
5474       break;
5475
5476     case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET:
5477       set_preview_widget (impl, g_value_get_object (value));
5478       break;
5479
5480     case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET_ACTIVE:
5481       impl->preview_widget_active = g_value_get_boolean (value);
5482       update_preview_widget_visibility (impl);
5483       break;
5484
5485     case GTK_FILE_CHOOSER_PROP_USE_PREVIEW_LABEL:
5486       impl->use_preview_label = g_value_get_boolean (value);
5487       update_preview_widget_visibility (impl);
5488       break;
5489
5490     case GTK_FILE_CHOOSER_PROP_EXTRA_WIDGET:
5491       set_extra_widget (impl, g_value_get_object (value));
5492       break;
5493
5494     case GTK_FILE_CHOOSER_PROP_SELECT_MULTIPLE:
5495       {
5496         gboolean select_multiple = g_value_get_boolean (value);
5497         if ((impl->action == GTK_FILE_CHOOSER_ACTION_SAVE ||
5498              impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
5499             && select_multiple)
5500           {
5501             g_warning ("Tried to set the file chooser to multiple selection mode, but this is "
5502                        "not allowed in SAVE or CREATE_FOLDER modes.  Ignoring the change and "
5503                        "leaving the file chooser in single selection mode.");
5504             return;
5505           }
5506
5507         set_select_multiple (impl, select_multiple, FALSE);
5508       }
5509       break;
5510
5511     case GTK_FILE_CHOOSER_PROP_SHOW_HIDDEN:
5512       {
5513         gboolean show_hidden = g_value_get_boolean (value);
5514         if (show_hidden != impl->show_hidden)
5515           {
5516             impl->show_hidden = show_hidden;
5517
5518             if (impl->browse_files_model)
5519               _gtk_file_system_model_set_show_hidden (impl->browse_files_model, show_hidden);
5520           }
5521       }
5522       break;
5523
5524     case GTK_FILE_CHOOSER_PROP_DO_OVERWRITE_CONFIRMATION:
5525       {
5526         gboolean do_overwrite_confirmation = g_value_get_boolean (value);
5527         impl->do_overwrite_confirmation = do_overwrite_confirmation;
5528       }
5529       break;
5530
5531     case GTK_FILE_CHOOSER_PROP_CREATE_FOLDERS:
5532       {
5533         gboolean create_folders = g_value_get_boolean (value);
5534         impl->create_folders = create_folders;
5535         update_appearance (impl);
5536       }
5537       break;
5538
5539     default:
5540       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
5541       break;
5542     }
5543 }
5544
5545 static void
5546 gtk_file_chooser_default_get_property (GObject    *object,
5547                                        guint       prop_id,
5548                                        GValue     *value,
5549                                        GParamSpec *pspec)
5550 {
5551   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (object);
5552
5553   switch (prop_id)
5554     {
5555     case GTK_FILE_CHOOSER_PROP_ACTION:
5556       g_value_set_enum (value, impl->action);
5557       break;
5558
5559     case GTK_FILE_CHOOSER_PROP_FILTER:
5560       g_value_set_object (value, impl->current_filter);
5561       break;
5562
5563     case GTK_FILE_CHOOSER_PROP_LOCAL_ONLY:
5564       g_value_set_boolean (value, impl->local_only);
5565       break;
5566
5567     case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET:
5568       g_value_set_object (value, impl->preview_widget);
5569       break;
5570
5571     case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET_ACTIVE:
5572       g_value_set_boolean (value, impl->preview_widget_active);
5573       break;
5574
5575     case GTK_FILE_CHOOSER_PROP_USE_PREVIEW_LABEL:
5576       g_value_set_boolean (value, impl->use_preview_label);
5577       break;
5578
5579     case GTK_FILE_CHOOSER_PROP_EXTRA_WIDGET:
5580       g_value_set_object (value, impl->extra_widget);
5581       break;
5582
5583     case GTK_FILE_CHOOSER_PROP_SELECT_MULTIPLE:
5584       g_value_set_boolean (value, impl->select_multiple);
5585       break;
5586
5587     case GTK_FILE_CHOOSER_PROP_SHOW_HIDDEN:
5588       g_value_set_boolean (value, impl->show_hidden);
5589       break;
5590
5591     case GTK_FILE_CHOOSER_PROP_DO_OVERWRITE_CONFIRMATION:
5592       g_value_set_boolean (value, impl->do_overwrite_confirmation);
5593       break;
5594
5595     case GTK_FILE_CHOOSER_PROP_CREATE_FOLDERS:
5596       g_value_set_boolean (value, impl->create_folders);
5597       break;
5598
5599     default:
5600       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
5601       break;
5602     }
5603 }
5604
5605 /* This cancels everything that may be going on in the background. */
5606 static void
5607 cancel_all_operations (GtkFileChooserDefault *impl)
5608 {
5609   GSList *l;
5610
5611   pending_select_files_free (impl);
5612
5613   /* cancel all pending operations */
5614   if (impl->pending_cancellables)
5615     {
5616       for (l = impl->pending_cancellables; l; l = l->next)
5617         {
5618           GCancellable *cancellable = G_CANCELLABLE (l->data);
5619           g_cancellable_cancel (cancellable);
5620         }
5621       g_slist_free (impl->pending_cancellables);
5622       impl->pending_cancellables = NULL;
5623     }
5624
5625   if (impl->reload_icon_cancellables)
5626     {
5627       for (l = impl->reload_icon_cancellables; l; l = l->next)
5628         {
5629           GCancellable *cancellable = G_CANCELLABLE (l->data);
5630           g_cancellable_cancel (cancellable);
5631         }
5632       g_slist_free (impl->reload_icon_cancellables);
5633       impl->reload_icon_cancellables = NULL;
5634     }
5635
5636   if (impl->loading_shortcuts)
5637     {
5638       for (l = impl->loading_shortcuts; l; l = l->next)
5639         {
5640           GCancellable *cancellable = G_CANCELLABLE (l->data);
5641           g_cancellable_cancel (cancellable);
5642         }
5643       g_slist_free (impl->loading_shortcuts);
5644       impl->loading_shortcuts = NULL;
5645     }
5646
5647   if (impl->file_list_drag_data_received_cancellable)
5648     {
5649       g_cancellable_cancel (impl->file_list_drag_data_received_cancellable);
5650       impl->file_list_drag_data_received_cancellable = NULL;
5651     }
5652
5653   if (impl->update_current_folder_cancellable)
5654     {
5655       g_cancellable_cancel (impl->update_current_folder_cancellable);
5656       impl->update_current_folder_cancellable = NULL;
5657     }
5658
5659   if (impl->should_respond_get_info_cancellable)
5660     {
5661       g_cancellable_cancel (impl->should_respond_get_info_cancellable);
5662       impl->should_respond_get_info_cancellable = NULL;
5663     }
5664
5665   if (impl->update_from_entry_cancellable)
5666     {
5667       g_cancellable_cancel (impl->update_from_entry_cancellable);
5668       impl->update_from_entry_cancellable = NULL;
5669     }
5670
5671   if (impl->shortcuts_activate_iter_cancellable)
5672     {
5673       g_cancellable_cancel (impl->shortcuts_activate_iter_cancellable);
5674       impl->shortcuts_activate_iter_cancellable = NULL;
5675     }
5676
5677   search_stop_searching (impl, TRUE);
5678   recent_stop_loading (impl);
5679 }
5680
5681 /* Removes the settings signal handler.  It's safe to call multiple times */
5682 static void
5683 remove_settings_signal (GtkFileChooserDefault *impl,
5684                         GdkScreen             *screen)
5685 {
5686   if (impl->settings_signal_id)
5687     {
5688       GtkSettings *settings;
5689
5690       settings = gtk_settings_get_for_screen (screen);
5691       g_signal_handler_disconnect (settings,
5692                                    impl->settings_signal_id);
5693       impl->settings_signal_id = 0;
5694     }
5695 }
5696
5697 static void
5698 gtk_file_chooser_default_dispose (GObject *object)
5699 {
5700   GtkFileChooserDefault *impl = (GtkFileChooserDefault *) object;
5701
5702   cancel_all_operations (impl);
5703
5704   if (impl->extra_widget)
5705     {
5706       g_object_unref (impl->extra_widget);
5707       impl->extra_widget = NULL;
5708     }
5709
5710   remove_settings_signal (impl, gtk_widget_get_screen (GTK_WIDGET (impl)));
5711
5712   G_OBJECT_CLASS (_gtk_file_chooser_default_parent_class)->dispose (object);
5713 }
5714
5715 /* We override show-all since we have internal widgets that
5716  * shouldn't be shown when you call show_all(), like the filter
5717  * combo box.
5718  */
5719 static void
5720 gtk_file_chooser_default_show_all (GtkWidget *widget)
5721 {
5722   GtkFileChooserDefault *impl = (GtkFileChooserDefault *) widget;
5723
5724   gtk_widget_show (widget);
5725
5726   if (impl->extra_widget)
5727     gtk_widget_show_all (impl->extra_widget);
5728 }
5729
5730 /* Handler for GtkWindow::set-focus; this is where we save the last-focused
5731  * widget on our toplevel.  See gtk_file_chooser_default_hierarchy_changed()
5732  */
5733 static void
5734 toplevel_set_focus_cb (GtkWindow             *window,
5735                        GtkWidget             *focus,
5736                        GtkFileChooserDefault *impl)
5737 {
5738   impl->toplevel_last_focus_widget = gtk_window_get_focus (window);
5739 }
5740
5741 /* We monitor the focus widget on our toplevel to be able to know which widget
5742  * was last focused at the time our "should_respond" method gets called.
5743  */
5744 static void
5745 gtk_file_chooser_default_hierarchy_changed (GtkWidget *widget,
5746                                             GtkWidget *previous_toplevel)
5747 {
5748   GtkFileChooserDefault *impl;
5749   GtkWidget *toplevel;
5750
5751   impl = GTK_FILE_CHOOSER_DEFAULT (widget);
5752   toplevel = gtk_widget_get_toplevel (widget);
5753
5754   if (previous_toplevel && 
5755       impl->toplevel_set_focus_id != 0)
5756     {
5757       g_signal_handler_disconnect (previous_toplevel,
5758                                    impl->toplevel_set_focus_id);
5759       impl->toplevel_set_focus_id = 0;
5760       impl->toplevel_last_focus_widget = NULL;
5761     }
5762
5763   if (gtk_widget_is_toplevel (toplevel))
5764     {
5765       g_assert (impl->toplevel_set_focus_id == 0);
5766       impl->toplevel_set_focus_id = g_signal_connect (toplevel, "set-focus",
5767                                                       G_CALLBACK (toplevel_set_focus_cb), impl);
5768       impl->toplevel_last_focus_widget = gtk_window_get_focus (GTK_WINDOW (toplevel));
5769     }
5770 }
5771
5772 /* Changes the icons wherever it is needed */
5773 static void
5774 change_icon_theme (GtkFileChooserDefault *impl)
5775 {
5776   GtkSettings *settings;
5777   gint width, height;
5778   GtkCellRenderer *renderer;
5779   GList *cells;
5780
5781   profile_start ("start", NULL);
5782
5783   settings = gtk_settings_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (impl)));
5784
5785   if (gtk_icon_size_lookup_for_settings (settings, GTK_ICON_SIZE_MENU, &width, &height))
5786     impl->icon_size = MAX (width, height);
5787   else
5788     impl->icon_size = FALLBACK_ICON_SIZE;
5789
5790   shortcuts_reload_icons (impl);
5791   /* the first cell in the first column is the icon column, and we have a fixed size there */
5792   cells = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (
5793         gtk_tree_view_get_column (GTK_TREE_VIEW (impl->browse_files_tree_view), 0)));
5794   renderer = GTK_CELL_RENDERER (cells->data);
5795   set_icon_cell_renderer_fixed_size (impl, renderer);
5796   g_list_free (cells);
5797   if (impl->browse_files_model)
5798     _gtk_file_system_model_clear_cache (impl->browse_files_model, MODEL_COL_PIXBUF);
5799   gtk_widget_queue_resize (impl->browse_files_tree_view);
5800
5801   profile_end ("end", NULL);
5802 }
5803
5804 /* Callback used when a GtkSettings value changes */
5805 static void
5806 settings_notify_cb (GObject               *object,
5807                     GParamSpec            *pspec,
5808                     GtkFileChooserDefault *impl)
5809 {
5810   const char *name;
5811
5812   profile_start ("start", NULL);
5813
5814   name = g_param_spec_get_name (pspec);
5815
5816   if (strcmp (name, "gtk-icon-theme-name") == 0 ||
5817       strcmp (name, "gtk-icon-sizes") == 0)
5818     change_icon_theme (impl);
5819
5820   profile_end ("end", NULL);
5821 }
5822
5823 /* Installs a signal handler for GtkSettings so that we can monitor changes in
5824  * the icon theme.
5825  */
5826 static void
5827 check_icon_theme (GtkFileChooserDefault *impl)
5828 {
5829   GtkSettings *settings;
5830
5831   profile_start ("start", NULL);
5832
5833   if (impl->settings_signal_id)
5834     {
5835       profile_end ("end", NULL);
5836       return;
5837     }
5838
5839   if (gtk_widget_has_screen (GTK_WIDGET (impl)))
5840     {
5841       settings = gtk_settings_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (impl)));
5842       impl->settings_signal_id = g_signal_connect (settings, "notify",
5843                                                    G_CALLBACK (settings_notify_cb), impl);
5844
5845       change_icon_theme (impl);
5846     }
5847
5848   profile_end ("end", NULL);
5849 }
5850
5851 static void
5852 gtk_file_chooser_default_style_updated (GtkWidget *widget)
5853 {
5854   GtkFileChooserDefault *impl;
5855
5856   profile_start ("start", NULL);
5857
5858   impl = GTK_FILE_CHOOSER_DEFAULT (widget);
5859
5860   profile_msg ("    parent class style_udpated start", NULL);
5861   GTK_WIDGET_CLASS (_gtk_file_chooser_default_parent_class)->style_updated (widget);
5862   profile_msg ("    parent class style_updated end", NULL);
5863
5864   if (gtk_widget_has_screen (GTK_WIDGET (impl)))
5865     change_icon_theme (impl);
5866
5867   emit_default_size_changed (impl);
5868
5869   profile_end ("end", NULL);
5870 }
5871
5872 static void
5873 gtk_file_chooser_default_screen_changed (GtkWidget *widget,
5874                                          GdkScreen *previous_screen)
5875 {
5876   GtkFileChooserDefault *impl;
5877
5878   profile_start ("start", NULL);
5879
5880   impl = GTK_FILE_CHOOSER_DEFAULT (widget);
5881
5882   if (GTK_WIDGET_CLASS (_gtk_file_chooser_default_parent_class)->screen_changed)
5883     GTK_WIDGET_CLASS (_gtk_file_chooser_default_parent_class)->screen_changed (widget, previous_screen);
5884
5885   remove_settings_signal (impl, previous_screen);
5886   check_icon_theme (impl);
5887
5888   emit_default_size_changed (impl);
5889
5890   profile_end ("end", NULL);
5891 }
5892
5893 static void
5894 gtk_file_chooser_default_size_allocate (GtkWidget     *widget,
5895                                         GtkAllocation *allocation)
5896 {
5897   GTK_WIDGET_CLASS (_gtk_file_chooser_default_parent_class)->size_allocate (widget, allocation);
5898 }
5899
5900 static void
5901 set_sort_column (GtkFileChooserDefault *impl)
5902 {
5903   GtkTreeSortable *sortable;
5904
5905   sortable = GTK_TREE_SORTABLE (gtk_tree_view_get_model (GTK_TREE_VIEW (impl->browse_files_tree_view)));
5906
5907   /* can happen when we're still populating the model */
5908   if (sortable == NULL)
5909     return;
5910
5911   gtk_tree_sortable_set_sort_column_id (sortable,
5912                                         impl->sort_column,
5913                                         impl->sort_order);
5914 }
5915
5916 static void
5917 settings_ensure (GtkFileChooserDefault *impl)
5918 {
5919   if (impl->settings != NULL)
5920     return;
5921
5922   impl->settings = g_settings_new_with_path ("org.gtk.Settings.FileChooser",
5923                                              "/org/gtk/settings/file-chooser/");
5924   g_settings_delay (impl->settings);
5925 }
5926
5927 static void
5928 settings_load (GtkFileChooserDefault *impl)
5929 {
5930   LocationMode location_mode;
5931   gboolean show_hidden;
5932   gboolean show_size_column;
5933   gint sort_column;
5934   GtkSortType sort_order;
5935
5936   settings_ensure (impl);
5937
5938   location_mode = g_settings_get_enum (impl->settings, SETTINGS_KEY_LOCATION_MODE);
5939   show_hidden = g_settings_get_boolean (impl->settings, SETTINGS_KEY_SHOW_HIDDEN);
5940   show_size_column = g_settings_get_boolean (impl->settings, SETTINGS_KEY_SHOW_SIZE_COLUMN);
5941   sort_column = g_settings_get_enum (impl->settings, SETTINGS_KEY_SORT_COLUMN);
5942   sort_order = g_settings_get_enum (impl->settings, SETTINGS_KEY_SORT_ORDER);
5943
5944   location_mode_set (impl, location_mode, TRUE);
5945
5946   gtk_file_chooser_set_show_hidden (GTK_FILE_CHOOSER (impl), show_hidden);
5947
5948   impl->show_size_column = show_size_column;
5949   gtk_tree_view_column_set_visible (impl->list_size_column, show_size_column);
5950
5951   impl->sort_column = sort_column;
5952   impl->sort_order = sort_order;
5953   /* We don't call set_sort_column() here as the models may not have been
5954    * created yet.  The individual functions that create and set the models will
5955    * call set_sort_column() themselves.
5956    */
5957 }
5958
5959 static void
5960 save_dialog_geometry (GtkFileChooserDefault *impl)
5961 {
5962   GtkWindow *toplevel;
5963   int x, y, width, height;
5964
5965   toplevel = get_toplevel (GTK_WIDGET (impl));
5966
5967   if (!(toplevel && GTK_IS_FILE_CHOOSER_DIALOG (toplevel)))
5968     return;
5969
5970   gtk_window_get_position (toplevel, &x, &y);
5971   gtk_window_get_size (toplevel, &width, &height);
5972
5973   g_settings_set (impl->settings, "window-position", "(ii)", x, y);
5974   g_settings_set (impl->settings, "window-size", "(ii)", width, height);
5975 }
5976
5977 static void
5978 settings_save (GtkFileChooserDefault *impl)
5979 {
5980   char *current_folder_uri;
5981
5982   settings_ensure (impl);
5983
5984   /* Current folder */
5985
5986   if (impl->current_folder)
5987     current_folder_uri = g_file_get_uri (impl->current_folder);
5988   else
5989     current_folder_uri = "";
5990
5991   g_settings_set_string (impl->settings, SETTINGS_KEY_LAST_FOLDER_URI, current_folder_uri);
5992
5993   if (impl->current_folder)
5994     g_free (current_folder_uri);
5995
5996   /* All the other state */
5997
5998   g_settings_set_enum (impl->settings, SETTINGS_KEY_LOCATION_MODE, impl->location_mode);
5999   g_settings_set_boolean (impl->settings, SETTINGS_KEY_SHOW_HIDDEN,
6000                           gtk_file_chooser_get_show_hidden (GTK_FILE_CHOOSER (impl)));
6001   g_settings_set_boolean (impl->settings, SETTINGS_KEY_SHOW_SIZE_COLUMN, impl->show_size_column);
6002   g_settings_set_enum (impl->settings, SETTINGS_KEY_SORT_COLUMN, impl->sort_column);
6003   g_settings_set_enum (impl->settings, SETTINGS_KEY_SORT_ORDER, impl->sort_order);
6004
6005   save_dialog_geometry (impl);
6006
6007   /* Now apply the settings */
6008   g_settings_apply (impl->settings);
6009
6010   g_object_unref (impl->settings);
6011   impl->settings = NULL;
6012 }
6013
6014 /* GtkWidget::realize method */
6015 static void
6016 gtk_file_chooser_default_realize (GtkWidget *widget)
6017 {
6018   GtkFileChooserDefault *impl;
6019
6020   impl = GTK_FILE_CHOOSER_DEFAULT (widget);
6021
6022   GTK_WIDGET_CLASS (_gtk_file_chooser_default_parent_class)->realize (widget);
6023
6024   emit_default_size_changed (impl);
6025 }
6026
6027 static GFile *
6028 get_file_for_last_folder_opened (GtkFileChooserDefault *impl)
6029 {
6030   char *last_folder_uri;
6031   GFile *file;
6032
6033   settings_ensure (impl);
6034
6035   last_folder_uri = g_settings_get_string (impl->settings, SETTINGS_KEY_LAST_FOLDER_URI);
6036
6037   /* If no last folder is set, we use the user's home directory, since
6038    * this is the starting point for most documents.
6039    */
6040   if (last_folder_uri[0] == '\0')
6041     file = g_file_new_for_path (g_get_home_dir ());
6042   else
6043     file = g_file_new_for_uri (last_folder_uri);
6044
6045   g_free (last_folder_uri);
6046
6047   return file;
6048 }
6049
6050 /* GtkWidget::map method */
6051 static void
6052 gtk_file_chooser_default_map (GtkWidget *widget)
6053 {
6054   GtkFileChooserDefault *impl;
6055
6056   profile_start ("start", NULL);
6057
6058   impl = GTK_FILE_CHOOSER_DEFAULT (widget);
6059
6060   GTK_WIDGET_CLASS (_gtk_file_chooser_default_parent_class)->map (widget);
6061
6062   if (impl->operation_mode == OPERATION_MODE_BROWSE)
6063     {
6064       switch (impl->reload_state)
6065         {
6066         case RELOAD_EMPTY:
6067           recent_shortcut_handler (impl);
6068           break;
6069         
6070         case RELOAD_HAS_FOLDER:
6071           /* Nothing; we are already loading or loaded, so we
6072            * don't need to reload
6073            */
6074           break;
6075
6076         default:
6077           g_assert_not_reached ();
6078       }
6079     }
6080
6081   volumes_bookmarks_changed_cb (impl->file_system, impl);
6082
6083   settings_load (impl);
6084
6085   profile_end ("end", NULL);
6086 }
6087
6088 /* GtkWidget::unmap method */
6089 static void
6090 gtk_file_chooser_default_unmap (GtkWidget *widget)
6091 {
6092   GtkFileChooserDefault *impl;
6093
6094   impl = GTK_FILE_CHOOSER_DEFAULT (widget);
6095
6096   settings_save (impl);
6097
6098   cancel_all_operations (impl);
6099   impl->reload_state = RELOAD_EMPTY;
6100
6101   GTK_WIDGET_CLASS (_gtk_file_chooser_default_parent_class)->unmap (widget);
6102 }
6103
6104 static void
6105 install_list_model_filter (GtkFileChooserDefault *impl)
6106 {
6107   _gtk_file_system_model_set_filter (impl->browse_files_model,
6108                                      impl->current_filter);
6109 }
6110
6111 #define COMPARE_DIRECTORIES                                                                                    \
6112   GtkFileChooserDefault *impl = user_data;                                                                     \
6113   GtkFileSystemModel *fs_model = GTK_FILE_SYSTEM_MODEL (model);                                                \
6114   gboolean dir_a, dir_b;                                                                                       \
6115                                                                                                                \
6116   dir_a = g_value_get_boolean (_gtk_file_system_model_get_value (fs_model, a, MODEL_COL_IS_FOLDER));           \
6117   dir_b = g_value_get_boolean (_gtk_file_system_model_get_value (fs_model, b, MODEL_COL_IS_FOLDER));           \
6118                                                                                                                \
6119   if (dir_a != dir_b)                                                                                          \
6120     return impl->list_sort_ascending ? (dir_a ? -1 : 1) : (dir_a ? 1 : -1) /* Directories *always* go first */
6121
6122 /* Sort callback for the filename column */
6123 static gint
6124 name_sort_func (GtkTreeModel *model,
6125                 GtkTreeIter  *a,
6126                 GtkTreeIter  *b,
6127                 gpointer      user_data)
6128 {
6129   COMPARE_DIRECTORIES;
6130   else
6131     {
6132       const char *key_a, *key_b;
6133       gint result;
6134
6135       key_a = g_value_get_string (_gtk_file_system_model_get_value (fs_model, a, MODEL_COL_NAME_COLLATED));
6136       key_b = g_value_get_string (_gtk_file_system_model_get_value (fs_model, b, MODEL_COL_NAME_COLLATED));
6137
6138       if (key_a && key_b)
6139         result = strcmp (key_a, key_b);
6140       else if (key_a)
6141         result = 1;
6142       else if (key_b)
6143         result = -1;
6144       else
6145         result = 0;
6146
6147       return result;
6148     }
6149 }
6150
6151 /* Sort callback for the size column */
6152 static gint
6153 size_sort_func (GtkTreeModel *model,
6154                 GtkTreeIter  *a,
6155                 GtkTreeIter  *b,
6156                 gpointer      user_data)
6157 {
6158   COMPARE_DIRECTORIES;
6159   else
6160     {
6161       gint64 size_a, size_b;
6162
6163       size_a = g_value_get_int64 (_gtk_file_system_model_get_value (fs_model, a, MODEL_COL_SIZE));
6164       size_b = g_value_get_int64 (_gtk_file_system_model_get_value (fs_model, b, MODEL_COL_SIZE));
6165
6166       return size_a < size_b ? -1 : (size_a == size_b ? 0 : 1);
6167     }
6168 }
6169
6170 /* Sort callback for the mtime column */
6171 static gint
6172 mtime_sort_func (GtkTreeModel *model,
6173                  GtkTreeIter  *a,
6174                  GtkTreeIter  *b,
6175                  gpointer      user_data)
6176 {
6177   COMPARE_DIRECTORIES;
6178   else
6179     {
6180       glong ta, tb;
6181
6182       ta = g_value_get_long (_gtk_file_system_model_get_value (fs_model, a, MODEL_COL_MTIME));
6183       tb = g_value_get_long (_gtk_file_system_model_get_value (fs_model, b, MODEL_COL_MTIME));
6184
6185       return ta < tb ? -1 : (ta == tb ? 0 : 1);
6186     }
6187 }
6188
6189 /* Callback used when the sort column changes.  We cache the sort order for use
6190  * in name_sort_func().
6191  */
6192 static void
6193 list_sort_column_changed_cb (GtkTreeSortable       *sortable,
6194                              GtkFileChooserDefault *impl)
6195 {
6196   gint sort_column_id;
6197   GtkSortType sort_type;
6198
6199   if (gtk_tree_sortable_get_sort_column_id (sortable, &sort_column_id, &sort_type))
6200     {
6201       impl->list_sort_ascending = (sort_type == GTK_SORT_ASCENDING);
6202       impl->sort_column = sort_column_id;
6203       impl->sort_order = sort_type;
6204     }
6205 }
6206
6207 static void
6208 set_busy_cursor (GtkFileChooserDefault *impl,
6209                  gboolean               busy)
6210 {
6211   GtkWidget *widget;
6212   GtkWindow *toplevel;
6213   GdkDisplay *display;
6214   GdkCursor *cursor;
6215
6216   toplevel = get_toplevel (GTK_WIDGET (impl));
6217   widget = GTK_WIDGET (toplevel);
6218   if (!toplevel || !gtk_widget_get_realized (widget))
6219     return;
6220
6221   display = gtk_widget_get_display (widget);
6222
6223   if (busy)
6224     cursor = gdk_cursor_new_for_display (display, GDK_WATCH);
6225   else
6226     cursor = NULL;
6227
6228   gdk_window_set_cursor (gtk_widget_get_window (widget), cursor);
6229   gdk_display_flush (display);
6230
6231   if (cursor)
6232     g_object_unref (cursor);
6233 }
6234
6235 /* Creates a sort model to wrap the file system model and sets it on the tree view */
6236 static void
6237 load_set_model (GtkFileChooserDefault *impl)
6238 {
6239   profile_start ("start", NULL);
6240
6241   g_assert (impl->browse_files_model != NULL);
6242
6243   profile_msg ("    gtk_tree_view_set_model start", NULL);
6244   gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_files_tree_view),
6245                            GTK_TREE_MODEL (impl->browse_files_model));
6246   gtk_tree_view_columns_autosize (GTK_TREE_VIEW (impl->browse_files_tree_view));
6247   gtk_tree_view_set_search_column (GTK_TREE_VIEW (impl->browse_files_tree_view),
6248                                    MODEL_COL_NAME);
6249   file_list_set_sort_column_ids (impl);
6250   set_sort_column (impl);
6251   profile_msg ("    gtk_tree_view_set_model end", NULL);
6252   impl->list_sort_ascending = TRUE;
6253
6254   profile_end ("end", NULL);
6255 }
6256
6257 /* Timeout callback used when the loading timer expires */
6258 static gboolean
6259 load_timeout_cb (gpointer data)
6260 {
6261   GtkFileChooserDefault *impl;
6262
6263   profile_start ("start", NULL);
6264
6265   impl = GTK_FILE_CHOOSER_DEFAULT (data);
6266   g_assert (impl->load_state == LOAD_PRELOAD);
6267   g_assert (impl->load_timeout_id != 0);
6268   g_assert (impl->browse_files_model != NULL);
6269
6270   impl->load_timeout_id = 0;
6271   impl->load_state = LOAD_LOADING;
6272
6273   load_set_model (impl);
6274
6275   profile_end ("end", NULL);
6276
6277   return FALSE;
6278 }
6279
6280 /* Sets up a new load timer for the model and switches to the LOAD_PRELOAD state */
6281 static void
6282 load_setup_timer (GtkFileChooserDefault *impl)
6283 {
6284   g_assert (impl->load_timeout_id == 0);
6285   g_assert (impl->load_state != LOAD_PRELOAD);
6286
6287   impl->load_timeout_id = gdk_threads_add_timeout (MAX_LOADING_TIME, load_timeout_cb, impl);
6288   impl->load_state = LOAD_PRELOAD;
6289 }
6290
6291 /* Removes the load timeout and switches to the LOAD_FINISHED state */
6292 static void
6293 load_remove_timer (GtkFileChooserDefault *impl)
6294 {
6295   if (impl->load_timeout_id != 0)
6296     {
6297       g_assert (impl->load_state == LOAD_PRELOAD);
6298
6299       g_source_remove (impl->load_timeout_id);
6300       impl->load_timeout_id = 0;
6301       impl->load_state = LOAD_EMPTY;
6302     }
6303   else
6304     g_assert (impl->load_state == LOAD_EMPTY ||
6305               impl->load_state == LOAD_LOADING ||
6306               impl->load_state == LOAD_FINISHED);
6307 }
6308
6309 /* Selects the first row in the file list */
6310 static void
6311 browse_files_select_first_row (GtkFileChooserDefault *impl)
6312 {
6313   GtkTreePath *path;
6314   GtkTreeIter dummy_iter;
6315   GtkTreeModel *tree_model;
6316
6317   tree_model = gtk_tree_view_get_model (GTK_TREE_VIEW (impl->browse_files_tree_view));
6318
6319   if (!tree_model)
6320     return;
6321
6322   path = gtk_tree_path_new_from_indices (0, -1);
6323
6324   /* If the list is empty, do nothing. */
6325   if (gtk_tree_model_get_iter (tree_model, &dummy_iter, path))
6326       gtk_tree_view_set_cursor (GTK_TREE_VIEW (impl->browse_files_tree_view), path, NULL, FALSE);
6327
6328   gtk_tree_path_free (path);
6329 }
6330
6331 struct center_selected_row_closure {
6332   GtkFileChooserDefault *impl;
6333   gboolean already_centered;
6334 };
6335
6336 /* Callback used from gtk_tree_selection_selected_foreach(); centers the
6337  * selected row in the tree view.
6338  */
6339 static void
6340 center_selected_row_foreach_cb (GtkTreeModel      *model,
6341                                 GtkTreePath       *path,
6342                                 GtkTreeIter       *iter,
6343                                 gpointer           data)
6344 {
6345   struct center_selected_row_closure *closure;
6346
6347   closure = data;
6348   if (closure->already_centered)
6349     return;
6350
6351   gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (closure->impl->browse_files_tree_view), path, NULL, TRUE, 0.5, 0.0);
6352   closure->already_centered = TRUE;
6353 }
6354
6355 /* Centers the selected row in the tree view */
6356 static void
6357 browse_files_center_selected_row (GtkFileChooserDefault *impl)
6358 {
6359   struct center_selected_row_closure closure;
6360   GtkTreeSelection *selection;
6361
6362   closure.impl = impl;
6363   closure.already_centered = FALSE;
6364
6365   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
6366   gtk_tree_selection_selected_foreach (selection, center_selected_row_foreach_cb, &closure);
6367 }
6368
6369 static gboolean
6370 show_and_select_files (GtkFileChooserDefault *impl,
6371                        GSList                *files)
6372 {
6373   GtkTreeSelection *selection;
6374   GtkFileSystemModel *fsmodel;
6375   gboolean enabled_hidden, removed_filters;
6376   gboolean selected_a_file;
6377   GSList *walk;
6378
6379   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
6380   fsmodel = GTK_FILE_SYSTEM_MODEL (gtk_tree_view_get_model (GTK_TREE_VIEW (impl->browse_files_tree_view)));
6381
6382   enabled_hidden = impl->show_hidden;
6383   removed_filters = (impl->current_filter == NULL);
6384
6385   selected_a_file = FALSE;
6386
6387   for (walk = files; walk; walk = walk->next)
6388     {
6389       GFile *file = walk->data;
6390       GtkTreeIter iter;
6391
6392       /* Is it a hidden file? */
6393
6394       if (!_gtk_file_system_model_get_iter_for_file (fsmodel, &iter, file))
6395         continue;
6396
6397       if (!_gtk_file_system_model_iter_is_visible (fsmodel, &iter))
6398         {
6399           GFileInfo *info = _gtk_file_system_model_get_info (fsmodel, &iter);
6400
6401           if (!enabled_hidden &&
6402               (g_file_info_get_is_hidden (info) ||
6403                g_file_info_get_is_backup (info)))
6404             {
6405               g_object_set (impl, "show-hidden", TRUE, NULL);
6406               enabled_hidden = TRUE;
6407             }
6408         }
6409
6410       /* Is it a filtered file? */
6411
6412       if (!_gtk_file_system_model_get_iter_for_file (fsmodel, &iter, file))
6413         continue; /* re-get the iter as it may change when the model refilters */
6414
6415       if (!_gtk_file_system_model_iter_is_visible (fsmodel, &iter))
6416         {
6417           /* Maybe we should have a way to ask the fsmodel if it had filtered a file */
6418           if (!removed_filters)
6419             {
6420               set_current_filter (impl, NULL);
6421               removed_filters = TRUE;
6422             }
6423         }
6424
6425       /* Okay, can we select the file now? */
6426           
6427       if (!_gtk_file_system_model_get_iter_for_file (fsmodel, &iter, file))
6428         continue;
6429
6430       if (_gtk_file_system_model_iter_is_visible (fsmodel, &iter))
6431         {
6432           GtkTreePath *path;
6433
6434           gtk_tree_selection_select_iter (selection, &iter);
6435
6436           path = gtk_tree_model_get_path (GTK_TREE_MODEL (fsmodel), &iter);
6437           gtk_tree_view_set_cursor (GTK_TREE_VIEW (impl->browse_files_tree_view),
6438                                     path, NULL, FALSE);
6439           gtk_tree_path_free (path);
6440
6441           selected_a_file = TRUE;
6442         }
6443     }
6444
6445   browse_files_center_selected_row (impl);
6446
6447   return selected_a_file;
6448 }
6449
6450 /* Processes the pending operation when a folder is finished loading */
6451 static void
6452 pending_select_files_process (GtkFileChooserDefault *impl)
6453 {
6454   g_assert (impl->load_state == LOAD_FINISHED);
6455   g_assert (impl->browse_files_model != NULL);
6456
6457   if (impl->pending_select_files)
6458     {
6459       show_and_select_files (impl, impl->pending_select_files);
6460       pending_select_files_free (impl);
6461       browse_files_center_selected_row (impl);
6462     }
6463   else
6464     {
6465       /* We only select the first row if the chooser is actually mapped ---
6466        * selecting the first row is to help the user when he is interacting with
6467        * the chooser, but sometimes a chooser works not on behalf of the user,
6468        * but rather on behalf of something else like GtkFileChooserButton.  In
6469        * that case, the chooser's selection should be what the caller expects,
6470        * as the user can't see that something else got selected.  See bug #165264.
6471        */
6472       if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN &&
6473           gtk_widget_get_mapped (GTK_WIDGET (impl)))
6474         browse_files_select_first_row (impl);
6475     }
6476
6477   g_assert (impl->pending_select_files == NULL);
6478 }
6479
6480 static void
6481 show_error_on_reading_current_folder (GtkFileChooserDefault *impl, GError *error)
6482 {
6483   GFileInfo *info;
6484   char *msg;
6485
6486   info = g_file_query_info (impl->current_folder,
6487                             G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME,
6488                             G_FILE_QUERY_INFO_NONE,
6489                             NULL,
6490                             NULL);
6491   if (info)
6492     {
6493       msg = g_strdup_printf (_("Could not read the contents of %s"), g_file_info_get_display_name (info));
6494       g_object_unref (info);
6495     }
6496   else
6497     msg = g_strdup (_("Could not read the contents of the folder"));
6498
6499   error_message (impl, msg, error->message);
6500   g_free (msg);
6501 }
6502
6503 /* Callback used when the file system model finishes loading */
6504 static void
6505 browse_files_model_finished_loading_cb (GtkFileSystemModel    *model,
6506                                         GError                *error,
6507                                         GtkFileChooserDefault *impl)
6508 {
6509   profile_start ("start", NULL);
6510
6511   if (error)
6512     show_error_on_reading_current_folder (impl, error);
6513
6514   if (impl->load_state == LOAD_PRELOAD)
6515     {
6516       load_remove_timer (impl);
6517       load_set_model (impl);
6518     }
6519   else if (impl->load_state == LOAD_LOADING)
6520     {
6521       /* Nothing */
6522     }
6523   else
6524     {
6525       /* We can't g_assert_not_reached(), as something other than us may have
6526        *  initiated a folder reload.  See #165556.
6527        */
6528       profile_end ("end", NULL);
6529       return;
6530     }
6531
6532   g_assert (impl->load_timeout_id == 0);
6533
6534   impl->load_state = LOAD_FINISHED;
6535
6536   pending_select_files_process (impl);
6537   set_busy_cursor (impl, FALSE);
6538 #ifdef PROFILE_FILE_CHOOSER
6539   access ("MARK: *** FINISHED LOADING", F_OK);
6540 #endif
6541
6542   profile_end ("end", NULL);
6543 }
6544
6545 static void
6546 stop_loading_and_clear_list_model (GtkFileChooserDefault *impl,
6547                                    gboolean remove_from_treeview)
6548 {
6549   load_remove_timer (impl); /* This changes the state to LOAD_EMPTY */
6550   
6551   if (impl->browse_files_model)
6552     {
6553       g_object_unref (impl->browse_files_model);
6554       impl->browse_files_model = NULL;
6555     }
6556
6557   if (remove_from_treeview)
6558     gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_files_tree_view), NULL);
6559 }
6560
6561 static char *
6562 my_g_format_time_for_display (glong secs)
6563 {
6564   GDate mtime, now;
6565   gint days_diff;
6566   struct tm tm_mtime;
6567   time_t time_mtime, time_now;
6568   const gchar *format;
6569   gchar *locale_format = NULL;
6570   gchar buf[256];
6571   char *date_str = NULL;
6572 #ifdef G_OS_WIN32
6573   const char *locale, *dot = NULL;
6574   gint64 codepage = -1;
6575   char charset[20];
6576 #endif
6577
6578   time_mtime = secs;
6579
6580 #ifdef HAVE_LOCALTIME_R
6581   localtime_r ((time_t *) &time_mtime, &tm_mtime);
6582 #else
6583   {
6584     struct tm *ptm = localtime ((time_t *) &time_mtime);
6585
6586     if (!ptm)
6587       {
6588         g_warning ("ptm != NULL failed");
6589         
6590         return g_strdup (_("Unknown"));
6591       }
6592     else
6593       memcpy ((void *) &tm_mtime, (void *) ptm, sizeof (struct tm));
6594   }
6595 #endif /* HAVE_LOCALTIME_R */
6596
6597   g_date_set_time_t (&mtime, time_mtime);
6598   time_now = time (NULL);
6599   g_date_set_time_t (&now, time_now);
6600
6601   days_diff = g_date_get_julian (&now) - g_date_get_julian (&mtime);
6602
6603   /* Translators: %H means "hours" and %M means "minutes" */
6604   if (days_diff == 0)
6605     format = _("%H:%M");
6606   else if (days_diff == 1)
6607     format = _("Yesterday at %H:%M");
6608   else
6609     {
6610       if (days_diff > 1 && days_diff < 7)
6611         format = "%A"; /* Days from last week */
6612       else
6613         format = "%x"; /* Any other date */
6614     }
6615
6616 #ifdef G_OS_WIN32
6617   /* g_locale_from_utf8() returns a string in the system
6618    * code-page, which is not always the same as that used by the C
6619    * library. For instance when running a GTK+ program with
6620    * LANG=ko on an English version of Windows, the system
6621    * code-page is 1252, but the code-page used by the C library is
6622    * 949. (It's GTK+ itself that sets the C library locale when it
6623    * notices the LANG environment variable. See gtkmain.c The
6624    * Microsoft C library doesn't look at any locale environment
6625    * variables.) We need to pass strftime() a string in the C
6626    * library's code-page. See bug #509885.
6627    */
6628   locale = setlocale (LC_ALL, NULL);
6629   if (locale != NULL)
6630     dot = strchr (locale, '.');
6631   if (dot != NULL)
6632     {
6633       codepage = g_ascii_strtoll (dot+1, NULL, 10);
6634       
6635       /* All codepages should fit in 16 bits AFAIK */
6636       if (codepage > 0 && codepage < 65536)
6637         {
6638           sprintf (charset, "CP%u", (guint) codepage);
6639           locale_format = g_convert (format, -1, charset, "UTF-8", NULL, NULL, NULL);
6640         }
6641     }
6642 #else
6643   locale_format = g_locale_from_utf8 (format, -1, NULL, NULL, NULL);
6644 #endif
6645   if (locale_format != NULL &&
6646       strftime (buf, sizeof (buf), locale_format, &tm_mtime) != 0)
6647     {
6648 #ifdef G_OS_WIN32
6649       /* As above but in opposite direction... */
6650       if (codepage > 0 && codepage < 65536)
6651         date_str = g_convert (buf, -1, "UTF-8", charset, NULL, NULL, NULL);
6652 #else
6653       date_str = g_locale_to_utf8 (buf, -1, NULL, NULL, NULL);
6654 #endif
6655     }
6656
6657   if (date_str == NULL)
6658     date_str = g_strdup (_("Unknown"));
6659
6660   g_free (locale_format);
6661   return date_str;
6662 }
6663
6664 static void
6665 copy_attribute (GFileInfo *to, GFileInfo *from, const char *attribute)
6666 {
6667   GFileAttributeType type;
6668   gpointer value;
6669
6670   if (g_file_info_get_attribute_data (from, attribute, &type, &value, NULL))
6671     g_file_info_set_attribute (to, attribute, type, value);
6672 }
6673
6674 static void
6675 file_system_model_got_thumbnail (GObject *object, GAsyncResult *res, gpointer data)
6676 {
6677   GtkFileSystemModel *model = data; /* might be unreffed if operation was cancelled */
6678   GFile *file = G_FILE (object);
6679   GFileInfo *queried, *info;
6680   GtkTreeIter iter;
6681
6682   queried = g_file_query_info_finish (file, res, NULL);
6683   if (queried == NULL)
6684     return;
6685
6686   GDK_THREADS_ENTER ();
6687
6688   /* now we know model is valid */
6689
6690   /* file was deleted */
6691   if (!_gtk_file_system_model_get_iter_for_file (model, &iter, file))
6692     {
6693       GDK_THREADS_LEAVE ();
6694       return;
6695     }
6696
6697   info = g_file_info_dup (_gtk_file_system_model_get_info (model, &iter));
6698
6699   copy_attribute (info, queried, G_FILE_ATTRIBUTE_THUMBNAIL_PATH);
6700   copy_attribute (info, queried, G_FILE_ATTRIBUTE_THUMBNAILING_FAILED);
6701   copy_attribute (info, queried, G_FILE_ATTRIBUTE_STANDARD_ICON);
6702
6703   _gtk_file_system_model_update_file (model, file, info, FALSE);
6704
6705   g_object_unref (info);
6706
6707   GDK_THREADS_LEAVE ();
6708 }
6709
6710 static gboolean
6711 file_system_model_set (GtkFileSystemModel *model,
6712                        GFile              *file,
6713                        GFileInfo          *info,
6714                        int                 column,
6715                        GValue             *value,
6716                        gpointer            data)
6717 {
6718   GtkFileChooserDefault *impl = data;
6719  
6720   switch (column)
6721     {
6722     case MODEL_COL_FILE:
6723       g_value_set_object (value, file);
6724       break;
6725     case MODEL_COL_NAME:
6726       if (info == NULL)
6727         g_value_set_string (value, DEFAULT_NEW_FOLDER_NAME);
6728       else 
6729         g_value_set_string (value, g_file_info_get_display_name (info));
6730       break;
6731     case MODEL_COL_NAME_COLLATED:
6732       if (info == NULL)
6733         g_value_take_string (value, g_utf8_collate_key_for_filename (DEFAULT_NEW_FOLDER_NAME, -1));
6734       else 
6735         g_value_take_string (value, g_utf8_collate_key_for_filename (g_file_info_get_display_name (info), -1));
6736       break;
6737     case MODEL_COL_IS_FOLDER:
6738       g_value_set_boolean (value, info == NULL || _gtk_file_info_consider_as_directory (info));
6739       break;
6740     case MODEL_COL_PIXBUF:
6741       if (info)
6742         {
6743           if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_STANDARD_ICON))
6744             {
6745               g_value_take_object (value, _gtk_file_info_render_icon (info, GTK_WIDGET (impl), impl->icon_size));
6746             }
6747           else
6748             {
6749               GtkTreeModel *tree_model;
6750               GtkTreePath *path, *start, *end;
6751               GtkTreeIter iter;
6752
6753               if (impl->browse_files_tree_view == NULL ||
6754                   g_file_info_has_attribute (info, "filechooser::queried"))
6755                 return FALSE;
6756
6757               tree_model = gtk_tree_view_get_model (GTK_TREE_VIEW (impl->browse_files_tree_view));
6758               if (tree_model != GTK_TREE_MODEL (model))
6759                 return FALSE;
6760
6761               if (!_gtk_file_system_model_get_iter_for_file (model,
6762                                                              &iter,
6763                                                              file))
6764                 g_assert_not_reached ();
6765               if (!gtk_tree_view_get_visible_range (GTK_TREE_VIEW (impl->browse_files_tree_view), &start, &end))
6766                 return FALSE;
6767               path = gtk_tree_model_get_path (tree_model, &iter);
6768               if (gtk_tree_path_compare (start, path) != 1 &&
6769                   gtk_tree_path_compare (path, end) != 1)
6770                 {
6771                   g_file_info_set_attribute_boolean (info, "filechooser::queried", TRUE);
6772                   g_file_query_info_async (file,
6773                                            G_FILE_ATTRIBUTE_THUMBNAIL_PATH ","
6774                                            G_FILE_ATTRIBUTE_THUMBNAILING_FAILED ","
6775                                            G_FILE_ATTRIBUTE_STANDARD_ICON,
6776                                            G_FILE_QUERY_INFO_NONE,
6777                                            G_PRIORITY_DEFAULT,
6778                                            _gtk_file_system_model_get_cancellable (model),
6779                                            file_system_model_got_thumbnail,
6780                                            model);
6781                 }
6782               gtk_tree_path_free (path);
6783               gtk_tree_path_free (start);
6784               gtk_tree_path_free (end);
6785               return FALSE;
6786             }
6787         }
6788       else
6789         g_value_set_object (value, NULL);
6790       break;
6791     case MODEL_COL_SIZE:
6792       g_value_set_int64 (value, info ? g_file_info_get_size (info) : 0);
6793       break;
6794     case MODEL_COL_SIZE_TEXT:
6795       if (info == NULL || _gtk_file_info_consider_as_directory (info))
6796         g_value_set_string (value, NULL);
6797       else
6798         g_value_take_string (value, g_format_size_for_display (g_file_info_get_size (info)));
6799       break;
6800     case MODEL_COL_MTIME:
6801     case MODEL_COL_MTIME_TEXT:
6802       {
6803         GTimeVal tv;
6804         if (info == NULL)
6805           break;
6806         g_file_info_get_modification_time (info, &tv);
6807         if (column == MODEL_COL_MTIME)
6808           g_value_set_long (value, tv.tv_sec);
6809         else if (tv.tv_sec == 0)
6810           g_value_set_static_string (value, _("Unknown"));
6811         else
6812           g_value_take_string (value, my_g_format_time_for_display (tv.tv_sec));
6813         break;
6814       }
6815     case MODEL_COL_ELLIPSIZE:
6816       g_value_set_enum (value, info ? PANGO_ELLIPSIZE_END : PANGO_ELLIPSIZE_NONE);
6817       break;
6818     default:
6819       g_assert_not_reached ();
6820       break;
6821     }
6822
6823   return TRUE;
6824 }
6825
6826 /* Gets rid of the old list model and creates a new one for the current folder */
6827 static gboolean
6828 set_list_model (GtkFileChooserDefault *impl,
6829                 GError               **error)
6830 {
6831   g_assert (impl->current_folder != NULL);
6832
6833   profile_start ("start", NULL);
6834
6835   stop_loading_and_clear_list_model (impl, TRUE);
6836
6837   set_busy_cursor (impl, TRUE);
6838
6839   impl->browse_files_model = 
6840     _gtk_file_system_model_new_for_directory (impl->current_folder,
6841                                               MODEL_ATTRIBUTES,
6842                                               file_system_model_set,
6843                                               impl,
6844                                               MODEL_COLUMN_TYPES);
6845
6846   _gtk_file_system_model_set_show_hidden (impl->browse_files_model, impl->show_hidden);
6847
6848   profile_msg ("    set sort function", NULL);
6849   gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->browse_files_model), MODEL_COL_NAME, name_sort_func, impl, NULL);
6850   gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->browse_files_model), MODEL_COL_SIZE, size_sort_func, impl, NULL);
6851   gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->browse_files_model), MODEL_COL_MTIME, mtime_sort_func, impl, NULL);
6852   gtk_tree_sortable_set_default_sort_func (GTK_TREE_SORTABLE (impl->browse_files_model), NULL, NULL, NULL);
6853   set_sort_column (impl);
6854   impl->list_sort_ascending = TRUE;
6855   g_signal_connect (impl->browse_files_model, "sort-column-changed",
6856                     G_CALLBACK (list_sort_column_changed_cb), impl);
6857
6858   load_setup_timer (impl); /* This changes the state to LOAD_PRELOAD */
6859
6860   g_signal_connect (impl->browse_files_model, "finished-loading",
6861                     G_CALLBACK (browse_files_model_finished_loading_cb), impl);
6862
6863   install_list_model_filter (impl);
6864
6865   profile_end ("end", NULL);
6866
6867   return TRUE;
6868 }
6869
6870 struct update_chooser_entry_selected_foreach_closure {
6871   int num_selected;
6872   GtkTreeIter first_selected_iter;
6873 };
6874
6875 static gint
6876 compare_utf8_filenames (const gchar *a,
6877                         const gchar *b)
6878 {
6879   gchar *a_folded, *b_folded;
6880   gint retval;
6881
6882   a_folded = g_utf8_strdown (a, -1);
6883   b_folded = g_utf8_strdown (b, -1);
6884
6885   retval = strcmp (a_folded, b_folded);
6886
6887   g_free (a_folded);
6888   g_free (b_folded);
6889
6890   return retval;
6891 }
6892
6893 static void
6894 update_chooser_entry_selected_foreach (GtkTreeModel *model,
6895                                        GtkTreePath *path,
6896                                        GtkTreeIter *iter,
6897                                        gpointer data)
6898 {
6899   struct update_chooser_entry_selected_foreach_closure *closure;
6900
6901   closure = data;
6902   closure->num_selected++;
6903
6904   if (closure->num_selected == 1)
6905     closure->first_selected_iter = *iter;
6906 }
6907
6908 static void
6909 update_chooser_entry (GtkFileChooserDefault *impl)
6910 {
6911   GtkTreeSelection *selection;
6912   struct update_chooser_entry_selected_foreach_closure closure;
6913
6914   /* no need to update the file chooser's entry if there's no entry */
6915   if (impl->operation_mode == OPERATION_MODE_SEARCH ||
6916       !impl->location_entry)
6917     return;
6918
6919   if (!(impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
6920         || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER
6921         || ((impl->action == GTK_FILE_CHOOSER_ACTION_OPEN
6922              || impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
6923             && impl->location_mode == LOCATION_MODE_FILENAME_ENTRY)))
6924     return;
6925
6926   g_assert (impl->location_entry != NULL);
6927
6928   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
6929   closure.num_selected = 0;
6930   gtk_tree_selection_selected_foreach (selection, update_chooser_entry_selected_foreach, &closure);
6931
6932   if (closure.num_selected == 0)
6933     {
6934       if (impl->operation_mode == OPERATION_MODE_RECENT)
6935         _gtk_file_chooser_entry_set_base_folder (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), NULL);
6936       else
6937         goto maybe_clear_entry;
6938     }
6939   else if (closure.num_selected == 1)
6940     {
6941       if (impl->operation_mode == OPERATION_MODE_BROWSE)
6942         {
6943           GFileInfo *info;
6944           gboolean change_entry;
6945
6946           info = _gtk_file_system_model_get_info (impl->browse_files_model, &closure.first_selected_iter);
6947
6948           /* If the cursor moved to the row of the newly created folder,
6949            * retrieving info will return NULL.
6950            */
6951           if (!info)
6952             return;
6953
6954           g_free (impl->browse_files_last_selected_name);
6955           impl->browse_files_last_selected_name =
6956             g_strdup (g_file_info_get_display_name (info));
6957
6958           if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
6959               impl->action == GTK_FILE_CHOOSER_ACTION_SAVE ||
6960               impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
6961             {
6962               /* Don't change the name when clicking on a folder... */
6963               change_entry = (! _gtk_file_info_consider_as_directory (info));
6964             }
6965           else
6966             change_entry = TRUE; /* ... unless we are in SELECT_FOLDER mode */
6967
6968           if (change_entry)
6969             {
6970               _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), impl->browse_files_last_selected_name);
6971
6972               if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
6973                 _gtk_file_chooser_entry_select_filename (GTK_FILE_CHOOSER_ENTRY (impl->location_entry));
6974             }
6975
6976           return;
6977         }
6978       else if (impl->operation_mode == OPERATION_MODE_RECENT
6979                && impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
6980         {
6981           GFile *folder;
6982
6983           /* Set the base folder on the name entry, so it will do completion relative to the correct recent-folder */
6984
6985           gtk_tree_model_get (GTK_TREE_MODEL (impl->recent_model), &closure.first_selected_iter,
6986                               MODEL_COL_FILE, &folder,
6987                               -1);
6988           _gtk_file_chooser_entry_set_base_folder (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), folder);
6989           g_object_unref (folder);
6990           return;
6991         }
6992     }
6993   else
6994     {
6995       g_assert (!(impl->action == GTK_FILE_CHOOSER_ACTION_SAVE ||
6996                   impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER));
6997
6998       /* Multiple selection, so just clear the entry. */
6999       g_free (impl->browse_files_last_selected_name);
7000       impl->browse_files_last_selected_name = NULL;
7001
7002       _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), "");
7003       return;
7004     }
7005
7006  maybe_clear_entry:
7007
7008   if ((impl->action == GTK_FILE_CHOOSER_ACTION_OPEN || impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
7009       && impl->browse_files_last_selected_name)
7010     {
7011       const char *entry_text;
7012       int len;
7013       gboolean clear_entry;
7014
7015       entry_text = gtk_entry_get_text (GTK_ENTRY (impl->location_entry));
7016       len = strlen (entry_text);
7017       if (len != 0)
7018         {
7019           /* The file chooser entry may have appended a "/" to its text.
7020            * So take it out, and compare the result to the old selection.
7021            */
7022           if (entry_text[len - 1] == G_DIR_SEPARATOR)
7023             {
7024               gchar *tmp;
7025
7026               tmp = g_strndup (entry_text, len - 1);
7027               clear_entry = (compare_utf8_filenames (impl->browse_files_last_selected_name, tmp) == 0);
7028               g_free (tmp);
7029             }
7030           else
7031             clear_entry = (compare_utf8_filenames (impl->browse_files_last_selected_name, entry_text) == 0);
7032         }
7033       else
7034         clear_entry = FALSE;
7035
7036       if (clear_entry)
7037         _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), "");
7038     }
7039 }
7040
7041 static gboolean
7042 gtk_file_chooser_default_set_current_folder (GtkFileChooser  *chooser,
7043                                              GFile           *file,
7044                                              GError         **error)
7045 {
7046   return gtk_file_chooser_default_update_current_folder (chooser, file, FALSE, FALSE, error);
7047 }
7048
7049
7050 struct UpdateCurrentFolderData
7051 {
7052   GtkFileChooserDefault *impl;
7053   GFile *file;
7054   gboolean keep_trail;
7055   gboolean clear_entry;
7056   GFile *original_file;
7057   GError *original_error;
7058 };
7059
7060 static void
7061 update_current_folder_mount_enclosing_volume_cb (GCancellable        *cancellable,
7062                                                  GtkFileSystemVolume *volume,
7063                                                  const GError        *error,
7064                                                  gpointer             user_data)
7065 {
7066   gboolean cancelled = g_cancellable_is_cancelled (cancellable);
7067   struct UpdateCurrentFolderData *data = user_data;
7068   GtkFileChooserDefault *impl = data->impl;
7069
7070   if (cancellable != impl->update_current_folder_cancellable)
7071     goto out;
7072
7073   impl->update_current_folder_cancellable = NULL;
7074   set_busy_cursor (impl, FALSE);
7075
7076   if (cancelled)
7077     goto out;
7078
7079   if (error)
7080     {
7081       error_changing_folder_dialog (data->impl, data->file, g_error_copy (error));
7082       impl->reload_state = RELOAD_EMPTY;
7083       goto out;
7084     }
7085
7086   change_folder_and_display_error (impl, data->file, data->clear_entry);
7087
7088 out:
7089   g_object_unref (data->file);
7090   g_free (data);
7091
7092   g_object_unref (cancellable);
7093 }
7094
7095 static void
7096 update_current_folder_get_info_cb (GCancellable *cancellable,
7097                                    GFileInfo    *info,
7098                                    const GError *error,
7099                                    gpointer      user_data)
7100 {
7101   gboolean cancelled = g_cancellable_is_cancelled (cancellable);
7102   struct UpdateCurrentFolderData *data = user_data;
7103   GtkFileChooserDefault *impl = data->impl;
7104
7105   if (cancellable != impl->update_current_folder_cancellable)
7106     goto out;
7107
7108   impl->update_current_folder_cancellable = NULL;
7109   impl->reload_state = RELOAD_EMPTY;
7110
7111   set_busy_cursor (impl, FALSE);
7112
7113   if (cancelled)
7114     goto out;
7115
7116   if (error)
7117     {
7118       GFile *parent_file;
7119
7120       if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_MOUNTED))
7121         {
7122           GMountOperation *mount_operation;
7123           GtkWidget *toplevel;
7124
7125           g_object_unref (cancellable);
7126           toplevel = gtk_widget_get_toplevel (GTK_WIDGET (impl));
7127
7128           mount_operation = gtk_mount_operation_new (GTK_WINDOW (toplevel));
7129
7130           set_busy_cursor (impl, TRUE);
7131
7132           impl->update_current_folder_cancellable =
7133             _gtk_file_system_mount_enclosing_volume (impl->file_system, data->file,
7134                                                      mount_operation,
7135                                                      update_current_folder_mount_enclosing_volume_cb,
7136                                                      data);
7137
7138           return;
7139         }
7140
7141       if (!data->original_file)
7142         {
7143           data->original_file = g_object_ref (data->file);
7144           data->original_error = g_error_copy (error);
7145         }
7146
7147       parent_file = g_file_get_parent (data->file);
7148
7149       /* get parent path and try to change the folder to that */
7150       if (parent_file)
7151         {
7152           g_object_unref (data->file);
7153           data->file = parent_file;
7154
7155           g_object_unref (cancellable);
7156
7157           /* restart the update current folder operation */
7158           impl->reload_state = RELOAD_HAS_FOLDER;
7159
7160           impl->update_current_folder_cancellable =
7161             _gtk_file_system_get_info (impl->file_system, data->file,
7162                                        "standard::type",
7163                                        update_current_folder_get_info_cb,
7164                                        data);
7165
7166           set_busy_cursor (impl, TRUE);
7167
7168           return;
7169         }
7170       else
7171         {
7172           /* Error and bail out, ignoring "not found" errors since they're useless:
7173            * they only happen when a program defaults to a folder that has been (re)moved.
7174            */
7175           if (!g_error_matches (data->original_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
7176             error_changing_folder_dialog (impl, data->original_file, data->original_error);
7177           else
7178             g_error_free (data->original_error);
7179
7180           g_object_unref (data->original_file);
7181
7182           goto out;
7183         }
7184     }
7185
7186   if (data->original_file)
7187     {
7188       /* Error and bail out, ignoring "not found" errors since they're useless:
7189        * they only happen when a program defaults to a folder that has been (re)moved.
7190        */
7191       if (!g_error_matches (data->original_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
7192         error_changing_folder_dialog (impl, data->original_file, data->original_error);
7193       else
7194         g_error_free (data->original_error);
7195
7196       g_object_unref (data->original_file);
7197     }
7198
7199   if (! _gtk_file_info_consider_as_directory (info))
7200     goto out;
7201
7202   if (!_gtk_path_bar_set_file (GTK_PATH_BAR (impl->browse_path_bar), data->file, data->keep_trail, NULL))
7203     goto out;
7204
7205   if (impl->current_folder != data->file)
7206     {
7207       if (impl->current_folder)
7208         g_object_unref (impl->current_folder);
7209
7210       impl->current_folder = g_object_ref (data->file);
7211     }
7212
7213   impl->reload_state = RELOAD_HAS_FOLDER;
7214
7215   /* Update the widgets that may trigger a folder change themselves.  */
7216
7217   if (!impl->changing_folder)
7218     {
7219       impl->changing_folder = TRUE;
7220
7221       shortcuts_update_current_folder (impl);
7222
7223       impl->changing_folder = FALSE;
7224     }
7225
7226   /* Set the folder on the save entry */
7227
7228   if (impl->location_entry)
7229     {
7230       _gtk_file_chooser_entry_set_base_folder (GTK_FILE_CHOOSER_ENTRY (impl->location_entry),
7231                                                impl->current_folder);
7232
7233       if (data->clear_entry)
7234         _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), "");
7235     }
7236
7237   /* Create a new list model.  This is slightly evil; we store the result value
7238    * but perform more actions rather than returning immediately even if it
7239    * generates an error.
7240    */
7241   set_list_model (impl, NULL);
7242
7243   /* Refresh controls */
7244
7245   shortcuts_find_current_folder (impl);
7246
7247   g_signal_emit_by_name (impl, "current-folder-changed", 0);
7248
7249   check_preview_change (impl);
7250   bookmarks_check_add_sensitivity (impl);
7251
7252   g_signal_emit_by_name (impl, "selection-changed", 0);
7253
7254 out:
7255   g_object_unref (data->file);
7256   g_free (data);
7257
7258   g_object_unref (cancellable);
7259 }
7260
7261 static gboolean
7262 gtk_file_chooser_default_update_current_folder (GtkFileChooser    *chooser,
7263                                                 GFile             *file,
7264                                                 gboolean           keep_trail,
7265                                                 gboolean           clear_entry,
7266                                                 GError           **error)
7267 {
7268   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
7269   struct UpdateCurrentFolderData *data;
7270
7271   profile_start ("start", NULL);
7272
7273   g_object_ref (file);
7274
7275   operation_mode_set (impl, OPERATION_MODE_BROWSE);
7276
7277   if (impl->local_only && !g_file_is_native (file))
7278     {
7279       g_set_error_literal (error,
7280                            GTK_FILE_CHOOSER_ERROR,
7281                            GTK_FILE_CHOOSER_ERROR_BAD_FILENAME,
7282                            _("Cannot change to folder because it is not local"));
7283
7284       g_object_unref (file);
7285       profile_end ("end - not local", NULL);
7286       return FALSE;
7287     }
7288
7289   if (impl->update_current_folder_cancellable)
7290     g_cancellable_cancel (impl->update_current_folder_cancellable);
7291
7292   /* Test validity of path here.  */
7293   data = g_new0 (struct UpdateCurrentFolderData, 1);
7294   data->impl = impl;
7295   data->file = g_object_ref (file);
7296   data->keep_trail = keep_trail;
7297   data->clear_entry = clear_entry;
7298
7299   impl->reload_state = RELOAD_HAS_FOLDER;
7300
7301   impl->update_current_folder_cancellable =
7302     _gtk_file_system_get_info (impl->file_system, file,
7303                                "standard::type",
7304                                update_current_folder_get_info_cb,
7305                                data);
7306
7307   set_busy_cursor (impl, TRUE);
7308   g_object_unref (file);
7309
7310   profile_end ("end", NULL);
7311   return TRUE;
7312 }
7313
7314 static GFile *
7315 gtk_file_chooser_default_get_current_folder (GtkFileChooser *chooser)
7316 {
7317   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
7318
7319   if (impl->operation_mode == OPERATION_MODE_SEARCH ||
7320       impl->operation_mode == OPERATION_MODE_RECENT)
7321     return NULL;
7322  
7323   if (impl->reload_state == RELOAD_EMPTY)
7324     {
7325       /* We are unmapped, or we had an error while loading the last folder.
7326        * We'll return the folder used by the last invocation of the file chooser
7327        * since once we get (re)mapped, we'll load *that* folder anyway unless
7328        * the caller explicitly calls set_current_folder() on us.
7329        */
7330       return get_file_for_last_folder_opened (impl);
7331     }
7332
7333   if (impl->current_folder)
7334     return g_object_ref (impl->current_folder);
7335
7336   return NULL;
7337 }
7338
7339 static void
7340 gtk_file_chooser_default_set_current_name (GtkFileChooser *chooser,
7341                                            const gchar    *name)
7342 {
7343   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
7344
7345   g_return_if_fail (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE ||
7346                     impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER);
7347
7348   pending_select_files_free (impl);
7349   _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), name);
7350 }
7351
7352 static gboolean
7353 gtk_file_chooser_default_select_file (GtkFileChooser  *chooser,
7354                                       GFile           *file,
7355                                       GError         **error)
7356 {
7357   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
7358   GFile *parent_file;
7359   gboolean same_path;
7360
7361   parent_file = g_file_get_parent (file);
7362
7363   if (!parent_file)
7364     return gtk_file_chooser_set_current_folder_file (chooser, file, error);
7365
7366   if (impl->operation_mode == OPERATION_MODE_SEARCH ||
7367       impl->operation_mode == OPERATION_MODE_RECENT ||
7368       impl->load_state == LOAD_EMPTY)
7369     {
7370       same_path = FALSE;
7371     }
7372   else
7373     {
7374       g_assert (impl->current_folder != NULL);
7375
7376       same_path = g_file_equal (parent_file, impl->current_folder);
7377     }
7378
7379   if (same_path && impl->load_state == LOAD_FINISHED)
7380     {
7381       gboolean result;
7382       GSList files;
7383
7384       files.data = (gpointer) file;
7385       files.next = NULL;
7386
7387       result = show_and_select_files (impl, &files);
7388       g_object_unref (parent_file);
7389       return result;
7390     }
7391
7392   pending_select_files_add (impl, file);
7393
7394   if (!same_path)
7395     {
7396       gboolean result;
7397
7398       result = gtk_file_chooser_set_current_folder_file (chooser, parent_file, error);
7399       g_object_unref (parent_file);
7400       return result;
7401     }
7402
7403   g_object_unref (parent_file);
7404   return TRUE;
7405 }
7406
7407 static void
7408 gtk_file_chooser_default_unselect_file (GtkFileChooser *chooser,
7409                                         GFile          *file)
7410 {
7411   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
7412   GtkTreeView *tree_view = GTK_TREE_VIEW (impl->browse_files_tree_view);
7413   GtkTreeIter iter;
7414
7415   if (!impl->browse_files_model)
7416     return;
7417
7418   if (!_gtk_file_system_model_get_iter_for_file (impl->browse_files_model,
7419                                                  &iter,
7420                                                  file))
7421     return;
7422
7423   gtk_tree_selection_unselect_iter (gtk_tree_view_get_selection (tree_view),
7424                                     &iter);
7425 }
7426
7427 static gboolean
7428 maybe_select (GtkTreeModel *model, 
7429               GtkTreePath  *path, 
7430               GtkTreeIter  *iter, 
7431               gpointer     data)
7432 {
7433   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (data);
7434   GtkTreeSelection *selection;
7435   gboolean is_folder;
7436   
7437   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
7438   
7439   gtk_tree_model_get (model, iter,
7440                       MODEL_COL_IS_FOLDER, &is_folder,
7441                       -1);
7442
7443   if ((is_folder && impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER) ||
7444       (!is_folder && impl->action == GTK_FILE_CHOOSER_ACTION_OPEN))
7445     gtk_tree_selection_select_iter (selection, iter);
7446   else
7447     gtk_tree_selection_unselect_iter (selection, iter);
7448     
7449   return FALSE;
7450 }
7451
7452 static void
7453 gtk_file_chooser_default_select_all (GtkFileChooser *chooser)
7454 {
7455   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
7456
7457   if (impl->operation_mode == OPERATION_MODE_SEARCH ||
7458       impl->operation_mode == OPERATION_MODE_RECENT)
7459     {
7460       GtkTreeSelection *selection;
7461       
7462       selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
7463       gtk_tree_selection_select_all (selection);
7464       return;
7465     }
7466
7467   if (impl->select_multiple)
7468     gtk_tree_model_foreach (GTK_TREE_MODEL (impl->browse_files_model), 
7469                             maybe_select, impl);
7470 }
7471
7472 static void
7473 gtk_file_chooser_default_unselect_all (GtkFileChooser *chooser)
7474 {
7475   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
7476   GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
7477
7478   gtk_tree_selection_unselect_all (selection);
7479   pending_select_files_free (impl);
7480 }
7481
7482 /* Checks whether the filename entry for the Save modes contains a well-formed filename.
7483  *
7484  * is_well_formed_ret - whether what the user typed passes gkt_file_system_make_path()
7485  *
7486  * is_empty_ret - whether the file entry is totally empty
7487  *
7488  * is_file_part_empty_ret - whether the file part is empty (will be if user types "foobar/", and
7489  *                          the path will be "$cwd/foobar")
7490  */
7491 static void
7492 check_save_entry (GtkFileChooserDefault *impl,
7493                   GFile                **file_ret,
7494                   gboolean              *is_well_formed_ret,
7495                   gboolean              *is_empty_ret,
7496                   gboolean              *is_file_part_empty_ret,
7497                   gboolean              *is_folder)
7498 {
7499   GtkFileChooserEntry *chooser_entry;
7500   GFile *current_folder;
7501   const char *file_part;
7502   GFile *file;
7503   GError *error;
7504
7505   g_assert (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
7506             || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER
7507             || ((impl->action == GTK_FILE_CHOOSER_ACTION_OPEN
7508                  || impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
7509                 && impl->location_mode == LOCATION_MODE_FILENAME_ENTRY));
7510
7511   chooser_entry = GTK_FILE_CHOOSER_ENTRY (impl->location_entry);
7512
7513   if (strlen (gtk_entry_get_text (GTK_ENTRY (chooser_entry))) == 0)
7514     {
7515       *file_ret = NULL;
7516       *is_well_formed_ret = TRUE;
7517       *is_empty_ret = TRUE;
7518       *is_file_part_empty_ret = TRUE;
7519       *is_folder = FALSE;
7520
7521       return;
7522     }
7523
7524   *is_empty_ret = FALSE;
7525
7526   current_folder = _gtk_file_chooser_entry_get_current_folder (chooser_entry);
7527   if (!current_folder)
7528     {
7529       *file_ret = NULL;
7530       *is_well_formed_ret = FALSE;
7531       *is_file_part_empty_ret = FALSE;
7532       *is_folder = FALSE;
7533
7534       return;
7535     }
7536
7537   file_part = _gtk_file_chooser_entry_get_file_part (chooser_entry);
7538
7539   if (!file_part || file_part[0] == '\0')
7540     {
7541       *file_ret = g_object_ref (current_folder);
7542       *is_well_formed_ret = TRUE;
7543       *is_file_part_empty_ret = TRUE;
7544       *is_folder = TRUE;
7545
7546       return;
7547     }
7548
7549   *is_file_part_empty_ret = FALSE;
7550
7551   error = NULL;
7552   file = g_file_get_child_for_display_name (current_folder, file_part, &error);
7553
7554   if (!file)
7555     {
7556       error_building_filename_dialog (impl, error);
7557       *file_ret = NULL;
7558       *is_well_formed_ret = FALSE;
7559       *is_folder = FALSE;
7560
7561       return;
7562     }
7563
7564   *file_ret = file;
7565   *is_well_formed_ret = TRUE;
7566   *is_folder = _gtk_file_chooser_entry_get_is_folder (chooser_entry, file);
7567 }
7568
7569 struct get_files_closure {
7570   GtkFileChooserDefault *impl;
7571   GSList *result;
7572   GFile *file_from_entry;
7573 };
7574
7575 static void
7576 get_files_foreach (GtkTreeModel *model,
7577                    GtkTreePath  *path,
7578                    GtkTreeIter  *iter,
7579                    gpointer      data)
7580 {
7581   struct get_files_closure *info;
7582   GFile *file;
7583   GtkFileSystemModel *fs_model;
7584
7585   info = data;
7586   fs_model = info->impl->browse_files_model;
7587
7588   file = _gtk_file_system_model_get_file (fs_model, iter);
7589   if (!file)
7590     return; /* We are on the editable row */
7591
7592   if (!info->file_from_entry || !g_file_equal (info->file_from_entry, file))
7593     info->result = g_slist_prepend (info->result, g_object_ref (file));
7594 }
7595
7596 static GSList *
7597 gtk_file_chooser_default_get_files (GtkFileChooser *chooser)
7598 {
7599   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
7600   struct get_files_closure info;
7601   GtkWindow *toplevel;
7602   GtkWidget *current_focus;
7603   gboolean file_list_seen;
7604
7605   info.impl = impl;
7606   info.result = NULL;
7607   info.file_from_entry = NULL;
7608
7609   if (impl->operation_mode == OPERATION_MODE_SEARCH)
7610     return search_get_selected_files (impl);
7611
7612   if (impl->operation_mode == OPERATION_MODE_RECENT)
7613     {
7614       if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
7615         {
7616           file_list_seen = TRUE;
7617           goto file_entry;
7618         }
7619       else
7620         return recent_get_selected_files (impl);
7621     }
7622
7623   toplevel = get_toplevel (GTK_WIDGET (impl));
7624   if (toplevel)
7625     current_focus = gtk_window_get_focus (toplevel);
7626   else
7627     current_focus = NULL;
7628
7629   file_list_seen = FALSE;
7630   if (current_focus == impl->browse_files_tree_view)
7631     {
7632       GtkTreeSelection *selection;
7633
7634     file_list:
7635
7636       file_list_seen = TRUE;
7637       selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
7638       gtk_tree_selection_selected_foreach (selection, get_files_foreach, &info);
7639
7640       /* If there is no selection in the file list, we probably have this situation:
7641        *
7642        * 1. The user typed a filename in the SAVE filename entry ("foo.txt").
7643        * 2. He then double-clicked on a folder ("bar") in the file list
7644        *
7645        * So we want the selection to be "bar/foo.txt".  Jump to the case for the
7646        * filename entry to see if that is the case.
7647        */
7648       if (info.result == NULL && impl->location_entry)
7649         goto file_entry;
7650     }
7651   else if (impl->location_entry && current_focus == impl->location_entry)
7652     {
7653       gboolean is_well_formed, is_empty, is_file_part_empty, is_folder;
7654
7655     file_entry:
7656
7657       check_save_entry (impl, &info.file_from_entry, &is_well_formed, &is_empty, &is_file_part_empty, &is_folder);
7658
7659       if (is_empty)
7660         goto out;
7661
7662       if (!is_well_formed)
7663         return NULL;
7664
7665       if (is_file_part_empty && impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
7666         {
7667           g_object_unref (info.file_from_entry);
7668           return NULL;
7669         }
7670
7671       if (info.file_from_entry)
7672         info.result = g_slist_prepend (info.result, info.file_from_entry);
7673       else if (!file_list_seen) 
7674         goto file_list;
7675       else
7676         return NULL;
7677     }
7678   else if (impl->toplevel_last_focus_widget == impl->browse_files_tree_view)
7679     goto file_list;
7680   else if (impl->location_entry && impl->toplevel_last_focus_widget == impl->location_entry)
7681     goto file_entry;
7682   else
7683     {
7684       /* The focus is on a dialog's action area button or something else */
7685       if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE ||
7686           impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
7687         goto file_entry;
7688       else
7689         goto file_list; 
7690     }
7691
7692  out:
7693
7694   /* If there's no folder selected, and we're in SELECT_FOLDER mode, then we
7695    * fall back to the current directory */
7696   if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER &&
7697       info.result == NULL)
7698     {
7699       GFile *current_folder;
7700
7701       current_folder = gtk_file_chooser_get_current_folder_file (chooser);
7702
7703       if (current_folder)
7704         info.result = g_slist_prepend (info.result, current_folder);
7705     }
7706
7707   return g_slist_reverse (info.result);
7708 }
7709
7710 GFile *
7711 gtk_file_chooser_default_get_preview_file (GtkFileChooser *chooser)
7712 {
7713   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
7714
7715   if (impl->preview_file)
7716     return g_object_ref (impl->preview_file);
7717   else
7718     return NULL;
7719 }
7720
7721 static GtkFileSystem *
7722 gtk_file_chooser_default_get_file_system (GtkFileChooser *chooser)
7723 {
7724   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
7725
7726   return impl->file_system;
7727 }
7728
7729 /* Shows or hides the filter widgets */
7730 static void
7731 show_filters (GtkFileChooserDefault *impl,
7732               gboolean               show)
7733 {
7734   if (show)
7735     gtk_widget_show (impl->filter_combo_hbox);
7736   else
7737     gtk_widget_hide (impl->filter_combo_hbox);
7738 }
7739
7740 static void
7741 gtk_file_chooser_default_add_filter (GtkFileChooser *chooser,
7742                                      GtkFileFilter  *filter)
7743 {
7744   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
7745   const gchar *name;
7746
7747   if (g_slist_find (impl->filters, filter))
7748     {
7749       g_warning ("gtk_file_chooser_add_filter() called on filter already in list\n");
7750       return;
7751     }
7752
7753   g_object_ref_sink (filter);
7754   impl->filters = g_slist_append (impl->filters, filter);
7755
7756   name = gtk_file_filter_get_name (filter);
7757   if (!name)
7758     name = "Untitled filter";   /* Place-holder, doesn't need to be marked for translation */
7759
7760   gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (impl->filter_combo), name);
7761
7762   if (!g_slist_find (impl->filters, impl->current_filter))
7763     set_current_filter (impl, filter);
7764
7765   show_filters (impl, TRUE);
7766 }
7767
7768 static void
7769 gtk_file_chooser_default_remove_filter (GtkFileChooser *chooser,
7770                                         GtkFileFilter  *filter)
7771 {
7772   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
7773   GtkTreeModel *model;
7774   GtkTreeIter iter;
7775   gint filter_index;
7776
7777   filter_index = g_slist_index (impl->filters, filter);
7778
7779   if (filter_index < 0)
7780     {
7781       g_warning ("gtk_file_chooser_remove_filter() called on filter not in list\n");
7782       return;
7783     }
7784
7785   impl->filters = g_slist_remove (impl->filters, filter);
7786
7787   if (filter == impl->current_filter)
7788     {
7789       if (impl->filters)
7790         set_current_filter (impl, impl->filters->data);
7791       else
7792         set_current_filter (impl, NULL);
7793     }
7794
7795   /* Remove row from the combo box */
7796   model = gtk_combo_box_get_model (GTK_COMBO_BOX (impl->filter_combo));
7797   if (!gtk_tree_model_iter_nth_child  (model, &iter, NULL, filter_index))
7798     g_assert_not_reached ();
7799
7800   gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
7801
7802   g_object_unref (filter);
7803
7804   if (!impl->filters)
7805     show_filters (impl, FALSE);
7806 }
7807
7808 static GSList *
7809 gtk_file_chooser_default_list_filters (GtkFileChooser *chooser)
7810 {
7811   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
7812
7813   return g_slist_copy (impl->filters);
7814 }
7815
7816 /* Returns the position in the shortcuts tree where the nth specified shortcut would appear */
7817 static int
7818 shortcuts_get_pos_for_shortcut_folder (GtkFileChooserDefault *impl,
7819                                        int                    pos)
7820 {
7821   return pos + shortcuts_get_index (impl, SHORTCUTS_SHORTCUTS);
7822 }
7823
7824 struct AddShortcutData
7825 {
7826   GtkFileChooserDefault *impl;
7827   GFile *file;
7828 };
7829
7830 static void
7831 add_shortcut_get_info_cb (GCancellable *cancellable,
7832                           GFileInfo    *info,
7833                           const GError *error,
7834                           gpointer      user_data)
7835 {
7836   int pos;
7837   gboolean cancelled = g_cancellable_is_cancelled (cancellable);
7838   struct AddShortcutData *data = user_data;
7839
7840   if (!g_slist_find (data->impl->loading_shortcuts, cancellable))
7841     goto out;
7842
7843   data->impl->loading_shortcuts = g_slist_remove (data->impl->loading_shortcuts, cancellable);
7844
7845   if (cancelled || error || (! _gtk_file_info_consider_as_directory (info)))
7846     goto out;
7847
7848   pos = shortcuts_get_pos_for_shortcut_folder (data->impl, data->impl->num_shortcuts);
7849
7850   shortcuts_insert_file (data->impl, pos, SHORTCUT_TYPE_FILE, NULL, data->file, NULL, FALSE, SHORTCUTS_SHORTCUTS);
7851
7852 out:
7853   g_object_unref (data->impl);
7854   g_object_unref (data->file);
7855   g_free (data);
7856
7857   g_object_unref (cancellable);
7858 }
7859
7860 static gboolean
7861 gtk_file_chooser_default_add_shortcut_folder (GtkFileChooser  *chooser,
7862                                               GFile           *file,
7863                                               GError         **error)
7864 {
7865   GCancellable *cancellable;
7866   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
7867   struct AddShortcutData *data;
7868   GSList *l;
7869   int pos;
7870
7871   /* Avoid adding duplicates */
7872   pos = shortcut_find_position (impl, file);
7873   if (pos >= 0 && pos < shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS_SEPARATOR))
7874     {
7875       gchar *uri;
7876
7877       uri = g_file_get_uri (file);
7878       /* translators, "Shortcut" means "Bookmark" here */
7879       g_set_error (error,
7880                    GTK_FILE_CHOOSER_ERROR,
7881                    GTK_FILE_CHOOSER_ERROR_ALREADY_EXISTS,
7882                    _("Shortcut %s already exists"),
7883                    uri);
7884       g_free (uri);
7885
7886       return FALSE;
7887     }
7888
7889   for (l = impl->loading_shortcuts; l; l = l->next)
7890     {
7891       GCancellable *c = l->data;
7892       GFile *f;
7893
7894       f = g_object_get_data (G_OBJECT (c), "add-shortcut-path-key");
7895       if (f && g_file_equal (file, f))
7896         {
7897           gchar *uri;
7898
7899           uri = g_file_get_uri (file);
7900           g_set_error (error,
7901                        GTK_FILE_CHOOSER_ERROR,
7902                        GTK_FILE_CHOOSER_ERROR_ALREADY_EXISTS,
7903                        _("Shortcut %s already exists"),
7904                        uri);
7905           g_free (uri);
7906
7907           return FALSE;
7908         }
7909     }
7910
7911   data = g_new0 (struct AddShortcutData, 1);
7912   data->impl = g_object_ref (impl);
7913   data->file = g_object_ref (file);
7914
7915   cancellable = _gtk_file_system_get_info (impl->file_system, file,
7916                                            "standard::type",
7917                                            add_shortcut_get_info_cb, data);
7918
7919   if (!cancellable)
7920     return FALSE;
7921
7922   impl->loading_shortcuts = g_slist_append (impl->loading_shortcuts, cancellable);
7923   g_object_set_data (G_OBJECT (cancellable), "add-shortcut-path-key", data->file);
7924
7925   return TRUE;
7926 }
7927
7928 static gboolean
7929 gtk_file_chooser_default_remove_shortcut_folder (GtkFileChooser  *chooser,
7930                                                  GFile           *file,
7931                                                  GError         **error)
7932 {
7933   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
7934   int pos;
7935   GtkTreeIter iter;
7936   GSList *l;
7937   char *uri;
7938   int i;
7939
7940   for (l = impl->loading_shortcuts; l; l = l->next)
7941     {
7942       GCancellable *c = l->data;
7943       GFile *f;
7944
7945       f = g_object_get_data (G_OBJECT (c), "add-shortcut-path-key");
7946       if (f && g_file_equal (file, f))
7947         {
7948           impl->loading_shortcuts = g_slist_remove (impl->loading_shortcuts, c);
7949           g_cancellable_cancel (c);
7950           return TRUE;
7951         }
7952     }
7953
7954   if (impl->num_shortcuts == 0)
7955     goto out;
7956
7957   pos = shortcuts_get_pos_for_shortcut_folder (impl, 0);
7958   if (!gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (impl->shortcuts_model), &iter, NULL, pos))
7959     g_assert_not_reached ();
7960
7961   for (i = 0; i < impl->num_shortcuts; i++)
7962     {
7963       gpointer col_data;
7964       ShortcutType shortcut_type;
7965       GFile *shortcut;
7966
7967       gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
7968                           SHORTCUTS_COL_DATA, &col_data,
7969                           SHORTCUTS_COL_TYPE, &shortcut_type,
7970                           -1);
7971       g_assert (col_data != NULL);
7972       g_assert (shortcut_type == SHORTCUT_TYPE_FILE);
7973
7974       shortcut = col_data;
7975       if (g_file_equal (shortcut, file))
7976         {
7977           shortcuts_remove_rows (impl, pos + i, 1);
7978           impl->num_shortcuts--;
7979           return TRUE;
7980         }
7981
7982       if (!gtk_tree_model_iter_next (GTK_TREE_MODEL (impl->shortcuts_model), &iter))
7983         g_assert_not_reached ();
7984     }
7985
7986  out:
7987
7988   uri = g_file_get_uri (file);
7989   /* translators, "Shortcut" means "Bookmark" here */
7990   g_set_error (error,
7991                GTK_FILE_CHOOSER_ERROR,
7992                GTK_FILE_CHOOSER_ERROR_NONEXISTENT,
7993                _("Shortcut %s does not exist"),
7994                uri);
7995   g_free (uri);
7996
7997   return FALSE;
7998 }
7999
8000 static GSList *
8001 gtk_file_chooser_default_list_shortcut_folders (GtkFileChooser *chooser)
8002 {
8003   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
8004   int pos;
8005   GtkTreeIter iter;
8006   int i;
8007   GSList *list;
8008
8009   if (impl->num_shortcuts == 0)
8010     return NULL;
8011
8012   pos = shortcuts_get_pos_for_shortcut_folder (impl, 0);
8013   if (!gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (impl->shortcuts_model), &iter, NULL, pos))
8014     g_assert_not_reached ();
8015
8016   list = NULL;
8017
8018   for (i = 0; i < impl->num_shortcuts; i++)
8019     {
8020       gpointer col_data;
8021       ShortcutType shortcut_type;
8022       GFile *shortcut;
8023
8024       gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
8025                           SHORTCUTS_COL_DATA, &col_data,
8026                           SHORTCUTS_COL_TYPE, &shortcut_type,
8027                           -1);
8028       g_assert (col_data != NULL);
8029       g_assert (shortcut_type == SHORTCUT_TYPE_FILE);
8030
8031       shortcut = col_data;
8032       list = g_slist_prepend (list, g_object_ref (shortcut));
8033
8034       if (i != impl->num_shortcuts - 1)
8035         {
8036           if (!gtk_tree_model_iter_next (GTK_TREE_MODEL (impl->shortcuts_model), &iter))
8037             g_assert_not_reached ();
8038         }
8039     }
8040
8041   return g_slist_reverse (list);
8042 }
8043
8044 /* Guesses a size based upon font sizes */
8045 static void
8046 find_good_size_from_style (GtkWidget *widget,
8047                            gint      *width,
8048                            gint      *height)
8049 {
8050   GtkStyleContext *context;
8051   GtkStateFlags state;
8052   int font_size;
8053   GdkScreen *screen;
8054   double resolution;
8055
8056   context = gtk_widget_get_style_context (widget);
8057   state = gtk_widget_get_state_flags (widget);
8058
8059   screen = gtk_widget_get_screen (widget);
8060   if (screen)
8061     {
8062       resolution = gdk_screen_get_resolution (screen);
8063       if (resolution < 0.0) /* will be -1 if the resolution is not defined in the GdkScreen */
8064         resolution = 96.0;
8065     }
8066   else
8067     resolution = 96.0; /* wheeee */
8068
8069   font_size = pango_font_description_get_size (gtk_style_context_get_font (context, state));
8070   font_size = PANGO_PIXELS (font_size) * resolution / 72.0;
8071
8072   *width = font_size * NUM_CHARS;
8073   *height = font_size * NUM_LINES;
8074 }
8075
8076 static void
8077 gtk_file_chooser_default_get_default_size (GtkFileChooserEmbed *chooser_embed,
8078                                            gint                *default_width,
8079                                            gint                *default_height)
8080 {
8081   GtkFileChooserDefault *impl;
8082   GtkRequisition req;
8083   int x, y, width, height;
8084
8085   impl = GTK_FILE_CHOOSER_DEFAULT (chooser_embed);
8086
8087   settings_ensure (impl);
8088
8089   g_settings_get (impl->settings, SETTINGS_KEY_WINDOW_POSITION, "(ii)", &x, &y);
8090   g_settings_get (impl->settings, SETTINGS_KEY_WINDOW_SIZE, "(ii)", &width, &height);
8091
8092   if (x >= 0 && y >= 0 && width > 0 && height > 0)
8093     {
8094       *default_width = width;
8095       *default_height = height;
8096       return;
8097     }
8098
8099   find_good_size_from_style (GTK_WIDGET (chooser_embed), default_width, default_height);
8100
8101   if (impl->preview_widget_active &&
8102       impl->preview_widget &&
8103       gtk_widget_get_visible (impl->preview_widget))
8104     {
8105       gtk_widget_get_preferred_size (impl->preview_box,
8106                                      &req, NULL);
8107       *default_width += PREVIEW_HBOX_SPACING + req.width;
8108     }
8109
8110   if (impl->extra_widget &&
8111       gtk_widget_get_visible (impl->extra_widget))
8112     {
8113       gtk_widget_get_preferred_size (impl->extra_align,
8114                                      &req, NULL);
8115       *default_height += gtk_box_get_spacing (GTK_BOX (chooser_embed)) + req.height;
8116     }
8117 }
8118
8119 struct switch_folder_closure {
8120   GtkFileChooserDefault *impl;
8121   GFile *file;
8122   int num_selected;
8123 };
8124
8125 /* Used from gtk_tree_selection_selected_foreach() in switch_to_selected_folder() */
8126 static void
8127 switch_folder_foreach_cb (GtkTreeModel      *model,
8128                           GtkTreePath       *path,
8129                           GtkTreeIter       *iter,
8130                           gpointer           data)
8131 {
8132   struct switch_folder_closure *closure;
8133
8134   closure = data;
8135
8136   closure->file = _gtk_file_system_model_get_file (closure->impl->browse_files_model, iter);
8137   closure->num_selected++;
8138 }
8139
8140 /* Changes to the selected folder in the list view */
8141 static void
8142 switch_to_selected_folder (GtkFileChooserDefault *impl)
8143 {
8144   GtkTreeSelection *selection;
8145   struct switch_folder_closure closure;
8146
8147   /* We do this with foreach() rather than get_selected() as we may be in
8148    * multiple selection mode
8149    */
8150
8151   closure.impl = impl;
8152   closure.file = NULL;
8153   closure.num_selected = 0;
8154
8155   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
8156   gtk_tree_selection_selected_foreach (selection, switch_folder_foreach_cb, &closure);
8157
8158   g_assert (closure.file && closure.num_selected == 1);
8159
8160   change_folder_and_display_error (impl, closure.file, FALSE);
8161 }
8162
8163 /* Gets the GFileInfo for the selected row in the file list; assumes single
8164  * selection mode.
8165  */
8166 static GFileInfo *
8167 get_selected_file_info_from_file_list (GtkFileChooserDefault *impl,
8168                                        gboolean              *had_selection)
8169 {
8170   GtkTreeSelection *selection;
8171   GtkTreeIter iter;
8172   GFileInfo *info;
8173
8174   g_assert (!impl->select_multiple);
8175   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
8176   if (!gtk_tree_selection_get_selected (selection, NULL, &iter))
8177     {
8178       *had_selection = FALSE;
8179       return NULL;
8180     }
8181
8182   *had_selection = TRUE;
8183
8184   info = _gtk_file_system_model_get_info (impl->browse_files_model, &iter);
8185   return info;
8186 }
8187
8188 /* Gets the display name of the selected file in the file list; assumes single
8189  * selection mode and that something is selected.
8190  */
8191 static const gchar *
8192 get_display_name_from_file_list (GtkFileChooserDefault *impl)
8193 {
8194   GFileInfo *info;
8195   gboolean had_selection;
8196
8197   info = get_selected_file_info_from_file_list (impl, &had_selection);
8198   g_assert (had_selection);
8199   g_assert (info != NULL);
8200
8201   return g_file_info_get_display_name (info);
8202 }
8203
8204 static void
8205 add_custom_button_to_dialog (GtkDialog   *dialog,
8206                              const gchar *mnemonic_label,
8207                              const gchar *stock_id,
8208                              gint         response_id)
8209 {
8210   GtkWidget *button;
8211
8212   button = gtk_button_new_with_mnemonic (mnemonic_label);
8213   gtk_widget_set_can_default (button, TRUE);
8214   gtk_button_set_image (GTK_BUTTON (button),
8215                         gtk_image_new_from_stock (stock_id, GTK_ICON_SIZE_BUTTON));
8216   gtk_widget_show (button);
8217
8218   gtk_dialog_add_action_widget (GTK_DIALOG (dialog), button, response_id);
8219 }
8220
8221 /* Presents an overwrite confirmation dialog; returns whether we should accept
8222  * the filename.
8223  */
8224 static gboolean
8225 confirm_dialog_should_accept_filename (GtkFileChooserDefault *impl,
8226                                        const gchar           *file_part,
8227                                        const gchar           *folder_display_name)
8228 {
8229   GtkWindow *toplevel;
8230   GtkWidget *dialog;
8231   int response;
8232
8233   toplevel = get_toplevel (GTK_WIDGET (impl));
8234
8235   dialog = gtk_message_dialog_new (toplevel,
8236                                    GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
8237                                    GTK_MESSAGE_QUESTION,
8238                                    GTK_BUTTONS_NONE,
8239                                    _("A file named \"%s\" already exists.  Do you want to replace it?"),
8240                                    file_part);
8241   gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
8242                                             _("The file already exists in \"%s\".  Replacing it will "
8243                                               "overwrite its contents."),
8244                                             folder_display_name);
8245
8246   gtk_dialog_add_button (GTK_DIALOG (dialog), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
8247   add_custom_button_to_dialog (GTK_DIALOG (dialog), _("_Replace"),
8248                                GTK_STOCK_SAVE_AS, GTK_RESPONSE_ACCEPT);
8249   gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
8250                                            GTK_RESPONSE_ACCEPT,
8251                                            GTK_RESPONSE_CANCEL,
8252                                            -1);
8253   gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT);
8254
8255   if (gtk_window_has_group (toplevel))
8256     gtk_window_group_add_window (gtk_window_get_group (toplevel),
8257                                  GTK_WINDOW (dialog));
8258
8259   response = gtk_dialog_run (GTK_DIALOG (dialog));
8260
8261   gtk_widget_destroy (dialog);
8262
8263   return (response == GTK_RESPONSE_ACCEPT);
8264 }
8265
8266 struct GetDisplayNameData
8267 {
8268   GtkFileChooserDefault *impl;
8269   gchar *file_part;
8270 };
8271
8272 /* Every time we request a response explicitly, we need to save the selection to the recently-used list,
8273  * as requesting a response means, "the dialog is confirmed".
8274  */
8275 static void
8276 request_response_and_add_to_recent_list (GtkFileChooserDefault *impl)
8277 {
8278   g_signal_emit_by_name (impl, "response-requested");
8279   add_selection_to_recent_list (impl);
8280 }
8281
8282 static void
8283 confirmation_confirm_get_info_cb (GCancellable *cancellable,
8284                                   GFileInfo    *info,
8285                                   const GError *error,
8286                                   gpointer      user_data)
8287 {
8288   gboolean cancelled = g_cancellable_is_cancelled (cancellable);
8289   gboolean should_respond = FALSE;
8290   struct GetDisplayNameData *data = user_data;
8291
8292   if (cancellable != data->impl->should_respond_get_info_cancellable)
8293     goto out;
8294
8295   data->impl->should_respond_get_info_cancellable = NULL;
8296
8297   if (cancelled)
8298     goto out;
8299
8300   if (error)
8301     /* Huh?  Did the folder disappear?  Let the caller deal with it */
8302     should_respond = TRUE;
8303   else
8304     should_respond = confirm_dialog_should_accept_filename (data->impl, data->file_part, g_file_info_get_display_name (info));
8305
8306   set_busy_cursor (data->impl, FALSE);
8307   if (should_respond)
8308     request_response_and_add_to_recent_list (data->impl);
8309
8310 out:
8311   g_object_unref (data->impl);
8312   g_free (data->file_part);
8313   g_free (data);
8314
8315   g_object_unref (cancellable);
8316 }
8317
8318 /* Does overwrite confirmation if appropriate, and returns whether the dialog
8319  * should respond.  Can get the file part from the file list or the save entry.
8320  */
8321 static gboolean
8322 should_respond_after_confirm_overwrite (GtkFileChooserDefault *impl,
8323                                         const gchar           *file_part,
8324                                         GFile                 *parent_file)
8325 {
8326   GtkFileChooserConfirmation conf;
8327
8328   if (!impl->do_overwrite_confirmation)
8329     return TRUE;
8330
8331   conf = GTK_FILE_CHOOSER_CONFIRMATION_CONFIRM;
8332
8333   g_signal_emit_by_name (impl, "confirm-overwrite", &conf);
8334
8335   switch (conf)
8336     {
8337     case GTK_FILE_CHOOSER_CONFIRMATION_CONFIRM:
8338       {
8339         struct GetDisplayNameData *data;
8340
8341         g_assert (file_part != NULL);
8342
8343         data = g_new0 (struct GetDisplayNameData, 1);
8344         data->impl = g_object_ref (impl);
8345         data->file_part = g_strdup (file_part);
8346
8347         if (impl->should_respond_get_info_cancellable)
8348           g_cancellable_cancel (impl->should_respond_get_info_cancellable);
8349
8350         impl->should_respond_get_info_cancellable =
8351           _gtk_file_system_get_info (impl->file_system, parent_file,
8352                                      "standard::display-name",
8353                                      confirmation_confirm_get_info_cb,
8354                                      data);
8355         set_busy_cursor (data->impl, TRUE);
8356         return FALSE;
8357       }
8358
8359     case GTK_FILE_CHOOSER_CONFIRMATION_ACCEPT_FILENAME:
8360       return TRUE;
8361
8362     case GTK_FILE_CHOOSER_CONFIRMATION_SELECT_AGAIN:
8363       return FALSE;
8364
8365     default:
8366       g_assert_not_reached ();
8367       return FALSE;
8368     }
8369 }
8370
8371 struct FileExistsData
8372 {
8373   GtkFileChooserDefault *impl;
8374   gboolean file_exists_and_is_not_folder;
8375   GFile *parent_file;
8376   GFile *file;
8377 };
8378
8379 static void
8380 name_entry_get_parent_info_cb (GCancellable *cancellable,
8381                                GFileInfo    *info,
8382                                const GError *error,
8383                                gpointer      user_data)
8384 {
8385   gboolean parent_is_folder;
8386   gboolean cancelled = g_cancellable_is_cancelled (cancellable);
8387   struct FileExistsData *data = user_data;
8388
8389   if (cancellable != data->impl->should_respond_get_info_cancellable)
8390     goto out;
8391
8392   data->impl->should_respond_get_info_cancellable = NULL;
8393
8394   set_busy_cursor (data->impl, FALSE);
8395
8396   if (cancelled)
8397     goto out;
8398
8399   if (!info)
8400     parent_is_folder = FALSE;
8401   else
8402     parent_is_folder = _gtk_file_info_consider_as_directory (info);
8403
8404   if (parent_is_folder)
8405     {
8406       if (data->impl->action == GTK_FILE_CHOOSER_ACTION_OPEN)
8407         {
8408           request_response_and_add_to_recent_list (data->impl); /* even if the file doesn't exist, apps can make good use of that (e.g. Emacs) */
8409         }
8410       else if (data->impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
8411         {
8412           if (data->file_exists_and_is_not_folder)
8413             {
8414               gboolean retval;
8415               char *file_part;
8416
8417               /* Dup the string because the string may be modified
8418                * depending on what clients do in the confirm-overwrite
8419                * signal and this corrupts the pointer
8420                */
8421               file_part = g_strdup (_gtk_file_chooser_entry_get_file_part (GTK_FILE_CHOOSER_ENTRY (data->impl->location_entry)));
8422               retval = should_respond_after_confirm_overwrite (data->impl, file_part, data->parent_file);
8423               g_free (file_part);
8424
8425               if (retval)
8426                 request_response_and_add_to_recent_list (data->impl);
8427             }
8428           else
8429             request_response_and_add_to_recent_list (data->impl);
8430         }
8431       else if (data->impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER
8432                || data->impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
8433         {
8434           GError *mkdir_error = NULL;
8435
8436           /* In both cases (SELECT_FOLDER and CREATE_FOLDER), if you type
8437            * "/blah/nonexistent" you *will* want a folder created.
8438            */
8439
8440           set_busy_cursor (data->impl, TRUE);
8441           g_file_make_directory (data->file, NULL, &mkdir_error);
8442           set_busy_cursor (data->impl, FALSE);
8443
8444           if (!mkdir_error)
8445             request_response_and_add_to_recent_list (data->impl);
8446           else
8447             error_creating_folder_dialog (data->impl, data->file, mkdir_error);
8448         }
8449       else
8450         g_assert_not_reached ();
8451     }
8452   else
8453     {
8454       if (info)
8455         {
8456           /* The parent exists, but it's not a folder!  Someone probably typed existing_file.txt/subfile.txt */
8457           error_with_file_under_nonfolder (data->impl, data->parent_file);
8458         }
8459       else
8460         {
8461           GError *error_copy;
8462
8463           /* The parent folder is not readable for some reason */
8464
8465           error_copy = g_error_copy (error);
8466           error_changing_folder_dialog (data->impl, data->parent_file, error_copy);
8467         }
8468     }
8469
8470 out:
8471   g_object_unref (data->impl);
8472   g_object_unref (data->file);
8473   g_object_unref (data->parent_file);
8474   g_free (data);
8475
8476   g_object_unref (cancellable);
8477 }
8478
8479 static void
8480 file_exists_get_info_cb (GCancellable *cancellable,
8481                          GFileInfo    *info,
8482                          const GError *error,
8483                          gpointer      user_data)
8484 {
8485   gboolean data_ownership_taken = FALSE;
8486   gboolean cancelled = g_cancellable_is_cancelled (cancellable);
8487   gboolean file_exists;
8488   gboolean is_folder;
8489   gboolean needs_parent_check = FALSE;
8490   struct FileExistsData *data = user_data;
8491
8492   if (cancellable != data->impl->file_exists_get_info_cancellable)
8493     goto out;
8494
8495   data->impl->file_exists_get_info_cancellable = NULL;
8496
8497   set_busy_cursor (data->impl, FALSE);
8498
8499   if (cancelled)
8500     goto out;
8501
8502   file_exists = (info != NULL);
8503   is_folder = (file_exists && _gtk_file_info_consider_as_directory (info));
8504
8505   if (data->impl->action == GTK_FILE_CHOOSER_ACTION_OPEN)
8506     {
8507       if (is_folder)
8508         change_folder_and_display_error (data->impl, data->file, TRUE);
8509       else
8510         {
8511           if (file_exists)
8512             request_response_and_add_to_recent_list (data->impl); /* user typed an existing filename; we are done */
8513           else
8514             needs_parent_check = TRUE; /* file doesn't exist; see if its parent exists */
8515         }
8516     }
8517   else if (data->impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
8518     {
8519       if (file_exists && !is_folder)
8520         {
8521           /* Oops, the user typed the name of an existing path which is not
8522            * a folder
8523            */
8524           error_creating_folder_over_existing_file_dialog (data->impl, data->file,
8525                                                            g_error_copy (error));
8526         }
8527       else
8528         {
8529           needs_parent_check = TRUE;
8530         }
8531     }
8532   else if (data->impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
8533     {
8534       if (!file_exists)
8535         {
8536           needs_parent_check = TRUE;
8537         }
8538       else
8539         {
8540           if (is_folder)
8541             {
8542               /* User typed a folder; we are done */
8543               request_response_and_add_to_recent_list (data->impl);
8544             }
8545           else
8546             error_selecting_folder_over_existing_file_dialog (data->impl, data->file);
8547         }
8548     }
8549   else if (data->impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
8550     {
8551       if (is_folder)
8552         change_folder_and_display_error (data->impl, data->file, TRUE);
8553       else
8554         needs_parent_check = TRUE;
8555     }
8556   else
8557     {
8558       g_assert_not_reached();
8559     }
8560
8561   if (needs_parent_check) {
8562     /* check that everything up to the last path component exists (i.e. the parent) */
8563
8564     data->file_exists_and_is_not_folder = file_exists && !is_folder;
8565     data_ownership_taken = TRUE;
8566
8567     if (data->impl->should_respond_get_info_cancellable)
8568       g_cancellable_cancel (data->impl->should_respond_get_info_cancellable);
8569
8570       data->impl->should_respond_get_info_cancellable =
8571         _gtk_file_system_get_info (data->impl->file_system,
8572                                    data->parent_file,
8573                                    "standard::type",
8574                                    name_entry_get_parent_info_cb,
8575                                    data);
8576       set_busy_cursor (data->impl, TRUE);
8577     }
8578
8579 out:
8580   if (!data_ownership_taken)
8581     {
8582       g_object_unref (data->impl);
8583       g_object_unref (data->file);
8584       g_object_unref (data->parent_file);
8585       g_free (data);
8586     }
8587
8588   g_object_unref (cancellable);
8589 }
8590
8591 static void
8592 paste_text_received (GtkClipboard          *clipboard,
8593                      const gchar           *text,
8594                      GtkFileChooserDefault *impl)
8595 {
8596   GFile *file;
8597
8598   if (!text)
8599     return;
8600
8601   file = g_file_new_for_uri (text);
8602
8603   if (!gtk_file_chooser_default_select_file (GTK_FILE_CHOOSER (impl), file, NULL))
8604     location_popup_handler (impl, text);
8605
8606   g_object_unref (file);
8607 }
8608
8609 /* Handler for the "location-popup-on-paste" keybinding signal */
8610 static void
8611 location_popup_on_paste_handler (GtkFileChooserDefault *impl)
8612 {
8613   GtkClipboard *clipboard = gtk_widget_get_clipboard (GTK_WIDGET (impl),
8614                                                       GDK_SELECTION_CLIPBOARD);
8615   gtk_clipboard_request_text (clipboard,
8616                               (GtkClipboardTextReceivedFunc) paste_text_received,
8617                               impl);
8618 }
8619
8620 /* Implementation for GtkFileChooserEmbed::should_respond() */
8621 static void
8622 add_selection_to_recent_list (GtkFileChooserDefault *impl)
8623 {
8624   GSList *files;
8625   GSList *l;
8626
8627   files = gtk_file_chooser_default_get_files (GTK_FILE_CHOOSER (impl));
8628
8629   for (l = files; l; l = l->next)
8630     {
8631       GFile *file = l->data;
8632       char *uri;
8633
8634       uri = g_file_get_uri (file);
8635       if (uri)
8636         {
8637           gtk_recent_manager_add_item (impl->recent_manager, uri);
8638           g_free (uri);
8639         }
8640     }
8641
8642   g_slist_foreach (files, (GFunc) g_object_unref, NULL);
8643   g_slist_free (files);
8644 }
8645
8646 static gboolean
8647 gtk_file_chooser_default_should_respond (GtkFileChooserEmbed *chooser_embed)
8648 {
8649   GtkFileChooserDefault *impl;
8650   GtkWidget *toplevel;
8651   GtkWidget *current_focus;
8652   gboolean retval;
8653
8654   impl = GTK_FILE_CHOOSER_DEFAULT (chooser_embed);
8655
8656   toplevel = gtk_widget_get_toplevel (GTK_WIDGET (impl));
8657   g_assert (GTK_IS_WINDOW (toplevel));
8658
8659   retval = FALSE;
8660
8661   current_focus = gtk_window_get_focus (GTK_WINDOW (toplevel));
8662
8663   if (current_focus == impl->browse_files_tree_view)
8664     {
8665       /* The following array encodes what we do based on the impl->action and the
8666        * number of files selected.
8667        */
8668       typedef enum {
8669         NOOP,                   /* Do nothing (don't respond) */
8670         RESPOND,                /* Respond immediately */
8671         RESPOND_OR_SWITCH,      /* Respond immediately if the selected item is a file; switch to it if it is a folder */
8672         ALL_FILES,              /* Respond only if everything selected is a file */
8673         ALL_FOLDERS,            /* Respond only if everything selected is a folder */
8674         SAVE_ENTRY,             /* Go to the code for handling the save entry */
8675         NOT_REACHED             /* Sanity check */
8676       } ActionToTake;
8677       static const ActionToTake what_to_do[4][3] = {
8678         /*                                0 selected            1 selected              many selected */
8679         /* ACTION_OPEN */               { NOOP,                 RESPOND_OR_SWITCH,      ALL_FILES   },
8680         /* ACTION_SAVE */               { SAVE_ENTRY,           RESPOND_OR_SWITCH,      NOT_REACHED },
8681         /* ACTION_SELECT_FOLDER */      { RESPOND,              ALL_FOLDERS,            ALL_FOLDERS },
8682         /* ACTION_CREATE_FOLDER */      { SAVE_ENTRY,           ALL_FOLDERS,            NOT_REACHED }
8683       };
8684
8685       int num_selected;
8686       gboolean all_files, all_folders;
8687       int k;
8688       ActionToTake action;
8689
8690     file_list:
8691
8692       g_assert (impl->action >= GTK_FILE_CHOOSER_ACTION_OPEN && impl->action <= GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER);
8693
8694       if (impl->operation_mode == OPERATION_MODE_SEARCH)
8695         {
8696           retval = search_should_respond (impl);
8697           goto out;
8698         }
8699
8700       if (impl->operation_mode == OPERATION_MODE_RECENT)
8701         {
8702           if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
8703             goto save_entry;
8704           else
8705             {
8706               retval = recent_should_respond (impl);
8707               goto out;
8708             }
8709         }
8710
8711       selection_check (impl, &num_selected, &all_files, &all_folders);
8712
8713       if (num_selected > 2)
8714         k = 2;
8715       else
8716         k = num_selected;
8717
8718       action = what_to_do [impl->action] [k];
8719
8720       switch (action)
8721         {
8722         case NOOP:
8723           return FALSE;
8724
8725         case RESPOND:
8726           retval = TRUE;
8727           goto out;
8728
8729         case RESPOND_OR_SWITCH:
8730           g_assert (num_selected == 1);
8731
8732           if (all_folders)
8733             {
8734               switch_to_selected_folder (impl);
8735               return FALSE;
8736             }
8737           else if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
8738             {
8739               retval = should_respond_after_confirm_overwrite (impl,
8740                                                                get_display_name_from_file_list (impl),
8741                                                                impl->current_folder);
8742               goto out;
8743             }
8744           else
8745             {
8746               retval = TRUE;
8747               goto out;
8748             }
8749
8750         case ALL_FILES:
8751           retval = all_files;
8752           goto out;
8753
8754         case ALL_FOLDERS:
8755           retval = all_folders;
8756           goto out;
8757
8758         case SAVE_ENTRY:
8759           goto save_entry;
8760
8761         default:
8762           g_assert_not_reached ();
8763         }
8764     }
8765   else if ((impl->location_entry != NULL) && (current_focus == impl->location_entry))
8766     {
8767       GFile *file;
8768       gboolean is_well_formed, is_empty, is_file_part_empty;
8769       gboolean is_folder;
8770       GtkFileChooserEntry *entry;
8771       GError *error;
8772
8773     save_entry:
8774
8775       g_assert (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
8776                 || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER
8777                 || ((impl->action == GTK_FILE_CHOOSER_ACTION_OPEN
8778                      || impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
8779                     && impl->location_mode == LOCATION_MODE_FILENAME_ENTRY));
8780
8781       entry = GTK_FILE_CHOOSER_ENTRY (impl->location_entry);
8782       check_save_entry (impl, &file, &is_well_formed, &is_empty, &is_file_part_empty, &is_folder);
8783
8784       if (!is_well_formed)
8785         {
8786           if (!is_empty
8787               && impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
8788               && impl->operation_mode == OPERATION_MODE_RECENT)
8789             {
8790               path_bar_set_mode (impl, PATH_BAR_ERROR_NO_FOLDER);
8791 #if 0
8792               /* We'll #ifdef this out, as the fucking treeview selects its first row,
8793                * thus changing our assumption that no selection is present - setting
8794                * a selection causes the error message from path_bar_set_mode() to go away,
8795                * but we want the user to see that message!
8796                */
8797               gtk_widget_grab_focus (impl->browse_files_tree_view);
8798 #endif
8799             }
8800           /* FIXME: else show an "invalid filename" error as the pathbar mode? */
8801
8802           return FALSE;
8803         }
8804
8805       if (is_empty)
8806         {
8807           if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
8808               || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
8809             {
8810               path_bar_set_mode (impl, PATH_BAR_ERROR_NO_FILENAME);
8811               gtk_widget_grab_focus (impl->location_entry);
8812               return FALSE;
8813             }
8814
8815           goto file_list;
8816         }
8817
8818       g_assert (file != NULL);
8819
8820       error = NULL;
8821       if (is_folder)
8822         {
8823           if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
8824               impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
8825             {
8826               change_folder_and_display_error (impl, file, TRUE);
8827             }
8828           else if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ||
8829                    impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
8830             {
8831               /* The folder already exists, so we do not need to create it.
8832                * Just respond to terminate the dialog.
8833                */
8834               retval = TRUE;
8835             }
8836           else
8837             {
8838               g_assert_not_reached ();
8839             }
8840         }
8841       else
8842         {
8843           struct FileExistsData *data;
8844
8845           /* We need to check whether file exists and whether it is a folder -
8846            * the GtkFileChooserEntry *does* report is_folder==FALSE as a false
8847            * negative (it doesn't know yet if your last path component is a
8848            * folder).
8849            */
8850
8851           data = g_new0 (struct FileExistsData, 1);
8852           data->impl = g_object_ref (impl);
8853           data->file = g_object_ref (file);
8854           data->parent_file = g_object_ref (_gtk_file_chooser_entry_get_current_folder (entry));
8855
8856           if (impl->file_exists_get_info_cancellable)
8857             g_cancellable_cancel (impl->file_exists_get_info_cancellable);
8858
8859           impl->file_exists_get_info_cancellable =
8860             _gtk_file_system_get_info (impl->file_system, file,
8861                                        "standard::type",
8862                                        file_exists_get_info_cb,
8863                                        data);
8864
8865           set_busy_cursor (impl, TRUE);
8866
8867           if (error != NULL)
8868             g_error_free (error);
8869         }
8870
8871       g_object_unref (file);
8872     }
8873   else if (impl->toplevel_last_focus_widget == impl->browse_files_tree_view)
8874     {
8875       /* The focus is on a dialog's action area button, *and* the widget that
8876        * was focused immediately before it is the file list.  
8877        */
8878       goto file_list;
8879     }
8880   else if (impl->operation_mode == OPERATION_MODE_SEARCH && impl->toplevel_last_focus_widget == impl->search_entry)
8881     {
8882       search_entry_activate_cb (GTK_ENTRY (impl->search_entry), impl);
8883       return FALSE;
8884     }
8885   else if (impl->location_entry && impl->toplevel_last_focus_widget == impl->location_entry)
8886     {
8887       /* The focus is on a dialog's action area button, *and* the widget that
8888        * was focused immediately before it is the location entry.
8889        */
8890       goto save_entry;
8891     }
8892   else
8893     /* The focus is on a dialog's action area button or something else */
8894     if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
8895         || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
8896       goto save_entry;
8897     else
8898       goto file_list; 
8899
8900  out:
8901
8902   if (retval)
8903     add_selection_to_recent_list (impl);
8904
8905   return retval;
8906 }
8907
8908 /* Implementation for GtkFileChooserEmbed::initial_focus() */
8909 static void
8910 gtk_file_chooser_default_initial_focus (GtkFileChooserEmbed *chooser_embed)
8911 {
8912   GtkFileChooserDefault *impl;
8913   GtkWidget *widget;
8914
8915   impl = GTK_FILE_CHOOSER_DEFAULT (chooser_embed);
8916
8917   if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
8918       impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
8919     {
8920       if (impl->location_mode == LOCATION_MODE_PATH_BAR)
8921         widget = impl->browse_files_tree_view;
8922       else
8923         widget = impl->location_entry;
8924     }
8925   else if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE ||
8926            impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
8927     widget = impl->location_entry;
8928   else
8929     {
8930       g_assert_not_reached ();
8931       widget = NULL;
8932     }
8933
8934   g_assert (widget != NULL);
8935   gtk_widget_grab_focus (widget);
8936 }
8937
8938 /* Callback used from gtk_tree_selection_selected_foreach(); gets the selected GFiles */
8939 static void
8940 search_selected_foreach_get_file_cb (GtkTreeModel *model,
8941                                      GtkTreePath  *path,
8942                                      GtkTreeIter  *iter,
8943                                      gpointer      data)
8944 {
8945   GSList **list;
8946   GFile *file;
8947
8948   list = data;
8949
8950   gtk_tree_model_get (model, iter, MODEL_COL_FILE, &file, -1);
8951   *list = g_slist_prepend (*list, file); /* The file already has a new ref courtesy of gtk_tree_model_get(); this will be unreffed by the caller */
8952 }
8953
8954 /* Constructs a list of the selected paths in search mode */
8955 static GSList *
8956 search_get_selected_files (GtkFileChooserDefault *impl)
8957 {
8958   GSList *result;
8959   GtkTreeSelection *selection;
8960
8961   result = NULL;
8962
8963   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
8964   gtk_tree_selection_selected_foreach (selection, search_selected_foreach_get_file_cb, &result);
8965   result = g_slist_reverse (result);
8966
8967   return result;
8968 }
8969
8970 /* Called from ::should_respond().  We return whether there are selected files
8971  * in the search list.
8972  */
8973 static gboolean
8974 search_should_respond (GtkFileChooserDefault *impl)
8975 {
8976   GtkTreeSelection *selection;
8977
8978   g_assert (impl->operation_mode == OPERATION_MODE_SEARCH);
8979
8980   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
8981   return (gtk_tree_selection_count_selected_rows (selection) != 0);
8982 }
8983
8984 /* Adds one hit from the search engine to the search_model */
8985 static void
8986 search_add_hit (GtkFileChooserDefault *impl,
8987                 gchar                 *uri)
8988 {
8989   GFile *file;
8990
8991   file = g_file_new_for_uri (uri);
8992   if (!file)
8993     return;
8994
8995   if (!g_file_is_native (file))
8996     {
8997       g_object_unref (file);
8998       return;
8999     }
9000
9001   _gtk_file_system_model_add_and_query_file (impl->search_model,
9002                                              file,
9003                                              MODEL_ATTRIBUTES);
9004
9005   g_object_unref (file);
9006 }
9007
9008 /* Callback used from GtkSearchEngine when we get new hits */
9009 static void
9010 search_engine_hits_added_cb (GtkSearchEngine *engine,
9011                              GList           *hits,
9012                              gpointer         data)
9013 {
9014   GtkFileChooserDefault *impl;
9015   GList *l;
9016   
9017   impl = GTK_FILE_CHOOSER_DEFAULT (data);
9018
9019   for (l = hits; l; l = l->next)
9020     search_add_hit (impl, (gchar*)l->data);
9021 }
9022
9023 /* Callback used from GtkSearchEngine when the query is done running */
9024 static void
9025 search_engine_finished_cb (GtkSearchEngine *engine,
9026                            gpointer         data)
9027 {
9028   GtkFileChooserDefault *impl;
9029   
9030   impl = GTK_FILE_CHOOSER_DEFAULT (data);
9031   
9032 #if 0
9033   /* EB: setting the model here will avoid loads of row events,
9034    * but it'll make the search look like blocked.
9035    */
9036   gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_files_tree_view),
9037                            GTK_TREE_MODEL (impl->search_model));
9038   file_list_set_sort_column_ids (impl);
9039 #endif
9040
9041   /* FMQ: if search was empty, say that we got no hits */
9042   set_busy_cursor (impl, FALSE);
9043 }
9044
9045 /* Displays a generic error when we cannot create a GtkSearchEngine.  
9046  * It would be better if _gtk_search_engine_new() gave us a GError 
9047  * with a better message, but it doesn't do that right now.
9048  */
9049 static void
9050 search_error_could_not_create_client (GtkFileChooserDefault *impl)
9051 {
9052   error_message (impl,
9053                  _("Could not start the search process"),
9054                  _("The program was not able to create a connection to the indexer "
9055                    "daemon.  Please make sure it is running."));
9056 }
9057
9058 static void
9059 search_engine_error_cb (GtkSearchEngine *engine,
9060                         const gchar     *message,
9061                         gpointer         data)
9062 {
9063   GtkFileChooserDefault *impl;
9064   
9065   impl = GTK_FILE_CHOOSER_DEFAULT (data);
9066
9067   search_stop_searching (impl, TRUE);
9068   error_message (impl, _("Could not send the search request"), message);
9069
9070   set_busy_cursor (impl, FALSE);
9071 }
9072
9073 /* Frees the data in the search_model */
9074 static void
9075 search_clear_model (GtkFileChooserDefault *impl, 
9076                     gboolean               remove_from_treeview)
9077 {
9078   if (!impl->search_model)
9079     return;
9080
9081   g_object_unref (impl->search_model);
9082   impl->search_model = NULL;
9083   
9084   if (remove_from_treeview)
9085     gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_files_tree_view), NULL);
9086 }
9087
9088 /* Stops any ongoing searches; does not touch the search_model */
9089 static void
9090 search_stop_searching (GtkFileChooserDefault *impl,
9091                        gboolean               remove_query)
9092 {
9093   if (remove_query && impl->search_query)
9094     {
9095       g_object_unref (impl->search_query);
9096       impl->search_query = NULL;
9097     }
9098   
9099   if (impl->search_engine)
9100     {
9101       _gtk_search_engine_stop (impl->search_engine);
9102       
9103       g_object_unref (impl->search_engine);
9104       impl->search_engine = NULL;
9105     }
9106 }
9107
9108 /* Creates the search_model and puts it in the tree view */
9109 static void
9110 search_setup_model (GtkFileChooserDefault *impl)
9111 {
9112   g_assert (impl->search_model == NULL);
9113
9114   impl->search_model = _gtk_file_system_model_new (file_system_model_set,
9115                                                    impl,
9116                                                    MODEL_COLUMN_TYPES);
9117
9118   gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->search_model),
9119                                    MODEL_COL_NAME,
9120                                    name_sort_func,
9121                                    impl, NULL);
9122   gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->search_model),
9123                                    MODEL_COL_MTIME,
9124                                    mtime_sort_func,
9125                                    impl, NULL);
9126   gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->search_model),
9127                                    MODEL_COL_SIZE,
9128                                    size_sort_func,
9129                                    impl, NULL);
9130   set_sort_column (impl);
9131
9132   /* EB: setting the model here will make the hits list update feel
9133    * more "alive" than setting the model at the end of the search
9134    * run
9135    */
9136   gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_files_tree_view),
9137                            GTK_TREE_MODEL (impl->search_model));
9138   file_list_set_sort_column_ids (impl);
9139 }
9140
9141 /* Creates a new query with the specified text and launches it */
9142 static void
9143 search_start_query (GtkFileChooserDefault *impl,
9144                     const gchar           *query_text)
9145 {
9146   search_stop_searching (impl, FALSE);
9147   search_clear_model (impl, TRUE);
9148   search_setup_model (impl);
9149   set_busy_cursor (impl, TRUE);
9150
9151   if (impl->search_engine == NULL)
9152     impl->search_engine = _gtk_search_engine_new ();
9153
9154   if (!impl->search_engine)
9155     {
9156       set_busy_cursor (impl, FALSE);
9157       search_error_could_not_create_client (impl); /* lame; we don't get an error code or anything */
9158       return;
9159     }
9160
9161   if (!impl->search_query)
9162     {
9163       impl->search_query = _gtk_query_new ();
9164       _gtk_query_set_text (impl->search_query, query_text);
9165     }
9166   
9167   _gtk_search_engine_set_query (impl->search_engine, impl->search_query);
9168
9169   g_signal_connect (impl->search_engine, "hits-added",
9170                     G_CALLBACK (search_engine_hits_added_cb), impl);
9171   g_signal_connect (impl->search_engine, "finished",
9172                     G_CALLBACK (search_engine_finished_cb), impl);
9173   g_signal_connect (impl->search_engine, "error",
9174                     G_CALLBACK (search_engine_error_cb), impl);
9175
9176   _gtk_search_engine_start (impl->search_engine);
9177 }
9178
9179 /* Callback used when the user presses Enter while typing on the search
9180  * entry; starts the query
9181  */
9182 static void
9183 search_entry_activate_cb (GtkEntry *entry,
9184                           gpointer data)
9185 {
9186   GtkFileChooserDefault *impl;
9187   const char *text;
9188
9189   impl = GTK_FILE_CHOOSER_DEFAULT (data);
9190
9191   text = gtk_entry_get_text (GTK_ENTRY (impl->search_entry));
9192   if (strlen (text) == 0)
9193     return;
9194
9195   /* reset any existing query object */
9196   if (impl->search_query)
9197     {
9198       g_object_unref (impl->search_query);
9199       impl->search_query = NULL;
9200     }
9201
9202   search_start_query (impl, text);
9203 }
9204
9205 static gboolean
9206 focus_entry_idle_cb (GtkFileChooserDefault *impl)
9207 {
9208   GDK_THREADS_ENTER ();
9209   
9210   g_source_destroy (impl->focus_entry_idle);
9211   impl->focus_entry_idle = NULL;
9212
9213   if (impl->search_entry)
9214     gtk_widget_grab_focus (impl->search_entry);
9215
9216   GDK_THREADS_LEAVE ();
9217
9218   return FALSE;
9219 }
9220
9221 static void
9222 focus_search_entry_in_idle (GtkFileChooserDefault *impl)
9223 {
9224   /* bgo#634558 - When the user clicks on the Search entry in the shortcuts
9225    * pane, we get a selection-changed signal and we set up the search widgets.
9226    * However, gtk_tree_view_button_press() focuses the treeview *after* making
9227    * the change to the selection.  So, we need to re-focus the search entry
9228    * after the treeview has finished doing its work; we'll do that in an idle
9229    * handler.
9230    */
9231
9232   if (!impl->focus_entry_idle)
9233     impl->focus_entry_idle = add_idle_while_impl_is_alive (impl, G_CALLBACK (focus_entry_idle_cb));
9234 }
9235
9236 /* Hides the path bar and creates the search entry */
9237 static void
9238 search_setup_widgets (GtkFileChooserDefault *impl)
9239 {
9240   impl->search_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
9241
9242   path_bar_update (impl);
9243
9244   impl->search_entry = gtk_entry_new ();
9245   g_signal_connect (impl->search_entry, "activate",
9246                     G_CALLBACK (search_entry_activate_cb),
9247                     impl);
9248   gtk_box_pack_start (GTK_BOX (impl->search_hbox), impl->search_entry, TRUE, TRUE, 0);
9249
9250   /* if there already is a query, restart it */
9251   if (impl->search_query)
9252     {
9253       gchar *query = _gtk_query_get_text (impl->search_query);
9254
9255       if (query)
9256         {
9257           gtk_entry_set_text (GTK_ENTRY (impl->search_entry), query);
9258           search_start_query (impl, query);
9259
9260           g_free (query);
9261         }
9262       else
9263         {
9264           g_object_unref (impl->search_query);
9265           impl->search_query = NULL;
9266         }
9267     }
9268
9269   /* Box for search widgets */
9270   gtk_box_pack_start (GTK_BOX (impl->browse_path_bar_hbox), impl->search_hbox, TRUE, TRUE, 0);
9271   gtk_widget_show_all (impl->search_hbox);
9272   gtk_size_group_add_widget (GTK_SIZE_GROUP (impl->browse_path_bar_size_group), impl->search_hbox);
9273
9274   /* Hide the location widgets temporarily */
9275
9276   if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
9277       impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
9278     {
9279       gtk_widget_hide (impl->location_button);
9280       gtk_widget_hide (impl->location_entry_box);
9281     }
9282
9283   focus_search_entry_in_idle (impl);
9284
9285   /* FMQ: hide the filter combo? */
9286 }
9287
9288 /*
9289  * Recent files support
9290  */
9291
9292 /* Frees the data in the recent_model */
9293 static void
9294 recent_clear_model (GtkFileChooserDefault *impl,
9295                     gboolean               remove_from_treeview)
9296 {
9297   if (!impl->recent_model)
9298     return;
9299
9300   if (remove_from_treeview)
9301     gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_files_tree_view), NULL);
9302
9303   g_object_unref (impl->recent_model);
9304   impl->recent_model = NULL;
9305 }
9306
9307 /* Stops any ongoing loading of the recent files list; does
9308  * not touch the recent_model
9309  */
9310 static void
9311 recent_stop_loading (GtkFileChooserDefault *impl)
9312 {
9313   if (impl->load_recent_id)
9314     {
9315       g_source_remove (impl->load_recent_id);
9316       impl->load_recent_id = 0;
9317     }
9318 }
9319
9320 static void
9321 recent_setup_model (GtkFileChooserDefault *impl)
9322 {
9323   g_assert (impl->recent_model == NULL);
9324
9325   impl->recent_model = _gtk_file_system_model_new (file_system_model_set,
9326                                                    impl,
9327                                                    MODEL_COLUMN_TYPES);
9328
9329   _gtk_file_system_model_set_filter (impl->recent_model,
9330                                      impl->current_filter);
9331   gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->recent_model),
9332                                    MODEL_COL_NAME,
9333                                    name_sort_func,
9334                                    impl, NULL);
9335   gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->recent_model),
9336                                    MODEL_COL_SIZE,
9337                                    size_sort_func,
9338                                    impl, NULL);
9339   gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->recent_model),
9340                                    MODEL_COL_MTIME,
9341                                    mtime_sort_func,
9342                                    impl, NULL);
9343   set_sort_column (impl);
9344 }
9345
9346 typedef struct
9347 {
9348   GtkFileChooserDefault *impl;
9349   GList *items;
9350   guint needs_sorting : 1;
9351 } RecentLoadData;
9352
9353 static void
9354 recent_idle_cleanup (gpointer data)
9355 {
9356   RecentLoadData *load_data = data;
9357   GtkFileChooserDefault *impl = load_data->impl;
9358
9359   gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_files_tree_view),
9360                            GTK_TREE_MODEL (impl->recent_model));
9361   file_list_set_sort_column_ids (impl);
9362
9363   set_busy_cursor (impl, FALSE);
9364   
9365   impl->load_recent_id = 0;
9366   
9367   g_free (load_data);
9368 }
9369
9370 static gint
9371 recent_sort_mru (gconstpointer a,
9372                  gconstpointer b)
9373 {
9374   GtkRecentInfo *info_a = (GtkRecentInfo *) a;
9375   GtkRecentInfo *info_b = (GtkRecentInfo *) b;
9376
9377   return (gtk_recent_info_get_modified (info_b) - gtk_recent_info_get_modified (info_a));
9378 }
9379
9380 static gint
9381 get_recent_files_limit (GtkWidget *widget)
9382 {
9383   GtkSettings *settings;
9384   gint limit;
9385
9386   if (gtk_widget_has_screen (widget))
9387     settings = gtk_settings_get_for_screen (gtk_widget_get_screen (widget));
9388   else
9389     settings = gtk_settings_get_default ();
9390
9391   g_object_get (G_OBJECT (settings), "gtk-recent-files-limit", &limit, NULL);
9392
9393   return limit;
9394 }
9395
9396 /* Populates the file system model with the GtkRecentInfo* items in the provided list; frees the items */
9397 static void
9398 populate_model_with_recent_items (GtkFileChooserDefault *impl, GList *items)
9399 {
9400   gint limit;
9401   GList *l;
9402   int n;
9403
9404   limit = get_recent_files_limit (GTK_WIDGET (impl));
9405
9406   n = 0;
9407
9408   for (l = items; l; l = l->next)
9409     {
9410       GtkRecentInfo *info = l->data;
9411       GFile *file;
9412
9413       file = g_file_new_for_uri (gtk_recent_info_get_uri (info));
9414       _gtk_file_system_model_add_and_query_file (impl->recent_model,
9415                                                  file,
9416                                                  MODEL_ATTRIBUTES);
9417       g_object_unref (file);
9418
9419       n++;
9420       if (limit != -1 && n >= limit)
9421         break;
9422     }
9423 }
9424
9425 static void
9426 populate_model_with_folders (GtkFileChooserDefault *impl, GList *items)
9427 {
9428   GList *folders;
9429   GList *l;
9430
9431   folders = _gtk_file_chooser_extract_recent_folders (items);
9432
9433   for (l = folders; l; l = l->next)
9434     {
9435       GFile *folder = l->data;
9436
9437       _gtk_file_system_model_add_and_query_file (impl->recent_model,
9438                                                  folder,
9439                                                  MODEL_ATTRIBUTES);
9440     }
9441
9442   g_list_foreach (folders, (GFunc) g_object_unref, NULL);
9443   g_list_free (folders);
9444 }
9445
9446 static gboolean
9447 recent_idle_load (gpointer data)
9448 {
9449   RecentLoadData *load_data = data;
9450   GtkFileChooserDefault *impl = load_data->impl;
9451
9452   if (!impl->recent_manager)
9453     return FALSE;
9454
9455   /* first iteration: load all the items */
9456   if (!load_data->items)
9457     {
9458       load_data->items = gtk_recent_manager_get_items (impl->recent_manager);
9459       if (!load_data->items)
9460         return FALSE;
9461
9462       load_data->needs_sorting = TRUE;
9463
9464       return TRUE;
9465     }
9466   
9467   /* second iteration: MRU sorting and clamping, and populating the model */
9468   if (load_data->needs_sorting)
9469     {
9470       load_data->items = g_list_sort (load_data->items, recent_sort_mru);
9471
9472       if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN)
9473         populate_model_with_recent_items (impl, load_data->items);
9474       else
9475         populate_model_with_folders (impl, load_data->items);
9476
9477       g_list_foreach (load_data->items, (GFunc) gtk_recent_info_unref, NULL);
9478       g_list_free (load_data->items);
9479       load_data->items = NULL;
9480     }
9481
9482   return FALSE;
9483 }
9484
9485 static void
9486 recent_start_loading (GtkFileChooserDefault *impl)
9487 {
9488   RecentLoadData *load_data;
9489
9490   recent_stop_loading (impl);
9491   recent_clear_model (impl, TRUE);
9492   recent_setup_model (impl);
9493   set_busy_cursor (impl, TRUE);
9494
9495   g_assert (impl->load_recent_id == 0);
9496
9497   load_data = g_new (RecentLoadData, 1);
9498   load_data->impl = impl;
9499   load_data->items = NULL;
9500   load_data->needs_sorting = TRUE;
9501
9502   /* begin lazy loading the recent files into the model */
9503   impl->load_recent_id = gdk_threads_add_idle_full (G_PRIORITY_HIGH_IDLE + 30,
9504                                                     recent_idle_load,
9505                                                     load_data,
9506                                                     recent_idle_cleanup);
9507 }
9508
9509 static void
9510 recent_selected_foreach_get_file_cb (GtkTreeModel *model,
9511                                      GtkTreePath  *path,
9512                                      GtkTreeIter  *iter,
9513                                      gpointer      data)
9514 {
9515   GSList **list;
9516   GFile *file;
9517
9518   list = data;
9519
9520   gtk_tree_model_get (model, iter, MODEL_COL_FILE, &file, -1);
9521   *list = g_slist_prepend (*list, file);
9522 }
9523
9524 /* Constructs a list of the selected paths in recent files mode */
9525 static GSList *
9526 recent_get_selected_files (GtkFileChooserDefault *impl)
9527 {
9528   GSList *result;
9529   GtkTreeSelection *selection;
9530
9531   result = NULL;
9532
9533   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
9534   gtk_tree_selection_selected_foreach (selection, recent_selected_foreach_get_file_cb, &result);
9535   result = g_slist_reverse (result);
9536
9537   return result;
9538 }
9539
9540 /* Called from ::should_respond().  We return whether there are selected
9541  * files in the recent files list.
9542  */
9543 static gboolean
9544 recent_should_respond (GtkFileChooserDefault *impl)
9545 {
9546   GtkTreeSelection *selection;
9547
9548   g_assert (impl->operation_mode == OPERATION_MODE_RECENT);
9549
9550   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
9551   return (gtk_tree_selection_count_selected_rows (selection) != 0);
9552 }
9553
9554 static void
9555 set_current_filter (GtkFileChooserDefault *impl,
9556                     GtkFileFilter         *filter)
9557 {
9558   if (impl->current_filter != filter)
9559     {
9560       int filter_index;
9561
9562       /* NULL filters are allowed to reset to non-filtered status
9563        */
9564       filter_index = g_slist_index (impl->filters, filter);
9565       if (impl->filters && filter && filter_index < 0)
9566         return;
9567
9568       if (impl->current_filter)
9569         g_object_unref (impl->current_filter);
9570       impl->current_filter = filter;
9571       if (impl->current_filter)
9572         {
9573           g_object_ref_sink (impl->current_filter);
9574         }
9575
9576       if (impl->filters)
9577         gtk_combo_box_set_active (GTK_COMBO_BOX (impl->filter_combo),
9578                                   filter_index);
9579
9580       if (impl->browse_files_model)
9581         install_list_model_filter (impl);
9582
9583       if (impl->search_model)
9584         _gtk_file_system_model_set_filter (impl->search_model, filter);
9585
9586       if (impl->recent_model)
9587         _gtk_file_system_model_set_filter (impl->recent_model, filter);
9588
9589       g_object_notify (G_OBJECT (impl), "filter");
9590     }
9591 }
9592
9593 static void
9594 filter_combo_changed (GtkComboBox           *combo_box,
9595                       GtkFileChooserDefault *impl)
9596 {
9597   gint new_index = gtk_combo_box_get_active (combo_box);
9598   GtkFileFilter *new_filter = g_slist_nth_data (impl->filters, new_index);
9599
9600   set_current_filter (impl, new_filter);
9601 }
9602
9603 static void
9604 check_preview_change (GtkFileChooserDefault *impl)
9605 {
9606   GtkTreePath *cursor_path;
9607   GFile *new_file;
9608   char *new_display_name;
9609   GtkTreeModel *model;
9610
9611   gtk_tree_view_get_cursor (GTK_TREE_VIEW (impl->browse_files_tree_view), &cursor_path, NULL);
9612   model = gtk_tree_view_get_model (GTK_TREE_VIEW (impl->browse_files_tree_view));
9613   if (cursor_path)
9614     {
9615       GtkTreeIter iter;
9616
9617       gtk_tree_model_get_iter (model, &iter, cursor_path);
9618       gtk_tree_model_get (model, &iter,
9619                           MODEL_COL_FILE, &new_file,
9620                           MODEL_COL_NAME, &new_display_name,
9621                           -1);
9622       
9623       gtk_tree_path_free (cursor_path);
9624     }
9625   else
9626     {
9627       new_file = NULL;
9628       new_display_name = NULL;
9629     }
9630
9631   if (new_file != impl->preview_file &&
9632       !(new_file && impl->preview_file &&
9633         g_file_equal (new_file, impl->preview_file)))
9634     {
9635       if (impl->preview_file)
9636         {
9637           g_object_unref (impl->preview_file);
9638           g_free (impl->preview_display_name);
9639         }
9640
9641       if (new_file)
9642         {
9643           impl->preview_file = new_file;
9644           impl->preview_display_name = new_display_name;
9645         }
9646       else
9647         {
9648           impl->preview_file = NULL;
9649           impl->preview_display_name = NULL;
9650           g_free (new_display_name);
9651         }
9652
9653       if (impl->use_preview_label && impl->preview_label)
9654         gtk_label_set_text (GTK_LABEL (impl->preview_label), impl->preview_display_name);
9655
9656       g_signal_emit_by_name (impl, "update-preview");
9657     }
9658   else
9659     {
9660       if (new_file)
9661         g_object_unref (new_file);
9662
9663       g_free (new_display_name);
9664     }
9665 }
9666
9667 static void
9668 shortcuts_activate_volume_mount_cb (GCancellable        *cancellable,
9669                                     GtkFileSystemVolume *volume,
9670                                     const GError        *error,
9671                                     gpointer             data)
9672 {
9673   GFile *file;
9674   gboolean cancelled = g_cancellable_is_cancelled (cancellable);
9675   GtkFileChooserDefault *impl = data;
9676
9677   if (cancellable != impl->shortcuts_activate_iter_cancellable)
9678     goto out;
9679
9680   impl->shortcuts_activate_iter_cancellable = NULL;
9681
9682   set_busy_cursor (impl, FALSE);
9683
9684   if (cancelled)
9685     goto out;
9686
9687   if (error)
9688     {
9689       if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_FAILED_HANDLED))
9690         {
9691           char *msg, *name;
9692
9693           name = _gtk_file_system_volume_get_display_name (volume);
9694           msg = g_strdup_printf (_("Could not mount %s"), name);
9695
9696           error_message (impl, msg, error->message);
9697
9698           g_free (msg);
9699           g_free (name);
9700         }
9701
9702       goto out;
9703     }
9704
9705   file = _gtk_file_system_volume_get_root (volume);
9706   if (file != NULL)
9707     {
9708       change_folder_and_display_error (impl, file, FALSE);
9709       g_object_unref (file);
9710     }
9711
9712 out:
9713   g_object_unref (impl);
9714   g_object_unref (cancellable);
9715 }
9716
9717
9718 /* Activates a volume by mounting it if necessary and then switching to its
9719  * base path.
9720  */
9721 static void
9722 shortcuts_activate_volume (GtkFileChooserDefault *impl,
9723                            GtkFileSystemVolume   *volume)
9724 {
9725   GFile *file;
9726
9727   operation_mode_set (impl, OPERATION_MODE_BROWSE);
9728
9729   /* We ref the file chooser since volume_mount() may run a main loop, and the
9730    * user could close the file chooser window in the meantime.
9731    */
9732   g_object_ref (impl);
9733
9734   if (!_gtk_file_system_volume_is_mounted (volume))
9735     {
9736       GMountOperation *mount_op;
9737
9738       set_busy_cursor (impl, TRUE);
9739    
9740       mount_op = gtk_mount_operation_new (get_toplevel (GTK_WIDGET (impl)));
9741       impl->shortcuts_activate_iter_cancellable =
9742         _gtk_file_system_mount_volume (impl->file_system, volume, mount_op,
9743                                        shortcuts_activate_volume_mount_cb,
9744                                        g_object_ref (impl));
9745       g_object_unref (mount_op);
9746     }
9747   else
9748     {
9749       file = _gtk_file_system_volume_get_root (volume);
9750       if (file != NULL)
9751         {
9752           change_folder_and_display_error (impl, file, FALSE);
9753           g_object_unref (file);
9754         }
9755     }
9756
9757   g_object_unref (impl);
9758 }
9759
9760 /* Opens the folder or volume at the specified iter in the shortcuts model */
9761 struct ShortcutsActivateData
9762 {
9763   GtkFileChooserDefault *impl;
9764   GFile *file;
9765 };
9766
9767 static void
9768 shortcuts_activate_get_info_cb (GCancellable *cancellable,
9769                                 GFileInfo    *info,
9770                                 const GError *error,
9771                                 gpointer      user_data)
9772 {
9773   gboolean cancelled = g_cancellable_is_cancelled (cancellable);
9774   struct ShortcutsActivateData *data = user_data;
9775
9776   if (cancellable != data->impl->shortcuts_activate_iter_cancellable)
9777     goto out;
9778
9779   data->impl->shortcuts_activate_iter_cancellable = NULL;
9780
9781   if (cancelled)
9782     goto out;
9783
9784   if (!error && _gtk_file_info_consider_as_directory (info))
9785     change_folder_and_display_error (data->impl, data->file, FALSE);
9786   else
9787     gtk_file_chooser_default_select_file (GTK_FILE_CHOOSER (data->impl),
9788                                           data->file,
9789                                           NULL);
9790
9791 out:
9792   g_object_unref (data->impl);
9793   g_object_unref (data->file);
9794   g_free (data);
9795
9796   g_object_unref (cancellable);
9797 }
9798
9799 static void
9800 shortcuts_activate_mount_enclosing_volume (GCancellable        *cancellable,
9801                                            GtkFileSystemVolume *volume,
9802                                            const GError        *error,
9803                                            gpointer             user_data)
9804 {
9805   struct ShortcutsActivateData *data = user_data;
9806
9807   if (error)
9808     {
9809       error_changing_folder_dialog (data->impl, data->file, g_error_copy (error));
9810
9811       g_object_unref (data->impl);
9812       g_object_unref (data->file);
9813       g_free (data);
9814
9815       return;
9816     }
9817
9818   data->impl->shortcuts_activate_iter_cancellable =
9819     _gtk_file_system_get_info (data->impl->file_system, data->file,
9820                                "standard::type",
9821                                shortcuts_activate_get_info_cb, data);
9822
9823   if (volume)
9824     _gtk_file_system_volume_unref (volume);
9825 }
9826
9827 static void
9828 shortcuts_activate_iter (GtkFileChooserDefault *impl,
9829                          GtkTreeIter           *iter)
9830 {
9831   gpointer col_data;
9832   ShortcutType shortcut_type;
9833
9834   /* In the Save modes, we want to preserve what the uesr typed in the filename
9835    * entry, so that he may choose another folder without erasing his typed name.
9836    */
9837   if (impl->location_entry
9838       && !(impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
9839            || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER))
9840     _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), "");
9841
9842   gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), iter,
9843                       SHORTCUTS_COL_DATA, &col_data,
9844                       SHORTCUTS_COL_TYPE, &shortcut_type,
9845                       -1);
9846
9847   if (impl->shortcuts_activate_iter_cancellable)
9848     {
9849       g_cancellable_cancel (impl->shortcuts_activate_iter_cancellable);
9850       impl->shortcuts_activate_iter_cancellable = NULL;
9851     }
9852
9853   if (shortcut_type == SHORTCUT_TYPE_SEPARATOR)
9854     return;
9855   else if (shortcut_type == SHORTCUT_TYPE_VOLUME)
9856     {
9857       GtkFileSystemVolume *volume;
9858
9859       volume = col_data;
9860
9861       shortcuts_activate_volume (impl, volume);
9862     }
9863   else if (shortcut_type == SHORTCUT_TYPE_FILE)
9864     {
9865       struct ShortcutsActivateData *data;
9866       GtkFileSystemVolume *volume;
9867
9868       volume = _gtk_file_system_get_volume_for_file (impl->file_system, col_data);
9869
9870       data = g_new0 (struct ShortcutsActivateData, 1);
9871       data->impl = g_object_ref (impl);
9872       data->file = g_object_ref (col_data);
9873
9874       if (!volume || !_gtk_file_system_volume_is_mounted (volume))
9875         {
9876           GMountOperation *mount_operation;
9877           GtkWidget *toplevel;
9878
9879           toplevel = gtk_widget_get_toplevel (GTK_WIDGET (impl));
9880
9881           mount_operation = gtk_mount_operation_new (GTK_WINDOW (toplevel));
9882
9883           impl->shortcuts_activate_iter_cancellable =
9884             _gtk_file_system_mount_enclosing_volume (impl->file_system, col_data,
9885                                                      mount_operation,
9886                                                      shortcuts_activate_mount_enclosing_volume,
9887                                                      data);
9888         }
9889       else
9890         {
9891           impl->shortcuts_activate_iter_cancellable =
9892             _gtk_file_system_get_info (impl->file_system, data->file,
9893                                        "standard::type",
9894                                        shortcuts_activate_get_info_cb, data);
9895         }
9896     }
9897   else if (shortcut_type == SHORTCUT_TYPE_SEARCH)
9898     {
9899       operation_mode_set (impl, OPERATION_MODE_SEARCH);
9900     }
9901   else if (shortcut_type == SHORTCUT_TYPE_RECENT)
9902     {
9903       operation_mode_set (impl, OPERATION_MODE_RECENT);
9904     }
9905 }
9906
9907 /* Handler for GtkWidget::key-press-event on the shortcuts list */
9908 static gboolean
9909 shortcuts_key_press_event_cb (GtkWidget             *widget,
9910                               GdkEventKey           *event,
9911                               GtkFileChooserDefault *impl)
9912 {
9913   guint modifiers;
9914
9915   modifiers = gtk_accelerator_get_default_mod_mask ();
9916
9917   if (key_is_left_or_right (event))
9918     {
9919       gtk_widget_grab_focus (impl->browse_files_tree_view);
9920       return TRUE;
9921     }
9922
9923   if ((event->keyval == GDK_KEY_BackSpace
9924       || event->keyval == GDK_KEY_Delete
9925       || event->keyval == GDK_KEY_KP_Delete)
9926       && (event->state & modifiers) == 0)
9927     {
9928       remove_selected_bookmarks (impl);
9929       return TRUE;
9930     }
9931
9932   if ((event->keyval == GDK_KEY_F2)
9933       && (event->state & modifiers) == 0)
9934     {
9935       rename_selected_bookmark (impl);
9936       return TRUE;
9937     }
9938
9939   return FALSE;
9940 }
9941
9942 static gboolean
9943 shortcuts_select_func  (GtkTreeSelection  *selection,
9944                         GtkTreeModel      *model,
9945                         GtkTreePath       *path,
9946                         gboolean           path_currently_selected,
9947                         gpointer           data)
9948 {
9949   GtkFileChooserDefault *impl = data;
9950   GtkTreeIter filter_iter;
9951   ShortcutType shortcut_type;
9952
9953   if (!gtk_tree_model_get_iter (impl->shortcuts_pane_filter_model, &filter_iter, path))
9954     g_assert_not_reached ();
9955
9956   gtk_tree_model_get (impl->shortcuts_pane_filter_model, &filter_iter, SHORTCUTS_COL_TYPE, &shortcut_type, -1);
9957
9958   return shortcut_type != SHORTCUT_TYPE_SEPARATOR;
9959 }
9960
9961 static gboolean
9962 list_select_func  (GtkTreeSelection  *selection,
9963                    GtkTreeModel      *model,
9964                    GtkTreePath       *path,
9965                    gboolean           path_currently_selected,
9966                    gpointer           data)
9967 {
9968   GtkFileChooserDefault *impl = data;
9969
9970   if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ||
9971       impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
9972     {
9973       GtkTreeIter iter;
9974       gboolean is_folder;
9975
9976       if (!gtk_tree_model_get_iter (model, &iter, path))
9977         return FALSE;
9978       gtk_tree_model_get (model, &iter,
9979                           MODEL_COL_IS_FOLDER, &is_folder,
9980                           -1);
9981       if (!is_folder)
9982         return FALSE;
9983     }
9984     
9985   return TRUE;
9986 }
9987
9988 static void
9989 list_selection_changed (GtkTreeSelection      *selection,
9990                         GtkFileChooserDefault *impl)
9991 {
9992   /* See if we are in the new folder editable row for Save mode */
9993   if (impl->operation_mode == OPERATION_MODE_BROWSE &&
9994       impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
9995     {
9996       GFileInfo *info;
9997       gboolean had_selection;
9998
9999       info = get_selected_file_info_from_file_list (impl, &had_selection);
10000       if (!had_selection)
10001         goto out; /* normal processing */
10002
10003       if (!info)
10004         return; /* We are on the editable row for New Folder */
10005     }
10006
10007  out:
10008
10009   if (impl->location_entry)
10010     update_chooser_entry (impl);
10011
10012   path_bar_update (impl);
10013
10014   check_preview_change (impl);
10015   bookmarks_check_add_sensitivity (impl);
10016
10017   g_signal_emit_by_name (impl, "selection-changed", 0);
10018 }
10019
10020 /* Callback used when a row in the file list is activated */
10021 static void
10022 list_row_activated (GtkTreeView           *tree_view,
10023                     GtkTreePath           *path,
10024                     GtkTreeViewColumn     *column,
10025                     GtkFileChooserDefault *impl)
10026 {
10027   GFile *file;
10028   GtkTreeIter iter;
10029   GtkTreeModel *model;
10030   gboolean is_folder;
10031
10032   model = gtk_tree_view_get_model (tree_view);
10033
10034   if (!gtk_tree_model_get_iter (model, &iter, path))
10035     return;
10036
10037   gtk_tree_model_get (model, &iter,
10038                       MODEL_COL_FILE, &file,
10039                       MODEL_COL_IS_FOLDER, &is_folder,
10040                       -1);
10041         
10042   if (is_folder && file)
10043     {
10044       change_folder_and_display_error (impl, file, FALSE);
10045       g_object_unref (file);
10046       goto out;
10047     }
10048
10049   if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
10050       impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
10051     g_signal_emit_by_name (impl, "file-activated");
10052
10053  out:
10054
10055   if (file)
10056     g_object_unref (file);
10057 }
10058
10059 static void
10060 path_bar_clicked (GtkPathBar            *path_bar,
10061                   GFile                 *file,
10062                   GFile                 *child_file,
10063                   gboolean               child_is_hidden,
10064                   GtkFileChooserDefault *impl)
10065 {
10066   if (child_file)
10067     pending_select_files_add (impl, child_file);
10068
10069   if (!change_folder_and_display_error (impl, file, FALSE))
10070     return;
10071
10072   /* Say we have "/foo/bar/[.baz]" and the user clicks on "bar".  We should then
10073    * show hidden files so that ".baz" appears in the file list, as it will still
10074    * be shown in the path bar: "/foo/[bar]/.baz"
10075    */
10076   if (child_is_hidden)
10077     g_object_set (impl, "show-hidden", TRUE, NULL);
10078 }
10079
10080 static void
10081 update_cell_renderer_attributes (GtkFileChooserDefault *impl)
10082 {
10083   GtkTreeViewColumn *column;
10084   GtkCellRenderer *renderer;
10085   GList *walk, *list;
10086   gboolean always_sensitive;
10087
10088   always_sensitive = impl->action != GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER &&
10089                      impl->action != GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER;
10090
10091   /* Keep the following column numbers in sync with create_file_list() */
10092
10093   /* name */
10094   column = gtk_tree_view_get_column (GTK_TREE_VIEW (impl->browse_files_tree_view), 0);
10095   list = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
10096   for (walk = list; walk; walk = walk->next)
10097     {
10098       renderer = walk->data;
10099       if (GTK_IS_CELL_RENDERER_PIXBUF (renderer))
10100         {
10101           gtk_tree_view_column_set_attributes (column, renderer, 
10102                                                "pixbuf", MODEL_COL_PIXBUF,
10103                                                NULL);
10104         }
10105       else
10106         {
10107           gtk_tree_view_column_set_attributes (column, renderer, 
10108                                                "text", MODEL_COL_NAME,
10109                                                "ellipsize", MODEL_COL_ELLIPSIZE,
10110                                                NULL);
10111         }
10112       if (always_sensitive)
10113         g_object_set (renderer, "sensitive", TRUE, NULL);
10114       else
10115         gtk_tree_view_column_add_attribute (column, renderer, "sensitive", MODEL_COL_IS_FOLDER);
10116     }
10117   g_list_free (list);
10118
10119   /* size */
10120   column = gtk_tree_view_get_column (GTK_TREE_VIEW (impl->browse_files_tree_view), 1);
10121   list = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
10122   renderer = list->data;
10123   gtk_tree_view_column_set_attributes (column, renderer, 
10124                                        "text", MODEL_COL_SIZE_TEXT,
10125                                        NULL);
10126   if (always_sensitive)
10127     g_object_set (renderer, "sensitive", TRUE, NULL);
10128   else
10129     gtk_tree_view_column_add_attribute (column, renderer, "sensitive", MODEL_COL_IS_FOLDER);
10130   g_list_free (list);
10131
10132   /* mtime */
10133   column = gtk_tree_view_get_column (GTK_TREE_VIEW (impl->browse_files_tree_view), 2);
10134   list = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
10135   renderer = list->data;
10136   gtk_tree_view_column_set_attributes (column, renderer, 
10137                                        "text", MODEL_COL_MTIME_TEXT,
10138                                        NULL);
10139   if (always_sensitive)
10140     g_object_set (renderer, "sensitive", TRUE, NULL);
10141   else
10142     gtk_tree_view_column_add_attribute (column, renderer, "sensitive", MODEL_COL_IS_FOLDER);
10143   g_list_free (list);
10144 }
10145
10146 GtkWidget *
10147 _gtk_file_chooser_default_new (void)
10148 {
10149   return g_object_new (GTK_TYPE_FILE_CHOOSER_DEFAULT, NULL);
10150 }
10151
10152 static void
10153 location_set_user_text (GtkFileChooserDefault *impl,
10154                         const gchar           *path)
10155 {
10156   _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), path);
10157   gtk_editable_set_position (GTK_EDITABLE (impl->location_entry), -1);
10158 }
10159
10160 static void
10161 location_popup_handler (GtkFileChooserDefault *impl,
10162                         const gchar           *path)
10163
10164   if (impl->operation_mode != OPERATION_MODE_BROWSE)
10165     {
10166       GtkWidget *widget_to_focus;
10167
10168       operation_mode_set (impl, OPERATION_MODE_BROWSE);
10169       
10170       if (impl->current_folder)
10171         change_folder_and_display_error (impl, impl->current_folder, FALSE);
10172
10173       if (impl->location_mode == LOCATION_MODE_PATH_BAR)
10174         widget_to_focus = impl->browse_files_tree_view;
10175       else
10176         widget_to_focus = impl->location_entry;
10177
10178       gtk_widget_grab_focus (widget_to_focus);
10179       return; 
10180     }
10181   
10182   if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
10183       impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
10184     {
10185       LocationMode new_mode;
10186
10187       if (path != NULL)
10188         {
10189           /* since the user typed something, we unconditionally want to turn on the entry */
10190           new_mode = LOCATION_MODE_FILENAME_ENTRY;
10191         }
10192       else if (impl->location_mode == LOCATION_MODE_PATH_BAR)
10193         new_mode = LOCATION_MODE_FILENAME_ENTRY;
10194       else if (impl->location_mode == LOCATION_MODE_FILENAME_ENTRY)
10195         new_mode = LOCATION_MODE_PATH_BAR;
10196       else
10197         {
10198           g_assert_not_reached ();
10199           return;
10200         }
10201
10202       location_mode_set (impl, new_mode, TRUE);
10203       if (new_mode == LOCATION_MODE_FILENAME_ENTRY)
10204         {
10205           if (path != NULL)
10206             location_set_user_text (impl, path);
10207           else
10208             {
10209               location_entry_set_initial_text (impl);
10210               gtk_editable_select_region (GTK_EDITABLE (impl->location_entry), 0, -1);
10211             }
10212         }
10213     }
10214   else if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE ||
10215            impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
10216     {
10217       gtk_widget_grab_focus (impl->location_entry);
10218       if (path != NULL)
10219         location_set_user_text (impl, path);
10220     }
10221   else
10222     g_assert_not_reached ();
10223 }
10224
10225 /* Handler for the "up-folder" keybinding signal */
10226 static void
10227 up_folder_handler (GtkFileChooserDefault *impl)
10228 {
10229   _gtk_path_bar_up (GTK_PATH_BAR (impl->browse_path_bar));
10230 }
10231
10232 /* Handler for the "down-folder" keybinding signal */
10233 static void
10234 down_folder_handler (GtkFileChooserDefault *impl)
10235 {
10236   _gtk_path_bar_down (GTK_PATH_BAR (impl->browse_path_bar));
10237 }
10238
10239 /* Switches to the shortcut in the specified index */
10240 static void
10241 switch_to_shortcut (GtkFileChooserDefault *impl,
10242                     int pos)
10243 {
10244   GtkTreeIter iter;
10245
10246   if (!gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (impl->shortcuts_model), &iter, NULL, pos))
10247     g_assert_not_reached ();
10248
10249   shortcuts_activate_iter (impl, &iter);
10250 }
10251
10252 /* Handler for the "home-folder" keybinding signal */
10253 static void
10254 home_folder_handler (GtkFileChooserDefault *impl)
10255 {
10256   if (impl->has_home)
10257     switch_to_shortcut (impl, shortcuts_get_index (impl, SHORTCUTS_HOME));
10258 }
10259
10260 /* Handler for the "desktop-folder" keybinding signal */
10261 static void
10262 desktop_folder_handler (GtkFileChooserDefault *impl)
10263 {
10264   if (impl->has_desktop)
10265     switch_to_shortcut (impl, shortcuts_get_index (impl, SHORTCUTS_DESKTOP));
10266 }
10267
10268 /* Handler for the "search-shortcut" keybinding signal */
10269 static void
10270 search_shortcut_handler (GtkFileChooserDefault *impl)
10271 {
10272   if (impl->has_search)
10273     {
10274       switch_to_shortcut (impl, shortcuts_get_index (impl, SHORTCUTS_SEARCH));
10275
10276       /* we want the entry widget to grab the focus the first
10277        * time, not the browse_files_tree_view widget.
10278        */
10279       if (impl->search_entry)
10280         gtk_widget_grab_focus (impl->search_entry);
10281     }
10282 }
10283
10284 /* Handler for the "recent-shortcut" keybinding signal */
10285 static void
10286 recent_shortcut_handler (GtkFileChooserDefault *impl)
10287 {
10288   switch_to_shortcut (impl, shortcuts_get_index (impl, SHORTCUTS_RECENT));
10289 }
10290
10291 static void
10292 quick_bookmark_handler (GtkFileChooserDefault *impl,
10293                         gint bookmark_index)
10294 {
10295   int bookmark_pos;
10296   GtkTreePath *path;
10297
10298   if (bookmark_index < 0 || bookmark_index >= impl->num_bookmarks)
10299     return;
10300
10301   bookmark_pos = shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS) + bookmark_index;
10302
10303   path = gtk_tree_path_new_from_indices (bookmark_pos, -1);
10304   gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view),
10305                                 path, NULL,
10306                                 FALSE, 0.0, 0.0);
10307   gtk_tree_path_free (path);
10308
10309   switch_to_shortcut (impl, bookmark_pos);
10310 }
10311
10312 static void
10313 show_hidden_handler (GtkFileChooserDefault *impl)
10314 {
10315   g_object_set (impl,
10316                 "show-hidden", !impl->show_hidden,
10317                 NULL);
10318 }
10319
10320
10321 /* Drag and drop interfaces */
10322
10323 static void
10324 _shortcuts_pane_model_filter_class_init (ShortcutsPaneModelFilterClass *class)
10325 {
10326 }
10327
10328 static void
10329 _shortcuts_pane_model_filter_init (ShortcutsPaneModelFilter *model)
10330 {
10331   model->impl = NULL;
10332 }
10333
10334 /* GtkTreeDragSource::row_draggable implementation for the shortcuts filter model */
10335 static gboolean
10336 shortcuts_pane_model_filter_row_draggable (GtkTreeDragSource *drag_source,
10337                                            GtkTreePath       *path)
10338 {
10339   ShortcutsPaneModelFilter *model;
10340   int pos;
10341   int bookmarks_pos;
10342
10343   model = SHORTCUTS_PANE_MODEL_FILTER (drag_source);
10344
10345   pos = *gtk_tree_path_get_indices (path);
10346   bookmarks_pos = shortcuts_get_index (model->impl, SHORTCUTS_BOOKMARKS);
10347
10348   return (pos >= bookmarks_pos && pos < bookmarks_pos + model->impl->num_bookmarks);
10349 }
10350
10351 /* GtkTreeDragSource::drag_data_get implementation for the shortcuts
10352  * filter model
10353  */
10354 static gboolean
10355 shortcuts_pane_model_filter_drag_data_get (GtkTreeDragSource *drag_source,
10356                                            GtkTreePath       *path,
10357                                            GtkSelectionData  *selection_data)
10358 {
10359   /* FIXME */
10360
10361   return FALSE;
10362 }
10363
10364 /* Fill the GtkTreeDragSourceIface vtable */
10365 static void
10366 shortcuts_pane_model_filter_drag_source_iface_init (GtkTreeDragSourceIface *iface)
10367 {
10368   iface->row_draggable = shortcuts_pane_model_filter_row_draggable;
10369   iface->drag_data_get = shortcuts_pane_model_filter_drag_data_get;
10370 }
10371
10372 #if 0
10373 /* Fill the GtkTreeDragDestIface vtable */
10374 static void
10375 shortcuts_pane_model_filter_drag_dest_iface_init (GtkTreeDragDestIface *iface)
10376 {
10377   iface->drag_data_received = shortcuts_pane_model_filter_drag_data_received;
10378   iface->row_drop_possible = shortcuts_pane_model_filter_row_drop_possible;
10379 }
10380 #endif
10381
10382 static GtkTreeModel *
10383 shortcuts_pane_model_filter_new (GtkFileChooserDefault *impl,
10384                                  GtkTreeModel          *child_model,
10385                                  GtkTreePath           *root)
10386 {
10387   ShortcutsPaneModelFilter *model;
10388
10389   model = g_object_new (SHORTCUTS_PANE_MODEL_FILTER_TYPE,
10390                         "child-model", child_model,
10391                         "virtual-root", root,
10392                         NULL);
10393
10394   model->impl = impl;
10395
10396   return GTK_TREE_MODEL (model);
10397 }
10398