]> Pileus Git - ~andy/gtk/blob - gtk/gtkfilechooserbutton.c
Update changelog
[~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         {
2009           pixbuf = gtk_icon_theme_load_icon (icon_theme, "gnome-fs-directory", 
2010                                              button->priv->icon_size, 0, NULL);
2011         }
2012       else
2013         {
2014           pixbuf = gtk_icon_theme_load_icon (icon_theme, "gnome-fs-share", 
2015                                              button->priv->icon_size, 0, NULL);
2016         }
2017       
2018       gtk_list_store_set (store, &iter,
2019                           ICON_COLUMN, pixbuf,
2020                           DISPLAY_NAME_COLUMN, label,
2021                           TYPE_COLUMN, ROW_TYPE_CURRENT_FOLDER,
2022                           DATA_COLUMN, gtk_file_path_copy (path),
2023                           IS_FOLDER_COLUMN, TRUE,
2024                           -1);
2025       
2026       g_free (label);
2027       g_object_unref (pixbuf);
2028     }
2029 }
2030
2031 static inline void
2032 model_add_other (GtkFileChooserButton *button)
2033 {
2034   GtkListStore *store;
2035   GtkTreeIter iter;
2036   gint pos;
2037   
2038   store = GTK_LIST_STORE (button->priv->model);
2039   pos = model_get_type_position (button, ROW_TYPE_OTHER_SEPARATOR);
2040
2041   gtk_list_store_insert (store, &iter, pos);
2042   gtk_list_store_set (store, &iter,
2043                       ICON_COLUMN, NULL,
2044                       DISPLAY_NAME_COLUMN, NULL,
2045                       TYPE_COLUMN, ROW_TYPE_OTHER_SEPARATOR,
2046                       DATA_COLUMN, NULL,
2047                       IS_FOLDER_COLUMN, FALSE,
2048                       -1);
2049   button->priv->has_other_separator = TRUE;
2050   pos++;
2051
2052   gtk_list_store_insert (store, &iter, pos);
2053   gtk_list_store_set (store, &iter,
2054                       ICON_COLUMN, NULL,
2055                       DISPLAY_NAME_COLUMN, _("Other..."),
2056                       TYPE_COLUMN, ROW_TYPE_OTHER,
2057                       DATA_COLUMN, NULL,
2058                       IS_FOLDER_COLUMN, FALSE,
2059                       -1);
2060 }
2061
2062 static void
2063 model_remove_rows (GtkFileChooserButton *button,
2064                    gint                  pos,
2065                    gint                  n_rows)
2066 {
2067   GtkListStore *store;
2068
2069   if (!n_rows)
2070     return;
2071
2072   store = GTK_LIST_STORE (button->priv->model);
2073
2074   do
2075     {
2076       GtkTreeIter iter;
2077
2078       if (!gtk_tree_model_iter_nth_child (button->priv->model, &iter, NULL, pos))
2079         g_assert_not_reached ();
2080
2081       model_free_row_data (button, &iter);
2082       gtk_list_store_remove (store, &iter);
2083       n_rows--;
2084     }
2085   while (n_rows);
2086 }
2087
2088 /* Filter Model */
2089 static inline gboolean
2090 test_if_path_is_visible (GtkFileSystem     *fs,
2091                          const GtkFilePath *path,
2092                          gboolean           local_only,
2093                          gboolean           is_folder)
2094 {
2095   if (!path)
2096     return FALSE;
2097
2098   if (local_only && !gtk_file_system_path_is_local (fs, path))
2099     return FALSE;
2100
2101   if (!is_folder)
2102     return FALSE;
2103
2104   return TRUE;
2105 }
2106
2107 static gboolean
2108 filter_model_visible_func (GtkTreeModel *model,
2109                            GtkTreeIter  *iter,
2110                            gpointer      user_data)
2111 {
2112   GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (user_data);
2113   GtkFileChooserButtonPrivate *priv = button->priv;
2114   gchar type;
2115   gpointer data;
2116   gboolean local_only, retval, is_folder;
2117
2118   type = ROW_TYPE_INVALID;
2119   data = NULL;
2120   local_only = gtk_file_chooser_get_local_only (GTK_FILE_CHOOSER (priv->dialog));
2121
2122   gtk_tree_model_get (model, iter,
2123                       TYPE_COLUMN, &type,
2124                       DATA_COLUMN, &data,
2125                       IS_FOLDER_COLUMN, &is_folder,
2126                       -1);
2127
2128   switch (type)
2129     {
2130     case ROW_TYPE_CURRENT_FOLDER:
2131       retval = TRUE;
2132       break;
2133     case ROW_TYPE_SPECIAL:
2134     case ROW_TYPE_SHORTCUT:
2135     case ROW_TYPE_BOOKMARK:
2136       retval = test_if_path_is_visible (priv->fs, data, local_only, is_folder);
2137       break;
2138     case ROW_TYPE_VOLUME:
2139       {
2140         retval = TRUE;
2141         if (local_only)
2142           {
2143             if (gtk_file_system_volume_get_is_mounted (priv->fs, data))
2144               {
2145                 GtkFilePath *base_path;
2146                 
2147                 base_path = gtk_file_system_volume_get_base_path (priv->fs, data);
2148                 if (base_path)
2149                   {
2150                     gboolean is_local = gtk_file_system_path_is_local (priv->fs, base_path);
2151                     
2152                     gtk_file_path_free (base_path);
2153
2154                     if (!is_local)
2155                       retval = FALSE;
2156                   }
2157                 else
2158                   retval = FALSE;
2159               }
2160           }
2161       }
2162       break;
2163     default:
2164       retval = TRUE;
2165       break;
2166     }
2167
2168   return retval;
2169 }
2170
2171 /* Combo Box */
2172 static void
2173 name_cell_data_func (GtkCellLayout   *layout,
2174                      GtkCellRenderer *cell,
2175                      GtkTreeModel    *model,
2176                      GtkTreeIter     *iter,
2177                      gpointer         user_data)
2178 {
2179   gchar type;
2180
2181   type = 0;
2182   gtk_tree_model_get (model, iter,
2183                       TYPE_COLUMN, &type,
2184                       -1);
2185
2186   if (type == ROW_TYPE_CURRENT_FOLDER)
2187     g_object_set (cell, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
2188   else
2189     g_object_set (cell, "ellipsize", PANGO_ELLIPSIZE_NONE, NULL);
2190 }
2191
2192 static gboolean
2193 combo_box_row_separator_func (GtkTreeModel *model,
2194                               GtkTreeIter  *iter,
2195                               gpointer      user_data)
2196 {
2197   gchar type = ROW_TYPE_INVALID;
2198
2199   gtk_tree_model_get (model, iter, TYPE_COLUMN, &type, -1);
2200
2201   return (type == ROW_TYPE_BOOKMARK_SEPARATOR ||
2202           type == ROW_TYPE_CURRENT_FOLDER_SEPARATOR ||
2203           type == ROW_TYPE_OTHER_SEPARATOR);
2204 }                         
2205
2206 static void
2207 update_combo_box (GtkFileChooserButton *button)
2208 {
2209   GtkFileChooserButtonPrivate *priv = button->priv;
2210   GSList *paths;
2211   GtkTreeIter iter;
2212   gboolean row_found;
2213
2214   gtk_tree_model_get_iter_first (priv->filter_model, &iter);
2215
2216   paths = _gtk_file_chooser_get_paths (GTK_FILE_CHOOSER (priv->dialog));
2217
2218   row_found = FALSE;
2219
2220   do
2221     {
2222       gchar type;
2223       gpointer data;
2224
2225       type = ROW_TYPE_INVALID;
2226       data = NULL;
2227
2228       gtk_tree_model_get (priv->filter_model, &iter,
2229                           TYPE_COLUMN, &type,
2230                           DATA_COLUMN, &data,
2231                           -1);
2232     
2233       switch (type)
2234         {
2235         case ROW_TYPE_SPECIAL:
2236         case ROW_TYPE_SHORTCUT:
2237         case ROW_TYPE_BOOKMARK:
2238         case ROW_TYPE_CURRENT_FOLDER:
2239           row_found = (paths &&
2240                        paths->data &&
2241                        gtk_file_path_compare (data, paths->data) == 0);
2242           break;
2243         case ROW_TYPE_VOLUME:
2244           {
2245             GtkFilePath *base_path;
2246
2247             base_path = gtk_file_system_volume_get_base_path (priv->fs, data);
2248             if (base_path)
2249               {
2250                 row_found = (paths &&
2251                              paths->data &&
2252                              gtk_file_path_compare (base_path, paths->data) == 0);
2253                 gtk_file_path_free (base_path);
2254               }
2255           }
2256           break;
2257         default:
2258           row_found = FALSE;
2259           break;
2260         }
2261
2262       if (row_found)
2263         {
2264           g_signal_handler_block (priv->combo_box, priv->combo_box_changed_id);
2265           gtk_combo_box_set_active_iter (GTK_COMBO_BOX (priv->combo_box),
2266                                          &iter);
2267           g_signal_handler_unblock (priv->combo_box,
2268                                     priv->combo_box_changed_id);
2269         }
2270     }
2271   while (!row_found && gtk_tree_model_iter_next (priv->filter_model, &iter));
2272
2273   /* If it hasn't been found already, update & select the current-folder row. */
2274   if (!row_found && paths && paths->data)
2275     {
2276       GtkTreeIter filter_iter;
2277       gint pos;
2278     
2279       model_update_current_folder (button, paths->data);
2280       gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (priv->filter_model));
2281
2282       pos = model_get_type_position (button, ROW_TYPE_CURRENT_FOLDER);
2283       gtk_tree_model_iter_nth_child (priv->model, &iter, NULL, pos);
2284
2285       gtk_tree_model_filter_convert_child_iter_to_iter (GTK_TREE_MODEL_FILTER (priv->filter_model),
2286                                                         &filter_iter, &iter);
2287
2288       g_signal_handler_block (priv->combo_box, priv->combo_box_changed_id);
2289       gtk_combo_box_set_active_iter (GTK_COMBO_BOX (priv->combo_box), &filter_iter);
2290       g_signal_handler_unblock (priv->combo_box, priv->combo_box_changed_id);
2291     }
2292
2293   gtk_file_paths_free (paths);
2294 }
2295
2296 /* Button */
2297 static void
2298 update_label_get_info_cb (GtkFileSystemHandle *handle,
2299                           const GtkFileInfo   *info,
2300                           const GError        *error,
2301                           gpointer             data)
2302 {
2303   gboolean cancelled = handle->cancelled;
2304   GdkPixbuf *pixbuf;
2305   GtkFileChooserButton *button = data;
2306   GtkFileChooserButtonPrivate *priv = button->priv;
2307
2308   if (handle != priv->update_button_handle)
2309     goto out;
2310
2311   priv->update_button_handle = NULL;
2312
2313   if (cancelled || error)
2314     goto out;
2315
2316   gtk_label_set_text (GTK_LABEL (priv->label), gtk_file_info_get_display_name (info));
2317
2318   pixbuf = gtk_file_info_render_icon (info, GTK_WIDGET (priv->image),
2319                                       priv->icon_size, NULL);
2320   if (!pixbuf)
2321     pixbuf = gtk_icon_theme_load_icon (get_icon_theme (GTK_WIDGET (priv->image)),
2322                                        FALLBACK_ICON_NAME,
2323                                        priv->icon_size, 0, NULL);
2324
2325   gtk_image_set_from_pixbuf (GTK_IMAGE (priv->image), pixbuf);
2326   if (pixbuf)
2327     g_object_unref (pixbuf);
2328
2329 out:
2330   g_object_unref (button);
2331   g_object_unref (handle);
2332 }
2333
2334 static void
2335 update_label_and_image (GtkFileChooserButton *button)
2336 {
2337   GtkFileChooserButtonPrivate *priv = button->priv;
2338   GdkPixbuf *pixbuf;
2339   gchar *label_text;
2340   GSList *paths;
2341
2342   paths = _gtk_file_chooser_get_paths (GTK_FILE_CHOOSER (priv->dialog));
2343   label_text = NULL;
2344   pixbuf = NULL;
2345
2346   if (paths && paths->data)
2347     {
2348       GtkFilePath *path;
2349       GtkFileSystemVolume *volume = NULL;
2350     
2351       path = paths->data;
2352
2353       volume = gtk_file_system_get_volume_for_path (priv->fs, path);
2354       if (volume)
2355         {
2356           GtkFilePath *base_path;
2357
2358           base_path = gtk_file_system_volume_get_base_path (priv->fs, volume);
2359           if (base_path && gtk_file_path_compare (base_path, path) == 0)
2360             {
2361               label_text = gtk_file_system_volume_get_display_name (priv->fs,
2362                                                                     volume);
2363               pixbuf = gtk_file_system_volume_render_icon (priv->fs, volume,
2364                                                            GTK_WIDGET (button),
2365                                                            priv->icon_size,
2366                                                            NULL);
2367             }
2368
2369           if (base_path)
2370             gtk_file_path_free (base_path);
2371
2372           gtk_file_system_volume_free (priv->fs, volume);
2373
2374           if (label_text)
2375             goto out;
2376         }
2377
2378       if (priv->update_button_handle)
2379         {
2380           gtk_file_system_cancel_operation (priv->update_button_handle);
2381           priv->update_button_handle = NULL;
2382         }
2383           
2384       if (gtk_file_system_path_is_local (priv->fs, path))
2385         {
2386           priv->update_button_handle =
2387             gtk_file_system_get_info (priv->fs, path,
2388                                       GTK_FILE_INFO_DISPLAY_NAME | GTK_FILE_INFO_ICON,
2389                                       update_label_get_info_cb,
2390                                       g_object_ref (button));
2391         }
2392       else
2393         {
2394           GdkPixbuf *pixbuf;
2395
2396           label_text = gtk_file_system_get_bookmark_label (button->priv->fs, path);
2397           
2398           pixbuf = gtk_icon_theme_load_icon (get_icon_theme (GTK_WIDGET (priv->image)), 
2399                                              "gnome-fs-regular",
2400                                              priv->icon_size, 0, NULL);
2401           
2402           gtk_image_set_from_pixbuf (GTK_IMAGE (priv->image), pixbuf);
2403
2404           if (pixbuf)
2405             g_object_unref (pixbuf);
2406         }
2407     }
2408 out:
2409   gtk_file_paths_free (paths);
2410
2411   if (label_text)
2412     {
2413       gtk_label_set_text (GTK_LABEL (priv->label), label_text);
2414       g_free (label_text);
2415     }
2416   else
2417     gtk_label_set_text (GTK_LABEL (priv->label), _(FALLBACK_DISPLAY_NAME));
2418 }
2419
2420
2421 /* ************************ *
2422  *  Child Object Callbacks  *
2423  * ************************ */
2424
2425 /* File System */
2426 static void
2427 fs_volumes_changed_cb (GtkFileSystem *fs,
2428                        gpointer       user_data)
2429 {
2430   GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (user_data);
2431   GtkFileChooserButtonPrivate *priv = button->priv;
2432   GSList *volumes;
2433
2434   model_remove_rows (user_data,
2435                      model_get_type_position (user_data, ROW_TYPE_VOLUME),
2436                      priv->n_volumes);
2437
2438   priv->n_volumes = 0;
2439
2440   volumes = gtk_file_system_list_volumes (fs);
2441   model_add_volumes (user_data, volumes);
2442   g_slist_free (volumes);
2443
2444   gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (priv->filter_model));
2445
2446   update_label_and_image (user_data);
2447   update_combo_box (user_data);
2448 }
2449
2450 static void
2451 fs_bookmarks_changed_cb (GtkFileSystem *fs,
2452                          gpointer       user_data)
2453 {
2454   GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (user_data);
2455   GtkFileChooserButtonPrivate *priv = button->priv;
2456   GSList *bookmarks;
2457
2458   bookmarks = gtk_file_system_list_bookmarks (fs);
2459   model_remove_rows (user_data,
2460                      model_get_type_position (user_data,
2461                                               ROW_TYPE_BOOKMARK_SEPARATOR),
2462                      (priv->n_bookmarks + priv->has_bookmark_separator));
2463   priv->has_bookmark_separator = FALSE;
2464   priv->n_bookmarks = 0;
2465   model_add_bookmarks (user_data, bookmarks);
2466   gtk_file_paths_free (bookmarks);
2467
2468   gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (priv->filter_model));
2469
2470   update_label_and_image (user_data);
2471   update_combo_box (user_data);
2472 }
2473
2474 /* Dialog */
2475 static void
2476 open_dialog (GtkFileChooserButton *button)
2477 {
2478   GtkFileChooserButtonPrivate *priv = button->priv;
2479
2480   /* Setup the dialog parent to be chooser button's toplevel, and be modal
2481      as needed. */
2482   if (!GTK_WIDGET_VISIBLE (priv->dialog))
2483     {
2484       GtkWidget *toplevel;
2485
2486       toplevel = gtk_widget_get_toplevel (GTK_WIDGET (button));
2487
2488       if (GTK_WIDGET_TOPLEVEL (toplevel) && GTK_IS_WINDOW (toplevel))
2489         {
2490           if (GTK_WINDOW (toplevel) != gtk_window_get_transient_for (GTK_WINDOW (priv->dialog)))
2491             gtk_window_set_transient_for (GTK_WINDOW (priv->dialog),
2492                                           GTK_WINDOW (toplevel));
2493               
2494           gtk_window_set_modal (GTK_WINDOW (priv->dialog),
2495                                 gtk_window_get_modal (GTK_WINDOW (toplevel)));
2496         }
2497     }
2498
2499   if (!priv->active)
2500     {
2501       GSList *paths;
2502
2503       g_signal_handler_block (priv->dialog,
2504                               priv->dialog_folder_changed_id);
2505       g_signal_handler_block (priv->dialog,
2506                               priv->dialog_file_activated_id);
2507       g_signal_handler_block (priv->dialog,
2508                               priv->dialog_selection_changed_id);
2509       paths = _gtk_file_chooser_get_paths (GTK_FILE_CHOOSER (priv->dialog));
2510       if (paths)
2511         {
2512           if (paths->data)
2513             priv->old_path = gtk_file_path_copy (paths->data);
2514
2515           gtk_file_paths_free (paths);
2516         }
2517
2518       priv->active = TRUE;
2519     }
2520
2521   gtk_widget_set_sensitive (priv->combo_box, FALSE);
2522   gtk_window_present (GTK_WINDOW (priv->dialog));
2523 }
2524
2525 /* Combo Box */
2526 static void
2527 combo_box_changed_cb (GtkComboBox *combo_box,
2528                       gpointer     user_data)
2529 {
2530   GtkTreeIter iter;
2531
2532   if (gtk_combo_box_get_active_iter (combo_box, &iter))
2533     {
2534       GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (user_data);
2535       GtkFileChooserButtonPrivate *priv = button->priv;
2536       gchar type;
2537       gpointer data;
2538
2539       type = ROW_TYPE_INVALID;
2540       data = NULL;
2541
2542       gtk_tree_model_get (priv->filter_model, &iter,
2543                           TYPE_COLUMN, &type,
2544                           DATA_COLUMN, &data,
2545                           -1);
2546
2547       switch (type)
2548         {
2549         case ROW_TYPE_SPECIAL:
2550         case ROW_TYPE_SHORTCUT:
2551         case ROW_TYPE_BOOKMARK:
2552         case ROW_TYPE_CURRENT_FOLDER:
2553           gtk_file_chooser_unselect_all (GTK_FILE_CHOOSER (priv->dialog));
2554           if (data)
2555             _gtk_file_chooser_set_current_folder_path (GTK_FILE_CHOOSER (priv->dialog),
2556                                                        data, NULL);
2557           break;
2558         case ROW_TYPE_VOLUME:
2559           {
2560             GtkFilePath *base_path;
2561
2562             gtk_file_chooser_unselect_all (GTK_FILE_CHOOSER (priv->dialog));
2563             base_path = gtk_file_system_volume_get_base_path (priv->fs, data);
2564             if (base_path)
2565               {
2566                 _gtk_file_chooser_set_current_folder_path (GTK_FILE_CHOOSER (priv->dialog),
2567                                                            base_path, NULL);
2568                 gtk_file_path_free (base_path);
2569               }
2570           }
2571           break;
2572         case ROW_TYPE_OTHER:
2573           open_dialog (user_data);
2574           break;
2575         default:
2576           break;
2577         }
2578     }
2579 }
2580
2581 /* Button */
2582 static void
2583 button_clicked_cb (GtkButton *real_button,
2584                    gpointer   user_data)
2585 {
2586   open_dialog (user_data);
2587 }
2588
2589 /* Dialog */
2590 static void
2591 dialog_current_folder_changed_cb (GtkFileChooser *dialog,
2592                                   gpointer        user_data)
2593 {
2594   GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (user_data);
2595   GtkFileChooserButtonPrivate *priv = button->priv;
2596
2597   priv->folder_has_been_set = TRUE;
2598
2599   g_signal_emit_by_name (button, "current-folder-changed");
2600 }
2601
2602 static void
2603 dialog_file_activated_cb (GtkFileChooser *dialog,
2604                           gpointer        user_data)
2605 {
2606   g_signal_emit_by_name (user_data, "file-activated");
2607 }
2608
2609 static void
2610 dialog_selection_changed_cb (GtkFileChooser *dialog,
2611                              gpointer        user_data)
2612 {
2613   update_label_and_image (user_data);
2614   update_combo_box (user_data);
2615   g_signal_emit_by_name (user_data, "selection-changed");
2616 }
2617
2618 static void
2619 dialog_update_preview_cb (GtkFileChooser *dialog,
2620                           gpointer        user_data)
2621 {
2622   g_signal_emit_by_name (user_data, "update-preview");
2623 }
2624
2625 static void
2626 dialog_notify_cb (GObject    *dialog,
2627                   GParamSpec *pspec,
2628                   gpointer    user_data)
2629 {
2630   gpointer iface;
2631
2632   iface = g_type_interface_peek (g_type_class_peek (G_OBJECT_TYPE (dialog)),
2633                                  GTK_TYPE_FILE_CHOOSER);
2634   if (g_object_interface_find_property (iface, pspec->name))
2635     g_object_notify (user_data, pspec->name);
2636
2637   if (g_ascii_strcasecmp (pspec->name, "local-only") == 0)
2638     {
2639       GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (user_data);
2640       GtkFileChooserButtonPrivate *priv = button->priv;
2641
2642       if (priv->has_current_folder)
2643         {
2644           GtkTreeIter iter;
2645           gint pos;
2646           gpointer data;
2647
2648           pos = model_get_type_position (user_data,
2649                                          ROW_TYPE_CURRENT_FOLDER);
2650           gtk_tree_model_iter_nth_child (priv->model, &iter, NULL, pos);
2651
2652           data = NULL;
2653           gtk_tree_model_get (priv->model, &iter, DATA_COLUMN, &data, -1);
2654
2655           /* If the path isn't local but we're in local-only mode now, remove
2656            * the custom-folder row */
2657           if (data &&
2658               (!gtk_file_system_path_is_local (priv->fs, data) &&
2659                gtk_file_chooser_get_local_only (GTK_FILE_CHOOSER (priv->dialog))))
2660             {
2661               pos--;
2662               model_remove_rows (user_data, pos, 2);
2663             }
2664         }
2665
2666       gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (priv->filter_model));
2667       update_combo_box (user_data);
2668     }
2669 }
2670
2671 static gboolean
2672 dialog_delete_event_cb (GtkWidget *dialog,
2673                         GdkEvent  *event,
2674                         gpointer   user_data)
2675 {
2676   g_signal_emit_by_name (dialog, "response", GTK_RESPONSE_DELETE_EVENT);
2677
2678   return TRUE;
2679 }
2680
2681 static void
2682 dialog_response_cb (GtkDialog *dialog,
2683                     gint       response,
2684                     gpointer   user_data)
2685 {
2686   GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (user_data);
2687   GtkFileChooserButtonPrivate *priv = button->priv;
2688
2689   if (response == GTK_RESPONSE_ACCEPT)
2690     {
2691       g_signal_emit_by_name (user_data, "current-folder-changed");
2692       g_signal_emit_by_name (user_data, "selection-changed");
2693     }
2694   else if (priv->old_path)
2695     {
2696       switch (gtk_file_chooser_get_action (GTK_FILE_CHOOSER (dialog)))
2697         {
2698         case GTK_FILE_CHOOSER_ACTION_OPEN:
2699           _gtk_file_chooser_select_path (GTK_FILE_CHOOSER (dialog), priv->old_path,
2700                                          NULL);
2701           break;
2702         case GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER:
2703           _gtk_file_chooser_set_current_folder_path (GTK_FILE_CHOOSER (dialog),
2704                                                      priv->old_path, NULL);
2705           break;
2706         default:
2707           g_assert_not_reached ();
2708           break;
2709         }
2710     }
2711   else
2712     gtk_file_chooser_unselect_all (GTK_FILE_CHOOSER (dialog));
2713
2714   if (priv->old_path)
2715     {
2716       gtk_file_path_free (priv->old_path);
2717       priv->old_path = NULL;
2718     }
2719
2720   update_label_and_image (user_data);
2721   update_combo_box (user_data);
2722   
2723   if (priv->active)
2724     {
2725       g_signal_handler_unblock (priv->dialog,
2726                                 priv->dialog_folder_changed_id);
2727       g_signal_handler_unblock (priv->dialog,
2728                                 priv->dialog_file_activated_id);
2729       g_signal_handler_unblock (priv->dialog,
2730                                 priv->dialog_selection_changed_id);
2731       priv->active = FALSE;
2732     }
2733
2734   gtk_widget_set_sensitive (priv->combo_box, TRUE);
2735   gtk_widget_hide (priv->dialog);
2736
2737   g_signal_emit_by_name (user_data, "file-set");
2738 }
2739
2740
2741 /* ************************************************************************** *
2742  *  Public API                                                                *
2743  * ************************************************************************** */
2744
2745 /**
2746  * gtk_file_chooser_button_new:
2747  * @title: the title of the browse dialog.
2748  * @action: the open mode for the widget.
2749  * 
2750  * Creates a new file-selecting button widget.
2751  * 
2752  * Returns: a new button widget.
2753  * 
2754  * Since: 2.6
2755  **/
2756 GtkWidget *
2757 gtk_file_chooser_button_new (const gchar          *title,
2758                              GtkFileChooserAction  action)
2759 {
2760   g_return_val_if_fail (action == GTK_FILE_CHOOSER_ACTION_OPEN ||
2761                         action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, NULL);
2762
2763   return g_object_new (GTK_TYPE_FILE_CHOOSER_BUTTON,
2764                        "action", action,
2765                        "title", (title ? title : _(DEFAULT_TITLE)),
2766                        NULL);
2767 }
2768
2769 /**
2770  * gtk_file_chooser_button_new_with_backend:
2771  * @title: the title of the browse dialog.
2772  * @action: the open mode for the widget.
2773  * @backend: the name of the #GtkFileSystem backend to use.
2774  * 
2775  * Creates a new file-selecting button widget using @backend.
2776  * 
2777  * Returns: a new button widget.
2778  * 
2779  * Since: 2.6
2780  **/
2781 GtkWidget *
2782 gtk_file_chooser_button_new_with_backend (const gchar          *title,
2783                                           GtkFileChooserAction  action,
2784                                           const gchar          *backend)
2785 {
2786   g_return_val_if_fail (action == GTK_FILE_CHOOSER_ACTION_OPEN ||
2787                         action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, NULL);
2788
2789   return g_object_new (GTK_TYPE_FILE_CHOOSER_BUTTON,
2790                        "action", action,
2791                        "title", (title ? title : _(DEFAULT_TITLE)),
2792                        "file-system-backend", backend,
2793                        NULL);
2794 }
2795
2796 /**
2797  * gtk_file_chooser_button_new_with_dialog:
2798  * @dialog: the widget to use as dialog
2799  * 
2800  * Creates a #GtkFileChooserButton widget which uses @dialog as it's
2801  * file-picking window. Note that @dialog must be a #GtkDialog (or
2802  * subclass) which implements the #GtkFileChooser interface and must 
2803  * not have %GTK_DIALOG_DESTROY_WITH_PARENT set.
2804  * 
2805  * Returns: a new button widget.
2806  * 
2807  * Since: 2.6
2808  **/
2809 GtkWidget *
2810 gtk_file_chooser_button_new_with_dialog (GtkWidget *dialog)
2811 {
2812   g_return_val_if_fail (GTK_IS_FILE_CHOOSER (dialog) && GTK_IS_DIALOG (dialog), NULL);
2813
2814   return g_object_new (GTK_TYPE_FILE_CHOOSER_BUTTON,
2815                        "dialog", dialog,
2816                        NULL);
2817 }
2818
2819 /**
2820  * gtk_file_chooser_button_set_title:
2821  * @button: the button widget to modify.
2822  * @title: the new browse dialog title.
2823  * 
2824  * Modifies the @title of the browse dialog used by @button.
2825  * 
2826  * Since: 2.6
2827  **/
2828 void
2829 gtk_file_chooser_button_set_title (GtkFileChooserButton *button,
2830                                    const gchar          *title)
2831 {
2832   g_return_if_fail (GTK_IS_FILE_CHOOSER_BUTTON (button));
2833
2834   gtk_window_set_title (GTK_WINDOW (button->priv->dialog), title);
2835   g_object_notify (G_OBJECT (button), "title");
2836 }
2837
2838 /**
2839  * gtk_file_chooser_button_get_title:
2840  * @button: the button widget to examine.
2841  * 
2842  * Retrieves the title of the browse dialog used by @button. The returned value
2843  * should not be modified or freed.
2844  * 
2845  * Returns: a pointer to the browse dialog's title.
2846  * 
2847  * Since: 2.6
2848  **/
2849 G_CONST_RETURN gchar *
2850 gtk_file_chooser_button_get_title (GtkFileChooserButton *button)
2851 {
2852   g_return_val_if_fail (GTK_IS_FILE_CHOOSER_BUTTON (button), NULL);
2853
2854   return gtk_window_get_title (GTK_WINDOW (button->priv->dialog));
2855 }
2856
2857 /**
2858  * gtk_file_chooser_button_get_width_chars:
2859  * @button: the button widget to examine.
2860  * 
2861  * Retrieves the width in characters of the @button widget's entry and/or label.
2862  * 
2863  * Returns: an integer width (in characters) that the button will use to size itself.
2864  * 
2865  * Since: 2.6
2866  **/
2867 gint
2868 gtk_file_chooser_button_get_width_chars (GtkFileChooserButton *button)
2869 {
2870   g_return_val_if_fail (GTK_IS_FILE_CHOOSER_BUTTON (button), -1);
2871
2872   return gtk_label_get_width_chars (GTK_LABEL (button->priv->label));
2873 }
2874
2875 /**
2876  * gtk_file_chooser_button_set_width_chars:
2877  * @button: the button widget to examine.
2878  * @n_chars: the new width, in characters.
2879  * 
2880  * Sets the width (in characters) that @button will use to @n_chars.
2881  * 
2882  * Since: 2.6
2883  **/
2884 void
2885 gtk_file_chooser_button_set_width_chars (GtkFileChooserButton *button,
2886                                          gint                  n_chars)
2887 {
2888   g_return_if_fail (GTK_IS_FILE_CHOOSER_BUTTON (button));
2889
2890   gtk_label_set_width_chars (GTK_LABEL (button->priv->label), n_chars);
2891   g_object_notify (G_OBJECT (button), "width-chars");
2892 }
2893
2894 /**
2895  * gtk_file_chooser_button_set_focus_on_click:
2896  * @button: a #GtkFileChooserButton
2897  * @focus_on_click: whether the button grabs focus when clicked with the mouse
2898  * 
2899  * Sets whether the button will grab focus when it is clicked with the mouse.
2900  * Making mouse clicks not grab focus is useful in places like toolbars where
2901  * you don't want the keyboard focus removed from the main area of the
2902  * application.
2903  *
2904  * Since: 2.10
2905  **/
2906 void
2907 gtk_file_chooser_button_set_focus_on_click (GtkFileChooserButton *button,
2908                                             gboolean              focus_on_click)
2909 {
2910   GtkFileChooserButtonPrivate *priv;
2911
2912   g_return_if_fail (GTK_IS_FILE_CHOOSER_BUTTON (button));
2913
2914   priv = button->priv;
2915
2916   focus_on_click = focus_on_click != FALSE;
2917
2918   if (priv->focus_on_click != focus_on_click)
2919     {
2920       priv->focus_on_click = focus_on_click;
2921       gtk_button_set_focus_on_click (GTK_BUTTON (priv->button), focus_on_click);
2922       gtk_combo_box_set_focus_on_click (GTK_COMBO_BOX (priv->combo_box), focus_on_click);
2923       
2924       g_object_notify (G_OBJECT (button), "focus-on-click");
2925     }
2926 }
2927
2928 /**
2929  * gtk_file_chooser_button_get_focus_on_click:
2930  * @button: a #GtkFileChooserButton
2931  * 
2932  * Returns whether the button grabs focus when it is clicked with the mouse.
2933  * See gtk_file_chooser_button_set_focus_on_click().
2934  *
2935  * Return value: %TRUE if the button grabs focus when it is clicked with
2936  *               the mouse.
2937  *
2938  * Since: 2.10
2939  **/
2940 gboolean
2941 gtk_file_chooser_button_get_focus_on_click (GtkFileChooserButton *button)
2942 {
2943   g_return_val_if_fail (GTK_IS_FILE_CHOOSER_BUTTON (button), FALSE);
2944   
2945   return button->priv->focus_on_click;
2946 }
2947
2948 #define __GTK_FILE_CHOOSER_BUTTON_C__
2949 #include "gtkaliasdef.c"