gnucash stable: Multiple changes pushed
John Ralls
jralls at code.gnucash.org
Sat Sep 19 20:14:10 EDT 2026
Updated via https://github.com/Gnucash/gnucash/commit/3c9cfbb9 (commit)
via https://github.com/Gnucash/gnucash/commit/1969d781 (commit)
via https://github.com/Gnucash/gnucash/commit/06f7324e (commit)
from https://github.com/Gnucash/gnucash/commit/1e5f5d18 (commit)
commit 3c9cfbb91661688034e09168a0631804a639f95e
Merge: 1e5f5d184d 1969d781e4
Author: John Ralls <jralls at ceridwen.us>
Date: Sat Sep 19 17:08:55 2026 -0700
Merge Roy Hansen's 'improve/natural-sort-id-columns' into stable.
commit 1969d781e4067c80d5e33ccb054c614d5694bf13
Author: Roy Hansen <roy at royhansen.no>
Date: Fri Sep 11 14:18:53 2026 +0200
Sort Id columns naturally in qof-query, which is used in gnucash search
dialogs.
Changed sorting to be natural (chunks), instead of sequential ascii-sort.
Number chunks are compared as whole numbers instead of one at a time.
diff --git a/gnucash/gnome-search/search-core-type.c b/gnucash/gnome-search/search-core-type.c
index 6612ec624c..e29204b193 100644
--- a/gnucash/gnome-search/search-core-type.c
+++ b/gnucash/gnome-search/search-core-type.c
@@ -238,6 +238,8 @@ init_table (void)
{
gnc_search_core_register_type (QOF_TYPE_STRING,
(GNCSearchCoreNew) gnc_search_string_new);
+ gnc_search_core_register_type (QOF_TYPE_NATURAL,
+ (GNCSearchCoreNew) gnc_search_string_new);
gnc_search_core_register_type (QOF_TYPE_DATE,
(GNCSearchCoreNew) gnc_search_date_new);
gnc_search_core_register_type (QOF_TYPE_INT64,
diff --git a/libgnucash/engine/gncCustomer.c b/libgnucash/engine/gncCustomer.c
index 9786b3dd7a..88c40823a3 100644
--- a/libgnucash/engine/gncCustomer.c
+++ b/libgnucash/engine/gncCustomer.c
@@ -927,7 +927,7 @@ gboolean gncCustomerRegister (void)
{
static QofParam params[] =
{
- { CUSTOMER_ID, QOF_TYPE_STRING, (QofAccessFunc)gncCustomerGetID, (QofSetterFunc)gncCustomerSetID },
+ { CUSTOMER_ID, QOF_TYPE_NATURAL, (QofAccessFunc)gncCustomerGetID, (QofSetterFunc)gncCustomerSetID },
{ CUSTOMER_NAME, QOF_TYPE_STRING, (QofAccessFunc)gncCustomerGetName, (QofSetterFunc)gncCustomerSetName },
{ CUSTOMER_NOTES, QOF_TYPE_STRING, (QofAccessFunc)gncCustomerGetNotes, (QofSetterFunc)gncCustomerSetNotes },
{
diff --git a/libgnucash/engine/gncEmployee.c b/libgnucash/engine/gncEmployee.c
index fd9518f88d..74e4324cf2 100644
--- a/libgnucash/engine/gncEmployee.c
+++ b/libgnucash/engine/gncEmployee.c
@@ -913,7 +913,7 @@ gboolean gncEmployeeRegister (void)
{
static QofParam params[] =
{
- { EMPLOYEE_ID, QOF_TYPE_STRING, (QofAccessFunc)gncEmployeeGetID, (QofSetterFunc)gncEmployeeSetID },
+ { EMPLOYEE_ID, QOF_TYPE_NATURAL, (QofAccessFunc)gncEmployeeGetID, (QofSetterFunc)gncEmployeeSetID },
{
EMPLOYEE_USERNAME, QOF_TYPE_STRING, (QofAccessFunc)gncEmployeeGetUsername,
(QofSetterFunc)gncEmployeeSetUsername
diff --git a/libgnucash/engine/gncInvoice.c b/libgnucash/engine/gncInvoice.c
index bd2aaba6d1..6e4cc497ce 100644
--- a/libgnucash/engine/gncInvoice.c
+++ b/libgnucash/engine/gncInvoice.c
@@ -2213,14 +2213,14 @@ gboolean gncInvoiceRegister (void)
{
static QofParam params[] =
{
- { INVOICE_ID, QOF_TYPE_STRING, (QofAccessFunc)gncInvoiceGetID, (QofSetterFunc)gncInvoiceSetID },
+ { INVOICE_ID, QOF_TYPE_NATURAL, (QofAccessFunc)gncInvoiceGetID, (QofSetterFunc)gncInvoiceSetID },
{ INVOICE_OWNER, GNC_ID_OWNER, (QofAccessFunc)gncInvoiceGetOwner, NULL },
{ INVOICE_OPENED, QOF_TYPE_DATE, (QofAccessFunc)gncInvoiceGetDateOpened, (QofSetterFunc)gncInvoiceSetDateOpened },
{ INVOICE_DUE, QOF_TYPE_DATE, (QofAccessFunc)gncInvoiceGetDateDue, NULL },
{ INVOICE_POSTED, QOF_TYPE_DATE, (QofAccessFunc)gncInvoiceGetDatePosted, (QofSetterFunc)gncInvoiceSetDatePosted },
{ INVOICE_IS_POSTED, QOF_TYPE_BOOLEAN, (QofAccessFunc)gncInvoiceIsPosted, NULL },
{ INVOICE_IS_PAID, QOF_TYPE_BOOLEAN, (QofAccessFunc)gncInvoiceIsPaid, NULL },
- { INVOICE_BILLINGID, QOF_TYPE_STRING, (QofAccessFunc)gncInvoiceGetBillingID, (QofSetterFunc)gncInvoiceSetBillingID },
+ { INVOICE_BILLINGID, QOF_TYPE_NATURAL, (QofAccessFunc)gncInvoiceGetBillingID, (QofSetterFunc)gncInvoiceSetBillingID },
{ INVOICE_NOTES, QOF_TYPE_STRING, (QofAccessFunc)gncInvoiceGetNotes, (QofSetterFunc)gncInvoiceSetNotes },
{ INVOICE_DOCLINK, QOF_TYPE_STRING, (QofAccessFunc)gncInvoiceGetDocLink, (QofSetterFunc)gncInvoiceSetDocLink },
{ INVOICE_ACC, GNC_ID_ACCOUNT, (QofAccessFunc)gncInvoiceGetPostedAcc, (QofSetterFunc)gncInvoiceSetPostedAcc },
diff --git a/libgnucash/engine/gncVendor.c b/libgnucash/engine/gncVendor.c
index 7d0340c707..52471aaf0f 100644
--- a/libgnucash/engine/gncVendor.c
+++ b/libgnucash/engine/gncVendor.c
@@ -989,7 +989,7 @@ gboolean gncVendorRegister (void)
{
static QofParam params[] =
{
- { VENDOR_ID, QOF_TYPE_STRING, (QofAccessFunc)gncVendorGetID, (QofSetterFunc)gncVendorSetID },
+ { VENDOR_ID, QOF_TYPE_NATURAL, (QofAccessFunc)gncVendorGetID, (QofSetterFunc)gncVendorSetID },
{ VENDOR_NAME, QOF_TYPE_STRING, (QofAccessFunc)gncVendorGetName, (QofSetterFunc)gncVendorSetName },
{ VENDOR_ADDR, GNC_ID_ADDRESS, (QofAccessFunc)gncVendorGetAddr, (QofSetterFunc)qofVendorSetAddr },
{ VENDOR_NOTES, QOF_TYPE_STRING, (QofAccessFunc)gncVendorGetNotes, (QofSetterFunc)gncVendorSetNotes },
diff --git a/libgnucash/engine/qofclass.h b/libgnucash/engine/qofclass.h
index f671df69a8..d5d074fad7 100644
--- a/libgnucash/engine/qofclass.h
+++ b/libgnucash/engine/qofclass.h
@@ -88,6 +88,7 @@ single reference between two known objects.
*/
#define QOF_TYPE_STRING "string"
+#define QOF_TYPE_NATURAL "natural"
#define QOF_TYPE_DATE "date"
#define QOF_TYPE_NUMERIC "numeric"
#define QOF_TYPE_DEBCRED "debcred"
diff --git a/libgnucash/engine/qofquerycore.cpp b/libgnucash/engine/qofquerycore.cpp
index 42acabdd60..e572720b35 100644
--- a/libgnucash/engine/qofquerycore.cpp
+++ b/libgnucash/engine/qofquerycore.cpp
@@ -27,6 +27,7 @@
#include <glib.h>
#include <stdlib.h>
+#include "gnc-glib-utils.h"
#include "qof.h"
#include "qofquerycore-p.h"
@@ -205,6 +206,31 @@ string_compare_func (gpointer a, gpointer b, gint options,
return g_strcmp0 (s1, s2);
}
+static int
+natural_compare_func (gpointer a, gpointer b, gint options,
+ QofParam *getter)
+{
+ const char *s1, *s2;
+ g_return_val_if_fail (a && b && getter && getter->param_getfcn, COMPARE_ERROR);
+
+ s1 = ((query_string_getter)getter->param_getfcn) (a, getter);
+ s2 = ((query_string_getter)getter->param_getfcn) (b, getter);
+
+ if (options == QOF_STRING_MATCH_CASEINSENSITIVE) {
+ // There is no case-insensitive natural sort.
+ // Downcasing/folding a/b for every compare
+ // operation is too wasteful.
+ //
+ // The best option is to downcase/fold only once
+ // for each item and then store that as a property
+ // that we use in natural_compare_func, but that
+ // will require some work outside natural_compare_func,
+ // and I dont think it is worth the extra code.
+ }
+
+ return safe_utf8_collate_natural(s1, s2);
+}
+
int
qof_string_number_compare_func (gpointer a, gpointer b, gint options,
QofParam *getter)
@@ -1460,6 +1486,11 @@ static void init_tables (void)
string_copy_predicate, string_free_pdata, string_to_string,
string_predicate_equal
},
+ {
+ QOF_TYPE_NATURAL, string_match_predicate, natural_compare_func,
+ string_copy_predicate, string_free_pdata, string_to_string,
+ string_predicate_equal
+ },
{
QOF_TYPE_DATE, date_match_predicate, date_compare_func,
date_copy_predicate, date_free_pdata, date_to_string,
commit 06f7324e589cf5f4c0dd5e221b8bc3f67544acf1
Author: Roy Hansen <roy at royhansen.no>
Date: Fri Sep 11 14:09:36 2026 +0200
Sort Id columns naturally in gnc-tree-views.
Changed sorting to be natural (chunks), instead of sequential ascii-sort.
Number chunks are compared as whole numbers instead of one at a time.
After this change, customer-overview, vendor-overview and
employee-overview will naturally sort "id" columns.
diff --git a/gnucash/gnome-utils/gnc-tree-view-owner.c b/gnucash/gnome-utils/gnc-tree-view-owner.c
index d1b1762845..68976ea84b 100644
--- a/gnucash/gnome-utils/gnc-tree-view-owner.c
+++ b/gnucash/gnome-utils/gnc-tree-view-owner.c
@@ -242,6 +242,35 @@ sort_by_string (GtkTreeModel *f_model,
return gncOwnerCompare(owner1, owner2);
}
+static gint
+sort_by_string_natural (GtkTreeModel *f_model,
+ GtkTreeIter *f_iter1,
+ GtkTreeIter *f_iter2,
+ gpointer user_data)
+{
+ GtkTreeModel *model;
+ GtkTreeIter iter1, iter2;
+ gint column = GPOINTER_TO_INT(user_data);
+ gchar *str1, *str2;
+ const GncOwner *owner1, *owner2;
+ gint result;
+
+ model = sort_cb_setup_w_iters(f_model, f_iter1, f_iter2, &iter1, &iter2, &owner1, &owner2);
+
+ gtk_tree_model_get(GTK_TREE_MODEL(model), &iter1, column, &str1, -1);
+ gtk_tree_model_get(GTK_TREE_MODEL(model), &iter2, column, &str2, -1);
+
+ result = safe_utf8_collate_natural(str1, str2);
+
+ g_free(str1);
+ g_free(str2);
+
+ if (result != 0)
+ return result;
+
+ return gncOwnerCompare(owner1, owner2);
+}
+
static gint
sort_by_boolean (GtkTreeModel *f_model,
GtkTreeIter *f_iter1,
@@ -390,7 +419,7 @@ gnc_tree_view_owner_new (GncOwnerType owner_type)
NULL, "1-123-1234",
GNC_TREE_MODEL_OWNER_COL_ID,
GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
- sort_by_string);
+ sort_by_string_natural);
gnc_tree_view_add_text_column(GNC_TREE_VIEW(view), _("Currency"), GNC_OWNER_TREE_CURRENCY_COL,
NULL, sample_currency,
GNC_TREE_MODEL_OWNER_COL_CURRENCY,
diff --git a/libgnucash/core-utils/gnc-glib-utils.c b/libgnucash/core-utils/gnc-glib-utils.c
index 0b55982625..591781d809 100644
--- a/libgnucash/core-utils/gnc-glib-utils.c
+++ b/libgnucash/core-utils/gnc-glib-utils.c
@@ -51,6 +51,35 @@ safe_utf8_collate (const char * da, const char * db)
return 0;
}
+int
+safe_utf8_collate_natural (const char * da, const char * db)
+{
+ if (da && !(*da))
+ da = NULL;
+
+ if (db && !(*db))
+ db = NULL;
+
+ if (da && db)
+ {
+ gchar *a = g_utf8_collate_key_for_filename(da ? da : "", -1);
+ gchar *b = g_utf8_collate_key_for_filename(db ? db : "", -1);
+
+ int result = strcmp(a, b);
+
+ g_free(a);
+ g_free(b);
+
+ return result;
+ }
+ else if (da)
+ return 1;
+ else if (db)
+ return -1;
+
+ return 0;
+}
+
/********************************************************************
* The following definitions are from gutf8.c, for use by
* gnc_utf8_validate(). These are all verbatim copies, except for
diff --git a/libgnucash/core-utils/gnc-glib-utils.h b/libgnucash/core-utils/gnc-glib-utils.h
index a2770aaaf8..c32e03be66 100644
--- a/libgnucash/core-utils/gnc-glib-utils.h
+++ b/libgnucash/core-utils/gnc-glib-utils.h
@@ -59,6 +59,18 @@ extern "C" {
* compares after str2. */
int safe_utf8_collate (const char *str1, const char *str2);
+/** Collate two UTF-8 strings _naturally_. This function performs basic argument
+ * checking before calling g_utf8_collate_key_for_filename.
+ *
+ * @param str1 The first string.
+ *
+ * @param str2 The first string.
+ *
+ * @return Same return value as g_utf8_collate. The values are: < 0
+ * if str1 compares before str2, 0 if they compare equal, > 0 if str1
+ * compares after str2. */
+int safe_utf8_collate_natural (const char *str1, const char *str2);
+
/**
* @brief Validates UTF-8 encoded text for use in GnuCash.
*
Summary of changes:
gnucash/gnome-search/search-core-type.c | 2 ++
gnucash/gnome-utils/gnc-tree-view-owner.c | 31 ++++++++++++++++++++++++++++++-
libgnucash/core-utils/gnc-glib-utils.c | 29 +++++++++++++++++++++++++++++
libgnucash/core-utils/gnc-glib-utils.h | 12 ++++++++++++
libgnucash/engine/gncCustomer.c | 2 +-
libgnucash/engine/gncEmployee.c | 2 +-
libgnucash/engine/gncInvoice.c | 4 ++--
libgnucash/engine/gncVendor.c | 2 +-
libgnucash/engine/qofclass.h | 1 +
libgnucash/engine/qofquerycore.cpp | 31 +++++++++++++++++++++++++++++++
10 files changed, 110 insertions(+), 6 deletions(-)
More information about the gnucash-changes
mailing list