Fix leak in dialogs, and correct name of options button.
[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 (GTK_WINDOW (grps->dialog), parent);
121
122   grps->val[0] = strdup ("");
123   grps->val[1] = strdup ("");
124
125   return grps;
126 }
127
128
129 struct tt_indep_samples_dialog
130 {
131   GladeXML *xml;  /* The xml that generated the widgets */
132   GtkWidget *dialog;
133   PsppireDict *dict;
134   GtkWidget *define_groups_button;
135   GtkWidget *groups_entry;
136
137   struct tt_groups_dialog *grps;
138   struct tt_options_dialog *opts;
139 };
140
141
142 static void
143 set_define_groups_sensitivity (GtkEntry *entry,
144                                struct tt_indep_samples_dialog *tt_d)
145 {
146   const gchar *text = gtk_entry_get_text (entry);
147
148   const struct variable *v = psppire_dict_lookup_var (tt_d->dict, text);
149
150   gtk_widget_set_sensitive (tt_d->define_groups_button, v != NULL);
151 }
152
153
154 static gchar *
155 generate_syntax (const struct tt_indep_samples_dialog *d)
156 {
157   struct variable *group_variable;
158   gchar *text;
159
160   GtkWidget *tv =
161     get_widget_assert (d->xml, "indep-samples-t-test-treeview2");
162
163   GString *str = g_string_new ("T-TEST /VARIABLES=");
164
165   append_variable_names (str, d->dict, GTK_TREE_VIEW (tv), 0);
166
167   g_string_append (str, "\n\t/GROUPS=");
168
169   group_variable =
170     psppire_dict_lookup_var (d->dict,
171                              gtk_entry_get_text (GTK_ENTRY (d->groups_entry)));
172
173   g_string_append (str, var_get_name (group_variable));
174
175   if (d->grps->group_defn != GROUPS_UNDEF)
176     {
177       g_string_append (str, "(");
178
179       if ( var_is_alpha (group_variable))
180         {
181           struct string s;
182           ds_init_cstr (&s, d->grps->val[0]);
183           gen_quoted_string (&s);
184           g_string_append (str, ds_cstr (&s));
185           ds_destroy (&s);
186         }
187       else
188         {
189           g_string_append (str, d->grps->val[0]);
190         }
191
192       if ( d->grps->group_defn == GROUPS_VALUES )
193         {
194           g_string_append (str, ",");
195
196           if ( var_is_alpha (group_variable))
197             {
198               struct string s;
199               ds_init_cstr (&s, d->grps->val[1]);
200               gen_quoted_string (&s);
201               g_string_append (str, ds_cstr (&s));
202               ds_destroy (&s);
203             }
204           else
205             {
206               g_string_append (str, d->grps->val[1]);
207             }
208         }
209
210       g_string_append (str, ")");
211     }
212
213   tt_options_dialog_append_syntax (d->opts, str);
214
215   g_string_append (str, ".\n");
216
217   text = str->str;
218
219   g_string_free (str, FALSE);
220
221   return text;
222 }
223
224 static void
225 refresh (struct tt_indep_samples_dialog *ttd)
226 {
227   GtkWidget *tv =
228     get_widget_assert (ttd->xml, "indep-samples-t-test-treeview2");
229
230   GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (tv));
231
232   gtk_entry_set_text (GTK_ENTRY (ttd->groups_entry), "");
233
234   gtk_list_store_clear (GTK_LIST_STORE (model));
235
236   gtk_widget_set_sensitive (ttd->define_groups_button, FALSE);
237 }
238
239
240 /* Returns TRUE iff the define groups subdialog has a
241    state which defines a valid group criterion */
242 static gboolean
243 define_groups_state_valid (gpointer data)
244 {
245   struct tt_groups_dialog *d = data;
246
247   if ( gtk_toggle_button_get_active
248        (GTK_TOGGLE_BUTTON (d->values_toggle_button)))
249     {
250       if ( 0 == strcmp ("", gtk_entry_get_text (GTK_ENTRY (d->grp_entry[0]))))
251         return FALSE;
252
253       if ( 0 == strcmp ("", gtk_entry_get_text (GTK_ENTRY (d->grp_entry[1]))))
254         return FALSE;
255     }
256   else
257     {
258       if ( 0 == strcmp ("",
259                         gtk_entry_get_text (GTK_ENTRY (d->cut_point_entry))))
260         return FALSE;
261     }
262
263   return TRUE;
264 }
265
266 static void
267 run_define_groups (struct tt_indep_samples_dialog *ttd)
268 {
269   struct tt_groups_dialog *grps = ttd->grps;
270
271   gint response;
272
273   GtkWidget *box = get_widget_assert (ttd->xml, "dialog-hbox2");
274
275   const gchar *text = gtk_entry_get_text (GTK_ENTRY (ttd->groups_entry));
276
277   const struct variable *v = psppire_dict_lookup_var (ttd->dict, text);
278
279   if ( grps->table2->parent)
280     gtk_container_remove (GTK_CONTAINER (grps->table2->parent), grps->table2);
281
282   if ( grps->table1->parent)
283     gtk_container_remove (GTK_CONTAINER (grps->table1->parent), grps->table1);
284
285
286   if ( var_is_numeric (v))
287     {
288       gtk_table_attach_defaults (GTK_TABLE (grps->table1), grps->table2,
289                                  1, 2, 1, 2);
290
291       gtk_container_add (GTK_CONTAINER (box), grps->table1);
292     }
293   else
294     {
295       gtk_container_add (GTK_CONTAINER (box), grps->table2);
296       grps->group_defn = GROUPS_VALUES;
297     }
298
299
300   psppire_dialog_set_valid_predicate (PSPPIRE_DIALOG (grps->dialog),
301                                       define_groups_state_valid, grps);
302
303   if ( grps->group_defn != GROUPS_CUT_POINT )
304     {
305       gtk_toggle_button_set_active
306         (GTK_TOGGLE_BUTTON (grps->cut_point_toggle_button), TRUE);
307
308       gtk_toggle_button_set_active
309         (GTK_TOGGLE_BUTTON (grps->values_toggle_button), TRUE);
310
311       gtk_entry_set_text (GTK_ENTRY (grps->grp_entry[0]), grps->val[0]);
312       gtk_entry_set_text (GTK_ENTRY (grps->grp_entry[1]), grps->val[1]);
313
314       gtk_entry_set_text (GTK_ENTRY (grps->cut_point_entry), "");
315     }
316   else
317     {
318       gtk_toggle_button_set_active
319         (GTK_TOGGLE_BUTTON (grps->values_toggle_button), TRUE);
320
321       gtk_toggle_button_set_active
322         (GTK_TOGGLE_BUTTON (grps->cut_point_toggle_button), TRUE);
323
324       gtk_entry_set_text (GTK_ENTRY (grps->grp_entry[0]), "");
325       gtk_entry_set_text (GTK_ENTRY (grps->grp_entry[1]), "");
326
327       gtk_entry_set_text (GTK_ENTRY (grps->cut_point_entry), grps->val[0]);
328     }
329
330   g_signal_emit_by_name (grps->grp_entry[0], "changed");
331   g_signal_emit_by_name (grps->grp_entry[1], "changed");
332   g_signal_emit_by_name (grps->cut_point_entry, "changed");
333
334   response = psppire_dialog_run (PSPPIRE_DIALOG (grps->dialog));
335
336   if (response == PSPPIRE_RESPONSE_CONTINUE)
337     {
338       g_free (grps->val[0]);
339       g_free (grps->val[1]);
340
341       if (gtk_toggle_button_get_active
342           (GTK_TOGGLE_BUTTON (grps->values_toggle_button)))
343         {
344           grps->group_defn = GROUPS_VALUES;
345
346           grps->val[0] =
347             strdup (gtk_entry_get_text (GTK_ENTRY (grps->grp_entry[0])));
348
349           grps->val[1] =
350             strdup (gtk_entry_get_text (GTK_ENTRY (grps->grp_entry[1])));
351         }
352       else
353         {
354           grps->group_defn = GROUPS_CUT_POINT;
355
356           grps->val[1] = NULL;
357
358           grps->val[0] =
359             strdup (gtk_entry_get_text (GTK_ENTRY (grps->cut_point_entry)));
360         }
361
362       psppire_dialog_notify_change (PSPPIRE_DIALOG (ttd->dialog));
363     }
364 }
365
366
367
368 static gboolean
369 dialog_state_valid (gpointer data)
370 {
371   struct tt_indep_samples_dialog *tt_d = data;
372
373   GtkWidget *tv_vars =
374     get_widget_assert (tt_d->xml, "indep-samples-t-test-treeview2");
375
376   GtkTreeModel *vars = gtk_tree_view_get_model (GTK_TREE_VIEW (tv_vars));
377
378   GtkTreeIter notused;
379
380   if ( 0 == strcmp ("", gtk_entry_get_text (GTK_ENTRY (tt_d->groups_entry))))
381     return FALSE;
382
383   if ( 0 == gtk_tree_model_get_iter_first (vars, &notused))
384     return FALSE;
385
386   if ( tt_d->grps->group_defn == GROUPS_UNDEF)
387     return FALSE;
388
389   return TRUE;
390 }
391
392 /* Pops up the dialog box */
393 void
394 t_test_independent_samples_dialog (GObject *o, gpointer data)
395 {
396   struct tt_indep_samples_dialog tt_d;
397   gint response;
398   struct data_editor *de = data;
399
400   PsppireVarStore *vs;
401
402   GladeXML *xml = XML_NEW ("t-test.glade");
403
404   GtkSheet *var_sheet =
405     GTK_SHEET (get_widget_assert (de->xml, "variable_sheet"));
406
407   GtkWidget *dict_view =
408     get_widget_assert (xml, "indep-samples-t-test-treeview1");
409
410   GtkWidget *test_variables_treeview =
411     get_widget_assert (xml, "indep-samples-t-test-treeview2");
412
413   GtkWidget *selector2 =
414     get_widget_assert (xml, "indep-samples-t-test-selector2");
415
416   GtkWidget *selector1 =
417     get_widget_assert (xml, "indep-samples-t-test-selector1");
418
419   GtkWidget *options_button =
420     get_widget_assert (xml, "indep-samples-t-test-options-button");
421
422   vs = PSPPIRE_VAR_STORE (gtk_sheet_get_model (var_sheet));
423
424   tt_d.dialog = get_widget_assert (xml, "t-test-independent-samples-dialog");
425   tt_d.xml = xml;
426   tt_d.dict = vs->dict;
427
428   tt_d.define_groups_button = get_widget_assert (xml, "define-groups-button");
429   tt_d.groups_entry = get_widget_assert (xml, "indep-samples-t-test-entry");
430   tt_d.opts = tt_options_dialog_create (xml, de->parent.window);
431   tt_d.grps = tt_groups_dialog_create (xml, de->parent.window);
432
433
434   gtk_window_set_transient_for (GTK_WINDOW (tt_d.dialog), de->parent.window);
435
436   attach_dictionary_to_treeview (GTK_TREE_VIEW (dict_view),
437                                  vs->dict,
438                                  GTK_SELECTION_MULTIPLE, NULL);
439
440   set_dest_model (GTK_TREE_VIEW (test_variables_treeview), vs->dict);
441
442
443   psppire_selector_set_subjects (PSPPIRE_SELECTOR (selector1),
444                                  dict_view, test_variables_treeview,
445                                  insert_source_row_into_tree_view,
446                                  NULL,
447                                  NULL);
448
449   psppire_selector_set_allow (PSPPIRE_SELECTOR (selector1),
450                               numeric_only);
451
452
453   psppire_selector_set_subjects (PSPPIRE_SELECTOR (selector2),
454                                  dict_view, tt_d.groups_entry,
455                                  insert_source_row_into_entry,
456                                  is_currently_in_entry,
457                                  NULL);
458
459   g_signal_connect_swapped (tt_d.define_groups_button, "clicked",
460                             G_CALLBACK (run_define_groups), &tt_d);
461
462
463   g_signal_connect_swapped (options_button, "clicked",
464                             G_CALLBACK (tt_options_dialog_run), tt_d.opts);
465
466
467   g_signal_connect_swapped (tt_d.dialog, "refresh", G_CALLBACK (refresh),
468                             &tt_d);
469
470   g_signal_connect (tt_d.groups_entry, "changed",
471                     G_CALLBACK (set_define_groups_sensitivity), &tt_d);
472
473
474   psppire_dialog_set_valid_predicate (PSPPIRE_DIALOG (tt_d.dialog),
475                                       dialog_state_valid, &tt_d);
476
477   response = psppire_dialog_run (PSPPIRE_DIALOG (tt_d.dialog));
478
479   switch (response)
480     {
481     case GTK_RESPONSE_OK:
482       {
483         gchar *syntax = generate_syntax (&tt_d);
484         struct getl_interface *sss = create_syntax_string_source (syntax);
485         execute_syntax (sss);
486
487         g_free (syntax);
488       }
489       break;
490     case PSPPIRE_RESPONSE_PASTE:
491       {
492         gchar *syntax = generate_syntax (&tt_d);
493
494         struct syntax_editor *se =
495           (struct syntax_editor *) window_create (WINDOW_SYNTAX, NULL);
496
497         gtk_text_buffer_insert_at_cursor (se->buffer, syntax, -1);
498
499         g_free (syntax);
500       }
501       break;
502     default:
503       break;
504     }
505
506   tt_options_dialog_destroy (tt_d.opts);
507   tt_groups_dialog_destroy (tt_d.grps);
508
509   g_object_unref (xml);
510 }
511
512