Improved the syntax generated by the select-cases dialog
[pspp] / src / ui / gui / select-cases-dialog.c
index 473dcdfb7ae51c0adcd742ab01cc08deaebf617d..0f50c91a3f96cf8dcbe60049cdfaa4158c74efb1 100644 (file)
@@ -1,35 +1,33 @@
-/*
-    PSPPIRE --- A Graphical User Interface for PSPP
-    Copyright (C) 2007  Free Software Foundation
+/* PSPPIRE - a graphical user interface for PSPP.
+   Copyright (C) 2007, 2008, 2009 Free Software Foundation, Inc.
 
-    This program is free software; you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation; either version 2 of the License, or
-    (at your option) any later version.
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
 
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
 
-    You should have received a copy of the GNU General Public License
-    along with this program; if not, write to the Free Software
-    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-    02110-1301, USA. */
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
 
 #include <config.h>
 
 #include "select-cases-dialog.h"
 #include <gtk/gtk.h>
-#include <glade/glade.h>
-#include "helper.h"
+#include "executor.h"
 #include "psppire-dialog.h"
-#include "data-editor.h"
-#include "dialog-common.h"
+#include "psppire-data-window.h"
+#include "psppire-selector.h"
 #include "dict-display.h"
+#include "dialog-common.h"
 #include "widget-io.h"
 #include <language/syntax-string-source.h>
-#include "syntax-editor.h"
+#include "helper.h"
+#include <xalloc.h>
 
 
 #include <gettext.h>
 
 
 /* FIXME: These shouldn't be here */
-#include <gtksheet/gtksheet.h>
 #include "psppire-data-store.h"
 
 
 struct select_cases_dialog
 {
   /* The XML that created the dialog */
-  GladeXML *xml;
+  GtkBuilder *xml;
 
   GtkWidget *spinbutton ;
   GtkWidget *spinbutton1 ;
@@ -114,7 +111,7 @@ sample_subdialog (GtkButton *b, gpointer data)
       gtk_table_attach_defaults (GTK_TABLE (table),
                                 scd->hbox1, 1, 2, 0, 1);
 
-      g_signal_connect (G_OBJECT (percent), "toggled",
+      g_signal_connect (percent, "toggled",
                        G_CALLBACK (set_sensitivity_from_toggle), scd->hbox1);
 
       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (percent), TRUE);
@@ -138,7 +135,7 @@ sample_subdialog (GtkButton *b, gpointer data)
       gtk_table_attach_defaults (GTK_TABLE (table),
                                 scd->hbox2, 1, 2, 1, 2);
 
-      g_signal_connect (G_OBJECT (sample_n_cases), "toggled",
+      g_signal_connect (sample_n_cases, "toggled",
                        G_CALLBACK (set_sensitivity_from_toggle), scd->hbox2);
 
       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (sample_n_cases), FALSE);
@@ -209,9 +206,8 @@ range_subdialog (GtkButton *b, gpointer data)
                                        "range-dialog-last");
 
 
-  gtk_spin_button_set_range (GTK_SPIN_BUTTON (last), 1, n_cases);
-
-  gtk_spin_button_set_range (GTK_SPIN_BUTTON (first), 1, n_cases);
+  gtk_spin_button_set_range (GTK_SPIN_BUTTON (last),  1,  n_cases);
+  gtk_spin_button_set_range (GTK_SPIN_BUTTON (first), 1,  n_cases);
 
   gtk_window_set_transient_for (GTK_WINDOW (dialog),
                                GTK_WINDOW (parent_dialog));
@@ -240,25 +236,19 @@ set_radiobutton (GtkWidget *button, gpointer data)
 
 /* Pops up the Select Cases dialog box */
 void
