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