r14909 - gnucash/branches/sx-cleanup/src - since-last-run variable handling, `remind`er constraints, consistency checking, Transaction creation. More model fleshing out.

Joshua Sled jsled at cvs.gnucash.org
Fri Sep 29 14:55:27 EDT 2006


Author: jsled
Date: 2006-09-29 14:55:23 -0400 (Fri, 29 Sep 2006)
New Revision: 14909
Trac: http://svn.gnucash.org/trac/changeset/14909

Modified:
   gnucash/branches/sx-cleanup/src/doc/sx.rst
   gnucash/branches/sx-cleanup/src/gnome/dialog-sx-since-last-run.c
   gnucash/branches/sx-cleanup/src/gnome/glade/sched-xact.glade
   gnucash/branches/sx-cleanup/src/gnome/gnc-plugin-page-sx-list.c
   gnucash/branches/sx-cleanup/src/gnome/gnc-plugin-page-sx-list.h
Log:
since-last-run variable handling, `remind`er constraints, consistency checking, Transaction creation.  More model fleshing out.


Modified: gnucash/branches/sx-cleanup/src/doc/sx.rst
===================================================================
--- gnucash/branches/sx-cleanup/src/doc/sx.rst	2006-09-27 20:36:56 UTC (rev 14908)
+++ gnucash/branches/sx-cleanup/src/doc/sx.rst	2006-09-29 18:55:23 UTC (rev 14909)
@@ -21,6 +21,7 @@
 - meta
   - [ ] move files around
   - [ ] GncSxListTreeModelAdapter: s/real/adapted/
+  - [ ] generic tree model adapter setup code
 
 - core
   - [x] sx list -> qof collection
@@ -43,19 +44,28 @@
   - [ ] XML migration, handling
 
 - since-last-run
-  - [ ] add reminders, postponed to SxInstanceModel
+  - [x] add reminders, postponed to SxInstanceModel
   - [ ] add obsolete flag to SxInstanceModel
-  - [ ] add mutation support to sx instance model
-    - [ ] state machine
-  - [ ] add variable state to sx instance model
-  - [ ] add sx_upcoming_instance_model()
+  - [x] add mutation support to sx instance model
+    - [x] state machine
+  - [x] add variable state to sx instance model
+  - [x] add sx_upcoming_instance_model()
       - [ ] add effect_auto_create()
   - [ ] add some sort of "ready to go" flag and api
-    - [ ] variable setting, primarily
-  - [ ] some sort of commit_changes()
+    - [x] variable setting, primarily
+  - [/] some sort of commit_changes()
     - ??? does effect_auto_create() imply or need commit_changes()?
   - [/] add variable table to instances
+  - [ ] ui: add 'review created transactions' checkbox to SLR dialog
+        using txn search.
 
+- destroy/cleanup
+  - [ ] GncSxInstanceModel
+  - [ ] GncSxSlr[Tree]Model[Adapter]
+  - [ ] GncSxList adapter
+  - [ ] GncPluginPageSxList
+  - ...
+
 Pedantic Todo
 ----------------------
 
@@ -63,7 +73,6 @@
 - s/temporal_state/instance_sequence_context/
 - change instance variable from 'i' to '__i' or something
 
-
 ============================================================
 
 (eventually real documentation... (?))
@@ -115,14 +124,16 @@
   - to-create
   - [obsolete SX]?
 
-There is no seperate to-review page.
+There is no seperate to-review page, however the user may (optionally) want
+to see the created transactions.  This is done using the transaction-search
+functionality over the created transactions by ID.
 
 Upcoming instance states
 ---------------------------------------
 
-    reminder -> to-create
+    reminder  -> to-create
     postponed -> to-create
-    to-create -> postponed  (with constraints)
+    to-create -> postponed
     to-create -> ignore
 
 Definitions:
@@ -134,14 +145,35 @@
     ignore: a scheduled instance the user has explicitly prevented the
         instantiation of.
 
-What does the SX need to store?
-- postponed instance list.
-- last state of created instance.
+The SX need to store?
+- last state of *created* instance
+- postponed instance list
 
-    void gnc_sx_instance_model_change_state(sx_id, instance_id, new_state, **gerror)
-    boolean gnc_sx_instance_model_get_readiness(sx_id, instance_id)
-    boolean gnc_sx_instance_model_set_variables(sx_id, instance_id, variable_name:string, value:string, **gerrror)
+There is a constraint around a sequence of upcoming instance states.  In
+short: the last-created state and a list of postponed instances are modeled,
+but upcoming reminders are not.  As such, a reminder can never be before any
+other (modeled) instance type.  For instance, the following sequences are
+disallowed:
 
+[...]
+remind    <- will be lost/skipped over; must be converted to `postponed`.
+to-create <- this will be the last-recorded state.
+[...]
+
+[...]
+remind    <- same as previous; will be lost/skipped; must be `postponed`.
+postponed
+[...]
+
+remind    <- same...
+ignore
+[...]
+
+
+As such, the SinceLastRun model will enforce that there are no previous
+`remind` instances at every state change.  They will be silently converted to
+`postponed`-state transactions.
+
 Formula Parsing
 ------------------------
 
@@ -153,4 +185,17 @@
 - a variable-binding table.
 
 
+Testing Notes
+---------------------
 
+- auto-create
+  - auto-create with postponed instances shouldn't destroy postponed
+    instances
+
+- basic sequence stuff
+
+dialog-sxsincelast.c:  ~L1241:
+"Handle an interesting corner case of postponing or
+ignoring the first instance. We only want to incrment the
+counters for newly-discovered-as-to-be-created SXes."
+

Modified: gnucash/branches/sx-cleanup/src/gnome/dialog-sx-since-last-run.c
===================================================================
--- gnucash/branches/sx-cleanup/src/gnome/dialog-sx-since-last-run.c	2006-09-27 20:36:56 UTC (rev 14908)
+++ gnucash/branches/sx-cleanup/src/gnome/dialog-sx-since-last-run.c	2006-09-29 18:55:23 UTC (rev 14909)
@@ -26,9 +26,18 @@
 #include <glade/glade-xml.h>
 
 #include "dialog-utils.h"
+#include "gnc-exp-parser.h"
 #include "gnc-plugin-page-sx-list.h"
 #include "dialog-sx-since-last-run.h"
 
+#include "gnc-ui-util.h"
+#include "Split.h"
+#include "Transaction.h"
+#include "Account.h"
+#include "Scrub.h"
+
+static QofLogModule log_module = GNC_MOD_GUI;
+
 typedef struct _GncSxSlrTreeModelAdapter GncSxSlrTreeModelAdapter;
 
 struct _GncSxSinceLastRunDialog
@@ -62,7 +71,31 @@
 GncSxSlrTreeModelAdapter* gnc_sx_slr_tree_model_adapter_new(GncSxInstanceModel *instances);
 
 GncSxInstances* gnc_sx_slr_tree_model_adapter_get_sx_instances(GncSxSlrTreeModelAdapter *model, GtkTreeIter *iter);
+static GncSxInstances* _gnc_sx_slr_tree_model_adapter_get_sx_instances(GncSxSlrTreeModelAdapter *model, GtkTreeIter *iter, gboolean check_depth);
+/** @return null if the iter is not actually an GncSxInstance. **/
+GncSxInstance* gnc_sx_slr_model_get_instance(GncSxSlrTreeModelAdapter *model, GtkTreeIter *iter);
+static GncSxInstance* _gnc_sx_slr_model_get_instance(GncSxSlrTreeModelAdapter *model, GtkTreeIter *iter, gboolean check_depth);
+/** @return false if the iter is not actaully an GncSxInstance's variable. **/
+gboolean gnc_sx_slr_model_get_instance_and_variable(GncSxSlrTreeModelAdapter *model, GtkTreeIter *iter, GncSxInstance **instance_loc, GncSxVariable **var_loc);
 
+void gnc_sx_slr_model_change_instance_state(GncSxSlrTreeModelAdapter *model, GncSxInstance *instance, GncSxInstanceState new_state);
+void gnc_sx_slr_model_change_variable(GncSxSlrTreeModelAdapter *model, GncSxInstance *instance, GncSxVariable *variable, gnc_numeric *new_value);
+
+void gnc_sx_slr_model_effect_change(GncSxSlrTreeModelAdapter *model, gboolean auto_create_only, GList **created_transaction_guids, GList **creation_errors);
+
+GtkTreeModel* gnc_sx_get_slr_state_model(void);
+
+typedef struct _GncSxSlrVariableNeeded
+{
+     GncSxInstance *instance;
+     GncSxVariable *variable;
+} GncSxSlrVariableNeeded;
+
+/**
+ * @return Caller-owned GList<GncSxSlrVariableNeeded*>.
+ **/
+GList* gnc_sx_slr_model_check_variables(GncSxSlrTreeModelAdapter *model);
+
 #define GNC_TYPE_SX_SLR_TREE_MODEL_ADAPTER	      (gnc_sx_slr_tree_model_adapter_get_type ())
 #define GNC_SX_SLR_TREE_MODEL_ADAPTER(obj)	      (G_TYPE_CHECK_INSTANCE_CAST ((obj), GNC_TYPE_SX_SLR_TREE_MODEL_ADAPTER, GncSxSlrTreeModelAdapter))
 #define GNC_SX_SLR_TREE_MODEL_ADAPTER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GNC_TYPE_SX_SLR_TREE_MODEL_ADAPTER, GncSxSlrTreeModelAdapterClass))
