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