]> Pileus Git - ~andy/gtk/blob - gtk/gtkfilechooserdialog.c
get the logic correct. This will fix sizing on save dialogs.
[~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_resizable_hints_changed (GtkWidget            *widget,
154                                              GtkFileChooserDialog *dialog)
155 {
156   GtkFileChooserDialogPrivate *priv;
157   gboolean resize_horizontally;
158   gboolean resize_vertically;
159   GdkGeometry geometry;
160
161   priv = GTK_FILE_CHOOSER_DIALOG_GET_PRIVATE (dialog);
162
163   _gtk_file_chooser_embed_get_resizable_hints (GTK_FILE_CHOOSER_EMBED (priv->widget),
164                                                &resize_horizontally,
165                                                &resize_vertically);
166
167
168   geometry.min_width = -1;
169   geometry.min_height = -1;
170   geometry.max_width = (resize_horizontally?G_MAXSHORT:-1);
171   geometry.max_height = (resize_vertically?G_MAXSHORT:-1);
172
173   gtk_window_set_geometry_hints (GTK_WINDOW (dialog), NULL,
174                                  &geometry,
175                                  GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE);
176 }
177
178 static void
179 file_chooser_widget_default_size_changed (GtkWidget            *widget,
180                                           GtkFileChooserDialog *dialog)
181 {
182   GtkFileChooserDialogPrivate *priv;
183   gint extra_width;
184   gint extra_height;
185   gint width, height;
186   GtkRequisition req;
187   gboolean resize_horizontally;
188   gboolean resize_vertically;
189
190   priv = GTK_FILE_CHOOSER_DIALOG_GET_PRIVATE (dialog);
191
192   /* Force a size request of everything before we start.  This will make sure
193    * that widget->requisition is meaningful. */
194   gtk_widget_size_request (GTK_WIDGET (dialog), &req);
195
196   /* Determine how much space the rest of the dialog uses compared to priv->widget */
197   extra_width = GTK_WIDGET (dialog)->requisition.width - priv->widget->requisition.width;
198   extra_height = GTK_WIDGET (dialog)->requisition.height - priv->widget->requisition.height;
199
200   _gtk_file_chooser_embed_get_default_size (GTK_FILE_CHOOSER_EMBED (priv->widget),
201                                             &width, &height);
202
203   width = extra_width + width;
204   height = extra_height + height;
205
206   /* FIXME: We should make sure that we arent' bigger than the current screen */
207   if (GTK_WIDGET_REALIZED (dialog) &&
208       priv->default_width > 0 &&
209       priv->default_height > 0)
210     {
211       gint cur_width, cur_height;
212       gint dx, dy;
213
214       gtk_window_get_size (GTK_WINDOW (dialog), &cur_width, &cur_height);
215
216       dx = width - priv->default_width;
217       dy = height - priv->default_height;
218       gtk_window_resize (GTK_WINDOW (dialog),
219                          cur_width + dx,
220                          cur_height + dy);
221     }
222   else
223     {
224       gtk_window_resize (GTK_WINDOW (dialog), width, height);
225     }
226
227   _gtk_file_chooser_embed_get_resizable_hints (GTK_FILE_CHOOSER_EMBED (priv->widget),
228                                                &resize_horizontally,
229                                                &resize_vertically);
230
231   /* Only store the size if we can resize in that direction. */
232   if (resize_horizontally)
233     priv->default_width = width;
234   if (resize_vertically)
235     priv->default_height = height;
236 }
237
238
239 static GObject*
240 gtk_file_chooser_dialog_constructor (GType                  type,
241                                      guint                  n_construct_properties,
242                                      GObjectConstructParam *construct_params)
243 {
244   GtkFileChooserDialogPrivate *priv;
245   GObject *object;
246
247   object = parent_class->constructor (type,
248                                       n_construct_properties,
249                                       construct_params);
250   priv = GTK_FILE_CHOOSER_DIALOG_GET_PRIVATE (object);
251
252   gtk_widget_push_composite_child ();
253
254   if (priv->file_system)
255     priv->widget = g_object_new (GTK_TYPE_FILE_CHOOSER_WIDGET,
256                                  "file-system-backend", priv->file_system,
257                                  NULL);
258   else
259     priv->widget = g_object_new (GTK_TYPE_FILE_CHOOSER_WIDGET, NULL);
260
261   g_signal_connect (priv->widget, "file-activated",
262                     G_CALLBACK (file_chooser_widget_file_activated), object);
263   g_signal_connect (priv->widget, "default-size-changed",
264                     G_CALLBACK (file_chooser_widget_default_size_changed), object);
265   g_signal_connect (priv->widget, "resizable-hints-changed",
266                     G_CALLBACK (file_chooser_widget_resizable_hints_changed), object);
267
268   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (object)->vbox), priv->widget, TRUE, TRUE, 0);
269   gtk_widget_show (priv->widget);
270
271   _gtk_file_chooser_set_delegate (GTK_FILE_CHOOSER (object),
272                                   GTK_FILE_CHOOSER (priv->widget));
273
274   gtk_widget_pop_composite_child ();
275
276   return object;
277 }
278
279 static void
280 gtk_file_chooser_dialog_set_property (GObject         *object,
281                                       guint            prop_id,
282                                       const GValue    *value,
283                                       GParamSpec      *pspec)
284
285 {
286   GtkFileChooserDialogPrivate *priv = GTK_FILE_CHOOSER_DIALOG_GET_PRIVATE (object);
287
288   switch (prop_id)
289     {
290     case GTK_FILE_CHOOSER_PROP_FILE_SYSTEM_BACKEND:
291       g_free (priv->file_system);
292       priv->file_system = g_value_dup_string (value);
293       break;
294     default:
295       g_object_set_property (G_OBJECT (priv->widget), pspec->name, value);
296       break;
297     }
298 }
299
300 static void
301 gtk_file_chooser_dialog_get_property (GObject         *object,
302                                       guint            prop_id,
303                                       GValue          *value,
304                                       GParamSpec      *pspec)
305 {
306   GtkFileChooserDialogPrivate *priv = GTK_FILE_CHOOSER_DIALOG_GET_PRIVATE (object);
307
308   g_object_get_property (G_OBJECT (priv->widget), pspec->name, value);
309 }
310
311 #if 0
312 static void
313 set_default_size (GtkFileChooserDialog *dialog)
314 {
315   GtkWidget *widget;
316   GtkWindow *window;
317   int default_width, default_height;
318   int width, height;
319   int font_size;
320   GdkScreen *screen;
321   int monitor_num;
322   GtkRequisition req;
323   GdkRectangle monitor;
324
325   widget = GTK_WIDGET (dialog);
326   window = GTK_WINDOW (dialog);
327
328   /* Size based on characters */
329
330   font_size = pango_font_description_get_size (widget->style->font_desc);
331   font_size = PANGO_PIXELS (font_size);
332
333   width = font_size * NUM_CHARS;
334   height = font_size * NUM_LINES;
335
336   /* Use at least the requisition size... */
337
338   gtk_widget_size_request (widget, &req);
339   width = MAX (width, req.width);
340   height = MAX (height, req.height);
341
342   /* ... but no larger than the monitor */
343
344   screen = gtk_widget_get_screen (widget);
345   monitor_num = gdk_screen_get_monitor_at_window (screen, widget->window);
346
347   gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
348
349   width = MIN (width, monitor.width * 3 / 4);
350   height = MIN (height, monitor.height * 3 / 4);
351
352   /* Set size */
353
354   gtk_window_get_default_size (window, &default_width, &default_height);
355
356   gtk_window_set_default_size (window,
357                                (default_width == -1) ? width : default_width,
358                                (default_height == -1) ? height : default_height);
359 }
360 #endif
361
362 static void
363 gtk_file_chooser_dialog_style_set (GtkWidget *widget,
364                                    GtkStyle  *previous_style)
365 {
366   GtkDialog *dialog;
367
368   if (GTK_WIDGET_CLASS (parent_class)->style_set)
369     GTK_WIDGET_CLASS (parent_class)->style_set (widget, previous_style);
370
371   dialog = GTK_DIALOG (widget);
372
373   /* Override the style properties with HIG-compliant spacings.  Ugh.
374    * http://developer.gnome.org/projects/gup/hig/1.0/layout.html#layout-dialogs
375    * http://developer.gnome.org/projects/gup/hig/1.0/windows.html#alert-spacing
376    */
377
378   gtk_container_set_border_width (GTK_CONTAINER (dialog->vbox), 12);
379   gtk_box_set_spacing (GTK_BOX (dialog->vbox), 24);
380
381   gtk_container_set_border_width (GTK_CONTAINER (dialog->action_area), 0);
382   gtk_box_set_spacing (GTK_BOX (dialog->action_area), 6);
383 }
384
385 static GtkWidget *
386 gtk_file_chooser_dialog_new_valist (const gchar          *title,
387                                     GtkWindow            *parent,
388                                     GtkFileChooserAction  action,
389                                     const gchar          *backend,
390                                     const gchar          *first_button_text,
391                                     va_list               varargs)
392 {
393   GtkWidget *result;
394   const char *button_text = first_button_text;
395   gint response_id;
396
397   result = g_object_new (GTK_TYPE_FILE_CHOOSER_DIALOG,
398                          "title", title,
399                          "action", action,
400                          "file-system-backend", backend,
401                          NULL);
402
403   if (parent)
404     gtk_window_set_transient_for (GTK_WINDOW (result), parent);
405
406   while (button_text)
407     {
408       response_id = va_arg (varargs, gint);
409       gtk_dialog_add_button (GTK_DIALOG (result), button_text, response_id);
410       button_text = va_arg (varargs, const gchar *);
411     }
412
413   return result;
414 }
415
416 /**
417  * gtk_file_chooser_dialog_new:
418  * @title: Title of the dialog, or %NULL
419  * @parent: Transient parent of the dialog, or %NULL
420  * @action: Open or save mode for the dialog
421  * @first_button_text: stock ID or text to go in the first button, or %NULL
422  * @Varargs: response ID for the first button, then additional (button, id) pairs, ending with %NULL
423  *
424  * Creates a new #GtkFileChooserDialog.  This function is analogous to
425  * gtk_dialog_new_with_buttons().
426  *
427  * Return value: a new #GtkFileChooserDialog
428  *
429  * Since: 2.4
430  **/
431 GtkWidget *
432 gtk_file_chooser_dialog_new (const gchar         *title,
433                              GtkWindow           *parent,
434                              GtkFileChooserAction action,
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                                                NULL, first_button_text,
444                                                varargs);
445   va_end (varargs);
446
447   return result;
448 }
449
450 /**
451  * gtk_file_chooser_dialog_new_with_backend:
452  * @title: Title of the dialog, or %NULL
453  * @parent: Transient parent of the dialog, or %NULL
454  * @backend: The name of the specific filesystem backend to use.
455  * @action: Open or save mode for the dialog
456  * @first_button_text: stock ID or text to go in the first button, or %NULL
457  * @Varargs: response ID for the first button, then additional (button, id) pairs, ending with %NULL
458  *
459  * Creates a new #GtkFileChooserDialog with a specified backend. This is
460  * especially useful if you use gtk_file_chooser_set_local_only() to allow
461  * non-local files and you use a more expressive vfs, such as gnome-vfs,
462  * to load files.
463  *
464  * Return value: a new #GtkFileChooserDialog
465  *
466  * Since: 2.4
467  **/
468 GtkWidget *
469 gtk_file_chooser_dialog_new_with_backend (const gchar          *title,
470                                           GtkWindow            *parent,
471                                           GtkFileChooserAction  action,
472                                           const gchar          *backend,
473                                           const gchar          *first_button_text,
474                                           ...)
475 {
476   GtkWidget *result;
477   va_list varargs;
478   
479   va_start (varargs, first_button_text);
480   result = gtk_file_chooser_dialog_new_valist (title, parent, action,
481                                                backend, first_button_text,
482                                                varargs);
483   va_end (varargs);
484
485   return result;
486 }