@@ -72,12 +105,19 @@
 
 /* ------------------------------------------------------------ */
 
-static void _cell_visibility_func(GtkTreeViewColumn *tree_column,
-                                  GtkCellRenderer *cell,
-                                  GtkTreeModel *tree_model,
-                                  GtkTreeIter *iter,
-                                  gpointer data);
+static void dialog_response_cb(GtkDialog *dialog, gint response_id, GncSxSinceLastRunDialog *app_dialog);
 
+/* ------------------------------------------------------------ */
+
+static void
+_var_numeric_to_string(gnc_numeric *value, GString **str)
+{
+     *str = g_string_sized_new(5);
+     g_string_printf(*str, "%0.2f", gnc_numeric_to_double(*value));
+}
+
+/* ------------------------------------------------------------ */
+
 GType
 gnc_sx_slr_tree_model_adapter_get_type(void)
 {
@@ -257,41 +297,50 @@
 
 static void
 gsslrtma_proxy_row_has_child_toggled(GtkTreeModel *treemodel,
-                                   GtkTreePath *arg1,
-                                   GtkTreeIter *arg2,
-                                   gpointer user_data)
+                                     GtkTreePath *arg1,
+                                     GtkTreeIter *arg2,
+                                     gpointer user_data)
 {
      g_signal_emit_by_name(user_data, "row-has-child-toggled", arg1, arg2);
 }
 
 static void
 gsslrtma_proxy_row_inserted(GtkTreeModel *treemodel,
-                          GtkTreePath *arg1,
-                          GtkTreeIter *arg2,
-                          gpointer user_data)
+                            GtkTreePath *arg1,
+                            GtkTreeIter *arg2,
+                            gpointer user_data)
 {
      g_signal_emit_by_name(user_data, "row-inserted", arg1, arg2);
 }
 
 static void
 gsslrtma_proxy_rows_reordered(GtkTreeModel *treemodel,
-                            GtkTreePath *arg1,
-                            GtkTreeIter *arg2,
-                            gpointer arg3,
-                            gpointer user_data)
+                              GtkTreePath *arg1,
+                              GtkTreeIter *arg2,
+                              gpointer arg3,
+                              gpointer user_data)
 {
      g_signal_emit_by_name(user_data, "rows-reordered", arg1, arg2, arg3);
 }
 
