gnucash maint: Multiple changes pushed

John Ralls jralls at code.gnucash.org
Tue Jun 8 14:58:50 EDT 2021


Updated	 via  https://github.com/Gnucash/gnucash/commit/66685f04 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/8f063159 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/862e0ca5 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/08b578a2 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/510e186e (commit)
	 via  https://github.com/Gnucash/gnucash/commit/92b877cc (commit)
	 via  https://github.com/Gnucash/gnucash/commit/faaed371 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/51b06252 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/7ee9dac2 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/db6f2337 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/788166b3 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/b728a331 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/32f605ae (commit)
	 via  https://github.com/Gnucash/gnucash/commit/6facaa06 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/748dbf54 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/435bb1c7 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/5779e72d (commit)
	 via  https://github.com/Gnucash/gnucash/commit/d55060c0 (commit)
	from  https://github.com/Gnucash/gnucash/commit/115b7bc2 (commit)



commit 66685f042ecb53bdcd5b2c67537b37cd4de5aeaa
Merge: 115b7bc2b 8f0631590
Author: John Ralls <jralls at ceridwen.us>
Date:   Tue Jun 8 11:57:18 2021 -0700

    Merge Bob Fewell's 'bug796761' into maint.

diff --cc gnucash/gnome-utils/gnc-amount-edit.c
index b334d24b9,8e9a5f665..1c6cacf23
--- a/gnucash/gnome-utils/gnc-amount-edit.c
+++ b/gnucash/gnome-utils/gnc-amount-edit.c
@@@ -139,6 -155,9 +155,10 @@@ gnc_amount_edit_init (GNCAmountEdit *ga
      gae->print_info = gnc_default_print_info (FALSE);
      gae->fraction = 0;
      gae->evaluate_on_enter = FALSE;
+     gae->validate_on_change = FALSE;
+     gae->block_changed = FALSE;
+     gae->show_warning_symbol = TRUE;
++    gae->disposed = FALSE;
  
      // Set the name for this widget so it can be easily manipulated with css
      gtk_widget_set_name (GTK_WIDGET(gae), "gnc-id-amount-edit");

commit 8f0631590b9952bf98c76da842327b348a87c6aa
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Mon May 31 11:01:23 2021 +0100

    Add validation to the use of GNCAmountEdit for rest
    
    Add validation of GNCAmountEdit where used in the remaining source
    files and make changes required for the change in GNCAmountEdit widget.

diff --git a/gnucash/gnome-utils/dialog-transfer.c b/gnucash/gnome-utils/dialog-transfer.c
index b7ba77157..543d0c608 100644
--- a/gnucash/gnome-utils/dialog-transfer.c
+++ b/gnucash/gnome-utils/dialog-transfer.c
@@ -529,6 +529,8 @@ gnc_xfer_dialog_from_tree_selection_changed_cb (GtkTreeSelection *selection,
     gnc_amount_edit_set_fraction (GNC_AMOUNT_EDIT (xferData->amount_edit),
                                   xaccAccountGetCommoditySCU (account));
 
+    gnc_amount_edit_evaluate (GNC_AMOUNT_EDIT (xferData->amount_edit), NULL);
+
     gnc_xfer_dialog_curr_acct_activate(xferData);
 
     /* Reload the xferDialog quickfill if it is based on the from account */
@@ -561,6 +563,8 @@ gnc_xfer_dialog_to_tree_selection_changed_cb (GtkTreeSelection *selection, gpoin
     gnc_amount_edit_set_fraction (GNC_AMOUNT_EDIT (xferData->to_amount_edit),
                                   xaccAccountGetCommoditySCU (account));
 
+    gnc_amount_edit_evaluate (GNC_AMOUNT_EDIT (xferData->to_amount_edit), NULL);
+
     gnc_xfer_dialog_curr_acct_activate(xferData);
 
     /* Reload the xferDialog quickfill if it is based on the to account */
diff --git a/gnucash/gnome/dialog-price-editor.c b/gnucash/gnome/dialog-price-editor.c
index 05706e5a3..94812eb65 100644
--- a/gnucash/gnome/dialog-price-editor.c
+++ b/gnucash/gnome/dialog-price-editor.c
@@ -246,6 +246,7 @@ pedit_dialog_replace_found_price (PriceEditDialog *pedit_dialog,
 static const char *
 gui_to_price (PriceEditDialog *pedit_dialog)
 {
+    GNCPrintAmountInfo print_info;
     gnc_commodity *commodity;
     gnc_commodity *currency;
     gchar         *name_space;
@@ -274,6 +275,11 @@ gui_to_price (PriceEditDialog *pedit_dialog)
     type = type_index_to_string
            (gtk_combo_box_get_active (GTK_COMBO_BOX (pedit_dialog->type_combobox)));
 
+    print_info = gnc_commodity_print_info (currency, FALSE);
+    gnc_amount_edit_set_print_info (GNC_AMOUNT_EDIT (pedit_dialog->price_edit), print_info);
+    gnc_amount_edit_set_fraction (GNC_AMOUNT_EDIT (pedit_dialog->price_edit),
+                                  gnc_commodity_get_fraction (currency));
+
     if (!gnc_amount_edit_evaluate (GNC_AMOUNT_EDIT (pedit_dialog->price_edit), NULL))
         return _("You must enter a valid amount.");
 
@@ -521,16 +527,17 @@ gnc_price_pedit_dialog_create (GtkWidget *parent,
     w = gnc_amount_edit_new ();
     pedit_dialog->price_edit = w;
     gtk_box_pack_start (GTK_BOX (box), w, TRUE, TRUE, 0);
+    entry = gnc_amount_edit_gtk_entry (GNC_AMOUNT_EDIT (w));
     gnc_amount_edit_set_evaluate_on_enter (GNC_AMOUNT_EDIT (w), TRUE);
-    print_info = gnc_default_price_print_info (gnc_currency_edit_get_currency (GNC_CURRENCY_EDIT (pedit_dialog->currency_edit)));
+    print_info = gnc_default_price_print_info (gnc_currency_edit_get_currency
+                                              (GNC_CURRENCY_EDIT (pedit_dialog->currency_edit)));
     gnc_amount_edit_set_print_info (GNC_AMOUNT_EDIT (w), print_info);
-    gtk_entry_set_activates_default(GTK_ENTRY(w), TRUE);
+    gtk_entry_set_activates_default(GTK_ENTRY(entry), TRUE);
     gtk_widget_show (w);
     label = GTK_WIDGET(gtk_builder_get_object (builder, "price_label"));
     gtk_label_set_mnemonic_widget (GTK_LABEL(label), w);
 
-    entry = gnc_amount_edit_gtk_entry (GNC_AMOUNT_EDIT (w));
-    g_signal_connect (G_OBJECT (entry), "changed",
+    g_signal_connect (G_OBJECT (w), "changed",
                       G_CALLBACK (pedit_data_changed_cb), pedit_dialog);
 
     w = GTK_WIDGET(gtk_builder_get_object (builder, "pd_cancel_button"));
diff --git a/gnucash/gnome/window-autoclear.c b/gnucash/gnome/window-autoclear.c
index f5270be82..aeb18b07b 100644
--- a/gnucash/gnome/window-autoclear.c
+++ b/gnucash/gnome/window-autoclear.c
@@ -122,28 +122,41 @@ void
 gnc_autoclear_window_ok_cb (GtkWidget *widget,
                             AutoClearWindow *data)
 {
-    GList *toclear_list;
+    GList *toclear_list = NULL;
     gnc_numeric toclear_value;
     gchar *errmsg = NULL;
+    GError* error = NULL;
 
     g_return_if_fail (widget && data);
 
-    toclear_value = gnc_amount_edit_get_amount(data->end_value);
+    /* test for valid value */
+    if (!gnc_amount_edit_evaluate (GNC_AMOUNT_EDIT(data->end_value), &error))
+    {
+        errmsg = g_strdup (error->message);
+        g_error_free (error);
+    }
+    else
+    {
+        toclear_value = gnc_amount_edit_get_amount(data->end_value);
 
-    if (gnc_reverse_balance(data->account))
-        toclear_value = gnc_numeric_neg (toclear_value);
+        if (gnc_reverse_balance(data->account))
+            toclear_value = gnc_numeric_neg (toclear_value);
 
-    toclear_value = gnc_numeric_convert
-        (toclear_value, xaccAccountGetCommoditySCU(data->account), GNC_HOW_RND_ROUND);
+        toclear_value = gnc_numeric_convert
+            (toclear_value, xaccAccountGetCommoditySCU(data->account), GNC_HOW_RND_ROUND);
 
-    toclear_list = gnc_account_get_autoclear_splits
-        (data->account, toclear_value, &errmsg);
+        toclear_list = gnc_account_get_autoclear_splits
+            (data->account, toclear_value, &errmsg);
+    }
 
     if (errmsg)
     {
+        GtkWidget *entry = gnc_amount_edit_gtk_entry (GNC_AMOUNT_EDIT(data->end_value));
         gtk_label_set_text (data->status_label, errmsg);
-        gnc_amount_edit_set_amount (data->end_value, toclear_value);
-        gtk_editable_select_region (GTK_EDITABLE (data->end_value), 0, -1);
+        if (gnc_numeric_check (toclear_value) == 0)
+            gnc_amount_edit_set_amount (data->end_value, toclear_value);
+        gtk_widget_grab_focus (GTK_WIDGET(entry));
+        gnc_amount_edit_select_region (GNC_AMOUNT_EDIT(data->end_value), 0, -1);
         g_free (errmsg);
     }
     else
@@ -197,6 +210,8 @@ autoClearWindow (GtkWidget *parent, Account *account)
     AutoClearWindow *data;
     char *title;
     gnc_numeric after;
+    GNCPrintAmountInfo print_info;
+    gnc_commodity *currency;
 
     data = g_new0 (AutoClearWindow, 1);
     data->account = account;
@@ -217,6 +232,13 @@ autoClearWindow (GtkWidget *parent, Account *account)
 
     /* Add amount edit box */
     data->end_value = GNC_AMOUNT_EDIT(gnc_amount_edit_new());
+
+    currency = xaccAccountGetCommodity (account);
+    print_info = gnc_commodity_print_info (currency, FALSE);
+    gnc_amount_edit_set_print_info (GNC_AMOUNT_EDIT(data->end_value), print_info);
+    gnc_amount_edit_set_fraction (GNC_AMOUNT_EDIT(data->end_value),
+                                  gnc_commodity_get_fraction (currency));
+
     g_signal_connect(GTK_WIDGET(data->end_value), "activate",
                      G_CALLBACK(gnc_autoclear_window_ok_cb), data);
 
@@ -232,7 +254,7 @@ autoClearWindow (GtkWidget *parent, Account *account)
         after = gnc_numeric_neg(after);
     gnc_amount_edit_set_amount (GNC_AMOUNT_EDIT (data->end_value), after);
     gtk_widget_grab_focus(GTK_WIDGET(data->end_value));
-    gtk_editable_select_region (GTK_EDITABLE (data->end_value), 0, -1);
+    gnc_amount_edit_select_region (GNC_AMOUNT_EDIT (data->end_value), 0, -1);
 
     data->status_label = GTK_LABEL(gtk_builder_get_object (builder, "status_label"));
 

commit 862e0ca579e2ca2cc269b8d452adeadb1c8337eb
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Mon May 31 11:00:39 2021 +0100

    Add validation to the use of GNCAmountEdit for search
    
    Add validation of GNCAmountEdit where used in the search source
    files and make changes required for the change in GNCAmountEdit widget.

diff --git a/gnucash/gnome-search/search-double.c b/gnucash/gnome-search/search-double.c
index c815f27d2..9131ef0f1 100644
--- a/gnucash/gnome-search/search-double.c
+++ b/gnucash/gnome-search/search-double.c
@@ -30,6 +30,7 @@
 
 #include "gnc-amount-edit.h"
 #include "qof.h"
+#include "gnc-gui-query.h"
 
 #include "search-double.h"
 #include "search-core-utils.h"
@@ -148,12 +149,21 @@ static gboolean
 gncs_validate (GNCSearchCoreType *fe)
 {
     GNCSearchDouble *fi = (GNCSearchDouble *)fe;
+    GNCSearchDoublePrivate *priv;
     gboolean valid = TRUE;
+    GError *error = NULL;
 
     g_return_val_if_fail (fi, FALSE);
     g_return_val_if_fail (IS_GNCSEARCH_DOUBLE (fi), FALSE);
 
-    /* XXX */
+    priv = _PRIVATE(fi);
+
+    if (!gnc_amount_edit_evaluate (GNC_AMOUNT_EDIT(priv->gae), &error))
+    {
+        gnc_error_dialog (GTK_WINDOW(priv->parent), "%s", error->message);
+        valid = FALSE;
+        g_error_free (error);
+    }
 
     return valid;
 }
diff --git a/gnucash/gnome-search/search-int64.c b/gnucash/gnome-search/search-int64.c
index f0c98930b..754e10449 100644
--- a/gnucash/gnome-search/search-int64.c
+++ b/gnucash/gnome-search/search-int64.c
@@ -30,6 +30,7 @@
 
 #include "gnc-amount-edit.h"
 #include "qof.h"
+#include "gnc-gui-query.h"
 
 #include "search-int64.h"
 #include "search-core-utils.h"
@@ -149,12 +150,21 @@ static gboolean
 gncs_validate (GNCSearchCoreType *fe)
 {
     GNCSearchInt64 *fi = (GNCSearchInt64 *)fe;
+    GNCSearchInt64Private *priv;
     gboolean valid = TRUE;
+    GError *error = NULL;
 
     g_return_val_if_fail (fi, FALSE);
     g_return_val_if_fail (IS_GNCSEARCH_INT64 (fi), FALSE);
 
-    /* XXX */
+    priv = _PRIVATE(fi);
+
+    if (!gnc_amount_edit_evaluate (GNC_AMOUNT_EDIT(priv->gae), &error))
+    {
+        gnc_error_dialog (GTK_WINDOW(priv->parent), "%s", error->message);
+        valid = FALSE;
+        g_error_free (error);
+    }
 
     return valid;
 }
diff --git a/gnucash/gnome-search/search-numeric.c b/gnucash/gnome-search/search-numeric.c
index 4e57b6ab7..f82988721 100644
--- a/gnucash/gnome-search/search-numeric.c
+++ b/gnucash/gnome-search/search-numeric.c
@@ -30,6 +30,7 @@
 
 #include "gnc-amount-edit.h"
 #include "qof.h"
+#include "gnc-gui-query.h"
 
 #include "search-numeric.h"
 #include "search-core-utils.h"
@@ -178,13 +179,21 @@ static gboolean
 gncs_validate (GNCSearchCoreType *fe)
 {
     GNCSearchNumeric *fi = (GNCSearchNumeric *)fe;
+    GNCSearchNumericPrivate *priv;
     gboolean valid = TRUE;
+    GError *error = NULL;
 
     g_return_val_if_fail (fi, FALSE);
     g_return_val_if_fail (IS_GNCSEARCH_NUMERIC (fi), FALSE);
 
-    /* XXX */
+    priv = _PRIVATE(fi);
 
+    if (!gnc_amount_edit_evaluate (GNC_AMOUNT_EDIT(priv->gae), &error))
+    {
+        gnc_error_dialog (GTK_WINDOW(priv->parent), "%s", error->message);
+        valid = FALSE;
+        g_error_free (error);
+    }
     return valid;
 }
 

commit 08b578a2e44d485275f24010927c69f5879df8a1
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Mon May 31 10:59:52 2021 +0100

    Add validation to the use of GNCAmountEdit for fincalc
    
    Add validation of GNCAmountEdit where used in the fincalc source files
    and make changes required for the change in GNCAmountEdit widget.

diff --git a/gnucash/gnome/dialog-fincalc.c b/gnucash/gnome/dialog-fincalc.c
index 286f3d98c..f84e3534d 100644
--- a/gnucash/gnome/dialog-fincalc.c
+++ b/gnucash/gnome/dialog-fincalc.c
@@ -181,6 +181,7 @@ static void
 gui_to_fi(FinCalcDialog *fcd)
 {
     GtkToggleButton *toggle;
+    GtkWidget *entry;
     gnc_numeric npp;
     int i;
     const gchar *text;
@@ -189,7 +190,8 @@ gui_to_fi(FinCalcDialog *fcd)
         return;
 
     /* treat PAYMENT_PERIODS as a plain GtkEntry */
-    text = gtk_entry_get_text (GTK_ENTRY(GNC_AMOUNT_EDIT(fcd->amounts[PAYMENT_PERIODS])));
+    entry = gnc_amount_edit_gtk_entry (GNC_AMOUNT_EDIT(fcd->amounts[PAYMENT_PERIODS]));
+    text = gtk_entry_get_text (GTK_ENTRY(entry));
     if (text && *text)
     {
         gnc_numeric out;
@@ -240,7 +242,8 @@ fincalc_update_calc_button_cb(GtkWidget *unused, FinCalcDialog *fcd)
 
     for (i = 0; i < NUM_FIN_CALC_VALUES; i++)
     {
-        text = gtk_entry_get_text(GTK_ENTRY(fcd->amounts[i]));
+        GtkWidget *entry = gnc_amount_edit_gtk_entry (GNC_AMOUNT_EDIT(fcd->amounts[i]));
+        text = gtk_entry_get_text(GTK_ENTRY(entry));
         if ((text == NULL) || (*text == '\0'))
         {
             gtk_widget_set_sensitive(GTK_WIDGET(fcd->calc_button), TRUE);
@@ -284,10 +287,14 @@ fincalc_compounding_radio_toggled(GtkToggleButton *togglebutton, gpointer data)
 void
 fincalc_amount_clear_clicked_cb(GtkButton *button, FinCalcDialog *fcd)
 {
-    GtkEntry * edit = GTK_ENTRY(g_object_get_data(G_OBJECT(button), "edit"));
+    GNCAmountEdit * edit = GNC_AMOUNT_EDIT(g_object_get_data(G_OBJECT(button), "edit"));
+    GtkWidget * entry = gnc_amount_edit_gtk_entry (edit);
+    gnc_numeric value;
 
-    if (edit && GTK_IS_ENTRY(edit))
-        gtk_entry_set_text(edit, "");
+    if (entry && GTK_IS_ENTRY(entry))
+        gtk_entry_set_text(GTK_ENTRY(entry), "");
+
+    gnc_amount_edit_expr_is_valid (edit, &value, TRUE, NULL);
 }
 
 void
@@ -340,7 +347,8 @@ can_calc_value(FinCalcDialog *fcd, FinCalcValue value, int *error_item)
     for (i = 0; i < NUM_FIN_CALC_VALUES; i++)
         if (i != value)
         {
-            string = gtk_entry_get_text(GTK_ENTRY(fcd->amounts[i]));
+            GtkWidget *entry = gnc_amount_edit_gtk_entry (GNC_AMOUNT_EDIT (fcd->amounts[i]));
+            string = gtk_entry_get_text(GTK_ENTRY(entry));
             if ((string == NULL) || (*string == '\0'))
             {
                 *error_item = i;
@@ -387,10 +395,9 @@ can_calc_value(FinCalcDialog *fcd, FinCalcValue value, int *error_item)
         {
             /* treat PAYMENT_PERIODS as a plain GtkEntry */
             GNCAmountEdit *edit = GNC_AMOUNT_EDIT(fcd->amounts[PAYMENT_PERIODS]);
-            const gchar *text = gtk_entry_get_text (GTK_ENTRY(edit));
-            gboolean result = string_to_gnc_numeric (text, &nvalue);
+            gint result = gnc_amount_edit_expr_is_valid (edit, &nvalue, TRUE, NULL);
 
-            if (!result)
+            if (result == 1)
             {
                 *error_item = PAYMENT_PERIODS;
                 return bad_exp;
@@ -430,9 +437,9 @@ calc_value(FinCalcDialog *fcd, FinCalcValue value)
 
         gnc_error_dialog (GTK_WINDOW (fcd->dialog), "%s", string);
         if (error_item == 0)
-            entry = fcd->amounts[0];
+            entry = gnc_amount_edit_gtk_entry (GNC_AMOUNT_EDIT(fcd->amounts[0]));
         else
-            entry = fcd->amounts[error_item];
+            entry = gnc_amount_edit_gtk_entry (GNC_AMOUNT_EDIT(fcd->amounts[error_item]));
         gtk_widget_grab_focus (entry);
         return;
     }
@@ -473,7 +480,8 @@ fincalc_calc_clicked_cb(GtkButton *button, FinCalcDialog *fcd)
 
     for (i = 0; i < NUM_FIN_CALC_VALUES; i++)
     {
-        text = gtk_entry_get_text(GTK_ENTRY(fcd->amounts[i]));
+        GtkWidget *entry = gnc_amount_edit_gtk_entry (GNC_AMOUNT_EDIT(fcd->amounts[i]));
+        text = gtk_entry_get_text(GTK_ENTRY(entry));
         if ((text != NULL) && (*text != '\0'))
             continue;
         calc_value(fcd, i);
@@ -545,6 +553,7 @@ fincalc_init_gae (GNCAmountEdit *edit,
                   gint fraction)
 {
     GNCPrintAmountInfo print_info;
+    GtkWidget *entry;
 
     print_info = gnc_integral_print_info ();
     print_info.min_decimal_places = min_places;
@@ -553,7 +562,8 @@ fincalc_init_gae (GNCAmountEdit *edit,
     gnc_amount_edit_set_print_info (edit, print_info);
     gnc_amount_edit_set_fraction (edit, fraction);
     gnc_amount_edit_set_evaluate_on_enter (edit, TRUE);
-    gtk_entry_set_alignment (GTK_ENTRY(edit), 1.0);
+    entry = gnc_amount_edit_gtk_entry (edit);
+    gtk_entry_set_alignment (GTK_ENTRY(entry), 1.0);
 }
 
 /** Initialize an edit field that will display a number in the users
@@ -567,6 +577,7 @@ fincalc_init_commodity_gae (GNCAmountEdit *edit)
     GNCPrintAmountInfo print_info;
     gnc_commodity *commodity;
     gint fraction;
+    GtkWidget *entry;
 
     commodity = gnc_default_currency();
     fraction = gnc_commodity_get_fraction(commodity);
@@ -575,7 +586,8 @@ fincalc_init_commodity_gae (GNCAmountEdit *edit)
     gnc_amount_edit_set_print_info (edit, print_info);
     gnc_amount_edit_set_fraction (edit, fraction);
     gnc_amount_edit_set_evaluate_on_enter (edit, TRUE);
-    gtk_entry_set_alignment (GTK_ENTRY(edit), 1.0);
+    entry = gnc_amount_edit_gtk_entry (edit);
+    gtk_entry_set_alignment (GTK_ENTRY(entry), 1.0);
 }
 
 void
@@ -622,7 +634,7 @@ gnc_ui_fincalc_dialog_create(GtkWindow *parent)
     edit = gnc_amount_edit_new();
     fincalc_init_gae (GNC_AMOUNT_EDIT (edit), 0, 0, 1);
     fcd->amounts[PAYMENT_PERIODS] = edit;
-    gtk_box_pack_end(GTK_BOX(hbox), edit, FALSE, FALSE, 0);
+    gtk_box_pack_end(GTK_BOX(hbox), edit, TRUE, TRUE, 0);
     g_signal_connect (G_OBJECT(edit), "changed",
                       G_CALLBACK (fincalc_update_calc_button_cb), fcd);
 
@@ -633,7 +645,7 @@ gnc_ui_fincalc_dialog_create(GtkWindow *parent)
     edit = gnc_amount_edit_new();
     fincalc_init_gae (GNC_AMOUNT_EDIT (edit), 2, 5, 100000);
     fcd->amounts[INTEREST_RATE] = edit;
-    gtk_box_pack_end(GTK_BOX(hbox), edit, FALSE, FALSE, 0);
+    gtk_box_pack_end(GTK_BOX(hbox), edit, TRUE, TRUE, 0);
     g_signal_connect (G_OBJECT(edit), "changed",
                       G_CALLBACK (fincalc_update_calc_button_cb), fcd);
 
@@ -644,7 +656,7 @@ gnc_ui_fincalc_dialog_create(GtkWindow *parent)
     edit = gnc_amount_edit_new();
     fincalc_init_commodity_gae (GNC_AMOUNT_EDIT (edit));
     fcd->amounts[PRESENT_VALUE] = edit;
-    gtk_box_pack_end(GTK_BOX(hbox), edit, FALSE, FALSE, 0);
+    gtk_box_pack_end(GTK_BOX(hbox), edit, TRUE, TRUE, 0);
     g_signal_connect (G_OBJECT(edit), "changed",
                       G_CALLBACK (fincalc_update_calc_button_cb), fcd);
 
@@ -655,7 +667,7 @@ gnc_ui_fincalc_dialog_create(GtkWindow *parent)
     edit = gnc_amount_edit_new();
     fincalc_init_commodity_gae (GNC_AMOUNT_EDIT (edit));
     fcd->amounts[PERIODIC_PAYMENT] = edit;
-    gtk_box_pack_end(GTK_BOX(hbox), edit, FALSE, FALSE, 0);
+    gtk_box_pack_end(GTK_BOX(hbox), edit, TRUE, TRUE, 0);
     g_signal_connect (G_OBJECT(edit), "changed",
                       G_CALLBACK (fincalc_update_calc_button_cb), fcd);
 
@@ -666,7 +678,7 @@ gnc_ui_fincalc_dialog_create(GtkWindow *parent)
     edit = gnc_amount_edit_new();
     fincalc_init_commodity_gae (GNC_AMOUNT_EDIT (edit));
     fcd->amounts[FUTURE_VALUE] = edit;
-    gtk_box_pack_end(GTK_BOX(hbox), edit, FALSE, FALSE, 0);
+    gtk_box_pack_end(GTK_BOX(hbox), edit, TRUE, TRUE, 0);
     g_signal_connect (G_OBJECT(edit), "changed",
                       G_CALLBACK (fincalc_update_calc_button_cb), fcd);
 
diff --git a/gnucash/gtkbuilder/dialog-fincalc.glade b/gnucash/gtkbuilder/dialog-fincalc.glade
index 2e35f4794..8a13fd05e 100644
--- a/gnucash/gtkbuilder/dialog-fincalc.glade
+++ b/gnucash/gtkbuilder/dialog-fincalc.glade
@@ -382,6 +382,7 @@
                     </child>
                     <child>
                       <object class="GtkBox" id="payment_periods_hbox">
+                        <property name="width_request">150</property>
                         <property name="visible">True</property>
                         <property name="can_focus">False</property>
                         <property name="spacing">6</property>
@@ -396,6 +397,7 @@
                     </child>
                     <child>
                       <object class="GtkBox" id="interest_rate_hbox">
+                        <property name="width_request">150</property>
                         <property name="visible">True</property>
                         <property name="can_focus">False</property>
                         <property name="spacing">6</property>
@@ -410,6 +412,7 @@
                     </child>
                     <child>
                       <object class="GtkBox" id="present_value_hbox">
+                        <property name="width_request">150</property>
                         <property name="visible">True</property>
                         <property name="can_focus">False</property>
                         <property name="spacing">6</property>
@@ -424,6 +427,7 @@
                     </child>
                     <child>
                       <object class="GtkBox" id="periodic_payment_hbox">
+                        <property name="width_request">150</property>
                         <property name="visible">True</property>
                         <property name="can_focus">False</property>
                         <property name="spacing">6</property>
@@ -438,6 +442,7 @@
                     </child>
                     <child>
                       <object class="GtkBox" id="future_value_hbox">
+                        <property name="width_request">150</property>
                         <property name="visible">True</property>
                         <property name="can_focus">False</property>
                         <property name="spacing">6</property>

commit 510e186ed28c6c7fba44df84d87e33ca6ca30676
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Mon May 31 10:59:10 2021 +0100

    Add validation to the use of GNCAmountEdit for assistants
    
    Add validation of GNCAmountEdit where used in the assistant source
    files and make changes required for the change in GNCAmountEdit widget.

diff --git a/gnucash/gnome/assistant-loan.cpp b/gnucash/gnome/assistant-loan.cpp
index 9f8f52d05..580f9d7d5 100644
--- a/gnucash/gnome/assistant-loan.cpp
+++ b/gnucash/gnome/assistant-loan.cpp
@@ -618,6 +618,9 @@ gnc_loan_assistant_create( LoanAssistantData *ldd )
             gtk_widget_set_hexpand (GTK_WIDGET(ldd->prmOrigPrincGAE), FALSE);
             g_object_set (GTK_WIDGET(ldd->prmOrigPrincGAE), "margin", 2, nullptr);
 
+            g_signal_connect (G_OBJECT(ldd->prmOrigPrincGAE), "changed",
+                              G_CALLBACK(loan_info_page_valid_cb), ldd);
+
             for ( i = 0; gas_data[i].loc != NULL; i++ )
             {
                 GNCAccountSel *gas = GNC_ACCOUNT_SEL(gnc_account_sel_new());
@@ -1061,12 +1064,28 @@ gboolean
 loan_info_page_complete( GtkAssistant *assistant, gpointer user_data )
 {
     LoanAssistantData *ldd = static_cast<LoanAssistantData*> (user_data);
+    GNCPrintAmountInfo print_info;
+    gnc_commodity *currency;
+    gint result;
+    gnc_numeric value;
 
     ldd->ld.primaryAcct = gnc_account_sel_get_account( ldd->prmAccountGAS );
     /* Test for valid Account */
     if ( ldd->ld.primaryAcct == NULL )
         return FALSE;
 
+    /* Test for loan amount */
+    currency = xaccAccountGetCommodity (ldd->ld.primaryAcct);
+    print_info = gnc_commodity_print_info (currency, FALSE);
+    gnc_amount_edit_set_print_info (GNC_AMOUNT_EDIT(ldd->prmOrigPrincGAE), print_info);
+    gnc_amount_edit_set_fraction (GNC_AMOUNT_EDIT(ldd->prmOrigPrincGAE),
+                                  gnc_commodity_get_fraction (currency));
+
+    result = gnc_amount_edit_expr_is_valid (GNC_AMOUNT_EDIT(ldd->prmOrigPrincGAE),
+                                            &value, FALSE, nullptr);
+    if (result == 1)
+        return FALSE;
+
     return TRUE;
 }
 
diff --git a/gnucash/gnome/assistant-stock-split.c b/gnucash/gnome/assistant-stock-split.c
index d557975f5..d33049e5d 100644
--- a/gnucash/gnome/assistant-stock-split.c
+++ b/gnucash/gnome/assistant-stock-split.c
@@ -274,17 +274,27 @@ gnc_stock_split_assistant_details_complete (GtkAssistant *assistant,
         gpointer user_data)
 {
     StockSplitInfo *info = user_data;
+    GNCPrintAmountInfo print_info;
+    gnc_commodity *currency;
     gnc_numeric amount;
     gint result;
 
-    result = gnc_amount_edit_expr_is_valid (GNC_AMOUNT_EDIT (info->distribution_edit), &amount, TRUE, NULL);
+    result = gnc_amount_edit_expr_is_valid (GNC_AMOUNT_EDIT (info->distribution_edit),
+                                            &amount, TRUE, NULL);
     if ( result != 0)
         return FALSE; /* Parsing error or field is empty */
 
     if (gnc_numeric_zero_p (amount))
         return FALSE; /* field value is 0 */
 
-    result = gnc_amount_edit_expr_is_valid (GNC_AMOUNT_EDIT (info->price_edit), &amount, TRUE, NULL);
+    currency = gnc_currency_edit_get_currency (GNC_CURRENCY_EDIT(info->price_currency_edit));
+    print_info = gnc_commodity_print_info (currency, FALSE);
+    gnc_amount_edit_set_print_info (GNC_AMOUNT_EDIT (info->price_edit), print_info);
+    gnc_amount_edit_set_fraction (GNC_AMOUNT_EDIT (info->price_edit),
+                                  gnc_commodity_get_fraction (currency));
+
+    result = gnc_amount_edit_expr_is_valid (GNC_AMOUNT_EDIT(info->price_edit),
+                                            &amount, TRUE, NULL);
     if (result == -1)
         return TRUE; /* Optional field is empty */
     else if ( result > 0)
@@ -640,6 +650,8 @@ gnc_stock_split_assistant_create (StockSplitInfo *info)
         gnc_currency_edit_set_currency (GNC_CURRENCY_EDIT(info->price_currency_edit), gnc_default_currency());
         gtk_widget_show (info->price_currency_edit);
         gtk_grid_attach (GTK_GRID(table), info->price_currency_edit, 1, 6, 1, 1);
+        g_signal_connect (info->price_currency_edit, "changed",
+                          G_CALLBACK (gnc_stock_split_details_valid_cb), info);
     }
 
     /* Cash page Widgets */

commit 92b877ccd1864224a0f02431e17d4453c3fbcca0
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Mon May 31 10:58:29 2021 +0100

    Add validation to the use of GNCAmountEdit for Business
    
    Add validation of GNCAmountEdit where used in the Business source files
    and make changes required for the change in GNCAmountEdit widget.

diff --git a/gnucash/gnome-utils/dialog-tax-table.c b/gnucash/gnome-utils/dialog-tax-table.c
index 803eed3c4..b8e2c7975 100644
--- a/gnucash/gnome-utils/dialog-tax-table.c
+++ b/gnucash/gnome-utils/dialog-tax-table.c
@@ -96,6 +96,45 @@ typedef struct _new_taxtable
     gboolean          new_table;
 } NewTaxTable;
 
+static gboolean
+new_tax_table_check_entry (NewTaxTable *ntt, GError **error)
+{
+    GNCPrintAmountInfo print_info;
+    gnc_numeric value;
+    gint result;
+    GError *tmp_error = NULL;
+
+    if (ntt->type == GNC_AMT_TYPE_VALUE)
+    {
+        Account *acc = gnc_tree_view_account_get_selected_account (GNC_TREE_VIEW_ACCOUNT(ntt->acct_tree));
+        gnc_commodity *currency = xaccAccountGetCommodity (acc);
+        print_info = gnc_commodity_print_info (currency, FALSE);
+        gnc_amount_edit_set_fraction (GNC_AMOUNT_EDIT(ntt->amount_entry),
+                                      gnc_commodity_get_fraction (currency));
+    }
+    else
+    {
+        print_info = gnc_integral_print_info ();
+        print_info.max_decimal_places = 5;
+        gnc_amount_edit_set_fraction (GNC_AMOUNT_EDIT (ntt->amount_entry), 100000);
+    }
+
+    gnc_amount_edit_set_print_info (GNC_AMOUNT_EDIT(ntt->amount_entry), print_info);
+
+    result = gnc_amount_edit_expr_is_valid (GNC_AMOUNT_EDIT(ntt->amount_entry), 
+                                            &value, TRUE, &tmp_error);
+
+    if (result == 1)
+    {
+        if (error)
+            g_propagate_error (error, tmp_error);
+        else
+            g_error_free (tmp_error);
+        return FALSE;
+    }
+    return TRUE;
+}
+
 static gboolean
 new_tax_table_ok_cb (NewTaxTable *ntt)
 {
@@ -104,6 +143,7 @@ new_tax_table_ok_cb (NewTaxTable *ntt)
     char *message;
     Account *acc;
     gnc_numeric amount;
+    GError *error = NULL;
 
     g_return_val_if_fail (ntt, FALSE);
     ttw = ntt->ttw;
@@ -131,6 +171,16 @@ new_tax_table_ok_cb (NewTaxTable *ntt)
         }
     }
 
+    /* test for valid value */
+    if (!new_tax_table_check_entry (ntt, &error))
+    {
+        message = g_strdup (error->message);
+        gnc_error_dialog (GTK_WINDOW(ntt->dialog), "%s", message);
+        g_free (message);
+        g_error_free (error);
+        return FALSE;
+    }
+
     /* verify the amount. Note that negative values are allowed (required for European tax rules) */
     amount = gnc_amount_edit_get_amount (GNC_AMOUNT_EDIT(ntt->amount_entry));
     if (ntt->type == GNC_AMT_TYPE_PERCENT &&
@@ -204,6 +254,15 @@ combo_changed (GtkWidget *widget, NewTaxTable *ntt)
 
     index = gtk_combo_box_get_active (GTK_COMBO_BOX(widget));
     ntt->type = index + 1;
+
+    new_tax_table_check_entry (ntt, NULL);
+}
+
+static void
+tax_table_account_selection_changed_cb (GtkTreeSelection *treeselection,
+                                        NewTaxTable *ntt)
+{
+    new_tax_table_check_entry (ntt, NULL);
 }
 
 static GncTaxTable *
@@ -216,6 +275,7 @@ new_tax_table_dialog (TaxTableWindow *ttw, gboolean new_table,
     GtkWidget *box, *widget, *combo;
     gboolean done;
     gint response, index;
+    GtkTreeSelection *selection;
 
     if (!ttw) return NULL;
     if (new_table && entry) return NULL;
@@ -263,6 +323,10 @@ new_tax_table_dialog (TaxTableWindow *ttw, gboolean new_table,
     gtk_container_add (GTK_CONTAINER(box), ntt->acct_tree);
     gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(ntt->acct_tree), FALSE);
 
+    selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(ntt->acct_tree));
+    g_signal_connect (G_OBJECT(selection), "changed",
+                      G_CALLBACK(tax_table_account_selection_changed_cb), ntt);
+
     /* Make 'enter' do the right thing */
     gtk_entry_set_activates_default (GTK_ENTRY(gnc_amount_edit_gtk_entry
                                     (GNC_AMOUNT_EDIT(ntt->amount_entry))),
diff --git a/gnucash/gnome/dialog-customer.c b/gnucash/gnome/dialog-customer.c
index d2d40f948..6f61fc875 100644
--- a/gnucash/gnome/dialog-customer.c
+++ b/gnucash/gnome/dialog-customer.c
@@ -283,10 +283,11 @@ static gboolean check_edit_amount (GtkWidget *amount,
                                    gnc_numeric *min, gnc_numeric *max,
                                    const char * error_message)
 {
-    if (!gnc_amount_edit_evaluate (GNC_AMOUNT_EDIT (amount), NULL))
+    GError *error = NULL;
+    if (!gnc_amount_edit_evaluate (GNC_AMOUNT_EDIT (amount), &error))
     {
-        if (error_message)
-            gnc_error_dialog (gnc_ui_get_gtk_window (amount), "%s", error_message);
+        gnc_error_dialog (gnc_ui_get_gtk_window (amount), "%s", error->message);
+        g_error_free (error);
         return TRUE;
     }
     /* We've got a valid-looking number; check mix/max */
@@ -323,6 +324,8 @@ gnc_customer_window_ok_cb (GtkWidget *widget, gpointer data)
     CustomerWindow *cw = data;
     gnc_numeric min, max;
     gchar *string;
+    gnc_commodity *currency;
+    GNCPrintAmountInfo print_info;
 
     /* Check for valid company name */
     if (check_entry_nonempty (cw->company_entry,
@@ -352,6 +355,12 @@ gnc_customer_window_ok_cb (GtkWidget *widget, gpointer data)
                              "or you must leave it blank.")))
         return;
 
+    currency = gnc_currency_edit_get_currency (GNC_CURRENCY_EDIT(cw->currency_edit));
+    print_info = gnc_commodity_print_info (currency, FALSE);
+    gnc_amount_edit_set_print_info (GNC_AMOUNT_EDIT (cw->credit_amount), print_info);
+    gnc_amount_edit_set_fraction (GNC_AMOUNT_EDIT (cw->credit_amount),
+                                  gnc_commodity_get_fraction (currency));
+
     if (check_edit_amount (cw->credit_amount, &min, NULL,
                            _("Credit must be a positive amount or "
                              "you must leave it blank.")))
diff --git a/gnucash/gnome/dialog-employee.c b/gnucash/gnome/dialog-employee.c
index 91b49eff6..ee71cdbfb 100644
--- a/gnucash/gnome/dialog-employee.c
+++ b/gnucash/gnome/dialog-employee.c
@@ -188,11 +188,25 @@ static gboolean check_entry_nonempty (GtkWidget *entry,
     return FALSE;
 }
 
+static gboolean check_edit_amount (GtkWidget *amount)
+{
+    GError *error = NULL;
+    if (!gnc_amount_edit_evaluate (GNC_AMOUNT_EDIT(amount), &error))
+    {
+        gnc_error_dialog (gnc_ui_get_gtk_window (amount), "%s", error->message);
+        g_error_free (error);
+        return TRUE;
+    }
+    return FALSE;
+}
+
 void
 gnc_employee_window_ok_cb (GtkWidget *widget, gpointer data)
 {
     EmployeeWindow *ew = data;
     gchar *string;
+    GNCPrintAmountInfo print_info;
+    gnc_commodity *currency;
 
     /* Check for valid username */
     if (check_entry_nonempty (ew->username_entry,
@@ -223,6 +237,20 @@ gnc_employee_window_ok_cb (GtkWidget *widget, gpointer data)
         g_free(string);
     }
 
+    /* Check for valid workday amount */
+    if (check_edit_amount (GTK_WIDGET(ew->workday_amount)))
+        return;
+
+    currency = gnc_currency_edit_get_currency (GNC_CURRENCY_EDIT(ew->currency_edit));
+    print_info = gnc_commodity_print_info (currency, FALSE);
+    gnc_amount_edit_set_print_info (GNC_AMOUNT_EDIT (ew->rate_amount), print_info);
+    gnc_amount_edit_set_fraction (GNC_AMOUNT_EDIT (ew->rate_amount),
+                                  gnc_commodity_get_fraction (currency));
+
+    /* Check for valid rate amount */
+    if (check_edit_amount (GTK_WIDGET(ew->rate_amount)))
+        return;
+
     /* Now save it off */
     {
         GncEmployee *employee = ew_get_employee (ew);
diff --git a/gnucash/gnome/dialog-invoice.c b/gnucash/gnome/dialog-invoice.c
index 723875eb1..17b592abf 100644
--- a/gnucash/gnome/dialog-invoice.c
+++ b/gnucash/gnome/dialog-invoice.c
@@ -1364,7 +1364,7 @@ static gboolean
 gnc_invoice_window_leave_to_charge_cb (GtkWidget *widget, GdkEventFocus *event,
                                        gpointer data)
 {
-    gnc_amount_edit_evaluate (GNC_AMOUNT_EDIT (widget), NULL);
+    gnc_amount_edit_evaluate (GNC_AMOUNT_EDIT(data), NULL);
     return FALSE;
 }
 
@@ -2552,7 +2552,7 @@ gnc_invoice_create_page (InvoiceWindow *iw, gpointer page)
 
         g_signal_connect(G_OBJECT(gnc_amount_edit_gtk_entry(GNC_AMOUNT_EDIT(edit))),
                          "focus-out-event",
-                         G_CALLBACK(gnc_invoice_window_leave_to_charge_cb), iw);
+                         G_CALLBACK(gnc_invoice_window_leave_to_charge_cb), edit);
         g_signal_connect(G_OBJECT(edit), "amount_changed",
                          G_CALLBACK(gnc_invoice_window_changed_to_charge_cb), iw);
     }
diff --git a/gnucash/gnome/dialog-job.c b/gnucash/gnome/dialog-job.c
index 7925e2132..459c35cac 100644
--- a/gnucash/gnome/dialog-job.c
+++ b/gnucash/gnome/dialog-job.c
@@ -151,6 +151,14 @@ gnc_job_verify_ok (JobWindow *jw)
         return FALSE;
     }
 
+    /* Check for valid rate */
+    if (!gnc_amount_edit_evaluate (GNC_AMOUNT_EDIT(jw->rate_entry), NULL))
+    {
+        const char *message = _("The rate amount must be valid or you must leave it blank.");
+        gnc_error_dialog (GTK_WINDOW (jw->dialog), "%s", message);
+        return FALSE;
+    }
+
     /* Set a valid id if one was not created */
     res = gtk_entry_get_text (GTK_ENTRY (jw->id_entry));
     if (g_strcmp0 (res, "") == 0)
diff --git a/gnucash/gnome/dialog-payment.c b/gnucash/gnome/dialog-payment.c
index ab3a33ddd..466832cbc 100644
--- a/gnucash/gnome/dialog-payment.c
+++ b/gnucash/gnome/dialog-payment.c
@@ -201,6 +201,7 @@ void gnc_payment_acct_tree_row_activated_cb (GtkWidget *widget, GtkTreePath *pat
         GtkTreeViewColumn *column, PaymentWindow *pw);
 void gnc_payment_leave_amount_cb (GtkWidget *widget, GdkEventFocus *event,
                                   PaymentWindow *pw);
+void gnc_payment_activate_amount_cb (GtkWidget *widget, PaymentWindow *pw);
 void gnc_payment_window_fill_docs_list (PaymentWindow *pw);
 
 
@@ -221,6 +222,7 @@ gnc_payment_window_check_payment (PaymentWindow *pw)
     gboolean enable_xfer_acct = TRUE;
     gboolean allow_payment = TRUE;
     GtkTreeSelection *selection;
+    gint c_result, d_result;
 
     if (!pw)
         return FALSE;
@@ -242,11 +244,24 @@ gnc_payment_window_check_payment (PaymentWindow *pw)
         goto update_cleanup;
     }
 
+    /* Verify the credit / debit amounts are valid */
+    d_result = gnc_amount_edit_expr_is_valid (GNC_AMOUNT_EDIT(pw->amount_debit_edit),
+                                              &amount_deb, FALSE, NULL);
+
+    c_result = gnc_amount_edit_expr_is_valid (GNC_AMOUNT_EDIT(pw->amount_credit_edit),
+                                              &amount_cred, FALSE, NULL);
+
+    if ((d_result == 1) || (c_result == 1))
+    {
+        conflict_msg = _("There is a problem with the Payment or Refund amount.");
+        allow_payment = FALSE;
+        goto update_cleanup;
+    }
+
     /* Test the total amount */
-    amount_deb  = gnc_amount_edit_get_amount (GNC_AMOUNT_EDIT (pw->amount_debit_edit));
-    amount_cred = gnc_amount_edit_get_amount (GNC_AMOUNT_EDIT (pw->amount_credit_edit));
     pw->amount_tot = gnc_numeric_sub (amount_cred, amount_deb,
-                                      gnc_commodity_get_fraction (xaccAccountGetCommodity (pw->post_acct)),
+                                      gnc_commodity_get_fraction (
+                                      xaccAccountGetCommodity (pw->post_acct)),
                                       GNC_HOW_RND_ROUND_HALF_UP);
 
     if (gnc_numeric_check (pw->amount_tot) || gnc_numeric_zero_p (pw->amount_tot))
@@ -1104,24 +1119,40 @@ gnc_payment_leave_amount_cb (G_GNUC_UNUSED GtkWidget *widget,
                              G_GNUC_UNUSED GdkEventFocus *event,
                              PaymentWindow *pw)
 {
-    gnc_numeric amount_deb, amount_cred, amount_tot;
+    gboolean d_payment_ok = FALSE;
+    gboolean c_payment_ok = FALSE;
 
     if (! pw->amount_credit_edit || ! pw->amount_debit_edit)
         return;
 
-    /* If both credit and debit amount are entered, simplify it to either one */
-    amount_deb  = gnc_amount_edit_get_amount (GNC_AMOUNT_EDIT (pw->amount_debit_edit));
-    amount_cred = gnc_amount_edit_get_amount (GNC_AMOUNT_EDIT (pw->amount_credit_edit));
-    amount_tot = gnc_numeric_sub (amount_cred, amount_deb,
-                                  gnc_commodity_get_fraction (xaccAccountGetCommodity (pw->post_acct)),
-                                  GNC_HOW_RND_ROUND_HALF_UP);
+    c_payment_ok = gnc_amount_edit_evaluate (GNC_AMOUNT_EDIT(pw->amount_credit_edit), NULL);
+    d_payment_ok = gnc_amount_edit_evaluate (GNC_AMOUNT_EDIT(pw->amount_debit_edit), NULL);
 
-    gnc_ui_payment_window_set_amount (pw, amount_tot);
+    if (c_payment_ok && d_payment_ok)
+    {
+        gnc_numeric amount_deb, amount_cred, amount_tot;
+
+        /* If both credit and debit amount are entered, simplify it to either one */
+        amount_deb  = gnc_amount_edit_get_amount (GNC_AMOUNT_EDIT (pw->amount_debit_edit));
+        amount_cred = gnc_amount_edit_get_amount (GNC_AMOUNT_EDIT (pw->amount_credit_edit));
+        amount_tot = gnc_numeric_sub (amount_cred, amount_deb,
+                                      gnc_commodity_get_fraction (
+                                      xaccAccountGetCommodity (pw->post_acct)),
+                                      GNC_HOW_RND_ROUND_HALF_UP);
 
+        gnc_ui_payment_window_set_amount (pw, amount_tot);
+    }
     /* Reflect if the payment could complete now */
     gnc_payment_window_check_payment (pw);
 }
 
+void
+gnc_payment_activate_amount_cb (G_GNUC_UNUSED GtkWidget *widget,
+                                PaymentWindow *pw)
+{
+      gnc_payment_leave_amount_cb (NULL, NULL, pw);
+}
+
 /* Select the list of accounts to show in the tree */
 static void
 gnc_payment_set_account_types (GncTreeViewAccount *tree)
@@ -1294,6 +1325,10 @@ new_payment_window (GtkWindow *parent, QofBook *book, InitialPaymentInfo *tx_inf
                      "focus-out-event",
                      G_CALLBACK(gnc_payment_leave_amount_cb), pw);
 
+    g_signal_connect(G_OBJECT(pw->amount_debit_edit),
+                     "activate",
+                     G_CALLBACK(gnc_payment_activate_amount_cb), pw);
+
     pw->amount_credit_edit = gnc_amount_edit_new ();
     gnc_amount_edit_set_evaluate_on_enter (GNC_AMOUNT_EDIT (pw->amount_credit_edit),
                                            TRUE);
@@ -1302,6 +1337,10 @@ new_payment_window (GtkWindow *parent, QofBook *book, InitialPaymentInfo *tx_inf
                      "focus-out-event",
                      G_CALLBACK(gnc_payment_leave_amount_cb), pw);
 
+    g_signal_connect(G_OBJECT(pw->amount_credit_edit),
+                     "activate",
+                     G_CALLBACK(gnc_payment_activate_amount_cb), pw);
+
     box = GTK_WIDGET (gtk_builder_get_object (builder, "date_box"));
     pw->date_edit = gnc_date_edit_new (time(NULL), FALSE, FALSE);
     gtk_box_pack_start (GTK_BOX (box), pw->date_edit, TRUE, TRUE, 0);

commit faaed371ed3f5bb1083284bd762e3d22d4b1216f
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Mon May 31 10:57:35 2021 +0100

    Bug 798144 - Reconciliation uses different number than entered
    
    If a number was pasted into the balance end value with a currency
    symbol the number would silently fail on evaluate and cause the wrong
    value to be used. With previous changes to the GNCAmountEdit widget a
    warning symbol will indicate a validation error and prevents going
    forward.

diff --git a/gnucash/gnome/window-reconcile.c b/gnucash/gnome/window-reconcile.c
index 210c4c94d..f8c0294c8 100644
--- a/gnucash/gnome/window-reconcile.c
+++ b/gnucash/gnome/window-reconcile.c
@@ -316,11 +316,20 @@ gnc_start_recn_update_cb(GtkWidget *widget, GdkEventFocus *event,
                          startRecnWindowData *data)
 {
     gnc_numeric value;
+    gint result = gnc_amount_edit_expr_is_valid (GNC_AMOUNT_EDIT(data->end_value),
+                                                 &value, TRUE, NULL);
 
-    gnc_amount_edit_evaluate (GNC_AMOUNT_EDIT(data->end_value), NULL);
+    data->user_set_value = FALSE;
 
-    value = gnc_amount_edit_get_amount (GNC_AMOUNT_EDIT(data->end_value));
-    data->user_set_value = !gnc_numeric_equal(value, data->original_value);
+    if (result < 1) // OK
+    {
+        if (result == -1) // blank entry is valid
+        {
+            gnc_amount_edit_set_amount (GNC_AMOUNT_EDIT(data->end_value), value);
+            gnc_amount_edit_select_region (GNC_AMOUNT_EDIT(data->end_value), 0, -1);
+        }
+        data->user_set_value = !gnc_numeric_equal (value, data->original_value);
+    }
     return FALSE;
 }
 
@@ -391,6 +400,8 @@ actions on this account. Please double-check this is the date you intended."));
     /* update the amount edit with the amount */
     gnc_amount_edit_set_amount (GNC_AMOUNT_EDIT (data->end_value),
                                 new_balance);
+
+    gnc_start_recn_update_cb (GTK_WIDGET(data->end_value), NULL, data);
 }
 
 
@@ -648,8 +659,10 @@ startRecnWindow(GtkWidget *parent, Account *account,
     gboolean auto_interest_xfer_option;
     GNCPrintAmountInfo print_info;
     gnc_numeric ending;
+    GtkWidget *entry;
     char *title;
-    int result;
+    int result = -6;
+    gulong fo_handler_id;
 
     /* Initialize the data structure that will be used for several callbacks
      * throughout this file with the relevant info.  Some initialization is
@@ -698,7 +711,7 @@ startRecnWindow(GtkWidget *parent, Account *account,
 
     {
         GtkWidget *start_value, *box;
-        GtkWidget *entry, *label;
+        GtkWidget *label;
         GtkWidget *interest = NULL;
 
         start_value = GTK_WIDGET(gtk_builder_get_object (builder, "start_value"));
@@ -746,8 +759,9 @@ startRecnWindow(GtkWidget *parent, Account *account,
 
         entry = gnc_amount_edit_gtk_entry (GNC_AMOUNT_EDIT (end_value));
         gtk_editable_select_region (GTK_EDITABLE(entry), 0, -1);
-        g_signal_connect(G_OBJECT(entry), "focus-out-event",
-                         G_CALLBACK(gnc_start_recn_update_cb), (gpointer) &data);
+        fo_handler_id = g_signal_connect (G_OBJECT(entry), "focus-out-event",
+                                          G_CALLBACK(gnc_start_recn_update_cb),
+                                          (gpointer) &data);
         gtk_entry_set_activates_default(GTK_ENTRY(entry), TRUE);
 
         /* if it's possible to enter an interest payment or charge for this
@@ -789,7 +803,16 @@ startRecnWindow(GtkWidget *parent, Account *account,
         gnc_reconcile_interest_xfer_run( &data );
     }
 
-    result = gtk_dialog_run(GTK_DIALOG(dialog));
+    while (gtk_dialog_run (GTK_DIALOG(dialog)) == GTK_RESPONSE_OK)
+    {
+        /* If response is OK but end_value not valid, try again */
+        if (gnc_amount_edit_evaluate (GNC_AMOUNT_EDIT(end_value), NULL))
+        {
+            result = GTK_RESPONSE_OK;
+            break;
+        }
+    }
+
     if (result == GTK_RESPONSE_OK)
     {
         *new_ending = gnc_amount_edit_get_amount (GNC_AMOUNT_EDIT (end_value));
@@ -802,6 +825,8 @@ startRecnWindow(GtkWidget *parent, Account *account,
 
         gnc_save_reconcile_interval(account, *statement_date);
     }
+    // must remove the focus-out handler
+    g_signal_handler_disconnect (G_OBJECT(entry), fo_handler_id);
     gtk_widget_destroy (dialog);
     g_object_unref(G_OBJECT(builder));
 
diff --git a/gnucash/gnome/window-reconcile2.c b/gnucash/gnome/window-reconcile2.c
index 82f4a74db..5a05774c9 100644
--- a/gnucash/gnome/window-reconcile2.c
+++ b/gnucash/gnome/window-reconcile2.c
@@ -306,11 +306,20 @@ gnc_start_recn2_update_cb (GtkWidget *widget, GdkEventFocus *event,
                          startRecnWindowData *data)
 {
     gnc_numeric value;
+    gint result = gnc_amount_edit_expr_is_valid (GNC_AMOUNT_EDIT(data->end_value),
+                                                 &value, TRUE, NULL);
 
-    gnc_amount_edit_evaluate (GNC_AMOUNT_EDIT (data->end_value), NULL);
+    data->user_set_value = FALSE;
 
-    value = gnc_amount_edit_get_amount (GNC_AMOUNT_EDIT (data->end_value));
-    data->user_set_value = !gnc_numeric_equal (value, data->original_value);
+    if (result < 1) // OK
+    {
+        if (result == -1) // blank entry is valid
+        {
+            gnc_amount_edit_set_amount (GNC_AMOUNT_EDIT(data->end_value), value);
+            gnc_amount_edit_select_region (GNC_AMOUNT_EDIT(data->end_value), 0, -1);
+        }
+        data->user_set_value = !gnc_numeric_equal (value, data->original_value);
+    }
     return FALSE;
 }
 
@@ -335,6 +344,8 @@ gnc_start_recn2_date_changed (GtkWidget *widget, startRecnWindowData *data)
     /* update the amount edit with the amount */
     gnc_amount_edit_set_amount (GNC_AMOUNT_EDIT (data->end_value),
                                 new_balance);
+
+    gnc_start_recn2_update_cb (GTK_WIDGET(data->end_value), NULL, data);
 }
 
 
@@ -616,8 +627,10 @@ startRecnWindow (GtkWidget *parent, Account *account,
     gboolean auto_interest_xfer_option;
     GNCPrintAmountInfo print_info;
     gnc_numeric ending;
+    GtkWidget *entry;
     char *title;
-    int result;
+    int result = -6;
+    gulong fo_handler_id;
 
     /* Initialize the data structure that will be used for several callbacks
      * throughout this file with the relevant info.  Some initialization is
@@ -665,7 +678,7 @@ startRecnWindow (GtkWidget *parent, Account *account,
 
     {
         GtkWidget *start_value, *box;
-        GtkWidget *entry, *label;
+        GtkWidget *label;
         GtkWidget *interest = NULL;
 
         start_value = GTK_WIDGET (gtk_builder_get_object (builder, "start_value"));
@@ -709,8 +722,9 @@ startRecnWindow (GtkWidget *parent, Account *account,
 
         entry = gnc_amount_edit_gtk_entry (GNC_AMOUNT_EDIT (end_value));
         gtk_editable_select_region (GTK_EDITABLE (entry), 0, -1);
-        g_signal_connect (G_OBJECT (entry), "focus-out-event",
-                         G_CALLBACK (gnc_start_recn2_update_cb), (gpointer) &data);
+        fo_handler_id = g_signal_connect (G_OBJECT(entry), "focus-out-event",
+                                          G_CALLBACK(gnc_start_recn2_update_cb),
+                                          (gpointer) &data);
         gtk_entry_set_activates_default (GTK_ENTRY (entry), TRUE);
 
         /* if it's possible to enter an interest payment or charge for this
@@ -749,7 +763,16 @@ startRecnWindow (GtkWidget *parent, Account *account,
         gnc_reconcile_interest_xfer_run (&data);
     }
 
-    result = gtk_dialog_run (GTK_DIALOG (dialog));
+    while (gtk_dialog_run (GTK_DIALOG(dialog)) == GTK_RESPONSE_OK)
+    {
+        /* If response is OK but end_value not valid, try again */
+        if (gnc_amount_edit_evaluate (GNC_AMOUNT_EDIT(end_value), NULL))
+        {
+            result = GTK_RESPONSE_OK;
+            break;
+        }
+    }
+
     if (result == GTK_RESPONSE_OK)
     {
         *new_ending = gnc_amount_edit_get_amount (GNC_AMOUNT_EDIT (end_value));
@@ -762,6 +785,8 @@ startRecnWindow (GtkWidget *parent, Account *account,
 
         gnc_save_reconcile_interval (account, *statement_date);
     }
+    // must remove the focus-out handler
+    g_signal_handler_disconnect (G_OBJECT(entry), fo_handler_id);
     gtk_widget_destroy (dialog);
     g_object_unref (G_OBJECT (builder));
 
@@ -893,7 +918,7 @@ gnc_reconcile_window_button_press_cb (GtkWidget *widget,
 
         /* Get tree path for row that was clicked */
         gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (qview),
-                                             (gint) event->x, 
+                                             (gint) event->x,
                                              (gint) event->y,
                                              &path, NULL, NULL, NULL);
 

commit 51b062524b8f841a50554177cce135f68396d592
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Mon May 31 10:56:22 2021 +0100

    Change GNCAmountEdit to be based on a GtkBox
    
    To indicate that the expression entered has failed change the widget it
    is based on to a box and add a warning triangle image with tooltip that
    is made visible when the validation has failed. This is less intrusive
    but if required the error message can be retrieved and used for a
    warning dialog if that is in keeping with other dialog checks.

diff --git a/gnucash/gnome-utils/gnc-amount-edit.c b/gnucash/gnome-utils/gnc-amount-edit.c
index 1920a85e4..8e9a5f665 100644
--- a/gnucash/gnome-utils/gnc-amount-edit.c
+++ b/gnucash/gnome-utils/gnc-amount-edit.c
@@ -45,6 +45,8 @@
 /* Signal codes */
 enum
 {
+    ACTIVATE,
+    CHANGED,
     AMOUNT_CHANGED,
     LAST_SIGNAL
 };
@@ -55,39 +57,47 @@ static void gnc_amount_edit_init (GNCAmountEdit *gae);
 static void gnc_amount_edit_class_init (GNCAmountEditClass *klass);
 static void gnc_amount_edit_changed (GtkEditable *gae,
                                      gpointer user_data);
-static void gnc_amount_edit_paste_clipboard (GNCAmountEdit *gae,
+static void gnc_amount_edit_paste_clipboard (GtkEntry *entry,
                                              gpointer user_data);
 static gint gnc_amount_edit_key_press (GtkWidget   *widget,
-                                       GdkEventKey *event);
+                                       GdkEventKey *event,
+                                       gpointer user_data);
 
-static GtkEntryClass *parent_class;
+#define GNC_AMOUNT_EDIT_PATH "gnc-amount-edit-path"
 
-GType
-gnc_amount_edit_get_type (void)
+G_DEFINE_TYPE (GNCAmountEdit, gnc_amount_edit, GTK_TYPE_BOX)
+
+static void
+gnc_amount_edit_finalize (GObject *object)
 {
-    static GType amount_edit_type = 0;
+    g_return_if_fail (object != NULL);
+    g_return_if_fail (GNC_IS_AMOUNT_EDIT(object));
 
-    if (amount_edit_type == 0)
-    {
-        GTypeInfo amount_edit_info =
-        {
-            sizeof (GNCAmountEditClass),
-            NULL,
-            NULL,
-            (GClassInitFunc) gnc_amount_edit_class_init,
-            NULL,
-            NULL,
-            sizeof (GNCAmountEdit),
-            0,
-            (GInstanceInitFunc) gnc_amount_edit_init
-        };
-
-        amount_edit_type = g_type_register_static (GTK_TYPE_ENTRY,
-                                                   "GNCAmountEdit",
-                                                    &amount_edit_info,
-                                                    0);
-    }
-    return amount_edit_type;
+    G_OBJECT_CLASS (gnc_amount_edit_parent_class)->finalize (object);
+}
+
+static void
+gnc_amount_edit_dispose (GObject *object)
+{
+    GNCAmountEdit *gae;
+
+    g_return_if_fail (object != NULL);
+    g_return_if_fail (GNC_IS_AMOUNT_EDIT(object));
+
+    gae = GNC_AMOUNT_EDIT(object);
+
+    if (gae->disposed)
+        return;
+
+    gae->disposed = TRUE;
+
+    gtk_widget_destroy (GTK_WIDGET(gae->entry));
+    gae->entry = NULL;
+
+    gtk_widget_destroy (GTK_WIDGET(gae->image));
+    gae->image = NULL;
+
+    G_OBJECT_CLASS (gnc_amount_edit_parent_class)->dispose (object);
 }
 
 static void
@@ -95,10 +105,31 @@ gnc_amount_edit_class_init (GNCAmountEditClass *klass)
 {
     GObjectClass *object_class = G_OBJECT_CLASS(klass);
     GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
- /* GtkEditableClass *editable_class = GTK_EDITABLE_CLASS(g_type_interface_peek
-                                                         (klass, GTK_TYPE_EDITABLE)); */
 
-    parent_class = g_type_class_peek_parent (klass);
+    object_class->dispose = gnc_amount_edit_dispose;
+    object_class->finalize = gnc_amount_edit_finalize;
+
+    amount_edit_signals [ACTIVATE] =
+        g_signal_new ("activate",
+                      G_OBJECT_CLASS_TYPE(object_class),
+                      G_SIGNAL_RUN_FIRST,
+                      G_STRUCT_OFFSET(GNCAmountEditClass, activate),
+                      NULL,
+                      NULL,
+                      g_cclosure_marshal_VOID__VOID,
+                      G_TYPE_NONE,
+                      0);
+
+    amount_edit_signals [CHANGED] =
+        g_signal_new ("changed",
+                      G_OBJECT_CLASS_TYPE(object_class),
+                      G_SIGNAL_RUN_FIRST,
+                      G_STRUCT_OFFSET(GNCAmountEditClass, changed),
+                      NULL,
+                      NULL,
+                      g_cclosure_marshal_VOID__VOID,
+                      G_TYPE_NONE,
+                      0);
 
     amount_edit_signals [AMOUNT_CHANGED] =
         g_signal_new ("amount_changed",
@@ -110,47 +141,60 @@ gnc_amount_edit_class_init (GNCAmountEditClass *klass)
                       g_cclosure_marshal_VOID__VOID,
                       G_TYPE_NONE,
                       0);
-
-    widget_class->key_press_event = gnc_amount_edit_key_press;
-
- /* editable_class->changed = gnc_amount_edit_changed; */
 }
 
 static void
 gnc_amount_edit_init (GNCAmountEdit *gae)
 {
+    gtk_orientable_set_orientation (GTK_ORIENTABLE(gae),
+                                    GTK_ORIENTATION_HORIZONTAL);
+
+    gae->entry = GTK_ENTRY(gtk_entry_new());
     gae->need_to_parse = FALSE;
     gae->amount = gnc_numeric_zero ();
     gae->print_info = gnc_default_print_info (FALSE);
     gae->fraction = 0;
     gae->evaluate_on_enter = FALSE;
+    gae->validate_on_change = FALSE;
     gae->block_changed = FALSE;
+    gae->show_warning_symbol = TRUE;
 
     // Set the name for this widget so it can be easily manipulated with css
     gtk_widget_set_name (GTK_WIDGET(gae), "gnc-id-amount-edit");
 
-    g_signal_connect (G_OBJECT(gae), "changed",
+    g_signal_connect (G_OBJECT(gae->entry), "key-press-event",
+                      G_CALLBACK(gnc_amount_edit_key_press), gae);
+
+    g_signal_connect (G_OBJECT(gae->entry), "changed",
                       G_CALLBACK(gnc_amount_edit_changed), gae);
 
-    g_signal_connect (G_OBJECT(gae), "paste-clipboard",
-                      G_CALLBACK(gnc_amount_edit_paste_clipboard), NULL);
+    g_signal_connect (G_OBJECT(gae->entry), "paste-clipboard",
+                      G_CALLBACK(gnc_amount_edit_paste_clipboard), gae);
 }
 
 static void
 gnc_amount_edit_changed (GtkEditable *editable, gpointer user_data)
 {
     GNCAmountEdit *gae = GNC_AMOUNT_EDIT(user_data);
-     /*GTK_EDITABLE_CLASS(parent_class)->changed(editable);*/
+
+    gae->need_to_parse = TRUE;
+
     if (gae->block_changed)
-        g_signal_stop_emission_by_name (G_OBJECT(gae), "changed");
+        return;
 
-    GNC_AMOUNT_EDIT(editable)->need_to_parse = TRUE;
+    if (gae->validate_on_change)
+    {
+        gnc_numeric amount;
+        gnc_amount_edit_expr_is_valid (gae, &amount, TRUE, NULL);
+    }
+    g_signal_emit (gae, amount_edit_signals [CHANGED], 0);
 }
 
 static void
-gnc_amount_edit_paste_clipboard (GNCAmountEdit *gae, gpointer user_data)
+gnc_amount_edit_paste_clipboard (GtkEntry *entry, gpointer user_data)
 {
-    GtkClipboard *clipboard = gtk_widget_get_clipboard (GTK_WIDGET(gae),
+    GNCAmountEdit *gae = GNC_AMOUNT_EDIT(user_data);
+    GtkClipboard *clipboard = gtk_widget_get_clipboard (GTK_WIDGET(entry),
                                                         GDK_SELECTION_CLIPBOARD);
     gchar *text = gtk_clipboard_wait_for_text (clipboard);
     gchar *filtered_text;
@@ -160,6 +204,12 @@ gnc_amount_edit_paste_clipboard (GNCAmountEdit *gae, gpointer user_data)
     if (!text)
         return;
 
+    if (gtk_widget_get_visible (GTK_WIDGET(gae->image)))
+    {
+        gtk_widget_hide (GTK_WIDGET(gae->image));
+        gtk_widget_set_tooltip_text (GTK_WIDGET(gae->image), NULL);
+    }
+
     filtered_text = gnc_filter_text_for_control_chars (text);
 
     if (!filtered_text)
@@ -168,37 +218,43 @@ gnc_amount_edit_paste_clipboard (GNCAmountEdit *gae, gpointer user_data)
         return;
     }
 
-    position = gtk_editable_get_position (GTK_EDITABLE(gae));
+    position = gtk_editable_get_position (GTK_EDITABLE(entry));
 
-    if (gtk_editable_get_selection_bounds (GTK_EDITABLE(gae),
+    if (gtk_editable_get_selection_bounds (GTK_EDITABLE(entry),
                                            &start_pos, &end_pos))
     {
         position = start_pos;
 
         gae->block_changed = TRUE;
-        gtk_editable_delete_selection (GTK_EDITABLE(gae));
+        gtk_editable_delete_selection (GTK_EDITABLE(entry));
         gae->block_changed = FALSE;
-        gtk_editable_insert_text (GTK_EDITABLE(gae),
+        gtk_editable_insert_text (GTK_EDITABLE(entry),
                                   filtered_text, -1, &position);
     }
     else
-        gtk_editable_insert_text (GTK_EDITABLE(gae),
+        gtk_editable_insert_text (GTK_EDITABLE(entry),
                                   filtered_text, -1, &position);
 
-    gtk_editable_set_position (GTK_EDITABLE(gae), position);
+    gtk_editable_set_position (GTK_EDITABLE(entry), position);
 
-    g_signal_stop_emission_by_name (G_OBJECT(gae), "paste-clipboard");
+    g_signal_stop_emission_by_name (G_OBJECT(entry), "paste-clipboard");
 
     g_free (text);
     g_free (filtered_text);
 }
 
 static gint
-gnc_amount_edit_key_press (GtkWidget *widget, GdkEventKey *event)
+gnc_amount_edit_key_press (GtkWidget *widget, GdkEventKey *event, gpointer user_data)
 {
-    GNCAmountEdit *gae = GNC_AMOUNT_EDIT(widget);
+    GNCAmountEdit *gae = GNC_AMOUNT_EDIT(user_data);
     gint result;
 
+    if (gtk_widget_get_visible (GTK_WIDGET(gae->image)))
+    {
+        gtk_widget_hide (GTK_WIDGET(gae->image));
+        gtk_widget_set_tooltip_text (GTK_WIDGET(gae->image), NULL);
+    }
+
 #ifdef G_OS_WIN32
     /* gdk never sends GDK_KEY_KP_Decimal on win32. See #486658 */
     if (event->hardware_keycode == VK_DECIMAL)
@@ -214,23 +270,28 @@ gnc_amount_edit_key_press (GtkWidget *widget, GdkEventKey *event)
         }
     }
 
-    result = (* GTK_WIDGET_CLASS(parent_class)->key_press_event)(widget, event);
+    result = (* GTK_WIDGET_GET_CLASS(widget)->key_press_event)(widget, event);
 
     switch (event->keyval)
     {
     case GDK_KEY_Return:
         if (gae->evaluate_on_enter)
             break;
+        else
+            g_signal_emit (gae, amount_edit_signals [ACTIVATE], 0);
         if (event->state & (GDK_MODIFIER_INTENT_DEFAULT_MOD_MASK))
             break;
         return result;
     case GDK_KEY_KP_Enter:
+        if (!gae->evaluate_on_enter)
+            g_signal_emit (gae, amount_edit_signals [ACTIVATE], 0);
         break;
     default:
         return result;
     }
 
     gnc_amount_edit_evaluate (gae, NULL);
+    g_signal_emit (gae, amount_edit_signals [ACTIVATE], 0);
 
     return TRUE;
 }
@@ -238,10 +299,15 @@ gnc_amount_edit_key_press (GtkWidget *widget, GdkEventKey *event)
 GtkWidget *
 gnc_amount_edit_new (void)
 {
-    GNCAmountEdit *gae;
+    GNCAmountEdit *gae = g_object_new (GNC_TYPE_AMOUNT_EDIT, NULL);
 
-    gae = g_object_new (GNC_TYPE_AMOUNT_EDIT, NULL);
-    gtk_widget_show (GTK_WIDGET(gae));
+    gtk_box_pack_start (GTK_BOX(gae), GTK_WIDGET(gae->entry), TRUE, TRUE, 0);
+    gtk_entry_set_width_chars (GTK_ENTRY(gae->entry), 12);
+    gae->image = gtk_image_new_from_icon_name ("dialog-warning", GTK_ICON_SIZE_SMALL_TOOLBAR);
+    gtk_box_pack_start (GTK_BOX(gae), GTK_WIDGET(gae->image), FALSE, FALSE, 6);
+    gtk_widget_set_no_show_all (GTK_WIDGET(gae->image), TRUE);
+    gtk_widget_hide (GTK_WIDGET(gae->image));
+    gtk_widget_show_all (GTK_WIDGET(gae));
 
     return GTK_WIDGET(gae);
 }
@@ -302,12 +368,18 @@ gnc_amount_edit_expr_is_valid (GNCAmountEdit *gae, gnc_numeric *amount,
     gint err_code;
     const gnc_commodity *comm;
     char *filtered_string;
-    const gchar *symbol;
+    const gchar *symbol = NULL;
 
     g_return_val_if_fail (gae != NULL, -1);
     g_return_val_if_fail (GNC_IS_AMOUNT_EDIT(gae), -1);
 
-    string = gtk_entry_get_text (GTK_ENTRY(gae));
+    string = gtk_entry_get_text (GTK_ENTRY(gae->entry));
+
+    if (gtk_widget_get_visible (GTK_WIDGET(gae->image)))
+    {
+        gtk_widget_hide (GTK_WIDGET(gae->image));
+        gtk_widget_set_tooltip_text (GTK_WIDGET(gae->image), NULL);
+    }
 
     comm = gae->print_info.commodity;
 
@@ -350,6 +422,13 @@ gnc_amount_edit_expr_is_valid (GNCAmountEdit *gae, gnc_numeric *amount,
     if (error)
         g_set_error_literal (error, exp_validate_quark(), err_code, err_msg);
 
+    if (gae->show_warning_symbol)
+    {
+        gtk_widget_set_tooltip_text (GTK_WIDGET(gae->image), err_msg);
+        gtk_widget_show (GTK_WIDGET(gae->image));
+        gtk_widget_queue_resize (GTK_WIDGET(gae->entry));
+    }
+
     g_free (filtered_string);
     g_free (err_msg);
     return 1;
@@ -385,7 +464,7 @@ gnc_amount_edit_evaluate (GNCAmountEdit *gae, GError **error)
         if (!gnc_numeric_equal (amount, old_amount))
             g_signal_emit (gae, amount_edit_signals [AMOUNT_CHANGED], 0);
 
-        gtk_editable_set_position (GTK_EDITABLE(gae), -1);
+        gtk_editable_set_position (GTK_EDITABLE(gae->entry), -1);
         return TRUE;
     }
 
@@ -393,7 +472,7 @@ gnc_amount_edit_evaluate (GNCAmountEdit *gae, GError **error)
     if (tmp_error)
     {
         if (tmp_error->code < 1000)
-            gtk_editable_set_position (GTK_EDITABLE(gae), tmp_error->code);
+            gtk_editable_set_position (GTK_EDITABLE(gae->entry), tmp_error->code);
 
         if (error)
             g_propagate_error (error, tmp_error);
@@ -434,9 +513,15 @@ gnc_amount_edit_set_amount (GNCAmountEdit *gae, gnc_numeric amount)
     g_return_if_fail (GNC_IS_AMOUNT_EDIT(gae));
     g_return_if_fail (!gnc_numeric_check (amount));
 
+    if (gtk_widget_get_visible (GTK_WIDGET(gae->image)))
+    {
+        gtk_widget_hide (GTK_WIDGET(gae->image));
+        gtk_widget_set_tooltip_text (GTK_WIDGET(gae->image), NULL);
+    }
+
     /* Update the display. */
     amount_string = xaccPrintAmount (amount, gae->print_info);
-    gtk_entry_set_text (GTK_ENTRY(gae), amount_string);
+    gtk_entry_set_text (GTK_ENTRY(gae->entry), amount_string);
 
     gae->amount = amount;
     gae->need_to_parse = FALSE;
@@ -489,7 +574,7 @@ gnc_amount_edit_gtk_entry (GNCAmountEdit *gae)
     g_return_val_if_fail (gae != NULL, NULL);
     g_return_val_if_fail (GNC_IS_AMOUNT_EDIT(gae), NULL);
 
-    return GTK_WIDGET(gae);
+    return GTK_WIDGET(gae->entry);
 }
 
 void
@@ -501,3 +586,35 @@ gnc_amount_edit_set_evaluate_on_enter (GNCAmountEdit *gae,
 
     gae->evaluate_on_enter = evaluate_on_enter;
 }
+
+void
+gnc_amount_edit_set_validate_on_change (GNCAmountEdit *gae,
+                                        gboolean validate_on_change)
+{
+    g_return_if_fail (gae != NULL);
+    g_return_if_fail (GNC_IS_AMOUNT_EDIT(gae));
+
+    gae->validate_on_change = validate_on_change;
+}
+
+void
+gnc_amount_edit_select_region (GNCAmountEdit *gae,
+                               gint start_pos,
+                               gint end_pos)
+{
+    g_return_if_fail (gae != NULL);
+    g_return_if_fail (GNC_IS_AMOUNT_EDIT(gae));
+
+    gtk_editable_select_region (GTK_EDITABLE(gae->entry),
+                                start_pos,
+                                end_pos);
+}
+
+void
+gnc_amount_edit_show_warning_symbol (GNCAmountEdit *gae, gboolean show)
+{
+    g_return_if_fail (gae != NULL);
+    g_return_if_fail (GNC_IS_AMOUNT_EDIT(gae));
+
+    gae->show_warning_symbol = show;
+}
diff --git a/gnucash/gnome-utils/gnc-amount-edit.h b/gnucash/gnome-utils/gnc-amount-edit.h
index 575b9a865..db3604651 100644
--- a/gnucash/gnome-utils/gnc-amount-edit.h
+++ b/gnucash/gnome-utils/gnc-amount-edit.h
@@ -39,7 +39,11 @@
 
 typedef struct
 {
-    GtkEntry entry;
+    GtkBox    box;
+    GtkEntry *entry;
+    GtkWidget *image;
+ 
+    gboolean disposed;
 
     gboolean need_to_parse;
 
@@ -52,14 +56,19 @@ typedef struct
     int fraction;
 
     gboolean evaluate_on_enter;
+    gboolean validate_on_change;
+
+    gboolean show_warning_symbol;
 
 } GNCAmountEdit;
 
 typedef struct
 {
-    GtkEntryClass parent_class;
+    GtkBoxClass parent_class;
 
     /* Signals for notification/filtering of changes */
+    void (*activate) (GNCAmountEdit *gae);
+    void (*changed) (GNCAmountEdit *gae);
     void (*amount_changed) (GNCAmountEdit *gae);
 } GNCAmountEditClass;
 
@@ -68,7 +77,7 @@ typedef struct
  *
  * Returns the GType for the GNCAmountEdit widget
  */
-GType gnc_amount_edit_get_type (void);
+GType gnc_amount_edit_get_type (void) G_GNUC_CONST;
 
 /**
  * gnc_amount_edit_new:
@@ -195,4 +204,36 @@ void gnc_amount_edit_set_fraction (GNCAmountEdit *gae, int fraction);
  */
 void gnc_amount_edit_set_evaluate_on_enter (GNCAmountEdit *gae,
                                             gboolean evaluate_on_enter);
+
+/**
+ * gnc_amount_edit_set_validate_on_change:
+ * @gae: The GNCAmountEdit widget
+ * @validate_on_change: The flag value to set
+ *
+ * Returns nothing.
+ */
+void gnc_amount_edit_set_validate_on_change (GNCAmountEdit *gae,
+                                             gboolean validate_on_change);
+
+/**
+ * gnc_amount_edit_select_region:
+ * @gae: The GNCAmountEdit widget
+ * @start_pos: start of region
+ * @end_pos: end of region
+ *
+ * Returns nothing.
+ */
+void gnc_amount_edit_select_region (GNCAmountEdit *gae,
+                                    gint start_pos,
+                                    gint end_pos);
+
+/**
+ * gnc_amount_edit_show_warning_symbol:
+ * @gae: The GNCAmountEdit widget
+ * @show: default is TRUE to show warning symbol, FALSE to not.
+ *
+ * Returns nothing.
+ */
+void gnc_amount_edit_show_warning_symbol (GNCAmountEdit *gae, gboolean show);
+
 #endif

commit 7ee9dac2e9d428ade889deb5e3f64c3d45e1d3eb
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Mon May 31 10:55:42 2021 +0100

    Add filter for currency symbol for GNCAmountEdit widget
    
    When pasting an amount that includes the currency symbol the validation
    will silently fail which may lead to unusual behaviour.

diff --git a/gnucash/gnome-utils/gnc-amount-edit.c b/gnucash/gnome-utils/gnc-amount-edit.c
index 28bd028f1..1920a85e4 100644
--- a/gnucash/gnome-utils/gnc-amount-edit.c
+++ b/gnucash/gnome-utils/gnc-amount-edit.c
@@ -246,6 +246,45 @@ gnc_amount_edit_new (void)
     return GTK_WIDGET(gae);
 }
 
+static gint
+get_original_error_position (const gchar *string, const gchar *symbol,
+                             gint error_pos)
+{
+    gint original_error_pos = error_pos;
+    gint text_len;
+    gint symbol_len;
+
+    if (error_pos == 0)
+        return 0;
+
+    if (!string || !symbol)
+        return error_pos;
+
+    if (g_strrstr (string, symbol) == NULL)
+        return error_pos;
+
+    if (!g_utf8_validate (string, -1, NULL))
+        return error_pos;
+
+    text_len = g_utf8_strlen (string, -1);
+    symbol_len = g_utf8_strlen (symbol, -1);
+
+    for (gint x = 0; x < text_len; x++)
+    {
+        gchar *temp = g_utf8_offset_to_pointer (string, x);
+
+        if (g_str_has_prefix (temp, symbol))
+            original_error_pos = original_error_pos + symbol_len;
+
+        if (x >= original_error_pos)
+            break;
+
+        if (g_strrstr (temp, symbol) == NULL)
+            break;
+    }
+    return original_error_pos;
+}
+
 static inline GQuark
 exp_validate_quark (void)
 {
@@ -261,12 +300,20 @@ gnc_amount_edit_expr_is_valid (GNCAmountEdit *gae, gnc_numeric *amount,
     gboolean ok;
     gchar *err_msg = NULL;
     gint err_code;
+    const gnc_commodity *comm;
+    char *filtered_string;
+    const gchar *symbol;
 
     g_return_val_if_fail (gae != NULL, -1);
     g_return_val_if_fail (GNC_IS_AMOUNT_EDIT(gae), -1);
 
     string = gtk_entry_get_text (GTK_ENTRY(gae));
-    if (!string || *string == '\0')
+
+    comm = gae->print_info.commodity;
+
+    filtered_string = gnc_filter_text_for_currency_commodity (comm, string, &symbol);
+
+    if (!filtered_string || *filtered_string == '\0')
     {
         *amount = gnc_numeric_zero ();
         if (empty_ok)
@@ -276,15 +323,20 @@ gnc_amount_edit_expr_is_valid (GNCAmountEdit *gae, gnc_numeric *amount,
     }
 
     error_loc = NULL;
-    ok = gnc_exp_parser_parse (string, amount, &error_loc);
+    ok = gnc_exp_parser_parse (filtered_string, amount, &error_loc);
 
     if (ok)
+    {
+        g_free (filtered_string);
         return 0;
+    }
 
     /* Not ok */
     if (error_loc != NULL)
     {
-        err_code = error_loc - string;
+        err_code = get_original_error_position (string, symbol,
+                                               (error_loc - filtered_string));
+
         err_msg = g_strdup_printf (_("An error occurred while processing '%s' at position %d"),
                                    string, err_code);
     }
@@ -298,6 +350,7 @@ gnc_amount_edit_expr_is_valid (GNCAmountEdit *gae, gnc_numeric *amount,
     if (error)
         g_set_error_literal (error, exp_validate_quark(), err_code, err_msg);
 
+    g_free (filtered_string);
     g_free (err_msg);
     return 1;
 }
@@ -332,6 +385,7 @@ gnc_amount_edit_evaluate (GNCAmountEdit *gae, GError **error)
         if (!gnc_numeric_equal (amount, old_amount))
             g_signal_emit (gae, amount_edit_signals [AMOUNT_CHANGED], 0);
 
+        gtk_editable_set_position (GTK_EDITABLE(gae), -1);
         return TRUE;
     }
 
diff --git a/libgnucash/app-utils/gnc-ui-util.c b/libgnucash/app-utils/gnc-ui-util.c
index f5ed8bbf6..707dbee28 100644
--- a/libgnucash/app-utils/gnc-ui-util.c
+++ b/libgnucash/app-utils/gnc-ui-util.c
@@ -2784,3 +2784,28 @@ gnc_filter_text_for_currency_symbol (const gchar *incoming_text,
     g_strfreev (split);
     return ret_text;
 }
+
+gchar *
+gnc_filter_text_for_currency_commodity (const gnc_commodity *comm,
+                                        const gchar *incoming_text,
+                                        const gchar **symbol)
+{
+    if (!incoming_text)
+    {
+        *symbol = NULL;
+        return NULL;
+    }
+
+    if (!gnc_commodity_is_currency (comm))
+    {
+        *symbol = NULL;
+        return g_strdup (incoming_text);
+    }
+
+    if (comm)
+        *symbol = gnc_commodity_get_nice_symbol (comm);
+    else
+        *symbol = gnc_commodity_get_nice_symbol (gnc_default_currency ());
+
+    return gnc_filter_text_for_currency_symbol (incoming_text, *symbol);
+}
diff --git a/libgnucash/app-utils/gnc-ui-util.h b/libgnucash/app-utils/gnc-ui-util.h
index ca72785a1..168fbe851 100644
--- a/libgnucash/app-utils/gnc-ui-util.h
+++ b/libgnucash/app-utils/gnc-ui-util.h
@@ -433,6 +433,20 @@ void gnc_filter_text_set_cursor_position (const gchar *incoming_text,
 gchar * gnc_filter_text_for_currency_symbol (const gchar *incoming_text,
                                              const gchar *symbol);
 
+/** Returns the incoming text removed of currency symbol
+ * 
+ * @param comm commodity of entry if known
+ * 
+ * @param incoming_text The text to filter
+ *
+ * @param symbol return the symbol used
+ *
+ * @return The incoming text with symbol removed to be freed by the caller
+*/
+gchar * gnc_filter_text_for_currency_commodity (const gnc_commodity *comm,
+                                                const gchar *incoming_text,
+                                                const gchar **symbol);
+
 #endif
 /** @} */
 /** @} */

commit db6f2337051434135ea8b7747546bfc1ead80b8e
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Mon May 31 10:53:56 2021 +0100

    Incorrect validation for GNCAmountEdit
    
    gnc_amount_edit_expr_is_valid can return 0 for validation OK, 1 for an
    error with no position information and also the position of that error.
    If the error was at position 0, that will look like the validation was
    OK so instead return 1 and use a GError parameter using the position as
    the code and setting the message to the common format used. Also as
    gnc_amount_edit_evaluate could also benefit from using the GError add
    that as a parameter and initially set all occurrences of use to NULL.

diff --git a/gnucash/gnome-utils/dialog-account.c b/gnucash/gnome-utils/dialog-account.c
index 410588a78..04c0b89b8 100644
--- a/gnucash/gnome-utils/dialog-account.c
+++ b/gnucash/gnome-utils/dialog-account.c
@@ -947,7 +947,7 @@ gnc_new_account_ok (AccountWindow *aw)
         return;
     }
 
-    if (!gnc_amount_edit_evaluate (GNC_AMOUNT_EDIT (aw->opening_balance_edit)))
+    if (!gnc_amount_edit_evaluate (GNC_AMOUNT_EDIT (aw->opening_balance_edit), NULL))
     {
         const char *message = _("You must enter a valid opening balance "
                                 "or leave it blank.");
diff --git a/gnucash/gnome-utils/dialog-transfer.c b/gnucash/gnome-utils/dialog-transfer.c
index 616223cf9..b7ba77157 100644
--- a/gnucash/gnome-utils/dialog-transfer.c
+++ b/gnucash/gnome-utils/dialog-transfer.c
@@ -991,7 +991,7 @@ gnc_xfer_amount_update_cb(GtkWidget *widget, GdkEventFocus *event,
     XferDialog * xferData = data;
     g_return_val_if_fail (xferData != NULL, FALSE);
 
-    gnc_amount_edit_evaluate (GNC_AMOUNT_EDIT (xferData->amount_edit));
+    gnc_amount_edit_evaluate (GNC_AMOUNT_EDIT (xferData->amount_edit), NULL);
 
     gnc_xfer_update_to_amount (xferData);
 
@@ -1027,7 +1027,7 @@ gnc_xfer_update_to_amount (XferDialog *xferData)
         scu = gnc_commodity_get_fraction(xferData->to_commodity);
 
     /* Determine the amount to transfer. */
-    if (!gnc_amount_edit_evaluate(price_edit) ||
+    if (!gnc_amount_edit_evaluate(price_edit, NULL) ||
         gnc_numeric_zero_p(price_value = gnc_amount_edit_get_amount(price_edit)))
         to_amount = gnc_numeric_zero();
     else
@@ -1075,7 +1075,7 @@ gnc_xfer_to_amount_update_cb(GtkWidget *widget, GdkEventFocus *event,
     XferDialog *xferData = data;
     gnc_numeric price_value;
 
-    gnc_amount_edit_evaluate (GNC_AMOUNT_EDIT (xferData->to_amount_edit));
+    gnc_amount_edit_evaluate (GNC_AMOUNT_EDIT (xferData->to_amount_edit), NULL);
     price_value = gnc_xfer_dialog_compute_price_value(xferData);
     gnc_amount_edit_set_amount(GNC_AMOUNT_EDIT(xferData->price_edit),
                                price_value);
@@ -1474,7 +1474,7 @@ check_accounts  (XferDialog* xferData, Account* from_account,
 static gboolean
 check_edit(XferDialog *xferData)
 {
-    if (!gnc_amount_edit_evaluate (GNC_AMOUNT_EDIT (xferData->price_edit)))
+    if (!gnc_amount_edit_evaluate (GNC_AMOUNT_EDIT (xferData->price_edit), NULL))
     {
         if (gtk_toggle_button_get_active
             (GTK_TOGGLE_BUTTON(xferData->price_radio)))
@@ -1485,7 +1485,7 @@ check_edit(XferDialog *xferData)
         }
     }
 
-    if (!gnc_amount_edit_evaluate (GNC_AMOUNT_EDIT (xferData->to_amount_edit)))
+    if (!gnc_amount_edit_evaluate (GNC_AMOUNT_EDIT (xferData->to_amount_edit), NULL))
     {
         if (gtk_toggle_button_get_active
             (GTK_TOGGLE_BUTTON(xferData->amount_radio)))
@@ -1708,7 +1708,7 @@ gnc_xfer_dialog_response_cb (GtkDialog *dialog, gint response, gpointer data)
         !check_accounts(xferData, from_account, to_account))
         return;
 
-    if (!gnc_amount_edit_evaluate (GNC_AMOUNT_EDIT (xferData->amount_edit)))
+    if (!gnc_amount_edit_evaluate (GNC_AMOUNT_EDIT (xferData->amount_edit), NULL))
     {
         gnc_parse_error_dialog (xferData, _("You must enter a valid amount."));
         LEAVE("no amount");
diff --git a/gnucash/gnome-utils/gnc-amount-edit.c b/gnucash/gnome-utils/gnc-amount-edit.c
index 5873beee5..28bd028f1 100644
--- a/gnucash/gnome-utils/gnc-amount-edit.c
+++ b/gnucash/gnome-utils/gnc-amount-edit.c
@@ -28,6 +28,7 @@
 
 #include <gtk/gtk.h>
 #include <gdk/gdkkeysyms.h>
+#include <glib/gi18n.h>
 
 #include "gnc-amount-edit.h"
 #include "gnc-exp-parser.h"
@@ -229,7 +230,7 @@ gnc_amount_edit_key_press (GtkWidget *widget, GdkEventKey *event)
         return result;
     }
 
-    gnc_amount_edit_evaluate (gae);
+    gnc_amount_edit_evaluate (gae, NULL);
 
     return TRUE;
 }
@@ -245,13 +246,21 @@ gnc_amount_edit_new (void)
     return GTK_WIDGET(gae);
 }
 
+static inline GQuark
+exp_validate_quark (void)
+{
+    return g_quark_from_static_string ("exp_validate");
+}
+
 gint
 gnc_amount_edit_expr_is_valid (GNCAmountEdit *gae, gnc_numeric *amount,
-                               gboolean empty_ok)
+                               gboolean empty_ok, GError **error)
 {
     const char *string;
     char *error_loc;
     gboolean ok;
+    gchar *err_msg = NULL;
+    gint err_code;
 
     g_return_val_if_fail (gae != NULL, -1);
     g_return_val_if_fail (GNC_IS_AMOUNT_EDIT(gae), -1);
@@ -274,25 +283,39 @@ gnc_amount_edit_expr_is_valid (GNCAmountEdit *gae, gnc_numeric *amount,
 
     /* Not ok */
     if (error_loc != NULL)
-        return  error_loc - string;
+    {
+        err_code = error_loc - string;
+        err_msg = g_strdup_printf (_("An error occurred while processing '%s' at position %d"),
+                                   string, err_code);
+    }
     else
-        return 1;
+    {
+        err_code = 1000;
+        err_msg = g_strdup_printf (_("An error occurred while processing '%s'"),
+                                   string);
+    }
+
+    if (error)
+        g_set_error_literal (error, exp_validate_quark(), err_code, err_msg);
+
+    g_free (err_msg);
+    return 1;
 }
 
 gboolean
-gnc_amount_edit_evaluate (GNCAmountEdit *gae)
+gnc_amount_edit_evaluate (GNCAmountEdit *gae, GError **error)
 {
     gint result;
     gnc_numeric amount;
+    GError *tmp_error = NULL;
 
     g_return_val_if_fail (gae != NULL, FALSE);
     g_return_val_if_fail (GNC_IS_AMOUNT_EDIT(gae), FALSE);
 
-
     if (!gae->need_to_parse)
         return TRUE;
 
-    result = gnc_amount_edit_expr_is_valid (gae, &amount, FALSE);
+    result = gnc_amount_edit_expr_is_valid (gae, &amount, FALSE, &tmp_error);
 
     if (result == -1)  /* field was empty and may remain so */
         return TRUE;
@@ -313,7 +336,16 @@ gnc_amount_edit_evaluate (GNCAmountEdit *gae)
     }
 
     /* Parse error */
-    gtk_editable_set_position (GTK_EDITABLE(gae), result);
+    if (tmp_error)
+    {
+        if (tmp_error->code < 1000)
+            gtk_editable_set_position (GTK_EDITABLE(gae), tmp_error->code);
+
+        if (error)
+            g_propagate_error (error, tmp_error);
+        else
+            g_error_free (tmp_error);
+    }
     return FALSE;
 }
 
@@ -323,7 +355,7 @@ gnc_amount_edit_get_amount (GNCAmountEdit *gae)
     g_return_val_if_fail (gae != NULL, gnc_numeric_zero ());
     g_return_val_if_fail (GNC_IS_AMOUNT_EDIT(gae), gnc_numeric_zero ());
 
-    gnc_amount_edit_evaluate (gae);
+    gnc_amount_edit_evaluate (gae, NULL);
 
     return gae->amount;
 }
@@ -334,7 +366,7 @@ gnc_amount_edit_get_damount (GNCAmountEdit *gae)
     g_return_val_if_fail (gae != NULL, 0.0);
     g_return_val_if_fail (GNC_IS_AMOUNT_EDIT(gae), 0.0);
 
-    gnc_amount_edit_evaluate (gae);
+    gnc_amount_edit_evaluate (gae, NULL);
 
     return gnc_numeric_to_double (gae->amount);
 }
diff --git a/gnucash/gnome-utils/gnc-amount-edit.h b/gnucash/gnome-utils/gnc-amount-edit.h
index e4596f2f7..575b9a865 100644
--- a/gnucash/gnome-utils/gnc-amount-edit.h
+++ b/gnucash/gnome-utils/gnc-amount-edit.h
@@ -135,7 +135,9 @@ double gnc_amount_edit_get_damount (GNCAmountEdit *gae);
  * @amount: parameter to hold the value of the parsed expression
  * @empty_ok: if true, an empty field is skipped, otherwise an empty field
  *            parses as 0
- *
+ * @error: if error location information is available it will be stored 
+ *         in this variable. Set it to NULL if you don't want the error.
+ * 
  * If needed, parse the expression in the amount entry. If there's no
  * parsing error, it returns the amount, otherwise it returns the
  * position in the expression where the error occurred.
@@ -143,15 +145,18 @@ double gnc_amount_edit_get_damount (GNCAmountEdit *gae);
  * Return *  0 if the parsing was successful (note that if !empty_ok,
  *             an empty field will parse to 0)
  *        * -1 if the field is empty and that's ok (empty_ok)
- *        * error position if there was a parsing error
+ *        *  1 parsing failed
  */
 gint gnc_amount_edit_expr_is_valid (GNCAmountEdit *gae,
                                     gnc_numeric *amount,
-                                    gboolean empty_ok);
+                                    gboolean empty_ok,
+                                    GError **error);
 
 /**
  * gnc_amount_edit_evaluate
  * @gae: The GNCAmountEdit widget
+ * @error: if error location information is available it will be stored 
+ *         in this variable. Set it to NULL if you don't want the error.
  *
  * If needed, parse the expression in the amount entry
  * and replace the expression with the result of evaluation.
@@ -160,7 +165,7 @@ gint gnc_amount_edit_expr_is_valid (GNCAmountEdit *gae,
  *
  * Return TRUE if parsing was successful or there was no need to parse.
  */
-gboolean gnc_amount_edit_evaluate (GNCAmountEdit *gae);
+gboolean gnc_amount_edit_evaluate (GNCAmountEdit *gae, GError **error);
 
 /**
  * gnc_amount_edit_set_print_flags:
diff --git a/gnucash/gnome/assistant-stock-split.c b/gnucash/gnome/assistant-stock-split.c
index f80be73e0..d557975f5 100644
--- a/gnucash/gnome/assistant-stock-split.c
+++ b/gnucash/gnome/assistant-stock-split.c
@@ -277,14 +277,14 @@ gnc_stock_split_assistant_details_complete (GtkAssistant *assistant,
     gnc_numeric amount;
     gint result;
 
-    result = gnc_amount_edit_expr_is_valid (GNC_AMOUNT_EDIT (info->distribution_edit), &amount, TRUE);
+    result = gnc_amount_edit_expr_is_valid (GNC_AMOUNT_EDIT (info->distribution_edit), &amount, TRUE, NULL);
     if ( result != 0)
         return FALSE; /* Parsing error or field is empty */
 
     if (gnc_numeric_zero_p (amount))
         return FALSE; /* field value is 0 */
 
-    result = gnc_amount_edit_expr_is_valid (GNC_AMOUNT_EDIT (info->price_edit), &amount, TRUE);
+    result = gnc_amount_edit_expr_is_valid (GNC_AMOUNT_EDIT (info->price_edit), &amount, TRUE, NULL);
     if (result == -1)
         return TRUE; /* Optional field is empty */
     else if ( result > 0)
@@ -305,7 +305,7 @@ gnc_stock_split_assistant_cash_complete (GtkAssistant *assistant,
     gint result;
     Account *account;
 
-    result = gnc_amount_edit_expr_is_valid (GNC_AMOUNT_EDIT (info->cash_edit), &amount, TRUE);
+    result = gnc_amount_edit_expr_is_valid (GNC_AMOUNT_EDIT (info->cash_edit), &amount, TRUE, NULL);
     if (result == -1)
         return TRUE; /* Optional field is empty */
     else if ( result > 0)
diff --git a/gnucash/gnome/dialog-customer.c b/gnucash/gnome/dialog-customer.c
index fbd3cc0de..d2d40f948 100644
--- a/gnucash/gnome/dialog-customer.c
+++ b/gnucash/gnome/dialog-customer.c
@@ -283,7 +283,7 @@ static gboolean check_edit_amount (GtkWidget *amount,
                                    gnc_numeric *min, gnc_numeric *max,
                                    const char * error_message)
 {
-    if (!gnc_amount_edit_evaluate (GNC_AMOUNT_EDIT (amount)))
+    if (!gnc_amount_edit_evaluate (GNC_AMOUNT_EDIT (amount), NULL))
     {
         if (error_message)
             gnc_error_dialog (gnc_ui_get_gtk_window (amount), "%s", error_message);
diff --git a/gnucash/gnome/dialog-fincalc.c b/gnucash/gnome/dialog-fincalc.c
index 995b6cd8c..286f3d98c 100644
--- a/gnucash/gnome/dialog-fincalc.c
+++ b/gnucash/gnome/dialog-fincalc.c
@@ -350,7 +350,7 @@ can_calc_value(FinCalcDialog *fcd, FinCalcValue value, int *error_item)
             /* treat PAYMENT_PERIODS as a plain GtkEntry */
             if (i != PAYMENT_PERIODS)
             {
-                if (!gnc_amount_edit_evaluate (GNC_AMOUNT_EDIT (fcd->amounts[i])))
+                if (!gnc_amount_edit_evaluate (GNC_AMOUNT_EDIT (fcd->amounts[i]), NULL))
                 {
                     *error_item = i;
                     return bad_exp;
diff --git a/gnucash/gnome/dialog-invoice.c b/gnucash/gnome/dialog-invoice.c
index b05760ea3..723875eb1 100644
--- a/gnucash/gnome/dialog-invoice.c
+++ b/gnucash/gnome/dialog-invoice.c
@@ -1364,7 +1364,7 @@ static gboolean
 gnc_invoice_window_leave_to_charge_cb (GtkWidget *widget, GdkEventFocus *event,
                                        gpointer data)
 {
-    gnc_amount_edit_evaluate (GNC_AMOUNT_EDIT (widget));
+    gnc_amount_edit_evaluate (GNC_AMOUNT_EDIT (widget), NULL);
     return FALSE;
 }
 
@@ -1761,7 +1761,7 @@ gnc_invoice_redraw_all_cb (GnucashRegister *g_reg, gpointer data)
 
     if (iw->to_charge_edit)
     {
-        gnc_amount_edit_evaluate (GNC_AMOUNT_EDIT (iw->to_charge_edit));
+        gnc_amount_edit_evaluate (GNC_AMOUNT_EDIT (iw->to_charge_edit), NULL);
         to_charge_amt = gnc_amount_edit_get_amount(GNC_AMOUNT_EDIT(iw->to_charge_edit));
     }
 
diff --git a/gnucash/gnome/dialog-price-editor.c b/gnucash/gnome/dialog-price-editor.c
index f5b455a0b..05706e5a3 100644
--- a/gnucash/gnome/dialog-price-editor.c
+++ b/gnucash/gnome/dialog-price-editor.c
@@ -274,7 +274,7 @@ gui_to_price (PriceEditDialog *pedit_dialog)
     type = type_index_to_string
            (gtk_combo_box_get_active (GTK_COMBO_BOX (pedit_dialog->type_combobox)));
 
-    if (!gnc_amount_edit_evaluate (GNC_AMOUNT_EDIT (pedit_dialog->price_edit)))
+    if (!gnc_amount_edit_evaluate (GNC_AMOUNT_EDIT (pedit_dialog->price_edit), NULL))
         return _("You must enter a valid amount.");
 
     value = gnc_amount_edit_get_amount
diff --git a/gnucash/gnome/window-reconcile.c b/gnucash/gnome/window-reconcile.c
index 2e8318ab0..210c4c94d 100644
--- a/gnucash/gnome/window-reconcile.c
+++ b/gnucash/gnome/window-reconcile.c
@@ -317,7 +317,7 @@ gnc_start_recn_update_cb(GtkWidget *widget, GdkEventFocus *event,
 {
     gnc_numeric value;
 
-    gnc_amount_edit_evaluate (GNC_AMOUNT_EDIT(data->end_value));
+    gnc_amount_edit_evaluate (GNC_AMOUNT_EDIT(data->end_value), NULL);
 
     value = gnc_amount_edit_get_amount (GNC_AMOUNT_EDIT(data->end_value));
     data->user_set_value = !gnc_numeric_equal(value, data->original_value);
diff --git a/gnucash/gnome/window-reconcile2.c b/gnucash/gnome/window-reconcile2.c
index 191713165..82f4a74db 100644
--- a/gnucash/gnome/window-reconcile2.c
+++ b/gnucash/gnome/window-reconcile2.c
@@ -307,7 +307,7 @@ gnc_start_recn2_update_cb (GtkWidget *widget, GdkEventFocus *event,
 {
     gnc_numeric value;
 
-    gnc_amount_edit_evaluate (GNC_AMOUNT_EDIT (data->end_value));
+    gnc_amount_edit_evaluate (GNC_AMOUNT_EDIT (data->end_value), NULL);
 
     value = gnc_amount_edit_get_amount (GNC_AMOUNT_EDIT (data->end_value));
     data->user_set_value = !gnc_numeric_equal (value, data->original_value);

commit 788166b3a74056a858949873652abff3c4ab6c1a
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Mon May 31 10:53:09 2021 +0100

    Filter control characters when pasted to GNCAmountEdit

diff --git a/gnucash/gnome-utils/gnc-amount-edit.c b/gnucash/gnome-utils/gnc-amount-edit.c
index 8281f4100..5873beee5 100644
--- a/gnucash/gnome-utils/gnc-amount-edit.c
+++ b/gnucash/gnome-utils/gnc-amount-edit.c
@@ -35,6 +35,7 @@
 #include "gnc-ui-util.h"
 #include "qof.h"
 #include "dialog-utils.h"
+#include "gnc-ui.h"
 
 #ifdef G_OS_WIN32
 # include <gdk/gdkwin32.h>
@@ -53,6 +54,8 @@ static void gnc_amount_edit_init (GNCAmountEdit *gae);
 static void gnc_amount_edit_class_init (GNCAmountEditClass *klass);
 static void gnc_amount_edit_changed (GtkEditable *gae,
                                      gpointer user_data);
+static void gnc_amount_edit_paste_clipboard (GNCAmountEdit *gae,
+                                             gpointer user_data);
 static gint gnc_amount_edit_key_press (GtkWidget   *widget,
                                        GdkEventKey *event);
 
@@ -120,22 +123,75 @@ gnc_amount_edit_init (GNCAmountEdit *gae)
     gae->print_info = gnc_default_print_info (FALSE);
     gae->fraction = 0;
     gae->evaluate_on_enter = FALSE;
+    gae->block_changed = FALSE;
 
     // Set the name for this widget so it can be easily manipulated with css
     gtk_widget_set_name (GTK_WIDGET(gae), "gnc-id-amount-edit");
 
     g_signal_connect (G_OBJECT(gae), "changed",
-                      G_CALLBACK(gnc_amount_edit_changed), NULL);
+                      G_CALLBACK(gnc_amount_edit_changed), gae);
+
+    g_signal_connect (G_OBJECT(gae), "paste-clipboard",
+                      G_CALLBACK(gnc_amount_edit_paste_clipboard), NULL);
 }
 
 static void
 gnc_amount_edit_changed (GtkEditable *editable, gpointer user_data)
 {
-    /*GTK_EDITABLE_CLASS(parent_class)->changed(editable);*/
+    GNCAmountEdit *gae = GNC_AMOUNT_EDIT(user_data);
+     /*GTK_EDITABLE_CLASS(parent_class)->changed(editable);*/
+    if (gae->block_changed)
+        g_signal_stop_emission_by_name (G_OBJECT(gae), "changed");
 
     GNC_AMOUNT_EDIT(editable)->need_to_parse = TRUE;
 }
 
+static void
+gnc_amount_edit_paste_clipboard (GNCAmountEdit *gae, gpointer user_data)
+{
+    GtkClipboard *clipboard = gtk_widget_get_clipboard (GTK_WIDGET(gae),
+                                                        GDK_SELECTION_CLIPBOARD);
+    gchar *text = gtk_clipboard_wait_for_text (clipboard);
+    gchar *filtered_text;
+    gint start_pos, end_pos;
+    gint position;
+
+    if (!text)
+        return;
+
+    filtered_text = gnc_filter_text_for_control_chars (text);
+
+    if (!filtered_text)
+    {
+        g_free (text);
+        return;
+    }
+
+    position = gtk_editable_get_position (GTK_EDITABLE(gae));
+
+    if (gtk_editable_get_selection_bounds (GTK_EDITABLE(gae),
+                                           &start_pos, &end_pos))
+    {
+        position = start_pos;
+
+        gae->block_changed = TRUE;
+        gtk_editable_delete_selection (GTK_EDITABLE(gae));
+        gae->block_changed = FALSE;
+        gtk_editable_insert_text (GTK_EDITABLE(gae),
+                                  filtered_text, -1, &position);
+    }
+    else
+        gtk_editable_insert_text (GTK_EDITABLE(gae),
+                                  filtered_text, -1, &position);
+
+    gtk_editable_set_position (GTK_EDITABLE(gae), position);
+
+    g_signal_stop_emission_by_name (G_OBJECT(gae), "paste-clipboard");
+
+    g_free (text);
+    g_free (filtered_text);
+}
+
 static gint
 gnc_amount_edit_key_press (GtkWidget *widget, GdkEventKey *event)
 {
diff --git a/gnucash/gnome-utils/gnc-amount-edit.h b/gnucash/gnome-utils/gnc-amount-edit.h
index 00275b01e..e4596f2f7 100644
--- a/gnucash/gnome-utils/gnc-amount-edit.h
+++ b/gnucash/gnome-utils/gnc-amount-edit.h
@@ -45,6 +45,8 @@ typedef struct
 
     GNCPrintAmountInfo print_info;
 
+    gboolean block_changed;
+
     gnc_numeric amount;
 
     int fraction;

commit b728a331cfcd699b4b4a0899e3594dfa632ab7f8
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Mon May 31 10:52:33 2021 +0100

    Realign source file gnc-amount-edit.* for white space

diff --git a/gnucash/gnome-utils/gnc-amount-edit.c b/gnucash/gnome-utils/gnc-amount-edit.c
index b334d24b9..8281f4100 100644
--- a/gnucash/gnome-utils/gnc-amount-edit.c
+++ b/gnucash/gnome-utils/gnc-amount-edit.c
@@ -1,37 +1,29 @@
-/*
- * gnc-amount-edit.c -- Amount editor widget
- *
- * Copyright (C) 2000 Dave Peticolas <dave at krondo.com>
- * All rights reserved.
- *
- * Gnucash is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Library General Public License as
- * published by the Free Software Foundation; either version 2 of the
- * License, or (at your option) any later version.
- *
- * Gnucash is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, contact:
- *
- * Free Software Foundation           Voice:  +1-617-542-5942
- * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
- * Boston, MA  02110-1301,  USA       gnu at gnu.org
- *
- */
+/********************************************************************\
+ * gnc-amount-edit.h -- amount editor widget                        *
+ *                                                                  *
+ * Copyright (C) 2000 Dave Peticolas <dave at krondo.com>              *
+ *                                                                  *
+ * This program is free software; you can redistribute it and/or    *
+ * modify it under the terms of the GNU General Public License as   *
+ * published by the Free Software Foundation; either version 2 of   *
+ * the License, or (at your option) any later version.              *
+ *                                                                  *
+ * This program is distributed in the hope that it will be useful,  *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
+ * GNU General Public License for more details.                     *
+ *                                                                  *
+ * You should have received a copy of the GNU General Public License*
+ * along with this program; if not, contact:                        *
+ *                                                                  *
+ * Free Software Foundation           Voice:  +1-617-542-5942       *
+ * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
+ * Boston, MA  02110-1301,  USA       gnu at gnu.org                   *
+\********************************************************************/
 /*
   @NOTATION@
  */
 
-/*
- * Amount editor widget
- *
- * Authors: Dave Peticolas <dave at krondo.com>
- */
-
 #include <config.h>
 
 #include <gtk/gtk.h>
@@ -55,24 +47,17 @@ enum
     LAST_SIGNAL
 };
 
-
 static guint amount_edit_signals [LAST_SIGNAL] = { 0 };
 
-
-static void gnc_amount_edit_init         (GNCAmountEdit      *gae);
-static void gnc_amount_edit_class_init   (GNCAmountEditClass *klass);
-static void gnc_amount_edit_changed      (GtkEditable        *gae, gpointer data);
-static gint gnc_amount_edit_key_press    (GtkWidget          *widget,
-        GdkEventKey        *event);
-
+static void gnc_amount_edit_init (GNCAmountEdit *gae);
+static void gnc_amount_edit_class_init (GNCAmountEditClass *klass);
+static void gnc_amount_edit_changed (GtkEditable *gae,
+                                     gpointer user_data);
+static gint gnc_amount_edit_key_press (GtkWidget   *widget,
+                                       GdkEventKey *event);
 
 static GtkEntryClass *parent_class;
 
-/**
- * gnc_amount_edit_get_type:
- *
- * Returns the GType for the GNCAmountEdit widget
- */
 GType
 gnc_amount_edit_get_type (void)
 {
@@ -94,32 +79,28 @@ gnc_amount_edit_get_type (void)
         };
 
         amount_edit_type = g_type_register_static (GTK_TYPE_ENTRY,
-                           "GNCAmountEdit",
-                           &amount_edit_info,
-                           0);
+                                                   "GNCAmountEdit",
+                                                    &amount_edit_info,
+                                                    0);
     }
-
     return amount_edit_type;
 }
 
 static void
 gnc_amount_edit_class_init (GNCAmountEditClass *klass)
 {
-    GObjectClass *object_class;
-    GtkWidgetClass *widget_class;
-    /* GtkEditableClass *editable_class; */
-
-    object_class = G_OBJECT_CLASS (klass);
-    widget_class = GTK_WIDGET_CLASS (klass);
-    /* editable_class = GTK_EDITABLE_CLASS (g_type_interface_peek (klass, GTK_TYPE_EDITABLE)); */
+    GObjectClass *object_class = G_OBJECT_CLASS(klass);
+    GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
+ /* GtkEditableClass *editable_class = GTK_EDITABLE_CLASS(g_type_interface_peek
+                                                         (klass, GTK_TYPE_EDITABLE)); */
 
     parent_class = g_type_class_peek_parent (klass);
 
     amount_edit_signals [AMOUNT_CHANGED] =
         g_signal_new ("amount_changed",
-                      G_OBJECT_CLASS_TYPE (object_class),
+                      G_OBJECT_CLASS_TYPE(object_class),
                       G_SIGNAL_RUN_FIRST,
-                      G_STRUCT_OFFSET (GNCAmountEditClass, amount_changed),
+                      G_STRUCT_OFFSET(GNCAmountEditClass, amount_changed),
                       NULL,
                       NULL,
                       g_cclosure_marshal_VOID__VOID,
@@ -128,7 +109,7 @@ gnc_amount_edit_class_init (GNCAmountEditClass *klass)
 
     widget_class->key_press_event = gnc_amount_edit_key_press;
 
-    /* editable_class->changed = gnc_amount_edit_changed; */
+ /* editable_class->changed = gnc_amount_edit_changed; */
 }
 
 static void
@@ -143,20 +124,20 @@ gnc_amount_edit_init (GNCAmountEdit *gae)
     // Set the name for this widget so it can be easily manipulated with css
     gtk_widget_set_name (GTK_WIDGET(gae), "gnc-id-amount-edit");
 
-    g_signal_connect (G_OBJECT (gae), "changed",
-                      G_CALLBACK (gnc_amount_edit_changed), NULL);
+    g_signal_connect (G_OBJECT(gae), "changed",
+                      G_CALLBACK(gnc_amount_edit_changed), NULL);
 }
 
 static void
-gnc_amount_edit_changed (GtkEditable *editable, gpointer data)
+gnc_amount_edit_changed (GtkEditable *editable, gpointer user_data)
 {
-    /*GTK_EDITABLE_CLASS (parent_class)->changed(editable);*/
+    /*GTK_EDITABLE_CLASS(parent_class)->changed(editable);*/
 
     GNC_AMOUNT_EDIT(editable)->need_to_parse = TRUE;
 }
 
 static gint
-gnc_amount_edit_key_press(GtkWidget *widget, GdkEventKey *event)
+gnc_amount_edit_key_press (GtkWidget *widget, GdkEventKey *event)
 {
     GNCAmountEdit *gae = GNC_AMOUNT_EDIT(widget);
     gint result;
@@ -176,7 +157,7 @@ gnc_amount_edit_key_press(GtkWidget *widget, GdkEventKey *event)
         }
     }
 
-    result = (* GTK_WIDGET_CLASS (parent_class)->key_press_event)(widget, event);
+    result = (* GTK_WIDGET_CLASS(parent_class)->key_press_event)(widget, event);
 
     switch (event->keyval)
     {
@@ -197,15 +178,6 @@ gnc_amount_edit_key_press(GtkWidget *widget, GdkEventKey *event)
     return TRUE;
 }
 
-/**
- * gnc_amount_edit_new:
- *
- * Creates a new GNCAmountEdit widget which can be used to provide
- * an easy to use way for entering amounts, allowing the user to
- * enter and evaluate expressions.
- *
- * Returns a GNCAmountEdit widget.
- */
 GtkWidget *
 gnc_amount_edit_new (void)
 {
@@ -214,25 +186,9 @@ gnc_amount_edit_new (void)
     gae = g_object_new (GNC_TYPE_AMOUNT_EDIT, NULL);
     gtk_widget_show (GTK_WIDGET(gae));
 
-    return GTK_WIDGET (gae);
+    return GTK_WIDGET(gae);
 }
 
-/**
- * gnc_amount_edit_expr_is_valid
- * @gae: The GNCAmountEdit widget
- * @amount: parameter to hold the value of the parsed expression
- * @empty_ok: if true, an empty field is skipped, otherwise an empty field
- *            parses as 0
- *
- * If needed, parse the expression in the amount entry. If there's no
- * parsing error, it returns the amount, otherwise it returns the
- * position in the expression where the error occurred.
- *
- * Return *  0 if the parsing was successful (note that if !empty_ok,
- *             an empty field will parse to 0)
- *        * -1 if the field is empty and that's ok (empty_ok)
- *        * error position if there was a parsing error
- */
 gint
 gnc_amount_edit_expr_is_valid (GNCAmountEdit *gae, gnc_numeric *amount,
                                gboolean empty_ok)
@@ -241,13 +197,13 @@ gnc_amount_edit_expr_is_valid (GNCAmountEdit *gae, gnc_numeric *amount,
     char *error_loc;
     gboolean ok;
 
-    g_return_val_if_fail(gae != NULL, -1);
-    g_return_val_if_fail(GNC_IS_AMOUNT_EDIT(gae), -1);
+    g_return_val_if_fail (gae != NULL, -1);
+    g_return_val_if_fail (GNC_IS_AMOUNT_EDIT(gae), -1);
 
-    string = gtk_entry_get_text(GTK_ENTRY(gae));
+    string = gtk_entry_get_text (GTK_ENTRY(gae));
     if (!string || *string == '\0')
     {
-        *amount = gnc_numeric_zero();
+        *amount = gnc_numeric_zero ();
         if (empty_ok)
             return -1; /* indicate an empty field */
         else
@@ -267,26 +223,14 @@ gnc_amount_edit_expr_is_valid (GNCAmountEdit *gae, gnc_numeric *amount,
         return 1;
 }
 
-
-/**
- * gnc_amount_edit_evaluate
- * @gae: The GNCAmountEdit widget
- *
- * If needed, parse the expression in the amount entry
- * and replace the expression with the result of evaluation.
- * If there is a parsing error, don't change the expression entry,
- * but do put the cursor at the point of the error.
- *
- * Return TRUE if parsing was successful or there was no need to parse.
- */
 gboolean
 gnc_amount_edit_evaluate (GNCAmountEdit *gae)
 {
     gint result;
     gnc_numeric amount;
 
-    g_return_val_if_fail(gae != NULL, FALSE);
-    g_return_val_if_fail(GNC_IS_AMOUNT_EDIT(gae), FALSE);
+    g_return_val_if_fail (gae != NULL, FALSE);
+    g_return_val_if_fail (GNC_IS_AMOUNT_EDIT(gae), FALSE);
 
 
     if (!gae->need_to_parse)
@@ -317,62 +261,36 @@ gnc_amount_edit_evaluate (GNCAmountEdit *gae)
     return FALSE;
 }
 
-
-/**
- * gnc_amount_edit_get_amount:
- * @gae: The GNCAmountEdit widget
- *
- * Returns the amount entered in the GNCAmountEdit widget,
- * parsing the expression if necessary. The result of parsing
- * replaces the expression.
- */
 gnc_numeric
 gnc_amount_edit_get_amount (GNCAmountEdit *gae)
 {
-    g_return_val_if_fail(gae != NULL, gnc_numeric_zero ());
-    g_return_val_if_fail(GNC_IS_AMOUNT_EDIT(gae), gnc_numeric_zero ());
+    g_return_val_if_fail (gae != NULL, gnc_numeric_zero ());
+    g_return_val_if_fail (GNC_IS_AMOUNT_EDIT(gae), gnc_numeric_zero ());
 
     gnc_amount_edit_evaluate (gae);
 
     return gae->amount;
 }
 
-
-/**
- * gnc_amount_edit_get_amount:
- * @gae: The GNCAmountEdit widget
- *
- * Returns the amount entered in the GNCAmountEdit widget,
- * parsing the expression if necessary. The result of parsing
- * replaces the expression.
- */
 double
 gnc_amount_edit_get_damount (GNCAmountEdit *gae)
 {
-    g_return_val_if_fail(gae != NULL, 0.0);
-    g_return_val_if_fail(GNC_IS_AMOUNT_EDIT(gae), 0.0);
+    g_return_val_if_fail (gae != NULL, 0.0);
+    g_return_val_if_fail (GNC_IS_AMOUNT_EDIT(gae), 0.0);
 
     gnc_amount_edit_evaluate (gae);
 
     return gnc_numeric_to_double (gae->amount);
 }
 
-
-/**
- * gnc_amount_edit_set_amount:
- * @gae: The GNCAmountEdit widget
- * @amount: The amount to set
- *
- * Returns nothing.
- */
 void
 gnc_amount_edit_set_amount (GNCAmountEdit *gae, gnc_numeric amount)
 {
     const char * amount_string;
 
-    g_return_if_fail(gae != NULL);
-    g_return_if_fail(GNC_IS_AMOUNT_EDIT(gae));
-    g_return_if_fail(!gnc_numeric_check (amount));
+    g_return_if_fail (gae != NULL);
+    g_return_if_fail (GNC_IS_AMOUNT_EDIT(gae));
+    g_return_if_fail (!gnc_numeric_check (amount));
 
     /* Update the display. */
     amount_string = xaccPrintAmount (amount, gae->print_info);
@@ -382,21 +300,14 @@ gnc_amount_edit_set_amount (GNCAmountEdit *gae, gnc_numeric amount)
     gae->need_to_parse = FALSE;
 }
 
-/**
- * gnc_amount_edit_set_amount:
- * @gae: The GNCAmountEdit widget
- * @amount: The amount to set
- *
- * Returns nothing.
- */
 void
 gnc_amount_edit_set_damount (GNCAmountEdit *gae, double damount)
 {
     gnc_numeric amount;
     int fraction;
 
-    g_return_if_fail(gae != NULL);
-    g_return_if_fail(GNC_IS_AMOUNT_EDIT(gae));
+    g_return_if_fail (gae != NULL);
+    g_return_if_fail (GNC_IS_AMOUNT_EDIT(gae));
 
     if (gae->fraction > 0)
         fraction = gae->fraction;
@@ -408,73 +319,43 @@ gnc_amount_edit_set_damount (GNCAmountEdit *gae, double damount)
     gnc_amount_edit_set_amount (gae, amount);
 }
 
-/**
- * gnc_amount_edit_set_print_flags:
- * @gae: The GNCAmountEdit widget
- * @print_flags: The print flags to set
- *
- * Returns nothing.
- */
 void
 gnc_amount_edit_set_print_info (GNCAmountEdit *gae,
                                 GNCPrintAmountInfo print_info)
 {
-    g_return_if_fail(gae != NULL);
-    g_return_if_fail(GNC_IS_AMOUNT_EDIT(gae));
+    g_return_if_fail (gae != NULL);
+    g_return_if_fail (GNC_IS_AMOUNT_EDIT(gae));
 
     gae->print_info = print_info;
     gae->print_info.use_symbol = 0;
 }
 
-
-/**
- * gnc_amount_edit_set_fraction:
- * @gae: The GNCAmountEdit widget
- * @fraction: The fraction to set
- *
- * Returns nothing.
- */
 void
 gnc_amount_edit_set_fraction (GNCAmountEdit *gae, int fraction)
 {
-    g_return_if_fail(gae != NULL);
-    g_return_if_fail(GNC_IS_AMOUNT_EDIT(gae));
+    g_return_if_fail (gae != NULL);
+    g_return_if_fail (GNC_IS_AMOUNT_EDIT(gae));
 
     fraction = MAX (0, fraction);
 
     gae->fraction = fraction;
 }
 
-
-/**
- * gnc_amount_edit_gtk_entry:
- * @gae: The GNCAmountEdit widget
- *
- * Returns the gtk entry of the widget..
- */
 GtkWidget *
 gnc_amount_edit_gtk_entry (GNCAmountEdit *gae)
 {
-    g_return_val_if_fail(gae != NULL, NULL);
-    g_return_val_if_fail(GNC_IS_AMOUNT_EDIT(gae), NULL);
+    g_return_val_if_fail (gae != NULL, NULL);
+    g_return_val_if_fail (GNC_IS_AMOUNT_EDIT(gae), NULL);
 
-    return (GtkWidget *)gae;
+    return GTK_WIDGET(gae);
 }
 
-
-/**
- * gnc_amount_edit_set_evaluate_on_enter:
- * @gae: The GNCAmountEdit widget
- * @evaluate_on_enter: The flag value to set
- *
- * Returns nothing.
- */
 void
 gnc_amount_edit_set_evaluate_on_enter (GNCAmountEdit *gae,
                                        gboolean evaluate_on_enter)
 {
-    g_return_if_fail(gae != NULL);
-    g_return_if_fail(GNC_IS_AMOUNT_EDIT(gae));
+    g_return_if_fail (gae != NULL);
+    g_return_if_fail (GNC_IS_AMOUNT_EDIT(gae));
 
     gae->evaluate_on_enter = evaluate_on_enter;
 }
diff --git a/gnucash/gnome-utils/gnc-amount-edit.h b/gnucash/gnome-utils/gnc-amount-edit.h
index 45d88dd4a..00275b01e 100644
--- a/gnucash/gnome-utils/gnc-amount-edit.h
+++ b/gnucash/gnome-utils/gnc-amount-edit.h
@@ -1,27 +1,25 @@
-/*
- * gnc-amount-edit.h -- amount editor widget
- *
- * Copyright (C) 2000 Dave Peticolas <dave at krondo.com>
- * All rights reserved.
- *
- * GnuCash is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Library General Public License as
- * published by the Free Software Foundation; either version 2 of the
- * License, or (at your option) any later version.
- *
- * Gnucash is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, contact:
- *
- * Free Software Foundation           Voice:  +1-617-542-5942
- * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
- * Boston, MA  02110-1301,  USA       gnu at gnu.org
- *
- */
+/********************************************************************\
+ * gnc-amount-edit.h -- amount editor widget                        *
+ *                                                                  *
+ * Copyright (C) 2000 Dave Peticolas <dave at krondo.com>              *
+ *                                                                  *
+ * This program is free software; you can redistribute it and/or    *
+ * modify it under the terms of the GNU General Public License as   *
+ * published by the Free Software Foundation; either version 2 of   *
+ * the License, or (at your option) any later version.              *
+ *                                                                  *
+ * This program is distributed in the hope that it will be useful,  *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
+ * GNU General Public License for more details.                     *
+ *                                                                  *
+ * You should have received a copy of the GNU General Public License*
+ * along with this program; if not, contact:                        *
+ *                                                                  *
+ * Free Software Foundation           Voice:  +1-617-542-5942       *
+ * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
+ * Boston, MA  02110-1301,  USA       gnu at gnu.org                   *
+\********************************************************************/
 /*
   @NOTATION@
  */
@@ -34,10 +32,10 @@
 
 #include <gtk/gtk.h>
 
-#define GNC_TYPE_AMOUNT_EDIT          (gnc_amount_edit_get_type())
-#define GNC_AMOUNT_EDIT(obj)          G_TYPE_CHECK_INSTANCE_CAST (obj, GNC_TYPE_AMOUNT_EDIT, GNCAmountEdit)
-#define GNC_AMOUNT_EDIT_CLASS(klass)  G_TYPE_CHECK_CLASS_CAST (klass, GNC_TYPE_AMOUNT_EDIT, GNCAmountEditClass)
-#define GNC_IS_AMOUNT_EDIT(obj)       G_TYPE_CHECK_INSTANCE_TYPE (obj, GNC_TYPE_AMOUNT_EDIT)
+#define GNC_TYPE_AMOUNT_EDIT          (gnc_amount_edit_get_type ())
+#define GNC_AMOUNT_EDIT(obj)          G_TYPE_CHECK_INSTANCE_CAST(obj, GNC_TYPE_AMOUNT_EDIT, GNCAmountEdit)
+#define GNC_AMOUNT_EDIT_CLASS(klass)  G_TYPE_CHECK_CLASS_CAST(klass, GNC_TYPE_AMOUNT_EDIT, GNCAmountEditClass)
+#define GNC_IS_AMOUNT_EDIT(obj)       G_TYPE_CHECK_INSTANCE_TYPE(obj, GNC_TYPE_AMOUNT_EDIT)
 
 typedef struct
 {
@@ -63,29 +61,131 @@ typedef struct
     void (*amount_changed) (GNCAmountEdit *gae);
 } GNCAmountEditClass;
 
-GType     gnc_amount_edit_get_type        (void);
+/**
+ * gnc_amount_edit_get_type:
+ *
+ * Returns the GType for the GNCAmountEdit widget
+ */
+GType gnc_amount_edit_get_type (void);
 
-GtkWidget *gnc_amount_edit_new            (void);
+/**
+ * gnc_amount_edit_new:
+ *
+ * Creates a new GNCAmountEdit widget which can be used to provide
+ * an easy to use way for entering amounts, allowing the user to
+ * enter and evaluate expressions.
+ *
+ * Returns a GNCAmountEdit widget.
+ */
+GtkWidget *gnc_amount_edit_new (void);
 
-GtkWidget *gnc_amount_edit_gtk_entry      (GNCAmountEdit *gae);
+/**
+ * gnc_amount_edit_gtk_entry:
+ * @gae: The GNCAmountEdit widget
+ *
+ * Returns the gtk entry of the widget..
+ */
+GtkWidget *gnc_amount_edit_gtk_entry (GNCAmountEdit *gae);
 
-void      gnc_amount_edit_set_amount      (GNCAmountEdit *gae,
-        gnc_numeric amount);
-void      gnc_amount_edit_set_damount     (GNCAmountEdit *gae,
-        double amount);
+/**
+ * gnc_amount_edit_set_amount:
+ * @gae: The GNCAmountEdit widget
+ * @amount: The amount to set as gnc_numeric
+ *
+ * Returns nothing.
+ */
+void gnc_amount_edit_set_amount (GNCAmountEdit *gae,
+                                 gnc_numeric amount);
 
-gnc_numeric gnc_amount_edit_get_amount    (GNCAmountEdit *gae);
-double      gnc_amount_edit_get_damount   (GNCAmountEdit *gae);
+/**
+ * gnc_amount_edit_set_damount:
+ * @gae: The GNCAmountEdit widget
+ * @amount: The amount to set as a double
+ *
+ * Returns nothing.
+ */
+void gnc_amount_edit_set_damount (GNCAmountEdit *gae,
+                                  double amount);
 
-gint      gnc_amount_edit_expr_is_valid (GNCAmountEdit *gae, gnc_numeric *amount,
-        gboolean empty_ok);
-gboolean  gnc_amount_edit_evaluate        (GNCAmountEdit *gae);
+/**
+ * gnc_amount_edit_get_amount:
+ * @gae: The GNCAmountEdit widget
+ *
+ * Returns the amount entered in the GNCAmountEdit widget as
+ * a gnc_numeric, parsing the expression if necessary.
+ * The result of parsing replaces the expression.
+ */
+gnc_numeric gnc_amount_edit_get_amount (GNCAmountEdit *gae);
+
+/**
+ * gnc_amount_edit_get_damount:
+ * @gae: The GNCAmountEdit widget
+ *
+ * Returns the amount entered in the GNCAmountEdit widget as
+ * a double, parsing the expression if necessary.
+ * The result of parsing replaces the expression.
+ */
+double gnc_amount_edit_get_damount (GNCAmountEdit *gae);
+
+/**
+ * gnc_amount_edit_expr_is_valid
+ * @gae: The GNCAmountEdit widget
+ * @amount: parameter to hold the value of the parsed expression
+ * @empty_ok: if true, an empty field is skipped, otherwise an empty field
+ *            parses as 0
+ *
+ * If needed, parse the expression in the amount entry. If there's no
+ * parsing error, it returns the amount, otherwise it returns the
+ * position in the expression where the error occurred.
+ *
+ * Return *  0 if the parsing was successful (note that if !empty_ok,
+ *             an empty field will parse to 0)
+ *        * -1 if the field is empty and that's ok (empty_ok)
+ *        * error position if there was a parsing error
+ */
+gint gnc_amount_edit_expr_is_valid (GNCAmountEdit *gae,
+                                    gnc_numeric *amount,
+                                    gboolean empty_ok);
+
+/**
+ * gnc_amount_edit_evaluate
+ * @gae: The GNCAmountEdit widget
+ *
+ * If needed, parse the expression in the amount entry
+ * and replace the expression with the result of evaluation.
+ * If there is a parsing error, don't change the expression entry,
+ * but do put the cursor at the point of the error.
+ *
+ * Return TRUE if parsing was successful or there was no need to parse.
+ */
+gboolean gnc_amount_edit_evaluate (GNCAmountEdit *gae);
 
-void      gnc_amount_edit_set_print_info  (GNCAmountEdit *gae,
-        GNCPrintAmountInfo print_info);
+/**
+ * gnc_amount_edit_set_print_flags:
+ * @gae: The GNCAmountEdit widget
+ * @print_flags: The print flags to set
+ *
+ * Returns nothing.
+ */
+void gnc_amount_edit_set_print_info (GNCAmountEdit *gae,
+                                     GNCPrintAmountInfo print_info);
 
-void      gnc_amount_edit_set_fraction    (GNCAmountEdit *gae, int fraction);
+/**
+ * gnc_amount_edit_set_fraction:
+ * @gae: The GNCAmountEdit widget
+ * @fraction: The fraction to set
+ *
+ * Returns nothing.
+ */
+void gnc_amount_edit_set_fraction (GNCAmountEdit *gae, int fraction);
 
-void      gnc_amount_edit_set_evaluate_on_enter (GNCAmountEdit *gae,
-        gboolean evaluate_on_enter);
+/**
+ * gnc_amount_edit_set_evaluate_on_enter:
+ * @gae: The GNCAmountEdit widget
+ * @evaluate_on_enter: The flag value to set
+ *
+ * Returns nothing.
+ */
+void gnc_amount_edit_set_evaluate_on_enter (GNCAmountEdit *gae,
+                                            gboolean evaluate_on_enter);
 #endif

commit 32f605aec2e63140563908dfc6c65344899844fc
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Thu May 27 11:59:48 2021 +0100

    Extract common code used in pricecell and formulacell to basiccell

diff --git a/gnucash/register/register-core/basiccell.c b/gnucash/register/register-core/basiccell.c
index 651cc8aa6..850cf9239 100644
--- a/gnucash/register/register-core/basiccell.c
+++ b/gnucash/register/register-core/basiccell.c
@@ -36,8 +36,11 @@
 #include <config.h>
 
 #include <stdlib.h>
+#include <locale.h>
 #include <string.h>
 
+#include "gnc-locale-utils.h"
+
 #include "basiccell.h"
 #include "gnc-engine.h"
 
@@ -274,3 +277,57 @@ gnc_basic_cell_set_value_internal (BasicCell *cell, const char *value)
     cell->value = g_strdup (value);
     cell->value_chars = g_utf8_strlen(value, -1);
 }
+
+char *
+gnc_basic_cell_validate (BasicCell *cell, GNCPrintAmountInfo print_info,
+                         const char *change, const char *newval,
+                         const char *toks, gint *cursor_position)
+{
+    struct lconv *lc = gnc_localeconv ();
+    gunichar decimal_point;
+    gunichar thousands_sep;
+    const char *symbol = NULL;
+    char *tokens;
+
+    if (print_info.monetary)
+    {
+        const gnc_commodity *comm = print_info.commodity;
+
+        decimal_point = g_utf8_get_char (lc->mon_decimal_point);
+        thousands_sep = g_utf8_get_char (lc->mon_thousands_sep);
+
+        if (comm)
+            symbol = gnc_commodity_get_nice_symbol (comm);
+        else
+            symbol = gnc_commodity_get_nice_symbol (gnc_default_currency ());
+
+        tokens = g_strconcat (toks, symbol, NULL);
+    }
+    else
+    {
+        decimal_point = g_utf8_get_char (lc->decimal_point);
+        thousands_sep = g_utf8_get_char (lc->thousands_sep);
+
+        tokens = g_strdup (toks);
+    }
+
+    for (const char *c = change; c && *c; c = g_utf8_next_char (c))
+    {
+        gunichar uc = g_utf8_get_char (c);
+        if (!g_unichar_isdigit (uc) &&
+            !g_unichar_isspace (uc) &&
+            !g_unichar_isalpha (uc) &&
+            (decimal_point != uc) &&
+            (thousands_sep != uc) &&
+            (g_utf8_strchr (tokens, -1, uc) == NULL))
+        {
+            g_free (tokens);
+            return NULL;
+        }
+    }
+    g_free (tokens);
+
+    gnc_filter_text_set_cursor_position (newval, symbol, cursor_position);
+
+    return gnc_filter_text_for_currency_symbol (newval, symbol);
+}
diff --git a/gnucash/register/register-core/basiccell.h b/gnucash/register/register-core/basiccell.h
index eae942076..3ffa72af8 100644
--- a/gnucash/register/register-core/basiccell.h
+++ b/gnucash/register/register-core/basiccell.h
@@ -161,6 +161,7 @@
 #include <glib.h>
 #include <gtk/gtk.h>
 
+#include "gnc-ui-util.h"
 
 typedef struct basic_cell BasicCell;
 
@@ -277,5 +278,12 @@ void         gnc_basic_cell_set_conditionally_changed (BasicCell *cell,
 void         gnc_basic_cell_set_value_internal (BasicCell *bcell,
         const char *value);
 
+char * gnc_basic_cell_validate (BasicCell *bcell, 
+                                GNCPrintAmountInfo print_info,
+                                const char *change,
+                                const char *newval,
+                                const char *toks,
+                                gint *cursor_position);
+
 /** @} @} */
 #endif /* BASIC_CELL_H */
diff --git a/gnucash/register/register-core/formulacell.c b/gnucash/register/register-core/formulacell.c
index fe02a3df4..ff12e0dd2 100644
--- a/gnucash/register/register-core/formulacell.c
+++ b/gnucash/register/register-core/formulacell.c
@@ -24,7 +24,6 @@
 #include <glib/gi18n.h>
 
 #include "gnc-exp-parser.h"
-#include "gnc-locale-utils.h"
 #include "gnc-engine.h"
 #include "gnc-ui-util.h"
 #include "gnc-ui.h"
@@ -134,19 +133,11 @@ gnc_formula_cell_modify_verify( BasicCell *_cell,
                                 int newval_len,
                                 int *cursor_position,
                                 int *start_selection,
-                                int *end_selection )
+                                int *end_selection)
 {
-    FormulaCell *cell = (FormulaCell *)_cell;
-    struct lconv *lc = gnc_localeconv ();
+    FormulaCell *fc = (FormulaCell *)_cell;
     const char *toks = "+-*/=()_:";
-    gunichar decimal_point;
-    gunichar thousands_sep;
-    const char *c;
-    gunichar uc;
-
-    gchar *filtered_newval;
-    const gchar *symbol = NULL;
-    gchar *tokens;
+    char *validated_newval = NULL;
 
     g_debug("%s, %d, %s, %d, %d, %d, %d",
             change ? (gchar *)change : "(null)", change_len,
@@ -156,55 +147,21 @@ gnc_formula_cell_modify_verify( BasicCell *_cell,
     /* accept the newval string if user action was delete */
     if (change == NULL)
     {
-        gnc_basic_cell_set_value_internal( &cell->cell, newval );
+        gnc_basic_cell_set_value_internal (&fc->cell, newval);
         // Remove any selection.
         *end_selection = *start_selection = *cursor_position;
         return;
     }
 
-    if (cell->print_info.monetary)
-    {
-        const gnc_commodity *comm = cell->print_info.commodity;
-
-        decimal_point = g_utf8_get_char(lc->mon_decimal_point);
-        thousands_sep = g_utf8_get_char(lc->mon_thousands_sep);
+    validated_newval = gnc_basic_cell_validate (_cell, fc->print_info,
+                                                change, newval, toks,
+                                                cursor_position);
 
-        if (comm)
-            symbol = gnc_commodity_get_nice_symbol (comm);
-        else
-            symbol = gnc_commodity_get_nice_symbol (gnc_default_currency ());
-
-        tokens = g_strconcat (toks, symbol, NULL);
-    }
-    else
-    {
-        decimal_point = g_utf8_get_char(lc->decimal_point);
-        thousands_sep = g_utf8_get_char(lc->thousands_sep);
-
-        tokens = g_strdup (toks);
-    }
+    if (!validated_newval)
+        return;
 
-    c = change;
-    while (*c)
-    {
-        uc = g_utf8_get_char (c);
-        if (!g_unichar_isdigit (uc) &&
-            !g_unichar_isspace (uc) &&
-            !g_unichar_isalpha (uc) &&
-            (decimal_point != uc) &&
-            (thousands_sep != uc) &&
-            (g_utf8_strchr (tokens, -1, uc) == NULL))
-         {
-            g_free (tokens);
-            return;
-         }
-        c = g_utf8_next_char (c);
-    }
-    gnc_filter_text_set_cursor_position (newval, symbol, cursor_position);
-    filtered_newval = gnc_filter_text_for_currency_symbol (newval, symbol);
-    gnc_basic_cell_set_value_internal (&cell->cell, filtered_newval);
-    g_free (filtered_newval);
-    g_free (tokens);
+    gnc_basic_cell_set_value_internal (_cell, validated_newval);
+    g_free (validated_newval);
 }
 
 static
diff --git a/gnucash/register/register-core/pricecell.c b/gnucash/register/register-core/pricecell.c
index 73a060a87..a4de74d3d 100644
--- a/gnucash/register/register-core/pricecell.c
+++ b/gnucash/register/register-core/pricecell.c
@@ -36,11 +36,9 @@
 
 #include <glib.h>
 #include <glib/gi18n.h>
-#include <locale.h>
 #include <string.h>
 
 #include "gnc-exp-parser.h"
-#include "gnc-locale-utils.h"
 #include "gnc-engine.h"
 #include "gnc-ui-util.h"
 #include "gnc-ui.h"
@@ -79,55 +77,23 @@ gnc_price_cell_modify_verify (BasicCell *_cell,
                               int *end_selection)
 {
     PriceCell *cell = (PriceCell *) _cell;
-    struct lconv *lc = gnc_localeconv ();
     const char *toks = "+-*/=()_";
-    gunichar decimal_point;
-    gunichar thousands_sep;
-    gchar *filtered_newval;
-    const gchar *symbol = NULL;
-    gchar *tokens;
+    char *validated_newval = NULL;
 
-    if (cell->print_info.monetary)
-    {
-        const gnc_commodity *comm = cell->print_info.commodity;
-
-        decimal_point = g_utf8_get_char(lc->mon_decimal_point);
-        thousands_sep = g_utf8_get_char(lc->mon_thousands_sep);
-
-        if (comm)
-            symbol = gnc_commodity_get_nice_symbol (comm);
-        else
-            symbol = gnc_commodity_get_nice_symbol (gnc_default_currency ());
+    g_debug("%s, %d, %s, %d, %d, %d, %d",
+            change ? (gchar *)change : "(null)", change_len,
+            newval ? (gchar *)newval : "(null)", newval_len,
+            *cursor_position, *start_selection, *end_selection);
 
-        tokens = g_strconcat (toks, symbol, NULL);
-    }
-    else
-    {
-        decimal_point = g_utf8_get_char(lc->decimal_point);
-        thousands_sep = g_utf8_get_char(lc->thousands_sep);
+    validated_newval = gnc_basic_cell_validate (_cell, cell->print_info,
+                                                change, newval, toks,
+                                                cursor_position);
 
-        tokens = g_strdup (toks);
-    }
+    if (!validated_newval)
+        return;
 
-    for (const char *c = change; c && *c; c = g_utf8_next_char (c))
-    {
-        gunichar uc = g_utf8_get_char (c);
-        if (!g_unichar_isdigit (uc) &&
-            !g_unichar_isspace (uc) &&
-            !g_unichar_isalpha (uc) &&
-            (decimal_point != uc) &&
-            (thousands_sep != uc) &&
-            (g_utf8_strchr (tokens, -1, uc) == NULL))
-        {
-            g_free (tokens);
-            return;
-        }
-    }
-    gnc_filter_text_set_cursor_position (newval, symbol, cursor_position);
-    filtered_newval = gnc_filter_text_for_currency_symbol (newval, symbol);
-    gnc_basic_cell_set_value_internal (_cell, filtered_newval);
-    g_free (filtered_newval);
-    g_free (tokens);
+    gnc_basic_cell_set_value_internal (_cell, validated_newval);
+    g_free (validated_newval);
 
     *end_selection = *start_selection = *cursor_position;
     cell->need_to_parse = TRUE;

commit 6facaa0683ac7f7676f17d2e42f7f20a1a5b5130
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Thu May 27 11:36:25 2021 +0100

    Change how the print_info is obtained for the register
    
    Currently the register sets up some 'print_info' values using the
    default account but if the register is a stock register they should be
    obtained from a parent account with a currency.

diff --git a/gnucash/register/ledger-core/split-register-load.c b/gnucash/register/ledger-core/split-register-load.c
index 2924db6cf..13ae25d2c 100644
--- a/gnucash/register/ledger-core/split-register-load.c
+++ b/gnucash/register/ledger-core/split-register-load.c
@@ -368,6 +368,7 @@ gnc_split_register_load (SplitRegister* reg, GList* slist,
     Split* split;
     Table* table;
     GList* node;
+    gnc_commodity *account_comm = NULL;
 
     gboolean start_primary_color = TRUE;
     gboolean found_pending = FALSE;
@@ -407,14 +408,24 @@ gnc_split_register_load (SplitRegister* reg, GList* slist,
     pending_trans = xaccTransLookup (&info->pending_trans_guid,
                                      gnc_get_current_book());
 
+    if (default_account)
+        account_comm = gnc_account_get_currency_or_parent (default_account);
+
+    if (!account_comm)
+        account_comm = gnc_default_currency ();
+
     /* Bug 742089: Set the debit and credit cells' print_info to the account */
     gnc_price_cell_set_print_info
     ((PriceCell*) gnc_table_layout_get_cell (table->layout, DEBT_CELL),
-     gnc_account_print_info (default_account, FALSE));
+     gnc_commodity_print_info (account_comm, FALSE));
 
     gnc_price_cell_set_print_info
     ((PriceCell*) gnc_table_layout_get_cell (table->layout, CRED_CELL),
-     gnc_account_print_info (default_account, FALSE));
+     gnc_commodity_print_info (account_comm, FALSE));
+
+    gnc_price_cell_set_print_info
+    ((PriceCell*) gnc_table_layout_get_cell (reg->table->layout, PRIC_CELL),
+     gnc_commodity_print_info (account_comm, FALSE));
 
     gnc_doclink_cell_set_use_glyphs
     ((Doclinkcell *) gnc_table_layout_get_cell (table->layout, DOCLINK_CELL));

commit 748dbf54e0fb4ab152bedf2df8a35256b56366fa
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Thu May 27 11:35:45 2021 +0100

    Change monetary value for gnc_default_share_print_info
    
    Share values are not currencies so set the monetary value to 0 for the
    GNCPrintAmountInfo

diff --git a/libgnucash/app-utils/gnc-ui-util.c b/libgnucash/app-utils/gnc-ui-util.c
index 2fd26fd6c..f5ed8bbf6 100644
--- a/libgnucash/app-utils/gnc-ui-util.c
+++ b/libgnucash/app-utils/gnc-ui-util.c
@@ -1414,6 +1414,7 @@ gnc_default_share_print_info (void)
     if (!got_it)
     {
         info = gnc_default_print_info_helper (5);
+        info.monetary = 0;
         got_it = TRUE;
     }
 

commit 435bb1c76bab4c94be1c530bf5fa3c8218559c33
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Thu May 27 11:34:59 2021 +0100

    Parse the register monetary cells for a currency symbol
    
    Add the ability to strip the currency symbol from a registry monetary
    cell if it is pasted with one so it can be validated.

diff --git a/gnucash/register/register-core/formulacell.c b/gnucash/register/register-core/formulacell.c
index f5fbba6bd..fe02a3df4 100644
--- a/gnucash/register/register-core/formulacell.c
+++ b/gnucash/register/register-core/formulacell.c
@@ -115,8 +115,10 @@ gnc_formula_cell_leave(BasicCell *_cell)
                 && strlen(str) != 0
                 && !gnc_exp_parser_parse(str, &amount, &error_location))
         {
-            gnc_warning_dialog(NULL, _("An error occurred while processing %s."),
-                               str);//, (error_location - str));
+            gint error_position = error_location - str;
+            gnc_warning_dialog (gnc_ui_get_main_window (NULL),
+                                _("An error occurred while processing '%s' at position %d"),
+                                str, error_position);
         }
     }
 
@@ -142,6 +144,10 @@ gnc_formula_cell_modify_verify( BasicCell *_cell,
     const char *c;
     gunichar uc;
 
+    gchar *filtered_newval;
+    const gchar *symbol = NULL;
+    gchar *tokens;
+
     g_debug("%s, %d, %s, %d, %d, %d, %d",
             change ? (gchar *)change : "(null)", change_len,
             newval ? (gchar *)newval : "(null)", newval_len,
@@ -157,30 +163,48 @@ gnc_formula_cell_modify_verify( BasicCell *_cell,
     }
 
     if (cell->print_info.monetary)
-        decimal_point = g_utf8_get_char(lc->mon_decimal_point);
-    else
-        decimal_point = g_utf8_get_char(lc->decimal_point);
+    {
+        const gnc_commodity *comm = cell->print_info.commodity;
 
-    if (cell->print_info.monetary)
+        decimal_point = g_utf8_get_char(lc->mon_decimal_point);
         thousands_sep = g_utf8_get_char(lc->mon_thousands_sep);
+
+        if (comm)
+            symbol = gnc_commodity_get_nice_symbol (comm);
+        else
+            symbol = gnc_commodity_get_nice_symbol (gnc_default_currency ());
+
+        tokens = g_strconcat (toks, symbol, NULL);
+    }
     else
+    {
+        decimal_point = g_utf8_get_char(lc->decimal_point);
         thousands_sep = g_utf8_get_char(lc->thousands_sep);
 
+        tokens = g_strdup (toks);
+    }
+
     c = change;
     while (*c)
     {
         uc = g_utf8_get_char (c);
         if (!g_unichar_isdigit (uc) &&
-                !g_unichar_isspace (uc) &&
-                !g_unichar_isalpha (uc) &&
-                (decimal_point != uc) &&
-                (thousands_sep != uc) &&
-                (g_utf8_strchr (toks, -1, uc) == NULL))
+            !g_unichar_isspace (uc) &&
+            !g_unichar_isalpha (uc) &&
+            (decimal_point != uc) &&
+            (thousands_sep != uc) &&
+            (g_utf8_strchr (tokens, -1, uc) == NULL))
+         {
+            g_free (tokens);
             return;
+         }
         c = g_utf8_next_char (c);
     }
-
-    gnc_basic_cell_set_value_internal( &cell->cell, newval );
+    gnc_filter_text_set_cursor_position (newval, symbol, cursor_position);
+    filtered_newval = gnc_filter_text_for_currency_symbol (newval, symbol);
+    gnc_basic_cell_set_value_internal (&cell->cell, filtered_newval);
+    g_free (filtered_newval);
+    g_free (tokens);
 }
 
 static
diff --git a/gnucash/register/register-core/pricecell.c b/gnucash/register/register-core/pricecell.c
index f6491d57b..73a060a87 100644
--- a/gnucash/register/register-core/pricecell.c
+++ b/gnucash/register/register-core/pricecell.c
@@ -83,18 +83,32 @@ gnc_price_cell_modify_verify (BasicCell *_cell,
     const char *toks = "+-*/=()_";
     gunichar decimal_point;
     gunichar thousands_sep;
-    char *new_newval = g_strdup (newval);
+    gchar *filtered_newval;
+    const gchar *symbol = NULL;
+    gchar *tokens;
 
     if (cell->print_info.monetary)
-        decimal_point = g_utf8_get_char(lc->mon_decimal_point);
-    else
-        decimal_point = g_utf8_get_char(lc->decimal_point);
+    {
+        const gnc_commodity *comm = cell->print_info.commodity;
 
-    if (cell->print_info.monetary)
+        decimal_point = g_utf8_get_char(lc->mon_decimal_point);
         thousands_sep = g_utf8_get_char(lc->mon_thousands_sep);
+
+        if (comm)
+            symbol = gnc_commodity_get_nice_symbol (comm);
+        else
+            symbol = gnc_commodity_get_nice_symbol (gnc_default_currency ());
+
+        tokens = g_strconcat (toks, symbol, NULL);
+    }
     else
+    {
+        decimal_point = g_utf8_get_char(lc->decimal_point);
         thousands_sep = g_utf8_get_char(lc->thousands_sep);
 
+        tokens = g_strdup (toks);
+    }
+
     for (const char *c = change; c && *c; c = g_utf8_next_char (c))
     {
         gunichar uc = g_utf8_get_char (c);
@@ -103,12 +117,18 @@ gnc_price_cell_modify_verify (BasicCell *_cell,
             !g_unichar_isalpha (uc) &&
             (decimal_point != uc) &&
             (thousands_sep != uc) &&
-            (g_utf8_strchr (toks, -1, uc) == NULL))
+            (g_utf8_strchr (tokens, -1, uc) == NULL))
+        {
+            g_free (tokens);
             return;
+        }
     }
+    gnc_filter_text_set_cursor_position (newval, symbol, cursor_position);
+    filtered_newval = gnc_filter_text_for_currency_symbol (newval, symbol);
+    gnc_basic_cell_set_value_internal (_cell, filtered_newval);
+    g_free (filtered_newval);
+    g_free (tokens);
 
-    gnc_basic_cell_set_value_internal (_cell, new_newval);
-    g_free (new_newval);
     *end_selection = *start_selection = *cursor_position;
     cell->need_to_parse = TRUE;
 }
@@ -169,8 +189,9 @@ gnc_price_cell_leave (BasicCell *_cell)
     error_position = gnc_price_cell_parse (cell, TRUE);
     if (error_position != -1)
     {
-        gnc_warning_dialog(NULL, _("An error occurred while processing %s."),
-                           cell->cell.value);
+        gnc_warning_dialog (gnc_ui_get_main_window (NULL),
+                            _("An error occurred while processing '%s' at position %d"),
+                            cell->cell.value, error_position);
     }
 
 }
diff --git a/libgnucash/app-utils/gnc-ui-util.c b/libgnucash/app-utils/gnc-ui-util.c
index 9fc8ae0b1..2fd26fd6c 100644
--- a/libgnucash/app-utils/gnc-ui-util.c
+++ b/libgnucash/app-utils/gnc-ui-util.c
@@ -2727,3 +2727,59 @@ gnc_filter_text_for_control_chars (const gchar *text)
     g_free (normal_text);
     return g_string_free (filtered, FALSE);
 }
+
+void
+gnc_filter_text_set_cursor_position (const gchar *incoming_text,
+                                     const gchar *symbol,
+                                     gint *cursor_position)
+{
+    gint text_len;
+    gint num = 0;
+
+    if (*cursor_position == 0)
+        return;
+
+    if (!incoming_text || !symbol)
+        return;
+
+    if (g_strrstr (incoming_text, symbol) == NULL)
+        return;
+
+    text_len = g_utf8_strlen (incoming_text, -1);
+
+    for (gint x = 0; x < text_len; x++)
+    {
+        gchar *temp = g_utf8_offset_to_pointer (incoming_text, x);
+
+        if (g_str_has_prefix (temp, symbol))
+            num++;
+
+        if (g_strrstr (temp, symbol) == NULL)
+            break;
+    }
+    *cursor_position = *cursor_position - (num * g_utf8_strlen (symbol, -1));
+}
+
+gchar *
+gnc_filter_text_for_currency_symbol (const gchar *incoming_text,
+                                     const gchar *symbol)
+{
+    gchar *ret_text = NULL;
+    gchar **split;
+
+    if (!incoming_text)
+        return NULL;
+
+    if (!symbol)
+       return g_strdup (incoming_text);
+
+    if (g_strrstr (incoming_text, symbol) == NULL)
+        return g_strdup (incoming_text);
+
+    split = g_strsplit (incoming_text, symbol, -1);
+
+    ret_text = g_strjoinv (NULL, split);
+
+    g_strfreev (split);
+    return ret_text;
+}
diff --git a/libgnucash/app-utils/gnc-ui-util.h b/libgnucash/app-utils/gnc-ui-util.h
index c400c0637..ca72785a1 100644
--- a/libgnucash/app-utils/gnc-ui-util.h
+++ b/libgnucash/app-utils/gnc-ui-util.h
@@ -406,6 +406,33 @@ void gnc_ui_util_remove_registered_prefs (void);
 */
 gchar * gnc_filter_text_for_control_chars (const gchar *incoming_text);
 
+/** Updates cursor_position after removal of currency symbols
+ *
+ * @param incoming_text The text to filter
+ *
+ * @param symbol to remove
+ *
+ * @param cursor_position the posistion of cursor in the incoming text
+ *
+ * @return nothing
+*/
+void gnc_filter_text_set_cursor_position (const gchar *incoming_text,
+                                          const gchar *symbol,
+                                          gint *cursor_position);
+
+/** Returns the incoming text removed of a currency symbol
+ *
+ * @param incoming_text The text to filter
+ *
+ * @param symbol to remove
+ *
+ * @param cursor_position the posistion of cursor in the incoming text
+ *
+ * @return The incoming text with symbol removed to be freed by the caller
+*/
+gchar * gnc_filter_text_for_currency_symbol (const gchar *incoming_text,
+                                             const gchar *symbol);
+
 #endif
 /** @} */
 /** @} */

commit 5779e72db49b12f3f9c9965e2661ebe89bde6a05
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Thu May 27 11:04:28 2021 +0100

    Realign source file gnucash-item-edit.c for white space

diff --git a/gnucash/register/register-gnome/gnucash-item-edit.c b/gnucash/register/register-gnome/gnucash-item-edit.c
index 9fb4f35c0..3b1d10a02 100644
--- a/gnucash/register/register-gnome/gnucash-item-edit.c
+++ b/gnucash/register/register-gnome/gnucash-item-edit.c
@@ -77,7 +77,7 @@ gnc_item_edit_tb_get_property (GObject *object,
                                GValue *value,
                                GParamSpec *pspec)
 {
-    GncItemEditTb *item_edit_tb = GNC_ITEM_EDIT_TB (object);
+    GncItemEditTb *item_edit_tb = GNC_ITEM_EDIT_TB(object);
 
     switch (param_id)
     {
@@ -85,7 +85,7 @@ gnc_item_edit_tb_get_property (GObject *object,
         g_value_take_object (value, item_edit_tb->sheet);
         break;
     default:
-        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, param_id, pspec);
         break;
     }
 }
@@ -96,30 +96,30 @@ gnc_item_edit_tb_set_property (GObject *object,
                                const GValue *value,
                                GParamSpec *pspec)
 {
-    GncItemEditTb *item_edit_tb = GNC_ITEM_EDIT_TB (object);
+    GncItemEditTb *item_edit_tb = GNC_ITEM_EDIT_TB(object);
 
     switch (param_id)
     {
     case PROP_SHEET:
-        item_edit_tb->sheet = GNUCASH_SHEET (g_value_get_object (value));
+        item_edit_tb->sheet = GNUCASH_SHEET(g_value_get_object (value));
         break;
     default:
-        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, param_id, pspec);
         break;
     }
 }
 
 static void
 gnc_item_edit_tb_get_preferred_width (GtkWidget *widget,
-                                   gint *minimal_width,
-                                   gint *natural_width)
+                                      gint *minimal_width,
+                                      gint *natural_width)
 {
-    GncItemEditTb *tb = GNC_ITEM_EDIT_TB (widget);
+    GncItemEditTb *tb = GNC_ITEM_EDIT_TB(widget);
     GncItemEdit *item_edit = GNC_ITEM_EDIT(tb->sheet->item_editor);
     GtkStyleContext *context = gtk_widget_get_style_context (GTK_WIDGET(tb));
     GtkBorder border;
     gint x, y, w, h = 2, width = 0;
-    gnc_item_edit_get_pixel_coords (GNC_ITEM_EDIT (item_edit), &x, &y, &w, &h);
+    gnc_item_edit_get_pixel_coords (GNC_ITEM_EDIT(item_edit), &x, &y, &w, &h);
     width = ((h - 2)*2)/3;
 
     gtk_style_context_get_border (context, GTK_STATE_FLAG_NORMAL, &border);
@@ -133,13 +133,13 @@ gnc_item_edit_tb_get_preferred_width (GtkWidget *widget,
 
 static void
 gnc_item_edit_tb_get_preferred_height (GtkWidget *widget,
-                                    gint *minimal_width,
-                                    gint *natural_width)
+                                       gint *minimal_width,
+                                       gint *natural_width)
 {
-    GncItemEditTb *tb = GNC_ITEM_EDIT_TB (widget);
+    GncItemEditTb *tb = GNC_ITEM_EDIT_TB(widget);
     GncItemEdit *item_edit = GNC_ITEM_EDIT(tb->sheet->item_editor);
     gint x, y, w, h = 2;
-    gnc_item_edit_get_pixel_coords (GNC_ITEM_EDIT (item_edit), &x, &y, &w, &h);
+    gnc_item_edit_get_pixel_coords (GNC_ITEM_EDIT(item_edit), &x, &y, &w, &h);
     *minimal_width = *natural_width = (h - 2);
 }
 
@@ -153,8 +153,8 @@ gnc_item_edit_tb_class_init (GncItemEditTbClass *gnc_item_edit_tb_class)
 
     gnc_item_edit_tb_parent_class = g_type_class_peek_parent (gnc_item_edit_tb_class);
 
-    object_class = G_OBJECT_CLASS (gnc_item_edit_tb_class);
-    widget_class = GTK_WIDGET_CLASS (gnc_item_edit_tb_class);
+    object_class = G_OBJECT_CLASS(gnc_item_edit_tb_class);
+    widget_class = GTK_WIDGET_CLASS(gnc_item_edit_tb_class);
 
     object_class->get_property = gnc_item_edit_tb_get_property;
     object_class->set_property = gnc_item_edit_tb_set_property;
@@ -184,18 +184,18 @@ gnc_item_edit_tb_get_type (void)
             sizeof (GncItemEditTbClass),
             NULL,
             NULL,
-            (GClassInitFunc) gnc_item_edit_tb_class_init,
+            (GClassInitFunc)gnc_item_edit_tb_class_init,
             NULL,
             NULL,
             sizeof (GncItemEditTb),
             0, /* n_preallocs */
-            (GInstanceInitFunc) gnc_item_edit_tb_init,
+            (GInstanceInitFunc)gnc_item_edit_tb_init,
             NULL,
         };
         gnc_item_edit_tb_type =
-            g_type_register_static(GTK_TYPE_TOGGLE_BUTTON,
-                                   "GncItemEditTb",
-                                   &gnc_item_edit_tb_info, 0);
+            g_type_register_static (GTK_TYPE_TOGGLE_BUTTON,
+                                    "GncItemEditTb",
+                                    &gnc_item_edit_tb_info, 0);
     }
     return gnc_item_edit_tb_type;
 }
@@ -204,10 +204,9 @@ GtkWidget *
 gnc_item_edit_tb_new (GnucashSheet *sheet)
 {
     GtkStyleContext *context;
-    GncItemEditTb *item_edit_tb =
-            g_object_new (GNC_TYPE_ITEM_EDIT_TB,
-                          "sheet", sheet,
-                           NULL);
+    GncItemEditTb *item_edit_tb = g_object_new (GNC_TYPE_ITEM_EDIT_TB,
+                                                "sheet", sheet,
+                                                NULL);
 
     context = gtk_widget_get_style_context (GTK_WIDGET(item_edit_tb));
     gtk_style_context_add_class (context, GTK_STYLE_CLASS_BUTTON);
@@ -237,11 +236,10 @@ gnc_item_edit_get_pixel_coords (GncItemEdit *item_edit,
     xd = block->origin_x;
     yd = block->origin_y;
 
-    gnucash_sheet_style_get_cell_pixel_rel_coords
-    (item_edit->style,
-     item_edit->virt_loc.phys_row_offset,
-     item_edit->virt_loc.phys_col_offset,
-     x, y, w, h);
+    gnucash_sheet_style_get_cell_pixel_rel_coords (item_edit->style,
+                                                   item_edit->virt_loc.phys_row_offset,
+                                                   item_edit->virt_loc.phys_col_offset,
+                                                   x, y, w, h);
 
     // alter cell size of first column
     if (item_edit->virt_loc.phys_col_offset == 0)
@@ -282,7 +280,7 @@ gnc_item_edit_focus_in (GncItemEdit *item_edit)
     g_return_if_fail (GNC_IS_ITEM_EDIT(item_edit));
 
     ev.type = GDK_FOCUS_CHANGE;
-    ev.window = gtk_widget_get_window (GTK_WIDGET (item_edit->sheet));
+    ev.window = gtk_widget_get_window (GTK_WIDGET(item_edit->sheet));
     ev.in = TRUE;
     gtk_widget_event (item_edit->editor, (GdkEvent*) &ev);
 }
@@ -296,7 +294,7 @@ gnc_item_edit_focus_out (GncItemEdit *item_edit)
     g_return_if_fail (GNC_IS_ITEM_EDIT(item_edit));
 
     ev.type = GDK_FOCUS_CHANGE;
-    ev.window = gtk_widget_get_window (GTK_WIDGET (item_edit->sheet));
+    ev.window = gtk_widget_get_window (GTK_WIDGET(item_edit->sheet));
     ev.in = FALSE;
     gtk_widget_event (item_edit->editor, (GdkEvent*) &ev);
 }
@@ -336,7 +334,7 @@ gnc_item_edit_init (GncItemEdit *item_edit)
     item_edit->style = NULL;
     item_edit->button_width = MIN_BUTT_WIDTH;
 
-    gnc_virtual_location_init(&item_edit->virt_loc);
+    gnc_virtual_location_init (&item_edit->virt_loc);
 }
 
 void
@@ -351,9 +349,8 @@ gnc_item_edit_configure (GncItemEdit *item_edit)
     item_edit->virt_loc.vcell_loc.virt_row = cursor->row;
     item_edit->virt_loc.vcell_loc.virt_col = cursor->col;
 
-    item_edit->style =
-        gnucash_sheet_get_style (sheet,
-                                 item_edit->virt_loc.vcell_loc);
+    item_edit->style = gnucash_sheet_get_style (sheet,
+                           item_edit->virt_loc.vcell_loc);
 
     item_edit->virt_loc.phys_row_offset = cursor->cell.row;
     item_edit->virt_loc.phys_col_offset = cursor->cell.col;
@@ -373,27 +370,27 @@ gnc_item_edit_configure (GncItemEdit *item_edit)
             xalign = 0.5;
             break;
     }
-    gtk_entry_set_alignment(GTK_ENTRY(item_edit->editor), xalign);
+    gtk_entry_set_alignment (GTK_ENTRY(item_edit->editor), xalign);
 
     if (!gnc_table_is_popup (sheet->table, item_edit->virt_loc))
         gnc_item_edit_set_popup (item_edit, NULL, NULL, NULL,
                                  NULL, NULL, NULL, NULL);
 
     g_idle_add_full (G_PRIORITY_HIGH_IDLE,
-                    (GSourceFunc) gnc_item_edit_update, item_edit, NULL);
+                    (GSourceFunc)gnc_item_edit_update, item_edit, NULL);
 }
 
 
 void
 gnc_item_edit_cut_clipboard (GncItemEdit *item_edit)
 {
-    gtk_editable_cut_clipboard(GTK_EDITABLE(item_edit->editor));
+    gtk_editable_cut_clipboard (GTK_EDITABLE(item_edit->editor));
 }
 
 void
 gnc_item_edit_copy_clipboard (GncItemEdit *item_edit)
 {
-    gtk_editable_copy_clipboard(GTK_EDITABLE(item_edit->editor));
+    gtk_editable_copy_clipboard (GTK_EDITABLE(item_edit->editor));
 }
 
 void
@@ -442,7 +439,7 @@ gnc_item_edit_paste_clipboard (GncItemEdit *item_edit)
 static gboolean
 key_press_popup_cb (GtkWidget *widget, GdkEventKey *event, gpointer data)
 {
-    GncItemEdit *item_edit = GNC_ITEM_EDIT (data);
+    GncItemEdit *item_edit = GNC_ITEM_EDIT(data);
 
     g_signal_stop_emission_by_name (widget, "key_press_event");
 
@@ -455,7 +452,7 @@ key_press_popup_cb (GtkWidget *widget, GdkEventKey *event, gpointer data)
 static void
 gnc_item_edit_popup_toggled (GtkToggleButton *button, gpointer data)
 {
-    GncItemEdit *item_edit = GNC_ITEM_EDIT (data);
+    GncItemEdit *item_edit = GNC_ITEM_EDIT(data);
     gboolean show_popup;
 
     show_popup = gtk_toggle_button_get_active (button);
@@ -470,14 +467,14 @@ gnc_item_edit_popup_toggled (GtkToggleButton *button, gpointer data)
         if (!gnc_table_confirm_change (table, virt_loc))
         {
             g_signal_handlers_block_matched
-            (button, G_SIGNAL_MATCH_DATA,
-             0, 0, NULL, NULL, data);
+                 (button, G_SIGNAL_MATCH_DATA,
+                 0, 0, NULL, NULL, data);
 
             gtk_toggle_button_set_active (button, FALSE);
 
             g_signal_handlers_unblock_matched
-            (button, G_SIGNAL_MATCH_DATA,
-             0, 0, NULL, NULL, data);
+                (button, G_SIGNAL_MATCH_DATA,
+                 0, 0, NULL, NULL, data);
 
             return;
         }
@@ -493,14 +490,14 @@ gnc_item_edit_popup_toggled (GtkToggleButton *button, gpointer data)
 
 
 static void
-block_toggle_signals(GncItemEdit *item_edit)
+block_toggle_signals (GncItemEdit *item_edit)
 {
     GObject *obj;
 
     if (!item_edit->popup_toggle.signals_connected)
         return;
 
-    obj = G_OBJECT (item_edit->popup_toggle.tbutton);
+    obj = G_OBJECT(item_edit->popup_toggle.tbutton);
 
     g_signal_handlers_block_matched (obj, G_SIGNAL_MATCH_DATA,
                                      0, 0, NULL, NULL, item_edit);
@@ -508,14 +505,14 @@ block_toggle_signals(GncItemEdit *item_edit)
 
 
 static void
-unblock_toggle_signals(GncItemEdit *item_edit)
+unblock_toggle_signals (GncItemEdit *item_edit)
 {
     GObject *obj;
 
     if (!item_edit->popup_toggle.signals_connected)
         return;
 
-    obj = G_OBJECT (item_edit->popup_toggle.tbutton);
+    obj = G_OBJECT(item_edit->popup_toggle.tbutton);
 
     g_signal_handlers_unblock_matched (obj, G_SIGNAL_MATCH_DATA,
                                        0, 0, NULL, NULL, item_edit);
@@ -526,7 +523,7 @@ static gboolean
 draw_background_cb (GtkWidget *widget, cairo_t *cr, gpointer user_data)
 {
     GtkStyleContext *stylectxt = gtk_widget_get_style_context (widget);
-    GncItemEdit *item_edit = GNC_ITEM_EDIT (user_data);
+    GncItemEdit *item_edit = GNC_ITEM_EDIT(user_data);
     gint width = gtk_widget_get_allocated_width (widget);
     gint height = gtk_widget_get_allocated_height (widget);
     guint32 color_type;
@@ -553,7 +550,7 @@ preedit_changed_cb (GtkEntry* entry, gchar *preedit, GncItemEdit* item_edit)
 {
     int pos, bound;
     item_edit->preedit_length = g_utf8_strlen (preedit, -1); // Note codepoints not bytes
-    DEBUG ("%s %lu", preedit, item_edit->preedit_length);
+    DEBUG("%s %lu", preedit, item_edit->preedit_length);
 }
 
 
@@ -566,7 +563,7 @@ draw_text_cursor_cb (GtkWidget *widget, cairo_t *cr, gpointer user_data)
     GtkStateFlags flags = gtk_widget_get_state_flags (GTK_WIDGET(widget));
     gint height = gtk_widget_get_allocated_height (widget);
     PangoLayout *layout = gtk_entry_get_layout (GTK_ENTRY(widget));
-    const char *pango_text = pango_layout_get_text(layout);
+    const char *pango_text = pango_layout_get_text (layout);
     GdkRGBA *fg_color;
     GdkRGBA color;
     gint x_offset;
@@ -590,7 +587,7 @@ draw_text_cursor_cb (GtkWidget *widget, cairo_t *cr, gpointer user_data)
         gint cursor_byte_pos = cursor_pos < text_len ?
             g_utf8_offset_to_pointer (pango_text, cursor_pos) - pango_text :
             strlen (pango_text);
-        DEBUG ("Cursor: %d, byte offset %d, text byte len %zu", cursor_pos,
+        DEBUG("Cursor: %d, byte offset %d, text byte len %zu", cursor_pos,
                cursor_byte_pos, strlen (pango_text));
         pango_layout_get_cursor_pos (layout, cursor_byte_pos,
                                      &strong_pos, NULL);
@@ -598,7 +595,7 @@ draw_text_cursor_cb (GtkWidget *widget, cairo_t *cr, gpointer user_data)
     }
     else
     {
-        DEBUG ("No text, cursor at %d.", x_offset);
+        DEBUG("No text, cursor at %d.", x_offset);
         cursor_x = x_offset;
     }
     // Now draw a vertical line
@@ -622,7 +619,7 @@ draw_text_cursor_cb (GtkWidget *widget, cairo_t *cr, gpointer user_data)
 static gboolean
 draw_arrow_cb (GtkWidget *widget, cairo_t *cr, gpointer data)
 {
-    GncItemEdit *item_edit = GNC_ITEM_EDIT (data);
+    GncItemEdit *item_edit = GNC_ITEM_EDIT(data);
     GtkStyleContext *context = gtk_widget_get_style_context (widget);
     gint width = gtk_widget_get_allocated_width (widget);
     gint height = gtk_widget_get_allocated_height (widget);
@@ -651,7 +648,7 @@ connect_popup_toggle_signals (GncItemEdit *item_edit)
 {
     GObject *object;
 
-    g_return_if_fail(GNC_IS_ITEM_EDIT(item_edit));
+    g_return_if_fail (GNC_IS_ITEM_EDIT(item_edit));
 
     if (item_edit->popup_toggle.signals_connected)
         return;
@@ -667,7 +664,7 @@ connect_popup_toggle_signals (GncItemEdit *item_edit)
                       item_edit);
 
     g_signal_connect_after (object, "draw",
-                            G_CALLBACK (draw_arrow_cb),
+                            G_CALLBACK(draw_arrow_cb),
                             item_edit);
 
     item_edit->popup_toggle.signals_connected = TRUE;
@@ -677,14 +674,13 @@ connect_popup_toggle_signals (GncItemEdit *item_edit)
 static void
 disconnect_popup_toggle_signals (GncItemEdit *item_edit)
 {
-    g_return_if_fail(GNC_IS_ITEM_EDIT(item_edit));
+    g_return_if_fail (GNC_IS_ITEM_EDIT(item_edit));
 
     if (!item_edit->popup_toggle.signals_connected)
         return;
 
-    g_signal_handlers_disconnect_matched
-    (item_edit->popup_toggle.tbutton, G_SIGNAL_MATCH_DATA,
-     0, 0, NULL, NULL, item_edit);
+    g_signal_handlers_disconnect_matched (item_edit->popup_toggle.tbutton,
+        G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, item_edit);
 
     item_edit->popup_toggle.signals_connected = FALSE;
 }
@@ -701,7 +697,7 @@ gnc_item_edit_get_property (GObject *object,
                             GValue *value,
                             GParamSpec *pspec)
 {
-    GncItemEdit *item_edit = GNC_ITEM_EDIT (object);
+    GncItemEdit *item_edit = GNC_ITEM_EDIT(object);
 
     switch (param_id)
     {
@@ -709,7 +705,7 @@ gnc_item_edit_get_property (GObject *object,
         g_value_take_object (value, item_edit->sheet);
         break;
     default:
-        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, param_id, pspec);
         break;
     }
 }
@@ -720,14 +716,14 @@ gnc_item_edit_set_property (GObject *object,
                             const GValue *value,
                             GParamSpec *pspec)
 {
-    GncItemEdit *item_edit = GNC_ITEM_EDIT (object);
+    GncItemEdit *item_edit = GNC_ITEM_EDIT(object);
     switch (param_id)
     {
     case PROP_SHEET:
-        item_edit->sheet = GNUCASH_SHEET (g_value_get_object (value));
+        item_edit->sheet = GNUCASH_SHEET(g_value_get_object (value));
         break;
     default:
-        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, param_id, pspec);
         break;
     }
 }
@@ -738,7 +734,7 @@ gnc_item_edit_get_preferred_width (GtkWidget *widget,
                                    gint *natural_width)
 {
     gint x, y, w = 1, h;
-    gnc_item_edit_get_pixel_coords (GNC_ITEM_EDIT (widget), &x, &y, &w, &h);
+    gnc_item_edit_get_pixel_coords (GNC_ITEM_EDIT(widget), &x, &y, &w, &h);
     *minimal_width = *natural_width = w - 1;
 }
 
@@ -749,7 +745,7 @@ gnc_item_edit_get_preferred_height (GtkWidget *widget,
                                     gint *natural_width)
 {
     gint x, y, w, h = 1;
-    gnc_item_edit_get_pixel_coords (GNC_ITEM_EDIT (widget), &x, &y, &w, &h);
+    gnc_item_edit_get_pixel_coords (GNC_ITEM_EDIT(widget), &x, &y, &w, &h);
     *minimal_width = *natural_width = h - 1;
 }
 
@@ -766,8 +762,8 @@ gnc_item_edit_class_init (GncItemEditClass *gnc_item_edit_class)
 
     gnc_item_edit_parent_class = g_type_class_peek_parent (gnc_item_edit_class);
 
-    object_class = G_OBJECT_CLASS (gnc_item_edit_class);
-    widget_class = GTK_WIDGET_CLASS (gnc_item_edit_class);
+    object_class = G_OBJECT_CLASS(gnc_item_edit_class);
+    widget_class = GTK_WIDGET_CLASS(gnc_item_edit_class);
 
     object_class->get_property = gnc_item_edit_get_property;
     object_class->set_property = gnc_item_edit_set_property;
@@ -810,9 +806,9 @@ gnc_item_edit_get_type (void)
         };
 
         gnc_item_edit_type =
-            g_type_register_static(GTK_TYPE_BOX,
-                                   "GncItemEdit",
-                                   &gnc_item_edit_info, 0);
+            g_type_register_static (GTK_TYPE_BOX,
+                                    "GncItemEdit",
+                                    &gnc_item_edit_info, 0);
     }
 
     return gnc_item_edit_type;
@@ -873,7 +869,8 @@ gnc_item_edit_get_button_width (GncItemEdit *item_edit)
             return item_edit->button_width;
         else
         {
-            GtkStyleContext *context = gtk_widget_get_style_context (GTK_WIDGET(item_edit->popup_toggle.tbutton));
+            GtkStyleContext *context = gtk_widget_get_style_context (
+                                           GTK_WIDGET(item_edit->popup_toggle.tbutton));
             GtkBorder border;
 
             gtk_style_context_get_border (context, GTK_STATE_FLAG_NORMAL, &border);
@@ -906,16 +903,15 @@ gnc_item_edit_new (GnucashSheet *sheet)
     GtkBorder padding;
     GtkBorder margin;
     GtkBorder border;
-    GncItemEdit *item_edit =
-            g_object_new (GNC_TYPE_ITEM_EDIT,
-                          "sheet", sheet,
-                          "spacing",     0,
-                          "homogeneous", FALSE,
-                           NULL);
+    GncItemEdit *item_edit = g_object_new (GNC_TYPE_ITEM_EDIT,
+                                           "sheet", sheet,
+                                           "spacing",     0,
+                                           "homogeneous", FALSE,
+                                            NULL);
     gtk_layout_put (GTK_LAYOUT(sheet), GTK_WIDGET(item_edit), 0, 0);
 
     /* Create the text entry */
-    item_edit->editor = gtk_entry_new();
+    item_edit->editor = gtk_entry_new ();
     sheet->entry = item_edit->editor;
     gtk_entry_set_width_chars (GTK_ENTRY(item_edit->editor), 1);
     gtk_box_pack_start (GTK_BOX(item_edit), item_edit->editor, TRUE, TRUE, 0);
@@ -937,48 +933,48 @@ gnc_item_edit_new (GnucashSheet *sheet)
 
     // Connect to the draw signal so we can draw a cursor
     g_signal_connect_after (item_edit->editor, "draw",
-                            G_CALLBACK (draw_text_cursor_cb), item_edit);
+                            G_CALLBACK(draw_text_cursor_cb), item_edit);
 
     g_signal_connect (item_edit->editor, "preedit-changed",
-                      G_CALLBACK (preedit_changed_cb), item_edit);
+                      G_CALLBACK(preedit_changed_cb), item_edit);
 
     // Fill in the background so the underlying sheet cell can not be seen
     g_signal_connect (item_edit, "draw",
-                            G_CALLBACK (draw_background_cb), item_edit);
+                      G_CALLBACK(draw_background_cb), item_edit);
 
     // This call back intercepts the mouse button event so the main
     // register popup menu can be displayed instead of the entry one.
     g_signal_connect (item_edit->editor, "button-press-event",
-                            G_CALLBACK (button_press_cb), sheet);
+                      G_CALLBACK(button_press_cb), sheet);
 
     /* Create the popup button
        It will only be displayed when the cell being edited provides
        a popup item (like a calendar or account list) */
     item_edit->popup_toggle.tbutton = gnc_item_edit_tb_new (sheet);
-    gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (item_edit->popup_toggle.tbutton), FALSE);
+    gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON(item_edit->popup_toggle.tbutton), FALSE);
 
     /* Wrap the popup button in an event box to give it its own gdkwindow.
      * Without one the button would disappear behind the grid object. */
-    item_edit->popup_toggle.ebox = gtk_event_box_new();
-    g_object_ref(item_edit->popup_toggle.ebox);
-    gtk_container_add(GTK_CONTAINER(item_edit->popup_toggle.ebox),
-                      item_edit->popup_toggle.tbutton);
+    item_edit->popup_toggle.ebox = gtk_event_box_new ();
+    g_object_ref (item_edit->popup_toggle.ebox);
+    gtk_container_add (GTK_CONTAINER(item_edit->popup_toggle.ebox),
+                       item_edit->popup_toggle.tbutton);
 
     gtk_box_pack_start (GTK_BOX(item_edit), item_edit->popup_toggle.ebox, FALSE, FALSE, 0);
-    gtk_widget_show_all(GTK_WIDGET(item_edit));
-    g_signal_connect(G_OBJECT(item_edit), "destroy",
-                     G_CALLBACK(gnc_item_edit_destroying), NULL);
+    gtk_widget_show_all (GTK_WIDGET(item_edit));
+    g_signal_connect (G_OBJECT(item_edit), "destroy",
+                      G_CALLBACK(gnc_item_edit_destroying), NULL);
     return GTK_WIDGET(item_edit);
 }
 
 static void
 gnc_item_edit_destroying (GtkWidget *item_edit, gpointer data)
 {
-    if (GNC_ITEM_EDIT (item_edit)->popup_height_signal_id > 0)
-        g_signal_handler_disconnect (GNC_ITEM_EDIT (item_edit)->popup_item,
-                                     GNC_ITEM_EDIT (item_edit)->popup_height_signal_id);
+    if (GNC_ITEM_EDIT(item_edit)->popup_height_signal_id > 0)
+        g_signal_handler_disconnect (GNC_ITEM_EDIT(item_edit)->popup_item,
+                                     GNC_ITEM_EDIT(item_edit)->popup_height_signal_id);
 
-    while (g_idle_remove_by_data((gpointer)item_edit))
+    while (g_idle_remove_by_data ((gpointer)item_edit))
         continue;
 }
 
@@ -997,7 +993,7 @@ check_popup_height_is_true (GtkWidget    *widget,
         gtk_container_remove (GTK_CONTAINER(item_edit->sheet), item_edit->popup_item);
 
         g_idle_add_full (G_PRIORITY_HIGH_IDLE,
-                        (GSourceFunc) gnc_item_edit_update, item_edit, NULL);
+                        (GSourceFunc)gnc_item_edit_update, item_edit, NULL);
     }
 }
 
@@ -1027,14 +1023,14 @@ gnc_item_edit_show_popup (GncItemEdit *item_edit)
 
     sheet_width = sheet->width;
 
-    gtk_widget_get_allocation (GTK_WIDGET (sheet), &alloc);
+    gtk_widget_get_allocation (GTK_WIDGET(sheet), &alloc);
     view_height = alloc.height;
 
-    vadj = gtk_scrollable_get_vadjustment(GTK_SCROLLABLE(sheet));
-    hadj = gtk_scrollable_get_hadjustment(GTK_SCROLLABLE(sheet));
+    vadj = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE(sheet));
+    hadj = gtk_scrollable_get_hadjustment (GTK_SCROLLABLE(sheet));
 
-    y_offset = gtk_adjustment_get_value(vadj);
-    x_offset = gtk_adjustment_get_value(hadj);
+    y_offset = gtk_adjustment_get_value (vadj);
+    x_offset = gtk_adjustment_get_value (hadj);
     gnc_item_edit_get_pixel_coords (item_edit, &x, &y, &w, &h);
 
     popup_x = x;
@@ -1042,7 +1038,7 @@ gnc_item_edit_show_popup (GncItemEdit *item_edit)
     up_height = y - y_offset;
     down_height = view_height - (up_height + h);
 
-    popup_max_height = MAX (up_height, down_height);
+    popup_max_height = MAX(up_height, down_height);
     popup_max_width = sheet_width - popup_x + x_offset; // always pops to the right
 
     if (item_edit->popup_get_height)
@@ -1109,7 +1105,7 @@ gnc_item_edit_show_popup (GncItemEdit *item_edit)
         if (popup_width > popup_max_width)
         {
             popup_x -= popup_width - popup_max_width;
-            popup_x = MAX (0, popup_x);
+            popup_x = MAX(0, popup_x);
         }
         else
             popup_x = x;
@@ -1122,13 +1118,13 @@ gnc_item_edit_show_popup (GncItemEdit *item_edit)
 void
 gnc_item_edit_hide_popup (GncItemEdit *item_edit)
 {
-    g_return_if_fail(item_edit != NULL);
-    g_return_if_fail(GNC_IS_ITEM_EDIT(item_edit));
+    g_return_if_fail (item_edit != NULL);
+    g_return_if_fail (GNC_IS_ITEM_EDIT(item_edit));
 
     if (!item_edit->is_popup)
         return;
 
-    if (gtk_widget_get_parent (GTK_WIDGET(item_edit->popup_item)) != GTK_WIDGET (item_edit->sheet))
+    if (gtk_widget_get_parent (GTK_WIDGET(item_edit->popup_item)) != GTK_WIDGET(item_edit->sheet))
         return;
 
     gtk_container_remove (GTK_CONTAINER(item_edit->sheet), item_edit->popup_item);
@@ -1137,9 +1133,9 @@ gnc_item_edit_hide_popup (GncItemEdit *item_edit)
     item_edit->popup_toggle.arrow_down = TRUE;
 
     gtk_toggle_button_set_active
-    (GTK_TOGGLE_BUTTON(item_edit->popup_toggle.tbutton), FALSE);
+        (GTK_TOGGLE_BUTTON(item_edit->popup_toggle.tbutton), FALSE);
 
-    gtk_widget_grab_focus (GTK_WIDGET (item_edit->sheet));
+    gtk_widget_grab_focus (GTK_WIDGET(item_edit->sheet));
 }
 
 
@@ -1169,7 +1165,7 @@ gnc_item_edit_set_popup (GncItemEdit    *item_edit,
     }
     else
     {
-        if (GNC_ITEM_EDIT (item_edit)->popup_height_signal_id > 0)
+        if (GNC_ITEM_EDIT(item_edit)->popup_height_signal_id > 0)
         {
             g_signal_handler_disconnect (item_edit->popup_item, item_edit->popup_height_signal_id);
             item_edit->popup_height_signal_id = 0;
@@ -1203,9 +1199,9 @@ gnc_item_edit_get_has_selection (GncItemEdit *item_edit)
     GtkEditable *editable;
 
     g_return_val_if_fail ((item_edit != NULL), FALSE);
-    g_return_val_if_fail (GNC_IS_ITEM_EDIT (item_edit), FALSE);
+    g_return_val_if_fail (GNC_IS_ITEM_EDIT(item_edit), FALSE);
 
-    editable = GTK_EDITABLE (item_edit->editor);
-    return gtk_editable_get_selection_bounds(editable, NULL, NULL);
+    editable = GTK_EDITABLE(item_edit->editor);
+    return gtk_editable_get_selection_bounds (editable, NULL, NULL);
 }
 

commit d55060c0fc838d89cbdea453a2ce58c3fb1b9b45
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Mon Apr 5 14:32:48 2021 +0100

    Bug 796761 - Control characters can be pasted in register fields
    
    If copied text includes control characters they are inserted when
    pasted which can cause alignment issues. This commit filters the
    clipboard text for control characters before it is pasted.

diff --git a/gnucash/register/register-gnome/gnucash-item-edit.c b/gnucash/register/register-gnome/gnucash-item-edit.c
index 3ea0de310..9fb4f35c0 100644
--- a/gnucash/register/register-gnome/gnucash-item-edit.c
+++ b/gnucash/register/register-gnome/gnucash-item-edit.c
@@ -40,6 +40,7 @@
 #include "gnucash-sheetP.h"
 #include "gnucash-style.h"
 
+#include "gnc-ui-util.h"
 
 /* The arguments we take */
 enum
@@ -398,7 +399,43 @@ gnc_item_edit_copy_clipboard (GncItemEdit *item_edit)
 void
 gnc_item_edit_paste_clipboard (GncItemEdit *item_edit)
 {
-    gtk_editable_paste_clipboard(GTK_EDITABLE(item_edit->editor));
+    GtkClipboard *clipboard = gtk_widget_get_clipboard (GTK_WIDGET(item_edit->editor),
+                                                        GDK_SELECTION_CLIPBOARD);
+    gchar *text = gtk_clipboard_wait_for_text (clipboard);
+    gchar *filtered_text;
+    gint start_pos, end_pos;
+    gint position;
+
+    if (!text)
+        return;
+
+    filtered_text = gnc_filter_text_for_control_chars (text);
+
+    if (!filtered_text)
+    {
+        g_free (text);
+        return;
+    }
+
+    position = gtk_editable_get_position (GTK_EDITABLE(item_edit->editor));
+
+    if (gtk_editable_get_selection_bounds (GTK_EDITABLE(item_edit->editor),
+                                           &start_pos, &end_pos))
+    {
+        position = start_pos;
+
+        gtk_editable_delete_selection (GTK_EDITABLE(item_edit->editor));
+        gtk_editable_insert_text (GTK_EDITABLE(item_edit->editor),
+                                  filtered_text, -1, &position);
+    }
+    else
+        gtk_editable_insert_text (GTK_EDITABLE(item_edit->editor),
+                                  filtered_text, -1, &position);
+
+    gtk_editable_set_position (GTK_EDITABLE(item_edit->editor), position);
+
+    g_free (text);
+    g_free (filtered_text);
 }
 
 
diff --git a/libgnucash/app-utils/gnc-ui-util.c b/libgnucash/app-utils/gnc-ui-util.c
index 8ab9dd7fb..9fc8ae0b1 100644
--- a/libgnucash/app-utils/gnc-ui-util.c
+++ b/libgnucash/app-utils/gnc-ui-util.c
@@ -2663,3 +2663,67 @@ gnc_ui_util_remove_registered_prefs (void)
                                  GNC_PREF_AUTO_DECIMAL_PLACES,
                                  gnc_set_auto_decimal_places, NULL);
 }
+
+static gboolean
+unichar_is_cntrl (gunichar uc)
+{
+    if (uc < 0x20 || (uc > 0x7e && uc < 0xa0))
+        return TRUE;
+    else
+        return FALSE;
+}
+
+gchar *
+gnc_filter_text_for_control_chars (const gchar *text)
+{
+    gchar *normal_text, *nt;
+    GString *filtered;
+    gboolean cntrl = FALSE;
+    gboolean text_found = FALSE;
+
+    if (!text)
+        return NULL;
+
+    if (!g_utf8_validate (text, -1, NULL))
+        return NULL;
+
+    normal_text = g_utf8_normalize (text, -1, G_NORMALIZE_ALL_COMPOSE);
+
+    filtered = g_string_sized_new (strlen (normal_text) + 1);
+
+    nt = normal_text;
+
+    while (*nt)
+    {
+        gunichar uc = g_utf8_get_char (nt);
+
+        // check for starting with control characters
+        if (unichar_is_cntrl (uc) && !text_found)
+        {
+            nt = g_utf8_next_char (nt);
+            continue;
+        }
+        // check for alpha, num and punctuation
+        if (!unichar_is_cntrl (uc))
+        {
+            filtered = g_string_append_unichar (filtered, uc);
+            text_found = TRUE;
+        }
+        // check for control characters after text
+        if (unichar_is_cntrl (uc))
+            cntrl = TRUE;
+
+        nt = g_utf8_next_char (nt);
+
+        if (cntrl) // if control characters in text replace with space
+        {
+            gunichar uc2 = g_utf8_get_char (nt);
+
+            if (!unichar_is_cntrl (uc2))
+                filtered = g_string_append_unichar (filtered, ' ');
+        }
+        cntrl = FALSE;
+    }
+    g_free (normal_text);
+    return g_string_free (filtered, FALSE);
+}
diff --git a/libgnucash/app-utils/gnc-ui-util.h b/libgnucash/app-utils/gnc-ui-util.h
index 40c99f7e1..c400c0637 100644
--- a/libgnucash/app-utils/gnc-ui-util.h
+++ b/libgnucash/app-utils/gnc-ui-util.h
@@ -397,6 +397,15 @@ void gnc_ui_util_init (void);
 
 void gnc_ui_util_remove_registered_prefs (void);
 
+/** Returns the incoming text removed of control characters
+ *
+ * @param incoming_text The text to filter
+ *
+ * @return The incoming text filtered of control characters to be
+ *         freed by the caller.
+*/
+gchar * gnc_filter_text_for_control_chars (const gchar *incoming_text);
+
 #endif
 /** @} */
 /** @} */



Summary of changes:
 gnucash/gnome-search/search-double.c               |  12 +-
 gnucash/gnome-search/search-int64.c                |  12 +-
 gnucash/gnome-search/search-numeric.c              |  11 +-
 gnucash/gnome-utils/dialog-account.c               |   2 +-
 gnucash/gnome-utils/dialog-tax-table.c             |  64 +++
 gnucash/gnome-utils/dialog-transfer.c              |  16 +-
 gnucash/gnome-utils/gnc-amount-edit.c              | 597 +++++++++++++--------
 gnucash/gnome-utils/gnc-amount-edit.h              | 242 +++++++--
 gnucash/gnome/assistant-loan.cpp                   |  19 +
 gnucash/gnome/assistant-stock-split.c              |  18 +-
 gnucash/gnome/dialog-customer.c                    |  15 +-
 gnucash/gnome/dialog-employee.c                    |  28 +
 gnucash/gnome/dialog-fincalc.c                     |  52 +-
 gnucash/gnome/dialog-invoice.c                     |   6 +-
 gnucash/gnome/dialog-job.c                         |   8 +
 gnucash/gnome/dialog-payment.c                     |  61 ++-
 gnucash/gnome/dialog-price-editor.c                |  17 +-
 gnucash/gnome/window-autoclear.c                   |  44 +-
 gnucash/gnome/window-reconcile.c                   |  41 +-
 gnucash/gnome/window-reconcile2.c                  |  43 +-
 gnucash/gtkbuilder/dialog-fincalc.glade            |   5 +
 gnucash/register/ledger-core/split-register-load.c |  15 +-
 gnucash/register/register-core/basiccell.c         |  57 ++
 gnucash/register/register-core/basiccell.h         |   8 +
 gnucash/register/register-core/formulacell.c       |  49 +-
 gnucash/register/register-core/pricecell.c         |  45 +-
 .../register/register-gnome/gnucash-item-edit.c    | 263 +++++----
 libgnucash/app-utils/gnc-ui-util.c                 | 146 +++++
 libgnucash/app-utils/gnc-ui-util.h                 |  50 ++
 29 files changed, 1408 insertions(+), 538 deletions(-)



More information about the gnucash-changes mailing list