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