New module psppire-var-view
[pspp-builds.git] / src / ui / gui / t-test-independent-samples-dialog.c
1 /* PSPPIRE - a graphical user interface for PSPP.
2    Copyright (C) 2007, 2009  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
19 #include <config.h>
20 #include <gtk/gtk.h>
21 #include "t-test-independent-samples-dialog.h"
22 #include "psppire-dict.h"
23 #include "psppire-var-store.h"
24 #include "psppire-var-view.h"
25 #include "executor.h"
26 #include "psppire-data-window.h"
27 #include "psppire-dialog.h"
28 #include "dialog-common.h"
29 #include "dict-display.h"
30 #include "widget-io.h"
31 #include "t-test-options.h"
32 #include <ui/syntax-gen.h>
33
34 #include <language/syntax-string-source.h>
35 #include "helper.h"
36
37 #include <gl/xalloc.h>
38
39 #include <gettext.h>
40 #define _(msgid) gettext (msgid)
41 #define N_(msgid) msgid
42
43
44 enum group_definition
45   {
46     GROUPS_UNDEF,
47     GROUPS_VALUES,
48     GROUPS_CUT_POINT
49   };
50
51 struct tt_groups_dialog
52 {
53   GtkWidget *dialog;
54   GtkWidget *label;
55   GtkWidget *table1;
56   GtkWidget *table2;
57   GtkWidget *hbox1;
58
59   GtkWidget *values_toggle_button;
60   GtkWidget *cut_point_toggle_button;
61
62   GtkWidget *grp_entry[2];
63   GtkWidget *cut_point_entry;
64
65   enum group_definition group_defn;
66   gchar *val[2];
67 };
68
69 static void
70 set_group_criterion_type (GtkToggleButton *button,
71                           struct tt_groups_dialog *groups)
72 {
73   gboolean by_values = gtk_toggle_button_get_active (button);
74
75   gtk_widget_set_sensitive (groups->label, by_values);
76   gtk_widget_set_sensitive (groups->table2, by_values);
77
78   gtk_widget_set_sensitive (groups->hbox1, !by_values);
79 }
80
81 static void
82 tt_groups_dialog_destroy (struct tt_groups_dialog *grps)
83 {
84   g_object_unref (grps->table1);
85   g_object_unref (grps->table2);
86
87   g_free (grps->val[0]);
88   g_free (grps->val[1]);
89
90   g_free (grps);
91 }
92
93 static struct tt_groups_dialog *
94 tt_groups_dialog_create (GtkBuilder *xml, GtkWindow *parent)
95 {
96   struct tt_groups_dialog *grps = xmalloc (sizeof (*grps));
97
98   grps->group_defn = GROUPS_UNDEF;
99
100   grps->dialog = get_widget_assert (xml, "define-groups-dialog");
101   grps->table1 = get_widget_assert (xml, "table1");
102   grps->table2 = get_widget_assert (xml, "table2");
103   grps->label  = get_widget_assert (xml, "label4");
104   grps->hbox1  = get_widget_assert (xml, "hbox1");
105
106   grps->grp_entry[0] = get_widget_assert (xml, "group1-entry");
107   grps->grp_entry[1] = get_widget_assert (xml, "group2-entry");
108   grps->cut_point_entry = get_widget_assert (xml, "cut-point-entry");
109
110   grps->cut_point_toggle_button = get_widget_assert (xml, "radiobutton4");
111   grps->values_toggle_button = get_widget_assert (xml, "radiobutton3");
112
113   g_object_ref (grps->table1);
114   g_object_ref (grps->table2);
115
116   g_signal_connect (grps->values_toggle_button, "toggled",
117                     G_CALLBACK (set_group_criterion_type), grps);
118
119   gtk_window_set_transient_for (GTK_WINDOW (grps->dialog), parent);
120
121   grps->val[0] = xstrdup ("");
122   grps->val[1] = xstrdup ("");
123
124   return grps;
125 }
126
127
128 struct tt_indep_samples_dialog
129 {
130   GtkBuilder *xml;  /* The xml that generated the widgets */
131   GtkWidget *dialog;
132   PsppireDict *dict;
133   GtkWidget *define_groups_button;
134   GtkWidget *groups_entry;
135
136   struct tt_groups_dialog *grps;
137   struct tt_options_dialog *opts;
138 };
139
140
141 static void
142 set_define_groups_sensitivity (GtkEntry *entry,
143                                struct tt_indep_samples_dialog *tt_d)
144 {
145   const gchar *text = gtk_entry_get_text (entry);
146
147   const struct variable *v = psppire_dict_lookup_var (tt_d->dict, text);
148
149   gtk_widget_set_sensitive (tt_d->define_groups_button, v != NULL);
150 }
151
152
153 static gchar *
154 generate_syntax (const struct tt_indep_samples_dialog *d)
155 {
156   struct variable *group_variable;
157   gchar *text;
158
159   GtkWidget *tv =
160     get_widget_assert (d->xml, "indep-samples-t-test-treeview2");
161
162   GString *str = g_string_new ("T-TEST /VARIABLES=");
163
164   psppire_var_view_append_names (PSPPIRE_VAR_VIEW (tv), 0, str);
165
166   g_string_append (str, "\n\t/GROUPS=");
167
168   group_variable =
169     psppire_dict_lookup_var (d->dict,
170                              gtk_entry_get_text (GTK_ENTRY (d->groups_entry)));
171
172   g_string_append (str, var_get_name (group_variable));
173
174   if (d->grps->group_defn != GROUPS_UNDEF)
175     {
176       g_string_append (str, "(");
177
178       if ( var_is_alpha (group_variable))
179         {
180           struct string s = DS_EMPTY_INITIALIZER;
181           syntax_gen_string (&s, ss_cstr (d->grps->val[0]));
182           g_string_append (str, ds_cstr (&s));
183           ds_destroy (&s);
184         }
185       else
186         {
187           g_string_append (str, d->grps->val[0]);
188         }
189
190       if ( d->grps->group_defn == GROUPS_VALUES )
191         {
192           g_string_append (str, ",");
193
194           if ( var_is_alpha (group_variable))
195             {
196               struct string s = DS_EMPTY_INITIALIZER;
197               syntax_gen_string (&s, ss_cstr (d->grps->val[1]));
198               g_string_append (str, ds_cstr (&s));
199               ds_destroy (&s);
200             }
201           else
202             {
203               g_string_append (str, d->grps->val[1]);
204             }
205         }
206
207       g_string_append (str, ")");
208     }
209
210   tt_options_dialog_append_syntax (d->opts, str);
211
212   g_string_append (str, ".\n");
213
214   text = str->str;
215
216   g_string_free (str, FALSE);
217
218   return text;
219 }
220
221 static void
222 refresh (struct tt_indep_samples_dialog *ttd)
223 {
224   GtkWidget *tv =
225     get_widget_assert (ttd->xml, "indep-samples-t-test-treeview2");
226
227   GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (tv));
228
229   gtk_entry_set_text (GTK_ENTRY (ttd->groups_entry), "");
230
231   gtk_list_store_clear (GTK_LIST_STORE (model));
232
233   gtk_widget_set_sensitive (ttd->define_groups_button, FALSE);
234 }
235
236
237 /* Returns TRUE iff the define groups subdialog has a
238    state which defines a valid group criterion */
239 static gboolean
240 define_groups_state_valid (gpointer data)
241 {
242   struct tt_groups_dialog *d = data;
243
244   if ( gtk_toggle_button_get_active
245        (GTK_TOGGLE_BUTTON (d->values_toggle_button)))
246     {
247       if ( 0 == strcmp ("", gtk_entry_get_text (GTK_ENTRY (d->grp_entry[0]))))
248         return FALSE;
249
250       if ( 0 == strcmp ("", gtk_entry_get_text (GTK_ENTRY (d->grp_entry[1]))))
251         return FALSE;
252     }
253   else
254     {
255       if ( 0 == strcmp ("",
256                         gtk_entry_get_text (GTK_ENTRY (d->cut_point_entry))))
257         return FALSE;
258     }
259
260   return TRUE;
261 }
262
263 static void
264 run_define_groups (struct tt_indep_samples_dialog *ttd)
265 {
266   struct tt_groups_dialog *grps = ttd->grps;
267
268   gint response;
269
270   GtkWidget *box = get_widget_assert (ttd->xml, "dialog-hbox2");
271
272   const gchar *text = gtk_entry_get_text (GTK_ENTRY (ttd->groups_entry));
273
274   const struct variable *v = psppire_dict_lookup_var (ttd->dict, text);
275
276   if ( grps->table2->parent)
277     gtk_container_remove (GTK_CONTAINER (grps->table2->parent), grps->table2);
278
279   if ( grps->table1->parent)
280     gtk_container_remove (GTK_CONTAINER (grps->table1->parent), grps->table1);
281
282
283   if ( var_is_numeric (v))
284     {
285       gtk_table_attach_defaults (GTK_TABLE (grps->table1), grps->table2,
286                                  1, 2, 1, 2);
287
288       gtk_container_add (GTK_CONTAINER (box), grps->table1);
289     }
290   else
291     {
292       gtk_container_add (GTK_CONTAINER (box), grps->table2);
293       grps->group_defn = GROUPS_VALUES;
294     }
295
296
297   psppire_dialog_set_valid_predicate (PSPPIRE_DIALOG (grps->dialog),
298                                       define_groups_state_valid, grps);
299
300   if ( grps->group_defn != GROUPS_CUT_POINT )
301     {
302       gtk_toggle_button_set_active
303         (GTK_TOGGLE_BUTTON (grps->cut_point_toggle_button), TRUE);
304
305       gtk_toggle_button_set_active
306         (GTK_TOGGLE_BUTTON (grps->values_toggle_button), TRUE);
307
308       gtk_entry_set_text (GTK_ENTRY (grps->grp_entry[0]), grps->val[0]);
309       gtk_entry_set_text (GTK_ENTRY (grps->grp_entry[1]), grps->val[1]);
310
311       gtk_entry_set_text (GTK_ENTRY (grps->cut_point_entry), "");
312     }
313   else
314     {
315       gtk_toggle_button_set_active
316         (GTK_TOGGLE_BUTTON (grps->values_toggle_button), TRUE);
317
318       gtk_toggle_button_set_active
319         (GTK_TOGGLE_BUTTON (grps->cut_point_toggle_button), TRUE);
320
321       gtk_entry_set_text (GTK_ENTRY (grps->grp_entry[0]), "");
322       gtk_entry_set_text (GTK_ENTRY (grps->grp_entry[1]), "");
323
324       gtk_entry_set_text (GTK_ENTRY (grps->cut_point_entry), grps->val[0]);
325     }
326
327   g_signal_emit_by_name (grps->grp_entry[0], "changed");
328   g_signal_emit_by_name (grps->grp_entry[1], "changed");
329   g_signal_emit_by_name (grps->cut_point_entry, "changed");
330
331   response = psppire_dialog_run (PSPPIRE_DIALOG (grps->dialog));
332
333   if (response == PSPPIRE_RESPONSE_CONTINUE)
334     {
335       g_free (grps->val[0]);
336       g_free (grps->val[1]);
337
338       if (gtk_toggle_button_get_active
339           (GTK_TOGGLE_BUTTON (grps->values_toggle_button)))
340         {
341           grps->group_defn = GROUPS_VALUES;
342
343           grps->val[0] =
344             xstrdup (gtk_entry_get_text (GTK_ENTRY (grps->grp_entry[0])));
345
346           grps->val[1] =
347             xstrdup (gtk_entry_get_text (GTK_ENTRY (grps->grp_entry[1])));
348         }
349       else
350         {
351           grps->group_defn = GROUPS_CUT_POINT;
352
353           grps->val[1] = NULL;
354
355           grps->val[0] =
356             xstrdup (gtk_entry_get_text (GTK_ENTRY (grps->cut_point_entry)));
357         }
358
359       psppire_dialog_notify_change (PSPPIRE_DIALOG (ttd->dialog));
360     }
361 }
362
363
364
365 static gboolean
366 dialog_state_valid (gpointer data)
367 {
368   struct tt_indep_samples_dialog *tt_d = data;
369
370   GtkWidget *tv_vars =
371     get_widget_assert (tt_d->xml, "indep-samples-t-test-treeview2");
372
373   GtkTreeModel *vars = gtk_tree_view_get_model (GTK_TREE_VIEW (tv_vars));
374
375   GtkTreeIter notused;
376
377   if ( 0 == strcmp ("", gtk_entry_get_text (GTK_ENTRY (tt_d->groups_entry))))
378     return FALSE;
379
380   if ( 0 == gtk_tree_model_get_iter_first (vars, &notused))
381     return FALSE;
382
383   if ( tt_d->grps->group_defn == GROUPS_UNDEF)
384     return FALSE;
385
386   return TRUE;
387 }
388
389 /* Pops up the dialog box */
390 void
391 t_test_independent_samples_dialog (GObject *o, gpointer data)
392 {
393   struct tt_indep_samples_dialog tt_d;
394   gint response;
395   PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (data);
396
397   PsppireVarStore *vs = NULL;
398
399   GtkBuilder *xml = builder_new ("t-test.ui");
400
401   GtkWidget *dict_view =
402     get_widget_assert (xml, "indep-samples-t-test-treeview1");
403
404   GtkWidget *selector2 =
405     get_widget_assert (xml, "indep-samples-t-test-selector2");
406
407   GtkWidget *selector1 =
408     get_widget_assert (xml, "indep-samples-t-test-selector1");
409
410   GtkWidget *options_button =
411     get_widget_assert (xml, "indep-samples-t-test-options-button");
412
413   g_object_get (de->data_editor, "var-store", &vs, NULL);
414
415   tt_d.dialog = get_widget_assert (xml, "t-test-independent-samples-dialog");
416   tt_d.xml = xml;
417   g_object_get (vs, "dictionary", &tt_d.dict, NULL);
418
419   tt_d.define_groups_button = get_widget_assert (xml, "define-groups-button");
420   tt_d.groups_entry = get_widget_assert (xml, "indep-samples-t-test-entry");
421   tt_d.opts = tt_options_dialog_create (xml, GTK_WINDOW (de));
422   tt_d.grps = tt_groups_dialog_create (xml, GTK_WINDOW (de));
423
424
425   gtk_window_set_transient_for (GTK_WINDOW (tt_d.dialog), GTK_WINDOW (de));
426
427   g_object_set (dict_view, "model", tt_d.dict, NULL);
428
429   psppire_selector_set_allow (PSPPIRE_SELECTOR (selector1),
430                               numeric_only);
431
432
433   psppire_selector_set_filter_func (PSPPIRE_SELECTOR (selector2),
434                                  is_currently_in_entry);
435
436   g_signal_connect_swapped (tt_d.define_groups_button, "clicked",
437                             G_CALLBACK (run_define_groups), &tt_d);
438
439
440   g_signal_connect_swapped (options_button, "clicked",
441                             G_CALLBACK (tt_options_dialog_run), tt_d.opts);
442
443
444   g_signal_connect_swapped (tt_d.dialog, "refresh", G_CALLBACK (refresh),
445                             &tt_d);
446
447   g_signal_connect (tt_d.groups_entry, "changed",
448                     G_CALLBACK (set_define_groups_sensitivity), &tt_d);
449
450
451   psppire_dialog_set_valid_predicate (PSPPIRE_DIALOG (tt_d.dialog),
452                                       dialog_state_valid, &tt_d);
453
454   response = psppire_dialog_run (PSPPIRE_DIALOG (tt_d.dialog));
455
456   switch (response)
457     {
458     case GTK_RESPONSE_OK:
459       {
460         gchar *syntax = generate_syntax (&tt_d);
461
462         struct getl_interface *sss = create_syntax_string_source (syntax);
463         execute_syntax (sss);
464
465         g_free (syntax);
466       }
467       break;
468     case PSPPIRE_RESPONSE_PASTE:
469       {
470         gchar *syntax = generate_syntax (&tt_d);
471         paste_syntax_in_new_window (syntax);
472         g_free (syntax);
473       }
474       break;
475     default:
476       break;
477     }
478
479   tt_options_dialog_destroy (tt_d.opts);
480   tt_groups_dialog_destroy (tt_d.grps);
481
482   g_object_unref (xml);
483 }
484
485