]> Pileus Git - ~andy/gtk/blob - gtk/gtkfilechooserdialog.c
Merged from gtk-2-4:
[~andy/gtk] / gtk / gtkfilechooserdialog.c
1 /* GTK - The GIMP Toolkit
2  * gtkfilechooserdialog.c: File selector dialog
3  * Copyright (C) 2003, Red Hat, Inc.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20
21 #include <config.h>
22 #include "gtkalias.h"
23 #include "gtkfilechooserdialog.h"
24 #include "gtkfilechooserwidget.h"
25 #include "gtkfilechooserutils.h"
26 #include "gtkfilechooserembed.h"
27 #include "gtkfilesystem.h"
28 #include "gtktypebuiltins.h"
29
30 #include <stdarg.h>
31
32 struct _GtkFileChooserDialogPrivate
33 {
34   GtkWidget *widget;
35   
36   char *file_system;
37
38   /* for use with GtkFileChooserEmbed */
39   gint default_width;
40   gint default_height;
41   gboolean resize_horizontally;
42   gboolean resize_vertically;
43 };
44
45 #define GTK_FILE_CHOOSER_DIALOG_GET_PRIVATE(o)  (GTK_FILE_CHOOSER_DIALOG (o)->priv)
46
47 static void gtk_file_chooser_dialog_class_init (GtkFileChooserDialogClass *class);
48 static void gtk_file_chooser_dialog_init       (GtkFileChooserDialog      *dialog);
49 static void gtk_file_chooser_dialog_finalize   (GObject                   *object);
50
51 static GObject* gtk_file_chooser_dialog_constructor  (GType                  type,
52                                                       guint                  n_construct_properties,
53                                                       GObjectConstructParam *construct_params);
54 static void     gtk_file_chooser_dialog_set_property (GObject               *object,
55                                                       guint                  prop_id,
56                                                       const GValue          *value,
57                                                       GParamSpec            *pspec);
58 static void     gtk_file_chooser_dialog_get_property (GObject               *object,
59                                                       guint                  prop_id,
60                                                       GValue                *value,
61                                                       GParamSpec            *pspec);
62
63 static void     gtk_file_chooser_dialog_map          (GtkWidget             *widget);
64 static void     gtk_file_chooser_dialog_unmap        (GtkWidget             *widget);
65 static void     gtk_file_chooser_dialog_style_set    (GtkWidget             *widget,
66                                                       GtkStyle              *previous_style);
67
68 static void response_cb (GtkDialog *dialog,
69                          gint       response_id);
70
71 static GObjectClass *parent_class;
72
73 GType
74 gtk_file_chooser_dialog_get_type (void)
75 {
76   static GType file_chooser_dialog_type = 0;
77
78   if (!file_chooser_dialog_type)
79     {
80       static const GTypeInfo file_chooser_dialog_info =
81       {
82         sizeof (GtkFileChooserDialogClass),
83         NULL,           /* base_init */
84         NULL,           /* base_finalize */
85         (GClassInitFunc) gtk_file_chooser_dialog_class_init,
86         NULL,           /* class_finalize */
87         NULL,           /* class_data */
88         sizeof (GtkFileChooserDialog),
89         0,              /* n_preallocs */
90         (GInstanceInitFunc) gtk_file_chooser_dialog_init,
91       };
92
93       static const GInterfaceInfo file_chooser_info =
94       {
95         (GInterfaceInitFunc) _gtk_file_chooser_delegate_iface_init, /* interface_init */
96         NULL,                                                       /* interface_finalize */
97         NULL                                                        /* interface_data */
98       };
99
100       file_chooser_dialog_type = g_type_register_static (GTK_TYPE_DIALOG, "GtkFileChooserDialog",
101                                                          &file_chooser_dialog_info, 0);
102       g_type_add_interface_static (file_chooser_dialog_type,
103                                    GTK_TYPE_FILE_CHOOSER,
104                                    &file_chooser_info);
105     }
106
107   return file_chooser_dialog_type;
108 }
109
110 static void
111 gtk_file_chooser_dialog_class_init (GtkFileChooserDialogClass *class)
112 {
113   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
114   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
115
116   parent_class = g_type_class_peek_parent (class);
117
118   gobject_class->constructor = gtk_file_chooser_dialog_constructor;
119   gobject_class->set_property = gtk_file_chooser_dialog_set_property;
120   gobject_class->get_property = gtk_file_chooser_dialog_get_property;
121   gobject_class->finalize = gtk_file_chooser_dialog_finalize;
122
123   widget_class->map       = gtk_file_chooser_dialog_map;
124   widget_class->unmap     = gtk_file_chooser_dialog_unmap;
125   widget_class->style_set = gtk_file_chooser_dialog_style_set;
126
127   _gtk_file_chooser_install_properties (gobject_class);
128
129   g_type_class_add_private (class, sizeof (GtkFileChooserDialogPrivate));
130 }
131
132 static void
133 gtk_file_chooser_dialog_init (GtkFileChooserDialog *dialog)
134 {
135   GtkFileChooserDialogPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (dialog,
136                                                                    GTK_TYPE_FILE_CHOOSER_DIALOG,
137                                                                    GtkFileChooserDialogPrivate);
138   dialog->priv = priv;
139   dialog->priv->default_width = -1;
140   dialog->priv->default_height = -1;
141   dialog->priv->resize_horizontally = TRUE;
142   dialog->priv->resize_vertically = TRUE;
143
144   gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE);
145
146   /* We do a signal connection here rather than overriding the method in
147    * class_init because GtkDialog::response is a RUN_LAST signal.  We want *our*
148    * handler to be run *first*, regardless of whether the user installs response
149    * handlers of his own.
150    */
151   g_signal_connect (dialog, "response",
152                     G_CALLBACK (response_cb), NULL);
153 }
154
155 static void
156 gtk_file_chooser_dialog_finalize (GObject *object)
157 {
158   GtkFileChooserDialog *dialog = GTK_FILE_CHOOSER_DIALOG (object);
159
160   g_free (dialog->priv->file_system);
161
162   G_OBJECT_CLASS (parent_class)->finalize (object);  
163 }
164
165 /* Callback used when the user activates a file in the file chooser widget */
166 static void
167 file_chooser_widget_file_activated (GtkFileChooser       *chooser,
168                                     GtkFileChooserDialog *dialog)
169 {
170   GList *children, *l;
171
172   if (gtk_window_activate_default (GTK_WINDOW (dialog)))
173     return;
174
175   /* There probably isn't a default widget, so make things easier for the
176    * programmer by looking for a reasonable button on our own.
177    */
178
179   children = gtk_container_get_children (GTK_CONTAINER (GTK_DIALOG (dialog)->action_area));
180
181   for (l = children; l; l = l->next)
182     {
183       GtkWidget *widget;
184       int response_id;
185
186       widget = GTK_WIDGET (l->data);
187       response_id = _gtk_dialog_get_response_for_widget (GTK_DIALOG (dialog), widget);
188       if (response_id == GTK_RESPONSE_ACCEPT
189           || response_id == GTK_RESPONSE_OK
190           || response_id == GTK_RESPONSE_YES
191           || response_id == GTK_RESPONSE_APPLY)
192         {
193           gtk_widget_activate (widget); /* Should we gtk_dialog_response (dialog, response_id) instead? */
194           break;
195         }
196     }
197
198   g_list_free (children);
199 }
200
201 static void
202 file_chooser_widget_update_hints (GtkFileChooserDialog *dialog)
203 {
204   GtkFileChooserDialogPrivate *priv;
205   GdkGeometry geometry;
206
207   priv = GTK_FILE_CHOOSER_DIALOG_GET_PRIVATE (dialog);
208
209   geometry.min_width = -1;
210   geometry.min_height = -1;
211   geometry.max_width = (priv->resize_horizontally?G_MAXSHORT:-1);
212   geometry.max_height = (priv->resize_vertically?G_MAXSHORT:-1);
213
214   gtk_window_set_geometry_hints (GTK_WINDOW (dialog), NULL,
215                                  &geometry,
216                                  GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE);
217 }
218
219 static void
220 clamp_to_screen (GtkWidget *widget,
221                  gint      *width,
222                  gint      *height)
223 {
224   GdkScreen *screen;
225   int monitor_num;
226   GdkRectangle monitor;
227
228   g_return_if_fail (GTK_WIDGET_REALIZED (widget));
229   
230   screen = gtk_widget_get_screen (widget);
231   monitor_num = gdk_screen_get_monitor_at_window (screen, widget->window);
232
233   gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
234
235   if (width)
236     *width = MIN (*width, (monitor.width * 3) / 4);
237
238   if (height)
239     *height = MIN (*height, (monitor.height * 3) / 4);
240 }
241
242 static void
243 file_chooser_widget_default_realized_size_changed (GtkWidget            *widget,
244                                                    GtkFileChooserDialog *dialog)
245 {
246   GtkFileChooserDialogPrivate *priv;
247   gint width;
248   gint height;
249   gint default_width, default_height;
250   GtkRequisition req;
251   gboolean resize_horizontally;
252   gboolean resize_vertically;
253   gboolean update_hints;
254   gint dx = 0, dy = 0;
255   gint cur_width, cur_height;
256
257   priv = GTK_FILE_CHOOSER_DIALOG_GET_PRIVATE (dialog);
258
259   /* Force a size request of everything before we start.  This will make sure
260    * that widget->requisition is meaningful. */
261   gtk_widget_size_request (GTK_WIDGET (dialog), &req);
262   gtk_window_get_size (GTK_WINDOW (dialog), &cur_width, &cur_height);
263   width = GTK_WIDGET (dialog)->requisition.width - priv->widget->requisition.width;
264   height = GTK_WIDGET (dialog)->requisition.height - priv->widget->requisition.height;
265   _gtk_file_chooser_embed_get_default_size (GTK_FILE_CHOOSER_EMBED (priv->widget),
266                                             &default_width, &default_height);
267
268   /* Ideal target size modulo any resizing */
269   width = default_width + width;
270   height = default_height + height;
271
272   /* Now, we test for resizability */
273   update_hints = FALSE;
274   _gtk_file_chooser_embed_get_resizable_hints (GTK_FILE_CHOOSER_EMBED (priv->widget),
275                                                &resize_horizontally,
276                                                &resize_vertically);
277   resize_vertically = (!! resize_vertically);     /* normalize */
278   resize_horizontally = (!! resize_horizontally);
279
280   if (resize_horizontally && priv->resize_horizontally)
281     {
282       dx = default_width - priv->default_width;
283       priv->default_width = default_width;
284     }
285   else if (resize_horizontally && ! priv->resize_horizontally)
286     {
287       /* We restore to the ideal size + any change in default_size (which is not
288        * expected).  It would be nicer to store the older size to restore to in
289        * the future. */
290       dx = default_width - priv->default_width;
291       dx += width - cur_width;
292       priv->default_width = default_width;
293       update_hints = TRUE;
294     }
295   else
296     {
297       update_hints = TRUE;
298     }
299   
300   if (resize_vertically && priv->resize_vertically)
301     {
302       dy = default_height - priv->default_height;
303       priv->default_height = default_height;
304     }
305   else if (resize_vertically && ! priv->resize_vertically)
306     {
307       dy = default_height - priv->default_height;
308       dy += height - cur_height;
309       priv->default_height = default_height;
310       update_hints = TRUE;
311     }
312   else
313     {
314       update_hints = TRUE;
315     }
316
317   priv->resize_horizontally = resize_horizontally;
318   priv->resize_vertically = resize_vertically;
319
320   if (dx != 0 || dy != 0)
321     {
322       gint new_width = cur_width + dx;
323       gint new_height = cur_height + dy;
324
325       clamp_to_screen (GTK_WIDGET (dialog), &new_width, &new_height);
326       
327       gtk_window_resize (GTK_WINDOW (dialog), new_width, new_height);
328     }
329
330   /* Only store the size if we can resize in that direction. */
331   if (update_hints)
332     file_chooser_widget_update_hints (dialog);
333 }
334
335 static void
336 file_chooser_widget_default_unrealized_size_changed (GtkWidget            *widget,
337                                                      GtkFileChooserDialog *dialog)
338 {
339   GtkFileChooserDialogPrivate *priv;
340   GtkRequisition req;
341   gint width, height;
342
343   priv = GTK_FILE_CHOOSER_DIALOG_GET_PRIVATE (dialog);
344   gtk_widget_size_request (GTK_WIDGET (dialog), &req);
345
346   _gtk_file_chooser_embed_get_resizable_hints (GTK_FILE_CHOOSER_EMBED (priv->widget),
347                                                &(priv->resize_horizontally),
348                                                &(priv->resize_vertically));
349   _gtk_file_chooser_embed_get_default_size (GTK_FILE_CHOOSER_EMBED (priv->widget),
350                                             &(priv->default_width), &(priv->default_height));
351   
352   /* Determine how much space the rest of the dialog uses compared to priv->widget */
353   width = priv->default_width + GTK_WIDGET (dialog)->requisition.width - priv->widget->requisition.width;
354   height = priv->default_height + GTK_WIDGET (dialog)->requisition.height - priv->widget->requisition.height;
355
356   gtk_window_set_default_size (GTK_WINDOW (dialog), width, height);
357   file_chooser_widget_update_hints (dialog);
358 }
359
360 static void
361 file_chooser_widget_default_size_changed (GtkWidget            *widget,
362                                           GtkFileChooserDialog *dialog)
363 {
364   if (GTK_WIDGET_REALIZED (dialog))
365     file_chooser_widget_default_realized_size_changed (widget, dialog);
366   else
367     file_chooser_widget_default_unrealized_size_changed (widget, dialog);
368 }
369   
370 static GObject*
371 gtk_file_chooser_dialog_constructor (GType                  type,
372                                      guint                  n_construct_properties,
373                                      GObjectConstructParam *construct_params)
374 {
375   GtkFileChooserDialogPrivate *priv;
376   GObject *object;
377
378   object = parent_class->constructor (type,
379                                       n_construct_properties,
380                                       construct_params);
381   priv = GTK_FILE_CHOOSER_DIALOG_GET_PRIVATE (object);
382
383   gtk_widget_push_composite_child ();
384
385   if (priv->file_system)
386     priv->widget = g_object_new (GTK_TYPE_FILE_CHOOSER_WIDGET,
387                                  "file-system-backend", priv->file_system,
388                                  NULL);
389   else
390     priv->widget = g_object_new (GTK_TYPE_FILE_CHOOSER_WIDGET, NULL);
391
392   g_signal_connect (priv->widget, "file-activated",
393                     G_CALLBACK (file_chooser_widget_file_activated), object);
394   g_signal_connect (priv->widget, "default-size-changed",
395                     G_CALLBACK (file_chooser_widget_default_size_changed), object);
396
397   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (object)->vbox), priv->widget, TRUE, TRUE, 0);
398
399   gtk_widget_show (priv->widget);
400
401   _gtk_file_chooser_set_delegate (GTK_FILE_CHOOSER (object),
402                                   GTK_FILE_CHOOSER (priv->widget));
403
404   gtk_widget_pop_composite_child ();
405
406   return object;
407 }
408
409 static void
410 gtk_file_chooser_dialog_set_property (GObject         *object,
411                                       guint            prop_id,
412                                       const GValue    *value,
413                                       GParamSpec      *pspec)
414
415 {
416   GtkFileChooserDialogPrivate *priv = GTK_FILE_CHOOSER_DIALOG_GET_PRIVATE (object);
417
418   switch (prop_id)
419     {
420     case GTK_FILE_CHOOSER_PROP_FILE_SYSTEM_BACKEND:
421       g_free (priv->file_system);
422       priv->file_system = g_value_dup_string (value);
423       break;
424     default:
425       g_object_set_property (G_OBJECT (priv->widget), pspec->name, value);
426       break;
427     }
428 }
429
430 static void
431 gtk_file_chooser_dialog_get_property (GObject         *object,
432                                       guint            prop_id,
433                                       GValue          *value,
434                                       GParamSpec      *pspec)
435 {
436   GtkFileChooserDialogPrivate *priv = GTK_FILE_CHOOSER_DIALOG_GET_PRIVATE (object);
437
438   g_object_get_property (G_OBJECT (priv->widget), pspec->name, value);
439 }
440
441 #if 0
442 static void
443 set_default_size (GtkFileChooserDialog *dialog)
444 {
445   GtkWidget *widget;
446   GtkWindow *window;
447   int default_width, default_height;
448   int width, height;
449   int font_size;
450   GdkScreen *screen;
451   int monitor_num;
452   GtkRequisition req;
453   GdkRectangle monitor;
454
455   widget = GTK_WIDGET (dialog);
456   window = GTK_WINDOW (dialog);
457
458   /* Size based on characters */
459
460   font_size = pango_font_description_get_size (widget->style->font_desc);
461   font_size = PANGO_PIXELS (font_size);
462
463   width = font_size * NUM_CHARS;
464   height = font_size * NUM_LINES;
465
466   /* Use at least the requisition size... */
467
468   gtk_widget_size_request (widget, &req);
469   width = MAX (width, req.width);
470   height = MAX (height, req.height);
471
472   /* ... but no larger than the monitor */
473
474   screen = gtk_widget_get_screen (widget);
475   monitor_num = gdk_screen_get_monitor_at_window (screen, widget->window);
476
477   gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
478
479   width = MIN (width, monitor.width * 3 / 4);
480   height = MIN (height, monitor.height * 3 / 4);
481
482   /* Set size */
483
484   gtk_window_get_default_size (window, &default_width, &default_height);
485
486   gtk_window_set_default_size (window,
487                                (default_width == -1) ? width : default_width,
488                                (default_height == -1) ? height : default_height);
489 }
490 #endif
491
492 /* GtkWidget::map handler */
493 static void
494 gtk_file_chooser_dialog_map (GtkWidget *widget)
495 {
496   GtkFileChooserDialog *dialog = GTK_FILE_CHOOSER_DIALOG (widget);
497   GtkFileChooserDialogPrivate *priv = GTK_FILE_CHOOSER_DIALOG_GET_PRIVATE (dialog);
498
499   if (!GTK_WIDGET_MAPPED (priv->widget))
500     gtk_widget_map (priv->widget);
501
502   GTK_WIDGET_CLASS (parent_class)->map (widget);
503
504   _gtk_file_chooser_embed_initial_focus (GTK_FILE_CHOOSER_EMBED (priv->widget));
505 }
506
507 /* GtkWidget::unmap handler */
508 static void
509 gtk_file_chooser_dialog_unmap (GtkWidget *widget)
510 {
511   GtkFileChooserDialog *dialog = GTK_FILE_CHOOSER_DIALOG (widget);
512   GtkFileChooserDialogPrivate *priv = GTK_FILE_CHOOSER_DIALOG_GET_PRIVATE (dialog);
513
514   GTK_WIDGET_CLASS (parent_class)->unmap (widget);
515
516   /* See bug #145470.  We unmap the GtkFileChooserWidget so that if the dialog
517    * is remapped, the widget will be remapped as well.  Implementations should
518    * refresh their contents when this happens, as some applications keep a
519    * single file chooser alive and map/unmap it as needed, rather than creating
520    * a new file chooser every time they need one.
521    */
522   gtk_widget_unmap (priv->widget);
523 }
524
525 static void
526 gtk_file_chooser_dialog_style_set (GtkWidget *widget,
527                                    GtkStyle  *previous_style)
528 {
529   GtkDialog *dialog;
530
531   if (GTK_WIDGET_CLASS (parent_class)->style_set)
532     GTK_WIDGET_CLASS (parent_class)->style_set (widget, previous_style);
533
534   dialog = GTK_DIALOG (widget);
535
536   /* Override the style properties with HIG-compliant spacings.  Ugh.
537    * http://developer.gnome.org/projects/gup/hig/1.0/layout.html#layout-dialogs
538    * http://developer.gnome.org/projects/gup/hig/1.0/windows.html#alert-spacing
539    */
540
541   gtk_container_set_border_width (GTK_CONTAINER (dialog->vbox), 12);
542   gtk_box_set_spacing (GTK_BOX (dialog->vbox), 24);
543
544   gtk_container_set_border_width (GTK_CONTAINER (dialog->action_area), 0);
545   gtk_box_set_spacing (GTK_BOX (dialog->action_area), 6);
546 }
547
548 /* GtkDialog::response handler */
549 static void
550 response_cb (GtkDialog *dialog,
551              gint       response_id)
552 {
553   GtkFileChooserDialogPrivate *priv;
554
555   priv = GTK_FILE_CHOOSER_DIALOG_GET_PRIVATE (dialog);
556
557   /* Act only on response IDs we recognize */
558   if (!(response_id == GTK_RESPONSE_ACCEPT
559         || response_id == GTK_RESPONSE_OK
560         || response_id == GTK_RESPONSE_YES
561         || response_id == GTK_RESPONSE_APPLY))
562     return;
563
564   if (!_gtk_file_chooser_embed_should_respond (GTK_FILE_CHOOSER_EMBED (priv->widget)))
565     g_signal_stop_emission_by_name (dialog, "response");
566 }
567
568 static GtkWidget *
569 gtk_file_chooser_dialog_new_valist (const gchar          *title,
570                                     GtkWindow            *parent,
571                                     GtkFileChooserAction  action,
572                                     const gchar          *backend,
573                                     const gchar          *first_button_text,
574                                     va_list               varargs)
575 {
576   GtkWidget *result;
577   const char *button_text = first_button_text;
578   gint response_id;
579
580   result = g_object_new (GTK_TYPE_FILE_CHOOSER_DIALOG,
581                          "title", title,
582                          "action", action,
583                          "file-system-backend", backend,
584                          NULL);
585
586   if (parent)
587     gtk_window_set_transient_for (GTK_WINDOW (result), parent);
588
589   while (button_text)
590     {
591       response_id = va_arg (varargs, gint);
592       gtk_dialog_add_button (GTK_DIALOG (result), button_text, response_id);
593       button_text = va_arg (varargs, const gchar *);
594     }
595
596   return result;
597 }
598
599 /**
600  * gtk_file_chooser_dialog_new:
601  * @title: Title of the dialog, or %NULL
602  * @parent: Transient parent of the dialog, or %NULL
603  * @action: Open or save mode for the dialog
604  * @first_button_text: stock ID or text to go in the first button, or %NULL
605  * @Varargs: response ID for the first button, then additional (button, id) pairs, ending with %NULL
606  *
607  * Creates a new #GtkFileChooserDialog.  This function is analogous to
608  * gtk_dialog_new_with_buttons().
609  *
610  * Return value: a new #GtkFileChooserDialog
611  *
612  * Since: 2.4
613  **/
614 GtkWidget *
615 gtk_file_chooser_dialog_new (const gchar         *title,
616                              GtkWindow           *parent,
617                              GtkFileChooserAction action,
618                              const gchar         *first_button_text,
619                              ...)
620 {
621   GtkWidget *result;
622   va_list varargs;
623   
624   va_start (varargs, first_button_text);
625   result = gtk_file_chooser_dialog_new_valist (title, parent, action,
626                                                NULL, first_button_text,
627                                                varargs);
628   va_end (varargs);
629
630   return result;
631 }
632
633 /**
634  * gtk_file_chooser_dialog_new_with_backend:
635  * @title: Title of the dialog, or %NULL
636  * @parent: Transient parent of the dialog, or %NULL
637  * @backend: The name of the specific filesystem backend to use.
638  * @action: Open or save mode for the dialog
639  * @first_button_text: stock ID or text to go in the first button, or %NULL
640  * @Varargs: response ID for the first button, then additional (button, id) pairs, ending with %NULL
641  *
642  * Creates a new #GtkFileChooserDialog with a specified backend. This is
643  * especially useful if you use gtk_file_chooser_set_local_only() to allow
644  * non-local files and you use a more expressive vfs, such as gnome-vfs,
645  * to load files.
646  *
647  * Return value: a new #GtkFileChooserDialog
648  *
649  * Since: 2.4
650  **/
651 GtkWidget *
652 gtk_file_chooser_dialog_new_with_backend (const gchar          *title,
653                                           GtkWindow            *parent,
654                                           GtkFileChooserAction  action,
655                                           const gchar          *backend,
656                                           const gchar          *first_button_text,
657                                           ...)
658 {
659   GtkWidget *result;
660   va_list varargs;
661   
662   va_start (varargs, first_button_text);
663   result = gtk_file_chooser_dialog_new_valist (title, parent, action,
664                                                backend, first_button_text,
665                                                varargs);
666   va_end (varargs);
667
668   return result;
669 }