From 4ea6e37d180c9412cbcd39afa48a516522e6dd71 Mon Sep 17 00:00:00 2001
From: John Darrington <john@darrington.wattle.id.au>
Date: Thu, 26 Jul 2007 02:02:22 +0000
Subject: [PATCH] Patch #6086. Adds "transformation pending" state. Thanks to
 Ben for the review.

---
 src/data/ChangeLog           |  5 ++++
 src/data/procedure.c         | 45 ++++++++++++++++++++++++++++++++----
 src/data/procedure.h         |  8 ++++---
 src/ui/gui/ChangeLog         |  8 +++++++
 src/ui/gui/data-editor.c     | 43 ++++++++++++++++++++++++++++++++++
 src/ui/gui/data-editor.glade | 18 ++++++++++++++-
 src/ui/gui/helper.c          | 12 ++--------
 src/ui/gui/helper.h          |  2 +-
 8 files changed, 122 insertions(+), 19 deletions(-)

diff --git a/src/data/ChangeLog b/src/data/ChangeLog
index 70485fd915..ddf6e685bc 100644
--- a/src/data/ChangeLog
+++ b/src/data/ChangeLog
@@ -1,3 +1,8 @@
+2007-07-26 John Darrington <john@darrington.wattle.id.au>
+
+	* procedure.c procedure.h: Added callbacks which get invoked whenever 
+	a dataset's transformation chain changes.
+
 2007-07-24  Ben Pfaff  <blp@gnu.org>
 
 	Fix bug #6113.
diff --git a/src/data/procedure.c b/src/data/procedure.c
index 5f3ad2065a..6690490f30 100644
--- a/src/data/procedure.c
+++ b/src/data/procedure.c
@@ -37,6 +37,7 @@
 #include <libpspp/str.h>
 #include <libpspp/taint.h>
 
+
 struct dataset {
   /* Cases are read from source,
      their transformation variables are initialized,
@@ -61,6 +62,11 @@ struct dataset {
   /* Callback which occurs whenever the DICT is replaced by a new one */
   replace_dictionary_callback *replace_dict;
 
+  /* Callback which occurs whenever the transformation chain(s) have
+     been modified */
+  transformation_change_callback_func *xform_callback;
+  void *xform_callback_aux;
+
   /* If true, cases are discarded instead of being written to
      sink. */
   bool discard_output;
@@ -384,6 +390,10 @@ proc_capture_transformations (struct dataset *ds)
   assert (ds->temporary_trns_chain == NULL);
   chain = ds->permanent_trns_chain;
   ds->cur_trns_chain = ds->permanent_trns_chain = trns_chain_create ();
+
+  if ( ds->xform_callback)
+    ds->xform_callback (false, ds->xform_callback_aux);
+
   return chain;
 }
 
@@ -394,6 +404,8 @@ void
 add_transformation (struct dataset *ds, trns_proc_func *proc, trns_free_func *free, void *aux)
 {
   trns_chain_append (ds->cur_trns_chain, NULL, proc, free, aux);
+  if ( ds->xform_callback)
+    ds->xform_callback (true, ds->xform_callback_aux);
 }
 
 /* Adds a transformation that processes a case with PROC and
@@ -408,6 +420,9 @@ add_transformation_with_finalizer (struct dataset *ds,
                                    trns_free_func *free, void *aux)
 {
   trns_chain_append (ds->cur_trns_chain, finalize, proc, free, aux);
+
+  if ( ds->xform_callback)
+    ds->xform_callback (true, ds->xform_callback_aux);
 }
 
 /* Returns the index of the next transformation.
@@ -442,6 +457,9 @@ proc_start_temporary_transformations (struct dataset *ds)
 
       trns_chain_finalize (ds->permanent_trns_chain);
       ds->temporary_trns_chain = ds->cur_trns_chain = trns_chain_create ();
+
+      if ( ds->xform_callback)
+	ds->xform_callback (true, ds->xform_callback_aux);
     }
 }
 
@@ -483,6 +501,10 @@ proc_cancel_temporary_transformations (struct dataset *ds)
       trns_chain_destroy (ds->temporary_trns_chain);
       ds->temporary_trns_chain = NULL;
 
+      if ( ds->xform_callback)
+	ds->xform_callback (!trns_chain_is_empty (ds->permanent_trns_chain),
+			    ds->xform_callback_aux);
+
       return true;
     }
   else
@@ -500,23 +522,35 @@ proc_cancel_all_transformations (struct dataset *ds)
   ok = trns_chain_destroy (ds->temporary_trns_chain) && ok;
   ds->permanent_trns_chain = ds->cur_trns_chain = trns_chain_create ();
   ds->temporary_trns_chain = NULL;
+  if ( ds->xform_callback)
+    ds->xform_callback (false, ds->xform_callback_aux);
+
   return ok;
 }
 
 /* Initializes procedure handling. */
 struct dataset *
