de265e1fcdf32480d767bb231f81f4780db0fcd1
[pspp-builds.git] / src / ui / gui / psppire-buttonbox.c
1 /* PSPPIRE - a graphical user interface for PSPP.
2    Copyright (C) 2007, 2010, 2011  Free Software Foundation
3
4    This program is free software: you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation, either version 3 of the License, or
7    (at your option) any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
16
17
18 #include <config.h>
19
20 #include <glib.h>
21 #include <gtk/gtk.h>
22 #include "psppire-buttonbox.h"
23 #include "psppire-dialog.h"
24
25 #include "helper.h"
26
27 #include <gettext.h>
28
29 #define _(msgid) gettext (msgid)
30 #define N_(msgid) msgid
31
32 GType psppire_button_flags_get_type (void);
33
34
35 static void psppire_button_box_class_init          (PsppireButtonBoxClass *);
36 static void psppire_button_box_init                (PsppireButtonBox      *);
37
38
39 GType
40 psppire_button_box_get_type (void)
41 {
42   static GType button_box_type = 0;
43
44   if (!button_box_type)
45     {
46       static const GTypeInfo button_box_info =
47       {
48         sizeof (PsppireButtonBoxClass),
49         NULL, /* base_init */
50         NULL, /* base_finalize */
51         (GClassInitFunc) psppire_button_box_class_init,
52         NULL, /* class_finalize */
53         NULL, /* class_data */
54         sizeof (PsppireButtonBox),
55         0,
56         (GInstanceInitFunc) psppire_button_box_init,
57       };
58
59       button_box_type = g_type_register_static (GTK_TYPE_BUTTON_BOX,
60                                             "PsppireButtonBox", &button_box_info, G_TYPE_FLAG_ABSTRACT);
61     }
62
63   return button_box_type;
64 }
65
66 enum {
67   PROP_BUTTONS = 1,
68   PROP_DEFAULT = 2
69 };
70
71 static void
72 set_default (PsppireButtonBox *bb)
73 {
74   int i;
75
76   for (i = 0 ; i < n_PsppireButtonBoxButtons ; ++i )
77     if (bb->def == (1 << i))
78       {
79         gtk_widget_set_can_default (bb->button[i], TRUE);
80         gtk_widget_grab_default (bb->button[i]);
81       }
82 }
83
84 static void
85 psppire_buttonbox_set_property (GObject         *object,
86                                 guint            prop_id,
87                                 const GValue    *value,
88                                 GParamSpec      *pspec)
89 {
90   gint i;
91   guint flags;
92   PsppireButtonBox *bb = PSPPIRE_BUTTONBOX (object);
93
94   switch (prop_id)
95     {
96     case PROP_BUTTONS:
97       flags = g_value_get_flags (value);
98       for (i = 0 ; i < n_PsppireButtonBoxButtons ; ++i )
99         g_object_set (bb->button[i], "visible", 0x01 & (flags >> i)  , NULL);
100       break;
101
102     case PROP_DEFAULT:
103       bb->def = g_value_get_flags (value);
104       if (gtk_widget_get_realized (GTK_WIDGET (bb)))
105         set_default (bb);
106       break;
107
108     default:
109       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
110     }
111 }
112
113 static void
114 psppire_buttonbox_get_property (GObject         *object,
115                                 guint            prop_id,
116                                 GValue          *value,
117                                 GParamSpec      *pspec)
118 {
119   guint flags = 0;
120   gint i;
121
122   PsppireButtonBox *bb = PSPPIRE_BUTTONBOX (object);
123
124   switch (prop_id)
125     {
126     case PROP_BUTTONS:
127       for (i = 0 ; i < n_PsppireButtonBoxButtons ; ++i )
128         {
129           gboolean visibility;
130           g_object_get (bb->button[i], "visible", &visibility, NULL);
131
132           if ( visibility )
133             flags |= (0x01 << i);
134         }
135
136       g_value_set_flags (value, flags);
137       break;
138
139     case PROP_DEFAULT:
140       g_value_set_flags (value, bb->def);
141
142     default:
143       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
144       break;
145     }
146 }
147
148
149 typedef enum
150   {
151     PSPPIRE_BUTTON_OK_MASK     = (1 << PSPPIRE_BUTTON_OK),
152     PSPPIRE_BUTTON_GOTO_MASK   = (1 << PSPPIRE_BUTTON_GOTO),
153     PSPPIRE_BUTTON_CONTINUE_MASK = (1 << PSPPIRE_BUTTON_CONTINUE),
154     PSPPIRE_BUTTON_CANCEL_MASK = (1 << PSPPIRE_BUTTON_CANCEL),
155     PSPPIRE_BUTTON_HELP_MASK   = (1 << PSPPIRE_BUTTON_HELP),
156     PSPPIRE_BUTTON_RESET_MASK  = (1 << PSPPIRE_BUTTON_RESET),
157     PSPPIRE_BUTTON_PASTE_MASK  = (1 << PSPPIRE_BUTTON_PASTE)
158   } PsppireButtonMask;
159
160 static GParamSpec *button_flags;
161 static GParamSpec *default_flags;
162
163 static void
164 psppire_button_box_class_init (PsppireButtonBoxClass *class)
165 {
166   GObjectClass *object_class = G_OBJECT_CLASS (class);
167
168   object_class->set_property = psppire_buttonbox_set_property;
169   object_class->get_property = psppire_buttonbox_get_property;
170
171   button_flags =
172     g_param_spec_flags ("buttons",
173                         "Buttons",
174                         "The mask that decides what buttons appear in the button box",
175                         PSPPIRE_TYPE_BUTTON_MASK,
176                         PSPPIRE_BUTTON_OK_MASK |
177                         PSPPIRE_BUTTON_CANCEL_MASK |
178                         PSPPIRE_BUTTON_RESET_MASK |
179                         PSPPIRE_BUTTON_HELP_MASK |
180                         PSPPIRE_BUTTON_PASTE_MASK,
181                         G_PARAM_READWRITE);
182   g_object_class_install_property (object_class,
183                                    PROP_BUTTONS,
184                                    button_flags);
185
186   default_flags =
187     g_param_spec_flags ("default",
188                         "Default",
189                         "The mask that decides what what button grabs the default",
190                         PSPPIRE_TYPE_BUTTON_MASK,
191                         0,
192                         G_PARAM_READWRITE);
193   g_object_class_install_property (object_class,
194                                    PROP_DEFAULT,
195                                    default_flags);
196 }
197
198 static void
199 close_and_respond (GtkWidget *w, gint response)
200 {
201   PsppireDialog *dialog;
202
203   GtkWidget *toplevel = gtk_widget_get_toplevel (w);
204
205   /* If we're not in a psppire dialog (for example when in glade)
206      then do nothing */
207   if ( ! PSPPIRE_IS_DIALOG (toplevel))
208     return;
209
210   dialog = PSPPIRE_DIALOG (toplevel);
211
212   dialog->response = response;
213
214   psppire_dialog_close (dialog);
215 }
216
217
218 static void
219 close_dialog (GtkWidget *w, gpointer data)
220 {
221   close_and_respond (w, GTK_RESPONSE_CLOSE);
222 }
223
224 static void
225 continue_button_clicked (GtkWidget *w, gpointer data)
226 {
227   close_and_respond (w, PSPPIRE_RESPONSE_CONTINUE);
228 }
229
230
231 static void
232 ok_button_clicked (GtkWidget *w, gpointer data)
233 {
234   close_and_respond (w, GTK_RESPONSE_OK);
235 }
236
237
238 static void
239 paste_button_clicked (GtkWidget *w, gpointer data)
240 {
241   close_and_respond (w, PSPPIRE_RESPONSE_PASTE);
242 }
243
244 static void
245 goto_button_clicked (GtkWidget *w, gpointer data)
246 {
247   close_and_respond (w, PSPPIRE_RESPONSE_GOTO);
248 }
249
250
251 static void
252 refresh_clicked (GtkWidget *w, gpointer data)
253 {
254   GtkWidget *toplevel = gtk_widget_get_toplevel (w);
255   PsppireDialog *dialog;
256
257   if ( ! PSPPIRE_IS_DIALOG (toplevel))
258     return;
259
260   dialog = PSPPIRE_DIALOG (toplevel);
261
262   psppire_dialog_reload (dialog);
263 }
264
265
266
267 static void
268 on_validity_change (GtkWidget *toplevel, gboolean valid, gpointer data)
269 {
270   PsppireButtonBox *bb = data;
271
272   /* Set the sensitivity of all the 'executive order' buttons */
273   gtk_widget_set_sensitive (GTK_WIDGET (bb->button[PSPPIRE_BUTTON_OK]), valid);
274   gtk_widget_set_sensitive (GTK_WIDGET (bb->button[PSPPIRE_BUTTON_PASTE]), valid);
275   gtk_widget_set_sensitive (GTK_WIDGET (bb->button[PSPPIRE_BUTTON_GOTO]), valid);
276   gtk_widget_set_sensitive (GTK_WIDGET (bb->button[PSPPIRE_BUTTON_CONTINUE]), valid);
277 }
278
279 static void
280 on_realize (GtkWidget *buttonbox, gpointer data)
281 {
282   GtkWidget *toplevel = gtk_widget_get_toplevel (buttonbox);
283
284   if ( PSPPIRE_IS_DIALOG (toplevel))
285     {
286       g_signal_connect (toplevel, "validity-changed",
287                         G_CALLBACK (on_validity_change), buttonbox);
288     }
289   set_default (PSPPIRE_BUTTONBOX (buttonbox));
290 }
291
292 static void
293 psppire_button_box_init (PsppireButtonBox *bb)
294 {
295   bb->def = PSPPIRE_BUTTON_CONTINUE;
296
297   bb->button[PSPPIRE_BUTTON_OK] = gtk_button_new_from_stock (GTK_STOCK_OK);
298   psppire_box_pack_start_defaults (GTK_BOX (bb), bb->button[PSPPIRE_BUTTON_OK]);
299   g_signal_connect (bb->button[PSPPIRE_BUTTON_OK], "clicked",
300                     G_CALLBACK (ok_button_clicked), NULL);
301   g_object_set (bb->button[PSPPIRE_BUTTON_OK], "no-show-all", TRUE, NULL);
302
303
304   bb->button[PSPPIRE_BUTTON_GOTO] =
305     gtk_button_new_from_stock (GTK_STOCK_JUMP_TO);
306   psppire_box_pack_start_defaults (GTK_BOX (bb), bb->button[PSPPIRE_BUTTON_GOTO]);
307   g_signal_connect (bb->button[PSPPIRE_BUTTON_GOTO], "clicked",
308                     G_CALLBACK (goto_button_clicked), NULL);
309   g_object_set (bb->button[PSPPIRE_BUTTON_GOTO], "no-show-all", TRUE, NULL);
310
311
312   bb->button[PSPPIRE_BUTTON_CONTINUE] =
313     gtk_button_new_with_mnemonic (_("Continue"));
314
315   psppire_box_pack_start_defaults (GTK_BOX (bb),
316                                bb->button[PSPPIRE_BUTTON_CONTINUE]);
317   g_signal_connect (bb->button[PSPPIRE_BUTTON_CONTINUE], "clicked",
318                     G_CALLBACK (continue_button_clicked), NULL);
319
320   g_object_set (bb->button[PSPPIRE_BUTTON_CONTINUE],
321                 "no-show-all", TRUE, NULL);
322
323
324
325   bb->button[PSPPIRE_BUTTON_PASTE] = gtk_button_new_from_stock (GTK_STOCK_PASTE);
326   g_signal_connect (bb->button[PSPPIRE_BUTTON_PASTE], "clicked",
327                     G_CALLBACK (paste_button_clicked), NULL);
328   psppire_box_pack_start_defaults (GTK_BOX (bb), bb->button[PSPPIRE_BUTTON_PASTE]);
329   g_object_set (bb->button[PSPPIRE_BUTTON_PASTE], "no-show-all", TRUE, NULL);
330
331   bb->button[PSPPIRE_BUTTON_CANCEL] = gtk_button_new_from_stock (GTK_STOCK_CANCEL);
332   g_signal_connect (bb->button[PSPPIRE_BUTTON_CANCEL], "clicked",
333                     G_CALLBACK (close_dialog), NULL);
334   psppire_box_pack_start_defaults (GTK_BOX (bb), bb->button[PSPPIRE_BUTTON_CANCEL]);
335   g_object_set (bb->button[PSPPIRE_BUTTON_CANCEL], "no-show-all", TRUE, NULL);
336
337
338   bb->button[PSPPIRE_BUTTON_RESET] = gtk_button_new_from_stock ("pspp-stock-reset");
339   g_signal_connect (bb->button[PSPPIRE_BUTTON_RESET], "clicked",
340                     G_CALLBACK (refresh_clicked), NULL);
341   psppire_box_pack_start_defaults (GTK_BOX (bb), bb->button[PSPPIRE_BUTTON_RESET]);
342   g_object_set (bb->button[PSPPIRE_BUTTON_RESET], "no-show-all", TRUE, NULL);
343
344
345   bb->button[PSPPIRE_BUTTON_HELP] = gtk_button_new_from_stock (GTK_STOCK_HELP);
346   psppire_box_pack_start_defaults (GTK_BOX (bb), bb->button[PSPPIRE_BUTTON_HELP]);
347   g_object_set (bb->button[PSPPIRE_BUTTON_HELP], "no-show-all", TRUE, NULL);
348
349
350   /* Set the default visibilities */
351   {
352     GValue value = { 0 };
353     guint flags;
354     gint i;
355     g_value_init (&value, button_flags->value_type);
356     g_param_value_set_default(button_flags, &value);
357
358
359     flags = g_value_get_flags (&value);
360
361     for (i = 0 ; i < n_PsppireButtonBoxButtons ; ++i )
362       g_object_set (bb->button[i], "visible", 0x01 & (flags >> i)  , NULL);
363
364     g_value_unset (&value);
365   }
366
367
368   g_signal_connect (bb, "realize", G_CALLBACK (on_realize), NULL);
369 }
370
371
372 /* This function is lifted verbatim from the Gtk2.10.6 library */
373
374 void
375 _psppire_button_box_child_requisition (GtkWidget *widget,
376                                        int       *nvis_children,
377                                        int       *nvis_secondaries,
378                                        int       *width,
379                                        int       *height)
380 {
381   GtkButtonBox *bbox;
382   GtkBoxChild *child;
383   GList *children;
384   gint nchildren;
385   gint nsecondaries;
386   gint needed_width;
387   gint needed_height;
388   GtkRequisition child_requisition;
389   gint ipad_w;
390   gint ipad_h;
391   gint width_default;
392   gint height_default;
393   gint ipad_x_default;
394   gint ipad_y_default;
395
396   gint child_min_width;
397   gint child_min_height;
398   gint ipad_x;
399   gint ipad_y;
400
401   g_return_if_fail (GTK_IS_BUTTON_BOX (widget));
402
403   bbox = GTK_BUTTON_BOX (widget);
404
405   gtk_widget_style_get (widget,
406                         "child-min-width", &width_default,
407                         "child-min-height", &height_default,
408                         "child-internal-pad-x", &ipad_x_default,
409                         "child-internal-pad-y", &ipad_y_default,
410                         NULL);
411
412   child_min_width = bbox->child_min_width   != GTK_BUTTONBOX_DEFAULT
413     ? bbox->child_min_width : width_default;
414   child_min_height = bbox->child_min_height !=GTK_BUTTONBOX_DEFAULT
415     ? bbox->child_min_height : height_default;
416   ipad_x = bbox->child_ipad_x != GTK_BUTTONBOX_DEFAULT
417     ? bbox->child_ipad_x : ipad_x_default;
418   ipad_y = bbox->child_ipad_y != GTK_BUTTONBOX_DEFAULT
419     ? bbox->child_ipad_y : ipad_y_default;
420
421   nchildren = 0;
422   nsecondaries = 0;
423   children = GTK_BOX(bbox)->children;
424   needed_width = child_min_width;
425   needed_height = child_min_height;
426   ipad_w = ipad_x * 2;
427   ipad_h = ipad_y * 2;
428
429   while (children)
430     {
431       child = children->data;
432       children = children->next;
433
434       if (gtk_widget_get_visible (child->widget))
435         {
436           nchildren += 1;
437           gtk_widget_size_request (child->widget, &child_requisition);
438
439           if (child_requisition.width + ipad_w > needed_width)
440             needed_width = child_requisition.width + ipad_w;
441           if (child_requisition.height + ipad_h > needed_height)
442             needed_height = child_requisition.height + ipad_h;
443           if (child->is_secondary)
444             nsecondaries++;
445         }
446     }
447
448   if (nvis_children)
449     *nvis_children = nchildren;
450   if (nvis_secondaries)
451     *nvis_secondaries = nsecondaries;
452   if (width)
453     *width = needed_width;
454   if (height)
455     *height = needed_height;
456 }
457
458
459 GType
460 psppire_button_flags_get_type (void)
461 {
462   static GType ftype = 0;
463   if (ftype == 0)
464     {
465       static const GFlagsValue values[] =
466         {
467           { PSPPIRE_BUTTON_OK_MASK,     "PSPPIRE_BUTTON_OK_MASK",     N_("OK") },
468           { PSPPIRE_BUTTON_GOTO_MASK,   "PSPPIRE_BUTTON_GOTO_MASK", N_("Go To") },
469           { PSPPIRE_BUTTON_CONTINUE_MASK,"PSPPIRE_BUTTON_CONTINUE_MASK", N_("Continue") },
470           { PSPPIRE_BUTTON_CANCEL_MASK, "PSPPIRE_BUTTON_CANCEL_MASK", N_("Cancel") },
471           { PSPPIRE_BUTTON_HELP_MASK,   "PSPPIRE_BUTTON_HELP_MASK",   N_("Help") },
472           { PSPPIRE_BUTTON_RESET_MASK,  "PSPPIRE_BUTTON_RESET_MASK",  N_("Reset") },
473           { PSPPIRE_BUTTON_PASTE_MASK,  "PSPPIRE_BUTTON_PASTE_MASK",  N_("Paste") },
474           { 0, NULL, NULL }
475         };
476
477       ftype = g_flags_register_static
478         (g_intern_static_string ("PsppireButtonFlags"), values);
479
480     }
481   return ftype;
482 }
483