]> Pileus Git - ~andy/gtk/blob - gtk/gtkfilechooserbutton.c
gtk/gtkaccellabel.c gtk/gtkcalendar.c gtk/gtkclist.c gtk/gtkentry.c
[~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   GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->map (widget);
1163 }
1164
1165 static gboolean
1166 gtk_file_chooser_button_mnemonic_activate (GtkWidget *widget,
1167                                            gboolean   group_cycling)
1168 {
1169   GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (widget);
1170   GtkFileChooserButtonPrivate *priv = button->priv;
1171
1172   switch (gtk_file_chooser_get_action (GTK_FILE_CHOOSER (priv->dialog)))
1173     {
1174     case GTK_FILE_CHOOSER_ACTION_OPEN:
1175       gtk_widget_grab_focus (priv->button);
1176       break;
1177     case GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER:
1178       return gtk_widget_mnemonic_activate (priv->combo_box, group_cycling);
1179       break;
1180     default:
1181       g_assert_not_reached ();
1182       break;
1183     }
1184
1185   return TRUE;
1186 }
1187
1188 /* Changes the icons wherever it is needed */
1189 struct ChangeIconThemeData
1190 {
1191   GtkFileChooserButton *button;
1192   GtkTreeRowReference *row_ref;
1193 };
1194
1195 static void
1196 change_icon_theme_get_info_cb (GCancellable *cancellable,
1197                                GFileInfo    *info,
1198                                const GError *error,
1199                                gpointer      user_data)
1200 {
1201   gboolean cancelled = g_cancellable_is_cancelled (cancellable);
1202   GdkPixbuf *pixbuf;
1203   struct ChangeIconThemeData *data = user_data;
1204
1205   if (!g_slist_find (data->button->priv->change_icon_theme_cancellables, cancellable))
1206     goto out;
1207
1208   data->button->priv->change_icon_theme_cancellables =
1209     g_slist_remove (data->button->priv->change_icon_theme_cancellables, cancellable);
1210
1211   if (cancelled || error)
1212     goto out;
1213
1214   pixbuf = _gtk_file_info_render_icon (info, GTK_WIDGET (data->button), data->button->priv->icon_size);
1215
1216   if (pixbuf)
1217     {
1218       gint width = 0;
1219       GtkTreeIter iter;
1220       GtkTreePath *path;
1221
1222       width = MAX (width, gdk_pixbuf_get_width (pixbuf));
1223
1224       path = gtk_tree_row_reference_get_path (data->row_ref);
1225       if (path) 
1226         {
1227           gtk_tree_model_get_iter (data->button->priv->model, &iter, path);
1228           gtk_tree_path_free (path);
1229
1230           gtk_list_store_set (GTK_LIST_STORE (data->button->priv->model), &iter,
1231                               ICON_COLUMN, pixbuf,
1232                               -1);
1233
1234           g_object_set (data->button->priv->icon_cell,
1235                         "width", width,
1236                         NULL);
1237         }
1238       g_object_unref (pixbuf);
1239     }
1240
1241 out:
1242   g_object_unref (data->button);
1243   gtk_tree_row_reference_free (data->row_ref);
1244   g_free (data);
1245
1246   g_object_unref (cancellable);
1247 }
1248
1249 static void
1250 change_icon_theme (GtkFileChooserButton *button)
1251 {
1252   GtkFileChooserButtonPrivate *priv = button->priv;
1253   GtkSettings *settings;
1254   GtkIconTheme *theme;
1255   GtkTreeIter iter;
1256   GSList *l;
1257   gint width = 0, height = 0;
1258
1259   for (l = button->priv->change_icon_theme_cancellables; l; l = l->next)
1260     {
1261       GCancellable *cancellable = G_CANCELLABLE (l->data);
1262       g_cancellable_cancel (cancellable);
1263     }
1264   g_slist_free (button->priv->change_icon_theme_cancellables);
1265   button->priv->change_icon_theme_cancellables = NULL;
1266
1267   settings = gtk_settings_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (button)));
1268
1269   if (gtk_icon_size_lookup_for_settings (settings, GTK_ICON_SIZE_MENU,
1270                                          &width, &height))
1271     priv->icon_size = MAX (width, height);
1272   else
1273     priv->icon_size = FALLBACK_ICON_SIZE;
1274
1275   update_label_and_image (button);
1276
1277   gtk_tree_model_get_iter_first (priv->model, &iter);
1278
1279   theme = get_icon_theme (GTK_WIDGET (button));
1280
1281   do
1282     {
1283       GdkPixbuf *pixbuf;
1284       gchar type;
1285       gpointer data;
1286
1287       type = ROW_TYPE_INVALID;
1288       gtk_tree_model_get (priv->model, &iter,
1289                           TYPE_COLUMN, &type,
1290                           DATA_COLUMN, &data,
1291                           -1);
1292
1293       switch (type)
1294         {
1295         case ROW_TYPE_SPECIAL:
1296         case ROW_TYPE_SHORTCUT:
1297         case ROW_TYPE_BOOKMARK:
1298         case ROW_TYPE_CURRENT_FOLDER:
1299           if (data)
1300             {
1301               if (g_file_is_native (G_FILE (data)))
1302                 {
1303                   GtkTreePath *path;
1304                   GCancellable *cancellable;
1305                   struct ChangeIconThemeData *info;               
1306                   
1307                   info = g_new0 (struct ChangeIconThemeData, 1);
1308                   info->button = g_object_ref (button);
1309                   path = gtk_tree_model_get_path (priv->model, &iter);
1310                   info->row_ref = gtk_tree_row_reference_new (priv->model, path);
1311                   gtk_tree_path_free (path);
1312
1313                   cancellable =
1314                     _gtk_file_system_get_info (priv->fs, data,
1315                                                "standard::icon",
1316                                                change_icon_theme_get_info_cb,
1317                                                info);
1318                   button->priv->change_icon_theme_cancellables =
1319                     g_slist_append (button->priv->change_icon_theme_cancellables, cancellable);
1320                   pixbuf = NULL;
1321                 }
1322               else
1323                 /* Don't call get_info for remote paths to avoid latency and
1324                  * auth dialogs.
1325                  * If we switch to a better bookmarks file format (XBEL), we
1326                  * should use mime info to get a better icon.
1327                  */
1328                 pixbuf = gtk_icon_theme_load_icon (theme, "folder-remote",
1329                                                    priv->icon_size, 0, NULL);
1330             }
1331           else
1332             pixbuf = gtk_icon_theme_load_icon (theme, FALLBACK_ICON_NAME,
1333                                                priv->icon_size, 0, NULL);
1334           break;
1335         case ROW_TYPE_VOLUME:
1336           if (data)
1337             pixbuf = _gtk_file_system_volume_render_icon (data,
1338                                                           GTK_WIDGET (button),
1339                                                           priv->icon_size,
1340                                                           NULL);
1341           else
1342             pixbuf = gtk_icon_theme_load_icon (theme, FALLBACK_ICON_NAME,
1343                                                priv->icon_size, 0, NULL);
1344           break;
1345         default:
1346           continue;
1347           break;
1348         }
1349
1350       if (pixbuf)
1351         width = MAX (width, gdk_pixbuf_get_width (pixbuf));
1352
1353       gtk_list_store_set (GTK_LIST_STORE (priv->model), &iter,
1354                           ICON_COLUMN, pixbuf,
1355                           -1);
1356
1357       if (pixbuf)
1358         g_object_unref (pixbuf);
1359     }
1360   while (gtk_tree_model_iter_next (priv->model, &iter));
1361
1362   g_object_set (button->priv->icon_cell,
1363                 "width", width,
1364                 NULL);
1365 }
1366
1367 static void
1368 gtk_file_chooser_button_style_set (GtkWidget *widget,
1369                                    GtkStyle  *old_style)
1370 {
1371   GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->style_set (widget,
1372                                                                       old_style);
1373
1374   if (gtk_widget_has_screen (widget))
1375     change_icon_theme (GTK_FILE_CHOOSER_BUTTON (widget));
1376 }
1377
1378 static void
1379 gtk_file_chooser_button_screen_changed (GtkWidget *widget,
1380                                         GdkScreen *old_screen)
1381 {
1382   if (GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->screen_changed)
1383     GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->screen_changed (widget,
1384                                                                              old_screen);
1385
1386   change_icon_theme (GTK_FILE_CHOOSER_BUTTON (widget));
1387 }
1388
1389
1390 /* ******************* *
1391  *  Utility Functions  *
1392  * ******************* */
1393
1394 /* General */
1395 static GtkIconTheme *
1396 get_icon_theme (GtkWidget *widget)
1397 {
1398   if (gtk_widget_has_screen (widget))
1399     return gtk_icon_theme_get_for_screen (gtk_widget_get_screen (widget));
1400
1401   return gtk_icon_theme_get_default ();
1402 }
1403
1404
1405 struct SetDisplayNameData
1406 {
1407   GtkFileChooserButton *button;
1408   char *label;
1409   GtkTreeRowReference *row_ref;
1410 };
1411
1412 static void
1413 set_info_get_info_cb (GCancellable *cancellable,
1414                       GFileInfo    *info,
1415                       const GError *error,
1416                       gpointer      callback_data)
1417 {
1418   gboolean cancelled = g_cancellable_is_cancelled (cancellable);
1419   GdkPixbuf *pixbuf;
1420   GtkTreePath *path;
1421   GtkTreeIter iter;
1422   GCancellable *model_cancellable;
1423   struct SetDisplayNameData *data = callback_data;
1424   gboolean is_folder;
1425
1426   if (!data->button->priv->model)
1427     /* button got destroyed */
1428     goto out;
1429
1430   path = gtk_tree_row_reference_get_path (data->row_ref);
1431   if (!path)
1432     /* Cancellable doesn't exist anymore in the model */
1433     goto out;
1434
1435   gtk_tree_model_get_iter (data->button->priv->model, &iter, path);
1436   gtk_tree_path_free (path);
1437
1438   /* Validate the cancellable */
1439   gtk_tree_model_get (data->button->priv->model, &iter,
1440                       CANCELLABLE_COLUMN, &model_cancellable,
1441                       -1);
1442   if (cancellable != model_cancellable)
1443     goto out;
1444
1445   gtk_list_store_set (GTK_LIST_STORE (data->button->priv->model), &iter,
1446                       CANCELLABLE_COLUMN, NULL,
1447                       -1);
1448
1449   if (cancelled || error)
1450     /* There was an error, leave the fallback name in there */
1451     goto out;
1452
1453   pixbuf = _gtk_file_info_render_icon (info, GTK_WIDGET (data->button), data->button->priv->icon_size);
1454
1455   if (!data->label)
1456     data->label = g_strdup (g_file_info_get_display_name (info));
1457
1458   is_folder = (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY);
1459
1460   gtk_list_store_set (GTK_LIST_STORE (data->button->priv->model), &iter,
1461                       ICON_COLUMN, pixbuf,
1462                       DISPLAY_NAME_COLUMN, data->label,
1463                       IS_FOLDER_COLUMN, is_folder,
1464                       -1);
1465
1466   if (pixbuf)
1467     g_object_unref (pixbuf);
1468
1469 out:
1470   g_object_unref (data->button);
1471   g_free (data->label);
1472   gtk_tree_row_reference_free (data->row_ref);
1473   g_free (data);
1474
1475   g_object_unref (cancellable);
1476 }
1477
1478 static void
1479 set_info_for_file_at_iter (GtkFileChooserButton *button,
1480                            GFile                *file,
1481                            GtkTreeIter          *iter)
1482 {
1483   struct SetDisplayNameData *data;
1484   GtkTreePath *tree_path;
1485   GCancellable *cancellable;
1486
1487   data = g_new0 (struct SetDisplayNameData, 1);
1488   data->button = g_object_ref (button);
1489   data->label = _gtk_file_system_get_bookmark_label (button->priv->fs, file);
1490
1491   tree_path = gtk_tree_model_get_path (button->priv->model, iter);
1492   data->row_ref = gtk_tree_row_reference_new (button->priv->model, tree_path);
1493   gtk_tree_path_free (tree_path);
1494
1495   cancellable = _gtk_file_system_get_info (button->priv->fs, file,
1496                                            "standard::type,standard::icon,standard::display-name",
1497                                            set_info_get_info_cb, data);
1498
1499   gtk_list_store_set (GTK_LIST_STORE (button->priv->model), iter,
1500                       CANCELLABLE_COLUMN, cancellable,
1501                       -1);
1502 }
1503
1504 /* Shortcuts Model */
1505 static gint
1506 model_get_type_position (GtkFileChooserButton *button,
1507                          RowType               row_type)
1508 {
1509   gint retval = 0;
1510
1511   if (row_type == ROW_TYPE_SPECIAL)
1512     return retval;
1513
1514   retval += button->priv->n_special;
1515
1516   if (row_type == ROW_TYPE_VOLUME)
1517     return retval;
1518
1519   retval += button->priv->n_volumes;
1520
1521   if (row_type == ROW_TYPE_SHORTCUT)
1522     return retval;
1523
1524   retval += button->priv->n_shortcuts;
1525
1526   if (row_type == ROW_TYPE_BOOKMARK_SEPARATOR)
1527     return retval;
1528
1529   retval += button->priv->has_bookmark_separator;
1530
1531   if (row_type == ROW_TYPE_BOOKMARK)
1532     return retval;
1533
1534   retval += button->priv->n_bookmarks;
1535
1536   if (row_type == ROW_TYPE_CURRENT_FOLDER_SEPARATOR)
1537     return retval;
1538
1539   retval += button->priv->has_current_folder_separator;
1540
1541   if (row_type == ROW_TYPE_CURRENT_FOLDER)
1542     return retval;
1543
1544   retval += button->priv->has_current_folder;
1545
1546   if (row_type == ROW_TYPE_OTHER_SEPARATOR)
1547     return retval;
1548
1549   retval += button->priv->has_other_separator;
1550
1551   if (row_type == ROW_TYPE_OTHER)
1552     return retval;
1553
1554   g_assert_not_reached ();
1555   return -1;
1556 }
1557
1558 static void
1559 model_free_row_data (GtkFileChooserButton *button,
1560                      GtkTreeIter          *iter)
1561 {
1562   gchar type;
1563   gpointer data;
1564   GCancellable *cancellable;
1565
1566   gtk_tree_model_get (button->priv->model, iter,
1567                       TYPE_COLUMN, &type,
1568                       DATA_COLUMN, &data,
1569                       CANCELLABLE_COLUMN, &cancellable,
1570                       -1);
1571
1572   if (cancellable)
1573     g_cancellable_cancel (cancellable);
1574
1575   switch (type)
1576     {
1577     case ROW_TYPE_SPECIAL:
1578     case ROW_TYPE_SHORTCUT:
1579     case ROW_TYPE_BOOKMARK:
1580     case ROW_TYPE_CURRENT_FOLDER:
1581       g_object_unref (data);
1582       break;
1583     case ROW_TYPE_VOLUME:
1584       _gtk_file_system_volume_free (data);
1585       break;
1586     default:
1587       break;
1588     }
1589 }
1590
1591 static void
1592 model_add_special_get_info_cb (GCancellable *cancellable,
1593                                GFileInfo    *info,
1594                                const GError *error,
1595                                gpointer      user_data)
1596 {
1597   gboolean cancelled = g_cancellable_is_cancelled (cancellable);
1598   GtkTreeIter iter;
1599   GtkTreePath *path;
1600   GdkPixbuf *pixbuf;
1601   GCancellable *model_cancellable;
1602   struct ChangeIconThemeData *data = user_data;
1603   gchar *name;
1604
1605   if (!data->button->priv->model)
1606     /* button got destroyed */
1607     goto out;
1608
1609   path = gtk_tree_row_reference_get_path (data->row_ref);
1610   if (!path)
1611     /* Cancellable doesn't exist anymore in the model */
1612     goto out;
1613
1614   gtk_tree_model_get_iter (data->button->priv->model, &iter, path);
1615   gtk_tree_path_free (path);
1616
1617   gtk_tree_model_get (data->button->priv->model, &iter,
1618                       CANCELLABLE_COLUMN, &model_cancellable,
1619                       -1);
1620   if (cancellable != model_cancellable)
1621     goto out;
1622
1623   gtk_list_store_set (GTK_LIST_STORE (data->button->priv->model), &iter,
1624                       CANCELLABLE_COLUMN, NULL,
1625                       -1);
1626
1627   if (cancelled || error)
1628     goto out;
1629
1630   pixbuf = _gtk_file_info_render_icon (info, GTK_WIDGET (data->button), data->button->priv->icon_size);
1631
1632   if (pixbuf)
1633     {
1634       gtk_list_store_set (GTK_LIST_STORE (data->button->priv->model), &iter,
1635                           ICON_COLUMN, pixbuf,
1636                           -1);
1637       g_object_unref (pixbuf);
1638     }
1639
1640   gtk_tree_model_get (data->button->priv->model, &iter,
1641                       DISPLAY_NAME_COLUMN, &name,
1642                       -1);
1643   if (!name)
1644     gtk_list_store_set (GTK_LIST_STORE (data->button->priv->model), &iter,
1645                         DISPLAY_NAME_COLUMN, g_file_info_get_display_name (info),
1646                         -1);
1647   g_free (name);
1648    
1649 out:
1650   g_object_unref (data->button);
1651   gtk_tree_row_reference_free (data->row_ref);
1652   g_free (data);
1653
1654   g_object_unref (cancellable);
1655 }
1656
1657 static inline void
1658 model_add_special (GtkFileChooserButton *button)
1659 {
1660   const gchar *homedir;
1661   const gchar *desktopdir;
1662   GtkListStore *store;
1663   GtkTreeIter iter;
1664   GFile *file;
1665   gint pos;
1666
1667   store = GTK_LIST_STORE (button->priv->model);
1668   pos = model_get_type_position (button, ROW_TYPE_SPECIAL);
1669
1670   homedir = g_get_home_dir ();
1671
1672   if (homedir)
1673     {
1674       GtkTreePath *tree_path;
1675       GCancellable *cancellable;
1676       struct ChangeIconThemeData *info;
1677
1678       file = g_file_new_for_path (homedir);
1679       gtk_list_store_insert (store, &iter, pos);
1680       pos++;
1681
1682       info = g_new0 (struct ChangeIconThemeData, 1);
1683       info->button = g_object_ref (button);
1684       tree_path = gtk_tree_model_get_path (GTK_TREE_MODEL (store), &iter);
1685       info->row_ref = gtk_tree_row_reference_new (GTK_TREE_MODEL (store),
1686                                                   tree_path);
1687       gtk_tree_path_free (tree_path);
1688
1689       cancellable = _gtk_file_system_get_info (button->priv->fs, file,
1690                                                "standard::icon,standard::display-name",
1691                                                model_add_special_get_info_cb, info);
1692
1693       gtk_list_store_set (store, &iter,
1694                           ICON_COLUMN, NULL,
1695                           DISPLAY_NAME_COLUMN, NULL,
1696                           TYPE_COLUMN, ROW_TYPE_SPECIAL,
1697                           DATA_COLUMN, file,
1698                           IS_FOLDER_COLUMN, TRUE,
1699                           CANCELLABLE_COLUMN, cancellable,
1700                           -1);
1701
1702       button->priv->n_special++;
1703     }
1704
1705   desktopdir = g_get_user_special_dir (G_USER_DIRECTORY_DESKTOP);
1706
1707   if (desktopdir)
1708     {
1709       GtkTreePath *tree_path;
1710       GCancellable *cancellable;
1711       struct ChangeIconThemeData *info;
1712
1713       file = g_file_new_for_path (desktopdir);
1714       gtk_list_store_insert (store, &iter, pos);
1715       pos++;
1716
1717       info = g_new0 (struct ChangeIconThemeData, 1);
1718       info->button = g_object_ref (button);
1719       tree_path = gtk_tree_model_get_path (GTK_TREE_MODEL (store), &iter);
1720       info->row_ref = gtk_tree_row_reference_new (GTK_TREE_MODEL (store),
1721                                                   tree_path);
1722       gtk_tree_path_free (tree_path);
1723
1724       cancellable = _gtk_file_system_get_info (button->priv->fs, file,
1725                                                "standard::icon,standard::display-name",
1726                                                model_add_special_get_info_cb, info);
1727
1728       gtk_list_store_set (store, &iter,
1729                           TYPE_COLUMN, ROW_TYPE_SPECIAL,
1730                           ICON_COLUMN, NULL,
1731                           DISPLAY_NAME_COLUMN, _(DESKTOP_DISPLAY_NAME),
1732                           DATA_COLUMN, file,
1733                           IS_FOLDER_COLUMN, TRUE,
1734                           CANCELLABLE_COLUMN, cancellable,
1735                           -1);
1736
1737       button->priv->n_special++;
1738     }
1739 }
1740
1741 static void
1742 model_add_volumes (GtkFileChooserButton *button,
1743                    GSList               *volumes)
1744 {
1745   GtkListStore *store;
1746   gint pos;
1747   gboolean local_only;
1748   GtkFileSystem *file_system;
1749   GSList *l;
1750   
1751   if (!volumes)
1752     return;
1753
1754   store = GTK_LIST_STORE (button->priv->model);
1755   pos = model_get_type_position (button, ROW_TYPE_VOLUME);
1756   local_only = gtk_file_chooser_get_local_only (GTK_FILE_CHOOSER (button->priv->dialog));
1757   file_system = button->priv->fs;
1758
1759   for (l = volumes; l; l = l->next)
1760     {
1761       GtkFileSystemVolume *volume;
1762       GtkTreeIter iter;
1763       GdkPixbuf *pixbuf;
1764       gchar *display_name;
1765
1766       volume = l->data;
1767
1768       if (local_only)
1769         {
1770           if (_gtk_file_system_volume_is_mounted (volume))
1771             {
1772               GFile *base_file;
1773
1774               base_file = _gtk_file_system_volume_get_root (volume);
1775               if (base_file != NULL && !g_file_is_native (base_file))
1776                 {
1777                   _gtk_file_system_volume_free (volume);
1778                   continue;
1779                 }
1780             }
1781         }
1782
1783       pixbuf = _gtk_file_system_volume_render_icon (volume,
1784                                                     GTK_WIDGET (button),
1785                                                     button->priv->icon_size,
1786                                                     NULL);
1787       display_name = _gtk_file_system_volume_get_display_name (volume);
1788
1789       gtk_list_store_insert (store, &iter, pos);
1790       gtk_list_store_set (store, &iter,
1791                           ICON_COLUMN, pixbuf,
1792                           DISPLAY_NAME_COLUMN, display_name,
1793                           TYPE_COLUMN, ROW_TYPE_VOLUME,
1794                           DATA_COLUMN, volume,
1795                           IS_FOLDER_COLUMN, TRUE,
1796                           -1);
1797
1798       if (pixbuf)
1799         g_object_unref (pixbuf);
1800       g_free (display_name);
1801
1802       button->priv->n_volumes++;
1803       pos++;
1804     }
1805 }
1806
1807 extern gchar * _gtk_file_chooser_label_for_file (GFile *file);
1808
1809 static void
1810 model_add_bookmarks (GtkFileChooserButton *button,
1811                      GSList               *bookmarks)
1812 {
1813   GtkListStore *store;
1814   GtkTreeIter iter;
1815   gint pos;
1816   gboolean local_only;
1817   GSList *l;
1818
1819   if (!bookmarks)
1820     return;
1821
1822   store = GTK_LIST_STORE (button->priv->model);
1823   pos = model_get_type_position (button, ROW_TYPE_BOOKMARK);
1824   local_only = gtk_file_chooser_get_local_only (GTK_FILE_CHOOSER (button->priv->dialog));
1825
1826   for (l = bookmarks; l; l = l->next)
1827     {
1828       GFile *file;
1829
1830       file = l->data;
1831
1832       if (g_file_is_native (file))
1833         {
1834           gtk_list_store_insert (store, &iter, pos);
1835           gtk_list_store_set (store, &iter,
1836                               ICON_COLUMN, NULL,
1837                               DISPLAY_NAME_COLUMN, _(FALLBACK_DISPLAY_NAME),
1838                               TYPE_COLUMN, ROW_TYPE_BOOKMARK,
1839                               DATA_COLUMN, g_object_ref (file),
1840                               IS_FOLDER_COLUMN, FALSE,
1841                               -1);
1842           set_info_for_file_at_iter (button, file, &iter);
1843         }
1844       else
1845         {
1846           gchar *label;
1847           GtkIconTheme *icon_theme;
1848           GdkPixbuf *pixbuf;
1849
1850           if (local_only)
1851             continue;
1852
1853           /* Don't call get_info for remote paths to avoid latency and
1854            * auth dialogs.
1855            * If we switch to a better bookmarks file format (XBEL), we
1856            * should use mime info to get a better icon.
1857            */
1858           label = _gtk_file_system_get_bookmark_label (button->priv->fs, file);
1859           if (!label)
1860             label = _gtk_file_chooser_label_for_file (file);
1861
1862           icon_theme = gtk_icon_theme_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (button)));
1863           pixbuf = gtk_icon_theme_load_icon (icon_theme, "folder-remote", 
1864                                              button->priv->icon_size, 0, NULL);
1865
1866           gtk_list_store_insert (store, &iter, pos);
1867           gtk_list_store_set (store, &iter,
1868                               ICON_COLUMN, pixbuf,
1869                               DISPLAY_NAME_COLUMN, label,
1870                               TYPE_COLUMN, ROW_TYPE_BOOKMARK,
1871                               DATA_COLUMN, g_object_ref (file),
1872                               IS_FOLDER_COLUMN, TRUE,
1873                               -1);
1874
1875           g_free (label);
1876           g_object_unref (pixbuf);
1877         }
1878
1879       button->priv->n_bookmarks++;
1880       pos++;
1881     }
1882
1883   if (button->priv->n_bookmarks > 0 && 
1884       !button->priv->has_bookmark_separator)
1885     {
1886       pos = model_get_type_position (button, ROW_TYPE_BOOKMARK_SEPARATOR);
1887
1888       gtk_list_store_insert (store, &iter, pos);
1889       gtk_list_store_set (store, &iter,
1890                           ICON_COLUMN, NULL,
1891                           DISPLAY_NAME_COLUMN, NULL,
1892                           TYPE_COLUMN, ROW_TYPE_BOOKMARK_SEPARATOR,
1893                           DATA_COLUMN, NULL,
1894                           IS_FOLDER_COLUMN, FALSE,
1895                           -1);
1896       button->priv->has_bookmark_separator = TRUE;
1897     }
1898 }
1899
1900 static void
1901 model_update_current_folder (GtkFileChooserButton *button,
1902                              GFile                *file)
1903 {
1904   GtkListStore *store;
1905   GtkTreeIter iter;
1906   gint pos;
1907
1908   if (!file)
1909     return;
1910
1911   store = GTK_LIST_STORE (button->priv->model);
1912
1913   if (!button->priv->has_current_folder_separator)
1914     {
1915       pos = model_get_type_position (button, ROW_TYPE_CURRENT_FOLDER_SEPARATOR);
1916       gtk_list_store_insert (store, &iter, pos);
1917       gtk_list_store_set (store, &iter,
1918                           ICON_COLUMN, NULL,
1919                           DISPLAY_NAME_COLUMN, NULL,
1920                           TYPE_COLUMN, ROW_TYPE_CURRENT_FOLDER_SEPARATOR,
1921                           DATA_COLUMN, NULL,
1922                           IS_FOLDER_COLUMN, FALSE,
1923                           -1);
1924       button->priv->has_current_folder_separator = TRUE;
1925     }
1926
1927   pos = model_get_type_position (button, ROW_TYPE_CURRENT_FOLDER);
1928   if (!button->priv->has_current_folder)
1929     {
1930       gtk_list_store_insert (store, &iter, pos);
1931       button->priv->has_current_folder = TRUE;
1932     }
1933   else
1934     {
1935       gtk_tree_model_iter_nth_child (button->priv->model, &iter, NULL, pos);
1936       model_free_row_data (button, &iter);
1937     }
1938
1939   if (g_file_is_native (file))
1940     {
1941       gtk_list_store_set (store, &iter,
1942                           ICON_COLUMN, NULL,
1943                           DISPLAY_NAME_COLUMN, _(FALLBACK_DISPLAY_NAME),
1944                           TYPE_COLUMN, ROW_TYPE_CURRENT_FOLDER,
1945                           DATA_COLUMN, g_object_ref (file),
1946                           IS_FOLDER_COLUMN, FALSE,
1947                           -1);
1948       set_info_for_file_at_iter (button, file, &iter);
1949     }
1950   else
1951     {
1952       gchar *label;
1953       GtkIconTheme *icon_theme;
1954       GdkPixbuf *pixbuf;
1955
1956       /* Don't call get_info for remote paths to avoid latency and
1957        * auth dialogs.
1958        * If we switch to a better bookmarks file format (XBEL), we
1959        * should use mime info to get a better icon.
1960        */
1961       label = _gtk_file_system_get_bookmark_label (button->priv->fs, file);
1962       if (!label)
1963         label = _gtk_file_chooser_label_for_file (file);
1964
1965       icon_theme = gtk_icon_theme_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (button)));
1966
1967       if (g_file_is_native (file))
1968           pixbuf = gtk_icon_theme_load_icon (icon_theme, "folder", 
1969                                              button->priv->icon_size, 0, NULL);
1970       else
1971           pixbuf = gtk_icon_theme_load_icon (icon_theme, "folder-remote", 
1972                                              button->priv->icon_size, 0, NULL);
1973
1974       gtk_list_store_set (store, &iter,
1975                           ICON_COLUMN, pixbuf,
1976                           DISPLAY_NAME_COLUMN, label,
1977                           TYPE_COLUMN, ROW_TYPE_CURRENT_FOLDER,
1978                           DATA_COLUMN, g_object_ref (file),
1979                           IS_FOLDER_COLUMN, TRUE,
1980                           -1);
1981
1982       g_free (label);
1983       g_object_unref (pixbuf);
1984     }
1985 }
1986
1987 static inline void
1988 model_add_other (GtkFileChooserButton *button)
1989 {
1990   GtkListStore *store;
1991   GtkTreeIter iter;
1992   gint pos;
1993   
1994   store = GTK_LIST_STORE (button->priv->model);
1995   pos = model_get_type_position (button, ROW_TYPE_OTHER_SEPARATOR);
1996
1997   gtk_list_store_insert (store, &iter, pos);
1998   gtk_list_store_set (store, &iter,
1999                       ICON_COLUMN, NULL,
2000                       DISPLAY_NAME_COLUMN, NULL,
2001                       TYPE_COLUMN, ROW_TYPE_OTHER_SEPARATOR,
2002                       DATA_COLUMN, NULL,
2003                       IS_FOLDER_COLUMN, FALSE,
2004                       -1);
2005   button->priv->has_other_separator = TRUE;
2006   pos++;
2007
2008   gtk_list_store_insert (store, &iter, pos);
2009   gtk_list_store_set (store, &iter,
2010                       ICON_COLUMN, NULL,
2011                       DISPLAY_NAME_COLUMN, _("Other..."),
2012                       TYPE_COLUMN, ROW_TYPE_OTHER,
2013                       DATA_COLUMN, NULL,
2014                       IS_FOLDER_COLUMN, FALSE,
2015                       -1);
2016 }
2017
2018 static void
2019 model_remove_rows (GtkFileChooserButton *button,
2020                    gint                  pos,
2021                    gint                  n_rows)
2022 {
2023   GtkListStore *store;
2024
2025   if (!n_rows)
2026     return;
2027
2028   store = GTK_LIST_STORE (button->priv->model);
2029
2030   do
2031     {
2032       GtkTreeIter iter;
2033
2034       if (!gtk_tree_model_iter_nth_child (button->priv->model, &iter, NULL, pos))
2035         g_assert_not_reached ();
2036
2037       model_free_row_data (button, &iter);
2038       gtk_list_store_remove (store, &iter);
2039       n_rows--;
2040     }
2041   while (n_rows);
2042 }
2043
2044 /* Filter Model */
2045 static inline gboolean
2046 test_if_file_is_visible (GtkFileSystem *fs,
2047                          GFile         *file,
2048                          gboolean       local_only,
2049                          gboolean       is_folder)
2050 {
2051   if (!file)
2052     return FALSE;
2053
2054   if (local_only && !g_file_is_native (file))
2055     return FALSE;
2056
2057   if (!is_folder)
2058     return FALSE;
2059
2060   return TRUE;
2061 }
2062
2063 static gboolean
2064 filter_model_visible_func (GtkTreeModel *model,
2065                            GtkTreeIter  *iter,
2066                            gpointer      user_data)
2067 {
2068   GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (user_data);
2069   GtkFileChooserButtonPrivate *priv = button->priv;
2070   gchar type;
2071   gpointer data;
2072   gboolean local_only, retval, is_folder;
2073
2074   type = ROW_TYPE_INVALID;
2075   data = NULL;
2076   local_only = gtk_file_chooser_get_local_only (GTK_FILE_CHOOSER (priv->dialog));
2077
2078   gtk_tree_model_get (model, iter,
2079                       TYPE_COLUMN, &type,
2080                       DATA_COLUMN, &data,
2081                       IS_FOLDER_COLUMN, &is_folder,
2082                       -1);
2083
2084   switch (type)
2085     {
2086     case ROW_TYPE_CURRENT_FOLDER:
2087       retval = TRUE;
2088       break;
2089     case ROW_TYPE_SPECIAL:
2090     case ROW_TYPE_SHORTCUT:
2091     case ROW_TYPE_BOOKMARK:
2092       retval = test_if_file_is_visible (priv->fs, data, local_only, is_folder);
2093       break;
2094     case ROW_TYPE_VOLUME:
2095       {
2096         retval = TRUE;
2097         if (local_only)
2098           {
2099             if (_gtk_file_system_volume_is_mounted (data))
2100               {
2101                 GFile *base_file;
2102
2103                 base_file = _gtk_file_system_volume_get_root (data);
2104
2105                 if (base_file)
2106                   {
2107                     if (!g_file_is_native (base_file))
2108                       retval = FALSE;
2109                   }
2110                 else
2111                   retval = FALSE;
2112               }
2113           }
2114       }
2115       break;
2116     default:
2117       retval = TRUE;
2118       break;
2119     }
2120
2121   return retval;
2122 }
2123
2124 /* Combo Box */
2125 static void
2126 name_cell_data_func (GtkCellLayout   *layout,
2127                      GtkCellRenderer *cell,
2128                      GtkTreeModel    *model,
2129                      GtkTreeIter     *iter,
2130                      gpointer         user_data)
2131 {
2132   gchar type;
2133
2134   type = 0;
2135   gtk_tree_model_get (model, iter,
2136                       TYPE_COLUMN, &type,
2137                       -1);
2138
2139   if (type == ROW_TYPE_CURRENT_FOLDER)
2140     g_object_set (cell, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
2141   else
2142     g_object_set (cell, "ellipsize", PANGO_ELLIPSIZE_NONE, NULL);
2143 }
2144
2145 static gboolean
2146 combo_box_row_separator_func (GtkTreeModel *model,
2147                               GtkTreeIter  *iter,
2148                               gpointer      user_data)
2149 {
2150   gchar type = ROW_TYPE_INVALID;
2151
2152   gtk_tree_model_get (model, iter, TYPE_COLUMN, &type, -1);
2153
2154   return (type == ROW_TYPE_BOOKMARK_SEPARATOR ||
2155           type == ROW_TYPE_CURRENT_FOLDER_SEPARATOR ||
2156           type == ROW_TYPE_OTHER_SEPARATOR);
2157 }                         
2158
2159 static void
2160 update_combo_box (GtkFileChooserButton *button)
2161 {
2162   GtkFileChooserButtonPrivate *priv = button->priv;
2163   GSList *files;
2164   GtkTreeIter iter;
2165   gboolean row_found;
2166
2167   gtk_tree_model_get_iter_first (priv->filter_model, &iter);
2168
2169   files = gtk_file_chooser_get_files (GTK_FILE_CHOOSER (priv->dialog));
2170
2171   row_found = FALSE;
2172
2173   do
2174     {
2175       gchar type;
2176       gpointer data;
2177
2178       type = ROW_TYPE_INVALID;
2179       data = NULL;
2180
2181       gtk_tree_model_get (priv->filter_model, &iter,
2182                           TYPE_COLUMN, &type,
2183                           DATA_COLUMN, &data,
2184                           -1);
2185     
2186       switch (type)
2187         {
2188         case ROW_TYPE_SPECIAL:
2189         case ROW_TYPE_SHORTCUT:
2190         case ROW_TYPE_BOOKMARK:
2191         case ROW_TYPE_CURRENT_FOLDER:
2192           row_found = (files &&
2193                        files->data &&
2194                        g_file_equal (data, files->data));
2195           break;
2196         case ROW_TYPE_VOLUME:
2197           {
2198             GFile *base_file;
2199
2200             base_file = _gtk_file_system_volume_get_root (data);
2201             if (base_file)
2202               {
2203                 row_found = (files &&
2204                              files->data &&
2205                              g_file_equal (base_file, files->data));
2206                 g_object_unref (base_file);
2207               }
2208           }
2209           break;
2210         default:
2211           row_found = FALSE;
2212           break;
2213         }
2214
2215       if (row_found)
2216         {
2217           g_signal_handler_block (priv->combo_box, priv->combo_box_changed_id);
2218           gtk_combo_box_set_active_iter (GTK_COMBO_BOX (priv->combo_box),
2219                                          &iter);
2220           g_signal_handler_unblock (priv->combo_box,
2221                                     priv->combo_box_changed_id);
2222         }
2223     }
2224   while (!row_found && gtk_tree_model_iter_next (priv->filter_model, &iter));
2225
2226   /* If it hasn't been found already, update & select the current-folder row. */
2227   if (!row_found && files && files->data)
2228     {
2229       GtkTreeIter filter_iter;
2230       gint pos;
2231     
2232       model_update_current_folder (button, files->data);
2233       gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (priv->filter_model));
2234
2235       pos = model_get_type_position (button, ROW_TYPE_CURRENT_FOLDER);
2236       gtk_tree_model_iter_nth_child (priv->model, &iter, NULL, pos);
2237
2238       gtk_tree_model_filter_convert_child_iter_to_iter (GTK_TREE_MODEL_FILTER (priv->filter_model),
2239                                                         &filter_iter, &iter);
2240
2241       g_signal_handler_block (priv->combo_box, priv->combo_box_changed_id);
2242       gtk_combo_box_set_active_iter (GTK_COMBO_BOX (priv->combo_box), &filter_iter);
2243       g_signal_handler_unblock (priv->combo_box, priv->combo_box_changed_id);
2244     }
2245
2246   g_slist_foreach (files, (GFunc) g_object_unref, NULL);
2247   g_slist_free (files);
2248 }
2249
2250 /* Button */
2251 static void
2252 update_label_get_info_cb (GCancellable *cancellable,
2253                           GFileInfo    *info,
2254                           const GError *error,
2255                           gpointer      data)
2256 {
2257   gboolean cancelled = g_cancellable_is_cancelled (cancellable);
2258   GdkPixbuf *pixbuf;
2259   GtkFileChooserButton *button = data;
2260   GtkFileChooserButtonPrivate *priv = button->priv;
2261
2262   if (cancellable != priv->update_button_cancellable)
2263     goto out;
2264
2265   priv->update_button_cancellable = NULL;
2266
2267   if (cancelled || error)
2268     goto out;
2269
2270   gtk_label_set_text (GTK_LABEL (priv->label), g_file_info_get_display_name (info));
2271
2272   pixbuf = _gtk_file_info_render_icon (info, GTK_WIDGET (priv->image), priv->icon_size);
2273
2274   if (!pixbuf)
2275     pixbuf = gtk_icon_theme_load_icon (get_icon_theme (GTK_WIDGET (priv->image)),
2276                                        FALLBACK_ICON_NAME,
2277                                        priv->icon_size, 0, NULL);
2278
2279   gtk_image_set_from_pixbuf (GTK_IMAGE (priv->image), pixbuf);
2280   if (pixbuf)
2281     g_object_unref (pixbuf);
2282
2283 out:
2284   g_object_unref (button);
2285   g_object_unref (cancellable);
2286 }
2287
2288 static void
2289 update_label_and_image (GtkFileChooserButton *button)
2290 {
2291   GtkFileChooserButtonPrivate *priv = button->priv;
2292   GdkPixbuf *pixbuf;
2293   gchar *label_text;
2294   GSList *files;
2295
2296   files = gtk_file_chooser_get_files (GTK_FILE_CHOOSER (priv->dialog));
2297   label_text = NULL;
2298   pixbuf = NULL;
2299
2300   if (files && files->data)
2301     {
2302       GFile *file;
2303       GtkFileSystemVolume *volume = NULL;
2304
2305       file = files->data;
2306
2307       volume = _gtk_file_system_get_volume_for_file (priv->fs, file);
2308       if (volume)
2309         {
2310           GFile *base_file;
2311
2312           base_file = _gtk_file_system_volume_get_root (volume);
2313           if (base_file && g_file_equal (base_file, file))
2314             {
2315               label_text = _gtk_file_system_volume_get_display_name (volume);
2316               pixbuf = _gtk_file_system_volume_render_icon (volume,
2317                                                             GTK_WIDGET (button),
2318                                                             priv->icon_size,
2319                                                             NULL);
2320             }
2321
2322           if (base_file)
2323             g_object_unref (base_file);
2324
2325           _gtk_file_system_volume_free (volume);
2326
2327           if (label_text)
2328             goto out;
2329         }
2330
2331       if (priv->update_button_cancellable)
2332         {
2333           g_cancellable_cancel (priv->update_button_cancellable);
2334           priv->update_button_cancellable = NULL;
2335         }
2336
2337       if (g_file_is_native (file))
2338         {
2339           priv->update_button_cancellable =
2340             _gtk_file_system_get_info (priv->fs, file,
2341                                        "standard::icon,standard::display-name",
2342                                        update_label_get_info_cb,
2343                                        g_object_ref (button));
2344         }
2345       else
2346         {
2347           GdkPixbuf *pixbuf;
2348
2349           label_text = _gtk_file_system_get_bookmark_label (button->priv->fs, file);
2350           
2351           pixbuf = gtk_icon_theme_load_icon (get_icon_theme (GTK_WIDGET (priv->image)), 
2352                                              "text-x-generic",
2353                                              priv->icon_size, 0, NULL);
2354           
2355           gtk_image_set_from_pixbuf (GTK_IMAGE (priv->image), pixbuf);
2356
2357           if (pixbuf)
2358             g_object_unref (pixbuf);
2359         }
2360     }
2361 out:
2362   g_slist_foreach (files, (GFunc) g_object_unref, NULL);
2363   g_slist_free (files);
2364
2365   if (label_text)
2366     {
2367       gtk_label_set_text (GTK_LABEL (priv->label), label_text);
2368       g_free (label_text);
2369     }
2370   else
2371     {
2372       gtk_label_set_text (GTK_LABEL (priv->label), _(FALLBACK_DISPLAY_NAME));
2373       gtk_image_set_from_pixbuf (GTK_IMAGE (priv->image), NULL);
2374     }
2375 }
2376
2377
2378 /* ************************ *
2379  *  Child Object Callbacks  *
2380  * ************************ */
2381
2382 /* File System */
2383 static void
2384 fs_volumes_changed_cb (GtkFileSystem *fs,
2385                        gpointer       user_data)
2386 {
2387   GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (user_data);
2388   GtkFileChooserButtonPrivate *priv = button->priv;
2389   GSList *volumes;
2390
2391   model_remove_rows (user_data,
2392                      model_get_type_position (user_data, ROW_TYPE_VOLUME),
2393                      priv->n_volumes);
2394
2395   priv->n_volumes = 0;
2396
2397   volumes = _gtk_file_system_list_volumes (fs);
2398   model_add_volumes (user_data, volumes);
2399   g_slist_free (volumes);
2400
2401   gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (priv->filter_model));
2402
2403   update_label_and_image (user_data);
2404   update_combo_box (user_data);
2405 }
2406
2407 static void
2408 fs_bookmarks_changed_cb (GtkFileSystem *fs,
2409                          gpointer       user_data)
2410 {
2411   GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (user_data);
2412   GtkFileChooserButtonPrivate *priv = button->priv;
2413   GSList *bookmarks;
2414
2415   bookmarks = _gtk_file_system_list_bookmarks (fs);
2416   model_remove_rows (user_data,
2417                      model_get_type_position (user_data,
2418                                               ROW_TYPE_BOOKMARK_SEPARATOR),
2419                      (priv->n_bookmarks + priv->has_bookmark_separator));
2420   priv->has_bookmark_separator = FALSE;
2421   priv->n_bookmarks = 0;
2422   model_add_bookmarks (user_data, bookmarks);
2423   g_slist_foreach (bookmarks, (GFunc) g_object_unref, NULL);
2424   g_slist_free (bookmarks);
2425
2426   gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (priv->filter_model));
2427
2428   update_label_and_image (user_data);
2429   update_combo_box (user_data);
2430 }
2431
2432 /* Dialog */
2433 static void
2434 open_dialog (GtkFileChooserButton *button)
2435 {
2436   GtkFileChooserButtonPrivate *priv = button->priv;
2437
2438   /* Setup the dialog parent to be chooser button's toplevel, and be modal
2439      as needed. */
2440   if (!GTK_WIDGET_VISIBLE (priv->dialog))
2441     {
2442       GtkWidget *toplevel;
2443
2444       toplevel = gtk_widget_get_toplevel (GTK_WIDGET (button));
2445
2446       if (GTK_WIDGET_TOPLEVEL (toplevel) && GTK_IS_WINDOW (toplevel))
2447         {
2448           if (GTK_WINDOW (toplevel) != gtk_window_get_transient_for (GTK_WINDOW (priv->dialog)))
2449             gtk_window_set_transient_for (GTK_WINDOW (priv->dialog),
2450                                           GTK_WINDOW (toplevel));
2451               
2452           gtk_window_set_modal (GTK_WINDOW (priv->dialog),
2453                                 gtk_window_get_modal (GTK_WINDOW (toplevel)));
2454         }
2455     }
2456
2457   if (!priv->active)
2458     {
2459       GSList *files;
2460
2461       g_signal_handler_block (priv->dialog,
2462                               priv->dialog_folder_changed_id);
2463       g_signal_handler_block (priv->dialog,
2464                               priv->dialog_file_activated_id);
2465       g_signal_handler_block (priv->dialog,
2466                               priv->dialog_selection_changed_id);
2467       files = gtk_file_chooser_get_files (GTK_FILE_CHOOSER (priv->dialog));
2468       if (files)
2469         {
2470           if (files->data)
2471             priv->old_file = g_object_ref (files->data);
2472
2473           g_slist_foreach (files, (GFunc) g_object_unref, NULL);
2474           g_slist_free (files);
2475         }
2476
2477       priv->active = TRUE;
2478     }
2479
2480   gtk_widget_set_sensitive (priv->combo_box, FALSE);
2481   gtk_window_present (GTK_WINDOW (priv->dialog));
2482 }
2483
2484 /* Combo Box */
2485 static void
2486 combo_box_changed_cb (GtkComboBox *combo_box,
2487                       gpointer     user_data)
2488 {
2489   GtkTreeIter iter;
2490
2491   if (gtk_combo_box_get_active_iter (combo_box, &iter))
2492     {
2493       GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (user_data);
2494       GtkFileChooserButtonPrivate *priv = button->priv;
2495       gchar type;
2496       gpointer data;
2497
2498       type = ROW_TYPE_INVALID;
2499       data = NULL;
2500
2501       gtk_tree_model_get (priv->filter_model, &iter,
2502                           TYPE_COLUMN, &type,
2503                           DATA_COLUMN, &data,
2504                           -1);
2505
2506       switch (type)
2507         {
2508         case ROW_TYPE_SPECIAL:
2509         case ROW_TYPE_SHORTCUT:
2510         case ROW_TYPE_BOOKMARK:
2511         case ROW_TYPE_CURRENT_FOLDER:
2512           gtk_file_chooser_unselect_all (GTK_FILE_CHOOSER (priv->dialog));
2513           if (data)
2514             gtk_file_chooser_set_current_folder_file (GTK_FILE_CHOOSER (priv->dialog),
2515                                                       data, NULL);
2516           break;
2517         case ROW_TYPE_VOLUME:
2518           {
2519             GFile *base_file;
2520
2521             gtk_file_chooser_unselect_all (GTK_FILE_CHOOSER (priv->dialog));
2522             base_file = _gtk_file_system_volume_get_root (data);
2523             if (base_file)
2524               {
2525                 gtk_file_chooser_set_current_folder_file (GTK_FILE_CHOOSER (priv->dialog),
2526                                                           base_file, NULL);
2527                 g_object_unref (base_file);
2528               }
2529           }
2530           break;
2531         case ROW_TYPE_OTHER:
2532           open_dialog (user_data);
2533           break;
2534         default:
2535           break;
2536         }
2537     }
2538 }
2539
2540 /* Button */
2541 static void
2542 button_clicked_cb (GtkButton *real_button,
2543                    gpointer   user_data)
2544 {
2545   open_dialog (user_data);
2546 }
2547
2548 /* Dialog */
2549 static void
2550 dialog_current_folder_changed_cb (GtkFileChooser *dialog,
2551                                   gpointer        user_data)
2552 {
2553   GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (user_data);
2554   GtkFileChooserButtonPrivate *priv = button->priv;
2555
2556   priv->folder_has_been_set = TRUE;
2557
2558   g_signal_emit_by_name (button, "current-folder-changed");
2559 }
2560
2561 static void
2562 dialog_file_activated_cb (GtkFileChooser *dialog,
2563                           gpointer        user_data)
2564 {
2565   g_signal_emit_by_name (user_data, "file-activated");
2566 }
2567
2568 static void
2569 dialog_selection_changed_cb (GtkFileChooser *dialog,
2570                              gpointer        user_data)
2571 {
2572   update_label_and_image (user_data);
2573   update_combo_box (user_data);
2574   g_signal_emit_by_name (user_data, "selection-changed");
2575 }
2576
2577 static void
2578 dialog_update_preview_cb (GtkFileChooser *dialog,
2579                           gpointer        user_data)
2580 {
2581   g_signal_emit_by_name (user_data, "update-preview");
2582 }
2583
2584 static void
2585 dialog_notify_cb (GObject    *dialog,
2586                   GParamSpec *pspec,
2587                   gpointer    user_data)
2588 {
2589   gpointer iface;
2590
2591   iface = g_type_interface_peek (g_type_class_peek (G_OBJECT_TYPE (dialog)),
2592                                  GTK_TYPE_FILE_CHOOSER);
2593   if (g_object_interface_find_property (iface, pspec->name))
2594     g_object_notify (user_data, pspec->name);
2595
2596   if (g_ascii_strcasecmp (pspec->name, "local-only") == 0)
2597     {
2598       GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (user_data);
2599       GtkFileChooserButtonPrivate *priv = button->priv;
2600
2601       if (priv->has_current_folder)
2602         {
2603           GtkTreeIter iter;
2604           gint pos;
2605           gpointer data;
2606
2607           pos = model_get_type_position (user_data,
2608                                          ROW_TYPE_CURRENT_FOLDER);
2609           gtk_tree_model_iter_nth_child (priv->model, &iter, NULL, pos);
2610
2611           data = NULL;
2612           gtk_tree_model_get (priv->model, &iter, DATA_COLUMN, &data, -1);
2613
2614           /* If the path isn't local but we're in local-only mode now, remove
2615            * the custom-folder row */
2616           if (data && g_file_is_native (G_FILE (data)) &&
2617               gtk_file_chooser_get_local_only (GTK_FILE_CHOOSER (priv->dialog)))
2618             {
2619               pos--;
2620               model_remove_rows (user_data, pos, 2);
2621             }
2622         }
2623
2624       gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (priv->filter_model));
2625       update_combo_box (user_data);
2626     }
2627 }
2628
2629 static gboolean
2630 dialog_delete_event_cb (GtkWidget *dialog,
2631                         GdkEvent  *event,
2632                         gpointer   user_data)
2633 {
2634   g_signal_emit_by_name (dialog, "response", GTK_RESPONSE_DELETE_EVENT);
2635
2636   return TRUE;
2637 }
2638
2639 static void
2640 dialog_response_cb (GtkDialog *dialog,
2641                     gint       response,
2642                     gpointer   user_data)
2643 {
2644   GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (user_data);
2645   GtkFileChooserButtonPrivate *priv = button->priv;
2646
2647   if (response == GTK_RESPONSE_ACCEPT ||
2648       response == GTK_RESPONSE_OK)
2649     {
2650       g_signal_emit_by_name (user_data, "current-folder-changed");
2651       g_signal_emit_by_name (user_data, "selection-changed");
2652     }
2653   else if (priv->old_file)
2654     {
2655       switch (gtk_file_chooser_get_action (GTK_FILE_CHOOSER (dialog)))
2656         {
2657         case GTK_FILE_CHOOSER_ACTION_OPEN:
2658           gtk_file_chooser_select_file (GTK_FILE_CHOOSER (dialog), priv->old_file,
2659                                         NULL);
2660           break;
2661         case GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER:
2662           gtk_file_chooser_set_current_folder_file (GTK_FILE_CHOOSER (dialog),
2663                                                     priv->old_file, NULL);
2664           break;
2665         default:
2666           g_assert_not_reached ();
2667           break;
2668         }
2669     }
2670   else
2671     gtk_file_chooser_unselect_all (GTK_FILE_CHOOSER (dialog));
2672
2673   if (priv->old_file)
2674     {
2675       g_object_unref (priv->old_file);
2676       priv->old_file = NULL;
2677     }
2678
2679   update_label_and_image (user_data);
2680   update_combo_box (user_data);
2681   
2682   if (priv->active)
2683     {
2684       g_signal_handler_unblock (priv->dialog,
2685                                 priv->dialog_folder_changed_id);
2686       g_signal_handler_unblock (priv->dialog,
2687                                 priv->dialog_file_activated_id);
2688       g_signal_handler_unblock (priv->dialog,
2689                                 priv->dialog_selection_changed_id);
2690       priv->active = FALSE;
2691     }
2692
2693   gtk_widget_set_sensitive (priv->combo_box, TRUE);
2694   gtk_widget_hide (priv->dialog);
2695
2696   g_signal_emit_by_name (user_data, "file-set");
2697 }
2698
2699
2700 /* ************************************************************************** *
2701  *  Public API                                                                *
2702  * ************************************************************************** */
2703
2704 /**
2705  * gtk_file_chooser_button_new:
2706  * @title: the title of the browse dialog.
2707  * @action: the open mode for the widget.
2708  * 
2709  * Creates a new file-selecting button widget.
2710  * 
2711  * Returns: a new button widget.
2712  * 
2713  * Since: 2.6
2714  **/
2715 GtkWidget *
2716 gtk_file_chooser_button_new (const gchar          *title,
2717                              GtkFileChooserAction  action)
2718 {
2719   g_return_val_if_fail (action == GTK_FILE_CHOOSER_ACTION_OPEN ||
2720                         action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, NULL);
2721
2722   return g_object_new (GTK_TYPE_FILE_CHOOSER_BUTTON,
2723                        "action", action,
2724                        "title", (title ? title : _(DEFAULT_TITLE)),
2725                        NULL);
2726 }
2727
2728 /**
2729  * gtk_file_chooser_button_new_with_backend:
2730  * @title: the title of the browse dialog.
2731  * @action: the open mode for the widget.
2732  * @backend: the name of the #GtkFileSystem backend to use.
2733  * 
2734  * Creates a new file-selecting button widget using @backend.
2735  * 
2736  * Returns: a new button widget.
2737  * 
2738  * Since: 2.6
2739  * Deprecated: 2.14
2740  **/
2741 GtkWidget *
2742 gtk_file_chooser_button_new_with_backend (const gchar          *title,
2743                                           GtkFileChooserAction  action,
2744                                           const gchar          *backend)
2745 {
2746   g_return_val_if_fail (action == GTK_FILE_CHOOSER_ACTION_OPEN ||
2747                         action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, NULL);
2748
2749   return g_object_new (GTK_TYPE_FILE_CHOOSER_BUTTON,
2750                        "action", action,
2751                        "title", (title ? title : _(DEFAULT_TITLE)),
2752                        NULL);
2753 }
2754
2755 /**
2756  * gtk_file_chooser_button_new_with_dialog:
2757  * @dialog: the widget to use as dialog
2758  *
2759  * Creates a #GtkFileChooserButton widget which uses @dialog as its
2760  * file-picking window.
2761  *
2762  * Note that @dialog must be a #GtkDialog (or subclass) which
2763  * implements the #GtkFileChooser interface and must not have
2764  * %GTK_DIALOG_DESTROY_WITH_PARENT set.
2765  *
2766  * Also note that the dialog needs to have its confirmative button
2767  * added with response %GTK_RESPONSE_ACCEPT or %GTK_RESPONSE_OK in
2768  * order for the button to take over the file selected in the dialog.
2769  *
2770  * Returns: a new button widget.
2771  *
2772  * Since: 2.6
2773  **/
2774 GtkWidget *
2775 gtk_file_chooser_button_new_with_dialog (GtkWidget *dialog)
2776 {
2777   g_return_val_if_fail (GTK_IS_FILE_CHOOSER (dialog) && GTK_IS_DIALOG (dialog), NULL);
2778
2779   return g_object_new (GTK_TYPE_FILE_CHOOSER_BUTTON,
2780                        "dialog", dialog,
2781                        NULL);
2782 }
2783
2784 /**
2785  * gtk_file_chooser_button_set_title:
2786  * @button: the button widget to modify.
2787  * @title: the new browse dialog title.
2788  * 
2789  * Modifies the @title of the browse dialog used by @button.
2790  * 
2791  * Since: 2.6
2792  **/
2793 void
2794 gtk_file_chooser_button_set_title (GtkFileChooserButton *button,
2795                                    const gchar          *title)
2796 {
2797   g_return_if_fail (GTK_IS_FILE_CHOOSER_BUTTON (button));
2798
2799   gtk_window_set_title (GTK_WINDOW (button->priv->dialog), title);
2800   g_object_notify (G_OBJECT (button), "title");
2801 }
2802
2803 /**
2804  * gtk_file_chooser_button_get_title:
2805  * @button: the button widget to examine.
2806  * 
2807  * Retrieves the title of the browse dialog used by @button. The returned value
2808  * should not be modified or freed.
2809  * 
2810  * Returns: a pointer to the browse dialog's title.
2811  * 
2812  * Since: 2.6
2813  **/
2814 G_CONST_RETURN gchar *
2815 gtk_file_chooser_button_get_title (GtkFileChooserButton *button)
2816 {
2817   g_return_val_if_fail (GTK_IS_FILE_CHOOSER_BUTTON (button), NULL);
2818
2819   return gtk_window_get_title (GTK_WINDOW (button->priv->dialog));
2820 }
2821
2822 /**
2823  * gtk_file_chooser_button_get_width_chars:
2824  * @button: the button widget to examine.
2825  * 
2826  * Retrieves the width in characters of the @button widget's entry and/or label.
2827  * 
2828  * Returns: an integer width (in characters) that the button will use to size itself.
2829  * 
2830  * Since: 2.6
2831  **/
2832 gint
2833 gtk_file_chooser_button_get_width_chars (GtkFileChooserButton *button)
2834 {
2835   g_return_val_if_fail (GTK_IS_FILE_CHOOSER_BUTTON (button), -1);
2836
2837   return gtk_label_get_width_chars (GTK_LABEL (button->priv->label));
2838 }
2839
2840 /**
2841  * gtk_file_chooser_button_set_width_chars:
2842  * @button: the button widget to examine.
2843  * @n_chars: the new width, in characters.
2844  * 
2845  * Sets the width (in characters) that @button will use to @n_chars.
2846  * 
2847  * Since: 2.6
2848  **/
2849 void
2850 gtk_file_chooser_button_set_width_chars (GtkFileChooserButton *button,
2851                                          gint                  n_chars)
2852 {
2853   g_return_if_fail (GTK_IS_FILE_CHOOSER_BUTTON (button));
2854
2855   gtk_label_set_width_chars (GTK_LABEL (button->priv->label), n_chars);
2856   g_object_notify (G_OBJECT (button), "width-chars");
2857 }
2858
2859 /**
2860  * gtk_file_chooser_button_set_focus_on_click:
2861  * @button: a #GtkFileChooserButton
2862  * @focus_on_click: whether the button grabs focus when clicked with the mouse
2863  * 
2864  * Sets whether the button will grab focus when it is clicked with the mouse.
2865  * Making mouse clicks not grab focus is useful in places like toolbars where
2866  * you don't want the keyboard focus removed from the main area of the
2867  * application.
2868  *
2869  * Since: 2.10
2870  **/
2871 void
2872 gtk_file_chooser_button_set_focus_on_click (GtkFileChooserButton *button,
2873                                             gboolean              focus_on_click)
2874 {
2875   GtkFileChooserButtonPrivate *priv;
2876
2877   g_return_if_fail (GTK_IS_FILE_CHOOSER_BUTTON (button));
2878
2879   priv = button->priv;
2880
2881   focus_on_click = focus_on_click != FALSE;
2882
2883   if (priv->focus_on_click != focus_on_click)
2884     {
2885       priv->focus_on_click = focus_on_click;
2886       gtk_button_set_focus_on_click (GTK_BUTTON (priv->button), focus_on_click);
2887       gtk_combo_box_set_focus_on_click (GTK_COMBO_BOX (priv->combo_box), focus_on_click);
2888       
2889       g_object_notify (G_OBJECT (button), "focus-on-click");
2890     }
2891 }
2892
2893 /**
2894  * gtk_file_chooser_button_get_focus_on_click:
2895  * @button: a #GtkFileChooserButton
2896  * 
2897  * Returns whether the button grabs focus when it is clicked with the mouse.
2898  * See gtk_file_chooser_button_set_focus_on_click().
2899  *
2900  * Return value: %TRUE if the button grabs focus when it is clicked with
2901  *               the mouse.
2902  *
2903  * Since: 2.10
2904  **/
2905 gboolean
2906 gtk_file_chooser_button_get_focus_on_click (GtkFileChooserButton *button)
2907 {
2908   g_return_val_if_fail (GTK_IS_FILE_CHOOSER_BUTTON (button), FALSE);
2909   
2910   return button->priv->focus_on_click;
2911 }
2912
2913 #define __GTK_FILE_CHOOSER_BUTTON_C__
2914 #include "gtkaliasdef.c"