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