+// model columns
+enum {
+     SLR_MODEL_COL_NAME = 0,
+     SLR_MODEL_COL_INSTANCE_STATE,
+     SLR_MODEL_COL_VARAIBLE_VALUE,
+     SLR_MODEL_COL_INSTANCE_VISIBILITY,
+     SLR_MODEL_COL_VARIABLE_VISIBILITY
+};
+
 static void
 gnc_sx_slr_tree_model_adapter_init(GTypeInstance *instance, gpointer klass)
 {
      GncSxSlrTreeModelAdapter *adapter = GNC_SX_SLR_TREE_MODEL_ADAPTER(instance);
-     // columns:    thing-name, instance-state, variable-value
-     // at depth=0: <sx>,       N/A,            N/A
-     // at depth=1: <instance>, <state>,        N/A
-     // at depth=2: <variable>, N/A,            <value>
-     adapter->real = gtk_tree_store_new(3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
+     // columns:    thing-name, instance-state, variable-value, instance-visible, variable-visible
+     // at depth=0: <sx>,       N/A,            N/A,            N/A               N/A,
+     // at depth=1: <instance>, <state>,        N/A,            <valid>,          N/A,
+     // at depth=2: <variable>, N/A,            <value>,        N/A,              <valid>,
+     adapter->real = gtk_tree_store_new(5, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN);
 
      g_signal_connect(adapter->real, "row-changed", G_CALLBACK(gsslrtma_proxy_row_changed), adapter);
      g_signal_connect(adapter->real, "row-deleted", G_CALLBACK(gsslrtma_proxy_row_deleted), adapter);
@@ -300,16 +349,9 @@
      g_signal_connect(adapter->real, "rows-reordered", G_CALLBACK(gsslrtma_proxy_rows_reordered), adapter);
 }
 
-static void
-_build_variable_name_list(gpointer key, gpointer value, gpointer user_data)
-{
-     GList **name_list = (GList**)user_data;
-     *name_list = g_list_append(*name_list, key);
-}
-
 /* @@fixme: i18n. **/
 /* @@fixme: non-staticize. **/
-static char* gnc_sx_instance_type_names[] = {
+static char* gnc_sx_instance_state_names[] = {
      ("Ignored"),
      ("Postponed"),
      ("To-Create"),
@@ -317,15 +359,37 @@
      NULL
 };
 
+static GtkTreeModel* _singleton_slr_state_model = NULL;
+
+GtkTreeModel*
+gnc_sx_get_slr_state_model(void)
+{
+     if (_singleton_slr_state_model == NULL)
+     {
+          int i;
+          GtkTreeIter iter;
+
+          _singleton_slr_state_model = GTK_TREE_MODEL(gtk_list_store_new(1, G_TYPE_STRING));
+          for (i = 0; gnc_sx_instance_state_names[i] != NULL; i++)
+          {
+               gtk_list_store_insert_with_values(GTK_LIST_STORE(_singleton_slr_state_model),
+                                                 &iter,
+                                                 SX_INSTANCE_STATE_MAX_STATE + 1,
+                                                 0, gnc_sx_instance_state_names[i], -1);
+          }
+     }
+     return _singleton_slr_state_model;
+}
+
 static void
 gsslrtma_populate_tree_store(GncSxSlrTreeModelAdapter *model)
 {
-     GtkTreeIter sx_iter;
-     GList *list;
+     GtkTreeIter sx_tree_iter;
+     GList *sx_iter;
 
-     for (list = model->instances->sx_instance_list; list != NULL; list = list->next)
+     for (sx_iter = model->instances->sx_instance_list; sx_iter != NULL; sx_iter = sx_iter->next)
      {
-          GncSxInstances *instances = (GncSxInstances*)list->data;
+          GncSxInstances *instances = (GncSxInstances*)sx_iter->data;
           FreqSpec *fs;
           GString *frequency_str;
           char last_occur_date_buf[MAX_DATE_LENGTH+1];
@@ -351,11 +415,13 @@
 
           qof_print_gdate(next_occur_date_buf, MAX_DATE_LENGTH, &instances->next_instance_date);
 
-          gtk_tree_store_append(model->real, &sx_iter, NULL);
-          gtk_tree_store_set(model->real, &sx_iter,
-                             0, xaccSchedXactionGetName(instances->sx),
-                             1, NULL,
-                             2, NULL,
+          gtk_tree_store_append(model->real, &sx_tree_iter, NULL);
+          gtk_tree_store_set(model->real, &sx_tree_iter,
+                             SLR_MODEL_COL_NAME, xaccSchedXactionGetName(instances->sx),
+                             SLR_MODEL_COL_INSTANCE_STATE, NULL,
+                             SLR_MODEL_COL_VARAIBLE_VALUE, NULL,
+                             SLR_MODEL_COL_INSTANCE_VISIBILITY, FALSE,
+                             SLR_MODEL_COL_VARIABLE_VISIBILITY, FALSE,
                              -1);
           g_string_free(frequency_str, TRUE);
 
@@ -369,39 +435,261 @@
                {
                     GncSxInstance *inst = (GncSxInstance*)inst_iter->data;
                     qof_print_gdate(instance_date_buf, MAX_DATE_LENGTH, &inst->date);
-                    gtk_tree_store_append(model->real, &inst_tree_iter, &sx_iter);
+                    gtk_tree_store_append(model->real, &inst_tree_iter, &sx_tree_iter);
                     gtk_tree_store_set(model->real, &inst_tree_iter,
-                                       0, instance_date_buf,
-                                       1, gnc_sx_instance_type_names[inst->type],
-                                       2, NULL,
+                                       SLR_MODEL_COL_NAME, instance_date_buf,
+                                       SLR_MODEL_COL_INSTANCE_STATE, gnc_sx_instance_state_names[inst->state],
+                                       SLR_MODEL_COL_VARAIBLE_VALUE, NULL,
+                                       SLR_MODEL_COL_INSTANCE_VISIBILITY, TRUE,
+                                       SLR_MODEL_COL_VARIABLE_VISIBILITY, FALSE,
                                        -1);
 
                     // Insert variable information
                     {
-                         GList *names = NULL, *names_iter;
-                         GtkTreeIter var_iter;
+                         GList *vars = NULL, *var_iter;
+                         GtkTreeIter var_tree_iter;
 
-                         g_hash_table_foreach(inst->variable_bindings, _build_variable_name_list, &names);
-                         for (names_iter = names; names_iter != NULL; names_iter = names_iter->next)
+                         vars = gnc_sx_instance_get_variables(inst);
+                         for (var_iter = vars; var_iter != NULL; var_iter = var_iter->next)
                          {
-                              gtk_tree_store_append(model->real, &var_iter, &inst_tree_iter);
-                              gtk_tree_store_set(model->real, &var_iter,
-                                                 0, (gchar*)names_iter->data,
-                                                 1, NULL,
-                                                 2, "(@fixme - value)"
+                              GncSxVariable *var = (GncSxVariable*)var_iter->data;
+                              GString *tmp_str;
+                              if (gnc_numeric_check(var->value) == GNC_ERROR_OK)
+                              {
+                                   _var_numeric_to_string(&var->value, &tmp_str);
+                              }
+                              else
+                              {
+                                   tmp_str = g_string_new("(need value)");
+                              }
+                              gtk_tree_store_append(model->real, &var_tree_iter, &inst_tree_iter);
+                              gtk_tree_store_set(model->real, &var_tree_iter,
+                                                 SLR_MODEL_COL_NAME, var->name,
+                                                 SLR_MODEL_COL_INSTANCE_STATE, NULL,
+                                                 SLR_MODEL_COL_VARAIBLE_VALUE, tmp_str->str,
+                                                 SLR_MODEL_COL_INSTANCE_VISIBILITY, FALSE,
+                                                 SLR_MODEL_COL_VARIABLE_VISIBILITY, TRUE,
                                                  -1);
+                              g_string_free(tmp_str, TRUE);
                          }
+                         g_list_free(vars);
                     }
                }
           }
      }
 }
 
+GncSxInstances*
+gnc_sx_slr_tree_model_adapter_get_sx_instances(GncSxSlrTreeModelAdapter *model, GtkTreeIter *iter)
+{
+     return _gnc_sx_slr_tree_model_adapter_get_sx_instances(model, iter, TRUE);
+}
+
+static GncSxInstances*
+_gnc_sx_slr_tree_model_adapter_get_sx_instances(GncSxSlrTreeModelAdapter *model, GtkTreeIter *iter, gboolean check_depth)
+{
+     GtkTreePath *path;
+     gint *indices, index;
+     path = gtk_tree_model_get_path(GTK_TREE_MODEL(model), iter);
+     if (check_depth && gtk_tree_path_get_depth(path) != 1)
+     {
+          gtk_tree_path_free(path);
+          return NULL;
+     }
+     indices = gtk_tree_path_get_indices(path);
+     index = indices[0];
+     gtk_tree_path_free(path);
+
+     return (GncSxInstances*)g_list_nth_data(model->instances->sx_instance_list, index);
+}
+
+GncSxInstance*
+gnc_sx_slr_model_get_instance(GncSxSlrTreeModelAdapter *model, GtkTreeIter *iter)
+{
+     return _gnc_sx_slr_model_get_instance(model, iter, TRUE);
+}
+
+static GncSxInstance*
+_gnc_sx_slr_model_get_instance(GncSxSlrTreeModelAdapter *model, GtkTreeIter *iter, gboolean check_depth)
+{
+     GtkTreePath *path;
+     gint *indices, instances_index, instance_index;
+     GncSxInstances *instances;
+     path = gtk_tree_model_get_path(GTK_TREE_MODEL(model), iter);
+     if (check_depth && gtk_tree_path_get_depth(path) != 2)
+     {
+          gtk_tree_path_free(path);
+          return NULL;
+     }
+     indices = gtk_tree_path_get_indices(path);
+     instances_index = indices[0];
+     instance_index = indices[1];
+     gtk_tree_path_free(path);
+
+     instances = (GncSxInstances*)g_list_nth_data(model->instances->sx_instance_list, instances_index);
+     if (instance_index < 0 || instance_index >= g_list_length(instances->list))
+     {
+          return NULL;
+     }
+
+     return (GncSxInstance*)g_list_nth_data(instances->list, instance_index);
+}
+
+gboolean
+gnc_sx_slr_model_get_instance_and_variable(GncSxSlrTreeModelAdapter *model, GtkTreeIter *iter, GncSxInstance **instance_loc, GncSxVariable **var_loc)
+{
+     GtkTreePath *path;
+     gint *indices, variable_index;
+     GncSxInstance *instance;
+     GList *variables;
+
+     instance = _gnc_sx_slr_model_get_instance(model, iter, FALSE);
+     if (instance == NULL)
+     {
+          return FALSE;
+     }
+     variables = gnc_sx_instance_get_variables(instance);
+
+     path = gtk_tree_model_get_path(GTK_TREE_MODEL(model), iter);
+     if (gtk_tree_path_get_depth(path) != 3)
+     {
+          gtk_tree_path_free(path);
+          return FALSE;
+     }
+     indices = gtk_tree_path_get_indices(path);
+     variable_index = indices[2];
+     gtk_tree_path_free(path);
+
+     if (variable_index < 0 || variable_index >= g_list_length(variables))
+     {
+          g_list_free(variables);
+          return FALSE;
+     }
+
+     if (instance_loc != NULL)
+     {
+          *instance_loc = instance;
+     }
+
+     if (var_loc != NULL)
+     {
+          *var_loc = (GncSxVariable*)g_list_nth_data(variables, variable_index);
+     }
+
+     g_list_free(variables);
+     return TRUE;
+}
+
+void
+gnc_sx_slr_model_change_instance_state(GncSxSlrTreeModelAdapter *model, GncSxInstance *instance, GncSxInstanceState new_state)
+{
+     GtkTreePath *path;
+     GtkTreeIter iter;
+     GList *inst_iter;
+     int indices[2];
+
+     indices[0] = g_list_index(model->instances->sx_instance_list, instance->parent);
+     if (indices[0] == -1)
+          return;
+     indices[1] = g_list_index(instance->parent->list, instance);
+     if (indices[1] == -1)
+          return;
+     path = gtk_tree_path_new_from_indices(indices[0], indices[1], -1);
+     gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, path);
+     gtk_tree_path_free(path);
+
+     instance->state = new_state;
+
+     gtk_tree_store_set(model->real, &iter,
+                        SLR_MODEL_COL_INSTANCE_STATE, gnc_sx_instance_state_names[instance->state],
+                        -1);
+
+     // ensure 'remind' constraints are met
+     inst_iter = g_list_find(instance->parent->list, instance);
+     g_assert(inst_iter != NULL);
+     if (instance->state != SX_INSTANCE_STATE_REMINDER)
+     {
+          // iterate backwards, making sure reminders are changed to 'postponed'
+          for (inst_iter = inst_iter->prev; inst_iter != NULL; inst_iter = inst_iter->prev)
+          {
+               GncSxInstance *prev_inst = (GncSxInstance*)inst_iter->data;
+               indices[1] -= 1;
+               if (prev_inst->state != SX_INSTANCE_STATE_REMINDER)
+                    continue;
+
+               prev_inst->state = SX_INSTANCE_STATE_POSTPONED;
+
+               path = gtk_tree_path_new_from_indices(indices[0], indices[1], -1);
+               gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, path);
+               gtk_tree_path_free(path);
+               gtk_tree_store_set(model->real, &iter,
+                                  SLR_MODEL_COL_INSTANCE_STATE, gnc_sx_instance_state_names[prev_inst->state],
+                                  -1);
+          }
+     }
+     else
+     {
+          // iterate forward, make sure transactions are set to 'remind'
+          for (inst_iter = inst_iter->next; inst_iter != NULL; inst_iter = inst_iter->next)
+          {
+               GncSxInstance *next_inst = (GncSxInstance*)inst_iter->data;
+               indices[1] += 1;
+               if (next_inst->state == SX_INSTANCE_STATE_REMINDER)
+                    continue;
+
+               next_inst->state = SX_INSTANCE_STATE_REMINDER;
+
+               path = gtk_tree_path_new_from_indices(indices[0], indices[1], -1);
+               gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, path);
+               gtk_tree_path_free(path);
+               gtk_tree_store_set(model->real, &iter,
+                                  SLR_MODEL_COL_INSTANCE_STATE, gnc_sx_instance_state_names[next_inst->state],
+                                  -1);
+          }
+     }
+}
+
+void
+gnc_sx_slr_model_change_variable(GncSxSlrTreeModelAdapter *model, GncSxInstance *instance, GncSxVariable *variable, gnc_numeric *new_value)
+{
+     GList *variables;
+     GtkTreePath *path;
+     GtkTreeIter iter;
+     int indices[3];
+     GString *tmp_str;
+
+     indices[0] = g_list_index(model->instances->sx_instance_list, instance->parent);
+     if (indices[0] == -1)
+          return;
+     indices[1] = g_list_index(instance->parent->list, instance);
+     if (indices[1] == -1)
+          return;
+     variables = gnc_sx_instance_get_variables(instance);
+     indices[2] = g_list_index(variables, variable);
+     g_list_free(variables);
+     if (indices[2] == -1)
+          return;
+     path = gtk_tree_path_new_from_indices(indices[0], indices[1], indices[2], -1);
+     gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, path);
+     
+     variable->value = *new_value;
+
+     _var_numeric_to_string(&variable->value, &tmp_str);
+     gtk_tree_store_set(model->real, &iter,
+                        SLR_MODEL_COL_VARAIBLE_VALUE, tmp_str->str,
+                        -1);
+     g_string_free(tmp_str, TRUE);
+     gtk_tree_path_free(path);
+}
+
 static void
 gsslrtma_updated_cb(GncSxInstanceModel *instances, gpointer user_data)
 {
-     GncSxSlrTreeModelAdapter *model = GNC_SX_SLR_TREE_MODEL_ADAPTER(user_data);
+     GncSxSlrTreeModelAdapter *model = GNC_SX_SLR_TREE_MODEL_ADAPTER(user_data); 
      printf("update\n");
+     // @@fixme: this should be better about, say, trying to match up changed
+     // instance-state and variable-binding values.  More of a merge
+     // operation than a replace...
      gtk_tree_store_clear(model->real);
      gsslrtma_populate_tree_store(model);
 }
