gnucash master: Multiple changes pushed

Christopher Lam clam at code.gnucash.org
Tue Feb 21 20:09:03 EST 2023


Updated	 via  https://github.com/Gnucash/gnucash/commit/45cdf407 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/50e9e705 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/bf79d6fa (commit)
	 via  https://github.com/Gnucash/gnucash/commit/8d44c1c3 (commit)
	from  https://github.com/Gnucash/gnucash/commit/4666a426 (commit)



commit 45cdf40785320141c5a4c4c2289c2381271c8660
Merge: 4666a426e 50e9e7059
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Wed Feb 22 09:08:28 2023 +0800

    Merge branch 'master-progress' #1527


commit 50e9e70592e7984f79ff9b40149f17c65560b25f
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Tue Feb 7 23:59:36 2023 +0800

    [import-backend] don't search GHashTable twice
    
    search acct_id_hash once for dest_acct

diff --git a/gnucash/import-export/import-backend.cpp b/gnucash/import-export/import-backend.cpp
index 23df1f601..b90a029f7 100644
--- a/gnucash/import-export/import-backend.cpp
+++ b/gnucash/import-export/import-backend.cpp
@@ -1017,12 +1017,16 @@ gboolean gnc_import_exists_online_id (Transaction *trans, GHashTable* acct_id_ha
     // Create a hash per account of a hash of all split IDs. Then the
     // test below will be fast if we have many transactions to import.
     auto dest_acct = xaccSplitGetAccount (source_split);
-    if (!g_hash_table_contains (acct_id_hash, dest_acct))
-         g_hash_table_insert (acct_id_hash, dest_acct,
-                              hash_account_online_ids (dest_acct));
-    auto online_id_exists = g_hash_table_contains (
-                static_cast<GHashTable*>(g_hash_table_lookup (acct_id_hash, dest_acct)),
-                source_online_id);
+
+    auto online_id_hash = static_cast<GHashTable*>(g_hash_table_lookup (acct_id_hash, dest_acct));
+
+    if (!online_id_hash)
+    {
+        online_id_hash = hash_account_online_ids (dest_acct);
+        g_hash_table_insert (acct_id_hash, dest_acct, online_id_hash);
+    }
+
+    auto online_id_exists = g_hash_table_contains (online_id_hash, source_online_id);
     
     /* If it does, abort the process for this transaction, since it is
        already in the system. */

commit bf79d6fa5827c2d254ad72143bfc8d90e1bff2f3
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Mon Feb 20 09:53:51 2023 +0800

    gtk_entry_get_text instead of gtk_editable_get_chars

diff --git a/gnucash/gnome-utils/dialog-account.c b/gnucash/gnome-utils/dialog-account.c
index 4b0a598b3..f576046b9 100644
--- a/gnucash/gnome-utils/dialog-account.c
+++ b/gnucash/gnome-utils/dialog-account.c
@@ -2196,14 +2196,13 @@ static void
 gnc_account_renumber_update_examples (RenumberDialog *data)
 {
     gchar *str;
-    gchar *prefix;
     gint   interval;
     gint   digits;
     unsigned int num_digits = 1;
 
     g_return_if_fail (data->num_children > 0);
 
-    prefix = gtk_editable_get_chars (GTK_EDITABLE(data->prefix), 0, -1);
+    const gchar *prefix = gtk_entry_get_text (GTK_ENTRY(data->prefix));
     interval = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON(data->interval));
     digits = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON(data->digits));
 
@@ -2225,7 +2224,7 @@ gnc_account_renumber_update_examples (RenumberDialog *data)
     else
         num_digits = digits;
 
-    if (strlen (prefix))
+    if (prefix && *prefix)
         str = g_strdup_printf ("%s-%0*d", prefix, num_digits, interval);
     else
         str = g_strdup_printf ("%0*d", num_digits, interval);
@@ -2243,7 +2242,6 @@ gnc_account_renumber_update_examples (RenumberDialog *data)
     gtk_label_set_text (GTK_LABEL(data->example2), str);
 
     g_free (str);
-    g_free (prefix);
 }
 
 void
