From c054b977185a2f5f52ef718e0655122db586f020 Mon Sep 17 00:00:00 2001 From: Robert Fewell <14uBobIT@gmail.com> Date: Fri, 4 Feb 2022 11:24:20 +0000 Subject: [PATCH] Bug 798438 - on reconcile window, 'R' column sorting broken Sorting on the 'R' column was enabled in version 3.0 by mistake and never worked as expected. Due to the 'R' column using a function to populate the model, the existing sorting method would not work as this was achieved by changing the query. To fix this add a sorting function to sort the model for the 'R' column. --- gnucash/gnome-utils/gnc-query-view.c | 7 ++ gnucash/gnome-utils/search-param.c | 16 +++ gnucash/gnome-utils/search-param.h | 3 + gnucash/gnome/reconcile-view.c | 154 ++++++++++++++++++++++++--- gnucash/gnome/reconcile-view.h | 2 + 5 files changed, 168 insertions(+), 14 deletions(-) diff --git a/gnucash/gnome-utils/gnc-query-view.c b/gnucash/gnome-utils/gnc-query-view.c index 683b078fef..0da3765dd9 100644 --- a/gnucash/gnome-utils/gnc-query-view.c +++ b/gnucash/gnome-utils/gnc-query-view.c @@ -691,6 +691,13 @@ gnc_query_view_set_query_sort (GNCQueryView *qview, gboolean new_column) g_assert (GNC_IS_SEARCH_PARAM_SIMPLE(param)); + /* If param values are based on a param function, sorting is not possible */ + if (gnc_search_param_has_param_fcn (param)) + { + gnc_query_view_refresh (qview); + return; + } + /* If we're asked to invert numerics, and if this is a numeric or * debred column, then invert the sort order. */ diff --git a/gnucash/gnome-utils/search-param.c b/gnucash/gnome-utils/search-param.c index 60a5b9fb5a..18f54e23c5 100644 --- a/gnucash/gnome-utils/search-param.c +++ b/gnucash/gnome-utils/search-param.c @@ -538,6 +538,22 @@ gnc_search_param_set_param_fcn (GNCSearchParamSimple *param, gnc_search_param_override_param_type (param, param_type); } +gboolean +gnc_search_param_has_param_fcn (GNCSearchParamSimple *param) +{ + GNCSearchParamSimplePrivate *priv; + + g_return_val_if_fail (param, FALSE); + g_return_val_if_fail (GNC_IS_SEARCH_PARAM_SIMPLE(param), FALSE); + + priv = GNC_SEARCH_PARAM_SIMPLE_GET_PRIVATE(param); + + if (priv->lookup_fcn) + return TRUE; + + return FALSE; +} + /* Compute the value of this parameter for this object */ gpointer gnc_search_param_compute_value (GNCSearchParamSimple *param, gpointer object) diff --git a/gnucash/gnome-utils/search-param.h b/gnucash/gnome-utils/search-param.h index 08d07223ce..4dc18e93e6 100644 --- a/gnucash/gnome-utils/search-param.h +++ b/gnucash/gnome-utils/search-param.h @@ -184,6 +184,9 @@ void gnc_search_param_set_param_fcn (GNCSearchParamSimple *param, GNCSearchParamFcn fcn, gpointer arg); +/* check to see if this parameter is a lookup function */ +gboolean gnc_search_param_has_param_fcn (GNCSearchParamSimple *param); + /* Compute the value of this parameter for this object */ gpointer gnc_search_param_compute_value (GNCSearchParamSimple *param, gpointer object); diff --git a/gnucash/gnome/reconcile-view.c b/gnucash/gnome/reconcile-view.c index 864dbdbab0..39acbf00de 100644 --- a/gnucash/gnome/reconcile-view.c +++ b/gnucash/gnome/reconcile-view.c @@ -315,17 +315,58 @@ gnc_reconcile_view_construct (GNCReconcileView *view, Query *query) G_CALLBACK(gnc_reconcile_view_tooltip_cb), view); } +static gint +sort_date_helper (time64 date_a, time64 date_b) +{ + gint ret = 0; + + if (date_a < date_b) + ret = -1; + else if (date_a > date_b) + ret = 1; + + return ret; +} + +static gint +sort_iter_compare_func (GtkTreeModel *model, + GtkTreeIter *a, + GtkTreeIter *b, + gpointer user_data) +{ + gboolean rec_a, rec_b; + Split *split_a, *split_b; + time64 date_a, date_b; + gint ret = 0; + + gtk_tree_model_get (model, a, REC_POINTER, &split_a, REC_RECN, &rec_a, -1); + gtk_tree_model_get (model, b, REC_POINTER, &split_b, REC_RECN, &rec_b, -1); + + date_a = xaccTransGetDate (xaccSplitGetParent (split_a)); + date_b = xaccTransGetDate (xaccSplitGetParent (split_b)); + + if (rec_a > rec_b) + ret = -1; + else if (rec_b > rec_a) + ret = 1; + else ret = sort_date_helper (date_a, date_b); + + return ret; +} + GtkWidget * gnc_reconcile_view_new (Account *account, GNCReconcileViewType type, time64 statement_date) { - GNCReconcileView *view; - GtkListStore *liststore; - gboolean include_children, auto_check; - GList *accounts = NULL; - GList *splits; - Query *query; - QofNumericMatch sign; + GNCReconcileView *view; + GtkListStore *liststore; + GtkTreeSortable *sortable; + GtkTreeViewColumn *col; + gboolean include_children, auto_check; + GList *accounts = NULL; + GList *splits; + Query *query; + QofNumericMatch sign; g_return_val_if_fail (account, NULL); g_return_val_if_fail ((type == RECLIST_DEBIT) || @@ -392,6 +433,13 @@ gnc_reconcile_view_new (Account *account, GNCReconcileViewType type, } } + /* set up a separate sort function for the recn column as it is + * derived from a search function */ + sortable = GTK_TREE_SORTABLE(gtk_tree_view_get_model (GTK_TREE_VIEW(view))); + col = gtk_tree_view_get_column (GTK_TREE_VIEW(view), REC_RECN -1); + gtk_tree_sortable_set_sort_func (sortable, REC_RECN, sort_iter_compare_func, + GINT_TO_POINTER (REC_RECN), NULL); + /* Free the query -- we don't need it anymore */ qof_query_destroy (query); @@ -519,6 +567,28 @@ gnc_reconcile_view_toggle (GNCReconcileView *view, Split *split) reconcile_view_signals[TOGGLE_RECONCILED], 0, split); } +static gboolean +follow_select_tree_path (GNCReconcileView *view) +{ + if (view->rowref) + { + GtkTreePath *tree_path = gtk_tree_row_reference_get_path (view->rowref); + GNCQueryView qview = view->qview; + GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(&qview)); + + gtk_tree_selection_unselect_all (selection); + gtk_tree_selection_select_path (selection, tree_path); + + gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW(&qview), + tree_path, NULL, FALSE, 0.0, 0.0); + + gtk_tree_path_free (tree_path); + gtk_tree_row_reference_free (view->rowref); + view->rowref = NULL; + } + return FALSE; +} + static void gnc_reconcile_view_line_toggled (GNCQueryView *qview, gpointer item, @@ -528,6 +598,7 @@ gnc_reconcile_view_line_toggled (GNCQueryView *qview, GtkTreeModel *model; GtkTreeIter iter; gpointer entry; + GtkTreePath *tree_path; g_return_if_fail (user_data); g_return_if_fail (GNC_IS_QUERY_VIEW(qview)); @@ -536,10 +607,32 @@ gnc_reconcile_view_line_toggled (GNCQueryView *qview, model = gtk_tree_view_get_model (GTK_TREE_VIEW(qview)); gtk_tree_model_iter_nth_child (model, &iter, NULL, qview->toggled_row); - gtk_list_store_set (GTK_LIST_STORE(model), &iter, qview->toggled_column, GPOINTER_TO_INT(item), -1); - gtk_tree_model_get (model, &iter, REC_POINTER, &entry, -1); - gnc_reconcile_view_toggle (view, entry); + tree_path = gtk_tree_model_get_path (model, &iter); + view->rowref = gtk_tree_row_reference_new (model, tree_path); + gtk_tree_path_free (tree_path); + + gtk_list_store_set (GTK_LIST_STORE(model), &iter, qview->toggled_column, + GPOINTER_TO_INT(item), -1); + + tree_path = gtk_tree_row_reference_get_path (view->rowref); + + if (gtk_tree_model_get_iter (model, &iter, tree_path)) + { + gtk_tree_model_get (model, &iter, REC_POINTER, &entry, -1); + gnc_reconcile_view_toggle (view, entry); + } + + // See if sorting on rec column, -1 to allow for the model pointer column at 0 + if (qview->sort_column == REC_RECN - 1) + g_idle_add ((GSourceFunc)follow_select_tree_path, view); + else + { + gtk_tree_row_reference_free (view->rowref); + view->rowref = NULL; + } + + gtk_tree_path_free (tree_path); } static void @@ -586,16 +679,30 @@ gnc_reconcile_view_set_list (GNCReconcileView *view, gboolean reconcile) gboolean toggled; GList *node; GList *list_of_rows; + GList *rr_list = NULL; + GtkTreePath *last_tree_path = NULL; model = gtk_tree_view_get_model (GTK_TREE_VIEW(qview)); selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(qview)); list_of_rows = gtk_tree_selection_get_selected_rows (selection, &model); - /* We get a list of TreePaths */ + /* First create a list of Row references */ for (node = list_of_rows; node; node = node->next) { - GtkTreeIter iter; - if (gtk_tree_model_get_iter(model, &iter, node->data)) + GtkTreeRowReference *rowref = gtk_tree_row_reference_new (model, node->data); + rr_list = g_list_append (rr_list, rowref); + gtk_tree_path_free (node->data); + } + + for (node = rr_list; node; node = node->next) + { + GtkTreeIter iter; + GtkTreePath *path; + GtkTreeRowReference *rowref = node->data; + + path = gtk_tree_row_reference_get_path (rowref); + + if (gtk_tree_model_get_iter (model, &iter, path)) { /* now iter is a valid row iterator */ gtk_tree_model_get (model, &iter, REC_POINTER, &entry, @@ -603,11 +710,30 @@ gnc_reconcile_view_set_list (GNCReconcileView *view, gboolean reconcile) gtk_list_store_set (GTK_LIST_STORE(model), &iter, REC_RECN, reconcile, -1); + if (last_tree_path) + gtk_tree_path_free (last_tree_path); + last_tree_path = gtk_tree_row_reference_get_path (rowref); + if (reconcile != toggled) gnc_reconcile_view_toggle (view, entry); } - gtk_tree_path_free (node->data); + gtk_tree_path_free (path); } + + if (last_tree_path) + { + // See if sorting on rec column, -1 to allow for the model pointer column at 0 + if (qview->sort_column == REC_RECN -1) + { + gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW(qview), + last_tree_path, NULL, FALSE, 0.0, 0.0); + } + gtk_tree_path_free (last_tree_path); + last_tree_path = NULL; + } + g_list_foreach (rr_list, (GFunc) gtk_tree_row_reference_free, NULL); + g_list_free (rr_list); + // Out of site toggles on selected rows may not appear correctly drawn so // queue a draw for the treeview widget gtk_widget_queue_draw (GTK_WIDGET(qview)); diff --git a/gnucash/gnome/reconcile-view.h b/gnucash/gnome/reconcile-view.h index 267154b7fe..4d927a5f22 100644 --- a/gnucash/gnome/reconcile-view.h +++ b/gnucash/gnome/reconcile-view.h @@ -67,6 +67,8 @@ struct GNCReconcileView GNCReconcileView *sibling; GNCReconcileViewType view_type; gboolean no_toggle; + + GtkTreeRowReference *rowref; }; typedef struct