@@ -417,7 +705,6 @@
      return rtn;
 }
 
-
 void
 gnc_sx_sxsincelast_book_opened(void)
 {
@@ -440,6 +727,80 @@
      return 1;
 }
 
+static void
+instance_state_changed_cb(GtkCellRendererText *cell,
+                          const gchar *path,
+                          const gchar *value,
+                          GncSxSinceLastRunDialog *dialog)
+{
+     GtkTreeIter tree_iter;
+     GncSxInstance *inst;
+     int i;
+     GncSxInstanceState new_state;
+     
+     for (i = 0; i < SX_INSTANCE_STATE_MAX_STATE; i++)
+     {
+          if (strcmp(value, gnc_sx_instance_state_names[i]) == 0)
+               break;
+     }
+     if (i == SX_INSTANCE_STATE_MAX_STATE)
+     {
+          printf("unknown value [%s]\n", value);
+          return;
+     }
+     new_state = i;
+
+     if (!gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(dialog->editing_model), &tree_iter, path))
+     {
+          printf("unknown path [%s]\n", path);
+          return;
+     }
+
+     inst = gnc_sx_slr_model_get_instance(dialog->editing_model, &tree_iter);
+     if (inst == NULL)
+     {
+          printf("invalid path [%s]\n", path);
+          return;
+     }
+
+     gnc_sx_slr_model_change_instance_state(dialog->editing_model, inst, new_state);
+}
+
+static void
+variable_value_changed_cb(GtkCellRendererText *cell,
+                          const gchar *path,
+                          const gchar *value,
+                          GncSxSinceLastRunDialog *dialog)
+{
+     GncSxVariable *var;
+     GncSxInstance *inst;
+     GtkTreeIter tree_iter;
+     gnc_numeric parsed_num;
+     char *endStr = NULL;
+
+     printf("variable to [%s] at path [%s]\n", value, path);
+     if (!gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(dialog->editing_model), &tree_iter, path))
+     {
+          printf("invalid path [%s]\n", path);
+          return;
+     }
+
+     if (!gnc_sx_slr_model_get_instance_and_variable(dialog->editing_model, &tree_iter, &inst, &var))
+     {
+          printf("path [%s] doesn't correspond to a valid variable\n", path);
+          return;
+     }
+
+     if (!xaccParseAmount(value, TRUE, &parsed_num, &endStr)
+         || gnc_numeric_check(parsed_num) != GNC_ERROR_OK)
+     {
+          printf("@@fixme: better parse error handling\n");
+          // @fixme: set location (back) to "(need value)"
+          return;
+     }
+     gnc_sx_slr_model_change_variable(dialog->editing_model, inst, var, &parsed_num);
+}
+
 GncSxSinceLastRunDialog*
 gnc_ui_sx_since_last_run_dialog(GncSxInstanceModel *model)
 {
@@ -455,48 +816,529 @@
      dialog->editing_model = gnc_sx_slr_tree_model_adapter_new(model);
 
      {
+          GtkPaned *paned;
+
+          paned = GTK_PANED(glade_xml_get_widget(glade, "paned"));
+          gtk_paned_set_position(paned, 240);
+     }
+
+     {
           GtkCellRenderer *renderer;
           GtkTreeViewColumn *col;
-          int position = -1;
           
           dialog->instance_view = GTK_TREE_VIEW(glade_xml_get_widget(glade, "instance_view"));
           gtk_tree_view_set_model(dialog->instance_view, GTK_TREE_MODEL(dialog->editing_model));
 
           renderer = gtk_cell_renderer_text_new();
-          col = gtk_tree_view_column_new_with_attributes("SX, Instance, Variable", renderer, "text", ++position, NULL);
+          col = gtk_tree_view_column_new_with_attributes("SX, Instance, Variable", renderer,
+                                                         "text", SLR_MODEL_COL_NAME,
+                                                         NULL);
           gtk_tree_view_append_column(dialog->instance_view, col);
 
-          renderer = gtk_cell_renderer_text_new();
-          col = gtk_tree_view_column_new_with_attributes("Instance State", renderer, "text", ++position, NULL);
-          gtk_tree_view_column_set_cell_data_func(col, renderer, _cell_visibility_func, GINT_TO_POINTER(position), NULL);
+
+          renderer = gtk_cell_renderer_combo_new();
+          g_object_set(G_OBJECT(renderer),
+                       "model", gnc_sx_get_slr_state_model(),
+                       "text-column", 0,
+                       "has-entry", FALSE,
+                       "editable", TRUE,
+                       NULL);
+          g_signal_connect(G_OBJECT(renderer),
+                           "edited",
+                           G_CALLBACK(instance_state_changed_cb),
+                           dialog);
+          col = gtk_tree_view_column_new_with_attributes("Instance State", renderer,
+                                                         "text", SLR_MODEL_COL_INSTANCE_STATE,
+                                                         "visible", SLR_MODEL_COL_INSTANCE_VISIBILITY,
+                                                         NULL);
           gtk_tree_view_append_column(dialog->instance_view, col);
 
+          
           renderer = gtk_cell_renderer_text_new();
-          col = gtk_tree_view_column_new_with_attributes("Variable Value", renderer, "text", ++position, NULL);
-          gtk_tree_view_column_set_cell_data_func(col, renderer, _cell_visibility_func, GINT_TO_POINTER(position), NULL);
+          g_object_set(G_OBJECT(renderer),
+                       "editable", TRUE,
+                       NULL);
+          g_signal_connect(G_OBJECT(renderer),
+                           "edited",
+                           G_CALLBACK(variable_value_changed_cb),
+                           dialog);
+          col = gtk_tree_view_column_new_with_attributes("Variable Value", renderer,
+                                                         "text", SLR_MODEL_COL_VARAIBLE_VALUE,
+                                                         "visible", SLR_MODEL_COL_VARIABLE_VISIBILITY,
+                                                         NULL);
           gtk_tree_view_append_column(dialog->instance_view, col);
+
+          gtk_tree_view_expand_all(dialog->instance_view);
      }
 
+     g_signal_connect(G_OBJECT(dialog->dialog), "response", G_CALLBACK(dialog_response_cb), dialog);
+     
      gtk_widget_show_all(dialog->dialog);
 
      return dialog;
 }
 
+static void
+dialog_response_cb(GtkDialog *dialog, gint response_id, GncSxSinceLastRunDialog *app_dialog)
+{
+     switch (response_id)
+     {
+     case GTK_RESPONSE_OK:
+          // @@fixme: create transactions
+          printf("should effect change and stuff\n");
+          // @@fixme validate current state(GError *errs);
+          // - instance state constraints
+          // - required variable binding
+          // - ability to create transactions
+          {
+               GList *unbound_variables;
+               unbound_variables = gnc_sx_slr_model_check_variables(app_dialog->editing_model);
+               printf("%d variables unbound\n", g_list_length(unbound_variables));
+               if (g_list_length(unbound_variables) > 0)
+               {
+                    //printf("%d variables unbound\n", g_list_length(unbound_variables));
+                    // focus first variable
+                    //GtkTreePath *variable_path = _get_path_for_variable(model, instance, variable);
+                    // gtk_tree_view_set_cursor(app_dialog->instance_view, );
+                    return;
+               }
+          }
+          gnc_sx_slr_model_effect_change(app_dialog->editing_model, FALSE, NULL, NULL);
+          /* FALLTHROUGH */
+     case GTK_RESPONSE_CANCEL: 
+          gtk_widget_destroy(GTK_WIDGET(dialog));
+          // @@fixme: destroy models, &c.
+          break;
+     default:
+          printf("unknown response id [%d]\n", response_id);
+          g_assert_not_reached();
+          break;
+     }
+}
 
 static void
