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