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