68a147f74ca1322777fb681bab993a2423f15e0b
[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 G_DEFINE_TYPE (PsppireButtonBox, psppire_button_box, GTK_TYPE_BUTTON_BOX)
35
36 enum {
37   PROP_BUTTONS = 1,
38   PROP_DEFAULT = 2
39 };
40
41 static void
42 set_default (PsppireButtonBox *bb)
43 {
44   int i;
45
46   for (i = 0 ; i < n_PsppireButtonBoxButtons ; ++i)
47     if (bb->def == (1 << i))
48       {
49         gtk_widget_set_can_default (bb->button[i], TRUE);
50         gtk_widget_grab_default (bb->button[i]);
51       }
52 }
53
54 static void
55 psppire_buttonbox_set_property (GObject         *object,
56                                 guint            prop_id,
57                                 const GValue    *value,
58                                 GParamSpec      *pspec)
59 {
60   gint i;
61   guint flags;
62   PsppireButtonBox *bb = PSPPIRE_BUTTON_BOX (object);
63
64   switch (prop_id)
65     {
66     case PROP_BUTTONS:
67       flags = g_value_get_flags (value);
68       for (i = 0 ; i < n_PsppireButtonBoxButtons ; ++i)
69         g_object_set (bb->button[i], "visible", 0x01 & (flags >> i)  , NULL);
70       break;
71
72     case PROP_DEFAULT:
73       bb->def = g_value_get_flags (value);
74       if (gtk_widget_get_realized (GTK_WIDGET (bb)))
75         set_default (bb);
76       break;
77
78     default:
79       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
80     }
81 }
82
83 static void
84 psppire_buttonbox_get_property (GObject         *object,
85                                 guint            prop_id,
86                                 GValue          *value,
87                                 GParamSpec      *pspec)
88 {
89   guint flags = 0;
90   gint i;
91
92   PsppireButtonBox *bb = PSPPIRE_BUTTON_BOX (object);
93
94   switch (prop_id)
95     {
96     case PROP_BUTTONS:
97       for (i = 0 ; i < n_PsppireButtonBoxButtons ; ++i)
98         {
99           gboolean visibility;
100           g_object_get (bb->button[i], "visible", &visibility, NULL);
101
102           if (visibility)
103             flags |= (0x01 << i);
104         }
105
106       g_value_set_flags (value, flags);
107       break;
108
109     case PROP_DEFAULT:
110       g_value_set_flags (value, bb->def);
111       break;
112
113     default:
114       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
115       break;
116     }
117 }
118
119
120 static GParamSpec *button_flags;
121 static GParamSpec *default_flags;
122
123 static void
124 psppire_button_box_class_init (PsppireButtonBoxClass *class)
125 {
126   GObjectClass *object_class = G_OBJECT_CLASS (class);
127
128   object_class->set_property = psppire_buttonbox_set_property;
129   object_class->get_property = psppire_buttonbox_get_property;
130
131   button_flags =
132     g_param_spec_flags ("buttons",
133                         "Buttons",
134                         "The mask that decides what buttons appear in the button box",
135                         PSPPIRE_TYPE_BUTTON_MASK,
136                         PSPPIRE_BUTTON_OK_MASK |
137                         PSPPIRE_BUTTON_CANCEL_MASK |
138                         PSPPIRE_BUTTON_RESET_MASK |
139                         PSPPIRE_BUTTON_HELP_MASK |
140                         PSPPIRE_BUTTON_PASTE_MASK,
141                         G_PARAM_READWRITE);
142   g_object_class_install_property (object_class,
143                                    PROP_BUTTONS,
144                                    button_flags);
145
146   default_flags =
147     g_param_spec_flags ("default",
148                         "Default",
149                         "The mask that decides what what button grabs the default",
150                         PSPPIRE_TYPE_BUTTON_MASK,
151                         0,
152                         G_PARAM_READWRITE);
153   g_object_class_install_property (object_class,
154                                    PROP_DEFAULT,
155                                    default_flags);
156 }
157
158 static void
159 close_and_respond (GtkWidget *w, gint response)
160 {
161   PsppireDialog *dialog;
162
163   GtkWidget *toplevel = gtk_widget_get_toplevel (w);
164
165   /* If we're not in a psppire dialog (for example when in glade)
166      then do nothing */
167   if (! PSPPIRE_IS_DIALOG (toplevel))
168     return;
169
170   dialog = PSPPIRE_DIALOG (toplevel);
171
172   dialog->response = response;
173
174   psppire_dialog_close (dialog);
175 }
176
177 static gboolean
178 is_acceptable (GtkWidget *w)
179 {
180   GtkWidget *toplevel = gtk_widget_get_toplevel (w);
181
182   return (PSPPIRE_IS_DIALOG (toplevel)
183           && psppire_dialog_is_acceptable (PSPPIRE_DIALOG (toplevel)));
184 }
185
186 static void
187 close_dialog (GtkWidget *w, gpointer data)
188 {
189   close_and_respond (w, GTK_RESPONSE_CLOSE);
190 }
191
192 static void
193 continue_button_clicked (GtkWidget *w, gpointer data)
194 {
195   if (is_acceptable (w))
196     close_and_respond (w, PSPPIRE_RESPONSE_CONTINUE);
197 }
198
199
200 static void
201 ok_button_clicked (GtkWidget *w, gpointer data)
202 {
203   if (is_acceptable (w))
204     close_and_respond (w, GTK_RESPONSE_OK);
205 }
206
207
208 static void
209 paste_button_clicked (GtkWidget *w, gpointer data)
210 {
211   if (is_acceptable (w))
212     close_and_respond (w, PSPPIRE_RESPONSE_PASTE);
213 }
214
215 static void
216 goto_button_clicked (GtkWidget *w, gpointer data)
217 {
218   if (is_acceptable (w))
219     close_and_respond (w, PSPPIRE_RESPONSE_GOTO);
220 }
221
222
223 static void
224 refresh_clicked (GtkWidget *w, gpointer data)
225 {
226   GtkWidget *toplevel = gtk_widget_get_toplevel (w);
227   PsppireDialog *dialog;
228
229   if (! PSPPIRE_IS_DIALOG (toplevel))
230     return;
231
232   dialog = PSPPIRE_DIALOG (toplevel);
233
234   psppire_dialog_reload (dialog);
235 }
236
237 static void
238 help_clicked (GtkWidget *w, gpointer data)
239 {
240   GtkWidget *toplevel = gtk_widget_get_toplevel (w);
241   PsppireDialog *dialog;
242
243   if (! PSPPIRE_IS_DIALOG (toplevel))
244     return;
245
246   dialog = PSPPIRE_DIALOG (toplevel);
247
248   psppire_dialog_help (dialog);
249 }
250
251 static void
252 on_validity_change (GtkWidget *toplevel, gboolean valid, gpointer data)
253 {
254   PsppireButtonBox *bb = data;
255
256   /* Set the sensitivity of all the 'executive order' buttons */
257   gtk_widget_set_sensitive (GTK_WIDGET (bb->button[PSPPIRE_BUTTON_OK]), valid);
258   gtk_widget_set_sensitive (GTK_WIDGET (bb->button[PSPPIRE_BUTTON_PASTE]), valid);
259   gtk_widget_set_sensitive (GTK_WIDGET (bb->button[PSPPIRE_BUTTON_GOTO]), valid);
260   gtk_widget_set_sensitive (GTK_WIDGET (bb->button[PSPPIRE_BUTTON_CONTINUE]), valid);
261 }
262
263 static gboolean
264 on_key_press (GtkWidget *w, GdkEventKey *e, gpointer ud)
265 {
266   PsppireButtonBox *bb = PSPPIRE_BUTTON_BOX (ud);
267   if (e->keyval == GDK_KEY_Escape)
268     {
269       g_signal_emit_by_name (bb->button[PSPPIRE_BUTTON_CANCEL], "activate");
270       g_signal_emit_by_name (bb->button[PSPPIRE_BUTTON_CLOSE], "activate");
271     }
272   return FALSE;
273 }
274
275
276 static void
277 on_realize (GtkWidget *buttonbox, gpointer data)
278 {
279   GtkWidget *toplevel = gtk_widget_get_toplevel (buttonbox);
280
281   if (PSPPIRE_IS_DIALOG (toplevel))
282     {
283       g_signal_connect (toplevel, "validity-changed",
284                         G_CALLBACK (on_validity_change), buttonbox);
285
286       g_signal_connect (toplevel, "key-press-event",
287                         G_CALLBACK (on_key_press), buttonbox);
288     }
289
290   set_default (PSPPIRE_BUTTON_BOX (buttonbox));
291 }
292
293
294 static void
295 psppire_button_box_init (PsppireButtonBox *bb)
296 {
297   bb->def = PSPPIRE_BUTTON_CONTINUE;
298
299   bb->button[PSPPIRE_BUTTON_OK] = gtk_button_new_with_label (_("OK"));
300   psppire_box_pack_start_defaults (GTK_BOX (bb), bb->button[PSPPIRE_BUTTON_OK]);
301   g_signal_connect (bb->button[PSPPIRE_BUTTON_OK], "clicked",
302                     G_CALLBACK (ok_button_clicked), NULL);
303   g_object_set (bb->button[PSPPIRE_BUTTON_OK], "no-show-all", TRUE, NULL);
304
305
306   bb->button[PSPPIRE_BUTTON_GOTO] =
307     gtk_button_new_with_label (_("Go To"));
308   psppire_box_pack_start_defaults (GTK_BOX (bb), bb->button[PSPPIRE_BUTTON_GOTO]);
309   g_signal_connect (bb->button[PSPPIRE_BUTTON_GOTO], "clicked",
310                     G_CALLBACK (goto_button_clicked), NULL);
311   g_object_set (bb->button[PSPPIRE_BUTTON_GOTO], "no-show-all", TRUE, NULL);
312
313
314   bb->button[PSPPIRE_BUTTON_CONTINUE] =
315     gtk_button_new_with_mnemonic (_("Continue"));
316
317   psppire_box_pack_start_defaults (GTK_BOX (bb),
318                                bb->button[PSPPIRE_BUTTON_CONTINUE]);
319   g_signal_connect (bb->button[PSPPIRE_BUTTON_CONTINUE], "clicked",
320                     G_CALLBACK (continue_button_clicked), NULL);
321
322   g_object_set (bb->button[PSPPIRE_BUTTON_CONTINUE],
323                 "no-show-all", TRUE, NULL);
324
325
326
327   bb->button[PSPPIRE_BUTTON_PASTE] = gtk_button_new_with_label (_("Paste"));
328   g_signal_connect (bb->button[PSPPIRE_BUTTON_PASTE], "clicked",
329                     G_CALLBACK (paste_button_clicked), NULL);
330   psppire_box_pack_start_defaults (GTK_BOX (bb), bb->button[PSPPIRE_BUTTON_PASTE]);
331   g_object_set (bb->button[PSPPIRE_BUTTON_PASTE], "no-show-all", TRUE, NULL);
332
333   bb->button[PSPPIRE_BUTTON_CANCEL] = gtk_button_new_with_label (_("Cancel"));
334   g_signal_connect (bb->button[PSPPIRE_BUTTON_CANCEL], "clicked",
335                     G_CALLBACK (close_dialog), NULL);
336   psppire_box_pack_start_defaults (GTK_BOX (bb), bb->button[PSPPIRE_BUTTON_CANCEL]);
337   g_object_set (bb->button[PSPPIRE_BUTTON_CANCEL], "no-show-all", TRUE, NULL);
338
339   bb->button[PSPPIRE_BUTTON_CLOSE] = gtk_button_new_with_label (_("Close"));
340   g_signal_connect (bb->button[PSPPIRE_BUTTON_CLOSE], "clicked",
341                     G_CALLBACK (close_dialog), NULL);
342   psppire_box_pack_start_defaults (GTK_BOX (bb), bb->button[PSPPIRE_BUTTON_CLOSE]);
343   g_object_set (bb->button[PSPPIRE_BUTTON_CLOSE], "no-show-all", TRUE, NULL);
344
345
346   bb->button[PSPPIRE_BUTTON_RESET] = gtk_button_new_with_label (_("Reset"));
347   g_signal_connect (bb->button[PSPPIRE_BUTTON_RESET], "clicked",
348                     G_CALLBACK (refresh_clicked), NULL);
349   psppire_box_pack_start_defaults (GTK_BOX (bb), bb->button[PSPPIRE_BUTTON_RESET]);
350   g_object_set (bb->button[PSPPIRE_BUTTON_RESET], "no-show-all", TRUE, NULL);
351
352
353   bb->button[PSPPIRE_BUTTON_HELP] = gtk_button_new_with_label (_("Help"));
354   g_signal_connect (bb->button[PSPPIRE_BUTTON_HELP], "clicked",
355                     G_CALLBACK (help_clicked), NULL);
356   psppire_box_pack_start_defaults (GTK_BOX (bb), bb->button[PSPPIRE_BUTTON_HELP]);
357   g_object_set (bb->button[PSPPIRE_BUTTON_HELP], "no-show-all", TRUE, NULL);
358
359
360   /* Set the default visibilities */
361   {
362     GValue value = { 0 };
363     guint flags;
364     gint i;
365     g_value_init (&value, button_flags->value_type);
366     g_param_value_set_default(button_flags, &value);
367
368
369     flags = g_value_get_flags (&value);
370
371     for (i = 0 ; i < n_PsppireButtonBoxButtons ; ++i)
372       g_object_set (bb->button[i], "visible", 0x01 & (flags >> i)  , NULL);
373
374     g_value_unset (&value);
375   }
376
377
378   g_signal_connect (bb, "realize", G_CALLBACK (on_realize), NULL);
379 }
380
381 GType
382 psppire_button_flags_get_type (void)
383 {
384   static GType ftype = 0;
385   if (ftype == 0)
386     {
387       static const GFlagsValue values[] =
388         {
389           { PSPPIRE_BUTTON_OK_MASK,      "PSPPIRE_BUTTON_OK_MASK",       "Accept dialog and run it" },
390           { PSPPIRE_BUTTON_GOTO_MASK,    "PSPPIRE_BUTTON_GOTO_MASK",     "Goto case/variable" },
391           { PSPPIRE_BUTTON_CONTINUE_MASK,"PSPPIRE_BUTTON_CONTINUE_MASK", "Accept and close the subdialog" },
392           { PSPPIRE_BUTTON_CANCEL_MASK,  "PSPPIRE_BUTTON_CANCEL_MASK",   "Close dialog and discard settings" },
393           { PSPPIRE_BUTTON_CLOSE_MASK,   "PSPPIRE_BUTTON_CLOSE_MASK",    "Close dialog" },
394           { PSPPIRE_BUTTON_HELP_MASK,    "PSPPIRE_BUTTON_HELP_MASK",     "Invoke context sensitive help" },
395           { PSPPIRE_BUTTON_RESET_MASK,   "PSPPIRE_BUTTON_RESET_MASK",    "Restore dialog to its default settings" },
396           { PSPPIRE_BUTTON_PASTE_MASK,   "PSPPIRE_BUTTON_PASTE_MASK",    "Accept dialog and paste syntax" },
397           { 0, NULL, NULL }
398         };
399
400       ftype = g_flags_register_static
401         (g_intern_static_string ("PsppireButtonFlags"), values);
402
403     }
404   return ftype;
405 }
406
407 GtkWidget*
408 psppire_button_box_new (void)
409 {
410   return GTK_WIDGET (g_object_new (psppire_button_box_get_type (), NULL));
411 }