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