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