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