Merge Bob Fewell's 'bug799179' into stable.

This commit is contained in:
John Ralls 2024-02-05 10:38:37 -08:00
commit ef094082b1
2 changed files with 375 additions and 174 deletions

View File

@ -63,6 +63,7 @@ G_GNUC_UNUSED static QofLogModule log_module = GNC_MOD_GUI_SX;
#define GNC_PREF_SET_REVIEW "review-transactions"
#define GNC_PREF_SLR_SORT_COL "sort-column"
#define GNC_PREF_SLR_SORT_ASC "sort-ascending"
#define GNC_PREF_SLR_SORT_DEPTH "sort-depth"
struct _GncSxSinceLastRunDialog
{
@ -74,6 +75,7 @@ struct _GncSxSinceLastRunDialog
GList *created_txns;
GtkCellEditable *temp_ce; // used when editing values
gint sort_selection_depth; // used when sorting transaction column
};
/* ------------------------------------------------------------ */
@ -103,10 +105,7 @@ static void gnc_sx_slr_tree_model_adapter_dispose (GObject *obj);
static void gnc_sx_slr_tree_model_adapter_finalize (GObject *obj);
GncSxInstanceModel* gnc_sx_slr_tree_model_adapter_get_instance_model (GncSxSlrTreeModelAdapter *slr_model);
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,
@ -124,7 +123,7 @@ void gnc_sx_slr_model_effect_change (GncSxSlrTreeModelAdapter *model,
GList **created_transaction_guids,
GList **creation_errors);
GtkTreeModel* gnc_sx_get_slr_state_model(void);
GtkTreeModel* gnc_sx_get_slr_state_model (void);
#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))
@ -141,6 +140,12 @@ static void close_handler (gpointer user_data);
static void dialog_destroy_cb (GtkWidget *object, GncSxSinceLastRunDialog *app_dialog);
static void dialog_response_cb (GtkDialog *dialog, gint response_id, GncSxSinceLastRunDialog *app_dialog);
#define debug_path(fn, text, path) {\
gchar *path_string = gtk_tree_path_to_string (path);\
fn("%s %s", text, path_string? path_string : "NULL");\
g_free (path_string);\
}
/* ------------------------------------------------------------ */
static void
@ -334,6 +339,7 @@ gsslrtma_proxy_rows_reordered (GtkTreeModel *treemodel,
enum
{
SLR_MODEL_COL_NAME = 0,
SLR_MODEL_COL_INSTANCE_PTR,
SLR_MODEL_COL_INSTANCE_STATE,
SLR_MODEL_COL_VARAIBLE_VALUE,
SLR_MODEL_COL_INSTANCE_VISIBILITY,
@ -345,11 +351,11 @@ enum
static void
gnc_sx_slr_tree_model_adapter_init (GncSxSlrTreeModelAdapter *adapter)
{
// columns: thing-name, instance-state, variable-value, instance-visible, variable-visible, instance_state_sensitivity, date
// at depth=0: <sx>, N/A, N/A, N/A N/A, N/A N/A
// at depth=1: <instance>, <state>, N/A, <valid>, N/A, <valid> <date>
// at depth=2: <variable>, N/A, <value>, N/A, <valid>, N/A N/A
adapter->real = gtk_tree_store_new (7, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, G_TYPE_INT64);
// columns: thing-name, ptr, instance-state, variable-value, instance-visible, variable-visible, instance_state_sensitivity, date
// at depth=0: <sx>, instances, N/A, N/A N/A, N/A N/A N/A
// at depth=1: <instance>, instance, <state>, N/A, <valid>, N/A, <valid> <date>
// at depth=2: <variable>, var, N/A, <value>, N/A, <valid>, N/A N/A
adapter->real = gtk_tree_store_new (8, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, G_TYPE_INT64);
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);
@ -454,8 +460,16 @@ gsslrtma_populate_tree_store (GncSxSlrTreeModelAdapter *model)
SLR_MODEL_COL_VARIABLE_VISIBILITY, FALSE,
SLR_MODEL_COL_INSTANCE_STATE_SENSITIVITY, FALSE,
SLR_MODEL_COL_INSTANCE_DATE, INT64_MAX,
SLR_MODEL_COL_INSTANCE_PTR, instances,
-1);
if (qof_log_check (GNC_MOD_GUI_SX, QOF_LOG_DEBUG))
{
gchar *path_str = gtk_tree_path_to_string (gtk_tree_model_get_path (GTK_TREE_MODEL(model->real), &sx_tree_iter));
DEBUG("Add schedule [%s], instances %p at path [%s]", xaccSchedXactionGetName (instances->sx), instances, path_str);
g_free (path_str);
}
// Insert instance information
{
GList *inst_iter;
@ -481,6 +495,7 @@ gsslrtma_populate_tree_store (GncSxSlrTreeModelAdapter *model)
SLR_MODEL_COL_VARIABLE_VISIBILITY, FALSE,
SLR_MODEL_COL_INSTANCE_STATE_SENSITIVITY, inst->state != SX_INSTANCE_STATE_CREATED,
SLR_MODEL_COL_INSTANCE_DATE, t,
SLR_MODEL_COL_INSTANCE_PTR, inst,
-1);
// Insert variable information
@ -521,6 +536,7 @@ gsslrtma_populate_tree_store (GncSxSlrTreeModelAdapter *model)
SLR_MODEL_COL_VARIABLE_VISIBILITY, TRUE,
SLR_MODEL_COL_INSTANCE_STATE_SENSITIVITY, FALSE,
SLR_MODEL_COL_INSTANCE_DATE, INT64_MAX,
SLR_MODEL_COL_INSTANCE_PTR, var,
-1);
g_string_free (tmp_str, TRUE);
}
@ -543,30 +559,6 @@ gnc_sx_slr_tree_model_adapter_get_instance_model (GncSxSlrTreeModelAdapter *slr_
return slr_model->instances;
}
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 (gnc_sx_instance_model_get_sx_instances_list (model->instances), index);
}
GncSxInstance*
gnc_sx_slr_model_get_instance (GncSxSlrTreeModelAdapter *model, GtkTreeIter *iter)
{
@ -576,33 +568,51 @@ gnc_sx_slr_model_get_instance (GncSxSlrTreeModelAdapter *model, GtkTreeIter *ite
static GncSxInstance*
_gnc_sx_slr_model_get_instance (GncSxSlrTreeModelAdapter *model, GtkTreeIter *iter, gboolean check_depth)
{
GtkTreePath *path;
GtkTreePath *model_path = gtk_tree_model_get_path (GTK_TREE_MODEL(model), iter);
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)
GncSxInstance *instance = NULL;
GtkTreeIter new_iter;
debug_path (DEBUG, "model path is:", model_path);
if (check_depth && gtk_tree_path_get_depth (model_path) != 2)
{
gtk_tree_path_free (path);
PWARN("path depth not equal to 2");
gtk_tree_path_free (model_path);
return NULL;
}
indices = gtk_tree_path_get_indices (path);
if (gtk_tree_path_get_depth (model_path) == 1)
{
PWARN("path depth equal to 1");
gtk_tree_path_free (model_path);
return NULL;
}
indices = gtk_tree_path_get_indices (model_path);
instances_index = indices[0];
instance_index = indices[1];
gtk_tree_path_free (path);
instances = (GncSxInstances*)g_list_nth_data (gnc_sx_instance_model_get_sx_instances_list (model->instances), instances_index);
if (instance_index < 0 || instance_index >= g_list_length (instances->instance_list))
{
return NULL;
}
gtk_tree_path_free (model_path);
return (GncSxInstance*)g_list_nth_data (instances->instance_list, instance_index);
model_path = gtk_tree_path_new_from_indices (instances_index, instance_index, -1);
debug_path (DEBUG, "new model path is:", model_path);
if (gtk_tree_model_get_iter (GTK_TREE_MODEL(model), &new_iter, model_path))
gtk_tree_model_get (GTK_TREE_MODEL(model), &new_iter, SLR_MODEL_COL_INSTANCE_PTR, &instance, -1);
gtk_tree_path_free (model_path);
DEBUG("instance is %p", instance);
return instance;
}
gboolean
gnc_sx_slr_model_get_instance_and_variable (GncSxSlrTreeModelAdapter *model, GtkTreeIter *iter, GncSxInstance **instance_loc, GncSxVariable **var_loc)
{
GtkTreePath *path;
GtkTreePath *model_path;
gint *indices, variable_index;
GncSxInstance *instance;
GList *variables;
@ -610,22 +620,33 @@ gnc_sx_slr_model_get_instance_and_variable (GncSxSlrTreeModelAdapter *model, Gtk
instance = _gnc_sx_slr_model_get_instance (model, iter, FALSE);
if (instance == NULL)
{
gchar *iter_str = gtk_tree_model_get_string_from_iter (GTK_TREE_MODEL(model), iter);
PWARN("instance is NULL for iter %s", iter_str);
g_free (iter_str);
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)
model_path = gtk_tree_model_get_path (GTK_TREE_MODEL(model), iter);
if (gtk_tree_path_get_depth (model_path) != 3)
{
gtk_tree_path_free (path);
gchar *path_str = gtk_tree_path_to_string (model_path);
PWARN("invalid path [%s] for variable, not at depth 3", path_str);
gtk_tree_path_free (model_path);
g_free (path_str);
return FALSE;
}
indices = gtk_tree_path_get_indices (path);
debug_path (DEBUG, "model path is:", model_path);
indices = gtk_tree_path_get_indices (model_path);
variable_index = indices[2];
gtk_tree_path_free (path);
gtk_tree_path_free (model_path);
if (variable_index < 0 || variable_index >= g_list_length (variables))
{
PWARN("variable index %d out of range", variable_index);
g_list_free (variables);
return FALSE;
}
@ -637,21 +658,12 @@ gnc_sx_slr_model_get_instance_and_variable (GncSxSlrTreeModelAdapter *model, Gtk
if (var_loc != NULL)
{
// *var_loc = (GncSxVariable*)g_list_nth_data(variables, variable_index);
GList *list_iter = variables;
for (; list_iter != NULL; list_iter = list_iter->next)
{
GncSxVariable *var = (GncSxVariable*)list_iter->data;
if (!var->editable)
continue;
if (variable_index-- == 0)
{
*var_loc = var;
break;
}
}
}
GncSxVariable *var;
gtk_tree_model_get (GTK_TREE_MODEL(model), iter, SLR_MODEL_COL_INSTANCE_PTR, &var, -1);
*var_loc = var;
}
g_list_free (variables);
return TRUE;
}
@ -675,30 +687,102 @@ _variable_list_index (GList *variables, GncSxVariable *variable)
return -1;
}
typedef struct _findInstanceData
{
gpointer find_item;
GtkTreePath *found_path;
} FindInstanceData;
static gboolean
for_each_find_item (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer user_data)
{
FindInstanceData* to_find = (FindInstanceData*)user_data;
gpointer item;
gtk_tree_model_get (model, iter, SLR_MODEL_COL_INSTANCE_PTR, &item, -1);
if (item == to_find->find_item)
{
to_find->found_path = gtk_tree_path_copy (path);
return TRUE;
}
return FALSE;
}
static GtkTreePath*
_get_model_path_for_item (GtkTreeModel *model, gpointer find_item)
{
GtkTreePath *model_path = NULL;
FindInstanceData* to_find_data;
to_find_data = (FindInstanceData*)g_new0 (FindInstanceData, 1);
to_find_data->find_item = find_item;
to_find_data->found_path = NULL;
gtk_tree_model_foreach (model, (GtkTreeModelForeachFunc)for_each_find_item, to_find_data);
if (to_find_data->found_path)
{
model_path = gtk_tree_path_copy (to_find_data->found_path);
gtk_tree_path_free (to_find_data->found_path);
}
g_free (to_find_data);
return model_path;
}
static GtkTreePath*
_get_model_path_for_instance (GtkTreeModel *model, GncSxInstance *instance)
{
return _get_model_path_for_item (model, instance);
}
static GtkTreePath*
_get_model_path_for_instances (GtkTreeModel *model, GncSxInstances *instances)
{
return _get_model_path_for_item (model, instances);
}
static GtkTreePath*
_get_path_for_variable (GncSxSinceLastRunDialog *app_dialog, GncSxInstance *instance, GncSxVariable *variable)
{
GList *variables;
int indices[3];
GtkTreePath *path, *child_path;
GncSxSlrTreeModelAdapter *model = app_dialog->editing_model;
GtkTreeModel *sort_model = gtk_tree_view_get_model (app_dialog->instance_view);
gint *indices, instances_index, instance_index, variable_index;
GtkTreePath *view_path, *model_path;
GList *variables;
indices[0] = g_list_index (gnc_sx_instance_model_get_sx_instances_list (model->instances), instance->parent);
if (indices[0] == -1)
return NULL;
indices[1] = g_list_index (instance->parent->instance_list, instance);
if (indices[1] == -1)
model_path = _get_model_path_for_instance (GTK_TREE_MODEL(model), instance);
if (!model_path)
{
PWARN("model path is NULL for instance %p", instance);
return NULL;
}
debug_path (DEBUG, "instance model path is:", model_path);
indices = gtk_tree_path_get_indices (model_path);
instances_index = indices[0];
instance_index = indices[1];
gtk_tree_path_free (model_path);
variables = gnc_sx_instance_get_variables (instance);
indices[2] = _variable_list_index (variables, variable);
variable_index = _variable_list_index (variables, variable);
g_list_free (variables);
if (indices[2] == -1)
if (variable_index == -1)
return NULL;
child_path = gtk_tree_path_new_from_indices (indices[0], indices[1], indices[2], -1);
path = gtk_tree_model_sort_convert_child_path_to_path (GTK_TREE_MODEL_SORT(sort_model), child_path);
gtk_tree_path_free (child_path);
return path;
model_path = gtk_tree_path_new_from_indices (instances_index, instance_index, variable_index, -1);
debug_path (DEBUG, "model variable path is:", model_path);
view_path = gtk_tree_model_sort_convert_child_path_to_path (GTK_TREE_MODEL_SORT(sort_model), model_path);
gtk_tree_path_free (model_path);
debug_path (DEBUG, "return view variable path is:", view_path);
return view_path;
}
static void
@ -718,26 +802,45 @@ gsslrtma_updated_cb (GncSxInstanceModel *instances, SchedXaction *updated_sx, gp
}
static void
gsslrtma_removing_cb (GncSxInstanceModel *instances, SchedXaction *to_remove_sx, gpointer user_data)
gsslrtma_removing_cb (GncSxInstanceModel *inst_model, SchedXaction *to_remove_sx, gpointer user_data)
{
GncSxSlrTreeModelAdapter *model = GNC_SX_SLR_TREE_MODEL_ADAPTER(user_data);
GtkTreePath *model_path;
GtkTreeIter tree_iter;
GList *iter;
int index = 0;
GncSxInstances *instances;
// get index, create path, remove
for (iter = gnc_sx_instance_model_get_sx_instances_list (instances); iter != NULL; iter = iter->next, index++)
for (iter = gnc_sx_instance_model_get_sx_instances_list (inst_model); iter != NULL; iter = iter->next, index++)
{
GncSxInstances *instances = (GncSxInstances*)iter->data;
instances = (GncSxInstances*)iter->data;
if (instances->sx == to_remove_sx)
break;
}
if (iter == NULL)
{
PWARN("could not find sx %p in the model", to_remove_sx);
return; // couldn't find sx in our model, which is weird.
if (!gtk_tree_model_iter_nth_child (GTK_TREE_MODEL(model->real), &tree_iter, NULL, index))
return; // perr(couldn't get something that should exist.
}
model_path = _get_model_path_for_instances (GTK_TREE_MODEL(model), instances);
debug_path (DEBUG, "remove model_path", model_path);
if (!gtk_tree_model_get_iter (GTK_TREE_MODEL(model->real), &tree_iter, model_path))
{
gchar *path_str = gtk_tree_path_to_string (model_path);
PWARN("invalid path [%s] for instances %p to remove", path_str, instances);
gtk_tree_path_free (model_path);
g_free (path_str);
return;
}
gtk_tree_path_free (model_path);
gtk_tree_store_remove (model->real, &tree_iter);
gnc_sx_instance_model_remove_sx_instances (instances, to_remove_sx);
gnc_sx_instance_model_remove_sx_instances (inst_model, to_remove_sx);
}
static void
@ -858,11 +961,12 @@ gnc_sx_sxsincelast_book_opened (void)
static GtkTreePath *
instance_get_model_path (GtkTreeView *view, const gchar *path)
{
GtkTreePath *sort_path = gtk_tree_path_new_from_string (path);
GtkTreePath *view_path = gtk_tree_path_new_from_string (path);
GtkTreeModelSort *sort_model = GTK_TREE_MODEL_SORT(gtk_tree_view_get_model (view));
GtkTreePath *model_path = gtk_tree_model_sort_convert_path_to_child_path (sort_model, sort_path);
gtk_tree_path_free (sort_path);
GtkTreePath *model_path = gtk_tree_model_sort_convert_path_to_child_path (sort_model, view_path);
gtk_tree_path_free (view_path);
return model_path;
}
@ -873,11 +977,15 @@ instance_state_changed_cb (GtkCellRendererText *cell,
const gchar *value,
GncSxSinceLastRunDialog *dialog)
{
GtkTreeIter tree_iter;
GncSxInstance *inst;
int i;
GncSxInstanceState new_state;
GtkTreePath *model_path = instance_get_model_path (dialog->instance_view, path);
GtkTreeIter tree_iter;
DEBUG("change instance state to [%s] at path [%s]", value, path);
debug_path (DEBUG, "instance model path is:", model_path);
for (i = 0; i < SX_INSTANCE_STATE_CREATED; i++)
{
@ -886,7 +994,7 @@ instance_state_changed_cb (GtkCellRendererText *cell,
}
if (i == SX_INSTANCE_STATE_CREATED)
{
g_warning ("unknown value [%s]", value);
PWARN("unknown value [%s]", value);
return;
}
new_state = i;
@ -894,18 +1002,21 @@ instance_state_changed_cb (GtkCellRendererText *cell,
if (!gtk_tree_model_get_iter (GTK_TREE_MODEL(dialog->editing_model), &tree_iter, model_path))
{
gtk_tree_path_free (model_path);
g_warning ("unknown path [%s]", path);
PWARN("unknown path [%s]", path);
return;
}
gtk_tree_path_free (model_path);
inst = gnc_sx_slr_model_get_instance (dialog->editing_model, &tree_iter);
if (inst == NULL)
{
g_warning ("invalid path [%s]", path);
PWARN("invalid path [%s]", path);
return;
}
DEBUG("instance is %p", inst);
gnc_sx_instance_model_change_instance_state (dialog->editing_model->instances, inst, new_state);
}
@ -931,12 +1042,14 @@ variable_value_changed_cb (GtkCellRendererText *cell,
{
GncSxVariable *var = NULL;
GncSxInstance *inst;
GtkTreeIter tree_iter;
gnc_numeric parsed_num;
char *endStr = NULL;
GtkTreePath *model_path = instance_get_model_path (dialog->instance_view, path);
GtkTreeIter tree_iter;
DEBUG ("variable to [%s] at path [%s]", value, path);
DEBUG("change variable to [%s] at view path [%s]", value, path);
debug_path (DEBUG, "instance model path is:", model_path);
dialog->temp_ce = NULL;
control_scroll_bars (dialog);
@ -944,14 +1057,14 @@ variable_value_changed_cb (GtkCellRendererText *cell,
if (!gtk_tree_model_get_iter (GTK_TREE_MODEL(dialog->editing_model), &tree_iter, model_path))
{
gtk_tree_path_free (model_path);
g_warning ("invalid path [%s]", path);
PWARN("invalid path [%s]", path);
return;
}
gtk_tree_path_free (model_path);
if (!gnc_sx_slr_model_get_instance_and_variable (dialog->editing_model, &tree_iter, &inst, &var))
{
g_critical ("path [%s] doesn't correspond to a valid variable", path);
PWARN("path [%s] doesn't correspond to a valid variable", path);
return;
}
@ -967,7 +1080,7 @@ variable_value_changed_cb (GtkCellRendererText *cell,
}
else
{
g_warning ("error parsing value [%s]", value);
PWARN("error parsing value [%s]", value);
}
g_free (value_copy);
return;
@ -999,31 +1112,41 @@ variable_value_cancel_changed_cb (GtkCellRenderer *renderer, gpointer user_data)
}
static gint
_transaction_sort_func (GtkTreeModel *model, GtkTreeIter *iter_a, GtkTreeIter *iter_b, gpointer user_data)
_sort_text (const gchar *text_a, const gchar *text_b)
{
gchar *a_caseless, *b_caseless;
gint rtn = 0;
if (text_a == NULL && text_b == NULL) return 0;
if (text_a == NULL) return 1;
if (text_b == NULL) return -1;
a_caseless = g_utf8_casefold (text_a, -1);
b_caseless = g_utf8_casefold (text_b, -1);
rtn = g_strcmp0 (a_caseless, b_caseless);
g_free (a_caseless);
g_free (b_caseless);
return rtn;
}
static gint
_transaction_sort_func_date (GtkTreeModel *model, GtkTreeIter *iter_a, GtkTreeIter *iter_b)
{
GtkTreePath *path_a = gtk_tree_model_get_path (model, iter_a);
if (gtk_tree_path_get_depth (path_a) != 1)
{
gtk_tree_path_free (path_a);
return rtn;
}
gint child_num_a = gtk_tree_model_iter_has_child (model, iter_a) ? 1 : 0;
gint child_num_b = gtk_tree_model_iter_has_child (model, iter_b) ? 1 : 0;
gint depth = gtk_tree_path_get_depth (path_a);
gint64 date_a = 0, date_b = 0;
gint rtn = 0;
gtk_tree_path_free (path_a);
if (child_num_a > child_num_b)
rtn = -1;
if (child_num_b > child_num_a)
rtn = 1;
if (depth == 3)
return rtn;
if ((child_num_a == 1) && (child_num_b == 1))
// if top level, look at the first date for order
if (depth == 1)
{
GtkTreeIter child_iter_a, child_iter_b;
gint64 date_a = 0, date_b = 0;
if (gtk_tree_model_iter_nth_child (model, &child_iter_a, iter_a, 0))
gtk_tree_model_get (model, &child_iter_a, SLR_MODEL_COL_INSTANCE_DATE, &date_a, -1);
@ -1035,75 +1158,81 @@ _transaction_sort_func (GtkTreeModel *model, GtkTreeIter *iter_a, GtkTreeIter *i
rtn = 1;
if (date_b > date_a)
rtn = -1;
if (rtn == 0) // if dates are equal, look at name
{
gchar *name_text_a, *name_text_b;
gtk_tree_model_get (model, iter_a, SLR_MODEL_COL_NAME, &name_text_a, -1);
gtk_tree_model_get (model, iter_b, SLR_MODEL_COL_NAME, &name_text_b, -1);
rtn = _sort_text (name_text_a, name_text_b);
g_free (name_text_a);
g_free (name_text_b);
}
return rtn;
}
if (((child_num_a == 0) && (child_num_b == 0)) || rtn == 0)
{
gchar *name_text_a, *name_text_b;
gtk_tree_model_get (model, iter_a, SLR_MODEL_COL_INSTANCE_DATE, &date_a, -1);
gtk_tree_model_get (model, iter_b, SLR_MODEL_COL_INSTANCE_DATE, &date_b, -1);
if (date_a > date_b)
rtn = 1;
if (date_b > date_a)
rtn = -1;
return rtn;
}
static gint
_transaction_sort_func_desc (GtkTreeModel *model, GtkTreeIter *iter_a, GtkTreeIter *iter_b)
{
GtkTreePath *path_a = gtk_tree_model_get_path (model, iter_a);
gint depth = gtk_tree_path_get_depth (path_a);
gchar *name_text_a, *name_text_b;
gint rtn = 0;
gtk_tree_path_free (path_a);
if (depth == 3)
return rtn;
if (depth == 1)
{
gtk_tree_model_get (model, iter_a, SLR_MODEL_COL_NAME, &name_text_a, -1);
gtk_tree_model_get (model, iter_b, SLR_MODEL_COL_NAME, &name_text_b, -1);
rtn = safe_utf8_collate (name_text_a, name_text_b);
rtn = _sort_text (name_text_a, name_text_b);
g_free (name_text_a);
g_free (name_text_b);
}
if (depth == 2)
{
gint64 date_a = 0, date_b = 0;
gtk_tree_model_get (model, iter_a, SLR_MODEL_COL_INSTANCE_DATE, &date_a, -1);
gtk_tree_model_get (model, iter_b, SLR_MODEL_COL_INSTANCE_DATE, &date_b, -1);
if (date_a > date_b)
rtn = 1;
if (date_b > date_a)
rtn = -1;
}
return rtn;
}
static gint
_status_sort_func (GtkTreeModel *model, GtkTreeIter *iter_a, GtkTreeIter *iter_b, gpointer user_data)
_transaction_sort_func (GtkTreeModel *model, GtkTreeIter *iter_a, GtkTreeIter *iter_b, gpointer user_data)
{
gint rtn = 0;
GtkTreePath *path_a = gtk_tree_model_get_path (model, iter_a);
GncSxSinceLastRunDialog *dialog = user_data;
if (gtk_tree_path_get_depth (path_a) != 1)
{
gtk_tree_path_free (path_a);
return rtn;
}
gint child_num_a = gtk_tree_model_iter_has_child (model, iter_a) ? 1 : 0;
gint child_num_b = gtk_tree_model_iter_has_child (model, iter_b) ? 1 : 0;
gtk_tree_path_free (path_a);
if (child_num_a > child_num_b)
rtn = -1;
if (child_num_b > child_num_a)
rtn = 1;
if ((child_num_a == 1) && (child_num_b == 1))
{
GtkTreeIter child_iter_a, child_iter_b;
gchar *state_text_a = NULL, *state_text_b = NULL;
if (gtk_tree_model_iter_nth_child (model, &child_iter_a, iter_a, 0))
gtk_tree_model_get (model, &child_iter_a, SLR_MODEL_COL_INSTANCE_STATE, &state_text_a, -1);
if (gtk_tree_model_iter_nth_child (model, &child_iter_b, iter_b, 0))
gtk_tree_model_get (model, &child_iter_b, SLR_MODEL_COL_INSTANCE_STATE, &state_text_b, -1);
rtn = safe_utf8_collate (state_text_a, state_text_b);
g_free (state_text_a);
g_free (state_text_b);
}
if (((child_num_a == 0) && (child_num_b == 0)) || rtn == 0)
{
gchar *name_text_a, *name_text_b;
gtk_tree_model_get (model, iter_a, SLR_MODEL_COL_NAME, &name_text_a, -1);
gtk_tree_model_get (model, iter_b, SLR_MODEL_COL_NAME, &name_text_b, -1);
rtn = safe_utf8_collate (name_text_a, name_text_b);
g_free (name_text_a);
g_free (name_text_b);
}
return rtn;
if (dialog->sort_selection_depth == 1)
return _transaction_sort_func_desc (model, iter_a, iter_b);
else
return _transaction_sort_func_date (model, iter_a, iter_b);
}
static gboolean
@ -1130,6 +1259,66 @@ scroll_event (GtkWidget *widget, GdkEventScroll *event, gpointer user_data)
return FALSE;
}
static void
set_transaction_sort_column_tooltip (GncSxSinceLastRunDialog *dialog)
{
GtkTreeViewColumn *col = gtk_tree_view_get_column (GTK_TREE_VIEW(dialog->instance_view), 0);
const gchar *date_text = _("Highlight a date first to sort by occurrence date.");
const gchar *sched_text = _("Highlight a schedule first to sort by schedule name.");
gchar *tooltip;
if (dialog->sort_selection_depth == 1)
tooltip = g_strconcat (sched_text, " *\n", date_text, NULL);
else
tooltip = g_strconcat (sched_text, "\n", date_text, " *", NULL);
gtk_widget_set_tooltip_text (gtk_tree_view_column_get_button (col), tooltip);
g_free (tooltip);
}
static gboolean
follow_select_tree_path (GtkTreeView *view)
{
GtkTreeSelection *selection = gtk_tree_view_get_selection (view);
GtkTreeModel *sort_model;
GtkTreeIter iter;
if (gtk_tree_selection_get_selected (selection, &sort_model, &iter))
{
GtkTreePath *view_path = gtk_tree_model_get_path (sort_model, &iter);
gtk_tree_view_scroll_to_cell (view, view_path, NULL, TRUE, 0.5, 0.0);
gtk_tree_path_free (view_path);
}
return FALSE;
}
static void
sort_column_changed (GtkTreeSortable* self, gpointer user_data)
{
// this is triggered before a sort change
GncSxSinceLastRunDialog *dialog = user_data;
GtkTreeIter iter;
GtkTreeSelection *selection = gtk_tree_view_get_selection (dialog->instance_view);
GtkTreeModel *sort_model;
if (gtk_tree_selection_get_selected (selection, &sort_model, &iter))
{
GtkTreePath *view_path = gtk_tree_model_get_path (sort_model, &iter);
dialog->sort_selection_depth = gtk_tree_path_get_depth (view_path);
gtk_tree_path_free (view_path);
}
else
dialog->sort_selection_depth = 1;
set_transaction_sort_column_tooltip (dialog);
g_idle_add ((GSourceFunc)follow_select_tree_path, dialog->instance_view);
}
GncSxSinceLastRunDialog*
gnc_ui_sx_since_last_run_dialog (GtkWindow *parent, GncSxInstanceModel *sx_instances, GList *auto_created_txn_guids)
{
@ -1172,9 +1361,14 @@ gnc_ui_sx_since_last_run_dialog (GtkWindow *parent, GncSxInstanceModel *sx_insta
g_object_unref (sort_model);
/* default sort order */
dialog->sort_selection_depth = gnc_prefs_get_int (GNC_PREFS_GROUP_STARTUP, GNC_PREF_SLR_SORT_DEPTH);
gboolean sort_ascending = gnc_prefs_get_bool (GNC_PREFS_GROUP_STARTUP, GNC_PREF_SLR_SORT_ASC);
gint sort_column = gnc_prefs_get_int (GNC_PREFS_GROUP_STARTUP, GNC_PREF_SLR_SORT_COL);
GtkSortType sort_type = sort_ascending ? GTK_SORT_ASCENDING : GTK_SORT_DESCENDING;
if (sort_column != 0)
sort_column = 0;
gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sort_model),
sort_column, sort_type);
@ -1192,6 +1386,8 @@ gnc_ui_sx_since_last_run_dialog (GtkWindow *parent, GncSxInstanceModel *sx_insta
gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sort_model), SLR_MODEL_COL_NAME,
_transaction_sort_func, dialog, NULL);
set_transaction_sort_column_tooltip (dialog);
renderer = gtk_cell_renderer_combo_new ();
g_object_set (G_OBJECT(renderer),
"model", gnc_sx_get_slr_state_model (),
@ -1213,10 +1409,8 @@ gnc_ui_sx_since_last_run_dialog (GtkWindow *parent, GncSxInstanceModel *sx_insta
"sensitive", SLR_MODEL_COL_INSTANCE_STATE_SENSITIVITY,
NULL);
gtk_tree_view_column_set_sort_column_id (col, SLR_MODEL_COL_INSTANCE_STATE);
gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sort_model), SLR_MODEL_COL_INSTANCE_STATE,
_status_sort_func, dialog, NULL);
g_signal_connect (G_OBJECT(sort_model), "sort-column-changed",
G_CALLBACK(sort_column_changed), dialog);
renderer = gtk_cell_renderer_pixbuf_new ();
g_object_set (G_OBJECT(renderer),
@ -1325,6 +1519,8 @@ close_handler (gpointer user_data)
gnc_prefs_set_bool (GNC_PREFS_GROUP_STARTUP, GNC_PREF_SLR_SORT_ASC, sort_ascending);
gnc_prefs_set_int (GNC_PREFS_GROUP_STARTUP, GNC_PREF_SLR_SORT_COL, column);
gnc_prefs_set_int (GNC_PREFS_GROUP_STARTUP, GNC_PREF_SLR_SORT_DEPTH,
app_dialog->sort_selection_depth);
}
gnc_save_window_size (GNC_PREFS_GROUP_STARTUP, GTK_WINDOW(app_dialog->dialog));
@ -1361,7 +1557,7 @@ dialog_response_cb (GtkDialog *dialog, gint response_id, GncSxSinceLastRunDialog
gint unbound_len;
unbound_variables = gnc_sx_instance_model_check_variables (app_dialog->editing_model->instances);
unbound_len = g_list_length (unbound_variables);
PINFO ("%d variables unbound", unbound_len);
PINFO("%d variables unbound", unbound_len);
if (unbound_len > 0)
{
// focus first variable
@ -1408,7 +1604,7 @@ dialog_response_cb (GtkDialog *dialog, gint response_id, GncSxSinceLastRunDialog
gnc_close_gui_component (app_dialog->component_id);
break;
default:
g_error ("unknown response id [%d]", response_id);
PWARN("unknown response id [%d]", response_id);
break;
}
}

View File

@ -37,6 +37,11 @@
<summary>Set the sort direction in the "since last run" dialog.</summary>
<description>This settings sets the sort direction in the "since last run" dialog.</description>
</key>
<key name="sort-depth" type="i">
<default>1</default>
<summary>The depth used in the tree to sort in the "since last run" dialog.</summary>
<description>The depth used in the tree to sort in the "since last run" dialog.</description>
</key>
</schema>
<schema id="org.gnucash.GnuCash.dialogs.sxs.transaction-editor" path="/org/gnucash/GnuCash/dialogs/scheduled-trans/transaction-editor/">
<key name="create-auto" type="b">