]> Pileus Git - ~andy/gtk/blob - gtk/gtkfilechooserbutton.c
stylecontext: Do invalidation on first resize container
[~andy/gtk] / gtk / gtkfilechooserbutton.c
1 /* -*- Mode: C; c-file-style: "gnu"; tab-width: 8 -*- */
2
3 /* GTK+: gtkfilechooserbutton.c
4  *
5  * Copyright (c) 2004 James M. Cape <jcape@ignore-your.tv>
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public
18  * License along with this library. If not, see <http://www.gnu.org/licenses/>.
19  */
20
21 #include "config.h"
22
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 #ifdef HAVE_UNISTD_H
26 #include <unistd.h>
27 #endif
28
29 #include <string.h>
30
31 #include "gtkintl.h"
32 #include "gtkbutton.h"
33 #include "gtkcelllayout.h"
34 #include "gtkcellrenderertext.h"
35 #include "gtkcellrendererpixbuf.h"
36 #include "gtkcombobox.h"
37 #include "gtkdnd.h"
38 #include "gtkicontheme.h"
39 #include "gtkiconfactory.h"
40 #include "gtkimage.h"
41 #include "gtklabel.h"
42 #include "gtkliststore.h"
43 #include "gtkstock.h"
44 #include "gtktreemodelfilter.h"
45 #include "gtkseparator.h"
46 #include "gtkfilechooserdialog.h"
47 #include "gtkfilechooserprivate.h"
48 #include "gtkfilechooserutils.h"
49 #include "gtkmarshalers.h"
50
51 #include "gtkfilechooserbutton.h"
52
53 #include "gtkorientable.h"
54
55 #include "gtktypebuiltins.h"
56 #include "gtkprivate.h"
57 #include "gtksettings.h"
58
59
60 /**
61  * SECTION:gtkfilechooserbutton
62  * @Short_description: A button to launch a file selection dialog
63  * @Title: GtkFileChooserButton
64  * @See_also:#GtkFileChooserDialog
65  *
66  * The #GtkFileChooserButton is a widget that lets the user select a
67  * file.  It implements the #GtkFileChooser interface.  Visually, it is a
68  * file name with a button to bring up a #GtkFileChooserDialog.
69  * The user can then use that dialog to change the file associated with
70  * that button.  This widget does not support setting the
71  * #GtkFileChooser:select-multiple property to %TRUE.
72  *
73  * <example>
74  * <title>Create a button to let the user select a file in /etc</title>
75  * <programlisting>
76  * {
77  *   GtkWidget *button;
78  *
79  *   button = gtk_file_chooser_button_new (_("Select a file"),
80  *                                         GTK_FILE_CHOOSER_ACTION_OPEN);
81  *   gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (button),
82  *                                        "/etc");
83  * }
84  * </programlisting>
85  * </example>
86  *
87  * The #GtkFileChooserButton supports the #GtkFileChooserAction<!-- -->s
88  * %GTK_FILE_CHOOSER_ACTION_OPEN and %GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER.
89  *
90  * <important>
91  * The #GtkFileChooserButton will ellipsize the label,
92  * and thus will thus request little horizontal space.  To give the button
93  * more space, you should call gtk_widget_get_preferred_size(),
94  * gtk_file_chooser_button_set_width_chars(), or pack the button in
95  * such a way that other interface elements give space to the widget.
96  * </important>
97  */
98
99
100 /* **************** *
101  *  Private Macros  *
102  * **************** */
103
104 #define DEFAULT_TITLE           N_("Select a File")
105 #define DESKTOP_DISPLAY_NAME    N_("Desktop")
106 #define FALLBACK_DISPLAY_NAME   N_("(None)") /* this string is used in gtk+/gtk/tests/filechooser.c - change it there if you change it here */
107 #define FALLBACK_ICON_NAME      "stock_unknown"
108 #define FALLBACK_ICON_SIZE      16
109
110
111 /* ********************** *
112  *  Private Enumerations  *
113  * ********************** */
114
115 /* Property IDs */
116 enum
117 {
118   PROP_0,
119
120   PROP_DIALOG,
121   PROP_FOCUS_ON_CLICK,
122   PROP_TITLE,
123   PROP_WIDTH_CHARS
124 };
125
126 /* Signals */
127 enum
128 {
129   FILE_SET,
130   LAST_SIGNAL
131 };
132
133 /* TreeModel Columns */
134 enum
135 {
136   ICON_COLUMN,
137   DISPLAY_NAME_COLUMN,
138   TYPE_COLUMN,
139   DATA_COLUMN,
140   IS_FOLDER_COLUMN,
141   CANCELLABLE_COLUMN,
142   NUM_COLUMNS
143 };
144
145 /* TreeModel Row Types */
146 typedef enum
147 {
148   ROW_TYPE_SPECIAL,
149   ROW_TYPE_VOLUME,
150   ROW_TYPE_SHORTCUT,
151   ROW_TYPE_BOOKMARK_SEPARATOR,
152   ROW_TYPE_BOOKMARK,
153   ROW_TYPE_CURRENT_FOLDER_SEPARATOR,
154   ROW_TYPE_CURRENT_FOLDER,
155   ROW_TYPE_OTHER_SEPARATOR,
156   ROW_TYPE_OTHER,
157   ROW_TYPE_EMPTY_SELECTION,
158
159   ROW_TYPE_INVALID = -1
160 }
161 RowType;
162
163
164 /* ******************** *
165  *  Private Structures  *
166  * ******************** */
167
168 struct _GtkFileChooserButtonPrivate
169 {
170   GtkWidget *dialog;
171   GtkWidget *button;
172   GtkWidget *image;
173   GtkWidget *label;
174   GtkWidget *combo_box;
175   GtkCellRenderer *icon_cell;
176   GtkCellRenderer *name_cell;
177
178   GtkTreeModel *model;
179   GtkTreeModel *filter_model;
180
181   GtkFileSystem *fs;
182   GFile *selection_while_inactive;
183   GFile *current_folder_while_inactive;
184
185   gulong combo_box_changed_id;
186   gulong fs_volumes_changed_id;
187   gulong fs_bookmarks_changed_id;
188
189   GCancellable *dnd_select_folder_cancellable;
190   GCancellable *update_button_cancellable;
191   GSList *change_icon_theme_cancellables;
192
193   gint icon_size;
194
195   guint8 n_special;
196   guint8 n_volumes;
197   guint8 n_shortcuts;
198   guint8 n_bookmarks;
199   guint  has_bookmark_separator       : 1;
200   guint  has_current_folder_separator : 1;
201   guint  has_current_folder           : 1;
202   guint  has_other_separator          : 1;
203
204   /* Used for hiding/showing the dialog when the button is hidden */
205   guint  active                       : 1;
206
207   guint  focus_on_click               : 1;
208
209   /* Whether the next async callback from GIO should emit the "selection-changed" signal */
210   guint  is_changing_selection        : 1;
211 };
212
213
214 /* ************* *
215  *  DnD Support  *
216  * ************* */
217
218 enum
219 {
220   TEXT_PLAIN,
221   TEXT_URI_LIST
222 };
223
224
225 /* ********************* *
226  *  Function Prototypes  *
227  * ********************* */
228
229 /* GtkFileChooserIface Functions */
230 static void     gtk_file_chooser_button_file_chooser_iface_init (GtkFileChooserIface *iface);
231 static gboolean gtk_file_chooser_button_set_current_folder (GtkFileChooser    *chooser,
232                                                             GFile             *file,
233                                                             GError           **error);
234 static GFile *gtk_file_chooser_button_get_current_folder (GtkFileChooser    *chooser);
235 static gboolean gtk_file_chooser_button_select_file (GtkFileChooser *chooser,
236                                                      GFile          *file,
237                                                      GError        **error);
238 static void gtk_file_chooser_button_unselect_file (GtkFileChooser *chooser,
239                                                    GFile          *file);
240 static void gtk_file_chooser_button_unselect_all (GtkFileChooser *chooser);
241 static GSList *gtk_file_chooser_button_get_files (GtkFileChooser *chooser);
242 static gboolean gtk_file_chooser_button_add_shortcut_folder     (GtkFileChooser      *chooser,
243                                                                  GFile               *file,
244                                                                  GError             **error);
245 static gboolean gtk_file_chooser_button_remove_shortcut_folder  (GtkFileChooser      *chooser,
246                                                                  GFile               *file,
247                                                                  GError             **error);
248
249 /* GObject Functions */
250 static GObject *gtk_file_chooser_button_constructor        (GType             type,
251                                                             guint             n_params,
252                                                             GObjectConstructParam *params);
253 static void     gtk_file_chooser_button_set_property       (GObject          *object,
254                                                             guint             param_id,
255                                                             const GValue     *value,
256                                                             GParamSpec       *pspec);
257 static void     gtk_file_chooser_button_get_property       (GObject          *object,
258                                                             guint             param_id,
259                                                             GValue           *value,
260                                                             GParamSpec       *pspec);
261 static void     gtk_file_chooser_button_finalize           (GObject          *object);
262
263 /* GtkWidget Functions */
264 static void     gtk_file_chooser_button_destroy            (GtkWidget        *widget);
265 static void     gtk_file_chooser_button_drag_data_received (GtkWidget        *widget,
266                                                             GdkDragContext   *context,
267                                                             gint              x,
268                                                             gint              y,
269                                                             GtkSelectionData *data,
270                                                             guint             type,
271                                                             guint             drag_time);
272 static void     gtk_file_chooser_button_show_all           (GtkWidget        *widget);
273 static void     gtk_file_chooser_button_show               (GtkWidget        *widget);
274 static void     gtk_file_chooser_button_hide               (GtkWidget        *widget);
275 static void     gtk_file_chooser_button_map                (GtkWidget        *widget);
276 static gboolean gtk_file_chooser_button_mnemonic_activate  (GtkWidget        *widget,
277                                                             gboolean          group_cycling);
278 static void     gtk_file_chooser_button_style_updated      (GtkWidget        *widget);
279 static void     gtk_file_chooser_button_screen_changed     (GtkWidget        *widget,
280                                                             GdkScreen        *old_screen);
281
282 /* Utility Functions */
283 static GtkIconTheme *get_icon_theme               (GtkWidget            *widget);
284 static void          set_info_for_file_at_iter         (GtkFileChooserButton *fs,
285                                                         GFile                *file,
286                                                         GtkTreeIter          *iter);
287
288 static gint          model_get_type_position      (GtkFileChooserButton *button,
289                                                    RowType               row_type);
290 static void          model_free_row_data          (GtkFileChooserButton *button,
291                                                    GtkTreeIter          *iter);
292 static void          model_add_special            (GtkFileChooserButton *button);
293 static void          model_add_other              (GtkFileChooserButton *button);
294 static void          model_add_empty_selection    (GtkFileChooserButton *button);
295 static void          model_add_volumes            (GtkFileChooserButton *button,
296                                                    GSList               *volumes);
297 static void          model_add_bookmarks          (GtkFileChooserButton *button,
298                                                    GSList               *bookmarks);
299 static void          model_update_current_folder  (GtkFileChooserButton *button,
300                                                    GFile                *file);
301 static void          model_remove_rows            (GtkFileChooserButton *button,
302                                                    gint                  pos,
303                                                    gint                  n_rows);
304
305 static gboolean      filter_model_visible_func    (GtkTreeModel         *model,
306                                                    GtkTreeIter          *iter,
307                                                    gpointer              user_data);
308
309 static gboolean      combo_box_row_separator_func (GtkTreeModel         *model,
310                                                    GtkTreeIter          *iter,
311                                                    gpointer              user_data);
312 static void          name_cell_data_func          (GtkCellLayout        *layout,
313                                                    GtkCellRenderer      *cell,
314                                                    GtkTreeModel         *model,
315                                                    GtkTreeIter          *iter,
316                                                    gpointer              user_data);
317 static void          open_dialog                  (GtkFileChooserButton *button);
318 static void          update_combo_box             (GtkFileChooserButton *button);
319 static void          update_label_and_image       (GtkFileChooserButton *button);
320
321 /* Child Object Callbacks */
322 static void     fs_volumes_changed_cb            (GtkFileSystem  *fs,
323                                                   gpointer        user_data);
324 static void     fs_bookmarks_changed_cb          (GtkFileSystem  *fs,
325                                                   gpointer        user_data);
326
327 static void     combo_box_changed_cb             (GtkComboBox    *combo_box,
328                                                   gpointer        user_data);
329 static void     combo_box_notify_popup_shown_cb  (GObject        *object,
330                                                   GParamSpec     *pspec,
331                                                   gpointer        user_data);
332
333 static void     button_clicked_cb                (GtkButton      *real_button,
334                                                   gpointer        user_data);
335
336 static void     dialog_update_preview_cb         (GtkFileChooser *dialog,
337                                                   gpointer        user_data);
338 static void     dialog_notify_cb                 (GObject        *dialog,
339                                                   GParamSpec     *pspec,
340                                                   gpointer        user_data);
341 static gboolean dialog_delete_event_cb           (GtkWidget      *dialog,
342                                                   GdkEvent       *event,
343                                                   gpointer        user_data);
344 static void     dialog_response_cb               (GtkDialog      *dialog,
345                                                   gint            response,
346                                                   gpointer        user_data);
347
348 static guint file_chooser_button_signals[LAST_SIGNAL] = { 0 };
349
350 /* ******************* *
351  *  GType Declaration  *
352  * ******************* */
353
354 G_DEFINE_TYPE_WITH_CODE (GtkFileChooserButton, gtk_file_chooser_button, GTK_TYPE_BOX, { \
355     G_IMPLEMENT_INTERFACE (GTK_TYPE_FILE_CHOOSER, gtk_file_chooser_button_file_chooser_iface_init) \
356 })
357
358
359 /* ***************** *
360  *  GType Functions  *
361  * ***************** */
362
363 static void
364 gtk_file_chooser_button_class_init (GtkFileChooserButtonClass * class)
365 {
366   GObjectClass *gobject_class;
367   GtkWidgetClass *widget_class;
368
369   gobject_class = G_OBJECT_CLASS (class);
370   widget_class = GTK_WIDGET_CLASS (class);
371
372   gobject_class->constructor = gtk_file_chooser_button_constructor;
373   gobject_class->set_property = gtk_file_chooser_button_set_property;
374   gobject_class->get_property = gtk_file_chooser_button_get_property;
375   gobject_class->finalize = gtk_file_chooser_button_finalize;
376
377   widget_class->destroy = gtk_file_chooser_button_destroy;
378   widget_class->drag_data_received = gtk_file_chooser_button_drag_data_received;
379   widget_class->show_all = gtk_file_chooser_button_show_all;
380   widget_class->show = gtk_file_chooser_button_show;
381   widget_class->hide = gtk_file_chooser_button_hide;
382   widget_class->map = gtk_file_chooser_button_map;
383   widget_class->style_updated = gtk_file_chooser_button_style_updated;
384   widget_class->screen_changed = gtk_file_chooser_button_screen_changed;
385   widget_class->mnemonic_activate = gtk_file_chooser_button_mnemonic_activate;
386
387   /**
388    * GtkFileChooserButton::file-set:
389    * @widget: the object which received the signal.
390    *
391    * The ::file-set signal is emitted when the user selects a file.
392    *
393    * Note that this signal is only emitted when the <emphasis>user</emphasis>
394    * changes the file.
395    *
396    * Since: 2.12
397    */
398   file_chooser_button_signals[FILE_SET] =
399     g_signal_new (I_("file-set"),
400                   G_TYPE_FROM_CLASS (gobject_class),
401                   G_SIGNAL_RUN_FIRST,
402                   G_STRUCT_OFFSET (GtkFileChooserButtonClass, file_set),
403                   NULL, NULL,
404                   _gtk_marshal_VOID__VOID,
405                   G_TYPE_NONE, 0);
406
407   /**
408    * GtkFileChooserButton:dialog:
409    *
410    * Instance of the #GtkFileChooserDialog associated with the button.
411    *
412    * Since: 2.6
413    */
414   g_object_class_install_property (gobject_class, PROP_DIALOG,
415                                    g_param_spec_object ("dialog",
416                                                         P_("Dialog"),
417                                                         P_("The file chooser dialog to use."),
418                                                         GTK_TYPE_FILE_CHOOSER,
419                                                         (GTK_PARAM_WRITABLE |
420                                                          G_PARAM_CONSTRUCT_ONLY)));
421
422   /**
423    * GtkFileChooserButton:focus-on-click:
424    *
425    * Whether the #GtkFileChooserButton button grabs focus when it is clicked
426    * with the mouse.
427    *
428    * Since: 2.10
429    */
430   g_object_class_install_property (gobject_class,
431                                    PROP_FOCUS_ON_CLICK,
432                                    g_param_spec_boolean ("focus-on-click",
433                                                          P_("Focus on click"),
434                                                          P_("Whether the button grabs focus when it is clicked with the mouse"),
435                                                          TRUE,
436                                                          GTK_PARAM_READWRITE));
437
438   /**
439    * GtkFileChooserButton:title:
440    *
441    * Title to put on the #GtkFileChooserDialog associated with the button.
442    *
443    * Since: 2.6
444    */
445   g_object_class_install_property (gobject_class, PROP_TITLE,
446                                    g_param_spec_string ("title",
447                                                         P_("Title"),
448                                                         P_("The title of the file chooser dialog."),
449                                                         _(DEFAULT_TITLE),
450                                                         GTK_PARAM_READWRITE));
451
452   /**
453    * GtkFileChooserButton:width-chars:
454    *
455    * The width of the entry and label inside the button, in characters.
456    *
457    * Since: 2.6
458    */
459   g_object_class_install_property (gobject_class, PROP_WIDTH_CHARS,
460                                    g_param_spec_int ("width-chars",
461                                                      P_("Width In Characters"),
462                                                      P_("The desired width of the button widget, in characters."),
463                                                      -1, G_MAXINT, -1,
464                                                      GTK_PARAM_READWRITE));
465
466   _gtk_file_chooser_install_properties (gobject_class);
467
468   g_type_class_add_private (class, sizeof (GtkFileChooserButtonPrivate));
469 }
470
471 static void
472 gtk_file_chooser_button_init (GtkFileChooserButton *button)
473 {
474   GtkFileChooserButtonPrivate *priv;
475   GtkWidget *box, *image, *sep;
476   GtkTargetList *target_list;
477
478   priv = button->priv = G_TYPE_INSTANCE_GET_PRIVATE (button,
479                                                      GTK_TYPE_FILE_CHOOSER_BUTTON,
480                                                      GtkFileChooserButtonPrivate);
481
482   priv->icon_size = FALLBACK_ICON_SIZE;
483   priv->focus_on_click = TRUE;
484
485   gtk_widget_push_composite_child ();
486
487   /* Button */
488   priv->button = gtk_button_new ();
489   g_signal_connect (priv->button, "clicked",
490                     G_CALLBACK (button_clicked_cb), button);
491   gtk_box_pack_start (GTK_BOX (button), priv->button, TRUE, TRUE, 0);
492   gtk_widget_set_halign (priv->button, GTK_ALIGN_FILL);
493   gtk_widget_show (priv->button);
494
495   box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
496   gtk_container_add (GTK_CONTAINER (priv->button), box);
497   gtk_widget_show (box);
498
499   priv->image = gtk_image_new ();
500   gtk_box_pack_start (GTK_BOX (box), priv->image, FALSE, FALSE, 0);
501   gtk_widget_show (priv->image);
502
503   priv->label = gtk_label_new (_(FALLBACK_DISPLAY_NAME));
504   gtk_label_set_ellipsize (GTK_LABEL (priv->label), PANGO_ELLIPSIZE_END);
505   gtk_widget_set_halign (priv->label, GTK_ALIGN_START);
506   gtk_widget_set_valign (priv->label, GTK_ALIGN_CENTER);
507   gtk_box_pack_start (GTK_BOX (box), priv->label, TRUE, TRUE, 0);
508   //gtk_container_add (GTK_CONTAINER (box), priv->label);
509   gtk_widget_show (priv->label);
510
511   sep = gtk_separator_new (GTK_ORIENTATION_VERTICAL);
512   gtk_box_pack_start (GTK_BOX (box), sep, FALSE, FALSE, 0);
513   gtk_widget_show (sep);
514
515   image = gtk_image_new_from_stock (GTK_STOCK_OPEN, GTK_ICON_SIZE_MENU);
516   gtk_box_pack_start (GTK_BOX (box), image, FALSE, FALSE, 0);
517   gtk_widget_show (image);
518
519   /* Combo Box */
520   /* Keep in sync with columns enum, line 88 */
521   priv->model =
522     GTK_TREE_MODEL (gtk_list_store_new (NUM_COLUMNS,
523                                         GDK_TYPE_PIXBUF, /* ICON_COLUMN */
524                                         G_TYPE_STRING,   /* DISPLAY_NAME_COLUMN */
525                                         G_TYPE_CHAR,     /* TYPE_COLUMN */
526                                         G_TYPE_POINTER   /* DATA_COLUMN (Volume || Path) */,
527                                         G_TYPE_BOOLEAN   /* IS_FOLDER_COLUMN */,
528                                         G_TYPE_POINTER   /* CANCELLABLE_COLUMN */));
529
530   priv->combo_box = gtk_combo_box_new ();
531   priv->combo_box_changed_id = g_signal_connect (priv->combo_box, "changed",
532                                                  G_CALLBACK (combo_box_changed_cb), button);
533
534   g_signal_connect (priv->combo_box, "notify::popup-shown",
535                     G_CALLBACK (combo_box_notify_popup_shown_cb), button);
536
537   gtk_box_pack_start (GTK_BOX (button), priv->combo_box, TRUE, TRUE, 0);
538   gtk_widget_set_halign (priv->combo_box, GTK_ALIGN_FILL);
539
540   priv->icon_cell = gtk_cell_renderer_pixbuf_new ();
541   gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (priv->combo_box),
542                               priv->icon_cell, FALSE);
543   gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (priv->combo_box),
544                                  priv->icon_cell, "pixbuf", ICON_COLUMN);
545
546   priv->name_cell = gtk_cell_renderer_text_new ();
547   gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (priv->combo_box),
548                               priv->name_cell, TRUE);
549   gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (priv->combo_box),
550                                  priv->name_cell, "text", DISPLAY_NAME_COLUMN);
551   gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (priv->combo_box),
552                                       priv->name_cell, name_cell_data_func,
553                                       NULL, NULL);
554
555   gtk_widget_pop_composite_child ();
556
557   /* DnD */
558   gtk_drag_dest_set (GTK_WIDGET (button),
559                      (GTK_DEST_DEFAULT_ALL),
560                      NULL, 0,
561                      GDK_ACTION_COPY);
562   target_list = gtk_target_list_new (NULL, 0);
563   gtk_target_list_add_uri_targets (target_list, TEXT_URI_LIST);
564   gtk_target_list_add_text_targets (target_list, TEXT_PLAIN);
565   gtk_drag_dest_set_target_list (GTK_WIDGET (button), target_list);
566   gtk_target_list_unref (target_list);
567 }
568
569
570 /* ******************************* *
571  *  GtkFileChooserIface Functions  *
572  * ******************************* */
573 static void
574 gtk_file_chooser_button_file_chooser_iface_init (GtkFileChooserIface *iface)
575 {
576   _gtk_file_chooser_delegate_iface_init (iface);
577
578   iface->set_current_folder = gtk_file_chooser_button_set_current_folder;
579   iface->get_current_folder = gtk_file_chooser_button_get_current_folder;
580   iface->select_file = gtk_file_chooser_button_select_file;
581   iface->unselect_file = gtk_file_chooser_button_unselect_file;
582   iface->unselect_all = gtk_file_chooser_button_unselect_all;
583   iface->get_files = gtk_file_chooser_button_get_files;
584   iface->add_shortcut_folder = gtk_file_chooser_button_add_shortcut_folder;
585   iface->remove_shortcut_folder = gtk_file_chooser_button_remove_shortcut_folder;
586 }
587
588 static void
589 emit_selection_changed_if_changing_selection (GtkFileChooserButton *button)
590 {
591   GtkFileChooserButtonPrivate *priv = button->priv;
592
593   if (priv->is_changing_selection)
594     {
595       priv->is_changing_selection = FALSE;
596       g_signal_emit_by_name (button, "selection-changed");
597     }
598 }
599
600 static gboolean
601 gtk_file_chooser_button_set_current_folder (GtkFileChooser    *chooser,
602                                             GFile             *file,
603                                             GError           **error)
604 {
605   GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (chooser);
606   GtkFileChooserButtonPrivate *priv = button->priv;
607
608   if (priv->current_folder_while_inactive)
609     g_object_unref (priv->current_folder_while_inactive);
610
611   priv->current_folder_while_inactive = g_object_ref (file);
612
613   update_combo_box (button);
614
615   g_signal_emit_by_name (button, "current-folder-changed");
616
617   if (priv->active)
618     gtk_file_chooser_set_current_folder_file (GTK_FILE_CHOOSER (priv->dialog), file, NULL);
619
620   return TRUE;
621 }
622
623 static GFile *
624 gtk_file_chooser_button_get_current_folder (GtkFileChooser *chooser)
625 {
626   GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (chooser);
627   GtkFileChooserButtonPrivate *priv = button->priv;
628
629   if (priv->current_folder_while_inactive)
630     return g_object_ref (priv->current_folder_while_inactive);
631   else
632     return NULL;
633 }
634
635 static gboolean
636 gtk_file_chooser_button_select_file (GtkFileChooser *chooser,
637                                      GFile          *file,
638                                      GError        **error)
639 {
640   GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (chooser);
641   GtkFileChooserButtonPrivate *priv = button->priv;
642
643   if (priv->selection_while_inactive)
644     g_object_unref (priv->selection_while_inactive);
645
646   priv->selection_while_inactive = g_object_ref (file);
647
648   priv->is_changing_selection = TRUE;
649
650   update_label_and_image (button);
651   update_combo_box (button);
652
653   if (priv->active)
654     gtk_file_chooser_select_file (GTK_FILE_CHOOSER (priv->dialog), file, NULL);
655
656   return TRUE;
657 }
658
659 static void
660 unselect_current_file (GtkFileChooserButton *button)
661 {
662   GtkFileChooserButtonPrivate *priv = button->priv;
663
664   if (priv->selection_while_inactive)
665     {
666       g_object_unref (priv->selection_while_inactive);
667       priv->selection_while_inactive = NULL;
668     }
669
670   priv->is_changing_selection = TRUE;
671
672   update_label_and_image (button);
673   update_combo_box (button);
674 }
675
676 static void
677 gtk_file_chooser_button_unselect_file (GtkFileChooser *chooser,
678                                        GFile          *file)
679 {
680   GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (chooser);
681   GtkFileChooserButtonPrivate *priv = button->priv;
682
683   if (g_file_equal (priv->selection_while_inactive, file))
684     unselect_current_file (button);
685
686   if (priv->active)
687     gtk_file_chooser_unselect_file (GTK_FILE_CHOOSER (priv->dialog), file);
688 }
689
690 static void
691 gtk_file_chooser_button_unselect_all (GtkFileChooser *chooser)
692 {
693   GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (chooser);
694   GtkFileChooserButtonPrivate *priv = button->priv;
695
696   unselect_current_file (button);
697
698   if (priv->active)
699     gtk_file_chooser_unselect_all (GTK_FILE_CHOOSER (priv->dialog));
700 }
701
702 static GFile *
703 get_selected_file (GtkFileChooserButton *button)
704 {
705   GtkFileChooserButtonPrivate *priv = button->priv;
706   GFile *retval;
707
708   retval = NULL;
709
710   if (priv->selection_while_inactive)
711     retval = priv->selection_while_inactive;
712   else if (gtk_file_chooser_get_action (GTK_FILE_CHOOSER (priv->dialog)) == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
713     {
714       /* If there is no "real" selection in SELECT_FOLDER mode, then we'll just return
715        * the current folder, since that is what GtkFileChooserDefault would do.
716        */
717       if (priv->current_folder_while_inactive)
718         retval = priv->current_folder_while_inactive;
719     }
720
721   if (retval)
722     return g_object_ref (retval);
723   else
724     return NULL;
725 }
726
727 static GSList *
728 gtk_file_chooser_button_get_files (GtkFileChooser *chooser)
729 {
730   GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (chooser);
731   GFile *file;
732
733   file = get_selected_file (button);
734   if (file)
735     return g_slist_prepend (NULL, file);
736   else
737     return NULL;
738 }
739
740 static gboolean
741 gtk_file_chooser_button_add_shortcut_folder (GtkFileChooser  *chooser,
742                                              GFile           *file,
743                                              GError         **error)
744 {
745   GtkFileChooser *delegate;
746   gboolean retval;
747
748   delegate = g_object_get_qdata (G_OBJECT (chooser),
749                                  GTK_FILE_CHOOSER_DELEGATE_QUARK);
750   retval = _gtk_file_chooser_add_shortcut_folder (delegate, file, error);
751
752   if (retval)
753     {
754       GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (chooser);
755       GtkFileChooserButtonPrivate *priv = button->priv;
756       GtkTreeIter iter;
757       gint pos;
758
759       pos = model_get_type_position (button, ROW_TYPE_SHORTCUT);
760       pos += priv->n_shortcuts;
761
762       gtk_list_store_insert (GTK_LIST_STORE (priv->model), &iter, pos);
763       gtk_list_store_set (GTK_LIST_STORE (priv->model), &iter,
764                           ICON_COLUMN, NULL,
765                           DISPLAY_NAME_COLUMN, _(FALLBACK_DISPLAY_NAME),
766                           TYPE_COLUMN, ROW_TYPE_SHORTCUT,
767                           DATA_COLUMN, g_object_ref (file),
768                           IS_FOLDER_COLUMN, FALSE,
769                           -1);
770       set_info_for_file_at_iter (button, file, &iter);
771       priv->n_shortcuts++;
772
773       gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (priv->filter_model));
774     }
775
776   return retval;
777 }
778
779 static gboolean
780 gtk_file_chooser_button_remove_shortcut_folder (GtkFileChooser  *chooser,
781                                                 GFile           *file,
782                                                 GError         **error)
783 {
784   GtkFileChooser *delegate;
785   gboolean retval;
786
787   delegate = g_object_get_qdata (G_OBJECT (chooser),
788                                  GTK_FILE_CHOOSER_DELEGATE_QUARK);
789
790   retval = _gtk_file_chooser_remove_shortcut_folder (delegate, file, error);
791
792   if (retval)
793     {
794       GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (chooser);
795       GtkFileChooserButtonPrivate *priv = button->priv;
796       GtkTreeIter iter;
797       gint pos;
798       gchar type;
799
800       pos = model_get_type_position (button, ROW_TYPE_SHORTCUT);
801       gtk_tree_model_iter_nth_child (priv->model, &iter, NULL, pos);
802
803       do
804         {
805           gpointer data;
806
807           gtk_tree_model_get (priv->model, &iter,
808                               TYPE_COLUMN, &type,
809                               DATA_COLUMN, &data,
810                               -1);
811
812           if (type == ROW_TYPE_SHORTCUT &&
813               data && g_file_equal (data, file))
814             {
815               model_free_row_data (GTK_FILE_CHOOSER_BUTTON (chooser), &iter);
816               gtk_list_store_remove (GTK_LIST_STORE (priv->model), &iter);
817               priv->n_shortcuts--;
818               gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (priv->filter_model));
819               update_combo_box (GTK_FILE_CHOOSER_BUTTON (chooser));
820               break;
821             }
822         }
823       while (type == ROW_TYPE_SHORTCUT &&
824              gtk_tree_model_iter_next (priv->model, &iter));
825     }
826
827   return retval;
828 }
829
830
831 /* ******************* *
832  *  GObject Functions  *
833  * ******************* */
834
835 static GObject *
836 gtk_file_chooser_button_constructor (GType                  type,
837                                      guint                  n_params,
838                                      GObjectConstructParam *params)
839 {
840   GObject *object;
841   GtkFileChooserButton *button;
842   GtkFileChooserButtonPrivate *priv;
843   GSList *list;
844
845   object = G_OBJECT_CLASS (gtk_file_chooser_button_parent_class)->constructor (type,
846                                                                                n_params,
847                                                                                params);
848   button = GTK_FILE_CHOOSER_BUTTON (object);
849   priv = button->priv;
850
851   if (!priv->dialog)
852     {
853       priv->dialog = gtk_file_chooser_dialog_new (NULL, NULL,
854                                                   GTK_FILE_CHOOSER_ACTION_OPEN,
855                                                   GTK_STOCK_CANCEL,
856                                                   GTK_RESPONSE_CANCEL,
857                                                   GTK_STOCK_OPEN,
858                                                   GTK_RESPONSE_ACCEPT,
859                                                   NULL);
860
861       gtk_dialog_set_default_response (GTK_DIALOG (priv->dialog),
862                                        GTK_RESPONSE_ACCEPT);
863       gtk_dialog_set_alternative_button_order (GTK_DIALOG (priv->dialog),
864                                                GTK_RESPONSE_ACCEPT,
865                                                GTK_RESPONSE_CANCEL,
866                                                -1);
867
868       gtk_file_chooser_button_set_title (button, _(DEFAULT_TITLE));
869     }
870   else if (!gtk_window_get_title (GTK_WINDOW (priv->dialog)))
871     {
872       gtk_file_chooser_button_set_title (button, _(DEFAULT_TITLE));
873     }
874
875   g_signal_connect (priv->dialog, "delete-event",
876                     G_CALLBACK (dialog_delete_event_cb), object);
877   g_signal_connect (priv->dialog, "response",
878                     G_CALLBACK (dialog_response_cb), object);
879
880   /* This is used, instead of the standard delegate, to ensure that signals are only
881    * delegated when the OK button is pressed. */
882   g_object_set_qdata (object, GTK_FILE_CHOOSER_DELEGATE_QUARK, priv->dialog);
883
884   g_signal_connect (priv->dialog, "update-preview",
885                     G_CALLBACK (dialog_update_preview_cb), object);
886   g_signal_connect (priv->dialog, "notify",
887                     G_CALLBACK (dialog_notify_cb), object);
888   g_object_add_weak_pointer (G_OBJECT (priv->dialog),
889                              (gpointer) (&priv->dialog));
890
891   priv->fs =
892     g_object_ref (_gtk_file_chooser_get_file_system (GTK_FILE_CHOOSER (priv->dialog)));
893
894   model_add_special (button);
895
896   list = _gtk_file_system_list_volumes (priv->fs);
897   model_add_volumes (button, list);
898   g_slist_free (list);
899
900   list = _gtk_file_system_list_bookmarks (priv->fs);
901   model_add_bookmarks (button, list);
902   g_slist_foreach (list, (GFunc) g_object_unref, NULL);
903   g_slist_free (list);
904
905   model_add_other (button);
906
907   model_add_empty_selection (button);
908
909   priv->filter_model = gtk_tree_model_filter_new (priv->model, NULL);
910   gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (priv->filter_model),
911                                           filter_model_visible_func,
912                                           object, NULL);
913
914   gtk_combo_box_set_model (GTK_COMBO_BOX (priv->combo_box), priv->filter_model);
915   gtk_combo_box_set_row_separator_func (GTK_COMBO_BOX (priv->combo_box),
916                                         combo_box_row_separator_func,
917                                         NULL, NULL);
918
919   /* set up the action for a user-provided dialog, this also updates
920    * the label, image and combobox
921    */
922   g_object_set (object,
923                 "action", gtk_file_chooser_get_action (GTK_FILE_CHOOSER (priv->dialog)),
924                 NULL);
925
926   priv->fs_volumes_changed_id =
927     g_signal_connect (priv->fs, "volumes-changed",
928                       G_CALLBACK (fs_volumes_changed_cb), object);
929   priv->fs_bookmarks_changed_id =
930     g_signal_connect (priv->fs, "bookmarks-changed",
931                       G_CALLBACK (fs_bookmarks_changed_cb), object);
932
933   update_label_and_image (button);
934   update_combo_box (button);
935
936   return object;
937 }
938
939 static void
940 gtk_file_chooser_button_set_property (GObject      *object,
941                                       guint         param_id,
942                                       const GValue *value,
943                                       GParamSpec   *pspec)
944 {
945   GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (object);
946   GtkFileChooserButtonPrivate *priv = button->priv;
947
948   switch (param_id)
949     {
950     case PROP_DIALOG:
951       /* Construct-only */
952       priv->dialog = g_value_get_object (value);
953       break;
954     case PROP_FOCUS_ON_CLICK:
955       gtk_file_chooser_button_set_focus_on_click (button, g_value_get_boolean (value));
956       break;
957     case PROP_WIDTH_CHARS:
958       gtk_file_chooser_button_set_width_chars (GTK_FILE_CHOOSER_BUTTON (object),
959                                                g_value_get_int (value));
960       break;
961     case GTK_FILE_CHOOSER_PROP_ACTION:
962       switch (g_value_get_enum (value))
963         {
964         case GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER:
965         case GTK_FILE_CHOOSER_ACTION_SAVE:
966           {
967             GEnumClass *eclass;
968             GEnumValue *eval;
969
970             eclass = g_type_class_peek (GTK_TYPE_FILE_CHOOSER_ACTION);
971             eval = g_enum_get_value (eclass, g_value_get_enum (value));
972             g_warning ("%s: Choosers of type `%s' do not support `%s'.",
973                        G_STRFUNC, G_OBJECT_TYPE_NAME (object), eval->value_name);
974
975             g_value_set_enum ((GValue *) value, GTK_FILE_CHOOSER_ACTION_OPEN);
976           }
977           break;
978         }
979
980       g_object_set_property (G_OBJECT (priv->dialog), pspec->name, value);
981       update_label_and_image (GTK_FILE_CHOOSER_BUTTON (object));
982       update_combo_box (GTK_FILE_CHOOSER_BUTTON (object));
983
984       switch (g_value_get_enum (value))
985         {
986         case GTK_FILE_CHOOSER_ACTION_OPEN:
987           gtk_widget_hide (priv->combo_box);
988           gtk_widget_show (priv->button);
989           break;
990         case GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER:
991           gtk_widget_hide (priv->button);
992           gtk_widget_show (priv->combo_box);
993           break;
994         default:
995           g_assert_not_reached ();
996           break;
997         }
998       break;
999
1000     case PROP_TITLE:
1001     case GTK_FILE_CHOOSER_PROP_FILTER:
1002     case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET:
1003     case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET_ACTIVE:
1004     case GTK_FILE_CHOOSER_PROP_USE_PREVIEW_LABEL:
1005     case GTK_FILE_CHOOSER_PROP_EXTRA_WIDGET:
1006     case GTK_FILE_CHOOSER_PROP_SHOW_HIDDEN:
1007     case GTK_FILE_CHOOSER_PROP_DO_OVERWRITE_CONFIRMATION:
1008     case GTK_FILE_CHOOSER_PROP_CREATE_FOLDERS:
1009       g_object_set_property (G_OBJECT (priv->dialog), pspec->name, value);
1010       break;
1011
1012     case GTK_FILE_CHOOSER_PROP_LOCAL_ONLY:
1013       g_object_set_property (G_OBJECT (priv->dialog), pspec->name, value);
1014       fs_volumes_changed_cb (priv->fs, button);
1015       fs_bookmarks_changed_cb (priv->fs, button);
1016       break;
1017
1018     case GTK_FILE_CHOOSER_PROP_SELECT_MULTIPLE:
1019       g_warning ("%s: Choosers of type `%s` do not support selecting multiple files.",
1020                  G_STRFUNC, G_OBJECT_TYPE_NAME (object));
1021       break;
1022     default:
1023       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
1024       break;
1025     }
1026 }
1027
1028 static void
1029 gtk_file_chooser_button_get_property (GObject    *object,
1030                                       guint       param_id,
1031                                       GValue     *value,
1032                                       GParamSpec *pspec)
1033 {
1034   GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (object);
1035   GtkFileChooserButtonPrivate *priv = button->priv;
1036
1037   switch (param_id)
1038     {
1039     case PROP_WIDTH_CHARS:
1040       g_value_set_int (value,
1041                        gtk_label_get_width_chars (GTK_LABEL (priv->label)));
1042       break;
1043     case PROP_FOCUS_ON_CLICK:
1044       g_value_set_boolean (value,
1045                            gtk_file_chooser_button_get_focus_on_click (button));
1046       break;
1047
1048     case PROP_TITLE:
1049     case GTK_FILE_CHOOSER_PROP_ACTION:
1050     case GTK_FILE_CHOOSER_PROP_FILTER:
1051     case GTK_FILE_CHOOSER_PROP_LOCAL_ONLY:
1052     case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET:
1053     case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET_ACTIVE:
1054     case GTK_FILE_CHOOSER_PROP_USE_PREVIEW_LABEL:
1055     case GTK_FILE_CHOOSER_PROP_EXTRA_WIDGET:
1056     case GTK_FILE_CHOOSER_PROP_SELECT_MULTIPLE:
1057     case GTK_FILE_CHOOSER_PROP_SHOW_HIDDEN:
1058     case GTK_FILE_CHOOSER_PROP_DO_OVERWRITE_CONFIRMATION:
1059     case GTK_FILE_CHOOSER_PROP_CREATE_FOLDERS:
1060       g_object_get_property (G_OBJECT (priv->dialog), pspec->name, value);
1061       break;
1062
1063     default:
1064       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
1065       break;
1066     }
1067 }
1068
1069 static void
1070 gtk_file_chooser_button_finalize (GObject *object)
1071 {
1072   GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (object);
1073   GtkFileChooserButtonPrivate *priv = button->priv;
1074
1075   if (priv->selection_while_inactive)
1076     g_object_unref (priv->selection_while_inactive);
1077
1078   if (priv->current_folder_while_inactive)
1079     g_object_unref (priv->current_folder_while_inactive);
1080
1081   G_OBJECT_CLASS (gtk_file_chooser_button_parent_class)->finalize (object);
1082 }
1083
1084 /* ********************* *
1085  *  GtkWidget Functions  *
1086  * ********************* */
1087
1088 static void
1089 gtk_file_chooser_button_destroy (GtkWidget *widget)
1090 {
1091   GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (widget);
1092   GtkFileChooserButtonPrivate *priv = button->priv;
1093   GtkTreeIter iter;
1094   GSList *l;
1095
1096   if (priv->dialog != NULL)
1097     {
1098       gtk_widget_destroy (priv->dialog);
1099       priv->dialog = NULL;
1100     }
1101
1102   if (priv->model && gtk_tree_model_get_iter_first (priv->model, &iter)) do
1103     {
1104       model_free_row_data (button, &iter);
1105     }
1106   while (gtk_tree_model_iter_next (priv->model, &iter));
1107
1108   if (priv->dnd_select_folder_cancellable)
1109     {
1110       g_cancellable_cancel (priv->dnd_select_folder_cancellable);
1111       priv->dnd_select_folder_cancellable = NULL;
1112     }
1113
1114   if (priv->update_button_cancellable)
1115     {
1116       g_cancellable_cancel (priv->update_button_cancellable);
1117       priv->update_button_cancellable = NULL;
1118     }
1119
1120   if (priv->change_icon_theme_cancellables)
1121     {
1122       for (l = priv->change_icon_theme_cancellables; l; l = l->next)
1123         {
1124           GCancellable *cancellable = G_CANCELLABLE (l->data);
1125           g_cancellable_cancel (cancellable);
1126         }
1127       g_slist_free (priv->change_icon_theme_cancellables);
1128       priv->change_icon_theme_cancellables = NULL;
1129     }
1130
1131   if (priv->model)
1132     {
1133       g_object_unref (priv->model);
1134       priv->model = NULL;
1135     }
1136
1137   if (priv->filter_model)
1138     {
1139       g_object_unref (priv->filter_model);
1140       priv->filter_model = NULL;
1141     }
1142
1143   if (priv->fs)
1144     {
1145       g_signal_handler_disconnect (priv->fs, priv->fs_volumes_changed_id);
1146       g_signal_handler_disconnect (priv->fs, priv->fs_bookmarks_changed_id);
1147       g_object_unref (priv->fs);
1148       priv->fs = NULL;
1149     }
1150
1151   GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->destroy (widget);
1152 }
1153
1154 struct DndSelectFolderData
1155 {
1156   GtkFileSystem *file_system;
1157   GtkFileChooserButton *button;
1158   GtkFileChooserAction action;
1159   GFile *file;
1160   gchar **uris;
1161   guint i;
1162   gboolean selected;
1163 };
1164
1165 static void
1166 dnd_select_folder_get_info_cb (GCancellable *cancellable,
1167                                GFileInfo    *info,
1168                                const GError *error,
1169                                gpointer      user_data)
1170 {
1171   gboolean cancelled = g_cancellable_is_cancelled (cancellable);
1172   struct DndSelectFolderData *data = user_data;
1173
1174   if (cancellable != data->button->priv->dnd_select_folder_cancellable)
1175     {
1176       g_object_unref (data->button);
1177       g_object_unref (data->file);
1178       g_strfreev (data->uris);
1179       g_free (data);
1180
1181       g_object_unref (cancellable);
1182       return;
1183     }
1184
1185   data->button->priv->dnd_select_folder_cancellable = NULL;
1186
1187   if (!cancelled && !error && info != NULL)
1188     {
1189       gboolean is_folder;
1190
1191       is_folder = _gtk_file_info_consider_as_directory (info);
1192
1193       data->selected =
1194         (((data->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER && is_folder) ||
1195           (data->action == GTK_FILE_CHOOSER_ACTION_OPEN && !is_folder)) &&
1196          gtk_file_chooser_select_file (GTK_FILE_CHOOSER (data->button->priv->dialog),
1197                                        data->file, NULL));
1198     }
1199   else
1200     data->selected = FALSE;
1201
1202   if (data->selected || data->uris[++data->i] == NULL)
1203     {
1204       g_signal_emit (data->button, file_chooser_button_signals[FILE_SET], 0);
1205
1206       g_object_unref (data->button);
1207       g_object_unref (data->file);
1208       g_strfreev (data->uris);
1209       g_free (data);
1210
1211       g_object_unref (cancellable);
1212       return;
1213     }
1214
1215   if (data->file)
1216     g_object_unref (data->file);
1217
1218   data->file = g_file_new_for_uri (data->uris[data->i]);
1219
1220   data->button->priv->dnd_select_folder_cancellable =
1221     _gtk_file_system_get_info (data->file_system, data->file,
1222                                "standard::type",
1223                                dnd_select_folder_get_info_cb, user_data);
1224
1225   g_object_unref (cancellable);
1226 }
1227
1228 static void
1229 gtk_file_chooser_button_drag_data_received (GtkWidget        *widget,
1230                                             GdkDragContext   *context,
1231                                             gint              x,
1232                                             gint              y,
1233                                             GtkSelectionData *data,
1234                                             guint             type,
1235                                             guint             drag_time)
1236 {
1237   GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (widget);
1238   GtkFileChooserButtonPrivate *priv = button->priv;
1239   GFile *file;
1240   gchar *text;
1241
1242   if (GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->drag_data_received != NULL)
1243     GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->drag_data_received (widget,
1244                                                                                  context,
1245                                                                                  x, y,
1246                                                                                  data, type,
1247                                                                                  drag_time);
1248
1249   if (widget == NULL || context == NULL || data == NULL || gtk_selection_data_get_length (data) < 0)
1250     return;
1251
1252   switch (type)
1253     {
1254     case TEXT_URI_LIST:
1255       {
1256         gchar **uris;
1257         struct DndSelectFolderData *info;
1258
1259         uris = gtk_selection_data_get_uris (data);
1260
1261         if (uris == NULL)
1262           break;
1263
1264         info = g_new0 (struct DndSelectFolderData, 1);
1265         info->button = g_object_ref (button);
1266         info->i = 0;
1267         info->uris = uris;
1268         info->selected = FALSE;
1269         info->file_system = priv->fs;
1270         g_object_get (priv->dialog, "action", &info->action, NULL);
1271
1272         info->file = g_file_new_for_uri (info->uris[info->i]);
1273
1274         if (priv->dnd_select_folder_cancellable)
1275           g_cancellable_cancel (priv->dnd_select_folder_cancellable);
1276
1277         priv->dnd_select_folder_cancellable =
1278           _gtk_file_system_get_info (priv->fs, info->file,
1279                                      "standard::type",
1280                                      dnd_select_folder_get_info_cb, info);
1281       }
1282       break;
1283
1284     case TEXT_PLAIN:
1285       text = (char*) gtk_selection_data_get_text (data);
1286       file = g_file_new_for_uri (text);
1287       gtk_file_chooser_select_file (GTK_FILE_CHOOSER (priv->dialog), file,
1288                                     NULL);
1289       g_object_unref (file);
1290       g_free (text);
1291       g_signal_emit (button, file_chooser_button_signals[FILE_SET], 0);
1292       break;
1293
1294     default:
1295       break;
1296     }
1297
1298   gtk_drag_finish (context, TRUE, FALSE, drag_time);
1299 }
1300
1301 static void
1302 gtk_file_chooser_button_show_all (GtkWidget *widget)
1303 {
1304   gtk_widget_show (widget);
1305 }
1306
1307 static void
1308 gtk_file_chooser_button_show (GtkWidget *widget)
1309 {
1310   GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (widget);
1311   GtkFileChooserButtonPrivate *priv = button->priv;
1312
1313   if (GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->show)
1314     GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->show (widget);
1315
1316   if (priv->active)
1317     open_dialog (GTK_FILE_CHOOSER_BUTTON (widget));
1318 }
1319
1320 static void
1321 gtk_file_chooser_button_hide (GtkWidget *widget)
1322 {
1323   GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (widget);
1324   GtkFileChooserButtonPrivate *priv = button->priv;
1325
1326   gtk_widget_hide (priv->dialog);
1327
1328   if (GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->hide)
1329     GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->hide (widget);
1330 }
1331
1332 static void
1333 gtk_file_chooser_button_map (GtkWidget *widget)
1334 {
1335   GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->map (widget);
1336 }
1337
1338 static gboolean
1339 gtk_file_chooser_button_mnemonic_activate (GtkWidget *widget,
1340                                            gboolean   group_cycling)
1341 {
1342   GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (widget);
1343   GtkFileChooserButtonPrivate *priv = button->priv;
1344
1345   switch (gtk_file_chooser_get_action (GTK_FILE_CHOOSER (priv->dialog)))
1346     {
1347     case GTK_FILE_CHOOSER_ACTION_OPEN:
1348       gtk_widget_grab_focus (priv->button);
1349       break;
1350     case GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER:
1351       return gtk_widget_mnemonic_activate (priv->combo_box, group_cycling);
1352       break;
1353     default:
1354       g_assert_not_reached ();
1355       break;
1356     }
1357
1358   return TRUE;
1359 }
1360
1361 /* Changes the icons wherever it is needed */
1362 struct ChangeIconThemeData
1363 {
1364   GtkFileChooserButton *button;
1365   GtkTreeRowReference *row_ref;
1366 };
1367
1368 static void
1369 change_icon_theme_get_info_cb (GCancellable *cancellable,
1370                                GFileInfo    *info,
1371                                const GError *error,
1372                                gpointer      user_data)
1373 {
1374   gboolean cancelled = g_cancellable_is_cancelled (cancellable);
1375   GdkPixbuf *pixbuf;
1376   struct ChangeIconThemeData *data = user_data;
1377
1378   if (!g_slist_find (data->button->priv->change_icon_theme_cancellables, cancellable))
1379     goto out;
1380
1381   data->button->priv->change_icon_theme_cancellables =
1382     g_slist_remove (data->button->priv->change_icon_theme_cancellables, cancellable);
1383
1384   if (cancelled || error)
1385     goto out;
1386
1387   pixbuf = _gtk_file_info_render_icon (info, GTK_WIDGET (data->button), data->button->priv->icon_size);
1388
1389   if (pixbuf)
1390     {
1391       gint width = 0;
1392       GtkTreeIter iter;
1393       GtkTreePath *path;
1394
1395       width = MAX (width, gdk_pixbuf_get_width (pixbuf));
1396
1397       path = gtk_tree_row_reference_get_path (data->row_ref);
1398       if (path)
1399         {
1400           gtk_tree_model_get_iter (data->button->priv->model, &iter, path);
1401           gtk_tree_path_free (path);
1402
1403           gtk_list_store_set (GTK_LIST_STORE (data->button->priv->model), &iter,
1404                               ICON_COLUMN, pixbuf,
1405                               -1);
1406
1407           g_object_set (data->button->priv->icon_cell,
1408                         "width", width,
1409                         NULL);
1410         }
1411       g_object_unref (pixbuf);
1412     }
1413
1414 out:
1415   g_object_unref (data->button);
1416   gtk_tree_row_reference_free (data->row_ref);
1417   g_free (data);
1418
1419   g_object_unref (cancellable);
1420 }
1421
1422 static void
1423 change_icon_theme (GtkFileChooserButton *button)
1424 {
1425   GtkFileChooserButtonPrivate *priv = button->priv;
1426   GtkSettings *settings;
1427   GtkIconTheme *theme;
1428   GtkTreeIter iter;
1429   GSList *l;
1430   gint width = 0, height = 0;
1431
1432   for (l = button->priv->change_icon_theme_cancellables; l; l = l->next)
1433     {
1434       GCancellable *cancellable = G_CANCELLABLE (l->data);
1435       g_cancellable_cancel (cancellable);
1436     }
1437   g_slist_free (button->priv->change_icon_theme_cancellables);
1438   button->priv->change_icon_theme_cancellables = NULL;
1439
1440   settings = gtk_settings_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (button)));
1441
1442   if (gtk_icon_size_lookup_for_settings (settings, GTK_ICON_SIZE_MENU,
1443                                          &width, &height))
1444     priv->icon_size = MAX (width, height);
1445   else
1446     priv->icon_size = FALLBACK_ICON_SIZE;
1447
1448   update_label_and_image (button);
1449
1450   gtk_tree_model_get_iter_first (priv->model, &iter);
1451
1452   theme = get_icon_theme (GTK_WIDGET (button));
1453
1454   do
1455     {
1456       GdkPixbuf *pixbuf;
1457       gchar type;
1458       gpointer data;
1459
1460       type = ROW_TYPE_INVALID;
1461       gtk_tree_model_get (priv->model, &iter,
1462                           TYPE_COLUMN, &type,
1463                           DATA_COLUMN, &data,
1464                           -1);
1465
1466       switch (type)
1467         {
1468         case ROW_TYPE_SPECIAL:
1469         case ROW_TYPE_SHORTCUT:
1470         case ROW_TYPE_BOOKMARK:
1471         case ROW_TYPE_CURRENT_FOLDER:
1472           if (data)
1473             {
1474               if (g_file_is_native (G_FILE (data)))
1475                 {
1476                   GtkTreePath *path;
1477                   GCancellable *cancellable;
1478                   struct ChangeIconThemeData *info;
1479
1480                   info = g_new0 (struct ChangeIconThemeData, 1);
1481                   info->button = g_object_ref (button);
1482                   path = gtk_tree_model_get_path (priv->model, &iter);
1483                   info->row_ref = gtk_tree_row_reference_new (priv->model, path);
1484                   gtk_tree_path_free (path);
1485
1486                   cancellable =
1487                     _gtk_file_system_get_info (priv->fs, data,
1488                                                "standard::icon",
1489                                                change_icon_theme_get_info_cb,
1490                                                info);
1491                   button->priv->change_icon_theme_cancellables =
1492                     g_slist_append (button->priv->change_icon_theme_cancellables, cancellable);
1493                   pixbuf = NULL;
1494                 }
1495               else
1496                 /* Don't call get_info for remote paths to avoid latency and
1497                  * auth dialogs.
1498                  * If we switch to a better bookmarks file format (XBEL), we
1499                  * should use mime info to get a better icon.
1500                  */
1501                 pixbuf = gtk_icon_theme_load_icon (theme, "folder-remote",
1502                                                    priv->icon_size, 0, NULL);
1503             }
1504           else
1505             pixbuf = gtk_icon_theme_load_icon (theme, FALLBACK_ICON_NAME,
1506                                                priv->icon_size, 0, NULL);
1507           break;
1508         case ROW_TYPE_VOLUME:
1509           if (data)
1510             pixbuf = _gtk_file_system_volume_render_icon (data,
1511                                                           GTK_WIDGET (button),
1512                                                           priv->icon_size,
1513                                                           NULL);
1514           else
1515             pixbuf = gtk_icon_theme_load_icon (theme, FALLBACK_ICON_NAME,
1516                                                priv->icon_size, 0, NULL);
1517           break;
1518         default:
1519           continue;
1520           break;
1521         }
1522
1523       if (pixbuf)
1524         width = MAX (width, gdk_pixbuf_get_width (pixbuf));
1525
1526       gtk_list_store_set (GTK_LIST_STORE (priv->model), &iter,
1527                           ICON_COLUMN, pixbuf,
1528                           -1);
1529
1530       if (pixbuf)
1531         g_object_unref (pixbuf);
1532     }
1533   while (gtk_tree_model_iter_next (priv->model, &iter));
1534
1535   g_object_set (button->priv->icon_cell,
1536                 "width", width,
1537                 NULL);
1538 }
1539
1540 static void
1541 gtk_file_chooser_button_style_updated (GtkWidget *widget)
1542 {
1543   GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->style_updated (widget);
1544
1545   if (gtk_widget_has_screen (widget))
1546     change_icon_theme (GTK_FILE_CHOOSER_BUTTON (widget));
1547 }
1548
1549 static void
1550 gtk_file_chooser_button_screen_changed (GtkWidget *widget,
1551                                         GdkScreen *old_screen)
1552 {
1553   if (GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->screen_changed)
1554     GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->screen_changed (widget,
1555                                                                              old_screen);
1556
1557   change_icon_theme (GTK_FILE_CHOOSER_BUTTON (widget));
1558 }
1559
1560
1561 /* ******************* *
1562  *  Utility Functions  *
1563  * ******************* */
1564
1565 /* General */
1566 static GtkIconTheme *
1567 get_icon_theme (GtkWidget *widget)
1568 {
1569   if (gtk_widget_has_screen (widget))
1570     return gtk_icon_theme_get_for_screen (gtk_widget_get_screen (widget));
1571
1572   return gtk_icon_theme_get_default ();
1573 }
1574
1575
1576 struct SetDisplayNameData
1577 {
1578   GtkFileChooserButton *button;
1579   char *label;
1580   GtkTreeRowReference *row_ref;
1581 };
1582
1583 static void
1584 set_info_get_info_cb (GCancellable *cancellable,
1585                       GFileInfo    *info,
1586                       const GError *error,
1587                       gpointer      callback_data)
1588 {
1589   gboolean cancelled = g_cancellable_is_cancelled (cancellable);
1590   GdkPixbuf *pixbuf;
1591   GtkTreePath *path;
1592   GtkTreeIter iter;
1593   GCancellable *model_cancellable = NULL;
1594   struct SetDisplayNameData *data = callback_data;
1595   gboolean is_folder;
1596
1597   if (!data->button->priv->model)
1598     /* button got destroyed */
1599     goto out;
1600
1601   path = gtk_tree_row_reference_get_path (data->row_ref);
1602   if (!path)
1603     /* Cancellable doesn't exist anymore in the model */
1604     goto out;
1605
1606   gtk_tree_model_get_iter (data->button->priv->model, &iter, path);
1607   gtk_tree_path_free (path);
1608
1609   /* Validate the cancellable */
1610   gtk_tree_model_get (data->button->priv->model, &iter,
1611                       CANCELLABLE_COLUMN, &model_cancellable,
1612                       -1);
1613   if (cancellable != model_cancellable)
1614     goto out;
1615
1616   gtk_list_store_set (GTK_LIST_STORE (data->button->priv->model), &iter,
1617                       CANCELLABLE_COLUMN, NULL,
1618                       -1);
1619
1620   if (cancelled || error)
1621     /* There was an error, leave the fallback name in there */
1622     goto out;
1623
1624   pixbuf = _gtk_file_info_render_icon (info, GTK_WIDGET (data->button), data->button->priv->icon_size);
1625
1626   if (!data->label)
1627     data->label = g_strdup (g_file_info_get_display_name (info));
1628
1629   is_folder = _gtk_file_info_consider_as_directory (info);
1630
1631   gtk_list_store_set (GTK_LIST_STORE (data->button->priv->model), &iter,
1632                       ICON_COLUMN, pixbuf,
1633                       DISPLAY_NAME_COLUMN, data->label,
1634                       IS_FOLDER_COLUMN, is_folder,
1635                       -1);
1636
1637   if (pixbuf)
1638     g_object_unref (pixbuf);
1639
1640 out:
1641   g_object_unref (data->button);
1642   g_free (data->label);
1643   gtk_tree_row_reference_free (data->row_ref);
1644   g_free (data);
1645
1646   if (model_cancellable)
1647     g_object_unref (model_cancellable);
1648 }
1649
1650 static void
1651 set_info_for_file_at_iter (GtkFileChooserButton *button,
1652                            GFile                *file,
1653                            GtkTreeIter          *iter)
1654 {
1655   struct SetDisplayNameData *data;
1656   GtkTreePath *tree_path;
1657   GCancellable *cancellable;
1658
1659   data = g_new0 (struct SetDisplayNameData, 1);
1660   data->button = g_object_ref (button);
1661   data->label = _gtk_file_system_get_bookmark_label (button->priv->fs, file);
1662
1663   tree_path = gtk_tree_model_get_path (button->priv->model, iter);
1664   data->row_ref = gtk_tree_row_reference_new (button->priv->model, tree_path);
1665   gtk_tree_path_free (tree_path);
1666
1667   cancellable = _gtk_file_system_get_info (button->priv->fs, file,
1668                                            "standard::type,standard::icon,standard::display-name",
1669                                            set_info_get_info_cb, data);
1670
1671   gtk_list_store_set (GTK_LIST_STORE (button->priv->model), iter,
1672                       CANCELLABLE_COLUMN, cancellable,
1673                       -1);
1674 }
1675
1676 /* Shortcuts Model */
1677 static gint
1678 model_get_type_position (GtkFileChooserButton *button,
1679                          RowType               row_type)
1680 {
1681   gint retval = 0;
1682
1683   if (row_type == ROW_TYPE_SPECIAL)
1684     return retval;
1685
1686   retval += button->priv->n_special;
1687
1688   if (row_type == ROW_TYPE_VOLUME)
1689     return retval;
1690
1691   retval += button->priv->n_volumes;
1692
1693   if (row_type == ROW_TYPE_SHORTCUT)
1694     return retval;
1695
1696   retval += button->priv->n_shortcuts;
1697
1698   if (row_type == ROW_TYPE_BOOKMARK_SEPARATOR)
1699     return retval;
1700
1701   retval += button->priv->has_bookmark_separator;
1702
1703   if (row_type == ROW_TYPE_BOOKMARK)
1704     return retval;
1705
1706   retval += button->priv->n_bookmarks;
1707
1708   if (row_type == ROW_TYPE_CURRENT_FOLDER_SEPARATOR)
1709     return retval;
1710
1711   retval += button->priv->has_current_folder_separator;
1712
1713   if (row_type == ROW_TYPE_CURRENT_FOLDER)
1714     return retval;
1715
1716   retval += button->priv->has_current_folder;
1717
1718   if (row_type == ROW_TYPE_OTHER_SEPARATOR)
1719     return retval;
1720
1721   retval += button->priv->has_other_separator;
1722
1723   if (row_type == ROW_TYPE_OTHER)
1724     return retval;
1725
1726   retval++;
1727
1728   if (row_type == ROW_TYPE_EMPTY_SELECTION)
1729     return retval;
1730
1731   g_assert_not_reached ();
1732   return -1;
1733 }
1734
1735 static void
1736 model_free_row_data (GtkFileChooserButton *button,
1737                      GtkTreeIter          *iter)
1738 {
1739   gchar type;
1740   gpointer data;
1741   GCancellable *cancellable;
1742
1743   gtk_tree_model_get (button->priv->model, iter,
1744                       TYPE_COLUMN, &type,
1745                       DATA_COLUMN, &data,
1746                       CANCELLABLE_COLUMN, &cancellable,
1747                       -1);
1748
1749   if (cancellable)
1750     {
1751       g_cancellable_cancel (cancellable);
1752       g_object_unref (cancellable);
1753     }
1754
1755   switch (type)
1756     {
1757     case ROW_TYPE_SPECIAL:
1758     case ROW_TYPE_SHORTCUT:
1759     case ROW_TYPE_BOOKMARK:
1760     case ROW_TYPE_CURRENT_FOLDER:
1761       g_object_unref (data);
1762       break;
1763     case ROW_TYPE_VOLUME:
1764       _gtk_file_system_volume_unref (data);
1765       break;
1766     default:
1767       break;
1768     }
1769 }
1770
1771 static void
1772 model_add_special_get_info_cb (GCancellable *cancellable,
1773                                GFileInfo    *info,
1774                                const GError *error,
1775                                gpointer      user_data)
1776 {
1777   gboolean cancelled = g_cancellable_is_cancelled (cancellable);
1778   GtkTreeIter iter;
1779   GtkTreePath *path;
1780   GdkPixbuf *pixbuf;
1781   GCancellable *model_cancellable = NULL;
1782   struct ChangeIconThemeData *data = user_data;
1783   gchar *name;
1784
1785   if (!data->button->priv->model)
1786     /* button got destroyed */
1787     goto out;
1788
1789   path = gtk_tree_row_reference_get_path (data->row_ref);
1790   if (!path)
1791     /* Cancellable doesn't exist anymore in the model */
1792     goto out;
1793
1794   gtk_tree_model_get_iter (data->button->priv->model, &iter, path);
1795   gtk_tree_path_free (path);
1796
1797   gtk_tree_model_get (data->button->priv->model, &iter,
1798                       CANCELLABLE_COLUMN, &model_cancellable,
1799                       -1);
1800   if (cancellable != model_cancellable)
1801     goto out;
1802
1803   gtk_list_store_set (GTK_LIST_STORE (data->button->priv->model), &iter,
1804                       CANCELLABLE_COLUMN, NULL,
1805                       -1);
1806
1807   if (cancelled || error)
1808     goto out;
1809
1810   pixbuf = _gtk_file_info_render_icon (info, GTK_WIDGET (data->button), data->button->priv->icon_size);
1811
1812   if (pixbuf)
1813     {
1814       gtk_list_store_set (GTK_LIST_STORE (data->button->priv->model), &iter,
1815                           ICON_COLUMN, pixbuf,
1816                           -1);
1817       g_object_unref (pixbuf);
1818     }
1819
1820   gtk_tree_model_get (data->button->priv->model, &iter,
1821                       DISPLAY_NAME_COLUMN, &name,
1822                       -1);
1823   if (!name)
1824     gtk_list_store_set (GTK_LIST_STORE (data->button->priv->model), &iter,
1825                         DISPLAY_NAME_COLUMN, g_file_info_get_display_name (info),
1826                         -1);
1827   g_free (name);
1828
1829 out:
1830   g_object_unref (data->button);
1831   gtk_tree_row_reference_free (data->row_ref);
1832   g_free (data);
1833
1834   if (model_cancellable)
1835     g_object_unref (model_cancellable);
1836 }
1837
1838 static void
1839 model_add_special (GtkFileChooserButton *button)
1840 {
1841   const gchar *homedir;
1842   const gchar *desktopdir;
1843   GtkListStore *store;
1844   GtkTreeIter iter;
1845   GFile *file;
1846   gint pos;
1847
1848   store = GTK_LIST_STORE (button->priv->model);
1849   pos = model_get_type_position (button, ROW_TYPE_SPECIAL);
1850
1851   homedir = g_get_home_dir ();
1852
1853   if (homedir)
1854     {
1855       GtkTreePath *tree_path;
1856       GCancellable *cancellable;
1857       struct ChangeIconThemeData *info;
1858
1859       file = g_file_new_for_path (homedir);
1860       gtk_list_store_insert (store, &iter, pos);
1861       pos++;
1862
1863       info = g_new0 (struct ChangeIconThemeData, 1);
1864       info->button = g_object_ref (button);
1865       tree_path = gtk_tree_model_get_path (GTK_TREE_MODEL (store), &iter);
1866       info->row_ref = gtk_tree_row_reference_new (GTK_TREE_MODEL (store),
1867                                                   tree_path);
1868       gtk_tree_path_free (tree_path);
1869
1870       cancellable = _gtk_file_system_get_info (button->priv->fs, file,
1871                                                "standard::icon,standard::display-name",
1872                                                model_add_special_get_info_cb, info);
1873
1874       gtk_list_store_set (store, &iter,
1875                           ICON_COLUMN, NULL,
1876                           DISPLAY_NAME_COLUMN, NULL,
1877                           TYPE_COLUMN, ROW_TYPE_SPECIAL,
1878                           DATA_COLUMN, file,
1879                           IS_FOLDER_COLUMN, TRUE,
1880                           CANCELLABLE_COLUMN, cancellable,
1881                           -1);
1882
1883       button->priv->n_special++;
1884     }
1885
1886   desktopdir = g_get_user_special_dir (G_USER_DIRECTORY_DESKTOP);
1887
1888   /* "To disable a directory, point it to the homedir."
1889    * See http://freedesktop.org/wiki/Software/xdg-user-dirs
1890    **/
1891   if (g_strcmp0 (desktopdir, g_get_home_dir ()) != 0)
1892     {
1893       GtkTreePath *tree_path;
1894       GCancellable *cancellable;
1895       struct ChangeIconThemeData *info;
1896
1897       file = g_file_new_for_path (desktopdir);
1898       gtk_list_store_insert (store, &iter, pos);
1899       pos++;
1900
1901       info = g_new0 (struct ChangeIconThemeData, 1);
1902       info->button = g_object_ref (button);
1903       tree_path = gtk_tree_model_get_path (GTK_TREE_MODEL (store), &iter);
1904       info->row_ref = gtk_tree_row_reference_new (GTK_TREE_MODEL (store),
1905                                                   tree_path);
1906       gtk_tree_path_free (tree_path);
1907
1908       cancellable = _gtk_file_system_get_info (button->priv->fs, file,
1909                                                "standard::icon,standard::display-name",
1910                                                model_add_special_get_info_cb, info);
1911
1912       gtk_list_store_set (store, &iter,
1913                           TYPE_COLUMN, ROW_TYPE_SPECIAL,
1914                           ICON_COLUMN, NULL,
1915                           DISPLAY_NAME_COLUMN, _(DESKTOP_DISPLAY_NAME),
1916                           DATA_COLUMN, file,
1917                           IS_FOLDER_COLUMN, TRUE,
1918                           CANCELLABLE_COLUMN, cancellable,
1919                           -1);
1920
1921       button->priv->n_special++;
1922     }
1923 }
1924
1925 static void
1926 model_add_volumes (GtkFileChooserButton *button,
1927                    GSList               *volumes)
1928 {
1929   GtkListStore *store;
1930   gint pos;
1931   gboolean local_only;
1932   GSList *l;
1933
1934   if (!volumes)
1935     return;
1936
1937   store = GTK_LIST_STORE (button->priv->model);
1938   pos = model_get_type_position (button, ROW_TYPE_VOLUME);
1939   local_only = gtk_file_chooser_get_local_only (GTK_FILE_CHOOSER (button->priv->dialog));
1940
1941   for (l = volumes; l; l = l->next)
1942     {
1943       GtkFileSystemVolume *volume;
1944       GtkTreeIter iter;
1945       GdkPixbuf *pixbuf;
1946       gchar *display_name;
1947
1948       volume = l->data;
1949
1950       if (local_only)
1951         {
1952           if (_gtk_file_system_volume_is_mounted (volume))
1953             {
1954               GFile *base_file;
1955
1956               base_file = _gtk_file_system_volume_get_root (volume);
1957               if (base_file != NULL)
1958                 {
1959                   if (!_gtk_file_has_native_path (base_file))
1960                     {
1961                       g_object_unref (base_file);
1962                       continue;
1963                     }
1964                   else
1965                     g_object_unref (base_file);
1966                 }
1967             }
1968         }
1969
1970       pixbuf = _gtk_file_system_volume_render_icon (volume,
1971                                                     GTK_WIDGET (button),
1972                                                     button->priv->icon_size,
1973                                                     NULL);
1974       display_name = _gtk_file_system_volume_get_display_name (volume);
1975
1976       gtk_list_store_insert (store, &iter, pos);
1977       gtk_list_store_set (store, &iter,
1978                           ICON_COLUMN, pixbuf,
1979                           DISPLAY_NAME_COLUMN, display_name,
1980                           TYPE_COLUMN, ROW_TYPE_VOLUME,
1981                           DATA_COLUMN, _gtk_file_system_volume_ref (volume),
1982                           IS_FOLDER_COLUMN, TRUE,
1983                           -1);
1984
1985       if (pixbuf)
1986         g_object_unref (pixbuf);
1987       g_free (display_name);
1988
1989       button->priv->n_volumes++;
1990       pos++;
1991     }
1992 }
1993
1994 extern gchar * _gtk_file_chooser_label_for_file (GFile *file);
1995
1996 static void
1997 model_add_bookmarks (GtkFileChooserButton *button,
1998                      GSList               *bookmarks)
1999 {
2000   GtkListStore *store;
2001   GtkTreeIter iter;
2002   gint pos;
2003   gboolean local_only;
2004   GSList *l;
2005
2006   if (!bookmarks)
2007     return;
2008
2009   store = GTK_LIST_STORE (button->priv->model);
2010   pos = model_get_type_position (button, ROW_TYPE_BOOKMARK);
2011   local_only = gtk_file_chooser_get_local_only (GTK_FILE_CHOOSER (button->priv->dialog));
2012
2013   for (l = bookmarks; l; l = l->next)
2014     {
2015       GFile *file;
2016
2017       file = l->data;
2018
2019       if (_gtk_file_has_native_path (file))
2020         {
2021           gtk_list_store_insert (store, &iter, pos);
2022           gtk_list_store_set (store, &iter,
2023                               ICON_COLUMN, NULL,
2024                               DISPLAY_NAME_COLUMN, _(FALLBACK_DISPLAY_NAME),
2025                               TYPE_COLUMN, ROW_TYPE_BOOKMARK,
2026                               DATA_COLUMN, g_object_ref (file),
2027                               IS_FOLDER_COLUMN, FALSE,
2028                               -1);
2029           set_info_for_file_at_iter (button, file, &iter);
2030         }
2031       else
2032         {
2033           gchar *label;
2034           GtkIconTheme *icon_theme;
2035           GdkPixbuf *pixbuf;
2036
2037           if (local_only)
2038             continue;
2039
2040           /* Don't call get_info for remote paths to avoid latency and
2041            * auth dialogs.
2042            * If we switch to a better bookmarks file format (XBEL), we
2043            * should use mime info to get a better icon.
2044            */
2045           label = _gtk_file_system_get_bookmark_label (button->priv->fs, file);
2046           if (!label)
2047             label = _gtk_file_chooser_label_for_file (file);
2048
2049           icon_theme = gtk_icon_theme_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (button)));
2050           pixbuf = gtk_icon_theme_load_icon (icon_theme, "folder-remote",
2051                                              button->priv->icon_size, 0, NULL);
2052
2053           gtk_list_store_insert (store, &iter, pos);
2054           gtk_list_store_set (store, &iter,
2055                               ICON_COLUMN, pixbuf,
2056                               DISPLAY_NAME_COLUMN, label,
2057                               TYPE_COLUMN, ROW_TYPE_BOOKMARK,
2058                               DATA_COLUMN, g_object_ref (file),
2059                               IS_FOLDER_COLUMN, TRUE,
2060                               -1);
2061
2062           g_free (label);
2063           g_object_unref (pixbuf);
2064         }
2065
2066       button->priv->n_bookmarks++;
2067       pos++;
2068     }
2069
2070   if (button->priv->n_bookmarks > 0 &&
2071       !button->priv->has_bookmark_separator)
2072     {
2073       pos = model_get_type_position (button, ROW_TYPE_BOOKMARK_SEPARATOR);
2074
2075       gtk_list_store_insert (store, &iter, pos);
2076       gtk_list_store_set (store, &iter,
2077                           ICON_COLUMN, NULL,
2078                           DISPLAY_NAME_COLUMN, NULL,
2079                           TYPE_COLUMN, ROW_TYPE_BOOKMARK_SEPARATOR,
2080                           DATA_COLUMN, NULL,
2081                           IS_FOLDER_COLUMN, FALSE,
2082                           -1);
2083       button->priv->has_bookmark_separator = TRUE;
2084     }
2085 }
2086
2087 static void
2088 model_update_current_folder (GtkFileChooserButton *button,
2089                              GFile                *file)
2090 {
2091   GtkListStore *store;
2092   GtkTreeIter iter;
2093   gint pos;
2094
2095   if (!file)
2096     return;
2097
2098   store = GTK_LIST_STORE (button->priv->model);
2099
2100   if (!button->priv->has_current_folder_separator)
2101     {
2102       pos = model_get_type_position (button, ROW_TYPE_CURRENT_FOLDER_SEPARATOR);
2103       gtk_list_store_insert (store, &iter, pos);
2104       gtk_list_store_set (store, &iter,
2105                           ICON_COLUMN, NULL,
2106                           DISPLAY_NAME_COLUMN, NULL,
2107                           TYPE_COLUMN, ROW_TYPE_CURRENT_FOLDER_SEPARATOR,
2108                           DATA_COLUMN, NULL,
2109                           IS_FOLDER_COLUMN, FALSE,
2110                           -1);
2111       button->priv->has_current_folder_separator = TRUE;
2112     }
2113
2114   pos = model_get_type_position (button, ROW_TYPE_CURRENT_FOLDER);
2115   if (!button->priv->has_current_folder)
2116     {
2117       gtk_list_store_insert (store, &iter, pos);
2118       button->priv->has_current_folder = TRUE;
2119     }
2120   else
2121     {
2122       gtk_tree_model_iter_nth_child (button->priv->model, &iter, NULL, pos);
2123       model_free_row_data (button, &iter);
2124     }
2125
2126   if (g_file_is_native (file))
2127     {
2128       gtk_list_store_set (store, &iter,
2129                           ICON_COLUMN, NULL,
2130                           DISPLAY_NAME_COLUMN, _(FALLBACK_DISPLAY_NAME),
2131                           TYPE_COLUMN, ROW_TYPE_CURRENT_FOLDER,
2132                           DATA_COLUMN, g_object_ref (file),
2133                           IS_FOLDER_COLUMN, FALSE,
2134                           -1);
2135       set_info_for_file_at_iter (button, file, &iter);
2136     }
2137   else
2138     {
2139       gchar *label;
2140       GtkIconTheme *icon_theme;
2141       GdkPixbuf *pixbuf;
2142
2143       /* Don't call get_info for remote paths to avoid latency and
2144        * auth dialogs.
2145        * If we switch to a better bookmarks file format (XBEL), we
2146        * should use mime info to get a better icon.
2147        */
2148       label = _gtk_file_system_get_bookmark_label (button->priv->fs, file);
2149       if (!label)
2150         label = _gtk_file_chooser_label_for_file (file);
2151
2152       icon_theme = gtk_icon_theme_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (button)));
2153
2154       if (g_file_is_native (file))
2155           pixbuf = gtk_icon_theme_load_icon (icon_theme, "folder",
2156                                              button->priv->icon_size, 0, NULL);
2157       else
2158           pixbuf = gtk_icon_theme_load_icon (icon_theme, "folder-remote",
2159                                              button->priv->icon_size, 0, NULL);
2160
2161       gtk_list_store_set (store, &iter,
2162                           ICON_COLUMN, pixbuf,
2163                           DISPLAY_NAME_COLUMN, label,
2164                           TYPE_COLUMN, ROW_TYPE_CURRENT_FOLDER,
2165                           DATA_COLUMN, g_object_ref (file),
2166                           IS_FOLDER_COLUMN, TRUE,
2167                           -1);
2168
2169       g_free (label);
2170       g_object_unref (pixbuf);
2171     }
2172 }
2173
2174 static void
2175 model_add_other (GtkFileChooserButton *button)
2176 {
2177   GtkListStore *store;
2178   GtkTreeIter iter;
2179   gint pos;
2180
2181   store = GTK_LIST_STORE (button->priv->model);
2182   pos = model_get_type_position (button, ROW_TYPE_OTHER_SEPARATOR);
2183
2184   gtk_list_store_insert (store, &iter, pos);
2185   gtk_list_store_set (store, &iter,
2186                       ICON_COLUMN, NULL,
2187                       DISPLAY_NAME_COLUMN, NULL,
2188                       TYPE_COLUMN, ROW_TYPE_OTHER_SEPARATOR,
2189                       DATA_COLUMN, NULL,
2190                       IS_FOLDER_COLUMN, FALSE,
2191                       -1);
2192   button->priv->has_other_separator = TRUE;
2193   pos++;
2194
2195   gtk_list_store_insert (store, &iter, pos);
2196   gtk_list_store_set (store, &iter,
2197                       ICON_COLUMN, NULL,
2198                       DISPLAY_NAME_COLUMN, _("Other…"),
2199                       TYPE_COLUMN, ROW_TYPE_OTHER,
2200                       DATA_COLUMN, NULL,
2201                       IS_FOLDER_COLUMN, FALSE,
2202                       -1);
2203 }
2204
2205 static void
2206 model_add_empty_selection (GtkFileChooserButton *button)
2207 {
2208   GtkListStore *store;
2209   GtkTreeIter iter;
2210   gint pos;
2211
2212   store = GTK_LIST_STORE (button->priv->model);
2213   pos = model_get_type_position (button, ROW_TYPE_EMPTY_SELECTION);
2214
2215   gtk_list_store_insert (store, &iter, pos);
2216   gtk_list_store_set (store, &iter,
2217                       ICON_COLUMN, NULL,
2218                       DISPLAY_NAME_COLUMN, _(FALLBACK_DISPLAY_NAME),
2219                       TYPE_COLUMN, ROW_TYPE_EMPTY_SELECTION,
2220                       DATA_COLUMN, NULL,
2221                       IS_FOLDER_COLUMN, FALSE,
2222                       -1);
2223 }
2224
2225 static void
2226 model_remove_rows (GtkFileChooserButton *button,
2227                    gint                  pos,
2228                    gint                  n_rows)
2229 {
2230   GtkListStore *store;
2231
2232   if (!n_rows)
2233     return;
2234
2235   store = GTK_LIST_STORE (button->priv->model);
2236
2237   do
2238     {
2239       GtkTreeIter iter;
2240
2241       if (!gtk_tree_model_iter_nth_child (button->priv->model, &iter, NULL, pos))
2242         g_assert_not_reached ();
2243
2244       model_free_row_data (button, &iter);
2245       gtk_list_store_remove (store, &iter);
2246       n_rows--;
2247     }
2248   while (n_rows);
2249 }
2250
2251 /* Filter Model */
2252 static gboolean
2253 test_if_file_is_visible (GtkFileSystem *fs,
2254                          GFile         *file,
2255                          gboolean       local_only,
2256                          gboolean       is_folder)
2257 {
2258   if (!file)
2259     return FALSE;
2260
2261   if (local_only && !_gtk_file_has_native_path (file))
2262     return FALSE;
2263
2264   if (!is_folder)
2265     return FALSE;
2266
2267   return TRUE;
2268 }
2269
2270 static gboolean
2271 filter_model_visible_func (GtkTreeModel *model,
2272                            GtkTreeIter  *iter,
2273                            gpointer      user_data)
2274 {
2275   GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (user_data);
2276   GtkFileChooserButtonPrivate *priv = button->priv;
2277   gchar type;
2278   gpointer data;
2279   gboolean local_only, retval, is_folder;
2280
2281   type = ROW_TYPE_INVALID;
2282   data = NULL;
2283   local_only = gtk_file_chooser_get_local_only (GTK_FILE_CHOOSER (priv->dialog));
2284
2285   gtk_tree_model_get (model, iter,
2286                       TYPE_COLUMN, &type,
2287                       DATA_COLUMN, &data,
2288                       IS_FOLDER_COLUMN, &is_folder,
2289                       -1);
2290
2291   switch (type)
2292     {
2293     case ROW_TYPE_CURRENT_FOLDER:
2294       retval = TRUE;
2295       break;
2296     case ROW_TYPE_SPECIAL:
2297     case ROW_TYPE_SHORTCUT:
2298     case ROW_TYPE_BOOKMARK:
2299       retval = test_if_file_is_visible (priv->fs, data, local_only, is_folder);
2300       break;
2301     case ROW_TYPE_VOLUME:
2302       {
2303         retval = TRUE;
2304         if (local_only)
2305           {
2306             if (_gtk_file_system_volume_is_mounted (data))
2307               {
2308                 GFile *base_file;
2309
2310                 base_file = _gtk_file_system_volume_get_root (data);
2311
2312                 if (base_file)
2313                   {
2314                     if (!_gtk_file_has_native_path (base_file))
2315                       retval = FALSE;
2316                     g_object_unref (base_file);
2317                   }
2318                 else
2319                   retval = FALSE;
2320               }
2321           }
2322       }
2323       break;
2324     case ROW_TYPE_EMPTY_SELECTION:
2325       {
2326         gboolean popup_shown;
2327
2328         g_object_get (priv->combo_box,
2329                       "popup-shown", &popup_shown,
2330                       NULL);
2331
2332         if (popup_shown)
2333           retval = FALSE;
2334         else
2335           {
2336             GFile *selected;
2337
2338             /* When the combo box is not popped up... */
2339
2340             selected = get_selected_file (button);
2341             if (selected)
2342               retval = FALSE; /* ... nonempty selection means the ROW_TYPE_EMPTY_SELECTION is *not* visible... */
2343             else
2344               retval = TRUE;  /* ... and empty selection means the ROW_TYPE_EMPTY_SELECTION *is* visible */
2345
2346             if (selected)
2347               g_object_unref (selected);
2348           }
2349
2350         break;
2351       }
2352     default:
2353       retval = TRUE;
2354       break;
2355     }
2356
2357   return retval;
2358 }
2359
2360 /* Combo Box */
2361 static void
2362 name_cell_data_func (GtkCellLayout   *layout,
2363                      GtkCellRenderer *cell,
2364                      GtkTreeModel    *model,
2365                      GtkTreeIter     *iter,
2366                      gpointer         user_data)
2367 {
2368   gchar type;
2369
2370   type = 0;
2371   gtk_tree_model_get (model, iter,
2372                       TYPE_COLUMN, &type,
2373                       -1);
2374
2375   if (type == ROW_TYPE_CURRENT_FOLDER)
2376     g_object_set (cell, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
2377   else
2378     g_object_set (cell, "ellipsize", PANGO_ELLIPSIZE_NONE, NULL);
2379 }
2380
2381 static gboolean
2382 combo_box_row_separator_func (GtkTreeModel *model,
2383                               GtkTreeIter  *iter,
2384                               gpointer      user_data)
2385 {
2386   gchar type = ROW_TYPE_INVALID;
2387
2388   gtk_tree_model_get (model, iter, TYPE_COLUMN, &type, -1);
2389
2390   return (type == ROW_TYPE_BOOKMARK_SEPARATOR ||
2391           type == ROW_TYPE_CURRENT_FOLDER_SEPARATOR ||
2392           type == ROW_TYPE_OTHER_SEPARATOR);
2393 }
2394
2395 static void
2396 select_combo_box_row_no_notify (GtkFileChooserButton *button, int pos)
2397 {
2398   GtkFileChooserButtonPrivate *priv = button->priv;
2399   GtkTreeIter iter, filter_iter;
2400
2401   gtk_tree_model_iter_nth_child (priv->model, &iter, NULL, pos);
2402   gtk_tree_model_filter_convert_child_iter_to_iter (GTK_TREE_MODEL_FILTER (priv->filter_model),
2403                                                     &filter_iter, &iter);
2404
2405   g_signal_handler_block (priv->combo_box, priv->combo_box_changed_id);
2406   gtk_combo_box_set_active_iter (GTK_COMBO_BOX (priv->combo_box), &filter_iter);
2407   g_signal_handler_unblock (priv->combo_box, priv->combo_box_changed_id);
2408 }
2409
2410 static void
2411 update_combo_box (GtkFileChooserButton *button)
2412 {
2413   GtkFileChooserButtonPrivate *priv = button->priv;
2414   GFile *file;
2415   GtkTreeIter iter;
2416   gboolean row_found;
2417
2418   file = get_selected_file (button);
2419
2420   row_found = FALSE;
2421
2422   gtk_tree_model_get_iter_first (priv->filter_model, &iter);
2423
2424   do
2425     {
2426       gchar type;
2427       gpointer data;
2428
2429       type = ROW_TYPE_INVALID;
2430       data = NULL;
2431
2432       gtk_tree_model_get (priv->filter_model, &iter,
2433                           TYPE_COLUMN, &type,
2434                           DATA_COLUMN, &data,
2435                           -1);
2436
2437       switch (type)
2438         {
2439         case ROW_TYPE_SPECIAL:
2440         case ROW_TYPE_SHORTCUT:
2441         case ROW_TYPE_BOOKMARK:
2442         case ROW_TYPE_CURRENT_FOLDER:
2443           row_found = (file && g_file_equal (data, file));
2444           break;
2445         case ROW_TYPE_VOLUME:
2446           {
2447             GFile *base_file;
2448
2449             base_file = _gtk_file_system_volume_get_root (data);
2450             if (base_file)
2451               {
2452                 row_found = (file && g_file_equal (base_file, file));
2453                 g_object_unref (base_file);
2454               }
2455           }
2456           break;
2457         default:
2458           row_found = FALSE;
2459           break;
2460         }
2461
2462       if (row_found)
2463         {
2464           g_signal_handler_block (priv->combo_box, priv->combo_box_changed_id);
2465           gtk_combo_box_set_active_iter (GTK_COMBO_BOX (priv->combo_box),
2466                                          &iter);
2467           g_signal_handler_unblock (priv->combo_box,
2468                                     priv->combo_box_changed_id);
2469         }
2470     }
2471   while (!row_found && gtk_tree_model_iter_next (priv->filter_model, &iter));
2472
2473   if (!row_found)
2474     {
2475       gint pos;
2476
2477       /* If it hasn't been found already, update & select the current-folder row. */
2478       if (file)
2479         {
2480           model_update_current_folder (button, file);
2481           pos = model_get_type_position (button, ROW_TYPE_CURRENT_FOLDER);
2482         }
2483       else
2484         {
2485           /* No selection; switch to that row */
2486
2487           pos = model_get_type_position (button, ROW_TYPE_EMPTY_SELECTION);
2488         }
2489
2490       gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (priv->filter_model));
2491
2492       select_combo_box_row_no_notify (button, pos);
2493     }
2494
2495   if (file)
2496     g_object_unref (file);
2497 }
2498
2499 /* Button */
2500 static void
2501 update_label_get_info_cb (GCancellable *cancellable,
2502                           GFileInfo    *info,
2503                           const GError *error,
2504                           gpointer      data)
2505 {
2506   gboolean cancelled = g_cancellable_is_cancelled (cancellable);
2507   GdkPixbuf *pixbuf;
2508   GtkFileChooserButton *button = data;
2509   GtkFileChooserButtonPrivate *priv = button->priv;
2510
2511   if (cancellable != priv->update_button_cancellable)
2512     goto out;
2513
2514   priv->update_button_cancellable = NULL;
2515
2516   if (cancelled || error)
2517     goto out;
2518
2519   gtk_label_set_text (GTK_LABEL (priv->label), g_file_info_get_display_name (info));
2520
2521   pixbuf = _gtk_file_info_render_icon (info, GTK_WIDGET (priv->image), priv->icon_size);
2522
2523   if (!pixbuf)
2524     pixbuf = gtk_icon_theme_load_icon (get_icon_theme (GTK_WIDGET (priv->image)),
2525                                        FALLBACK_ICON_NAME,
2526                                        priv->icon_size, 0, NULL);
2527
2528   gtk_image_set_from_pixbuf (GTK_IMAGE (priv->image), pixbuf);
2529   if (pixbuf)
2530     g_object_unref (pixbuf);
2531
2532 out:
2533   emit_selection_changed_if_changing_selection (button);
2534
2535   g_object_unref (button);
2536   g_object_unref (cancellable);
2537 }
2538
2539 static void
2540 update_label_and_image (GtkFileChooserButton *button)
2541 {
2542   GtkFileChooserButtonPrivate *priv = button->priv;
2543   gchar *label_text;
2544   GFile *file;
2545   gboolean done_changing_selection;
2546
2547   file = get_selected_file (button);
2548
2549   label_text = NULL;
2550   done_changing_selection = FALSE;
2551
2552   if (priv->update_button_cancellable)
2553     {
2554       g_cancellable_cancel (priv->update_button_cancellable);
2555       priv->update_button_cancellable = NULL;
2556     }
2557
2558   if (file)
2559     {
2560       GtkFileSystemVolume *volume = NULL;
2561
2562       volume = _gtk_file_system_get_volume_for_file (priv->fs, file);
2563       if (volume)
2564         {
2565           GFile *base_file;
2566
2567           base_file = _gtk_file_system_volume_get_root (volume);
2568           if (base_file && g_file_equal (base_file, file))
2569             {
2570               GdkPixbuf *pixbuf;
2571
2572               label_text = _gtk_file_system_volume_get_display_name (volume);
2573               pixbuf = _gtk_file_system_volume_render_icon (volume,
2574                                                             GTK_WIDGET (button),
2575                                                             priv->icon_size,
2576                                                             NULL);
2577               gtk_image_set_from_pixbuf (GTK_IMAGE (priv->image), pixbuf);
2578               if (pixbuf)
2579                 g_object_unref (pixbuf);
2580             }
2581
2582           if (base_file)
2583             g_object_unref (base_file);
2584
2585           _gtk_file_system_volume_unref (volume);
2586
2587           if (label_text)
2588             {
2589               done_changing_selection = TRUE;
2590               goto out;
2591             }
2592         }
2593
2594       if (g_file_is_native (file))
2595         {
2596           priv->update_button_cancellable =
2597             _gtk_file_system_get_info (priv->fs, file,
2598                                        "standard::icon,standard::display-name",
2599                                        update_label_get_info_cb,
2600                                        g_object_ref (button));
2601         }
2602       else
2603         {
2604           GdkPixbuf *pixbuf;
2605
2606           label_text = _gtk_file_system_get_bookmark_label (button->priv->fs, file);
2607           pixbuf = gtk_icon_theme_load_icon (get_icon_theme (GTK_WIDGET (priv->image)),
2608                                              "text-x-generic",
2609                                              priv->icon_size, 0, NULL);
2610           gtk_image_set_from_pixbuf (GTK_IMAGE (priv->image), pixbuf);
2611           if (pixbuf)
2612             g_object_unref (pixbuf);
2613
2614           done_changing_selection = TRUE;
2615         }
2616     }
2617   else
2618     {
2619       /* We know the selection is empty */
2620       done_changing_selection = TRUE;
2621     }
2622
2623 out:
2624
2625   if (file)
2626     g_object_unref (file);
2627
2628   if (label_text)
2629     {
2630       gtk_label_set_text (GTK_LABEL (priv->label), label_text);
2631       g_free (label_text);
2632     }
2633   else
2634     {
2635       gtk_label_set_text (GTK_LABEL (priv->label), _(FALLBACK_DISPLAY_NAME));
2636       gtk_image_set_from_pixbuf (GTK_IMAGE (priv->image), NULL);
2637     }
2638
2639   if (done_changing_selection)
2640     emit_selection_changed_if_changing_selection (button);
2641 }
2642
2643
2644 /* ************************ *
2645  *  Child Object Callbacks  *
2646  * ************************ */
2647
2648 /* File System */
2649 static void
2650 fs_volumes_changed_cb (GtkFileSystem *fs,
2651                        gpointer       user_data)
2652 {
2653   GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (user_data);
2654   GtkFileChooserButtonPrivate *priv = button->priv;
2655   GSList *volumes;
2656
2657   model_remove_rows (user_data,
2658                      model_get_type_position (user_data, ROW_TYPE_VOLUME),
2659                      priv->n_volumes);
2660
2661   priv->n_volumes = 0;
2662
2663   volumes = _gtk_file_system_list_volumes (fs);
2664   model_add_volumes (user_data, volumes);
2665   g_slist_free (volumes);
2666
2667   gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (priv->filter_model));
2668
2669   update_label_and_image (user_data);
2670   update_combo_box (user_data);
2671 }
2672
2673 static void
2674 fs_bookmarks_changed_cb (GtkFileSystem *fs,
2675                          gpointer       user_data)
2676 {
2677   GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (user_data);
2678   GtkFileChooserButtonPrivate *priv = button->priv;
2679   GSList *bookmarks;
2680
2681   bookmarks = _gtk_file_system_list_bookmarks (fs);
2682   model_remove_rows (user_data,
2683                      model_get_type_position (user_data,
2684                                               ROW_TYPE_BOOKMARK_SEPARATOR),
2685                      (priv->n_bookmarks + priv->has_bookmark_separator));
2686   priv->has_bookmark_separator = FALSE;
2687   priv->n_bookmarks = 0;
2688   model_add_bookmarks (user_data, bookmarks);
2689   g_slist_foreach (bookmarks, (GFunc) g_object_unref, NULL);
2690   g_slist_free (bookmarks);
2691
2692   gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (priv->filter_model));
2693
2694   update_label_and_image (user_data);
2695   update_combo_box (user_data);
2696 }
2697
2698 static void
2699 save_inactive_state (GtkFileChooserButton *button)
2700 {
2701   GtkFileChooserButtonPrivate *priv = button->priv;
2702
2703   if (priv->current_folder_while_inactive)
2704     g_object_unref (priv->current_folder_while_inactive);
2705
2706   if (priv->selection_while_inactive)
2707     g_object_unref (priv->selection_while_inactive);
2708
2709   priv->current_folder_while_inactive = gtk_file_chooser_get_current_folder_file (GTK_FILE_CHOOSER (priv->dialog));
2710   priv->selection_while_inactive = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (priv->dialog));
2711 }
2712
2713 static void
2714 restore_inactive_state (GtkFileChooserButton *button)
2715 {
2716   GtkFileChooserButtonPrivate *priv = button->priv;
2717
2718   if (priv->current_folder_while_inactive)
2719     gtk_file_chooser_set_current_folder_file (GTK_FILE_CHOOSER (priv->dialog), priv->current_folder_while_inactive, NULL);
2720
2721   if (priv->selection_while_inactive)
2722     gtk_file_chooser_select_file (GTK_FILE_CHOOSER (priv->dialog), priv->selection_while_inactive, NULL);
2723   else
2724     gtk_file_chooser_unselect_all (GTK_FILE_CHOOSER (priv->dialog));
2725 }
2726
2727 /* Dialog */
2728 static void
2729 open_dialog (GtkFileChooserButton *button)
2730 {
2731   GtkFileChooserButtonPrivate *priv = button->priv;
2732
2733   /* Setup the dialog parent to be chooser button's toplevel, and be modal
2734      as needed. */
2735   if (!gtk_widget_get_visible (priv->dialog))
2736     {
2737       GtkWidget *toplevel;
2738
2739       toplevel = gtk_widget_get_toplevel (GTK_WIDGET (button));
2740
2741       if (gtk_widget_is_toplevel (toplevel) && GTK_IS_WINDOW (toplevel))
2742         {
2743           if (GTK_WINDOW (toplevel) != gtk_window_get_transient_for (GTK_WINDOW (priv->dialog)))
2744             gtk_window_set_transient_for (GTK_WINDOW (priv->dialog),
2745                                           GTK_WINDOW (toplevel));
2746
2747           gtk_window_set_modal (GTK_WINDOW (priv->dialog),
2748                                 gtk_window_get_modal (GTK_WINDOW (toplevel)));
2749         }
2750     }
2751
2752   if (!priv->active)
2753     {
2754       restore_inactive_state (button);
2755       priv->active = TRUE;
2756     }
2757
2758   gtk_widget_set_sensitive (priv->combo_box, FALSE);
2759   gtk_window_present (GTK_WINDOW (priv->dialog));
2760 }
2761
2762 /* Combo Box */
2763 static void
2764 combo_box_changed_cb (GtkComboBox *combo_box,
2765                       gpointer     user_data)
2766 {
2767   GtkTreeIter iter;
2768
2769   if (gtk_combo_box_get_active_iter (combo_box, &iter))
2770     {
2771       GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (user_data);
2772       GtkFileChooserButtonPrivate *priv = button->priv;
2773       gchar type;
2774       gpointer data;
2775
2776       type = ROW_TYPE_INVALID;
2777       data = NULL;
2778
2779       gtk_tree_model_get (priv->filter_model, &iter,
2780                           TYPE_COLUMN, &type,
2781                           DATA_COLUMN, &data,
2782                           -1);
2783
2784       switch (type)
2785         {
2786         case ROW_TYPE_SPECIAL:
2787         case ROW_TYPE_SHORTCUT:
2788         case ROW_TYPE_BOOKMARK:
2789         case ROW_TYPE_CURRENT_FOLDER:
2790           if (data)
2791             gtk_file_chooser_button_select_file (GTK_FILE_CHOOSER (button), data, NULL);
2792           break;
2793         case ROW_TYPE_VOLUME:
2794           {
2795             GFile *base_file;
2796
2797             base_file = _gtk_file_system_volume_get_root (data);
2798             if (base_file)
2799               {
2800                 gtk_file_chooser_button_select_file (GTK_FILE_CHOOSER (button), base_file, NULL);
2801                 g_object_unref (base_file);
2802               }
2803           }
2804           break;
2805         case ROW_TYPE_OTHER:
2806           open_dialog (user_data);
2807           break;
2808         default:
2809           break;
2810         }
2811     }
2812 }
2813
2814 /* Calback for the "notify::popup-shown" signal on the combo box.
2815  * When the combo is popped up, we don't want the ROW_TYPE_EMPTY_SELECTION to be visible
2816  * at all; otherwise we would be showing a "(None)" item in the combo box's popup.
2817  *
2818  * However, when the combo box is *not* popped up, we want the empty-selection row
2819  * to be visible depending on the selection.
2820  *
2821  * Since all that is done through the filter_model_visible_func(), this means
2822  * that we need to refilter the model when the combo box pops up - hence the
2823  * present signal handler.
2824  */
2825 static void
2826 combo_box_notify_popup_shown_cb (GObject    *object,
2827                                  GParamSpec *pspec,
2828                                  gpointer    user_data)
2829 {
2830   GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (user_data);
2831   GtkFileChooserButtonPrivate *priv = button->priv;
2832   gboolean popup_shown;
2833
2834   g_object_get (priv->combo_box,
2835                 "popup-shown", &popup_shown,
2836                 NULL);
2837
2838   /* Indicate that the ROW_TYPE_EMPTY_SELECTION will change visibility... */
2839   gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (priv->filter_model));
2840
2841   /* If the combo box popup got dismissed, go back to showing the ROW_TYPE_EMPTY_SELECTION if needed */
2842   if (!popup_shown)
2843     {
2844       GFile *selected = get_selected_file (button);
2845
2846       if (!selected)
2847         {
2848           int pos;
2849
2850           pos = model_get_type_position (button, ROW_TYPE_EMPTY_SELECTION);
2851           select_combo_box_row_no_notify (button, pos);
2852         }
2853       else
2854         g_object_unref (selected);
2855     }
2856 }
2857
2858 /* Button */
2859 static void
2860 button_clicked_cb (GtkButton *real_button,
2861                    gpointer   user_data)
2862 {
2863   open_dialog (user_data);
2864 }
2865
2866 /* Dialog */
2867
2868 static void
2869 dialog_update_preview_cb (GtkFileChooser *dialog,
2870                           gpointer        user_data)
2871 {
2872   g_signal_emit_by_name (user_data, "update-preview");
2873 }
2874
2875 static void
2876 dialog_notify_cb (GObject    *dialog,
2877                   GParamSpec *pspec,
2878                   gpointer    user_data)
2879 {
2880   gpointer iface;
2881
2882   iface = g_type_interface_peek (g_type_class_peek (G_OBJECT_TYPE (dialog)),
2883                                  GTK_TYPE_FILE_CHOOSER);
2884   if (g_object_interface_find_property (iface, pspec->name))
2885     g_object_notify (user_data, pspec->name);
2886
2887   if (g_ascii_strcasecmp (pspec->name, "local-only") == 0)
2888     {
2889       GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (user_data);
2890       GtkFileChooserButtonPrivate *priv = button->priv;
2891
2892       if (priv->has_current_folder)
2893         {
2894           GtkTreeIter iter;
2895           gint pos;
2896           gpointer data;
2897
2898           pos = model_get_type_position (user_data,
2899                                          ROW_TYPE_CURRENT_FOLDER);
2900           gtk_tree_model_iter_nth_child (priv->model, &iter, NULL, pos);
2901
2902           data = NULL;
2903           gtk_tree_model_get (priv->model, &iter, DATA_COLUMN, &data, -1);
2904
2905           /* If the path isn't local but we're in local-only mode now, remove
2906            * the custom-folder row */
2907           if (data && _gtk_file_has_native_path (G_FILE (data)) &&
2908               gtk_file_chooser_get_local_only (GTK_FILE_CHOOSER (priv->dialog)))
2909             {
2910               pos--;
2911               model_remove_rows (user_data, pos, 2);
2912             }
2913         }
2914
2915       gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (priv->filter_model));
2916       update_combo_box (user_data);
2917     }
2918 }
2919
2920 static gboolean
2921 dialog_delete_event_cb (GtkWidget *dialog,
2922                         GdkEvent  *event,
2923                         gpointer   user_data)
2924 {
2925   g_signal_emit_by_name (dialog, "response", GTK_RESPONSE_DELETE_EVENT);
2926
2927   return TRUE;
2928 }
2929
2930 static void
2931 dialog_response_cb (GtkDialog *dialog,
2932                     gint       response,
2933                     gpointer   user_data)
2934 {
2935   GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (user_data);
2936   GtkFileChooserButtonPrivate *priv = button->priv;
2937
2938   if (response == GTK_RESPONSE_ACCEPT ||
2939       response == GTK_RESPONSE_OK)
2940     {
2941       save_inactive_state (button);
2942
2943       g_signal_emit_by_name (button, "current-folder-changed");
2944       g_signal_emit_by_name (button, "selection-changed");
2945     }
2946   else
2947     {
2948       restore_inactive_state (button);
2949     }
2950
2951   if (priv->active)
2952     priv->active = FALSE;
2953
2954   update_label_and_image (button);
2955   update_combo_box (button);
2956
2957   gtk_widget_set_sensitive (priv->combo_box, TRUE);
2958   gtk_widget_hide (priv->dialog);
2959
2960   if (response == GTK_RESPONSE_ACCEPT ||
2961       response == GTK_RESPONSE_OK)
2962     g_signal_emit (button, file_chooser_button_signals[FILE_SET], 0);
2963 }
2964
2965
2966 /* ************************************************************************** *
2967  *  Public API                                                                *
2968  * ************************************************************************** */
2969
2970 /**
2971  * gtk_file_chooser_button_new:
2972  * @title: the title of the browse dialog.
2973  * @action: the open mode for the widget.
2974  *
2975  * Creates a new file-selecting button widget.
2976  *
2977  * Returns: a new button widget.
2978  *
2979  * Since: 2.6
2980  **/
2981 GtkWidget *
2982 gtk_file_chooser_button_new (const gchar          *title,
2983                              GtkFileChooserAction  action)
2984 {
2985   g_return_val_if_fail (action == GTK_FILE_CHOOSER_ACTION_OPEN ||
2986                         action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, NULL);
2987
2988   return g_object_new (GTK_TYPE_FILE_CHOOSER_BUTTON,
2989                        "action", action,
2990                        "title", (title ? title : _(DEFAULT_TITLE)),
2991                        NULL);
2992 }
2993
2994 /**
2995  * gtk_file_chooser_button_new_with_dialog:
2996  * @dialog: the widget to use as dialog
2997  *
2998  * Creates a #GtkFileChooserButton widget which uses @dialog as its
2999  * file-picking window.
3000  *
3001  * Note that @dialog must be a #GtkDialog (or subclass) which
3002  * implements the #GtkFileChooser interface and must not have
3003  * %GTK_DIALOG_DESTROY_WITH_PARENT set.
3004  *
3005  * Also note that the dialog needs to have its confirmative button
3006  * added with response %GTK_RESPONSE_ACCEPT or %GTK_RESPONSE_OK in
3007  * order for the button to take over the file selected in the dialog.
3008  *
3009  * Returns: a new button widget.
3010  *
3011  * Since: 2.6
3012  **/
3013 GtkWidget *
3014 gtk_file_chooser_button_new_with_dialog (GtkWidget *dialog)
3015 {
3016   g_return_val_if_fail (GTK_IS_FILE_CHOOSER (dialog) && GTK_IS_DIALOG (dialog), NULL);
3017
3018   return g_object_new (GTK_TYPE_FILE_CHOOSER_BUTTON,
3019                        "dialog", dialog,
3020                        NULL);
3021 }
3022
3023 /**
3024  * gtk_file_chooser_button_set_title:
3025  * @button: the button widget to modify.
3026  * @title: the new browse dialog title.
3027  *
3028  * Modifies the @title of the browse dialog used by @button.
3029  *
3030  * Since: 2.6
3031  **/
3032 void
3033 gtk_file_chooser_button_set_title (GtkFileChooserButton *button,
3034                                    const gchar          *title)
3035 {
3036   g_return_if_fail (GTK_IS_FILE_CHOOSER_BUTTON (button));
3037
3038   gtk_window_set_title (GTK_WINDOW (button->priv->dialog), title);
3039   g_object_notify (G_OBJECT (button), "title");
3040 }
3041
3042 /**
3043  * gtk_file_chooser_button_get_title:
3044  * @button: the button widget to examine.
3045  *
3046  * Retrieves the title of the browse dialog used by @button. The returned value
3047  * should not be modified or freed.
3048  *
3049  * Returns: a pointer to the browse dialog's title.
3050  *
3051  * Since: 2.6
3052  **/
3053 const gchar *
3054 gtk_file_chooser_button_get_title (GtkFileChooserButton *button)
3055 {
3056   g_return_val_if_fail (GTK_IS_FILE_CHOOSER_BUTTON (button), NULL);
3057
3058   return gtk_window_get_title (GTK_WINDOW (button->priv->dialog));
3059 }
3060
3061 /**
3062  * gtk_file_chooser_button_get_width_chars:
3063  * @button: the button widget to examine.
3064  *
3065  * Retrieves the width in characters of the @button widget's entry and/or label.
3066  *
3067  * Returns: an integer width (in characters) that the button will use to size itself.
3068  *
3069  * Since: 2.6
3070  **/
3071 gint
3072 gtk_file_chooser_button_get_width_chars (GtkFileChooserButton *button)
3073 {
3074   g_return_val_if_fail (GTK_IS_FILE_CHOOSER_BUTTON (button), -1);
3075
3076   return gtk_label_get_width_chars (GTK_LABEL (button->priv->label));
3077 }
3078
3079 /**
3080  * gtk_file_chooser_button_set_width_chars:
3081  * @button: the button widget to examine.
3082  * @n_chars: the new width, in characters.
3083  *
3084  * Sets the width (in characters) that @button will use to @n_chars.
3085  *
3086  * Since: 2.6
3087  **/
3088 void
3089 gtk_file_chooser_button_set_width_chars (GtkFileChooserButton *button,
3090                                          gint                  n_chars)
3091 {
3092   g_return_if_fail (GTK_IS_FILE_CHOOSER_BUTTON (button));
3093
3094   gtk_label_set_width_chars (GTK_LABEL (button->priv->label), n_chars);
3095   g_object_notify (G_OBJECT (button), "width-chars");
3096 }
3097
3098 /**
3099  * gtk_file_chooser_button_set_focus_on_click:
3100  * @button: a #GtkFileChooserButton
3101  * @focus_on_click: whether the button grabs focus when clicked with the mouse
3102  *
3103  * Sets whether the button will grab focus when it is clicked with the mouse.
3104  * Making mouse clicks not grab focus is useful in places like toolbars where
3105  * you don't want the keyboard focus removed from the main area of the
3106  * application.
3107  *
3108  * Since: 2.10
3109  **/
3110 void
3111 gtk_file_chooser_button_set_focus_on_click (GtkFileChooserButton *button,
3112                                             gboolean              focus_on_click)
3113 {
3114   GtkFileChooserButtonPrivate *priv;
3115
3116   g_return_if_fail (GTK_IS_FILE_CHOOSER_BUTTON (button));
3117
3118   priv = button->priv;
3119
3120   focus_on_click = focus_on_click != FALSE;
3121
3122   if (priv->focus_on_click != focus_on_click)
3123     {
3124       priv->focus_on_click = focus_on_click;
3125       gtk_button_set_focus_on_click (GTK_BUTTON (priv->button), focus_on_click);
3126       gtk_combo_box_set_focus_on_click (GTK_COMBO_BOX (priv->combo_box), focus_on_click);
3127
3128       g_object_notify (G_OBJECT (button), "focus-on-click");
3129     }
3130 }
3131
3132 /**
3133  * gtk_file_chooser_button_get_focus_on_click:
3134  * @button: a #GtkFileChooserButton
3135  *
3136  * Returns whether the button grabs focus when it is clicked with the mouse.
3137  * See gtk_file_chooser_button_set_focus_on_click().
3138  *
3139  * Return value: %TRUE if the button grabs focus when it is clicked with
3140  *               the mouse.
3141  *
3142  * Since: 2.10
3143  **/
3144 gboolean
3145 gtk_file_chooser_button_get_focus_on_click (GtkFileChooserButton *button)
3146 {
3147   g_return_val_if_fail (GTK_IS_FILE_CHOOSER_BUTTON (button), FALSE);
3148
3149   return button->priv->focus_on_click;
3150 }