-_cell_visibility_func(GtkTreeViewColumn *tree_column,
-                      GtkCellRenderer *cell,
-                      GtkTreeModel *tree_model,
-                      GtkTreeIter *iter,
-                      gpointer data)
+increment_sx_state(GncSxInstance *inst, GDate **last_occur_date, int *instance_count, int *remain_occur_count)
 {
-     GtkTreePath *path;
-     int select_depth, path_depth;
+     if (!g_date_valid(*last_occur_date)
+         || (g_date_valid(*last_occur_date)
+             && g_date_compare(*last_occur_date, &inst->date) <= 0))
+     {
+          *last_occur_date = &inst->date;
+     }
 
-     select_depth = GPOINTER_TO_INT(data);
-     path = gtk_tree_model_get_path(tree_model, iter);
-     path_depth = gtk_tree_path_get_depth(path);
-     // printf("item depth: %d\n", path_depth);
-     g_object_set(G_OBJECT(cell), "visible", path_depth == select_depth ? TRUE : FALSE, NULL);
+     *instance_count = gnc_sx_get_instance_count(inst->parent->sx, inst->temporal_state);
+
+     if (*remain_occur_count > 0)
+     {
+          *remain_occur_count -= 1;
+     }
 }
 
+typedef struct _SxTxnCreationData
+{
+     GncSxInstance *instance;
+     GList **created_txn_guids;
+     GList **creation_errors;
+} SxTxnCreationData;
+
+static gboolean
+_get_template_split_account(GncSxInstance *instance, Split *template_split, Account **split_acct, GList **creation_errors)
+{
+     GUID *acct_guid;
+     kvp_frame *split_kvpf;
+     kvp_value *kvp_val;
+
+     split_kvpf = xaccSplitGetSlots(template_split);
+     /* contains the guid of the split's actual account. */
+     kvp_val = kvp_frame_get_slot_path(split_kvpf,
+                                       GNC_SX_ID,
+                                       GNC_SX_ACCOUNT,
+                                       NULL);
+     if (kvp_val == NULL)
+     {
+          // @@fixme: this should be more of an assert...
+          GString *err = g_string_new("");
+          g_string_printf(err, "Null account kvp value for SX [%s], cancelling creation.",
+                          xaccSchedXactionGetName(instance->parent->sx));
+          *creation_errors = g_list_append(*creation_errors, err);
+          return FALSE;
+     }
+     acct_guid = kvp_value_get_guid( kvp_val );
+     *split_acct = xaccAccountLookup(acct_guid, gnc_get_current_book());
+     if (*split_acct == NULL)
+     {
+          const char *guid_str;
+          GString *err;
+          guid_str = guid_to_string((const GUID*)acct_guid);
+          err = g_string_new("");
+          g_string_printf(err, "Unknown account for guid [%s], cancelling SX [%s] creation.",
+                          guid_str, xaccSchedXactionGetName(instance->parent->sx));
+          g_free((char*)guid_str);
+          *creation_errors = g_list_append(*creation_errors, err);
+          return FALSE;
+     }
+
+     return TRUE;
+}
+
+static void
+_get_sx_formula(GncSxInstance *instance, Split *template_split, gnc_numeric *numeric, GList **creation_errors, const char *formula_key)
+{
+     kvp_frame *split_kvpf;
+     kvp_value *kvp_val;
+     char *formula_str, *parseErrorLoc;
+
+     split_kvpf = xaccSplitGetSlots(template_split);
+     kvp_val = kvp_frame_get_slot_path(split_kvpf,
+                                       GNC_SX_ID,
+                                       formula_key,
+                                       NULL);
+     formula_str = kvp_value_get_string(kvp_val);
+     if (formula_str != NULL && strlen(formula_str) != 0)
+     {
+          GHashTable *parser_vars = gnc_sx_instance_get_variables_for_parser(instance->variable_bindings);
+          if (!gnc_exp_parser_parse_separate_vars(formula_str,
+                                                  numeric,
+                                                  &parseErrorLoc,
+                                                  parser_vars))
+          {
+               GString *err = g_string_new("");
+               g_string_printf(err, "Error parsing SX [%s] key [%s]=formula [%s] at [%s]: %s",
+                               xaccSchedXactionGetName(instance->parent->sx),
+                               formula_key,
+                               formula_str,
+                               parseErrorLoc,
+                               gnc_exp_parser_error_string());
+               *creation_errors = g_list_append(*creation_errors, err);
+          }
+          g_hash_table_destroy(parser_vars);
+     }
+}
+
+static void
+_get_credit_formula(GncSxInstance *instance, Split *template_split, gnc_numeric *credit_num, GList **creation_errors)
+{
+     _get_sx_formula(instance, template_split, credit_num, creation_errors, GNC_SX_CREDIT_FORMULA);
+}
+
+static void
+_get_debit_formula(GncSxInstance *instance, Split *template_split, gnc_numeric *debit_num, GList **creation_errors)
+{
+     _get_sx_formula(instance, template_split, debit_num, creation_errors, GNC_SX_DEBIT_FORMULA);
+}
+
+static gboolean
+create_each_transaction_helper(Transaction *template_txn, void *user_data)
+{
+     Transaction *new_txn;
+     GList *txn_splits, *template_splits;
+     Split *copying_split;
+     gnc_commodity *first_cmdty = NULL;
+     gboolean err_flag = FALSE;
+     SxTxnCreationData *creation_data;
+
+     creation_data = (SxTxnCreationData*)user_data;
+
+     /* FIXME: In general, this should [correctly] deal with errors such
+        as not finding the approrpiate Accounts and not being able to
+        parse the formula|credit/debit strings. */
+
+     new_txn = xaccTransClone(template_txn);
+     xaccTransBeginEdit(new_txn);
+
+     /* clear any copied KVP data */
+     qof_instance_set_slots(QOF_INSTANCE(new_txn), kvp_frame_new());
+
+     xaccTransSetDate(new_txn,
+                      g_date_get_day(&creation_data->instance->date),
+                      g_date_get_month(&creation_data->instance->date),
+                      g_date_get_year(&creation_data->instance->date));
+        
+     /* the accounts and amounts are in the kvp_frames of the splits. */
+     template_splits = xaccTransGetSplitList(template_txn);
+     txn_splits = xaccTransGetSplitList(new_txn);
+     if ((template_splits == NULL) || (txn_splits == NULL))
+     {
+          PERR("\tseen transaction w/o splits. :(");
+          xaccTransDestroy(new_txn);
+          xaccTransCommitEdit(new_txn);
+          return FALSE;
+     }
+
+     for (;
+          txn_splits && template_splits;
+          txn_splits = txn_splits->next, template_splits = template_splits->next)
+     {
+          Split *template_split;
+          Account *split_acct;
+          gnc_commodity *split_cmdty = NULL;
+             
+          /* FIXME: Ick.  This assumes that the split lists will be ordered
+             identically. :( They are, but we'd rather not have to count on
+             it. --jsled */
+          template_split = (Split*)template_splits->data;
+          copying_split = (Split*)txn_splits->data;
+
+          /* clear out any copied Split frame data. */
+          qof_instance_set_slots(QOF_INSTANCE(copying_split), kvp_frame_new());
+
+          if (!_get_template_split_account(creation_data->instance, template_split, &split_acct, creation_data->creation_errors))
+          {
+               err_flag = TRUE;
+               break;
+          }
+             
+          split_cmdty = xaccAccountGetCommodity(split_acct);
+          if (first_cmdty == NULL)
+          {
+               first_cmdty = split_cmdty;
+               xaccTransSetCurrency(new_txn, first_cmdty);
+          }
+
+          xaccAccountBeginEdit(split_acct);
+          xaccAccountInsertSplit(split_acct, copying_split);
+
+          {
+               gnc_numeric credit_num, debit_num, final;
+               gint gncn_error;
+
+               credit_num = gnc_numeric_zero();
+               debit_num = gnc_numeric_zero();
+
+               _get_credit_formula(creation_data->instance, template_split, &credit_num, creation_data->creation_errors);
+               _get_debit_formula(creation_data->instance, template_split, &debit_num, creation_data->creation_errors);
+                       
+               final = gnc_numeric_sub_fixed( debit_num, credit_num );
+                        
+               gncn_error = gnc_numeric_check(final);
+               if (gncn_error != GNC_ERROR_OK) {
+                    GString *err = g_string_new("");
+                    g_string_printf(err, "Error %d in SX [%s] final gnc_numeric value, using 0 instead.", 
+                                    gncn_error,
+                                    xaccSchedXactionGetName(creation_data->instance->parent->sx));
+                    *creation_data->creation_errors = g_list_append(*creation_data->creation_errors, err);
+                    final = gnc_numeric_zero();
+               }
+
+               xaccSplitSetValue(copying_split, final);
+               if (! gnc_commodity_equal(split_cmdty, first_cmdty))
+               {
+                    GString *exchange_rate_var_name = g_string_sized_new(16);
+                    GncSxVariable *exchange_rate_var;
+                    gnc_numeric exchange_rate, amt;
+
+                    /*
+                      GNCPriceDB *price_db = gnc_pricedb_get_db(gnc_get_current_book());
+                      GNCPrice *price;
+
+                      price = gnc_pricedb_lookup_latest(price_db, first_cmdty, split_cmdty);
+                      if (price == NULL)
+                      {
+                      price = gnc_pricedb_lookup_latest(price_db, split_cmdty, first_cmdty);
+                      if (price == NULL)
+                      {
+                      GString *err = g_string_new("");
+                      g_string_printf(err, "could not find pricedb entry for commodity-pair (%s, %s).",
+                      gnc_commodity_get_mnemonic(first_cmdty),
+                      gnc_commodity_get_mnemonic(split_cmdty));
+                      exchange = gnc_numeric_create(1, 1);
+                      *creation_data->creation_errors = g_list_append(*creation_data->creation_errors, err);
+
+                      }
+                      else
+                      {
+                      exchange = gnc_numeric_div(gnc_numeric_create(1,1),
+                      gnc_price_get_value(price),
+                      1000, GNC_HOW_RND_ROUND);
+                      }
+                      }
+                      else
+                      {
+                      exchange = gnc_price_get_value(price);
+                      }
+                    */
+
+                    exchange_rate = gnc_numeric_zero();
+                    g_string_printf(exchange_rate_var_name, "%s -> %s",
+                                    gnc_commodity_get_mnemonic(split_cmdty),
+                                    gnc_commodity_get_mnemonic(first_cmdty));
+                    exchange_rate_var = (GncSxVariable*)g_hash_table_lookup(creation_data->instance->variable_bindings,
+                                                                            exchange_rate_var_name->str);
+                    if (exchange_rate_var != NULL)
+                    {
+                         exchange_rate = exchange_rate_var->value;
+                    }
+                    g_string_free(exchange_rate_var_name, TRUE);
+
+                    amt = gnc_numeric_mul(final, exchange_rate, 1000, GNC_HOW_RND_ROUND);
+                    xaccSplitSetAmount(copying_split, amt);
+               }
+
+               xaccSplitScrub(copying_split);
+          }
+
+          xaccAccountCommitEdit(split_acct);
+     }
+
+     if (err_flag)
+     {
+          PERR("Some error in new transaction creation...");
+          xaccTransDestroy(new_txn);
+          xaccTransCommitEdit(new_txn);
+          return FALSE;
+     }
+
+     {
+          kvp_frame *txn_frame;
+          /* set a kvp-frame element in the transaction indicating and
+           * pointing-to the SX this was created from. */
+          txn_frame = xaccTransGetSlots(new_txn);
+          kvp_frame_set_guid(txn_frame, "from-sched-xaction", xaccSchedXactionGetGUID(creation_data->instance->parent->sx));
+     }
+
+     xaccTransCommitEdit(new_txn);
+
+     if (creation_data->created_txn_guids != NULL)
+     {
+          *creation_data->created_txn_guids
+               = g_list_append(*(creation_data->created_txn_guids), (gpointer)xaccTransGetGUID(new_txn));
+     }
+
+     return TRUE;
+}
+
+static void
+create_transactions_for_instance(GncSxInstance *instance, GList **created_txn_guids, GList **creation_errors)
+{
+     SxTxnCreationData creation_data;
+     Account *sx_template_account;
+
+     sx_template_account = gnc_sx_get_template_transaction_account(instance->parent->sx);
+
+     creation_data.instance = instance;
+     creation_data.created_txn_guids = created_txn_guids;
+     creation_data.creation_errors = creation_errors;
+
+     xaccAccountForEachTransaction(sx_template_account,
+                                   create_each_transaction_helper,
+                                   &creation_data);
+}
+
+/**
+ * @param auto_create_only Will only affect auto-create transactions; the
+ * rest of the state will be left alone.
+ **/
+void
+gnc_sx_slr_model_effect_change(GncSxSlrTreeModelAdapter *model,
+                               gboolean auto_create_only,
+                               GList **created_transaction_guids,
+                               GList **creation_errors)
+{
+     GList *list;
+
+     // @@fixme engine event supression
+
+     for (list = model->instances->sx_instance_list; list != NULL; list = list->next)
+     {
+          GList *instance_list;
+          GncSxInstances *instances = (GncSxInstances*)list->data;
+          GDate *last_occur_date;
+          gint instance_count = 0;
+          gint remain_occur_count = 0;
+
+          last_occur_date = xaccSchedXactionGetLastOccurDate(instances->sx);
+          instance_count = gnc_sx_get_instance_count(instances->sx, NULL);
+          remain_occur_count = xaccSchedXactionGetRemOccur(instances->sx);
+
+          for (instance_list = instances->list; instance_list != NULL; instance_list = instance_list->next)
+          {
+               GncSxInstance *inst = (GncSxInstance*)instance_list->data;
+               gboolean sx_is_auto_create, sx_is_notify;
+
+               xaccSchedXactionGetAutoCreate(inst->parent->sx, &sx_is_auto_create, &sx_is_notify);
+               if (auto_create_only
+                   && sx_is_auto_create
+                   && inst->state != SX_INSTANCE_STATE_TO_CREATE)
+               {
+                    printf("@@fixme: auto-create behavior\n");
+                    break;
+               }
+
+               if (inst->orig_state == SX_INSTANCE_STATE_POSTPONED
+                   && inst->state != SX_INSTANCE_STATE_POSTPONED)
+               {
+                    // remove from postponed list
+                    g_assert(inst->temporal_state != NULL);
+                    gnc_sx_remove_defer_instance(inst->parent->sx, inst->temporal_state);
+               }
+
+               switch (inst->state)
+               {
+               case SX_INSTANCE_STATE_IGNORED:
+                    increment_sx_state(inst, &last_occur_date, &instance_count, &remain_occur_count);
+                    break;
+               case SX_INSTANCE_STATE_POSTPONED:
+                    if (inst->orig_state != SX_INSTANCE_STATE_POSTPONED)
+                    {
+                         gnc_sx_add_defer_instance(instances->sx, inst->temporal_state);
+                    }
+                    increment_sx_state(inst, &last_occur_date, &instance_count, &remain_occur_count);
+                    break;
+               case SX_INSTANCE_STATE_TO_CREATE:
+                    create_transactions_for_instance(inst, created_transaction_guids, creation_errors);
+                    increment_sx_state(inst, &last_occur_date, &instance_count, &remain_occur_count);
+                    break;
+               case SX_INSTANCE_STATE_REMINDER:
+                    // do nothing
+                    // assert no non-remind instances after this?
+                    break;
+               default:
+                    g_assert_not_reached();
+                    break;
+               }
+          }
+          
+          xaccSchedXactionSetLastOccurDate(instances->sx, last_occur_date);
+          gnc_sx_set_instance_count(instances->sx, instance_count);
+          xaccSchedXactionSetRemOccur(instances->sx, remain_occur_count);
+     }
+
+     // @fixme: re-generate instance model, repopulate [?]
+}
+
+static void
+_list_from_hash_elts(gpointer key, gpointer value, GList **result_list)
+{
+     *result_list = g_list_append(*result_list, value);
+}
+
+GList*
+gnc_sx_slr_model_check_variables(GncSxSlrTreeModelAdapter *model)
+{
+     GList *rtn = NULL;
+     GList *sx_iter, *inst_iter, *var_list = NULL, *var_iter;
+
+     for (sx_iter = model->instances->sx_instance_list; sx_iter != NULL; sx_iter = sx_iter->next)
+     {
+          GncSxInstances *instances = (GncSxInstances*)sx_iter->data;
+          for (inst_iter = instances->list; inst_iter != NULL; inst_iter = inst_iter->next)
+          {
+               GncSxInstance *inst = (GncSxInstance*)inst_iter->data;
+               g_hash_table_foreach(inst->variable_bindings, (GHFunc)_list_from_hash_elts, &var_list);
+               for (var_iter = var_list; var_iter != NULL; var_iter = var_iter->next)
+               {
+                    GncSxVariable *var = (GncSxVariable*)var_iter->data;
+                    if (gnc_numeric_check(var->value) != GNC_ERROR_OK)
+                    {
+                         GncSxSlrVariableNeeded *need = g_new0(GncSxSlrVariableNeeded, 1);
+                         need->instance = inst;
+                         need->variable = var;
+                         rtn = g_list_append(rtn, need);
+                    }
+               }
+               g_list_free(var_list);
+               var_list = NULL;
+          }
+     }
+     return rtn;
+}