-select_cases_dialog (GObject *o, gpointer data)
+select_cases_dialog (PsppireDataWindow *de)
 {
   gint response;
   struct select_cases_dialog scd = {0,0,0,0,0,0};
   GtkWidget *dialog   ;
-  struct data_editor *de = data;
   GtkWidget *entry = NULL;
   GtkWidget *selector ;
   GtkWidget *button_range;
   GtkWidget *button_sample;
 
-  scd.xml = XML_NEW ("psppire.glade");
-
-  {
-    GtkSheet *data_sheet =
-      GTK_SHEET (get_widget_assert (de->xml, "data_sheet"));
+  scd.xml = builder_new ("select-cases.ui");
 
-    scd.data_store = PSPPIRE_DATA_STORE (gtk_sheet_get_model (data_sheet));
-  }
+  g_object_get (de->data_editor, "data-store", &scd.data_store, NULL);
 
   button_range = get_widget_assert (scd.xml, "button-range");
   button_sample = get_widget_assert (scd.xml, "button-sample");
@@ -329,19 +319,17 @@ select_cases_dialog (GObject *o, gpointer data)
 
 
   dialog = get_widget_assert (scd.xml, "select-cases-dialog");
-  gtk_window_set_transient_for (GTK_WINDOW (dialog), de->parent.window);
+  gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (de));
 
   {
     GtkWidget *source = get_widget_assert   (scd.xml, "select-cases-treeview");
 
-    attach_dictionary_to_treeview (GTK_TREE_VIEW (source),
-                                  scd.data_store->dict,
-                                  GTK_SELECTION_SINGLE, NULL);
+    g_object_set (source, "model",
+                 scd.data_store->dict,
+                 "selection-mode",
+                 GTK_SELECTION_SINGLE, NULL);
 
-    psppire_selector_set_subjects (PSPPIRE_SELECTOR (selector),
-                                  source,
-                                  entry,
-                                  insert_source_row_into_entry,
+    psppire_selector_set_filter_func (PSPPIRE_SELECTOR (selector),
                                   is_currently_in_entry);
   }
 
@@ -362,6 +350,7 @@ select_cases_dialog (GObject *o, gpointer data)
     case GTK_RESPONSE_OK:
       {
        gchar *syntax = generate_syntax (&scd);
+
        struct getl_interface *sss = create_syntax_string_source (syntax);
        execute_syntax (sss);
 
@@ -371,11 +360,7 @@ select_cases_dialog (GObject *o, gpointer data)
     case PSPPIRE_RESPONSE_PASTE:
       {
        gchar *syntax = generate_syntax (&scd);
-
-       struct syntax_editor *se =
-         (struct syntax_editor *) window_create (WINDOW_SYNTAX, NULL);
-
-       gtk_text_buffer_insert_at_cursor (se->buffer, syntax, -1);
+        paste_syntax_to_window (syntax);
 
        g_free (syntax);
       }
@@ -389,7 +374,118 @@ select_cases_dialog (GObject *o, gpointer data)
 
 
 static gchar *
-generate_syntax (const struct select_cases_dialog *scd)
+generate_syntax_filter (const struct select_cases_dialog *scd)
+{
+  gchar *text = NULL;
+  GString *string = g_string_new ("");
+
+  const gchar filter[]="filter_$";
+  const gchar key[]="case_$";
+
+  if ( gtk_toggle_button_get_active
+       (GTK_TOGGLE_BUTTON (get_widget_assert (scd->xml,
+                                             "radiobutton-range"))))
+    {
+      GtkSpinButton *first =
+       GTK_SPIN_BUTTON (get_widget_assert (scd->xml,
+                                          "range-dialog-first"));
+
+      GtkSpinButton *last =
+       GTK_SPIN_BUTTON (get_widget_assert (scd->xml,
+                                          "range-dialog-last"));
+
+      g_string_append_printf (string,
+                             "COMPUTE filter_$ = ($CASENUM >= %ld "
+                              "AND $CASENUM <= %ld).\n",
+                             (long) gtk_spin_button_get_value (first),
+                             (long) gtk_spin_button_get_value (last)
+                             );
+
+      g_string_append (string, "EXECUTE.\n");
+    }
+  else if ( gtk_toggle_button_get_active
+       (GTK_TOGGLE_BUTTON (get_widget_assert (scd->xml,
+                                             "radiobutton-sample"))))
+    {
+      GtkWidget *random_sample =
+       get_widget_assert (scd->xml,
+                          "radiobutton-sample-percent");
+
+      if ( gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (random_sample)))
+       {
+         const double percentage =
+           gtk_spin_button_get_value (GTK_SPIN_BUTTON (scd->spinbutton));
+
+         g_string_append_printf (string,
+                                 "COMPUTE %s = RV.UNIFORM (0,1) < %g.\n",
+                                 filter,
+                                 percentage / 100.0 );
+       }
+      else
+       {
+         const gint n_cases =
+           gtk_spin_button_get_value (GTK_SPIN_BUTTON (scd->spinbutton1));
+         const gint from_n_cases =
+           gtk_spin_button_get_value (GTK_SPIN_BUTTON (scd->spinbutton2));
+
+
+         const gchar ranvar[]="rv_$";
+
+         g_string_append_printf (string,
+                                 "COMPUTE %s = $CASENUM.\n", key);
+
+         g_string_append_printf (string,
+                                 "COMPUTE %s = %s > %d.\n",
+                                 filter, key, from_n_cases);
+
+         g_string_append_printf (string,
+                                 "COMPUTE %s = RV.UNIFORM (0, 1).\n",
+                                 ranvar);
+
+         g_string_append_printf (string,
+                                 "SORT BY %s, %s.\n",
+                                 filter, ranvar);
+
+         g_string_append (string, "EXECUTE.\n");
+                                 
+
+         g_string_append_printf (string,
+                                 "COMPUTE %s = $CASENUM.\n",
+                                 filter );
+
+         g_string_append_printf (string,
+                                 "COMPUTE %s = %s <= %d\n",
+                                 filter,
+                                 filter,
+                                 n_cases );
+
+         g_string_append (string, "EXECUTE.\n");
+
+
+         g_string_append_printf (string,
+                                 "SORT BY %s.\n",
+                                 key);
+
+         g_string_append_printf (string,
+                                 "DELETE VARIABLES %s, %s.\n",
+                                 key, ranvar);
+
+       }
+
+      g_string_append (string, "EXECUTE.\n");
+
+    }
+
+
+  g_string_append_printf  (string, "FILTER BY %s.\n", filter);
+
+  text  = string->str;
+  g_string_free (string, FALSE);
+  return text;
+}
+
+static gchar *
+generate_syntax_delete (const struct select_cases_dialog *scd)
 {
   gchar *text = NULL;
   GString *string = NULL;
@@ -398,7 +494,7 @@ generate_syntax (const struct select_cases_dialog *scd)
        (GTK_TOGGLE_BUTTON (get_widget_assert (scd->xml,
                                              "radiobutton-all"))))
     {
-      return strdup ("\n");
+      return xstrdup ("\n");
     }
 
   string = g_string_new ("");
@@ -469,23 +565,38 @@ generate_syntax (const struct select_cases_dialog *scd)
   g_string_append (string, "\n");
 
 
+
+  text  = string->str;
+  g_string_free (string, FALSE);
+  return text;
+}
+
+
+static gchar *
+generate_syntax (const struct select_cases_dialog *scd)
+{
+  /* In the simple case, all we need to do is cancel any existing filter */
+  if ( gtk_toggle_button_get_active
+       (GTK_TOGGLE_BUTTON (get_widget_assert (scd->xml,
+                                             "radiobutton-all"))))
+    {
+      return g_strdup ("FILTER OFF.\n");
+    }
+
+
   /* Are we filtering or deleting ? */
   if ( gtk_toggle_button_get_active
        (GTK_TOGGLE_BUTTON (get_widget_assert (scd->xml,
                                              "radiobutton-delete"))))
     {
-      g_string_append (string, "EXECUTE.\n");
-
-      if ( gtk_toggle_button_get_active
-          (GTK_TOGGLE_BUTTON (get_widget_assert (scd->xml,
-                                                 "radiobutton-range"))))
-       {
-         g_string_append (string, "DELETE VARIABLES filter_$.\n");
-       }
+      return generate_syntax_delete (scd);
+    }
+  else
+    {
+      return generate_syntax_filter (scd);
     }
 
-
-  text  = string->str;
-  g_string_free (string, FALSE);
-  return text;
 }
+
+
+