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