CORRELATIONS: Fixed bug displaying non-sqaure correlation matrices
[pspp] / src / ui / gui / psppire-buttonbox.c
1 /* PSPPIRE - a graphical user interface for PSPP.
2    Copyright (C) 2007, 2010, 2011, 2012  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_CLOSE_MASK  = (1 << PSPPIRE_BUTTON_CLOSE),
156     PSPPIRE_BUTTON_HELP_MASK   = (1 << PSPPIRE_BUTTON_HELP),
157     PSPPIRE_BUTTON_RESET_MASK  = (1 << PSPPIRE_BUTTON_RESET),
158     PSPPIRE_BUTTON_PASTE_MASK  = (1 << PSPPIRE_BUTTON_PASTE)
159   } PsppireButtonMask;
160
161 static GParamSpec *button_flags;
162 static GParamSpec *default_flags;
163
164 static void
165 psppire_button_box_class_init (PsppireButtonBoxClass *class)
166 {
167   GObjectClass *object_class = G_OBJECT_CLASS (class);
168
169   object_class->set_property = psppire_buttonbox_set_property;
170   object_class->get_property = psppire_buttonbox_get_property;
171
172   button_flags =
173     g_param_spec_flags ("buttons",
174                         "Buttons",
175                         "The mask that decides what buttons appear in the button box",
176                         PSPPIRE_TYPE_BUTTON_MASK,
177                         PSPPIRE_BUTTON_OK_MASK |
178                         PSPPIRE_BUTTON_CANCEL_MASK |
179                         PSPPIRE_BUTTON_RESET_MASK |
180                         PSPPIRE_BUTTON_HELP_MASK |
181                         PSPPIRE_BUTTON_PASTE_MASK,
182                         G_PARAM_READWRITE);
183   g_object_class_install_property (object_class,
184                                    PROP_BUTTONS,
185                                    button_flags);
186
187   default_flags =
188     g_param_spec_flags ("default",
189                         "Default",
190                         "The mask that decides what what button grabs the default",
191                         PSPPIRE_TYPE_BUTTON_MASK,
192                         0,
193                         G_PARAM_READWRITE);
194   g_object_class_install_property (object_class,
195                                    PROP_DEFAULT,
196                                    default_flags);
197 }
198
199 static void
200 close_and_respond (GtkWidget *w, gint response)
201 {
202   PsppireDialog *dialog;
203
204   GtkWidget *toplevel = gtk_widget_get_toplevel (w);
205
206   /* If we're not in a psppire dialog (for example when in glade)
207      then do nothing */
208   if ( ! PSPPIRE_IS_DIALOG (toplevel))
209     return;
210
211   dialog = PSPPIRE_DIALOG (toplevel);
212
213   dialog->response = response;
214
215   psppire_dialog_close (dialog);
216 }
217
218
219 static void
220 close_dialog (GtkWidget *w, gpointer data)
221 {
222   close_and_respond (w, GTK_RESPONSE_CLOSE);
223 }
224
225 static void
226 continue_button_clicked (GtkWidget *w, gpointer data)
227 {
228   close_and_respond (w, PSPPIRE_RESPONSE_CONTINUE);
229 }
230
231
232 static void
233 ok_button_clicked (GtkWidget *w, gpointer data)
234 {
235   close_and_respond (w, GTK_RESPONSE_OK);
236 }
237
238
239 static void
240 paste_button_clicked (GtkWidget *w, gpointer data)
241 {
242   close_and_respond (w, PSPPIRE_RESPONSE_PASTE);
243 }
244
245 static void
246 goto_button_clicked (GtkWidget *w, gpointer data)
247 {
248   close_and_respond (w, PSPPIRE_RESPONSE_GOTO);
249 }
250
251
252 static void
253 refresh_clicked (GtkWidget *w, gpointer data)
254 {
255   GtkWidget *toplevel = gtk_widget_get_toplevel (w);
256   PsppireDialog *dialog;
257
258   if ( ! PSPPIRE_IS_DIALOG (toplevel))
259     return;
260
261   dialog = PSPPIRE_DIALOG (toplevel);
262
263   psppire_dialog_reload (dialog);
264 }
265
266
267
268 static void
269 help_clicked (GtkWidget *w, gpointer data)
270 {
271   GtkWidget *toplevel = gtk_widget_get_toplevel (w);
272   PsppireDialog *dialog;
273
274   if ( ! PSPPIRE_IS_DIALOG (toplevel))
275     return;
276
277   dialog = PSPPIRE_DIALOG (toplevel);
278
279   psppire_dialog_help (dialog);
280 }
281
282
283
284 static void
285 on_validity_change (GtkWidget *toplevel, gboolean valid, gpointer data)
286 {
287   PsppireButtonBox *bb = data;
288
289   /* Set the sensitivity of all the 'executive order' buttons */
290   gtk_widget_set_sensitive (GTK_WIDGET (bb->button[PSPPIRE_BUTTON_OK]), valid);
291   gtk_widget_set_sensitive (GTK_WIDGET (bb->button[PSPPIRE_BUTTON_PASTE]), valid);
292   gtk_widget_set_sensitive (GTK_WIDGET (bb->button[PSPPIRE_BUTTON_GOTO]), valid);
293   gtk_widget_set_sensitive (GTK_WIDGET (bb->button[PSPPIRE_BUTTON_CONTINUE]), valid);
294 }
295
296 static void
297 on_realize (GtkWidget *buttonbox, gpointer data)
298 {
299   GtkWidget *toplevel = gtk_widget_get_toplevel (buttonbox);
300
301   if ( PSPPIRE_IS_DIALOG (toplevel))
302     {
303       g_signal_connect (toplevel, "validity-changed",
304                         G_CALLBACK (on_validity_change), buttonbox);
305     }
306   set_default (PSPPIRE_BUTTONBOX (buttonbox));
307 }
308
309 static void
310 psppire_button_box_init (PsppireButtonBox *bb)
311 {
312   bb->def = PSPPIRE_BUTTON_CONTINUE;
313
314   bb->button[PSPPIRE_BUTTON_OK] = gtk_button_new_from_stock (GTK_STOCK_OK);
315   psppire_box_pack_start_defaults (GTK_BOX (bb), bb->button[PSPPIRE_BUTTON_OK]);
316   g_signal_connect (bb->button[PSPPIRE_BUTTON_OK], "clicked",
317                     G_CALLBACK (ok_button_clicked), NULL);
318   g_object_set (bb->button[PSPPIRE_BUTTON_OK], "no-show-all", TRUE, NULL);
319
320
321   bb->button[PSPPIRE_BUTTON_GOTO] =
322     gtk_button_new_from_stock (GTK_STOCK_JUMP_TO);
323   psppire_box_pack_start_defaults (GTK_BOX (bb), bb->button[PSPPIRE_BUTTON_GOTO]);
324   g_signal_connect (bb->button[PSPPIRE_BUTTON_GOTO], "clicked",
325                     G_CALLBACK (goto_button_clicked), NULL);
326   g_object_set (bb->button[PSPPIRE_BUTTON_GOTO], "no-show-all", TRUE, NULL);
327
328
329   bb->button[PSPPIRE_BUTTON_CONTINUE] =
330     gtk_button_new_with_mnemonic (_("Continue"));
331
332   psppire_box_pack_start_defaults (GTK_BOX (bb),
333                                bb->button[PSPPIRE_BUTTON_CONTINUE]);
334   g_signal_connect (bb->button[PSPPIRE_BUTTON_CONTINUE], "clicked",
335                     G_CALLBACK (continue_button_clicked), NULL);
336
337   g_object_set (bb->button[PSPPIRE_BUTTON_CONTINUE],
338                 "no-show-all", TRUE, NULL);
339
340
341
342   bb->button[PSPPIRE_BUTTON_PASTE] = gtk_button_new_from_stock (GTK_STOCK_PASTE);
343   g_signal_connect (bb->button[PSPPIRE_BUTTON_PASTE], "clicked",
344                     G_CALLBACK (paste_button_clicked), NULL);
345   psppire_box_pack_start_defaults (GTK_BOX (bb), bb->button[PSPPIRE_BUTTON_PASTE]);
346   g_object_set (bb->button[PSPPIRE_BUTTON_PASTE], "no-show-all", TRUE, NULL);
347
348   bb->button[PSPPIRE_BUTTON_CANCEL] = gtk_button_new_from_stock (GTK_STOCK_CANCEL);
349   g_signal_connect (bb->button[PSPPIRE_BUTTON_CANCEL], "clicked",
350                     G_CALLBACK (close_dialog), NULL);
351   psppire_box_pack_start_defaults (GTK_BOX (bb), bb->button[PSPPIRE_BUTTON_CANCEL]);
352   g_object_set (bb->button[PSPPIRE_BUTTON_CANCEL], "no-show-all", TRUE, NULL);
353
354   bb->button[PSPPIRE_BUTTON_CLOSE] = gtk_button_new_from_stock (GTK_STOCK_CLOSE);
355   g_signal_connect (bb->button[PSPPIRE_BUTTON_CLOSE], "clicked",
356                     G_CALLBACK (close_dialog), NULL);
357   psppire_box_pack_start_defaults (GTK_BOX (bb), bb->button[PSPPIRE_BUTTON_CLOSE]);
358   g_object_set (bb->button[PSPPIRE_BUTTON_CLOSE], "no-show-all", TRUE, NULL);
359
360
361   bb->button[PSPPIRE_BUTTON_RESET] = gtk_button_new_from_stock ("pspp-stock-reset");
362   g_signal_connect (bb->button[PSPPIRE_BUTTON_RESET], "clicked",
363                     G_CALLBACK (refresh_clicked), NULL);
364   psppire_box_pack_start_defaults (GTK_BOX (bb), bb->button[PSPPIRE_BUTTON_RESET]);
365   g_object_set (bb->button[PSPPIRE_BUTTON_RESET], "no-show-all", TRUE, NULL);
366
367
368   bb->button[PSPPIRE_BUTTON_HELP] = gtk_button_new_from_stock (GTK_STOCK_HELP);
369   g_signal_connect (bb->button[PSPPIRE_BUTTON_HELP], "clicked",
370                     G_CALLBACK (help_clicked), NULL);
371   psppire_box_pack_start_defaults (GTK_BOX (bb), bb->button[PSPPIRE_BUTTON_HELP]);
372   g_object_set (bb->button[PSPPIRE_BUTTON_HELP], "no-show-all", TRUE, NULL);
373
374
375   /* Set the default visibilities */
376   {
377     GValue value = { 0 };
378     guint flags;
379     gint i;
380     g_value_init (&value, button_flags->value_type);
381     g_param_value_set_default(button_flags, &value);
382
383
384     flags = g_value_get_flags (&value);
385
386     for (i = 0 ; i < n_PsppireButtonBoxButtons ; ++i )
387       g_object_set (bb->button[i], "visible", 0x01 & (flags >> i)  , NULL);
388
389     g_value_unset (&value);
390   }
391
392
393   g_signal_connect (bb, "realize", G_CALLBACK (on_realize), NULL);
394 }
395
396
397 /* This function is lifted verbatim from the Gtk2.10.6 library */
398
399 void
400 _psppire_button_box_child_requisition (GtkWidget *widget,
401                                        int       *nvis_children,
402                                        int       *nvis_secondaries,
403                                        int       *width,
404                                        int       *height)
405 {
406   GtkButtonBox *bbox;
407   GtkBoxChild *child;
408   GList *children;
409   gint nchildren;
410   gint nsecondaries;
411   gint needed_width;
412   gint needed_height;
413   GtkRequisition child_requisition;
414   gint ipad_w;
415   gint ipad_h;
416   gint width_default;
417   gint height_default;
418   gint ipad_x_default;
419   gint ipad_y_default;
420
421   gint child_min_width;
422   gint child_min_height;
423   gint ipad_x;
424   gint ipad_y;
425
426   g_return_if_fail (GTK_IS_BUTTON_BOX (widget));
427
428   bbox = GTK_BUTTON_BOX (widget);
429
430   gtk_widget_style_get (widget,
431                         "child-min-width", &width_default,
432                         "child-min-height", &height_default,
433                         "child-internal-pad-x", &ipad_x_default,
434                         "child-internal-pad-y", &ipad_y_default,
435                         NULL);
436
437   child_min_width = bbox->child_min_width   != GTK_BUTTONBOX_DEFAULT
438     ? bbox->child_min_width : width_default;
439   child_min_height = bbox->child_min_height !=GTK_BUTTONBOX_DEFAULT
440     ? bbox->child_min_height : height_default;
441   ipad_x = bbox->child_ipad_x != GTK_BUTTONBOX_DEFAULT
442     ? bbox->child_ipad_x : ipad_x_default;
443   ipad_y = bbox->child_ipad_y != GTK_BUTTONBOX_DEFAULT
444     ? bbox->child_ipad_y : ipad_y_default;
445
446   nchildren = 0;
447   nsecondaries = 0;
448   children = GTK_BOX(bbox)->children;
449   needed_width = child_min_width;
450   needed_height = child_min_height;
451   ipad_w = ipad_x * 2;
452   ipad_h = ipad_y * 2;
453
454   while (children)
455     {
456       child = children->data;
457       children = children->next;
458
459       if (gtk_widget_get_visible (child->widget))
460         {
461           nchildren += 1;
462           gtk_widget_size_request (child->widget, &child_requisition);
463
464           if (child_requisition.width + ipad_w > needed_width)
465             needed_width = child_requisition.width + ipad_w;
466           if (child_requisition.height + ipad_h > needed_height)
467             needed_height = child_requisition.height + ipad_h;
468           if (child->is_secondary)
469             nsecondaries++;
470         }
471     }
472
473   if (nvis_children)
474     *nvis_children = nchildren;
475   if (nvis_secondaries)
476     *nvis_secondaries = nsecondaries;
477   if (width)
478     *width = needed_width;
479   if (height)
480     *height = needed_height;
481 }
482
483
484 GType
485 psppire_button_flags_get_type (void)
486 {
487   static GType ftype = 0;
488   if (ftype == 0)
489     {
490       static const GFlagsValue values[] =
491         {
492           { PSPPIRE_BUTTON_OK_MASK,      "PSPPIRE_BUTTON_OK_MASK",       "Accept dialog and run it" },
493           { PSPPIRE_BUTTON_GOTO_MASK,    "PSPPIRE_BUTTON_GOTO_MASK",     "Goto case/variable" },
494           { PSPPIRE_BUTTON_CONTINUE_MASK,"PSPPIRE_BUTTON_CONTINUE_MASK", "Accept and close the subdialog" },
495           { PSPPIRE_BUTTON_CANCEL_MASK,  "PSPPIRE_BUTTON_CANCEL_MASK",   "Close dialog and discard settings" },
496           { PSPPIRE_BUTTON_CLOSE_MASK,   "PSPPIRE_BUTTON_CLOSE_MASK",    "Close dialog" },
497           { PSPPIRE_BUTTON_HELP_MASK,    "PSPPIRE_BUTTON_HELP_MASK",     "Invoke context sensitive help" },
498           { PSPPIRE_BUTTON_RESET_MASK,   "PSPPIRE_BUTTON_RESET_MASK",    "Restore dialog to its default settings" },
499           { PSPPIRE_BUTTON_PASTE_MASK,   "PSPPIRE_BUTTON_PASTE_MASK",    "Accept dialog and paste syntax" },
500           { 0, NULL, NULL }
501         };
502       
503       ftype = g_flags_register_static
504         (g_intern_static_string ("PsppireButtonFlags"), values);
505
506     }
507   return ftype;
508 }
509