-create_dataset (replace_source_callback *rps,
-		replace_dictionary_callback *rds)
+create_dataset (transformation_change_callback_func *cb, void *aux)
 {
   struct dataset *ds = xzalloc (sizeof(*ds));
   ds->dict = dict_create ();
   ds->caseinit = caseinit_create ();
-  ds->replace_source = rps;
-  ds->replace_dict = rds;
+  ds->xform_callback = cb;
+  ds->xform_callback_aux = aux;
   proc_cancel_all_transformations (ds);
   return ds;
 }
 
+
+void
+dataset_add_transform_change_callback (struct dataset *ds,
+				       transformation_change_callback_func *cb,
+				       void *aux)
+{
+  ds->xform_callback = cb;
+  ds->xform_callback_aux = aux;
+}
+
 /* Finishes up procedure handling. */
 void
 destroy_dataset (struct dataset *ds)
@@ -525,6 +559,9 @@ destroy_dataset (struct dataset *ds)
   dict_destroy (ds->dict);
   caseinit_destroy (ds->caseinit);
   trns_chain_destroy (ds->permanent_trns_chain);
+
+  if ( ds->xform_callback)
+    ds->xform_callback (false, ds->xform_callback_aux);
   free (ds);
 }
 
diff --git a/src/data/procedure.h b/src/data/procedure.h
index 1b014e81cf..cfb8f218c5 100644
--- a/src/data/procedure.h
+++ b/src/data/procedure.h
@@ -51,12 +51,14 @@ struct dictionary ;
 typedef void  replace_source_callback (struct casereader *);
 typedef void  replace_dictionary_callback (struct dictionary *);
 
+typedef void transformation_change_callback_func (bool non_empty, void *aux);
 
-struct dataset * create_dataset (replace_source_callback *,
-				 replace_dictionary_callback *);
-
+struct dataset * create_dataset (transformation_change_callback_func *, void *);
 void destroy_dataset (struct dataset *);
 
+void dataset_add_transform_change_callback (struct dataset *,
+					    transformation_change_callback_func *, void *);
+
 void proc_discard_active_file (struct dataset *);
 void proc_set_active_file (struct dataset *,
                            struct casereader *, struct dictionary *);
diff --git a/src/ui/gui/ChangeLog b/src/ui/gui/ChangeLog
index e0b6a97130..4f0a0949f1 100644
--- a/src/ui/gui/ChangeLog
+++ b/src/ui/gui/ChangeLog
@@ -1,3 +1,11 @@
+2007-07-26  John Darrington <john@darrington.wattle.id.au>
+
+	* helper.c helper.h (execute_syntax): removed implicit EXECUTE at end 
+	of commands.
+
+	* data-editor.c data-editor.glade: Added "Run Pending Transformations" 
+	menuitem.
+
 2007-07-25  John Darrington <john@darrington.wattle.id.au>
 
 	* customentry.c: Redraw button in insensitive state, if the widget's 
diff --git a/src/ui/gui/data-editor.c b/src/ui/gui/data-editor.c
index 94094dff93..9681a09f1b 100644
--- a/src/ui/gui/data-editor.c
+++ b/src/ui/gui/data-editor.c
@@ -26,6 +26,7 @@
 
 #include "helper.h"
 #include "about.h"
+#include <data/procedure.h>
 #include "psppire-dialog.h"
 #include "psppire-selector.h"
 #include "weight-cases-dialog.h"
@@ -119,6 +120,37 @@ enable_delete_variables (GtkWidget *w, gint var, gpointer data)
 }
 
 
+
+/* Run the EXECUTE command. */
+static void
+execute (GtkMenuItem *mi, gpointer data)
+{
+  struct getl_interface *sss = create_syntax_string_source ("EXECUTE.");
+
+  execute_syntax (sss);
+}
+
+static void
+transformation_change_callback (bool transformations_pending,
+				gpointer data)
+{
+  struct data_editor *de = data;
+  GtkWidget *menuitem =
+    get_widget_assert (de->xml, "transform_run-pending");
+  GtkWidget *status_label  =
+    get_widget_assert (de->xml, "case-counter-area");
+
+  gtk_widget_set_sensitive (menuitem, transformations_pending);
+
+
+  if ( transformations_pending)
+    gtk_label_set_text (GTK_LABEL (status_label),
+			_("Transformations Pending"));
+  else
+    gtk_label_set_text (GTK_LABEL (status_label), "");
+}
+
+
 static void open_data_file (const gchar *, struct data_editor *);
 
 
