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