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