Implemented the paired samples t test dialog. Closes patch #6378
[pspp-builds.git] / src / ui / gui / select-cases-dialog.c
1 /*
2     PSPPIRE --- A Graphical User Interface for PSPP
3     Copyright (C) 2007  Free Software Foundation
4
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14
15     You should have received a copy of the GNU General Public License
16     along with this program; if not, write to the Free Software
17     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18     02110-1301, USA. */
19
20 #include <config.h>
21
22 #include "select-cases-dialog.h"
23 #include <gtk/gtk.h>
24 #include <glade/glade.h>
25 #include "helper.h"
26 #include "psppire-dialog.h"
27 #include "data-editor.h"
28 #include "dialog-common.h"
29 #include "dict-display.h"
30 #include "widget-io.h"
31 #include <language/syntax-string-source.h>
32 #include "syntax-editor.h"
33
34
35 #include <gettext.h>
36 #define _(msgid) gettext (msgid)
37 #define N_(msgid) msgid
38
39
40
41 /* FIXME: These shouldn't be here */
42 #include <gtksheet/gtksheet.h>
43 #include "psppire-data-store.h"
44
45
46 struct select_cases_dialog
47 {
48   /* The XML that created the dialog */
49   GladeXML *xml;
50
51   GtkWidget *spinbutton ;
52   GtkWidget *spinbutton1 ;
53   GtkWidget *spinbutton2 ;
54
55   GtkWidget *hbox1;
56   GtkWidget *hbox2;
57
58   PsppireDataStore *data_store;
59 };
60
61 static gchar * generate_syntax (const struct select_cases_dialog *scd);
62
63
64 static void
65 set_sensitivity_from_toggle (GtkToggleButton *togglebutton,  gpointer data)
66 {
67   GtkWidget *w = data;
68   gboolean active = gtk_toggle_button_get_active (togglebutton);
69
70   gtk_widget_set_sensitive (w, active);
71 }
72
73 static void
74 set_sensitivity_from_toggle_invert (GtkToggleButton *togglebutton,
75                                     gpointer data)
76 {
77   GtkWidget *w = data;
78   gboolean active = gtk_toggle_button_get_active (togglebutton);
79
80   gtk_widget_set_sensitive (w, !active);
81 }
82
83
84
85 static const gchar label1[]=N_("Approximately %3d%% of all cases.");
86 static const gchar label2[]=N_("Exactly %3d cases from the first %3d cases.");
87
88
89 static void
90 sample_subdialog (GtkButton *b, gpointer data)
91 {
92   gint response;
93   struct select_cases_dialog *scd = data;
94
95   gint case_count = psppire_data_store_get_case_count (scd->data_store);
96
97   GtkWidget *parent_dialog = get_widget_assert (scd->xml,
98                                                 "select-cases-dialog");
99   GtkWidget *dialog = get_widget_assert (scd->xml,
100                                          "select-cases-random-sample-dialog");
101   GtkWidget *percent = get_widget_assert (scd->xml,
102                                           "radiobutton-sample-percent");
103   GtkWidget *sample_n_cases = get_widget_assert (scd->xml,
104                                                  "radiobutton-sample-n-cases");
105   GtkWidget *table = get_widget_assert (scd->xml,
106                                         "select-cases-random-sample-table");
107
108   if ( ! scd->hbox1 )
109     {
110       scd->hbox1 = widget_scanf (gettext (label1), &scd->spinbutton);
111
112       gtk_widget_show (scd->hbox1);
113
114       gtk_table_attach_defaults (GTK_TABLE (table),
115                                  scd->hbox1, 1, 2, 0, 1);
116
117       g_signal_connect (G_OBJECT (percent), "toggled",
118                         G_CALLBACK (set_sensitivity_from_toggle), scd->hbox1);
119
120       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (percent), TRUE);
121     }
122
123
124   if ( ! scd->hbox2 )
125     {
126       scd->hbox2 =
127         widget_scanf (gettext (label2), &scd->spinbutton1, &scd->spinbutton2);
128
129       gtk_spin_button_set_range (GTK_SPIN_BUTTON (scd->spinbutton1),
130                                  1, case_count);
131
132       gtk_spin_button_set_range (GTK_SPIN_BUTTON (scd->spinbutton2),
133                                  1, case_count);
134
135       gtk_widget_show (scd->hbox2);
136       gtk_widget_set_sensitive (scd->hbox2, FALSE);
137
138       gtk_table_attach_defaults (GTK_TABLE (table),
139                                  scd->hbox2, 1, 2, 1, 2);
140
141       g_signal_connect (G_OBJECT (sample_n_cases), "toggled",
142                         G_CALLBACK (set_sensitivity_from_toggle), scd->hbox2);
143
144       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (sample_n_cases), FALSE);
145     }
146
147
148   gtk_window_set_transient_for (GTK_WINDOW (dialog),
149                                 GTK_WINDOW (parent_dialog));
150
151   response = psppire_dialog_run (PSPPIRE_DIALOG (dialog));
152
153   if ( response != PSPPIRE_RESPONSE_CONTINUE)
154     {
155       g_signal_handlers_disconnect_by_func
156         (G_OBJECT (percent),
157          G_CALLBACK (set_sensitivity_from_toggle),
158          scd->hbox1);
159
160       g_signal_handlers_disconnect_by_func
161         (G_OBJECT (sample_n_cases),
162          G_CALLBACK (set_sensitivity_from_toggle),
163          scd->hbox2);
164
165       gtk_widget_destroy(scd->hbox1);
166       gtk_widget_destroy(scd->hbox2);
167       scd->hbox1 = scd->hbox2 = NULL;
168     }
169   else
170     {
171       gchar *text;
172       GtkWidget *l0 = get_widget_assert (scd->xml, "random-sample-label");
173
174       if ( gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (percent)))
175         {
176           text = widget_printf (gettext(label1), scd->spinbutton);
177           gtk_label_set_text (GTK_LABEL (l0), text);
178         }
179       else
180         {
181           text =
182             widget_printf (gettext(label2), scd->spinbutton1, scd->spinbutton2);
183           gtk_label_set_text (GTK_LABEL (l0), text);
184
185         }
186       g_free (text);
187
188     }
189 }
190
191 static void
192 range_subdialog (GtkButton *b, gpointer data)
193 {
194   gint response;
195   struct select_cases_dialog *scd = data;
196
197   gint n_cases = psppire_data_store_get_case_count (scd->data_store);
198
199   GtkWidget *parent_dialog = get_widget_assert (scd->xml,
200                                                 "select-cases-dialog");
201
202   GtkWidget *dialog = get_widget_assert (scd->xml,
203                                          "select-cases-range-dialog");
204
205   GtkWidget *first = get_widget_assert (scd->xml,
206                                         "range-dialog-first");
207
208   GtkWidget *last = get_widget_assert (scd->xml,
209                                         "range-dialog-last");
210
211
212   gtk_spin_button_set_range (GTK_SPIN_BUTTON (last), 1, n_cases);
213
214   gtk_spin_button_set_range (GTK_SPIN_BUTTON (first), 1, n_cases);
215
216   gtk_window_set_transient_for (GTK_WINDOW (dialog),
217                                 GTK_WINDOW (parent_dialog));
218
219
220   response = psppire_dialog_run (PSPPIRE_DIALOG (dialog));
221   if ( response == PSPPIRE_RESPONSE_CONTINUE)
222     {
223       GtkWidget *first = get_widget_assert (scd->xml, "range-dialog-first");
224       GtkWidget *last = get_widget_assert (scd->xml, "range-dialog-last");
225       GtkWidget *l1 = get_widget_assert (scd->xml, "range-sample-label");
226       gchar *text = widget_printf (_("%d thru %d"), first, last);
227
228       gtk_label_set_text (GTK_LABEL (l1), text);
229
230       g_free (text);
231     }
232 }
233
234 static void
235 set_radiobutton (GtkWidget *button, gpointer data)
236 {
237   GtkToggleButton *toggle = data;
238   gtk_toggle_button_set_active (toggle, TRUE);
239 }
240
241 /* Pops up the Select Cases dialog box */
242 void
243 select_cases_dialog (GObject *o, gpointer data)
244 {
245   gint response;
246   struct select_cases_dialog scd = {0,0,0,0,0,0};
247   GtkWidget *dialog   ;
248   struct data_editor *de = data;
249   GtkWidget *entry = NULL;
250   GtkWidget *selector ;
251   GtkWidget *button_range;
252   GtkWidget *button_sample;
253
254   scd.xml = XML_NEW ("psppire.glade");
255
256   {
257     GtkSheet *data_sheet =
258       GTK_SHEET (get_widget_assert (de->xml, "data_sheet"));
259
260     scd.data_store = PSPPIRE_DATA_STORE (gtk_sheet_get_model (data_sheet));
261   }
262
263   button_range = get_widget_assert (scd.xml, "button-range");
264   button_sample = get_widget_assert (scd.xml, "button-sample");
265   entry = get_widget_assert (scd.xml, "filter-variable-entry");
266   selector = get_widget_assert (scd.xml, "psppire-selector-filter");
267
268   {
269     GtkWidget *button_if =
270       get_widget_assert (scd.xml, "button-if");
271
272     GtkWidget *radiobutton_if =
273       get_widget_assert (scd.xml, "radiobutton-if");
274
275     GtkWidget *radiobutton_all =
276       get_widget_assert (scd.xml, "radiobutton-all");
277
278     GtkWidget *radiobutton_sample =
279       get_widget_assert (scd.xml, "radiobutton-sample");
280
281     GtkWidget *radiobutton_range =
282       get_widget_assert (scd.xml, "radiobutton-range");
283
284     GtkWidget *radiobutton_filter =
285       get_widget_assert (scd.xml, "radiobutton-filter-variable");
286
287     GtkWidget *range_label =
288       get_widget_assert (scd.xml, "range-sample-label");
289
290     GtkWidget *sample_label =
291       get_widget_assert (scd.xml, "random-sample-label");
292
293     g_signal_connect (radiobutton_all, "toggled",
294                       G_CALLBACK (set_sensitivity_from_toggle_invert),
295                       get_widget_assert (scd.xml, "filter-delete-button-box")
296                       );
297
298     g_signal_connect (button_if, "clicked",
299                       G_CALLBACK (set_radiobutton), radiobutton_if);
300
301     g_signal_connect (button_sample, "clicked",
302                       G_CALLBACK (set_radiobutton), radiobutton_sample);
303
304     g_signal_connect (button_range,  "clicked",
305                       G_CALLBACK (set_radiobutton), radiobutton_range);
306
307     g_signal_connect (selector, "clicked",
308                       G_CALLBACK (set_radiobutton), radiobutton_filter);
309
310     g_signal_connect (selector, "selected",
311                       G_CALLBACK (set_radiobutton), radiobutton_filter);
312
313     g_signal_connect (radiobutton_range, "toggled",
314                       G_CALLBACK (set_sensitivity_from_toggle),
315                       range_label
316                       );
317
318     g_signal_connect (radiobutton_sample, "toggled",
319                       G_CALLBACK (set_sensitivity_from_toggle),
320                       sample_label
321                       );
322
323     g_signal_connect (radiobutton_filter, "toggled",
324                       G_CALLBACK (set_sensitivity_from_toggle),
325                       entry
326                       );
327   }
328
329
330
331   dialog = get_widget_assert (scd.xml, "select-cases-dialog");
332   gtk_window_set_transient_for (GTK_WINDOW (dialog), de->parent.window);
333
334   {
335     GtkWidget *source = get_widget_assert   (scd.xml, "select-cases-treeview");
336
337     attach_dictionary_to_treeview (GTK_TREE_VIEW (source),
338                                    scd.data_store->dict,
339                                    GTK_SELECTION_SINGLE, NULL);
340
341     psppire_selector_set_subjects (PSPPIRE_SELECTOR (selector),
342                                    source,
343                                    entry,
344                                    insert_source_row_into_entry,
345                                    is_currently_in_entry,
346                                    NULL);
347   }
348
349
350
351   g_signal_connect (button_range,
352                     "clicked", G_CALLBACK (range_subdialog), &scd);
353
354
355   g_signal_connect (button_sample,
356                     "clicked", G_CALLBACK (sample_subdialog), &scd);
357
358
359   response = psppire_dialog_run (PSPPIRE_DIALOG (dialog));
360
361   switch (response)
362     {
363     case GTK_RESPONSE_OK:
364       {
365         gchar *syntax = generate_syntax (&scd);
366         struct getl_interface *sss = create_syntax_string_source (syntax);
367         execute_syntax (sss);
368
369         g_free (syntax);
370       }
371       break;
372     case PSPPIRE_RESPONSE_PASTE:
373       {
374         gchar *syntax = generate_syntax (&scd);
375
376         struct syntax_editor *se =
377           (struct syntax_editor *) window_create (WINDOW_SYNTAX, NULL);
378
379         gtk_text_buffer_insert_at_cursor (se->buffer, syntax, -1);
380
381         g_free (syntax);
382       }
383       break;
384     default:
385       break;
386     }
387
388   g_object_unref (scd.xml);
389 }
390
391
392 static gchar *
393 generate_syntax (const struct select_cases_dialog *scd)
394 {
395   gchar *text = NULL;
396   GString *string = NULL;
397
398   if ( gtk_toggle_button_get_active
399        (GTK_TOGGLE_BUTTON (get_widget_assert (scd->xml,
400                                               "radiobutton-all"))))
401     {
402       return strdup ("\n");
403     }
404
405   string = g_string_new ("");
406
407   if ( gtk_toggle_button_get_active
408        (GTK_TOGGLE_BUTTON (get_widget_assert (scd->xml,
409                                               "radiobutton-sample"))))
410   {
411     GtkWidget *random_sample =
412       get_widget_assert (scd->xml,
413                          "radiobutton-sample-percent");
414
415     g_string_append (string, "SAMPLE ");
416
417     if ( gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (random_sample)))
418       {
419         const double percentage =
420           gtk_spin_button_get_value (GTK_SPIN_BUTTON (scd->spinbutton));
421         g_string_append_printf (string, "%g.", percentage / 100.0);
422       }
423     else
424       {
425         const gint n_cases =
426           gtk_spin_button_get_value (GTK_SPIN_BUTTON (scd->spinbutton1));
427         const gint from_n_cases =
428           gtk_spin_button_get_value (GTK_SPIN_BUTTON (scd->spinbutton2));
429
430         g_string_append_printf (string, "%d FROM %d .", n_cases, from_n_cases);
431       }
432
433   }
434   else if ( gtk_toggle_button_get_active
435             (GTK_TOGGLE_BUTTON (get_widget_assert (scd->xml,
436                                                    "radiobutton-range"))))
437     {
438       GtkSpinButton *first =
439         GTK_SPIN_BUTTON (get_widget_assert (scd->xml,
440                                            "range-dialog-first"));
441
442       GtkSpinButton *last =
443         GTK_SPIN_BUTTON (get_widget_assert (scd->xml,
444                                            "range-dialog-last"));
445
446       g_string_append_printf (string,
447                               "COMPUTE filter_$ = ($CASENUM >= %ld "
448                                "AND $CASENUM <= %ld).\n",
449                               (long) gtk_spin_button_get_value (first),
450                               (long) gtk_spin_button_get_value (last)
451                               );
452       g_string_append (string, "EXECUTE.\n");
453       g_string_append_printf (string, "SELECT IF filter_$.\n");
454
455     }
456   else if ( gtk_toggle_button_get_active
457             (GTK_TOGGLE_BUTTON
458              (get_widget_assert (scd->xml,
459                                  "radiobutton-filter-variable"))))
460     {
461       GtkEntry *entry =
462         GTK_ENTRY (get_widget_assert (scd->xml,
463                                       "filter-variable-entry"));
464
465       g_string_append_printf (string, "SELECT IF (%s <> 0).",
466                               gtk_entry_get_text (entry));
467     }
468
469
470   g_string_append (string, "\n");
471
472
473   /* Are we filtering or deleting ? */
474   if ( gtk_toggle_button_get_active
475        (GTK_TOGGLE_BUTTON (get_widget_assert (scd->xml,
476                                               "radiobutton-delete"))))
477     {
478       g_string_append (string, "EXECUTE.\n");
479
480       if ( gtk_toggle_button_get_active
481            (GTK_TOGGLE_BUTTON (get_widget_assert (scd->xml,
482                                                   "radiobutton-range"))))
483         {
484           g_string_append (string, "DELETE VARIABLES filter_$.\n");
485         }
486     }
487
488
489   text  = string->str;
490   g_string_free (string, FALSE);
491   return text;
492 }