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