@@ -2276,7 +2274,6 @@ gnc_account_renumber_response_cb (GtkDialog *dialog,
     {
         GList *children = gnc_account_get_children_sorted (data->parent);
         GList *tmp;
-        gchar *prefix;
         gint interval;
         unsigned int num_digits, i;
 
@@ -2288,7 +2285,7 @@ gnc_account_renumber_response_cb (GtkDialog *dialog,
             g_free (data);
             return;
         }
-        prefix = gtk_editable_get_chars (GTK_EDITABLE(data->prefix), 0, -1);
+        const gchar *prefix = gtk_entry_get_text (GTK_ENTRY(data->prefix));
         interval = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON(data->interval));
         num_digits = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON(data->digits));
 
@@ -2306,7 +2303,6 @@ gnc_account_renumber_response_cb (GtkDialog *dialog,
             g_free (str);
         }
         gnc_unset_busy_cursor (NULL);
-        g_free (prefix);
         g_list_free (children);
     }
     gtk_widget_destroy (data->dialog);
diff --git a/gnucash/gnome-utils/gnc-dense-cal-store.c b/gnucash/gnome-utils/gnc-dense-cal-store.c
index 7a29aba92..db87640c3 100644
--- a/gnucash/gnome-utils/gnc-dense-cal-store.c
+++ b/gnucash/gnome-utils/gnc-dense-cal-store.c
@@ -154,7 +154,7 @@ gnc_dense_cal_store_clear(GncDenseCalStore *model)
 }
 
 void
