]> Pileus Git - ~andy/gtk/blob - gtk/gtkfilechooserdialog.c
9b0c30b784ac792bab24459f1b877efebbd6488c
[~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 "gtkfilechooserdialog.h"
22 #include "gtkfilechooserwidget.h"
23 #include "gtkfilechooserutils.h"
24 #include "gtkfilesystem.h"
25 #include "gtktypebuiltins.h"
26
27 #include <stdarg.h>
28
29 #define NUM_LINES 40
30 #define NUM_CHARS 50
31
32 struct _GtkFileChooserDialogPrivate
33 {
34   GtkWidget *widget;
35
36   char *file_system;
37 };
38
39 #define GTK_FILE_CHOOSER_DIALOG_GET_PRIVATE(o)  (GTK_FILE_CHOOSER_DIALOG (o)->priv)
40
41 static void gtk_file_chooser_dialog_class_init (GtkFileChooserDialogClass *class);
42 static void gtk_file_chooser_dialog_init       (GtkFileChooserDialog      *dialog);
43 static void gtk_file_chooser_dialog_finalize   (GObject                   *object);
44
45 static GObject* gtk_file_chooser_dialog_constructor  (GType                  type,
46                                                       guint                  n_construct_properties,
47                                                       GObjectConstructParam *construct_params);
48 static void     gtk_file_chooser_dialog_set_property (GObject               *object,
49                                                       guint                  prop_id,
50                                                       const GValue          *value,
51                                                       GParamSpec            *pspec);
52 static void     gtk_file_chooser_dialog_get_property (GObject               *object,
53                                                       guint                  prop_id,
54                                                       GValue                *value,
55                                                       GParamSpec            *pspec);
56
57 static void gtk_file_chooser_dialog_realize        (GtkWidget *widget);
58 static void gtk_file_chooser_dialog_style_set      (GtkWidget *widget,
59                                                     GtkStyle  *previous_style);
60 static void gtk_file_chooser_dialog_screen_changed (GtkWidget *widget,
61                                                     GdkScreen *previous_screen);
62
63 static GObjectClass *parent_class;
64
65 GType
66 gtk_file_chooser_dialog_get_type (void)
67 {
68   static GType file_chooser_dialog_type = 0;
69
70   if (!file_chooser_dialog_type)
71     {
72       static const GTypeInfo file_chooser_dialog_info =
73       {
74         sizeof (GtkFileChooserDialogClass),
75         NULL,           /* base_init */
76         NULL,           /* base_finalize */
77         (GClassInitFunc) gtk_file_chooser_dialog_class_init,
78         NULL,           /* class_finalize */
79         NULL,           /* class_data */
80         sizeof (GtkFileChooserDialog),
81         0,              /* n_preallocs */
82         (GInstanceInitFunc) gtk_file_chooser_dialog_init,
83       };
84
85       static const GInterfaceInfo file_chooser_info =
86       {
87         (GInterfaceInitFunc) _gtk_file_chooser_delegate_iface_init, /* interface_init */
88         NULL,                                                       /* interface_finalize */
89         NULL                                                        /* interface_data */
90       };
91
92       file_chooser_dialog_type = g_type_register_static (GTK_TYPE_DIALOG, "GtkFileChooserDialog",
93                                                          &file_chooser_dialog_info, 0);
94       g_type_add_interface_static (file_chooser_dialog_type,
95                                    GTK_TYPE_FILE_CHOOSER,
96                                    &file_chooser_info);
97     }
98
99   return file_chooser_dialog_type;
100 }
101
102 static void
103 gtk_file_chooser_dialog_class_init (GtkFileChooserDialogClass *class)
104 {
105   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
106   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
107
108   parent_class = g_type_class_peek_parent (class);
109
110   gobject_class->constructor = gtk_file_chooser_dialog_constructor;
111   gobject_class->set_property = gtk_file_chooser_dialog_set_property;
112   gobject_class->get_property = gtk_file_chooser_dialog_get_property;
113   gobject_class->finalize = gtk_file_chooser_dialog_finalize;
114
115   widget_class->realize = gtk_file_chooser_dialog_realize;
116   widget_class->style_set = gtk_file_chooser_dialog_style_set;
117   widget_class->screen_changed = gtk_file_chooser_dialog_screen_changed;
118
119   _gtk_file_chooser_install_properties (gobject_class);
120
121   g_type_class_add_private (class, sizeof (GtkFileChooserDialogPrivate));
122 }
123
124 static void
125 gtk_file_chooser_dialog_init (GtkFileChooserDialog *dialog)
126 {
127   GtkFileChooserDialogPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (dialog,
128                                                                    GTK_TYPE_FILE_CHOOSER_DIALOG,
129                                                                    GtkFileChooserDialogPrivate);
130   dialog->priv = priv;
131
132   gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE);
133 }
134
135 static void
136 gtk_file_chooser_dialog_finalize (GObject *object)
137 {
138   GtkFileChooserDialog *dialog = GTK_FILE_CHOOSER_DIALOG (object);
139
140   g_free (dialog->priv->file_system);
141
142   G_OBJECT_CLASS (parent_class)->finalize (object);  
143 }
144
145 /* Callback used when the user activates a file in the file chooser widget */
146 static void
147 file_chooser_widget_file_activated (GtkFileChooser       *chooser,
148                                     GtkFileChooserDialog *dialog)
149 {
150   gtk_window_activate_default (GTK_WINDOW (dialog));
151 }
152
153 static GObject*
154 gtk_file_chooser_dialog_constructor (GType                  type,
155                                      guint                  n_construct_properties,
156                                      GObjectConstructParam *construct_params)
157 {
158   GtkFileChooserDialogPrivate *priv;
159   GObject *object;
160
161   object = parent_class->constructor (type,
162                                       n_construct_properties,
163                                       construct_params);
164   priv = GTK_FILE_CHOOSER_DIALOG_GET_PRIVATE (object);
165
166   gtk_widget_push_composite_child ();
167
168   if (priv->file_system)
169     priv->widget = g_object_new (GTK_TYPE_FILE_CHOOSER_WIDGET,
170                                  "file-system-backend", priv->file_system,
171                                  NULL);
172   else
173     priv->widget = g_object_new (GTK_TYPE_FILE_CHOOSER_WIDGET, NULL);
174
175   g_signal_connect (priv->widget, "file-activated",
176                     G_CALLBACK (file_chooser_widget_file_activated), object);
177
178   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (object)->vbox), priv->widget, TRUE, TRUE, 0);
179   gtk_widget_show (priv->widget);
180
181   _gtk_file_chooser_set_delegate (GTK_FILE_CHOOSER (object),
182                                   GTK_FILE_CHOOSER (priv->widget));
183
184   gtk_widget_pop_composite_child ();
185
186   return object;
187 }
188
189 static void
190 gtk_file_chooser_dialog_set_property (GObject         *object,
191                                       guint            prop_id,
192                                       const GValue    *value,
193                                       GParamSpec      *pspec)
194
195 {
196   GtkFileChooserDialogPrivate *priv = GTK_FILE_CHOOSER_DIALOG_GET_PRIVATE (object);
197
198   switch (prop_id)
199     {
200     case GTK_FILE_CHOOSER_PROP_FILE_SYSTEM_BACKEND:
201       g_free (priv->file_system);
202       priv->file_system = g_value_dup_string (value);
203       break;
204     default:
205       g_object_set_property (G_OBJECT (priv->widget), pspec->name, value);
206       break;
207     }
208 }
209
210 static void
211 gtk_file_chooser_dialog_get_property (GObject         *object,
212                                       guint            prop_id,
213                                       GValue          *value,
214                                       GParamSpec      *pspec)
215 {
216   GtkFileChooserDialogPrivate *priv = GTK_FILE_CHOOSER_DIALOG_GET_PRIVATE (object);
217
218   g_object_get_property (G_OBJECT (priv->widget), pspec->name, value);
219 }
220
221 static void
222 set_default_size (GtkFileChooserDialog *dialog)
223 {
224   GtkWidget *widget;
225   GtkWindow *window;
226   int default_width, default_height;
227   int width, height;
228   int font_size;
229   GdkScreen *screen;
230   int monitor_num;
231   GtkRequisition req;
232   GdkRectangle monitor;
233
234   widget = GTK_WIDGET (dialog);
235   window = GTK_WINDOW (dialog);
236
237   /* Size based on characters */
238
239   font_size = pango_font_description_get_size (widget->style->font_desc);
240   font_size = PANGO_PIXELS (font_size);
241
242   width = font_size * NUM_CHARS;
243   height = font_size * NUM_LINES;
244
245   /* Use at least the requisition size... */
246
247   gtk_widget_size_request (widget, &req);
248   width = MAX (width, req.width);
249   height = MAX (height, req.height);
250
251   /* ... but no larger than the monitor */
252
253   screen = gtk_widget_get_screen (widget);
254   monitor_num = gdk_screen_get_monitor_at_window (screen, widget->window);
255
256   gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
257
258   width = MIN (width, monitor.width * 3 / 4);
259   height = MIN (height, monitor.height * 3 / 4);
260
261   /* Set size */
262
263   gtk_window_get_default_size (window, &default_width, &default_height);
264
265   gtk_window_set_default_size (window,
266                                (default_width == -1) ? width : default_width,
267                                (default_height == -1) ? height : default_height);
268 }
269
270 static void
271 gtk_file_chooser_dialog_realize (GtkWidget *widget)
272 {
273   GTK_WIDGET_CLASS (parent_class)->realize (widget);
274   set_default_size (GTK_FILE_CHOOSER_DIALOG (widget));
275 }
276
277 static void
278 gtk_file_chooser_dialog_style_set (GtkWidget *widget,
279                                    GtkStyle  *previous_style)
280 {
281   GtkDialog *dialog;
282
283   if (GTK_WIDGET_CLASS (parent_class)->style_set)
284     GTK_WIDGET_CLASS (parent_class)->style_set (widget, previous_style);
285
286   if (GTK_WIDGET_REALIZED (widget))
287     set_default_size (GTK_FILE_CHOOSER_DIALOG (widget));
288
289   dialog = GTK_DIALOG (widget);
290
291   /* Override the style properties with HIG-compliant spacings.  Ugh.
292    * http://developer.gnome.org/projects/gup/hig/1.0/layout.html#layout-dialogs
293    * http://developer.gnome.org/projects/gup/hig/1.0/windows.html#alert-spacing
294    */
295
296   gtk_container_set_border_width (GTK_CONTAINER (dialog->vbox), 12);
297   gtk_box_set_spacing (GTK_BOX (dialog->vbox), 24);
298
299   gtk_container_set_border_width (GTK_CONTAINER (dialog->action_area), 0);
300   gtk_box_set_spacing (GTK_BOX (dialog->action_area), 6);
301 }
302
303 static void
304 gtk_file_chooser_dialog_screen_changed (GtkWidget *widget,
305                                         GdkScreen *previous_screen)
306 {
307   if (GTK_WIDGET_CLASS (parent_class)->screen_changed)
308     GTK_WIDGET_CLASS (parent_class)->screen_changed (widget, previous_screen);
309
310   if (GTK_WIDGET_REALIZED (widget))
311     set_default_size (GTK_FILE_CHOOSER_DIALOG (widget));
312 }
313
314 static GtkWidget *
315 gtk_file_chooser_dialog_new_valist (const gchar          *title,
316                                     GtkWindow            *parent,
317                                     GtkFileChooserAction  action,
318                                     const gchar          *backend,
319                                     const gchar          *first_button_text,
320                                     va_list               varargs)
321 {
322   GtkWidget *result;
323   const char *button_text = first_button_text;
324   gint response_id;
325
326   result = g_object_new (GTK_TYPE_FILE_CHOOSER_DIALOG,
327                          "title", title,
328                          "action", action,
329                          "file-system-backend", backend,
330                          NULL);
331
332   if (parent)
333     gtk_window_set_transient_for (GTK_WINDOW (result), parent);
334
335   while (button_text)
336     {
337       response_id = va_arg (varargs, gint);
338       gtk_dialog_add_button (GTK_DIALOG (result), button_text, response_id);
339       button_text = va_arg (varargs, const gchar *);
340     }
341
342   return result;
343 }
344
345 /**
346  * gtk_file_chooser_dialog_new:
347  * @title: Title of the dialog, or %NULL
348  * @parent: Transient parent of the dialog, or %NULL
349  * @action: Open or save mode for the dialog
350  * @first_button_text: stock ID or text to go in the first button, or %NULL
351  * @Varargs: response ID for the first button, then additional (button, id) pairs, ending with %NULL
352  *
353  * Creates a new #GtkFileChooserDialog.  This function is analogous to
354  * gtk_dialog_new_with_buttons().
355  *
356  * Return value: a new #GtkFileChooserDialog
357  *
358  * Since: 2.4
359  **/
360 GtkWidget *
361 gtk_file_chooser_dialog_new (const gchar         *title,
362                              GtkWindow           *parent,
363                              GtkFileChooserAction action,
364                              const gchar         *first_button_text,
365                              ...)
366 {
367   GtkWidget *result;
368   va_list varargs;
369   
370   va_start (varargs, first_button_text);
371   result = gtk_file_chooser_dialog_new_valist (title, parent, action,
372                                                NULL, first_button_text,
373                                                varargs);
374   va_end (varargs);
375
376   return result;
377 }
378
379 /**
380  * gtk_file_chooser_dialog_new_with_backend:
381  * @title: Title of the dialog, or %NULL
382  * @parent: Transient parent of the dialog, or %NULL
383  * @backend: The name of the specific filesystem backend to use.
384  * @action: Open or save mode for the dialog
385  * @first_button_text: stock ID or text to go in the first button, or %NULL
386  * @Varargs: response ID for the first button, then additional (button, id) pairs, ending with %NULL
387  *
388  * Creates a new #GtkFileChooserDialog with a specified backend. This is
389  * especially useful if you use gtk_file_chooser_set_local_only() to allow
390  * non-local files and you use a more expressive vfs, such as gnome-vfs,
391  * to load files.
392  *
393  * Return value: a new #GtkFileChooserDialog
394  *
395  * Since: 2.4
396  **/
397 GtkWidget *
398 gtk_file_chooser_dialog_new_with_backend (const gchar          *title,
399                                           GtkWindow            *parent,
400                                           GtkFileChooserAction  action,
401                                           const gchar          *backend,
402                                           const gchar          *first_button_text,
403                                           ...)
404 {
405   GtkWidget *result;
406   va_list varargs;
407   
408   va_start (varargs, first_button_text);
409   result = gtk_file_chooser_dialog_new_valist (title, parent, action,
410                                                backend, first_button_text,
411                                                varargs);
412   va_end (varargs);
413
414   return result;
415 }