]> Pileus Git - ~andy/gtk/blob - gtk/gtkfilechooserbutton.c
Move some docs inline.
[~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 "gtkalias.h"
34 #include "gtkintl.h"
35 #include "gtkdnd.h"
36 #include "gtkentry.h"
37 #include "gtkhbox.h"
38 #include "gtkicontheme.h"
39 #include "gtkiconfactory.h"
40 #include "gtkimage.h"
41 #include "gtklabel.h"
42 #include "gtkstock.h"
43 #include "gtktogglebutton.h"
44 #include "gtkvseparator.h"
45 #include "gtkfilechooserdialog.h"
46 #include "gtkfilechooserentry.h"
47 #include "gtkfilechooserprivate.h"
48 #include "gtkfilechooserutils.h"
49
50 #include "gtkfilechooserbutton.h"
51
52
53 /* **************** *
54  *  Private Macros  *
55  * **************** */
56
57 #define GTK_FILE_CHOOSER_BUTTON_GET_PRIVATE(object) (GTK_FILE_CHOOSER_BUTTON ((object))->priv)
58
59 #define DEFAULT_FILENAME        N_("(None)")
60 #define MIN_LABEL_WIDTH         100
61 #define ENTRY_BUTTON_SPACING    0
62 #define FALLBACK_ICON_SIZE      20
63 #define FALLBACK_ICON_NAME      "stock_unknown"
64 #define NEW_FILE_ICON_NAME      "stock_new"
65 #define NEW_DIR_ICON_NAME       "stock_new-dir"
66
67 /* ********************** *
68  *  Private Enumerations  *
69  * ********************** */
70
71 /* Property IDs */
72 enum
73 {
74   PROP_0,
75
76   PROP_DIALOG,
77   PROP_TITLE,
78   PROP_ACTIVE,
79   PROP_WIDTH_CHARS
80 };
81
82
83 /* ******************** *
84  *  Private Structures  *
85  * ******************** */
86
87 struct _GtkFileChooserButtonPrivate
88 {
89   GtkWidget *dialog;
90   GtkWidget *accept_button;
91   GtkWidget *entry_box;
92   GtkWidget *entry_image;
93   GtkWidget *entry;
94   GtkWidget *label_box;
95   GtkWidget *label_image;
96   GtkWidget *label;
97   GtkWidget *button;
98
99   gchar *backend;
100   gulong entry_changed_id;
101   gulong dialog_file_activated_id;
102   gulong dialog_folder_changed_id;
103   gulong dialog_selection_changed_id;
104   gulong dialog_selection_changed_proxy_id;
105   gulong settings_signal_id;
106   guint update_id;
107   gint icon_size;
108 };
109
110
111 /* ************* *
112  *  DnD Support  *
113  * ************* */
114
115 enum
116 {
117   TEXT_PLAIN,
118   TEXT_URI_LIST
119 };
120
121 /* ********************* *
122  *  Function Prototypes  *
123  * ********************* */
124
125 /* GObject Functions */
126 static GObject *gtk_file_chooser_button_constructor        (GType             type,
127                                                             guint             n_params,
128                                                             GObjectConstructParam *params);
129 static void     gtk_file_chooser_button_set_property       (GObject          *object,
130                                                             guint             param_id,
131                                                             const GValue     *value,
132                                                             GParamSpec       *pspec);
133 static void     gtk_file_chooser_button_get_property       (GObject          *object,
134                                                             guint             param_id,
135                                                             GValue           *value,
136                                                             GParamSpec       *pspec);
137
138 /* GtkObject Functions */
139 static void     gtk_file_chooser_button_destroy            (GtkObject        *object);
140
141 /* GtkWidget Functions */
142 static void     gtk_file_chooser_button_drag_data_received (GtkWidget        *widget,
143                                                             GdkDragContext   *context,
144                                                             gint              x,
145                                                             gint              y,
146                                                             GtkSelectionData *data,
147                                                             guint             info,
148                                                             guint             drag_time);
149 static void     gtk_file_chooser_button_show_all           (GtkWidget        *widget);
150 static void     gtk_file_chooser_button_hide_all           (GtkWidget        *widget);
151 static void     gtk_file_chooser_button_show               (GtkWidget        *widget);
152 static void     gtk_file_chooser_button_hide               (GtkWidget        *widget);
153 static gboolean gtk_file_chooser_button_mnemonic_activate  (GtkWidget        *widget,
154                                                             gboolean          group_cycling);
155 static void     gtk_file_chooser_button_style_set          (GtkWidget        *widget,
156                                                             GtkStyle         *old_style);
157 static void     gtk_file_chooser_button_screen_changed     (GtkWidget        *widget,
158                                                             GdkScreen        *old_screen);
159
160 /* Child Widget Callbacks */
161 static void     dialog_update_preview_cb                   (GtkFileChooser   *dialog,
162                                                             gpointer          user_data);
163 static void     dialog_selection_changed_cb                (GtkFileChooser   *dialog,
164                                                             gpointer          user_data);
165 static void     dialog_selection_changed_proxy_cb          (GtkFileChooser   *dialog,
166                                                             gpointer          user_data);
167 static void     dialog_file_activated_cb                   (GtkFileChooser   *dialog,
168                                                             gpointer          user_data);
169 static void     dialog_current_folder_changed_cb           (GtkFileChooser   *dialog,
170                                                             gpointer          user_data);
171 static void     dialog_notify_cb                           (GObject          *dialog,
172                                                             GParamSpec       *pspec,
173                                                             gpointer          user_data);
174 static gboolean dialog_delete_event_cb                     (GtkWidget        *dialog,
175                                                             GdkEvent         *event,
176                                                             gpointer          user_data);
177 static void     dialog_response_cb                         (GtkFileChooser   *dialog,
178                                                             gint              response,
179                                                             gpointer          user_data);
180
181 static void     button_toggled_cb                          (GtkToggleButton  *real_button,
182                                                             gpointer          user_data);
183 static void     button_notify_active_cb                    (GObject          *real_button,
184                                                             GParamSpec       *pspec,
185                                                             gpointer          user_data);
186
187 static void     entry_size_allocate_cb                     (GtkWidget        *entry,
188                                                             GtkAllocation    *allocation,
189                                                             gpointer          user_data);
190 static void     entry_changed_cb                           (GtkEditable      *editable,
191                                                             gpointer          user_data);
192
193 /* Utility Functions */
194 static void     remove_settings_signal                     (GtkFileChooserButton *button,
195                                                             GdkScreen            *screen);
196
197 static void     update_dialog                              (GtkFileChooserButton *button);
198 static void     update_entry                               (GtkFileChooserButton *button);
199 static void     update_label                               (GtkFileChooserButton *button);
200 static void     update_icons                               (GtkFileChooserButton *button);
201
202
203 /* ******************* *
204  *  GType Declaration  *
205  * ******************* */
206
207 G_DEFINE_TYPE_WITH_CODE (GtkFileChooserButton, gtk_file_chooser_button, GTK_TYPE_HBOX, { \
208     G_IMPLEMENT_INTERFACE (GTK_TYPE_FILE_CHOOSER, _gtk_file_chooser_delegate_iface_init) \
209 });
210
211
212 /* ***************** *
213  *  GType Functions  *
214  * ***************** */
215
216 static void
217 gtk_file_chooser_button_class_init (GtkFileChooserButtonClass * class)
218 {
219   GObjectClass *gobject_class;
220   GtkObjectClass *gtkobject_class;
221   GtkWidgetClass *widget_class;
222
223   gobject_class = G_OBJECT_CLASS (class);
224   gtkobject_class = GTK_OBJECT_CLASS (class);
225   widget_class = GTK_WIDGET_CLASS (class);
226
227   gobject_class->constructor = gtk_file_chooser_button_constructor;
228   gobject_class->set_property = gtk_file_chooser_button_set_property;
229   gobject_class->get_property = gtk_file_chooser_button_get_property;
230
231   gtkobject_class->destroy = gtk_file_chooser_button_destroy;
232
233   widget_class->drag_data_received = gtk_file_chooser_button_drag_data_received;
234   widget_class->show_all = gtk_file_chooser_button_show_all;
235   widget_class->hide_all = gtk_file_chooser_button_hide_all;
236   widget_class->show = gtk_file_chooser_button_show;
237   widget_class->hide = gtk_file_chooser_button_hide;
238   widget_class->style_set = gtk_file_chooser_button_style_set;
239   widget_class->screen_changed = gtk_file_chooser_button_screen_changed;
240   widget_class->mnemonic_activate = gtk_file_chooser_button_mnemonic_activate;
241
242   /**
243    * GtkFileChooserButton:dialog:
244    * 
245    * Instance of the #GtkFileChooserDialog associated with the button.
246    *
247    * Since: 2.6
248    */
249   g_object_class_install_property (gobject_class, PROP_DIALOG,
250                                    g_param_spec_object ("dialog",
251                                                         P_("Dialog"),
252                                                         P_("The file chooser dialog to use."),
253                                                         GTK_TYPE_FILE_CHOOSER_DIALOG,
254                                                         (G_PARAM_WRITABLE |
255                                                          G_PARAM_CONSTRUCT_ONLY)));
256
257   /**
258    * GtkFileChooserButton:title:
259    * 
260    * Title to put on the #GtkFileChooserDialog associated with the button.
261    *
262    * Since: 2.6
263    */
264   g_object_class_install_property (gobject_class, PROP_TITLE,
265                                    g_param_spec_string ("title",
266                                                         P_("Title"),
267                                                         P_("The title of the file chooser dialog."),
268                                                         _("Select a File"),
269                                                         G_PARAM_READWRITE));
270
271   /**
272    * GtkFileChooserButton:active:
273    * 
274    * %TRUE, if the #GtkFileChooserDialog associated with the button has been
275    * made visible.  This can also be set by the application, though it is
276    * rarely useful to do so.
277    *
278    * Since: 2.6
279    */
280   g_object_class_install_property (gobject_class, PROP_ACTIVE,
281                                    g_param_spec_boolean ("active",
282                                                          P_("Active"),
283                                                          P_("Whether the browse dialog is visible or not."),
284                                                          FALSE, G_PARAM_READWRITE));
285
286   /**
287    * GtkFileChooserButton:
288    * 
289    * The width of the entry and label inside the button, in characters.
290    *
291    * Since: 2.6
292    */
293   g_object_class_install_property (gobject_class, PROP_WIDTH_CHARS,
294                                    g_param_spec_int ("width-chars",
295                                                      P_("Width In Characters"),
296                                                      P_("The desired width of the button widget, in characters."),
297                                                      -1, G_MAXINT, -1,
298                                                      G_PARAM_READWRITE));
299
300   _gtk_file_chooser_install_properties (gobject_class);
301
302   g_type_class_add_private (class, sizeof (GtkFileChooserButtonPrivate));
303 }
304
305
306 static void
307 gtk_file_chooser_button_init (GtkFileChooserButton *button)
308 {
309   GtkFileChooserButtonPrivate *priv;
310   GtkWidget *box, *image, *sep;
311
312   gtk_box_set_spacing (GTK_BOX (button), ENTRY_BUTTON_SPACING);
313
314   priv = G_TYPE_INSTANCE_GET_PRIVATE (button, GTK_TYPE_FILE_CHOOSER_BUTTON,
315                                       GtkFileChooserButtonPrivate);
316   button->priv = priv;
317
318   priv->icon_size = FALLBACK_ICON_SIZE;
319
320   gtk_widget_push_composite_child ();
321
322   priv->entry_box = gtk_hbox_new (FALSE, 4);
323   gtk_container_add (GTK_CONTAINER (button), priv->entry_box);
324
325   priv->entry_image = gtk_image_new ();
326   gtk_box_pack_start (GTK_BOX (priv->entry_box), priv->entry_image,
327                       FALSE, FALSE, 0);
328   gtk_widget_show (priv->entry_image);
329
330   priv->entry = _gtk_file_chooser_entry_new (FALSE);
331   gtk_container_add (GTK_CONTAINER (priv->entry_box), priv->entry);
332   gtk_widget_show (priv->entry);
333
334   priv->button = gtk_toggle_button_new ();
335   g_signal_connect (priv->button, "toggled",
336                     G_CALLBACK (button_toggled_cb), button);
337   g_signal_connect (priv->button, "notify::active",
338                     G_CALLBACK (button_notify_active_cb), button);
339   g_signal_connect (priv->entry, "size-allocate",
340                     G_CALLBACK (entry_size_allocate_cb), priv->button);
341   gtk_box_pack_start (GTK_BOX (button), priv->button, TRUE, TRUE, 0);
342   gtk_widget_show (priv->button);
343
344   box = gtk_hbox_new (FALSE, 4);
345   gtk_container_add (GTK_CONTAINER (priv->button), box);
346   gtk_widget_show (box);
347   
348   priv->label_box = gtk_hbox_new (FALSE, 6);
349   gtk_container_add (GTK_CONTAINER (box), priv->label_box);
350   gtk_widget_show (priv->label_box);
351
352   priv->label_image = gtk_image_new ();
353   gtk_box_pack_start (GTK_BOX (priv->label_box), priv->label_image,
354                       FALSE, FALSE, 0);
355   gtk_widget_show (priv->label_image);
356
357   priv->label = gtk_label_new (_(DEFAULT_FILENAME));
358   gtk_label_set_ellipsize (GTK_LABEL (priv->label), PANGO_ELLIPSIZE_START);
359   gtk_misc_set_alignment (GTK_MISC (priv->label), 0.0, 0.5);
360   gtk_container_add (GTK_CONTAINER (priv->label_box), priv->label);
361   gtk_widget_show (priv->label);
362
363   sep = gtk_vseparator_new ();
364   gtk_box_pack_end (GTK_BOX (priv->label_box), sep, FALSE, FALSE, 0);
365   gtk_widget_show (sep);
366
367   image = gtk_image_new_from_stock (GTK_STOCK_OPEN,
368                                     GTK_ICON_SIZE_SMALL_TOOLBAR);
369   gtk_box_pack_end (GTK_BOX (box), image, FALSE, FALSE, 0);
370   gtk_widget_show (image);
371
372   gtk_widget_pop_composite_child ();
373
374   /* DnD */
375   gtk_drag_dest_unset (priv->entry);
376   gtk_drag_dest_set (GTK_WIDGET (button),
377                      (GTK_DEST_DEFAULT_ALL),
378                      NULL, 0,
379                      GDK_ACTION_COPY);
380   gtk_drag_dest_add_text_targets (GTK_WIDGET (button));
381   gtk_target_list_add_uri_targets (gtk_drag_dest_get_target_list (GTK_WIDGET (button)), 
382                                    TEXT_URI_LIST);
383 }
384
385
386 /* ******************* *
387  *  GObject Functions  *
388  * ******************* */
389
390 static GObject *
391 gtk_file_chooser_button_constructor (GType                  type,
392                                      guint                  n_params,
393                                      GObjectConstructParam *params)
394 {
395   GObject *object;
396   GtkFileChooserButtonPrivate *priv;
397   GtkFilePath *path;
398
399   object = (*G_OBJECT_CLASS (gtk_file_chooser_button_parent_class)->constructor) (type,
400                                                                                   n_params,
401                                                                                   params);
402   priv = GTK_FILE_CHOOSER_BUTTON_GET_PRIVATE (object);
403
404   if (!priv->dialog)
405     {
406       if (priv->backend)
407         priv->dialog = gtk_file_chooser_dialog_new_with_backend (NULL, NULL,
408                                                                  GTK_FILE_CHOOSER_ACTION_OPEN,
409                                                                  priv->backend, NULL);
410       else
411         priv->dialog = gtk_file_chooser_dialog_new (NULL, NULL,
412                                                     GTK_FILE_CHOOSER_ACTION_OPEN,
413                                                     NULL);
414
415       gtk_dialog_add_button (GTK_DIALOG (priv->dialog),
416                              GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
417       priv->accept_button = gtk_dialog_add_button (GTK_DIALOG (priv->dialog),
418                                                    GTK_STOCK_OPEN,
419                                                    GTK_RESPONSE_ACCEPT);
420       gtk_dialog_set_default_response (GTK_DIALOG (priv->dialog),
421                                        GTK_RESPONSE_ACCEPT);
422
423       gtk_dialog_set_alternative_button_order (GTK_DIALOG (priv->dialog),
424                                                GTK_RESPONSE_ACCEPT,
425                                                GTK_RESPONSE_CANCEL,
426                                                -1);
427     }
428
429   g_free (priv->backend);
430   priv->backend = NULL;
431
432   g_signal_connect (priv->dialog, "delete-event",
433                     G_CALLBACK (dialog_delete_event_cb), object);
434   g_signal_connect (priv->dialog, "response",
435                     G_CALLBACK (dialog_response_cb), object);
436
437   /* This is used, instead of the standard delegate, to ensure that signals are only
438    * delegated when the OK button is pressed. */
439   g_object_set_qdata (object, GTK_FILE_CHOOSER_DELEGATE_QUARK, priv->dialog);
440   priv->dialog_folder_changed_id =
441     g_signal_connect (priv->dialog, "current-folder-changed",
442                       G_CALLBACK (dialog_current_folder_changed_cb), object);
443   priv->dialog_file_activated_id =
444     g_signal_connect (priv->dialog, "file-activated",
445                       G_CALLBACK (dialog_file_activated_cb), object);
446   priv->dialog_selection_changed_id =
447     g_signal_connect (priv->dialog, "selection-changed",
448                       G_CALLBACK (dialog_selection_changed_cb), object);
449   priv->dialog_selection_changed_proxy_id =
450     g_signal_connect (priv->dialog, "selection-changed",
451                       G_CALLBACK (dialog_selection_changed_proxy_cb), object);
452   g_signal_connect (priv->dialog, "update-preview",
453                     G_CALLBACK (dialog_update_preview_cb), object);
454   g_signal_connect (priv->dialog, "notify",
455                     G_CALLBACK (dialog_notify_cb), object);
456   g_object_add_weak_pointer (G_OBJECT (priv->dialog),
457                              (gpointer *) (&priv->dialog));
458
459   _gtk_file_chooser_entry_set_file_system (GTK_FILE_CHOOSER_ENTRY (priv->entry),
460                                            _gtk_file_chooser_get_file_system (GTK_FILE_CHOOSER (priv->dialog)));
461   path = gtk_file_path_new_steal ("/");
462   _gtk_file_chooser_entry_set_base_folder (GTK_FILE_CHOOSER_ENTRY (priv->entry),
463                                            path);
464   priv->entry_changed_id = g_signal_connect (priv->entry, "changed",
465                                              G_CALLBACK (entry_changed_cb),
466                                              object);
467
468   update_label (GTK_FILE_CHOOSER_BUTTON (object));
469
470   return object;
471 }
472
473 static void
474 gtk_file_chooser_button_set_property (GObject      *object,
475                                       guint         param_id,
476                                       const GValue *value,
477                                       GParamSpec   *pspec)
478 {
479   GtkFileChooserButtonPrivate *priv;
480
481   priv = GTK_FILE_CHOOSER_BUTTON_GET_PRIVATE (object);
482
483   switch (param_id)
484     {
485     case PROP_DIALOG:
486       /* Construct-only */
487       priv->dialog = g_value_get_object (value);
488       break;
489     case PROP_ACTIVE:
490       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->button),
491                                     g_value_get_boolean (value));
492       break;
493     case PROP_WIDTH_CHARS:
494       gtk_file_chooser_button_set_width_chars (GTK_FILE_CHOOSER_BUTTON (object),
495                                                g_value_get_int (value));
496       break;
497
498     case GTK_FILE_CHOOSER_PROP_ACTION:
499       g_object_set_property (G_OBJECT (priv->dialog), pspec->name, value);
500       _gtk_file_chooser_entry_set_action (GTK_FILE_CHOOSER_ENTRY (priv->entry),
501                                           (GtkFileChooserAction) g_value_get_enum (value));
502       update_icons (GTK_FILE_CHOOSER_BUTTON (object));
503
504       switch (g_value_get_enum (value))
505         {
506         case GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER:
507           gtk_file_chooser_unselect_all (GTK_FILE_CHOOSER (priv->dialog));
508           /* Fall through to set the widget states */
509         case GTK_FILE_CHOOSER_ACTION_OPEN:
510           gtk_widget_hide (priv->entry_box);
511           gtk_widget_show (priv->label_box);
512           gtk_box_set_child_packing (GTK_BOX (object), priv->button,
513                                      TRUE, TRUE, 0, GTK_PACK_START);
514           gtk_button_set_label (GTK_BUTTON (priv->accept_button),
515                                 GTK_STOCK_OPEN);
516           break;
517
518         case GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER:
519           gtk_file_chooser_unselect_all (GTK_FILE_CHOOSER (priv->dialog));
520           /* Fall through to set the widget states */
521         case GTK_FILE_CHOOSER_ACTION_SAVE:
522           gtk_widget_show (priv->entry_box);
523           gtk_widget_hide (priv->label_box);
524           gtk_box_set_child_packing (GTK_BOX (object), priv->button,
525                                      FALSE, FALSE, 0, GTK_PACK_START);
526           gtk_button_set_label (GTK_BUTTON (priv->accept_button),
527                                 GTK_STOCK_SAVE);
528           break;
529         }
530       break;
531
532     case PROP_TITLE:
533     case GTK_FILE_CHOOSER_PROP_FILTER:
534     case GTK_FILE_CHOOSER_PROP_LOCAL_ONLY:
535     case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET:
536     case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET_ACTIVE:
537     case GTK_FILE_CHOOSER_PROP_USE_PREVIEW_LABEL:
538     case GTK_FILE_CHOOSER_PROP_EXTRA_WIDGET:
539     case GTK_FILE_CHOOSER_PROP_SHOW_HIDDEN:
540       g_object_set_property (G_OBJECT (priv->dialog), pspec->name, value);
541       break;
542
543     case GTK_FILE_CHOOSER_PROP_FILE_SYSTEM_BACKEND:
544       /* Construct-only */
545       priv->backend = g_value_dup_string (value);
546       break;
547
548     case GTK_FILE_CHOOSER_PROP_SELECT_MULTIPLE:
549       g_warning ("%s: Choosers of type `%s` do not support selecting multiple files.",
550                  G_STRFUNC, G_OBJECT_TYPE_NAME (object));
551       break;
552     default:
553       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
554       break;
555     }
556 }
557
558 static void
559 gtk_file_chooser_button_get_property (GObject    *object,
560                                       guint       param_id,
561                                       GValue     *value,
562                                       GParamSpec *pspec)
563 {
564   GtkFileChooserButtonPrivate *priv;
565
566   priv = GTK_FILE_CHOOSER_BUTTON_GET_PRIVATE (object);
567
568   switch (param_id)
569     {
570     case PROP_ACTIVE:
571       g_value_set_boolean (value,
572                            gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->button)));
573       break;
574     case PROP_WIDTH_CHARS:
575       g_value_set_int (value,
576                        gtk_entry_get_width_chars (GTK_ENTRY (priv->entry)));
577       break;
578
579     case PROP_TITLE:
580     case GTK_FILE_CHOOSER_PROP_ACTION:
581     case GTK_FILE_CHOOSER_PROP_FILE_SYSTEM_BACKEND:
582     case GTK_FILE_CHOOSER_PROP_FILTER:
583     case GTK_FILE_CHOOSER_PROP_LOCAL_ONLY:
584     case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET:
585     case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET_ACTIVE:
586     case GTK_FILE_CHOOSER_PROP_USE_PREVIEW_LABEL:
587     case GTK_FILE_CHOOSER_PROP_EXTRA_WIDGET:
588     case GTK_FILE_CHOOSER_PROP_SELECT_MULTIPLE:
589     case GTK_FILE_CHOOSER_PROP_SHOW_HIDDEN:
590       g_object_get_property (G_OBJECT (priv->dialog), pspec->name, value);
591       break;
592
593     default:
594       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
595       break;
596     }
597 }
598
599
600 /* ********************* *
601  *  GtkObject Functions  *
602  * ********************* */
603
604 static void
605 gtk_file_chooser_button_destroy (GtkObject * object)
606 {
607   GtkFileChooserButtonPrivate *priv;
608
609   priv = GTK_FILE_CHOOSER_BUTTON_GET_PRIVATE (object);
610
611   if (priv->update_id)
612     g_source_remove (priv->update_id);
613
614   if (priv->dialog != NULL)
615     gtk_widget_destroy (priv->dialog);
616   
617   remove_settings_signal (GTK_FILE_CHOOSER_BUTTON (object),
618                           gtk_widget_get_screen (GTK_WIDGET (object)));
619
620   if (GTK_OBJECT_CLASS (gtk_file_chooser_button_parent_class)->destroy != NULL)
621     (*GTK_OBJECT_CLASS (gtk_file_chooser_button_parent_class)->destroy) (object);
622 }
623
624
625 /* ********************* *
626  *  GtkWidget Functions  *
627  * ********************* */
628
629 static void
630 gtk_file_chooser_button_drag_data_received (GtkWidget        *widget,
631                                             GdkDragContext   *context,
632                                             gint              x,
633                                             gint              y,
634                                             GtkSelectionData *data,
635                                             guint             info,
636                                             guint             drag_time)
637 {
638   GtkFileChooserButtonPrivate *priv;
639   gchar *text;
640
641   if (GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->drag_data_received != NULL)
642     (*GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->drag_data_received) (widget,
643                                                                                     context,
644                                                                                     x, y,
645                                                                                     data, info,
646                                                                                     drag_time);
647
648   if (widget == NULL || context == NULL || data == NULL || data->length < 0)
649     return;
650
651   priv = GTK_FILE_CHOOSER_BUTTON_GET_PRIVATE (widget);
652
653   switch (info)
654     {
655     case TEXT_URI_LIST:
656       {
657         gchar **uris;
658         guint i;
659         gboolean selected;
660
661         uris = gtk_selection_data_get_uris (data);
662
663         if (uris == NULL)
664           break;
665
666         selected = FALSE;
667         for (i = 0; !selected && uris[i] != NULL; i++)
668           {
669             GtkFileSystem *fs;
670             GtkFilePath *path, *base_path;
671
672             fs = _gtk_file_chooser_get_file_system (GTK_FILE_CHOOSER (priv->dialog));
673             path = gtk_file_system_uri_to_path (fs, uris[i]);
674
675             base_path = NULL;
676             if (path != NULL &&
677                 gtk_file_system_get_parent (fs, path, &base_path, NULL))
678               {
679                 GtkFileFolder *folder;
680                 GtkFileInfo *info;
681
682                 folder = gtk_file_system_get_folder (fs, base_path,
683                                                      GTK_FILE_INFO_IS_FOLDER,
684                                                      NULL);
685
686                 info = gtk_file_folder_get_info (folder, path, NULL);
687
688                 if (info != NULL)
689                   {
690                     GtkFileChooserAction action;
691
692                     g_object_get (priv->dialog, "action", &action, NULL);
693
694                     selected = 
695                       ((((action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER ||
696                           action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER) &&
697                          gtk_file_info_get_is_folder (info)) ||
698                         ((action == GTK_FILE_CHOOSER_ACTION_OPEN ||
699                           action == GTK_FILE_CHOOSER_ACTION_SAVE) &&
700                          !gtk_file_info_get_is_folder (info))) &&
701                         _gtk_file_chooser_select_path (GTK_FILE_CHOOSER (priv->dialog),
702                                                        path, NULL));
703
704                     gtk_file_info_free (info);
705                   }
706                 else
707                   selected = FALSE;
708
709                 gtk_file_path_free (base_path);
710               }
711
712             gtk_file_path_free (path);
713           }
714
715         g_strfreev (uris);
716       }
717       break;
718
719     case TEXT_PLAIN:
720       text = gtk_selection_data_get_text (data);
721       gtk_entry_set_text (GTK_ENTRY (priv->entry), text);
722       g_free (text);
723       break;
724     }
725
726   gtk_drag_finish (context, TRUE, FALSE, drag_time);
727 }
728
729 static void
730 gtk_file_chooser_button_show_all (GtkWidget *widget)
731 {
732   gtk_widget_show (widget);
733 }
734
735 static void
736 gtk_file_chooser_button_hide_all (GtkWidget *widget)
737 {
738   gtk_widget_hide (widget);
739 }
740
741 static void
742 gtk_file_chooser_button_show (GtkWidget *widget)
743 {
744   GtkFileChooserButtonPrivate *priv;
745
746   priv = GTK_FILE_CHOOSER_BUTTON_GET_PRIVATE (widget);
747
748   if (GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->show)
749     (*GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->show) (widget);
750
751   if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->button)))
752     gtk_widget_show (priv->dialog);
753 }
754
755 static void
756 gtk_file_chooser_button_hide (GtkWidget *widget)
757 {
758   GtkFileChooserButtonPrivate *priv;
759
760   priv = GTK_FILE_CHOOSER_BUTTON_GET_PRIVATE (widget);
761
762   gtk_widget_hide (priv->dialog);
763
764   if (GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->hide)
765     (*GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->hide) (widget);
766 }
767
768 static gboolean
769 gtk_file_chooser_button_mnemonic_activate (GtkWidget *widget,
770                                            gboolean   group_cycling)
771 {
772   GtkFileChooserButtonPrivate *priv;
773
774   priv = GTK_FILE_CHOOSER_BUTTON_GET_PRIVATE (widget);
775
776   switch (gtk_file_chooser_get_action (GTK_FILE_CHOOSER (priv->dialog)))
777     {
778     case GTK_FILE_CHOOSER_ACTION_OPEN:
779     case GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER:
780       gtk_widget_grab_focus (priv->button);
781       break;
782     case GTK_FILE_CHOOSER_ACTION_SAVE:
783     case GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER:
784       gtk_widget_grab_focus (priv->entry);
785       break;
786     }
787
788   return TRUE;
789 }
790
791 /* Changes the icons wherever it is needed */
792 static void
793 change_icon_theme (GtkFileChooserButton *button)
794 {
795   GtkSettings *settings;
796   gint width, height;
797
798   settings = gtk_settings_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (button)));
799
800   if (gtk_icon_size_lookup_for_settings (settings, GTK_ICON_SIZE_SMALL_TOOLBAR,
801                                          &width, &height))
802     button->priv->icon_size = MAX (width, height);
803   else
804     button->priv->icon_size = FALLBACK_ICON_SIZE;
805
806   update_icons (button);
807 }
808
809 /* Callback used when a GtkSettings value changes */
810 static void
811 settings_notify_cb (GObject    *object,
812                     GParamSpec *pspec,
813                     gpointer    user_data)
814 {
815   const char *name;
816
817   name = g_param_spec_get_name (pspec);
818
819   if (strcmp (name, "gtk-icon-theme-name") == 0
820       || strcmp (name, "gtk-icon-sizes") == 0)
821     change_icon_theme (user_data);
822 }
823
824 /* Installs a signal handler for GtkSettings so that we can monitor changes in
825  * the icon theme.
826  */
827 static void
828 check_icon_theme (GtkFileChooserButton *button)
829 {
830   GtkSettings *settings;
831
832   if (button->priv->settings_signal_id)
833     return;
834
835   if (gtk_widget_has_screen (GTK_WIDGET (button)))
836     {
837       settings = gtk_settings_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (button)));
838       button->priv->settings_signal_id = g_signal_connect (settings, "notify",
839                                                            G_CALLBACK (settings_notify_cb),
840                                                            button);
841
842       change_icon_theme (button);
843     }
844 }
845
846 static void
847 gtk_file_chooser_button_style_set (GtkWidget *widget,
848                                    GtkStyle  *old_style)
849 {
850   if (GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->style_set)
851     (*GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->style_set) (widget,
852                                                                            old_style);
853
854   if (gtk_widget_has_screen (widget))
855     change_icon_theme (GTK_FILE_CHOOSER_BUTTON (widget));
856 }
857
858 static void
859 gtk_file_chooser_button_screen_changed (GtkWidget *widget,
860                                         GdkScreen *old_screen)
861 {
862   GtkFileChooserButton *button;
863
864   button = GTK_FILE_CHOOSER_BUTTON (widget);
865
866   if (GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->screen_changed)
867     (*GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->screen_changed) (widget,
868                                                                                 old_screen);
869
870   remove_settings_signal (button, old_screen);
871   check_icon_theme (button); 
872 }
873
874
875 /* ************************************************************************** *
876  *  Public API                                                                *
877  * ************************************************************************** */
878
879 /**
880  * gtk_file_chooser_button_new:
881  * @title: the title of the browse dialog.
882  * 
883  * Creates a new file-selecting button widget.
884  * 
885  * Returns: a new button widget.
886  * 
887  * Since: 2.6
888  **/
889 GtkWidget *
890 gtk_file_chooser_button_new (const gchar *title)
891 {
892   return g_object_new (GTK_TYPE_FILE_CHOOSER_BUTTON,
893                        "title", title,
894                        NULL);
895 }
896
897 /**
898  * gtk_file_chooser_button_new_with_backend:
899  * @title: the title of the browse dialog.
900  * @backend: the name of the #GtkFileSystem backend to use.
901  * 
902  * Creates a new file-selecting button widget using @backend.
903  * 
904  * Returns: a new button widget.
905  * 
906  * Since: 2.6
907  **/
908 GtkWidget *
909 gtk_file_chooser_button_new_with_backend (const gchar *title,
910                                           const gchar *backend)
911 {
912   return g_object_new (GTK_TYPE_FILE_CHOOSER_BUTTON,
913                        "title", title,
914                        "file-system-backend", backend,
915                        NULL);
916 }
917
918 /**
919  * gtk_file_chooser_button_new_with_dialog:
920  * @dialog: the #GtkDialog widget to use.
921  * 
922  * Creates a #GtkFileChooserButton widget which uses @dialog as it's
923  * file-picking window. Note that @dialog must be a #GtkFileChooserDialog (or
924  * subclass).
925  * 
926  * Returns: a new button widget.
927  * 
928  * Since: 2.6
929  **/
930 GtkWidget *
931 gtk_file_chooser_button_new_with_dialog (GtkWidget *dialog)
932 {
933   g_return_val_if_fail (GTK_IS_FILE_CHOOSER_DIALOG (dialog), NULL);
934
935   return g_object_new (GTK_TYPE_FILE_CHOOSER_BUTTON,
936                        "dialog", dialog,
937                        NULL);
938 }
939
940 /**
941  * gtk_file_chooser_button_set_title:
942  * @button: the button widget to modify.
943  * @title: the new browse dialog title.
944  * 
945  * Modifies the @title of the browse dialog used by @button.
946  * 
947  * Since: 2.6
948  **/
949 void
950 gtk_file_chooser_button_set_title (GtkFileChooserButton *button,
951                                    const gchar          *title)
952 {
953   g_return_if_fail (GTK_IS_FILE_CHOOSER_BUTTON (button));
954
955   gtk_window_set_title (GTK_WINDOW (button->priv->dialog), title);
956   g_object_notify (G_OBJECT (button), "title");
957 }
958
959 /**
960  * gtk_file_chooser_button_get_title:
961  * @button: the button widget to examine.
962  * 
963  * Retrieves the title of the browse dialog used by @button. The returned value
964  * should not be modified or freed.
965  * 
966  * Returns: a pointer to the browse dialog's title.
967  * 
968  * Since: 2.6
969  **/
970 G_CONST_RETURN gchar *
971 gtk_file_chooser_button_get_title (GtkFileChooserButton *button)
972 {
973   g_return_val_if_fail (GTK_IS_FILE_CHOOSER_BUTTON (button), NULL);
974
975   return gtk_window_get_title (GTK_WINDOW (button->priv->dialog));
976 }
977
978 /**
979  * gtk_file_chooser_button_set_active:
980  * @button: the button widget to modify.
981  * @is_active: whether or not the dialog is visible.
982  * 
983  * Modifies whether or not the dialog attached to @button is visible or not.
984  * 
985  * Since: 2.6
986  **/
987 void
988 gtk_file_chooser_button_set_active (GtkFileChooserButton *button,
989                                     gboolean              is_active)
990 {
991   g_return_if_fail (GTK_IS_FILE_CHOOSER_BUTTON (button));
992
993   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button->priv->button), is_active);
994 }
995
996 /**
997  * gtk_file_chooser_button_get_active:
998  * @button: the button widget to examine.
999  * 
1000  * Retrieves whether or not the dialog attached to @button is visible.
1001  * 
1002  * Returns: a boolean whether the dialog is visible or not.
1003  * 
1004  * Since: 2.6
1005  **/
1006 gboolean
1007 gtk_file_chooser_button_get_active (GtkFileChooserButton *button)
1008 {
1009   g_return_val_if_fail (GTK_IS_FILE_CHOOSER_BUTTON (button), FALSE);
1010
1011   return gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button->priv->button));
1012 }
1013
1014 /**
1015  * gtk_file_chooser_button_get_width_chars:
1016  * @button: the button widget to examine.
1017  * 
1018  * Retrieves the width in characters of the @button widget's entry and/or label.
1019  * 
1020  * Returns: an integer width (in characters) that the button will use to size itself.
1021  * 
1022  * Since: 2.6
1023  **/
1024 gint
1025 gtk_file_chooser_button_get_width_chars (GtkFileChooserButton *button)
1026 {
1027   g_return_val_if_fail (GTK_IS_FILE_CHOOSER_BUTTON (button), -1);
1028
1029   return gtk_entry_get_width_chars (GTK_ENTRY (button->priv->entry));
1030 }
1031
1032 /**
1033  * gtk_file_chooser_button_set_width_chars:
1034  * @button: the button widget to examine.
1035  * @n_chars: the new width, in chracters.
1036  * 
1037  * Sets the width (in characters) that @button will use to @n_chars.
1038  * 
1039  * Since: 2.6
1040  **/
1041 void
1042 gtk_file_chooser_button_set_width_chars (GtkFileChooserButton *button,
1043                                          gint                  n_chars)
1044 {
1045   g_return_if_fail (GTK_IS_FILE_CHOOSER_BUTTON (button));
1046
1047   gtk_entry_set_width_chars (GTK_ENTRY (button->priv->entry), n_chars);
1048   gtk_label_set_width_chars (GTK_LABEL (button->priv->label), n_chars);
1049   g_object_notify (G_OBJECT (button), "width-chars");
1050 }
1051
1052
1053 /* ******************* *
1054  *  Utility Functions  *
1055  * ******************* */
1056
1057 /* Removes the settings signal handler.  It's safe to call multiple times */
1058 static void
1059 remove_settings_signal (GtkFileChooserButton *button,
1060                         GdkScreen            *screen)
1061 {
1062   if (button->priv->settings_signal_id)
1063     {
1064       GtkSettings *settings;
1065
1066       settings = gtk_settings_get_for_screen (screen);
1067       g_signal_handler_disconnect (settings,
1068                                    button->priv->settings_signal_id);
1069       button->priv->settings_signal_id = 0;
1070     }
1071 }
1072
1073 static GtkIconTheme *
1074 get_icon_theme (GtkWidget *widget)
1075 {
1076   if (gtk_widget_has_screen (widget))
1077     return gtk_icon_theme_get_for_screen (gtk_widget_get_screen (widget));
1078
1079   return gtk_icon_theme_get_default ();
1080 }
1081
1082 static gboolean
1083 check_if_path_exists (GtkFileSystem     *fs,
1084                       const GtkFilePath *path)
1085 {
1086   gboolean path_exists;
1087   GtkFilePath *parent_path;
1088
1089   path_exists = FALSE;
1090   parent_path = NULL;
1091
1092   if (gtk_file_system_get_parent (fs, path, &parent_path, NULL))
1093     {
1094       GtkFileFolder *folder;
1095     
1096       folder = gtk_file_system_get_folder (fs, parent_path, 0, NULL);
1097       if (folder)
1098         {
1099           GtkFileInfo *info;
1100         
1101           info = gtk_file_folder_get_info (folder, path, NULL);
1102           if (info)
1103             {
1104               path_exists = TRUE;
1105               gtk_file_info_free (info);
1106             }
1107
1108           g_object_unref (folder);
1109         }
1110     
1111       gtk_file_path_free (parent_path);
1112     }
1113
1114   return path_exists;
1115 }
1116
1117 static void
1118 update_icons (GtkFileChooserButton *button)
1119 {
1120   GtkFileChooserButtonPrivate *priv;
1121   GdkPixbuf *pixbuf;
1122   GSList *paths;
1123
1124   priv = GTK_FILE_CHOOSER_BUTTON_GET_PRIVATE (button);
1125   pixbuf = NULL;
1126   paths = _gtk_file_chooser_get_paths (GTK_FILE_CHOOSER (priv->dialog));
1127
1128   if (paths)
1129     {
1130       GtkFilePath *path;
1131       GtkFileSystem *fs;
1132
1133       path = paths->data;
1134       fs = _gtk_file_chooser_get_file_system (GTK_FILE_CHOOSER (priv->dialog));
1135
1136       switch (gtk_file_chooser_get_action (GTK_FILE_CHOOSER (priv->dialog)))
1137         {
1138         case GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER:
1139           {
1140             GtkFileSystemVolume *volume;
1141       
1142             volume = gtk_file_system_get_volume_for_path (fs, path);
1143             if (volume)
1144               {
1145                 GtkFilePath *base_path;
1146
1147                 base_path = gtk_file_system_volume_get_base_path (fs, volume);
1148                 
1149                 if (base_path && gtk_file_path_compare (base_path, path) == 0)
1150                   pixbuf = gtk_file_system_volume_render_icon (fs, volume,
1151                                                                GTK_WIDGET (button),
1152                                                                priv->icon_size,
1153                                                                NULL);
1154
1155                 if (base_path)
1156                   gtk_file_path_free (base_path);
1157
1158                 gtk_file_system_volume_free (fs, volume);
1159               }
1160           }
1161         
1162         case GTK_FILE_CHOOSER_ACTION_OPEN:
1163           if (!pixbuf)
1164             pixbuf = gtk_file_system_render_icon (fs, path, GTK_WIDGET (button),
1165                                                   priv->icon_size, NULL);
1166           break;
1167
1168         case GTK_FILE_CHOOSER_ACTION_SAVE:
1169           if (check_if_path_exists (fs, path))
1170             pixbuf = gtk_icon_theme_load_icon (get_icon_theme (GTK_WIDGET (button)),
1171                                                GTK_STOCK_DIALOG_WARNING,
1172                                                priv->icon_size, 0, NULL);
1173           else
1174             pixbuf = gtk_icon_theme_load_icon (get_icon_theme (GTK_WIDGET (button)),
1175                                                NEW_FILE_ICON_NAME,
1176                                                priv->icon_size, 0, NULL);
1177           break;
1178         case GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER:
1179           if (check_if_path_exists (fs, path))
1180             pixbuf = gtk_icon_theme_load_icon (get_icon_theme (GTK_WIDGET (button)),
1181                                                GTK_STOCK_DIALOG_WARNING,
1182                                                priv->icon_size, 0, NULL);
1183           else
1184             pixbuf = gtk_icon_theme_load_icon (get_icon_theme (GTK_WIDGET (button)),
1185                                                NEW_DIR_ICON_NAME,
1186                                                priv->icon_size, 0, NULL);
1187           break;
1188         }
1189
1190       gtk_file_paths_free (paths);
1191     }
1192
1193   if (!pixbuf)
1194     pixbuf = gtk_icon_theme_load_icon (get_icon_theme (GTK_WIDGET (button)),
1195                                        FALLBACK_ICON_NAME,
1196                                        priv->icon_size, 0, NULL);
1197
1198   gtk_image_set_from_pixbuf (GTK_IMAGE (priv->entry_image), pixbuf);
1199   gtk_image_set_from_pixbuf (GTK_IMAGE (priv->label_image), pixbuf);
1200
1201   if (pixbuf)
1202     g_object_unref (pixbuf);
1203 }
1204
1205
1206 static void
1207 update_label (GtkFileChooserButton *button)
1208 {
1209   GtkFileChooserButtonPrivate *priv;
1210   gchar *label_text;
1211   GSList *paths;
1212
1213   priv = GTK_FILE_CHOOSER_BUTTON_GET_PRIVATE (button);
1214   paths = _gtk_file_chooser_get_paths (GTK_FILE_CHOOSER (button->priv->dialog));
1215   label_text = NULL;
1216
1217   if (paths)
1218     {
1219       GtkFileSystem *fs;
1220       GtkFilePath *path;
1221       GtkFileSystemVolume *volume;
1222     
1223       path = paths->data;
1224
1225       fs = _gtk_file_chooser_get_file_system (GTK_FILE_CHOOSER (priv->dialog));
1226
1227       volume = gtk_file_system_get_volume_for_path (fs, path);
1228       if (volume)
1229         {
1230           GtkFilePath *base_path;
1231
1232           base_path = gtk_file_system_volume_get_base_path (fs, volume);
1233           if (base_path && gtk_file_path_compare (base_path, path) == 0)
1234             label_text = gtk_file_system_volume_get_display_name (fs, volume);
1235
1236           if (base_path)
1237             gtk_file_path_free (base_path);
1238
1239           gtk_file_system_volume_free (fs, volume);
1240
1241           if (label_text)
1242             goto out;
1243         }
1244     
1245       if (gtk_file_system_path_is_local (fs, path))
1246         {
1247           const gchar *home;
1248           gchar *tmp;
1249           gchar *filename;
1250
1251           filename = gtk_file_system_path_to_filename (fs, path);
1252
1253           if (!filename)
1254             goto out;
1255
1256           home = g_get_home_dir ();
1257
1258           /* Munging for psuedo-volumes and files in the user's home tree */
1259           if (home)
1260             {
1261               if (strcmp (filename, home) == 0)
1262                 {
1263                   label_text = g_strdup (_("Home"));
1264                   goto localout;
1265                 }
1266
1267               tmp = g_build_filename (home, "Desktop", NULL);
1268
1269               if (strcmp (filename, tmp) == 0)
1270                 label_text = g_strdup (_("Desktop"));
1271
1272               g_free (tmp);
1273
1274               if (label_text)
1275                 goto out;
1276
1277               if (g_str_has_prefix (filename, home))
1278                 {
1279                   label_text = g_strconcat ("~", filename + strlen (home), NULL);
1280                   goto localout;
1281                 }
1282             }
1283
1284           if (!label_text)
1285             label_text = g_strdup (filename);
1286
1287          localout:
1288           g_free (filename);
1289         }
1290       else
1291         {
1292           gchar *uri;
1293
1294           uri = gtk_file_system_path_to_uri (fs, path);
1295
1296           if (uri)
1297             label_text = uri;
1298         }
1299
1300      out:
1301       gtk_file_paths_free (paths);
1302     }
1303
1304   if (label_text)
1305     {
1306       gtk_label_set_text (GTK_LABEL (priv->label), label_text);
1307       g_free (label_text);
1308     }
1309   else
1310     gtk_label_set_text (GTK_LABEL (priv->label), _(DEFAULT_FILENAME));
1311 }
1312
1313 static void
1314 update_entry (GtkFileChooserButton *button)
1315 {
1316   GtkFileChooserButtonPrivate *priv;
1317   GSList *paths;
1318   gchar *filename;
1319
1320   priv = GTK_FILE_CHOOSER_BUTTON_GET_PRIVATE (button);
1321
1322   paths = _gtk_file_chooser_get_paths (GTK_FILE_CHOOSER (priv->dialog));
1323
1324   if (paths)
1325     {
1326       GtkFileSystem *fs;
1327       GtkFilePath *path;
1328     
1329       path = paths->data;
1330       fs = _gtk_file_chooser_get_file_system (GTK_FILE_CHOOSER (priv->dialog));
1331     
1332       if (gtk_file_system_path_is_local (fs, path))
1333         {
1334           filename = gtk_file_system_path_to_filename (fs, path);
1335
1336           if (filename)
1337             {
1338               const gchar *home;
1339               gchar *tmp;
1340
1341               if (g_file_test (filename, G_FILE_TEST_IS_DIR))
1342                 {
1343                   tmp = g_strconcat (filename, "/", NULL);
1344                   g_free (filename);
1345                   filename = tmp;
1346                 }
1347
1348               home = g_get_home_dir ();
1349
1350               if (home && g_str_has_prefix (filename, home))
1351                 {
1352                   tmp = g_strconcat ("~", filename + strlen (home), NULL);
1353                   g_free (filename);
1354                   filename = tmp;
1355                 }
1356             }
1357         }
1358       else
1359         filename = gtk_file_system_path_to_uri (fs, path);
1360     }
1361   else
1362     filename = NULL;
1363
1364   if (filename)
1365     {
1366       gchar *entry_text;
1367     
1368       entry_text = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
1369       g_free (filename);
1370
1371       gtk_entry_set_text (GTK_ENTRY (priv->entry), entry_text);
1372       g_free (entry_text);
1373     }
1374   else
1375     gtk_entry_set_text (GTK_ENTRY (priv->entry), "");
1376 }
1377
1378 static void
1379 update_dialog (GtkFileChooserButton *button)
1380 {
1381   GtkFileChooserButtonPrivate *priv;
1382   GtkFilePath *current_folder;
1383   GtkFileSystem *fs;
1384   GtkFilePath *folder_part, *full_path;
1385   gchar *file_part;
1386   const gchar *text;
1387   GtkFilePath *base_path;
1388
1389   priv = GTK_FILE_CHOOSER_BUTTON_GET_PRIVATE (button);
1390   file_part = NULL;
1391   folder_part = NULL;
1392   fs = _gtk_file_chooser_get_file_system (GTK_FILE_CHOOSER (priv->dialog));
1393
1394   text = gtk_entry_get_text (GTK_ENTRY (priv->entry));
1395
1396   base_path = gtk_file_path_new_dup ("/");
1397   gtk_file_system_parse (fs, base_path, text, &folder_part, &file_part, NULL);
1398   gtk_file_path_free (base_path);
1399
1400   switch (gtk_file_chooser_get_action (GTK_FILE_CHOOSER (priv->dialog)))
1401     {
1402     case GTK_FILE_CHOOSER_ACTION_OPEN:
1403       gtk_file_chooser_unselect_all (GTK_FILE_CHOOSER (priv->dialog));
1404       if (folder_part)
1405         {
1406           GtkFileFolder *folder;
1407           GtkFileInfo *info;
1408
1409           folder = gtk_file_system_get_folder (fs, folder_part,
1410                                                GTK_FILE_INFO_IS_FOLDER, NULL);
1411
1412           full_path = gtk_file_system_make_path (fs, folder_part, file_part, NULL);
1413           info = gtk_file_folder_get_info (folder, full_path, NULL);
1414
1415           /* Entry contents don't exist. */
1416           if (info == NULL)
1417             _gtk_file_chooser_set_current_folder_path (GTK_FILE_CHOOSER (priv->dialog),
1418                                                        folder_part, NULL);
1419           /* Entry contents are a folder */
1420           else if (gtk_file_info_get_is_folder (info))
1421             _gtk_file_chooser_set_current_folder_path (GTK_FILE_CHOOSER (priv->dialog),
1422                                                        full_path, NULL);
1423           /* Entry contents must be a file. */
1424           else
1425             _gtk_file_chooser_select_path (GTK_FILE_CHOOSER (priv->dialog),
1426                                            full_path, NULL);
1427
1428           if (info)
1429             gtk_file_info_free (info);
1430
1431           gtk_file_path_free (full_path);
1432         }
1433       break;
1434     case GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER:
1435       gtk_file_chooser_unselect_all (GTK_FILE_CHOOSER (priv->dialog));
1436       if (folder_part)
1437         {
1438           full_path = gtk_file_system_make_path (fs, folder_part, file_part, NULL);
1439
1440           /* Entry contents don't exist. */
1441           if (full_path)
1442             _gtk_file_chooser_select_path (GTK_FILE_CHOOSER (priv->dialog),
1443                                            full_path, NULL);
1444           else
1445             _gtk_file_chooser_set_current_folder_path (GTK_FILE_CHOOSER (priv->dialog),
1446                                                        folder_part, NULL);
1447
1448           gtk_file_path_free (full_path);
1449         }
1450       break;
1451
1452     case GTK_FILE_CHOOSER_ACTION_SAVE:
1453     case GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER:
1454       gtk_file_chooser_unselect_all (GTK_FILE_CHOOSER (priv->dialog));
1455       if (folder_part)
1456         {
1457           current_folder = _gtk_file_chooser_get_current_folder_path (GTK_FILE_CHOOSER (priv->dialog));
1458
1459           if (!current_folder ||
1460               gtk_file_path_compare (current_folder, folder_part) != 0)
1461             {
1462               _gtk_file_chooser_set_current_folder_path (GTK_FILE_CHOOSER (priv->dialog),
1463                                                          folder_part, NULL);
1464               g_signal_emit_by_name (button, "current-folder-changed");
1465             }
1466
1467           if (current_folder)
1468             gtk_file_path_free (current_folder);
1469         }
1470
1471       gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (priv->dialog),
1472                                          file_part);
1473       g_signal_emit_by_name (button, "selection-changed");
1474       break;
1475     }
1476 }
1477
1478 /* ************************ *
1479  *  Child-Widget Callbacks  *
1480  * ************************ */
1481
1482 static void
1483 dialog_current_folder_changed_cb (GtkFileChooser *dialog,
1484                                   gpointer        user_data)
1485 {
1486   g_signal_emit_by_name (user_data, "current-folder-changed");
1487 }
1488
1489 static void
1490 dialog_file_activated_cb (GtkFileChooser *dialog,
1491                           gpointer        user_data)
1492 {
1493   g_signal_emit_by_name (user_data, "file-activated");
1494 }
1495
1496 static void
1497 dialog_selection_changed_cb (GtkFileChooser *dialog,
1498                              gpointer        user_data)
1499 {
1500   GtkFileChooserButtonPrivate *priv;
1501
1502   priv = GTK_FILE_CHOOSER_BUTTON_GET_PRIVATE (user_data);
1503
1504   g_signal_handler_block (priv->entry, priv->entry_changed_id);
1505   update_entry (user_data);
1506   g_signal_handler_unblock (priv->entry, priv->entry_changed_id);
1507   update_icons (user_data);
1508   update_label (user_data);
1509 }
1510
1511 static void
1512 dialog_selection_changed_proxy_cb (GtkFileChooser *dialog,
1513                                    gpointer        user_data)
1514 {
1515   GtkFileChooserButtonPrivate *priv;
1516
1517   priv = GTK_FILE_CHOOSER_BUTTON_GET_PRIVATE (user_data);
1518
1519   g_signal_emit_by_name (user_data, "selection-changed");
1520 }
1521
1522 static void
1523 dialog_update_preview_cb (GtkFileChooser *dialog,
1524                           gpointer        user_data)
1525 {
1526   g_signal_emit_by_name (user_data, "update-preview");
1527 }
1528
1529 static void
1530 dialog_notify_cb (GObject    *dialog,
1531                   GParamSpec *pspec,
1532                   gpointer    user_data)
1533 {
1534   gpointer iface;
1535
1536   iface = g_type_interface_peek (g_type_class_peek (G_OBJECT_TYPE (dialog)),
1537                                  GTK_TYPE_FILE_CHOOSER);
1538   if (g_object_interface_find_property (iface, pspec->name))
1539     g_object_notify (user_data, pspec->name);
1540 }
1541
1542 static gboolean
1543 dialog_delete_event_cb (GtkWidget *dialog,
1544                         GdkEvent  *event,
1545                         gpointer   user_data)
1546 {
1547   g_signal_emit_by_name (dialog, "response", GTK_RESPONSE_DELETE_EVENT);
1548
1549   return TRUE;
1550 }
1551
1552 static void
1553 dialog_response_cb (GtkFileChooser *dialog,
1554                     gint            response,
1555                     gpointer        user_data)
1556 {
1557   GtkFileChooserButtonPrivate *priv;
1558
1559   priv = GTK_FILE_CHOOSER_BUTTON_GET_PRIVATE (user_data);
1560
1561   if (response == GTK_RESPONSE_ACCEPT)
1562     {
1563       g_signal_handler_block (priv->entry, priv->entry_changed_id);
1564       update_entry (user_data);
1565       g_signal_handler_unblock (priv->entry, priv->entry_changed_id);
1566       update_label (user_data);
1567       update_icons (user_data);
1568
1569       g_signal_emit_by_name (user_data, "current-folder-changed");
1570       g_signal_emit_by_name (user_data, "selection-changed");
1571     }
1572   else
1573     {
1574       update_dialog (user_data);
1575     }
1576
1577   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->button), FALSE);
1578 }
1579
1580
1581 static void
1582 button_toggled_cb (GtkToggleButton *real_button,
1583                    gpointer         user_data)
1584 {
1585   GtkFileChooserButtonPrivate *priv;
1586
1587   priv = GTK_FILE_CHOOSER_BUTTON_GET_PRIVATE (user_data);
1588
1589   if (gtk_toggle_button_get_active (real_button))
1590     {
1591       /* Setup the dialog parent to be chooser button's toplevel, and be modal
1592          as needed. */
1593       if (!GTK_WIDGET_VISIBLE (priv->dialog))
1594         {
1595           GtkWidget *toplevel;
1596
1597           toplevel = gtk_widget_get_toplevel (user_data);
1598
1599           if (GTK_WIDGET_TOPLEVEL (toplevel) && GTK_IS_WINDOW (toplevel))
1600             {
1601               if (GTK_WINDOW (toplevel) != gtk_window_get_transient_for (GTK_WINDOW (priv->dialog)))
1602                 gtk_window_set_transient_for (GTK_WINDOW (priv->dialog), GTK_WINDOW (toplevel));
1603               
1604               gtk_window_set_modal (GTK_WINDOW (priv->dialog),
1605                                     gtk_window_get_modal (GTK_WINDOW (toplevel)));
1606             }
1607         }
1608
1609       g_signal_handler_block (priv->dialog,
1610                               priv->dialog_folder_changed_id);
1611       g_signal_handler_block (priv->dialog,
1612                               priv->dialog_file_activated_id);
1613       g_signal_handler_block (priv->dialog,
1614                               priv->dialog_selection_changed_proxy_id);
1615       gtk_widget_set_sensitive (priv->entry, FALSE);
1616       gtk_window_present (GTK_WINDOW (priv->dialog));
1617     }
1618   else
1619     {
1620       g_signal_handler_unblock (priv->dialog,
1621                                 priv->dialog_folder_changed_id);
1622       g_signal_handler_unblock (priv->dialog,
1623                                 priv->dialog_file_activated_id);
1624       g_signal_handler_unblock (priv->dialog,
1625                                 priv->dialog_selection_changed_proxy_id);
1626       gtk_widget_set_sensitive (priv->entry, TRUE);
1627       gtk_widget_hide (priv->dialog);
1628     }
1629 }
1630
1631 static void
1632 button_notify_active_cb (GObject    *real_button,
1633                          GParamSpec *pspec,
1634                          gpointer    user_data)
1635 {
1636   g_object_notify (user_data, "active");
1637 }
1638
1639
1640 static gboolean
1641 update_idler (gpointer user_data)
1642 {
1643   GtkFileChooserButtonPrivate *priv;
1644   gboolean retval;
1645
1646   GDK_THREADS_ENTER ();
1647
1648   priv = GTK_FILE_CHOOSER_BUTTON_GET_PRIVATE (user_data);
1649
1650   if (!gtk_editable_get_selection_bounds (GTK_EDITABLE (priv->entry),
1651                                           NULL, NULL))
1652     {
1653       g_signal_handler_block (priv->dialog,
1654                               priv->dialog_selection_changed_id);
1655       update_dialog (user_data);
1656       g_signal_handler_unblock (priv->dialog,
1657                                 priv->dialog_selection_changed_id);
1658       update_icons (user_data);
1659       update_label (user_data);
1660       priv->update_id = 0;
1661       retval = FALSE;
1662     }
1663   else
1664     retval = TRUE;
1665   
1666   GDK_THREADS_LEAVE ();
1667
1668   return retval;
1669 }
1670
1671 static void
1672 entry_changed_cb (GtkEditable *editable,
1673                   gpointer     user_data)
1674 {
1675   GtkFileChooserButtonPrivate *priv;
1676
1677   priv = GTK_FILE_CHOOSER_BUTTON_GET_PRIVATE (user_data);
1678
1679   if (priv->update_id)
1680     g_source_remove (priv->update_id);
1681
1682   priv->update_id = g_idle_add_full (G_PRIORITY_LOW, update_idler,
1683                                      user_data, NULL);
1684 }
1685
1686 /* Ensure the button height == entry height */
1687 static void
1688 entry_size_allocate_cb (GtkWidget     *entry,
1689                         GtkAllocation *allocation,
1690                         gpointer       user_data)
1691 {
1692   gtk_widget_set_size_request (user_data, -1, allocation->height);
1693 }