Modified: gnucash/branches/sx-cleanup/src/gnome/glade/sched-xact.glade
===================================================================
--- gnucash/branches/sx-cleanup/src/gnome/glade/sched-xact.glade	2006-09-27 20:36:56 UTC (rev 14908)
+++ gnucash/branches/sx-cleanup/src/gnome/glade/sched-xact.glade	2006-09-29 18:55:23 UTC (rev 14909)
@@ -8544,10 +8544,10 @@
       </child>
 
       <child>
-	<widget class="GtkVPaned" id="vpaned1">
+	<widget class="GtkVPaned" id="paned">
 	  <property name="visible">True</property>
 	  <property name="can_focus">True</property>
-	  <property name="position">0</property>
+	  <property name="position">240</property>
 
 	  <child>
 	    <widget class="GtkScrolledWindow" id="scrolledwindow21">

Modified: gnucash/branches/sx-cleanup/src/gnome/gnc-plugin-page-sx-list.c
===================================================================
--- gnucash/branches/sx-cleanup/src/gnome/gnc-plugin-page-sx-list.c	2006-09-27 20:36:56 UTC (rev 14908)
+++ gnucash/branches/sx-cleanup/src/gnome/gnc-plugin-page-sx-list.c	2006-09-29 18:55:23 UTC (rev 14909)
@@ -93,14 +93,13 @@
 static void gnc_sx_instance_model_init(GTypeInstance *instance, gpointer klass);
 static GncSxInstanceModel* gnc_sx_instance_model_new(void);
 
