1d7591845b60e6605003c8a7d692be70c09327e2
[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   g_object_get (de->data_editor, "data-store", &scd.data_store, NULL);
257
258   button_range = get_widget_assert (scd.xml, "button-range");
259   button_sample = get_widget_assert (scd.xml, "button-sample");
260   entry = get_widget_assert (scd.xml, "filter-variable-entry");
261   selector = get_widget_assert (scd.xml, "psppire-selector-filter");
262
263   {
264     GtkWidget *button_if =
265       get_widget_assert (scd.xml, "button-if");
266
267     GtkWidget *radiobutton_if =
268       get_widget_assert (scd.xml, "radiobutton-if");
269
270     GtkWidget *radiobutton_all =
271       get_widget_assert (scd.xml, "radiobutton-all");
272
273     GtkWidget *radiobutton_sample =
274       get_widget_assert (scd.xml, "radiobutton-sample");
275
276     GtkWidget *radiobutton_range =
277       get_widget_assert (scd.xml, "radiobutton-range");
278
279     GtkWidget *radiobutton_filter =
280       get_widget_assert (scd.xml, "radiobutton-filter-variable");
281
282     GtkWidget *range_label =
283       get_widget_assert (scd.xml, "range-sample-label");
284
285     GtkWidget *sample_label =
286       get_widget_assert (scd.xml, "random-sample-label");
287
288     g_signal_connect (radiobutton_all, "toggled",
289                       G_CALLBACK (set_sensitivity_from_toggle_invert),
290                       get_widget_assert (scd.xml, "filter-delete-button-box")
291                       );
292
293     g_signal_connect (button_if, "clicked",
294                       G_CALLBACK (set_radiobutton), radiobutton_if);
295
296     g_signal_connect (button_sample, "clicked",
297                       G_CALLBACK (set_radiobutton), radiobutton_sample);
298
299     g_signal_connect (button_range,  "clicked",
300                       G_CALLBACK (set_radiobutton), radiobutton_range);
301
302     g_signal_connect (selector, "clicked",
303                       G_CALLBACK (set_radiobutton), radiobutton_filter);
304
305     g_signal_connect (selector, "selected",
306                       G_CALLBACK (set_radiobutton), radiobutton_filter);
307
308     g_signal_connect (radiobutton_range, "toggled",
309                       G_CALLBACK (set_sensitivity_from_toggle),
310                       range_label
311                       );
312
313     g_signal_connect (radiobutton_sample, "toggled",
314                       G_CALLBACK (set_sensitivity_from_toggle),
315                       sample_label
316                       );
317
318     g_signal_connect (radiobutton_filter, "toggled",
319                       G_CALLBACK (set_sensitivity_from_toggle),
320                       entry
321                       );
322   }
323
324
325
326   dialog = get_widget_assert (scd.xml, "select-cases-dialog");
327   gtk_window_set_transient_for (GTK_WINDOW (dialog), de->parent.window);
328
329   {
330     GtkWidget *source = get_widget_assert   (scd.xml, "select-cases-treeview");
331
332     attach_dictionary_to_treeview (GTK_TREE_VIEW (source),
333                                    scd.data_store->dict,
334                                    GTK_SELECTION_SINGLE, NULL);
335
336     psppire_selector_set_subjects (PSPPIRE_SELECTOR (selector),
337                                    source,
338                                    entry,
339                                    insert_source_row_into_entry,
340                                    is_currently_in_entry,
341                                    NULL);
342   }
343
344
345
346   g_signal_connect (button_range,
347                     "clicked", G_CALLBACK (range_subdialog), &scd);
348
349
350   g_signal_connect (button_sample,
351                     "clicked", G_CALLBACK (sample_subdialog), &scd);
352
353
354   response = psppire_dialog_run (PSPPIRE_DIALOG (dialog));
355
356   switch (response)
357     {
358     case GTK_RESPONSE_OK:
359       {
360         gchar *syntax = generate_syntax (&scd);
361         struct getl_interface *sss = create_syntax_string_source (syntax);
362         execute_syntax (sss);
363
364         g_free (syntax);
365       }
366       break;
367     case PSPPIRE_RESPONSE_PASTE:
368       {
369         gchar *syntax = generate_syntax (&scd);
370
371         struct syntax_editor *se =
372           (struct syntax_editor *) window_create (WINDOW_SYNTAX, NULL);
373
374         gtk_text_buffer_insert_at_cursor (se->buffer, syntax, -1);
375
376         g_free (syntax);
377       }
378       break;
379     default:
380       break;
381     }
382
383   g_object_unref (scd.xml);
384 }
385
386
387 static gchar *
388 generate_syntax (const struct select_cases_dialog *scd)
389 {
390   gchar *text = NULL;
391   GString *string = NULL;
392
393   if ( gtk_toggle_button_get_active
394        (GTK_TOGGLE_BUTTON (get_widget_assert (scd->xml,
395                                               "radiobutton-all"))))
396     {
397       return strdup ("\n");
398     }
399
400   string = g_string_new ("");
401
402   if ( gtk_toggle_button_get_active
403        (GTK_TOGGLE_BUTTON (get_widget_assert (scd->xml,
404                                               "radiobutton-sample"))))
405   {
406     GtkWidget *random_sample =
407       get_widget_assert (scd->xml,
408                          "radiobutton-sample-percent");
409
410     g_string_append (string, "SAMPLE ");
411
412     if ( gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (random_sample)))
413       {
414         const double percentage =
415           gtk_spin_button_get_value (GTK_SPIN_BUTTON (scd->spinbutton));
416         g_string_append_printf (string, "%g.", percentage / 100.0);
417       }
418     else
419       {
420         const gint n_cases =
421           gtk_spin_button_get_value (GTK_SPIN_BUTTON (scd->spinbutton1));
422         const gint from_n_cases =
423           gtk_spin_button_get_value (GTK_SPIN_BUTTON (scd->spinbutton2));
424
425         g_string_append_printf (string, "%d FROM %d .", n_cases, from_n_cases);
426       }
427
428   }
429   else if ( gtk_toggle_button_get_active
430             (GTK_TOGGLE_BUTTON (get_widget_assert (scd->xml,
431                                                    "radiobutton-range"))))
432     {
433       GtkSpinButton *first =
434         GTK_SPIN_BUTTON (get_widget_assert (scd->xml,
435                                            "range-dialog-first"));
436
437       GtkSpinButton *last =
438         GTK_SPIN_BUTTON (get_widget_assert (scd->xml,
439                                            "range-dialog-last"));
440
441       g_string_append_printf (string,
442                               "COMPUTE filter_$ = ($CASENUM >= %ld "
443                                "AND $CASENUM <= %ld).\n",
444                               (long) gtk_spin_button_get_value (first),
445                               (long) gtk_spin_button_get_value (last)
446                               );
447       g_string_append (string, "EXECUTE.\n");
448       g_string_append_printf (string, "SELECT IF filter_$.\n");
449
450     }
451   else if ( gtk_toggle_button_get_active
452             (GTK_TOGGLE_BUTTON
453              (get_widget_assert (scd->xml,
454                                  "radiobutton-filter-variable"))))
455     {
456       GtkEntry *entry =
457         GTK_ENTRY (get_widget_assert (scd->xml,
458                                       "filter-variable-entry"));
459
460       g_string_append_printf (string, "SELECT IF (%s <> 0).",
461                               gtk_entry_get_text (entry));
462     }
463
464
465   g_string_append (string, "\n");
466
467
468   /* Are we filtering or deleting ? */
469   if ( gtk_toggle_button_get_active
470        (GTK_TOGGLE_BUTTON (get_widget_assert (scd->xml,
471                                               "radiobutton-delete"))))
472     {
473       g_string_append (string, "EXECUTE.\n");
474
475       if ( gtk_toggle_button_get_active
476            (GTK_TOGGLE_BUTTON (get_widget_assert (scd->xml,
477                                                   "radiobutton-range"))))
478         {
479           g_string_append (string, "DELETE VARIABLES filter_$.\n");
480         }
481     }
482
483
484   text  = string->str;
485   g_string_free (string, FALSE);
486   return text;
487 }