@@ -184,6 +216,8 @@ datum_entry_activate (GtkEntry *entry, gpointer data)
   psppire_data_store_set_string (store, text, row, column);
 }
 
+extern struct dataset *the_dataset;
+
 /*
   Create a new data editor.
 */
@@ -203,6 +237,11 @@ new_data_editor (void)
 
   de->xml = XML_NEW ("data-editor.glade");
 
+
+  dataset_add_transform_change_callback (the_dataset,
+					 transformation_change_callback,
+					 de);
+
   var_sheet = GTK_SHEET (get_widget_assert (de->xml, "variable_sheet"));
   data_sheet = GTK_SHEET (get_widget_assert (de->xml, "data_sheet"));
 
@@ -614,6 +653,10 @@ new_data_editor (void)
 		    "activate",
 		    G_CALLBACK (file_quit), de);
 
+  g_signal_connect (get_widget_assert (de->xml, "transform_run-pending"),
+		    "activate",
+		    G_CALLBACK (execute), de);
+
 
   g_signal_connect (get_widget_assert (de->xml, "windows_minimise_all"),
 		    "activate",
diff --git a/src/ui/gui/data-editor.glade b/src/ui/gui/data-editor.glade
index 18e3400164..68813e02a1 100644
--- a/src/ui/gui/data-editor.glade
+++ b/src/ui/gui/data-editor.glade
@@ -399,6 +399,22 @@
                         <property name="use_underline">True</property>
                       </widget>
                     </child>
+                 <child>
+                   <widget class="GtkSeparatorMenuItem" id="separator7">
+                     <property name="visible">True</property>
+                   </widget>
+                 </child>
+
+                 <child>
+                   <widget class="GtkMenuItem" id="transform_run-pending">
+                     <property name="visible">True</property>
+                     <property name="sensitive">False</property>
+                     <property name="label" translatable="yes">_Run Pending Transforms</property>
+                     <property name="use_underline">True</property>
+                     <accelerator key="G" modifiers="GDK_CONTROL_MASK" signal="activate"/>
+                   </widget>
+                 </child>
+
                   </widget>
                 </child>
               </widget>
@@ -921,7 +937,7 @@
                     <child>
                       <widget class="GtkLabel" id="case-counter-area">
                         <property name="visible">True</property>
-                        <property name="width_chars">10</property>
+                        <property name="width_chars">25</property>
                         <property name="single_line_mode">True</property>
                       </widget>
                     </child>
diff --git a/src/ui/gui/helper.c b/src/ui/gui/helper.c
index 09711f1bd1..5668496833 100644
--- a/src/ui/gui/helper.c
+++ b/src/ui/gui/helper.c
@@ -164,17 +164,16 @@ extern struct dataset *the_dataset;
 extern struct source_stream *the_source_stream;
 extern PsppireDataStore *the_data_store;
 
-gboolean
+void
 execute_syntax (struct getl_interface *sss)
 {
-  gboolean status;
   struct lexer *lexer;
 
   struct casereader *reader = psppire_data_store_get_reader (the_data_store);
 
   proc_set_active_file_data (the_dataset, reader);
 
-  g_return_val_if_fail (proc_has_active_file (the_dataset), FALSE);
+  g_return_if_fail (proc_has_active_file (the_dataset));
 
   lexer = lex_create (the_source_stream);
 
@@ -192,11 +191,6 @@ execute_syntax (struct getl_interface *sss)
 
   lex_destroy (lexer);
 
-  /* GUI syntax needs this implicit EXECUTE command at the end of
-     every script.  Otherwise commands like GET could leave the GUI
-     without a datasheet. */
-  status = proc_execute (the_dataset);
-
   psppire_dict_replace_dictionary (the_data_store->dict,
 				   dataset_dict (the_dataset));
 
@@ -205,8 +199,6 @@ execute_syntax (struct getl_interface *sss)
 
     psppire_data_store_set_case_file (the_data_store, pcf);
   }
-
-  return status;
 }
 
 
diff --git a/src/ui/gui/helper.h b/src/ui/gui/helper.h
index ddced00fc3..7ada5b5298 100644
--- a/src/ui/gui/helper.h
+++ b/src/ui/gui/helper.h
@@ -54,7 +54,7 @@ void connect_help (GladeXML *);
 void reference_manual (GtkMenuItem *, gpointer);
 
 struct getl_interface;
-gboolean execute_syntax (struct getl_interface *sss);
+void execute_syntax (struct getl_interface *sss);
 
 
 #define XML_NEW(FILE) \
-- 
2.30.2