-static GncSxInstance* gnc_sx_instance_new(GncSxInstances *parent, GncSxInstanceType type, GDate *date, gint sequence_num);
+static GncSxInstance* gnc_sx_instance_new(GncSxInstances *parent, GncSxInstanceState state, GDate *date, void *temporal_state, gint sequence_num);
 
 static void sxsl_get_sx_vars(SchedXaction *sx, GHashTable *var_hash);
 static gint _get_vars_helper(Transaction *txn, void *var_hash_data);
-static void clear_variable_numerics(gpointer key, gpointer value, gpointer data);
 static int parse_vars_from_formula(const char *formula, GHashTable *varHash, gnc_numeric *result);
 
-static void gnc_util_copy_hash_table(GHashTable *from, GHashTable *to);
+static GncSxVariable* gnc_sx_variable_new(gchar *name);
 
 static void _gnc_sx_instance_event_handler(QofEntity *ent, QofEventId event_type, gpointer user_data, gpointer evt_data);
 
@@ -374,6 +373,17 @@
      gtk_action_set_sensitive(delete_action, selection_state);
 }
 
+#if 0
+static GtkTreeSortable*
+_setup_sortable_model(GtkTreeModel *model)
+{
+     GtkTreeModelSort *sortable;
+
+     sortable = gtk_tree_model_sort_new_with_model(model);
+     gtk_tree_model
+}
+#endif // 0
+
 static GtkWidget *
 gnc_plugin_page_sx_list_create_widget (GncPluginPage *plugin_page)
 {
@@ -411,10 +421,12 @@
           GtkCellRenderer *renderer;
           GtkTreeViewColumn *column;
           GtkTreeSelection *selection;
+          //GtkTreeModel *sortable_model;
 
           priv->tree_model = gnc_sx_list_tree_model_adapter_new(priv->instances);
           priv->tree_view = GTK_TREE_VIEW(glade_xml_get_widget(priv->gxml, "sx_list"));
-          gtk_tree_view_set_model(priv->tree_view, GTK_TREE_MODEL(priv->tree_model));
+          //sortable_model = _setup_sortable_model(GTK_TREE_MODEL(priv->tree_model));
+          gtk_tree_view_set_model(priv->tree_view, GTK_TREE_MODEL(priv->tree_model)); // GTK_TREE_MODEL(sortable_model));
 
           renderer = gtk_cell_renderer_text_new();
           column = gtk_tree_view_column_new_with_attributes("Name", renderer, "text", 0, NULL);
@@ -763,20 +775,64 @@
 
 /* ------------------------------------------------------------ */
 
+static void
+_sx_var_to_raw_numeric(gchar *name, GncSxVariable *var, GHashTable *parser_var_hash)
+{
+     g_hash_table_insert(parser_var_hash, name, &var->value);
+}
+
+static void
+_var_numeric_to_sx_var(gchar *name, gnc_numeric *num, GHashTable *sx_var_hash)
+{
+     gpointer p_var;
+     if (!g_hash_table_lookup_extended(sx_var_hash, name, NULL, &p_var))
+     {
+          p_var = (gpointer)gnc_sx_variable_new(name);
+          g_hash_table_insert(sx_var_hash, name, p_var);
+     }
+     ((GncSxVariable*)p_var)->value = *num;
+}
+
+static void
+_wipe_parsed_sx_var(gchar *key, GncSxVariable *var, gpointer unused_user_data)
+{
+     var->value = gnc_numeric_error(GNC_ERROR_ARG);
+}
+
+/**
+ * @return caller-owned.
+ **/
+GHashTable*
+gnc_sx_instance_get_variables_for_parser(GHashTable *instance_var_hash)
+{
+     GHashTable *parser_vars;
+     parser_vars = g_hash_table_new(g_str_hash, g_str_equal);
+     g_hash_table_foreach(instance_var_hash, (GHFunc)_sx_var_to_raw_numeric, parser_vars);
+     return parser_vars;
+}
+
 static int
 parse_vars_from_formula(const char *formula,
-                        GHashTable *varHash,
+                        GHashTable *var_hash,
                         gnc_numeric *result)
 {
      gnc_numeric num;
      char *errLoc;
      int toRet = 0;
+     GHashTable *parser_vars;
 
-     if (!gnc_exp_parser_parse_separate_vars(formula, &num, &errLoc, varHash))
+     // convert var_hash -> variables for the parser.
+     parser_vars = gnc_sx_instance_get_variables_for_parser(var_hash);
+
+     if (!gnc_exp_parser_parse_separate_vars(formula, &num, &errLoc, parser_vars))
      {
           toRet = -1;
      }
 
+     // convert back.
+     g_hash_table_foreach(parser_vars, (GHFunc)_var_numeric_to_sx_var, var_hash);
+     g_hash_table_destroy(parser_vars);
+
      if (result != NULL)
      {
           *result = num;
@@ -785,11 +841,14 @@
      return toRet;
 }
 
-static void
-clear_variable_numerics(gpointer key, gpointer value, gpointer data)
+static GncSxVariable*
+gnc_sx_variable_new(gchar *name)
 {
-     g_free((gnc_numeric*)value);
-     g_hash_table_insert((GHashTable*)data, key, NULL);
+     GncSxVariable *var = g_new0(GncSxVariable, 1);
+     var->name = name;
+     var->value = gnc_numeric_error(GNC_ERROR_ARG);
+     var->editable = TRUE;
+     return var;
 }
 
 static gint
@@ -831,14 +890,15 @@
                 
           if (! gnc_commodity_equal(split_cmdty, first_cmdty))
           {
-               gnc_numeric *tmp_num;
-               GString *var_name = g_string_sized_new(16);
+               GncSxVariable *var;
+               GString *var_name;
+
+               var_name = g_string_sized_new(16);
                g_string_printf(var_name, "%s -> %s",
                                gnc_commodity_get_mnemonic(split_cmdty),
                                gnc_commodity_get_mnemonic(first_cmdty));
-               tmp_num = g_new0(gnc_numeric, 1);
-               *tmp_num = gnc_numeric_create(0, 1);
-               g_hash_table_insert(var_hash, g_strdup(var_name->str), tmp_num);
+               var = gnc_sx_variable_new(g_strdup(var_name->str));
+               g_hash_table_insert(var_hash, var->name, var);
                g_string_free(var_name, TRUE);
           }
 
@@ -873,8 +933,8 @@
      return 0;
 }
 
-static void
-sxsl_get_sx_vars(SchedXaction *sx, GHashTable *var_hash)
+Account*
+gnc_sx_get_template_transaction_account(SchedXaction *sx)
 {
      AccountGroup *template_group;
      Account *sx_template_acct;
@@ -884,31 +944,35 @@
      sx_guid_str = guid_to_string(xaccSchedXactionGetGUID(sx));
      /* Get account named after guid string. */
      sx_template_acct = xaccGetAccountFromName(template_group, sx_guid_str);
-     xaccAccountForEachTransaction(sx_template_acct, _get_vars_helper, var_hash);
-
-     // @@fixme - This should actually create GncSxVariable structures.
-     g_hash_table_foreach(var_hash, clear_variable_numerics, (gpointer)var_hash);
+     return sx_template_acct;
 }
 
 static void
-_clone_hash_entry(gpointer key, gpointer value, gpointer user_data)
+sxsl_get_sx_vars(SchedXaction *sx, GHashTable *var_hash)
 {
-     GHashTable *to = (GHashTable*)user_data;
-     g_hash_table_insert(to, key, value);
+     Account *sx_template_acct;
+     sx_template_acct = gnc_sx_get_template_transaction_account(sx);
+     xaccAccountForEachTransaction(sx_template_acct, _get_vars_helper, var_hash);
 }
 
 static void
-gnc_util_copy_hash_table(GHashTable *from, GHashTable *to)
+_clone_sx_var_hash_entry(gpointer key, gpointer value, gpointer user_data)
 {
-     g_hash_table_foreach(from, _clone_hash_entry, (gpointer)to);
+     GHashTable *to = (GHashTable*)user_data;
+     GncSxVariable *to_copy = (GncSxVariable*)value;
+     GncSxVariable *var = gnc_sx_variable_new(to_copy->name);
+     var->value = to_copy->value;
+     var->editable = to_copy->editable;
+     g_hash_table_insert(to, key, var);
 }
 
 static GncSxInstance*
-gnc_sx_instance_new(GncSxInstances *parent, GncSxInstanceType type, GDate *date, gint sequence_num)
+gnc_sx_instance_new(GncSxInstances *parent, GncSxInstanceState state, GDate *date, void *temporal_state, gint sequence_num)
 {
      GncSxInstance *rtn = g_new0(GncSxInstance, 1);
      rtn->parent = parent;
-     rtn->type = type;
+     rtn->orig_state = state;
+     rtn->state = state;
      g_date_clear(&rtn->date, 1);
      rtn->date = *date;
 
@@ -916,16 +980,34 @@
      {
           parent->variable_names = g_hash_table_new(g_str_hash, g_str_equal);
           sxsl_get_sx_vars(parent->sx, parent->variable_names);
+          g_hash_table_foreach(parent->variable_names, (GHFunc)_wipe_parsed_sx_var, NULL);
           parent->variable_names_parsed = TRUE;
 
           // @@fixme: add sequence_num as `i`, non-editable
      }
 
      rtn->variable_bindings = g_hash_table_new(g_str_hash, g_str_equal);
-     gnc_util_copy_hash_table(parent->variable_names, rtn->variable_bindings);
+     g_hash_table_foreach(parent->variable_names, _clone_sx_var_hash_entry, rtn->variable_bindings);
      return rtn;
 }
 
+static void
+_build_list_from_hash_elts(gpointer key, gpointer value, gpointer user_data)
+{
+     GList **list = (GList**)user_data;
+     *list = g_list_append(*list, value);
+}
+
+GList *
+gnc_sx_instance_get_variables(GncSxInstance *inst)
+{
+     GList *vars = NULL;
+     g_hash_table_foreach(inst->variable_bindings, _build_list_from_hash_elts, &vars);
+     
+     // @@fixme sort by name
+     return vars;
+}
+
 static GncSxInstances*
 _gnc_sx_gen_instances(gpointer *data, gpointer user_data)
 {
@@ -954,7 +1036,8 @@
 
                inst_date = xaccSchedXactionGetNextInstance(sx, postponed->data);
                seq_num = gnc_sx_get_instance_count(sx, postponed->data);
-               inst = gnc_sx_instance_new(instances, POSTPONED, &inst_date, seq_num);
+               inst = gnc_sx_instance_new(instances, SX_INSTANCE_STATE_POSTPONED, &inst_date, postponed->data, seq_num);
+               //inst->sx_temporal_state = postponed->data;
                instances->list = g_list_append(instances->list, inst);
           }
      }
