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