Change some instances of GtkAction to PsppireDialogAction
[pspp] / src / ui / gui / psppire-dialog-action-frequencies.c
1 /* PSPPIRE - a graphical user interface for PSPP.
2    Copyright (C) 2012, 2015  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 "psppire-dialog-action-frequencies.h"
21 #include "psppire-value-entry.h"
22
23 #include "dialog-common.h"
24 #include "helper.h"
25 #include <ui/syntax-gen.h>
26 #include "psppire-var-view.h"
27
28 #include "psppire-dialog.h"
29 #include "builder-wrapper.h"
30 #include "psppire-checkbox-treeview.h"
31 #include "psppire-dict.h"
32 #include "libpspp/str.h"
33
34 #include "gettext.h"
35 #define _(msgid) gettext (msgid)
36 #define N_(msgid) msgid
37
38
39 #define FREQUENCY_STATS                                 \
40   FS (MEAN, N_("Mean"))                                 \
41   FS (STDDEV, N_("Standard deviation"))                 \
42   FS (MINIMUM, N_("Minimum"))                           \
43   FS (MAXIMUM, N_("Maximum"))                           \
44   FS (SEMEAN, N_("Standard error of the mean"))         \
45   FS (VARIANCE, N_("Variance"))                         \
46   FS (SKEWNESS, N_("Skewness"))                         \
47   FS (SESKEW, N_("Standard error of the skewness"))     \
48   FS (RANGE, N_("Range"))                               \
49   FS (MODE, N_("Mode"))                                 \
50   FS (KURTOSIS, N_("Kurtosis"))                         \
51   FS (SEKURT, N_("Standard error of the kurtosis"))     \
52   FS (MEDIAN, N_("Median"))                             \
53   FS (SUM, N_("Sum"))
54
55
56
57 enum
58   {
59 #define FS(NAME, LABEL) FS_##NAME,
60     FREQUENCY_STATS
61 #undef FS
62     N_FREQUENCY_STATS
63   };
64
65 enum
66   {
67 #define FS(NAME, LABEL) B_FS_##NAME = 1u << FS_##NAME,
68     FREQUENCY_STATS
69 #undef FS
70     B_FS_ALL = (1u << N_FREQUENCY_STATS) - 1,
71     B_FS_DEFAULT = B_FS_MEAN | B_FS_STDDEV | B_FS_MINIMUM | B_FS_MAXIMUM
72   };
73
74
75 static const struct checkbox_entry_item stats[] = {
76 #define FS(NAME, LABEL) {#NAME, LABEL},
77   FREQUENCY_STATS
78 #undef FS
79 };
80
81
82 static void
83 on_tables_clicked (PsppireDialogActionFrequencies * fd)
84 {
85   int ret;
86
87   switch (fd->tables_opts_order)
88     {
89     case FRQ_AVALUE:
90       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (fd->avalue), TRUE);
91       break;
92     case FRQ_DVALUE:
93       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (fd->dvalue), TRUE);
94       break;
95     case FRQ_ACOUNT:
96       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (fd->afreq), TRUE);
97       break;
98     case FRQ_DCOUNT:
99       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (fd->dfreq), TRUE);
100       break;
101     };
102
103   switch (fd->tables_opts_table)
104     {
105     case FRQ_TABLE:
106       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (fd->always), TRUE);
107       break;
108     case FRQ_NOTABLE:
109       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (fd->never), TRUE);
110       break;
111     case FRQ_LIMIT:
112       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (fd->limit), TRUE);
113       break;
114     }
115
116   gtk_spin_button_set_value (GTK_SPIN_BUTTON (fd->limit_spinbutton),
117                              fd->tables_opts_limit);
118
119   g_signal_emit_by_name (fd->limit, "toggled");
120
121   ret = psppire_dialog_run (PSPPIRE_DIALOG (fd->tables_dialog));
122
123   if (ret == PSPPIRE_RESPONSE_CONTINUE)
124     {
125       if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (fd->avalue)))
126         fd->tables_opts_order = FRQ_AVALUE;
127       else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (fd->dvalue)))
128         fd->tables_opts_order = FRQ_DVALUE;
129       else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (fd->afreq)))
130         fd->tables_opts_order = FRQ_ACOUNT;
131       else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (fd->dfreq)))
132         fd->tables_opts_order = FRQ_DCOUNT;
133
134       if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (fd->always)))
135         fd->tables_opts_table = FRQ_TABLE;
136       else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (fd->never)))
137         fd->tables_opts_table = FRQ_NOTABLE;
138       else
139         fd->tables_opts_table = FRQ_LIMIT;
140
141
142       fd->tables_opts_limit =
143         gtk_spin_button_get_value (GTK_SPIN_BUTTON (fd->limit_spinbutton));
144     }
145 }
146
147
148 static void
149 on_charts_clicked (PsppireDialogActionFrequencies *fd)
150 {
151   int ret;
152
153   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (fd->min), fd->charts_opts_use_min);
154   gtk_spin_button_set_value (GTK_SPIN_BUTTON (fd->min_spin), fd->charts_opts_min);
155   g_signal_emit_by_name (fd->min, "toggled");
156
157   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (fd->max), fd->charts_opts_use_max);
158   gtk_spin_button_set_value (GTK_SPIN_BUTTON (fd->max_spin), fd->charts_opts_max);
159   g_signal_emit_by_name (fd->max, "toggled");
160
161   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (fd->hist), fd->charts_opts_draw_hist);
162   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (fd->normal), fd->charts_opts_draw_normal);
163   g_signal_emit_by_name (fd->hist, "toggled");
164
165   switch (fd->charts_opts_scale)
166     {
167     case FRQ_FREQ:
168       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (fd->freqs), TRUE);
169       break;
170     case FRQ_DVALUE:
171       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (fd->percents), TRUE);
172       break;
173     };
174
175   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (fd->pie), fd->charts_opts_draw_pie);
176   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (fd->pie_include_missing),
177                                 fd->charts_opts_pie_include_missing);
178
179   g_signal_emit_by_name (fd->pie, "toggled");
180
181
182   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (fd->bar), fd->charts_opts_draw_bar);
183   g_signal_emit_by_name (fd->bar, "toggled");
184
185   ret = psppire_dialog_run (PSPPIRE_DIALOG (fd->charts_dialog));
186
187   if ( ret == PSPPIRE_RESPONSE_CONTINUE )
188     {
189       fd->charts_opts_use_min = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (fd->min));
190       fd->charts_opts_min = gtk_spin_button_get_value (GTK_SPIN_BUTTON (fd->min_spin));
191
192       fd->charts_opts_use_max = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (fd->max));
193       fd->charts_opts_max = gtk_spin_button_get_value (GTK_SPIN_BUTTON (fd->max_spin));
194
195       fd->charts_opts_draw_hist = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (fd->hist));
196       fd->charts_opts_draw_normal = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (fd->normal));
197       if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (fd->freqs)))
198         fd->charts_opts_scale = FRQ_FREQ;
199       else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (fd->percents)))
200         fd->charts_opts_scale = FRQ_PERCENT;
201
202       fd->charts_opts_draw_pie = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (fd->pie));
203       fd->charts_opts_pie_include_missing
204         = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (fd->pie_include_missing));
205
206
207       fd->charts_opts_draw_bar = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (fd->bar));
208     }
209 }
210
211
212 static void
213 psppire_dialog_action_frequencies_class_init
214 (PsppireDialogActionFrequenciesClass * class);
215
216 G_DEFINE_TYPE (PsppireDialogActionFrequencies,
217                psppire_dialog_action_frequencies, PSPPIRE_TYPE_DIALOG_ACTION);
218
219 static gboolean
220 dialog_state_valid (gpointer data)
221 {
222   PsppireDialogActionFrequencies *fd =
223     PSPPIRE_DIALOG_ACTION_FREQUENCIES (data);
224
225   GtkTreeModel *vars =
226     gtk_tree_view_get_model (GTK_TREE_VIEW (fd->stat_vars));
227
228   GtkTreeIter notused;
229
230   return gtk_tree_model_get_iter_first (vars, &notused);
231 }
232
233 static void
234 refresh (PsppireDialogAction * fdx)
235 {
236   PsppireDialogActionFrequencies *fd =
237     PSPPIRE_DIALOG_ACTION_FREQUENCIES (fdx);
238
239   GtkTreeIter iter;
240   size_t i;
241   bool ok;
242
243   GtkTreeModel *liststore =
244     gtk_tree_view_get_model (GTK_TREE_VIEW (fd->stat_vars));
245   gtk_list_store_clear (GTK_LIST_STORE (liststore));
246
247   for (i = 0, ok = gtk_tree_model_get_iter_first (fd->stats, &iter); ok;
248        i++, ok = gtk_tree_model_iter_next (fd->stats, &iter))
249     gtk_list_store_set (GTK_LIST_STORE (fd->stats), &iter,
250                         CHECKBOX_COLUMN_SELECTED,
251                         (B_FS_DEFAULT & (1u << i)) ? true : false, -1);
252 }
253
254
255
256 static void
257 psppire_dialog_action_frequencies_activate (PsppireDialogAction * a)
258 {
259   PsppireDialogActionFrequencies *act = PSPPIRE_DIALOG_ACTION_FREQUENCIES (a);
260   PsppireDialogAction *pda = PSPPIRE_DIALOG_ACTION (a);
261
262   GHashTable *thing = psppire_dialog_action_get_hash_table (pda);
263   GtkBuilder *xml = g_hash_table_lookup (thing, a);
264   if (!xml)
265     {
266       xml = builder_new ("frequencies.ui");
267       g_hash_table_insert (thing, a, xml);
268
269       GtkWidget *stats_treeview = get_widget_assert (xml, "stats-treeview");
270
271       psppire_checkbox_treeview_populate (PSPPIRE_CHECKBOX_TREEVIEW (stats_treeview),
272                                           B_FS_DEFAULT, N_FREQUENCY_STATS, stats);
273
274       act->stats = gtk_tree_view_get_model (GTK_TREE_VIEW (stats_treeview));
275
276       GtkWidget *tables_button = get_widget_assert (xml, "tables-button");
277       GtkWidget *charts_button = get_widget_assert (xml, "charts-button");
278
279       pda->dialog = get_widget_assert (xml, "frequencies-dialog");
280       pda->source = get_widget_assert (xml, "dict-treeview");
281
282       act->stat_vars = get_widget_assert (xml, "var-treeview");
283
284
285       act->include_missing = get_widget_assert (xml, "include_missing");
286
287
288       act->tables_dialog = get_widget_assert (xml, "tables-dialog");
289       act->charts_dialog = get_widget_assert (xml, "charts-dialog");
290       act->always = get_widget_assert (xml, "always");
291       act->never = get_widget_assert (xml, "never");
292       act->limit = get_widget_assert (xml, "limit");
293       act->limit_spinbutton = get_widget_assert (xml, "limit-spin");
294
295       g_signal_connect (act->limit, "toggled",
296                         G_CALLBACK (set_sensitivity_from_toggle),
297                         act->limit_spinbutton);
298
299       act->avalue = get_widget_assert (xml, "avalue");
300       act->dvalue = get_widget_assert (xml, "dvalue");
301       act->afreq = get_widget_assert (xml, "afreq");
302       act->dfreq = get_widget_assert (xml, "dfreq");
303
304       act->charts_opts_use_min = false;
305       act->charts_opts_min = 0;
306       act->charts_opts_use_max = false;
307       act->charts_opts_max = 100;
308       act->charts_opts_draw_hist = false;
309       act->charts_opts_draw_normal = false;
310       act->charts_opts_scale = FRQ_FREQ;
311       act->charts_opts_draw_pie = false;
312       act->charts_opts_draw_bar = false;
313       act->charts_opts_pie_include_missing = false;
314
315       act->freqs = get_widget_assert (xml, "freqs");
316       act->percents = get_widget_assert (xml, "percents");
317
318       act->min = get_widget_assert (xml, "min");
319       act->min_spin = get_widget_assert (xml, "min-spin");
320       g_signal_connect (act->min, "toggled",
321                         G_CALLBACK (set_sensitivity_from_toggle), act->min_spin);
322       act->max = get_widget_assert (xml, "max");
323       act->max_spin = get_widget_assert (xml, "max-spin");
324       g_signal_connect (act->max, "toggled",
325                         G_CALLBACK (set_sensitivity_from_toggle), act->max_spin);
326
327       act->hist = get_widget_assert (xml, "hist");
328       act->normal = get_widget_assert (xml, "normal");
329       g_signal_connect (act->hist, "toggled",
330                         G_CALLBACK (set_sensitivity_from_toggle), act->normal);
331
332       act->pie =  (get_widget_assert (xml, "pie"));
333       act->pie_include_missing = get_widget_assert (xml, "pie-include-missing");
334
335       act->bar =  (get_widget_assert (xml, "bar"));
336
337       act->tables_opts_order = FRQ_AVALUE;
338       act->tables_opts_table = FRQ_TABLE;
339       act->tables_opts_limit = 50;
340
341       g_signal_connect_swapped (tables_button, "clicked",
342                                 G_CALLBACK (on_tables_clicked),  act);
343
344       g_signal_connect_swapped (charts_button, "clicked",
345                                 G_CALLBACK (on_charts_clicked),  act);
346
347       psppire_dialog_action_set_refresh (pda, refresh);
348
349       psppire_dialog_action_set_valid_predicate (pda, dialog_state_valid);
350     }
351
352   
353   if (PSPPIRE_DIALOG_ACTION_CLASS (psppire_dialog_action_frequencies_parent_class)->activate)
354     PSPPIRE_DIALOG_ACTION_CLASS
355       (psppire_dialog_action_frequencies_parent_class)->activate (pda);
356 }
357
358 static char *
359 generate_syntax (PsppireDialogAction * a)
360 {
361   PsppireDialogActionFrequencies *fd = PSPPIRE_DIALOG_ACTION_FREQUENCIES (a);
362   gchar *text = NULL;
363   gint i;
364   gboolean ok;
365   GtkTreeIter iter;
366   guint selected = 0;
367   struct string str;
368
369   ds_init_cstr (&str, "FREQUENCIES");
370
371   ds_put_cstr (&str, "\n\t/VARIABLES=");
372   psppire_var_view_append_names_str (PSPPIRE_VAR_VIEW (fd->stat_vars), 0, &str);
373
374   ds_put_cstr (&str, "\n\t/FORMAT=");
375
376   switch (fd->tables_opts_order)
377     {
378     case FRQ_AVALUE:
379       ds_put_cstr (&str, "AVALUE");
380       break;
381     case FRQ_DVALUE:
382       ds_put_cstr (&str, "DVALUE");
383       break;
384     case FRQ_ACOUNT:
385       ds_put_cstr (&str, "AFREQ");
386       break;
387     case FRQ_DCOUNT:
388       ds_put_cstr (&str, "DFREQ");
389       break;
390     default:
391       g_assert_not_reached ();
392     }
393
394   ds_put_cstr (&str, " ");
395
396   switch (fd->tables_opts_table)
397     {
398     case FRQ_TABLE:
399       ds_put_cstr (&str, "TABLE");
400       break;
401     case FRQ_NOTABLE:
402       ds_put_cstr (&str, "NOTABLE");
403       break;
404     case FRQ_LIMIT:
405       ds_put_c_format (&str, "LIMIT (%d)", fd->tables_opts_limit);
406       break;
407     }
408
409
410   for (i = 0, ok = gtk_tree_model_get_iter_first (fd->stats, &iter); ok;
411        i++, ok = gtk_tree_model_iter_next (fd->stats, &iter))
412     {
413       gboolean toggled;
414       gtk_tree_model_get (fd->stats, &iter,
415                           CHECKBOX_COLUMN_SELECTED, &toggled, -1);
416       if (toggled)
417         selected |= 1u << i;
418     }
419
420   if (selected != B_FS_DEFAULT)
421     {
422       ds_put_cstr (&str, "\n\t/STATISTICS=");
423       if (selected == B_FS_ALL)
424         ds_put_cstr (&str, "ALL");
425       else if (selected == 0)
426         ds_put_cstr (&str, "NONE");
427       else
428         {
429           int n = 0;
430           if ((selected & B_FS_DEFAULT) == B_FS_DEFAULT)
431             {
432               ds_put_cstr (&str, "DEFAULT");
433               selected &= ~B_FS_DEFAULT;
434               n++;
435             }
436           for (i = 0; i < N_FREQUENCY_STATS; i++)
437             if (selected & (1u << i))
438               {
439                 if (n++)
440                   ds_put_cstr (&str, " ");
441                 ds_put_cstr (&str, stats[i].name);
442               }
443         }
444     }
445
446   if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (fd->include_missing)))
447     ds_put_cstr (&str, "\n\t/MISSING=INCLUDE");
448
449
450   if (fd->charts_opts_draw_hist)
451     {
452       ds_put_cstr (&str, "\n\t/HISTOGRAM=");
453       ds_put_cstr (&str,
454                    fd->charts_opts_draw_normal ? "NORMAL" : "NONORMAL");
455
456       if (fd->charts_opts_scale == FRQ_PERCENT)
457         ds_put_cstr (&str, " PERCENT");
458
459       if (fd->charts_opts_use_min)
460         ds_put_c_format (&str, " MIN(%.15g)", fd->charts_opts_min);
461       if (fd->charts_opts_use_max)
462         ds_put_c_format (&str, " MAX(%.15g)", fd->charts_opts_max);
463     }
464
465   if (fd->charts_opts_draw_pie)
466     {
467       ds_put_cstr (&str, "\n\t/PIECHART=");
468
469       if (fd->charts_opts_pie_include_missing)
470         ds_put_cstr (&str, " MISSING");
471
472       if (fd->charts_opts_use_min)
473         ds_put_c_format (&str, " MIN(%.15g)", fd->charts_opts_min);
474       if (fd->charts_opts_use_max)
475         ds_put_c_format (&str, " MAX(%.15g)", fd->charts_opts_max);
476     }
477
478
479   if (fd->charts_opts_draw_bar)
480     {
481       ds_put_cstr (&str, "\n\t/BARCHART=");
482
483       if (fd->charts_opts_scale == FRQ_PERCENT)
484         ds_put_cstr (&str, " PERCENT");
485
486       if (fd->charts_opts_use_min)
487         ds_put_c_format (&str, " MIN(%.15g)", fd->charts_opts_min);
488       if (fd->charts_opts_use_max)
489         ds_put_c_format (&str, " MAX(%.15g)", fd->charts_opts_max);
490     }
491
492
493   ds_put_cstr (&str, ".\n");
494
495   text = ds_steal_cstr (&str);
496
497   ds_destroy (&str);
498
499   return text;
500 }
501
502 static void
503 psppire_dialog_action_frequencies_class_init (PsppireDialogActionFrequenciesClass *class)
504 {
505   psppire_dialog_action_set_activation (class, psppire_dialog_action_frequencies_activate);
506   PSPPIRE_DIALOG_ACTION_CLASS (class)->generate_syntax = generate_syntax;
507 }
508
509
510 static void
511 psppire_dialog_action_frequencies_init (PsppireDialogActionFrequencies * act)
512 {
513 }