-gnc_dense_cal_store_update_name(GncDenseCalStore *model, gchar *name)
+gnc_dense_cal_store_update_name(GncDenseCalStore *model, const gchar *name)
 {
     if (model->name != NULL)
     {
@@ -165,7 +165,7 @@ gnc_dense_cal_store_update_name(GncDenseCalStore *model, gchar *name)
 }
 
 void
-gnc_dense_cal_store_update_info(GncDenseCalStore *model, gchar *info)
+gnc_dense_cal_store_update_info(GncDenseCalStore *model, const gchar *info)
 {
     if (model->info != NULL)
     {
diff --git a/gnucash/gnome-utils/gnc-dense-cal-store.h b/gnucash/gnome-utils/gnc-dense-cal-store.h
index 1ea916e44..704d9cb05 100644
--- a/gnucash/gnome-utils/gnc-dense-cal-store.h
+++ b/gnucash/gnome-utils/gnc-dense-cal-store.h
@@ -46,8 +46,8 @@ typedef struct _GncDenseCalStoreClass GncDenseCalStoreClass;
 GType gnc_dense_cal_store_get_type(void);
 GncDenseCalStore* gnc_dense_cal_store_new(int num_marks);
 void gnc_dense_cal_store_clear(GncDenseCalStore *model);
-void gnc_dense_cal_store_update_name(GncDenseCalStore *model, gchar* name);
-void gnc_dense_cal_store_update_info(GncDenseCalStore *model, gchar* info);
+void gnc_dense_cal_store_update_name(GncDenseCalStore *model, const gchar* name);
+void gnc_dense_cal_store_update_info(GncDenseCalStore *model, const gchar* info);
 
 void gnc_dense_cal_store_update_recurrences_no_end(GncDenseCalStore *model, GDate *start, GList *recurrences);
 void gnc_dense_cal_store_update_recurrences_count_end(GncDenseCalStore *model, GDate *start, GList *recurrences, int num_occur);
diff --git a/gnucash/gnome/dialog-sx-editor.c b/gnucash/gnome/dialog-sx-editor.c
index 4486cf3c0..87b1d953d 100644
--- a/gnucash/gnome/dialog-sx-editor.c
+++ b/gnucash/gnome/dialog-sx-editor.c
@@ -33,6 +33,7 @@
 \********************************************************************/
 
 #include <config.h>
+#include <stdbool.h>
 
 #include <gtk/gtk.h>
 #include <glib/gi18n.h>
@@ -114,7 +115,7 @@ struct _GncSxEditorDialog
     GncDenseCalStore *dense_cal_model;
     GncDenseCal *example_cal;
 
-    GtkEditable *nameEntry;
+    GtkEntry *nameEntry;
 
     GtkLabel *lastOccurLabel;
 
@@ -264,9 +265,9 @@ editor_ok_button_clicked_cb (GtkButton *b, GncSxEditorDialog *sxed)
 static gboolean
 gnc_sxed_check_name_changed (GncSxEditorDialog *sxed)
 {
-    const char *name = gtk_entry_get_text (GTK_ENTRY (sxed->nameEntry));
+    const char *name = gtk_entry_get_text (sxed->nameEntry);
 
-    if (strlen (name) == 0)
+    if (!name || !name[0])
         return TRUE;
 
     if (xaccSchedXactionGetName (sxed->sx) == NULL ||
@@ -481,33 +482,27 @@ check_credit_debit_balance (gpointer key, gpointer val, gpointer ud)
 static gboolean
 gnc_sxed_check_names (GncSxEditorDialog *sxed)
 {
-    gchar *name, *nameKey;
-    gboolean nameExists, nameHasChanged;
-    GList *sxList;
-
-    name = gtk_editable_get_chars (GTK_EDITABLE (sxed->nameEntry), 0, -1);
-    if (strlen (name) == 0)
+    const gchar *name = gtk_entry_get_text (sxed->nameEntry);
+    if (!name || !name[0])
     {
         const char *sx_has_no_name_msg =
             _("Please name the Scheduled Transaction.");
         gnc_error_dialog (GTK_WINDOW (sxed->dialog), "%s", sx_has_no_name_msg);
-        g_free (name);
         return FALSE;
 
     }
 
-    nameExists = FALSE;
-    nameKey = g_utf8_collate_key (name, -1);
-    nameHasChanged =
+    bool nameExists = FALSE;
+    gchar *nameKey = g_utf8_collate_key (name, -1);
+    bool nameHasChanged =
         (xaccSchedXactionGetName (sxed->sx) == NULL)
         || (strcmp (xaccSchedXactionGetName (sxed->sx), name) != 0);
-    for (sxList = gnc_book_get_schedxactions (gnc_get_current_book ())->sx_list;
+    for (GList *sxList = gnc_book_get_schedxactions (gnc_get_current_book ())->sx_list;
          nameHasChanged && !nameExists && sxList;
          sxList = sxList->next)
     {
-        char *existingName, *existingNameKey;
-        existingName = xaccSchedXactionGetName ((SchedXaction*)sxList->data);
-        existingNameKey = g_utf8_collate_key (existingName, -1);
+        const char *existingName = xaccSchedXactionGetName ((SchedXaction*)sxList->data);
+        char *existingNameKey = g_utf8_collate_key (existingName, -1);
         nameExists |=  (strcmp (nameKey, existingNameKey) == 0);
         g_free (existingNameKey);
     }
@@ -519,12 +514,8 @@ gnc_sxed_check_names (GncSxEditorDialog *sxed)
               "Are you sure you want to name this one the same?");
         if (!gnc_verify_dialog (GTK_WINDOW (sxed->dialog), FALSE,
                                 sx_has_existing_name_msg, name))
-        {
-            g_free (name);
             return FALSE;
-        }
     }
-    g_free (name);
     return TRUE;
 }
 
@@ -668,7 +659,7 @@ gnc_sxed_split_calculate_formula (GncSxEditorDialog *sxed, Split *s,
     qof_instance_get (QOF_INSTANCE (s),
                       key, &str,
                       NULL);
-    if (str == NULL || strlen (str) == 0)
+    if (!str || !str[0])
     {
         if (str)
             g_free (str);
@@ -910,13 +901,9 @@ gnc_sxed_save_sx (GncSxEditorDialog *sxed)
     gnc_sx_begin_edit (sxed->sx);
 
     /* name */
-    {
-        char *name;
-
-        name = gtk_editable_get_chars (sxed->nameEntry, 0, -1);
+    const gchar *name = gtk_entry_get_text (sxed->nameEntry);
+    if (name && *name)
         xaccSchedXactionSetName (sxed->sx, name);
-        g_free (name);
-    }
 
     /* date */
     {
@@ -1209,7 +1196,7 @@ gnc_ui_scheduled_xaction_editor_dialog_create (GtkWindow *parent,
     /* Connect the Widgets */
     sxed->dialog = GTK_WIDGET (gtk_builder_get_object (builder, "scheduled_transaction_editor_dialog"));
     sxed->notebook = GTK_NOTEBOOK (gtk_builder_get_object (builder, "editor_notebook"));
-    sxed->nameEntry = GTK_EDITABLE (gtk_builder_get_object (builder, "sxe_name"));
+    sxed->nameEntry = GTK_ENTRY (gtk_builder_get_object (builder, "sxe_name"));
     sxed->enabledOpt = GTK_TOGGLE_BUTTON (gtk_builder_get_object (builder, "enabled_opt"));
     sxed->autocreateOpt = GTK_TOGGLE_BUTTON (gtk_builder_get_object (builder, "autocreate_opt"));
     sxed->notifyOpt = GTK_TOGGLE_BUTTON (gtk_builder_get_object (builder, "notify_opt"));
@@ -1405,7 +1392,7 @@ schedXact_editor_populate (GncSxEditorDialog *sxed)
     name = xaccSchedXactionGetName (sxed->sx);
     if (name)
     {
-        gtk_entry_set_text (GTK_ENTRY (sxed->nameEntry), name );
+        gtk_entry_set_text (sxed->nameEntry, name);
     }
     {
         gd = xaccSchedXactionGetLastOccurDate (sxed->sx);
diff --git a/gnucash/gnome/dialog-sx-from-trans.c b/gnucash/gnome/dialog-sx-from-trans.c
index 4e3c68d1c..49d81eff6 100644
--- a/gnucash/gnome/dialog-sx-from-trans.c
+++ b/gnucash/gnome/dialog-sx-from-trans.c
@@ -177,23 +177,22 @@ sxftd_get_end_info(SXFromTransInfo *sxfti)
 
     if (gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON(sxfti->oc_but) ))
     {
-        gchar *text, *endptr;
         guint n_occs;
 
-        text = gtk_editable_get_chars(GTK_EDITABLE(sxfti->n_occurences), 0, -1);
-        if (text == NULL || strlen(text) == 0)
+        const gchar *text = gtk_entry_get_text (GTK_ENTRY(sxfti->n_occurences));
+        if (!text || !text[0])
         {
             n_occs = 0;
         }
         else
         {
+            gchar *endptr;
             n_occs = strtoul(text, &endptr, 10);
             if ( !endptr )
             {
                 n_occs = -1;
             }
         }
-        g_free(text);
 
         retval.type = END_AFTER_N_OCCS;
         retval.n_occurrences = n_occs;
@@ -447,7 +446,6 @@ sxftd_init( SXFromTransInfo *sxfti )
 static guint
 sxftd_compute_sx(SXFromTransInfo *sxfti)
 {
-    gchar *name;
     GDate date;
     GList *schedule = NULL;
     getEndTuple end_info;
@@ -456,10 +454,7 @@ sxftd_compute_sx(SXFromTransInfo *sxfti)
     SchedXaction *sx = sxfti->sx;
 
     /* get the name */
-    name = gtk_editable_get_chars(GTK_EDITABLE(sxfti->name), 0, -1);
-
-    xaccSchedXactionSetName(sx, name);
-    g_free(name);
+    xaccSchedXactionSetName(sx, gtk_entry_get_text (sxfti->name));
 
     gnc_gdate_set_time64( &date, gnc_date_edit_get_date( sxfti->startDateGDE ) );
 
@@ -695,14 +690,8 @@ sxftd_update_example_cal( SXFromTransInfo *sxfti )
     g_date_clear(&nextDate, 1);
     recurrenceListNextInstance(schedule, &date, &nextDate);
 
-    {
-        gchar *name;
-        /* get the name */
-        name = NULL;
-        name = gtk_editable_get_chars(GTK_EDITABLE(sxfti->name), 0, -1);
-        gnc_dense_cal_store_update_name(sxfti->dense_cal_model, name);
-        g_free(name);
-    }
+    gnc_dense_cal_store_update_name (sxfti->dense_cal_model,
+                                     gtk_entry_get_text (sxfti->name));
 
     {
         gchar *schedule_desc;

commit 8d44c1c38c8316985a17a18c86aee5dfff6cb0e3
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Mon Feb 20 09:53:11 2023 +0800

    [dialog-customer|employee|job|vendor] harmonize title setter

diff --git a/gnucash/gnome/dialog-customer.c b/gnucash/gnome/dialog-customer.c
index c5c308542..4f98c9763 100644
--- a/gnucash/gnome/dialog-customer.c
+++ b/gnucash/gnome/dialog-customer.c
@@ -399,28 +399,26 @@ void
 gnc_customer_name_changed_cb (GtkWidget *widget, gpointer data)
 {
     CustomerWindow *cw = data;
-    char *fullname, *title;
-    const char *id,  *name;
+    char *title;
 
     if (!cw)
         return;
 
-    name = gtk_entry_get_text (GTK_ENTRY (cw->company_entry));
+    const char *header = (cw->dialog_type == EDIT_CUSTOMER) ?
+        _("Edit Customer") : _("New Customer");
+
+    const char *name = gtk_entry_get_text (GTK_ENTRY (cw->company_entry));
     if (!name || *name == '\0')
         name = _("<No name>");
 
-    id = gtk_entry_get_text (GTK_ENTRY (cw->id_entry));
-
-    fullname = g_strconcat (name, " (", id, ")", (char *)NULL);
-
-    if (cw->dialog_type == EDIT_CUSTOMER)
-        title = g_strconcat (_("Edit Customer"), " - ", fullname, (char *)NULL);
+    const char *id = gtk_entry_get_text (GTK_ENTRY (cw->id_entry));
+    if (id && *id)
+        title = g_strdup_printf ("%s - %s (%s)", header, name, id);
     else
-        title = g_strconcat (_("New Customer"), " - ", fullname, (char *)NULL);
+        title = g_strdup_printf ("%s - %s", header, name);
 
     gtk_window_set_title (GTK_WINDOW (cw->dialog), title);
 
-    g_free (fullname);
     g_free (title);
 }
 
diff --git a/gnucash/gnome/dialog-employee.c b/gnucash/gnome/dialog-employee.c
index 47d0c4d47..64e111ab8 100644
--- a/gnucash/gnome/dialog-employee.c
+++ b/gnucash/gnome/dialog-employee.c
@@ -280,28 +280,26 @@ void
 gnc_employee_name_changed_cb (GtkWidget *widget, gpointer data)
 {
     EmployeeWindow *ew = data;
-    char *fullname, *title;
-    const char *name, *id;
+    char *title;
 
     if (!ew)
         return;
 
-    name = gtk_entry_get_text (GTK_ENTRY (ew->name_entry));
+    const char *header = (ew->dialog_type == EDIT_EMPLOYEE) ?
+        _("Edit Employee") : _("New Employee");
+
+    const char *name = gtk_entry_get_text (GTK_ENTRY (ew->name_entry));
     if (!name || *name == '\0')
         name = _("<No name>");
 
-    id = gtk_entry_get_text (GTK_ENTRY (ew->id_entry));
-
-    fullname = g_strconcat (name, " (", id, ")", (char *)NULL);
-
-    if (ew->dialog_type == EDIT_EMPLOYEE)
-        title = g_strconcat (_("Edit Employee"), " - ", fullname, (char *)NULL);
+    const char *id = gtk_entry_get_text (GTK_ENTRY (ew->id_entry));
+    if (id && *id)
+        title = g_strdup_printf ("%s - %s (%s)", header, name, id);
     else
-        title = g_strconcat (_("New Employee"), " - ", fullname, (char *)NULL);
+        title = g_strdup_printf ("%s - %s", header, name);
 
     gtk_window_set_title (GTK_WINDOW (ew->dialog), title);
 
-    g_free (fullname);
     g_free (title);
 }
 
diff --git a/gnucash/gnome/dialog-job.c b/gnucash/gnome/dialog-job.c
index f78cf005e..40c31b745 100644
--- a/gnucash/gnome/dialog-job.c
+++ b/gnucash/gnome/dialog-job.c
@@ -237,28 +237,26 @@ void
 gnc_job_name_changed_cb (GtkWidget *widget, gpointer data)
 {
     JobWindow *jw = data;
-    char *fullname, *title;
-    const char *name, *id;
+    char *title;
 
     if (!jw)
         return;
 
-    name = gtk_entry_get_text (GTK_ENTRY (jw->name_entry));
+    const char *header = (jw->dialog_type == EDIT_JOB) ?
+        _("Edit Job") : _("New Job");
+
+    const char *name = gtk_entry_get_text (GTK_ENTRY (jw->name_entry));
     if (!name || *name == '\0')
         name = _("<No name>");
 
-    id = gtk_entry_get_text (GTK_ENTRY (jw->id_entry));
-
-    fullname = g_strconcat (name, " (", id, ")", (char *)NULL);
-
-    if (jw->dialog_type == EDIT_JOB)
-        title = g_strconcat (_("Edit Job"), " - ", fullname, (char *)NULL);
+    const char *id = gtk_entry_get_text (GTK_ENTRY (jw->id_entry));
+    if (id && *id)
+        title = g_strdup_printf ("%s - %s (%s)", header, name, id);
     else
-        title = g_strconcat (_("New Job"), " - ", fullname, (char *)NULL);
+        title = g_strdup_printf ("%s - %s", header, name);
 
     gtk_window_set_title (GTK_WINDOW (jw->dialog), title);
 
-    g_free (fullname);
     g_free (title);
 }
 
diff --git a/gnucash/gnome/dialog-vendor.c b/gnucash/gnome/dialog-vendor.c
index 990a1a2a3..61575f2d1 100644
--- a/gnucash/gnome/dialog-vendor.c
+++ b/gnucash/gnome/dialog-vendor.c
@@ -267,29 +267,26 @@ void
 gnc_vendor_name_changed_cb (GtkWidget *widget, gpointer data)
 {
     VendorWindow *vw = data;
-    char *name, *id, *fullname, *title;
+    char *title;
 
     if (!vw)
         return;
 
-    name = gtk_editable_get_chars (GTK_EDITABLE (vw->company_entry), 0, -1);
-    if (!name || *name == '\0')
-        name = g_strdup (_("<No name>"));
-
-    id = gtk_editable_get_chars (GTK_EDITABLE (vw->id_entry), 0, -1);
+    const char *header = (vw->dialog_type == EDIT_VENDOR) ?
+        _("Edit Vendor") : _("New Vendor");
 
-    fullname = g_strconcat (name, " (", id, ")", (char *)NULL);
+    const char *name = gtk_entry_get_text (GTK_ENTRY (vw->company_entry));
+    if (!name || *name == '\0')
+        name = _("<No name>");
 
-    if (vw->dialog_type == EDIT_VENDOR)
-        title = g_strconcat (_("Edit Vendor"), " - ", fullname, (char *)NULL);
+    const char *id = gtk_entry_get_text (GTK_ENTRY (vw->id_entry));
+    if (id && *id)
+        title = g_strdup_printf ("%s - %s (%s)", header, name, id);
     else
-        title = g_strconcat (_("New Vendor"), " - ", fullname, (char *)NULL);
+        title = g_strdup_printf ("%s - %s", header, name);
 
     gtk_window_set_title (GTK_WINDOW (vw->dialog), title);
 
-    g_free (name);
-    g_free (id);
-    g_free (fullname);
     g_free (title);
 }
 



Summary of changes:
 gnucash/gnome-utils/dialog-account.c      | 10 ++-----
 gnucash/gnome-utils/gnc-dense-cal-store.c |  4 +--
 gnucash/gnome-utils/gnc-dense-cal-store.h |  4 +--
 gnucash/gnome/dialog-customer.c           | 20 ++++++-------
 gnucash/gnome/dialog-employee.c           | 20 ++++++-------
 gnucash/gnome/dialog-job.c                | 20 ++++++-------
 gnucash/gnome/dialog-sx-editor.c          | 47 +++++++++++--------------------
 gnucash/gnome/dialog-sx-from-trans.c      | 23 ++++-----------
 gnucash/gnome/dialog-vendor.c             | 23 +++++++--------
 gnucash/import-export/import-backend.cpp  | 16 +++++++----
 10 files changed, 77 insertions(+), 110 deletions(-)



More information about the gnucash-changes mailing list