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