@@ -969,7 +1052,7 @@
           GncSxInstance *inst;
           int seq_num;
           seq_num = gnc_sx_get_instance_count(sx, sequence_ctx);
-          inst = gnc_sx_instance_new(instances, TO_CREATE, &cur_date, seq_num);
+          inst = gnc_sx_instance_new(instances, SX_INSTANCE_STATE_TO_CREATE, &cur_date, sequence_ctx, seq_num);
           instances->list = g_list_append(instances->list, inst);
           gnc_sx_incr_temporal_state(sx, sequence_ctx);
           cur_date = xaccSchedXactionGetInstanceAfter(sx, &cur_date, sequence_ctx);
@@ -981,7 +1064,7 @@
           GncSxInstance *inst;
           int seq_num;
           seq_num = gnc_sx_get_instance_count(sx, sequence_ctx);
-          inst = gnc_sx_instance_new(instances, REMINDER, &cur_date, seq_num);
+          inst = gnc_sx_instance_new(instances, SX_INSTANCE_STATE_REMINDER, &cur_date, sequence_ctx, seq_num);
           instances->list = g_list_append(instances->list, inst);
           gnc_sx_incr_temporal_state(sx, sequence_ctx);
           cur_date = xaccSchedXactionGetInstanceAfter(sx, &cur_date, sequence_ctx);

Modified: gnucash/branches/sx-cleanup/src/gnome/gnc-plugin-page-sx-list.h
===================================================================
--- gnucash/branches/sx-cleanup/src/gnome/gnc-plugin-page-sx-list.h	2006-09-27 20:36:56 UTC (rev 14908)
+++ gnucash/branches/sx-cleanup/src/gnome/gnc-plugin-page-sx-list.h	2006-09-29 18:55:23 UTC (rev 14909)
@@ -53,11 +53,13 @@
 
 
 /* typedefs & structures */
-typedef struct {
+typedef struct
+{
      GncPluginPage gnc_plugin_page;
 } GncPluginPageSxList;
 
-typedef struct {
+typedef struct
+{
      GncPluginPageClass gnc_plugin_page;
 } GncPluginPageSxListClass;
 
@@ -67,12 +69,12 @@
  * Retrieve the type number for an "sx list" plugin page.
  * @return The type number.
  */
-GType gnc_plugin_page_sx_list_get_type (void);
+GType gnc_plugin_page_sx_list_get_type(void);
 
 /**
  * @return The newly created plugin page.
  **/
-GncPluginPage *gnc_plugin_page_sx_list_new  (void);
+GncPluginPage *gnc_plugin_page_sx_list_new(void);
 
 
 /** ------------------------------------------------------------ **/
@@ -109,36 +111,54 @@
 typedef struct _GncSxInstances
 {
      SchedXaction *sx;
-     GHashTable /** <char*,NULL> **/ *variable_names;
+     GHashTable /** <name:char*,GncSxVariable*> **/ *variable_names;
      gboolean variable_names_parsed;
      
      GDate next_instance_date;
      
      /** GList<GncSxInstance*> **/
-     GList *list;
+     GList *list; // @fixme: s/list/?/
 } GncSxInstances;
 
 typedef enum 
 {
-     IGNORED, POSTPONED, TO_CREATE, REMINDER, MAX_STATE
-} GncSxInstanceType;
+     SX_INSTANCE_STATE_IGNORED,
+     SX_INSTANCE_STATE_POSTPONED,
+     SX_INSTANCE_STATE_TO_CREATE,
+     SX_INSTANCE_STATE_REMINDER,
+     SX_INSTANCE_STATE_MAX_STATE
+} GncSxInstanceState;
 
 typedef struct _GncSxVariable
 {
-     GString *name;
-     GString *value;
+     gchar *name;
+     gnc_numeric value; /**< only numeric values are supported. **/
      gboolean editable;
 } GncSxVariable;
 
 typedef struct _GncSxInstance
 {
-     GncSxInstances *parent;
-     GncSxInstanceType type;
-     GDate date;
-     GHashTable *variable_bindings;
+     GncSxInstances *parent; /**< the parent instances collection. **/
+     void *temporal_state; /**< the sx creation temporal state. **/
+     GncSxInstanceState orig_state; /**< the original state at generation time. **/
+     GncSxInstanceState state; /**< the current state of the instance (during editing) **/
+     GDate date; /**< the instance date. **/
+     GHashTable *variable_bindings; /**< variable bindings. **/
 } GncSxInstance;
 
 GncSxInstanceModel* gnc_sx_get_instances(GDate *range_end);
+
+/** @return GList<GncSxVariable*> **/
+GList *gnc_sx_instance_get_variables(GncSxInstance *inst);
+
+Account* gnc_sx_get_template_transaction_account(SchedXaction *sx);
+
+/**
+ * @return caller-owned.
+ **/
+GHashTable* gnc_sx_instance_get_variables_for_parser(GHashTable *instance_var_hash);
+
+
 /** ------------------------------------------------------------ **/
 
 G_END_DECLS



More information about the gnucash-changes mailing list