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