gnucash maint: Multiple changes pushed

Christopher Lam clam at code.gnucash.org
Wed Jul 20 19:36:46 EDT 2022


Updated	 via  https://github.com/Gnucash/gnucash/commit/74525e91 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/078a579e (commit)
	 via  https://github.com/Gnucash/gnucash/commit/c4e02e1b (commit)
	 via  https://github.com/Gnucash/gnucash/commit/a2156648 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/1ee87fa4 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/0d3520f6 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/6d1f3c5a (commit)
	 via  https://github.com/Gnucash/gnucash/commit/2bafe700 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/d4ff2ede (commit)
	 via  https://github.com/Gnucash/gnucash/commit/fb6091fb (commit)
	 via  https://github.com/Gnucash/gnucash/commit/3db8c56a (commit)
	 via  https://github.com/Gnucash/gnucash/commit/6f74d4e3 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/b1071295 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/fac0cde7 (commit)
	from  https://github.com/Gnucash/gnucash/commit/754cb072 (commit)



commit 74525e91f36363de6830b58b8b67a765c4bb99cf
Merge: 754cb0723 078a579ed
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Thu Jul 21 07:35:25 2022 +0800

    Merge branch 'maint-edit-multiple-rows' into maint #1371


commit 078a579ed4d8144afc6eb9e1fd4bfead8f918dd1
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Wed Jul 20 08:13:02 2022 +0800

    [import-main-matcher] attach menu to treeview
    
    another leak plugged

diff --git a/gnucash/import-export/import-main-matcher.c b/gnucash/import-export/import-main-matcher.c
index 8145ef3f2..8ad8f3465 100644
--- a/gnucash/import-export/import-main-matcher.c
+++ b/gnucash/import-export/import-main-matcher.c
@@ -1378,6 +1378,8 @@ gnc_gen_trans_view_popup_menu (GtkTreeView *treeview,
                       info);
     gtk_menu_shell_append (GTK_MENU_SHELL(menu), menuitem);
 
+    gtk_menu_attach_to_widget (GTK_MENU (menu), GTK_WIDGET (treeview), NULL);
+
     gtk_widget_show_all (menu);
     /* Note: event can be NULL here when called from view_onPopupMenu; */
     gtk_menu_popup_at_pointer (GTK_MENU(menu), (GdkEvent*)event);

commit c4e02e1b4b9cc1a4d109df05cca1be5fdd17caca
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Wed Jul 20 08:13:02 2022 +0800

    [import-main-matcher] tidy destructors

diff --git a/gnucash/import-export/import-main-matcher.c b/gnucash/import-export/import-main-matcher.c
index b4392cb23..8145ef3f2 100644
--- a/gnucash/import-export/import-main-matcher.c
+++ b/gnucash/import-export/import-main-matcher.c
@@ -164,14 +164,6 @@ static gboolean query_tooltip_tree_view_cb (GtkWidget *widget, gint x, gint y,
                                             gpointer user_data);
 /* end local prototypes */
 
-static
-gboolean delete_hash (gpointer key, gpointer value, gpointer user_data)
-{
-    // Value is a hash table that needs to be destroyed.
-    g_hash_table_destroy (value);
-    return TRUE;
-}
-
 static void
 update_all_balances (GNCImportMainMatcher *info)
 {
@@ -239,10 +231,7 @@ gnc_gen_trans_list_delete (GNCImportMainMatcher *info)
     update_all_balances (info);
 
     gnc_import_PendingMatches_delete (info->pending_matches);
-    g_hash_table_foreach_remove (info->acct_id_hash, delete_hash, NULL);
     g_hash_table_destroy (info->acct_id_hash);
-    info->acct_id_hash = NULL;
-
     g_hash_table_destroy (info->desc_hash);
     g_hash_table_destroy (info->notes_hash);
     g_hash_table_destroy (info->memo_hash);
@@ -1593,7 +1582,8 @@ gnc_gen_trans_init_view (GNCImportMainMatcher *info,
     g_signal_connect (view, "popup-menu",
                       G_CALLBACK(gnc_gen_trans_onPopupMenu_cb), info);
 
-    info->acct_id_hash = g_hash_table_new (g_direct_hash, g_direct_equal);
+    info->acct_id_hash = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL,
+                                                (GDestroyNotify)g_hash_table_destroy);
 }
 
 static void

commit a215664870a00ea11d1770f2a732b36d2ba21e2d
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Mon Jul 18 23:27:28 2022 +0800

    [import-main-matcher] full substring search, add mnemonics

diff --git a/gnucash/gtkbuilder/dialog-import.glade b/gnucash/gtkbuilder/dialog-import.glade
index f7e3d001e..0fda9707a 100644
--- a/gnucash/gtkbuilder/dialog-import.glade
+++ b/gnucash/gtkbuilder/dialog-import.glade
@@ -927,7 +927,9 @@
                 <property name="visible">True</property>
                 <property name="can-focus">False</property>
                 <property name="halign">end</property>
-                <property name="label" translatable="yes">Description</property>
+                <property name="label" translatable="yes">_Description</property>
+                <property name="use-underline">True</property>
+                <property name="mnemonic-widget">desc_entry</property>
               </object>
               <packing>
                 <property name="left-attach">0</property>
@@ -939,7 +941,9 @@
                 <property name="visible">True</property>
                 <property name="can-focus">False</property>
                 <property name="halign">end</property>
-                <property name="label" translatable="yes">Notes</property>
+                <property name="label" translatable="yes">_Notes</property>
+                <property name="use-underline">True</property>
+                <property name="mnemonic-widget">notes_entry</property>
               </object>
               <packing>
                 <property name="left-attach">0</property>
@@ -951,7 +955,9 @@
                 <property name="visible">True</property>
                 <property name="can-focus">False</property>
                 <property name="halign">end</property>
-                <property name="label" translatable="yes">Memo</property>
+                <property name="label" translatable="yes">_Memo</property>
+                <property name="use-underline">True</property>
+                <property name="mnemonic-widget">memo_entry</property>
               </object>
               <packing>
                 <property name="left-attach">0</property>
diff --git a/gnucash/import-export/import-main-matcher.c b/gnucash/import-export/import-main-matcher.c
index d42da0a4c..b4392cb23 100644
--- a/gnucash/import-export/import-main-matcher.c
+++ b/gnucash/import-export/import-main-matcher.c
@@ -986,13 +986,45 @@ static RowInfo * row_get_info (gpointer row, GNCImportMainMatcher *info)
     return retval;
 }
 
+enum
+{
+    COMPLETION_LIST_ORIGINAL,
+    COMPLETION_LIST_COLLATED,
+    COMPLETION_LIST_NORMALIZED_FOLDED,
+};
+
 static void populate_list (gpointer key, gpointer value, GtkListStore *list)
 {
     GtkTreeIter iter;
-    char *collated_key = g_utf8_collate_key (key, -1);
+    const char *original = key;
+    char *collated = g_utf8_collate_key (original, -1);
+    char *normalized = collated ? g_utf8_normalize (original, -1, G_NORMALIZE_ALL) : NULL;
+    char *normalized_folded = normalized ? g_utf8_casefold (normalized, -1) : NULL;
     gtk_list_store_append (list, &iter);
-    gtk_list_store_set (list, &iter, 0, key, 1, collated_key, -1);
-    g_free (collated_key);
+    gtk_list_store_set (list, &iter,
+                        COMPLETION_LIST_ORIGINAL, original,
+                        COMPLETION_LIST_COLLATED, collated,
+                        COMPLETION_LIST_NORMALIZED_FOLDED, normalized_folded,
+                        -1);
+    g_free (normalized_folded);
+    g_free (normalized);
+    g_free (collated);
+}
+
+static gboolean
+match_func (GtkEntryCompletion *completion, const char *entry_str,
+            GtkTreeIter *iter, gpointer user_data)
+{
+    GtkTreeModel *model = user_data;
+    gchar *existing_str = NULL;
+    gboolean ret = FALSE;
+    gtk_tree_model_get (model, iter,
+                        COMPLETION_LIST_NORMALIZED_FOLDED, &existing_str,
+                        -1);
+    if (existing_str && *existing_str && strstr (existing_str, entry_str))
+        ret = TRUE;
+    g_free (existing_str);
+    return ret;
 }
 
 static void
@@ -1007,16 +1039,20 @@ setup_entry (GtkWidget *entry, gboolean sensitive, GHashTable *hash,
     if (!sensitive)
         return;
 
-    list = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_STRING);
+    list = gtk_list_store_new (3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
     g_hash_table_foreach (hash, (GHFunc)populate_list, list);
     if (!g_hash_table_lookup (hash, (gpointer)initial))
         populate_list ((gpointer)initial, NULL, list);
-    gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (list), 1,
+    gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (list),
+                                          COMPLETION_LIST_COLLATED,
                                           GTK_SORT_ASCENDING);
 
     completion = gtk_entry_completion_new ();
     gtk_entry_completion_set_model (completion, GTK_TREE_MODEL(list));
-    gtk_entry_completion_set_text_column (completion, 0);
+    gtk_entry_completion_set_text_column (completion, COMPLETION_LIST_ORIGINAL);
+    gtk_entry_completion_set_match_func (completion,
+                                         (GtkEntryCompletionMatchFunc)match_func,
+                                         GTK_TREE_MODEL(list), NULL);
     gtk_entry_set_completion (GTK_ENTRY (entry), completion);
 }
 

commit 1ee87fa4835f4db160b07a2c3b120a655284058d
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Sun Jul 17 23:12:13 2022 +0800

    [import-main-matcher] add mnemonic, and amend menuitem text

diff --git a/gnucash/import-export/import-main-matcher.c b/gnucash/import-export/import-main-matcher.c
index edf63684f..d42da0a4c 100644
--- a/gnucash/import-export/import-main-matcher.c
+++ b/gnucash/import-export/import-main-matcher.c
@@ -1291,8 +1291,8 @@ gnc_gen_trans_view_popup_menu (GtkTreeView *treeview,
 
     ENTER ("");
     menu = gtk_menu_new();
-    menuitem = gtk_menu_item_new_with_label (
-                   _("Assign a transfer account to the selection."));
+    menuitem = gtk_menu_item_new_with_mnemonic
+        (_("_Assign a transfer account to the selection"));
     g_signal_connect (menuitem, "activate",
                       G_CALLBACK(
                       gnc_gen_trans_assign_transfer_account_to_selection_cb),
@@ -1338,7 +1338,7 @@ gnc_gen_trans_view_popup_menu (GtkTreeView *treeview,
         rowinfo_free (rowinfo);
     }
 
-    menuitem = gtk_menu_item_new_with_label (_("Edit description, notes, memo."));
+    menuitem = gtk_menu_item_new_with_mnemonic (_("_Edit description, notes, memo"));
     gtk_widget_set_sensitive (menuitem,
                               info->edit_desc || info->edit_notes || info->edit_memo);
     g_signal_connect (menuitem, "activate",
@@ -1346,7 +1346,7 @@ gnc_gen_trans_view_popup_menu (GtkTreeView *treeview,
                       info);
     gtk_menu_shell_append (GTK_MENU_SHELL(menu), menuitem);
 
-    menuitem = gtk_menu_item_new_with_label (_("Reset edits."));
+    menuitem = gtk_menu_item_new_with_mnemonic (_("_Reset all edits"));
     gtk_widget_set_sensitive (menuitem, has_edits);
     g_signal_connect (menuitem, "activate",
                       G_CALLBACK (gnc_gen_trans_reset_edits_cb),

commit 0d3520f674b6e83a6096301bca15c1f2b2b5d967
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Sun Jul 17 00:56:39 2022 +0800

    [import-main-matcher] editing a field will add onto hash

diff --git a/gnucash/import-export/import-main-matcher.c b/gnucash/import-export/import-main-matcher.c
index 12ad9493e..edf63684f 100644
--- a/gnucash/import-export/import-main-matcher.c
+++ b/gnucash/import-export/import-main-matcher.c
@@ -89,6 +89,8 @@ struct _main_matcher_info
     GHashTable *desc_hash;
     GHashTable *notes_hash;
     GHashTable *memo_hash;
+
+    GList *new_strings;
 };
 
 enum downloaded_cols
@@ -245,6 +247,8 @@ gnc_gen_trans_list_delete (GNCImportMainMatcher *info)
     g_hash_table_destroy (info->notes_hash);
     g_hash_table_destroy (info->memo_hash);
 
+    g_list_free_full (info->new_strings, (GDestroyNotify)g_free);
+
     g_free (info);
 }
 
@@ -1102,10 +1106,24 @@ gnc_gen_trans_edit_fields (GtkMenuItem *menuitem, GNCImportMainMatcher *info)
                                     DOWNLOADED_COL_DESCRIPTION_STYLE, style,
                                     -1);
                 xaccTransSetDescription (row->trans, new_desc);
+                if (*new_desc && !g_hash_table_lookup (info->desc_hash, new_desc))
+                {
+                    char *new_string = g_strdup (new_desc);
+                    info->new_strings = g_list_prepend (info->new_strings, new_string);
+                    g_hash_table_insert (info->desc_hash, new_string, one);
+                }
             }
 
             if (info->edit_notes)
+            {
                 xaccTransSetNotes (row->trans, new_notes);
+                if (*new_notes && !g_hash_table_lookup (info->notes_hash, new_notes))
+                {
+                    char *new_string = g_strdup (new_notes);
+                    info->new_strings = g_list_prepend (info->new_strings, new_string);
+                    g_hash_table_insert (info->notes_hash, new_string, one);
+                }
+            }
 
             if (info->edit_memo)
             {
@@ -1116,6 +1134,12 @@ gnc_gen_trans_edit_fields (GtkMenuItem *menuitem, GNCImportMainMatcher *info)
                                     DOWNLOADED_COL_MEMO_STYLE, style,
                                     -1);
                 xaccSplitSetMemo (row->split, new_memo);
+                if (*new_memo && !g_hash_table_lookup (info->memo_hash, new_memo))
+                {
+                    char *new_string = g_strdup (new_memo);
+                    info->new_strings = g_list_prepend (info->new_strings, new_string);
+                    g_hash_table_insert (info->memo_hash, new_string, one);
+                }
             }
         }
         g_free (new_desc);
@@ -1682,6 +1706,7 @@ gnc_gen_trans_list_new (GtkWidget *parent,
     info->notes_hash = g_hash_table_new (g_str_hash, g_str_equal);
     info->memo_hash = g_hash_table_new (g_str_hash, g_str_equal);
 
+    info->new_strings = NULL;
     return info;
 }
 

commit 6d1f3c5af60aa5bc27d088124600b94eb1b5370d
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Sat Jul 16 00:00:20 2022 +0800

    [import-main-matcher] if multi-accounts, scan all accounts
    
    if an ofx has info on multiple accounts, and selection includes
    multiple accounts, scan all of them.
    
    also further separation between UI and data processing.

diff --git a/gnucash/import-export/import-main-matcher.c b/gnucash/import-export/import-main-matcher.c
index 443011699..12ad9493e 100644
--- a/gnucash/import-export/import-main-matcher.c
+++ b/gnucash/import-export/import-main-matcher.c
@@ -85,6 +85,10 @@ struct _main_matcher_info
     gboolean edit_desc;
     gboolean edit_notes;
     gboolean edit_memo;
+
+    GHashTable *desc_hash;
+    GHashTable *notes_hash;
+    GHashTable *memo_hash;
 };
 
 enum downloaded_cols
@@ -236,6 +240,11 @@ gnc_gen_trans_list_delete (GNCImportMainMatcher *info)
     g_hash_table_foreach_remove (info->acct_id_hash, delete_hash, NULL);
     g_hash_table_destroy (info->acct_id_hash);
     info->acct_id_hash = NULL;
+
+    g_hash_table_destroy (info->desc_hash);
+    g_hash_table_destroy (info->notes_hash);
+    g_hash_table_destroy (info->memo_hash);
+
     g_free (info);
 }
 
@@ -457,6 +466,47 @@ resolve_conflicts (GNCImportMainMatcher *info)
     }
 }
 
+
+static void
+load_hash_tables (GNCImportMainMatcher *info)
+{
+    GtkTreeModel *model = gtk_tree_view_get_model (info->view);
+    GtkTreeIter import_iter;
+    GList *accounts_list = NULL;
+    gboolean valid = gtk_tree_model_get_iter_first (model, &import_iter);
+    while (valid)
+    {
+        GNCImportTransInfo *trans_info = get_trans_info (model, &import_iter);
+        Split *s = gnc_import_TransInfo_get_fsplit (trans_info);
+        Account *acc = xaccSplitGetAccount (s);
+        if (!g_list_find (accounts_list, acc))
+            accounts_list = g_list_prepend (accounts_list, acc);
+        valid = gtk_tree_model_iter_next (model, &import_iter);
+    }
+    for (GList *m = accounts_list; m; m = m->next)
+    {
+        for (GList *n = xaccAccountGetSplitList (m->data); n; n = n->next)
+        {
+            const Split *s = n->data;
+            const Transaction *t = xaccSplitGetParent (s);
+            const gchar *key;
+
+            key = xaccTransGetDescription (t);
+            if (key && *key)
+                g_hash_table_insert (info->desc_hash, (gpointer)key, one);
+
+            key = xaccTransGetNotes (t);
+            if (key && *key)
+                g_hash_table_insert (info->notes_hash, (gpointer)key, one);
+
+            key = xaccSplitGetMemo (s);
+            if (key && *key)
+                g_hash_table_insert (info->memo_hash, (gpointer)key, one);
+        }
+    }
+    g_list_free (accounts_list);
+}
+
 void
 gnc_gen_trans_list_show_all (GNCImportMainMatcher *info)
 {
@@ -482,6 +532,7 @@ gnc_gen_trans_list_show_all (GNCImportMainMatcher *info)
                                  xaccAccountGetAppendText(account));
 
     gnc_gen_trans_list_create_matches (info);
+    load_hash_tables (info);
     resolve_conflicts (info);
     gtk_widget_show_all (GTK_WIDGET(info->main_widget));
     gnc_gen_trans_list_show_accounts_column (info);
@@ -966,15 +1017,13 @@ setup_entry (GtkWidget *entry, gboolean sensitive, GHashTable *hash,
 }
 
 static gboolean
-input_new_fields (GtkWidget *parent, RowInfo *info, GtkTreeStore *store,
-                  gboolean edit_desc, gboolean edit_notes, gboolean edit_memo,
+input_new_fields (GNCImportMainMatcher *info, RowInfo *rowinfo,
                   char **new_desc, char **new_notes, char **new_memo)
 {
     GtkWidget  *desc_entry, *notes_entry, *memo_entry, *label;
     GtkWidget  *dialog;
     GtkBuilder *builder;
     gboolean    retval = FALSE;
-    GHashTable *desc_hash, *notes_hash, *memo_hash;
 
     builder = gtk_builder_new ();
     gnc_builder_add_from_file (builder, "dialog-import.glade", "transaction_edit_dialog");
@@ -985,39 +1034,11 @@ input_new_fields (GtkWidget *parent, RowInfo *info, GtkTreeStore *store,
     memo_entry = GTK_WIDGET(gtk_builder_get_object (builder, "memo_entry"));
     notes_entry = GTK_WIDGET(gtk_builder_get_object (builder, "notes_entry"));
 
-    desc_hash = g_hash_table_new (g_str_hash, g_str_equal);
-    notes_hash = g_hash_table_new (g_str_hash, g_str_equal);
-    memo_hash = g_hash_table_new (g_str_hash, g_str_equal);
-
-    for (GList *n = xaccAccountGetSplitList (xaccSplitGetAccount (info->split));
-         n; n = n->next)
-    {
-        const Split *s = n->data;
-        const Transaction *t = xaccSplitGetParent (s);
-        const gchar *key;
-
-        key = xaccTransGetDescription (t);
-        if (key && *key)
-            g_hash_table_insert (desc_hash, (gpointer)key, one);
+    setup_entry (desc_entry, info->edit_desc, info->desc_hash, xaccTransGetDescription (rowinfo->trans));
+    setup_entry (notes_entry, info->edit_notes, info->notes_hash, xaccTransGetNotes (rowinfo->trans));
+    setup_entry (memo_entry, info->edit_memo, info->memo_hash, xaccSplitGetMemo (rowinfo->split));
 
-        key = xaccTransGetNotes (t);
-        if (key && *key)
-            g_hash_table_insert (notes_hash, (gpointer)key, one);
-
-        key = xaccSplitGetMemo (s);
-        if (key && *key)
-            g_hash_table_insert (memo_hash, (gpointer)key, one);
-    };
-
-    setup_entry (desc_entry, edit_desc, desc_hash, xaccTransGetDescription (info->trans));
-    setup_entry (notes_entry, edit_notes, notes_hash, xaccTransGetNotes (info->trans));
-    setup_entry (memo_entry, edit_memo, memo_hash, xaccSplitGetMemo (info->split));
-
-    g_hash_table_destroy (desc_hash);
-    g_hash_table_destroy (notes_hash);
-    g_hash_table_destroy (memo_hash);
-
-    gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (parent));
+    gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (info->main_widget));
 
     // run the dialog
     gtk_widget_show_all (dialog);
@@ -1047,7 +1068,7 @@ gnc_gen_trans_edit_fields (GtkMenuItem *menuitem, GNCImportMainMatcher *info)
     GtkTreeModel *model;
     GList *selected_rows, *row_info_list;
     GtkTreeStore* store;
-    char *new_desc, *new_notes, *new_memo;
+    char *new_desc = NULL, *new_notes = NULL, *new_memo = NULL;
 
     g_return_if_fail (info != NULL);
     ENTER("assign_transfer_account_to_selection_cb");
@@ -1066,8 +1087,7 @@ gnc_gen_trans_edit_fields (GtkMenuItem *menuitem, GNCImportMainMatcher *info)
 
     row_info_list = gnc_g_list_map (selected_rows, (GncGMapFunc) row_get_info, info);
 
-    if (input_new_fields (info->main_widget, row_info_list->data, store,
-                          info->edit_desc, info->edit_notes, info->edit_memo,
+    if (input_new_fields (info, row_info_list->data,
                           &new_desc, &new_notes, &new_memo))
     {
         for (GList *n = row_info_list; n; n = g_list_next (n))
@@ -1657,6 +1677,11 @@ gnc_gen_trans_list_new (GtkWidget *parent,
                                            info);
     // This ensure this dialog is closed when the session is closed.
     gnc_gui_component_set_session (info->id, gnc_get_current_session());
+
+    info->desc_hash = g_hash_table_new (g_str_hash, g_str_equal);
+    info->notes_hash = g_hash_table_new (g_str_hash, g_str_equal);
+    info->memo_hash = g_hash_table_new (g_str_hash, g_str_equal);
+
     return info;
 }
 

commit 2bafe700dbd0c208bd65a07edbc95ee8933a7686
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Fri Jul 15 17:40:30 2022 +0800

    [import-main-matcher] use GtkEntry with GtkEntryCompletion
    
    completion enabled using existing transaction strings.

diff --git a/gnucash/import-export/import-main-matcher.c b/gnucash/import-export/import-main-matcher.c
index a9f6f4382..443011699 100644
--- a/gnucash/import-export/import-main-matcher.c
+++ b/gnucash/import-export/import-main-matcher.c
@@ -121,6 +121,8 @@ enum downloaded_cols
 /*static QofLogModule log_module = GNC_MOD_IMPORT;*/
 static QofLogModule log_module = G_MOD_IMPORT_MATCHER;
 
+static const gpointer one = GINT_TO_POINTER (1);
+
 void on_matcher_ok_clicked (GtkButton *button, GNCImportMainMatcher *info);
 void on_matcher_cancel_clicked (GtkButton *button, gpointer user_data);
 gboolean on_matcher_delete_event (GtkWidget *widget, GdkEvent *event, gpointer data);
@@ -929,6 +931,40 @@ static RowInfo * row_get_info (gpointer row, GNCImportMainMatcher *info)
     return retval;
 }
 
+static void populate_list (gpointer key, gpointer value, GtkListStore *list)
+{
+    GtkTreeIter iter;
+    char *collated_key = g_utf8_collate_key (key, -1);
+    gtk_list_store_append (list, &iter);
+    gtk_list_store_set (list, &iter, 0, key, 1, collated_key, -1);
+    g_free (collated_key);
+}
+
+static void
+setup_entry (GtkWidget *entry, gboolean sensitive, GHashTable *hash,
+             const char *initial)
+{
+    GtkEntryCompletion* completion;
+    GtkListStore *list;
+
+    gtk_entry_set_text (GTK_ENTRY (entry), sensitive ? initial : _("Disabled"));
+    gtk_widget_set_sensitive (entry, sensitive);
+    if (!sensitive)
+        return;
+
+    list = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_STRING);
+    g_hash_table_foreach (hash, (GHFunc)populate_list, list);
+    if (!g_hash_table_lookup (hash, (gpointer)initial))
+        populate_list ((gpointer)initial, NULL, list);
+    gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (list), 1,
+                                          GTK_SORT_ASCENDING);
+
+    completion = gtk_entry_completion_new ();
+    gtk_entry_completion_set_model (completion, GTK_TREE_MODEL(list));
+    gtk_entry_completion_set_text_column (completion, 0);
+    gtk_entry_set_completion (GTK_ENTRY (entry), completion);
+}
+
 static gboolean
 input_new_fields (GtkWidget *parent, RowInfo *info, GtkTreeStore *store,
                   gboolean edit_desc, gboolean edit_notes, gboolean edit_memo,
@@ -938,6 +974,7 @@ input_new_fields (GtkWidget *parent, RowInfo *info, GtkTreeStore *store,
     GtkWidget  *dialog;
     GtkBuilder *builder;
     gboolean    retval = FALSE;
+    GHashTable *desc_hash, *notes_hash, *memo_hash;
 
     builder = gtk_builder_new ();
     gnc_builder_add_from_file (builder, "dialog-import.glade", "transaction_edit_dialog");
@@ -948,14 +985,37 @@ input_new_fields (GtkWidget *parent, RowInfo *info, GtkTreeStore *store,
     memo_entry = GTK_WIDGET(gtk_builder_get_object (builder, "memo_entry"));
     notes_entry = GTK_WIDGET(gtk_builder_get_object (builder, "notes_entry"));
 
-    gtk_widget_set_sensitive (desc_entry, edit_desc);
-    gtk_entry_set_text (GTK_ENTRY (desc_entry), xaccTransGetDescription (info->trans));
+    desc_hash = g_hash_table_new (g_str_hash, g_str_equal);
+    notes_hash = g_hash_table_new (g_str_hash, g_str_equal);
+    memo_hash = g_hash_table_new (g_str_hash, g_str_equal);
+
+    for (GList *n = xaccAccountGetSplitList (xaccSplitGetAccount (info->split));
+         n; n = n->next)
+    {
+        const Split *s = n->data;
+        const Transaction *t = xaccSplitGetParent (s);
+        const gchar *key;
+
+        key = xaccTransGetDescription (t);
+        if (key && *key)
+            g_hash_table_insert (desc_hash, (gpointer)key, one);
+
+        key = xaccTransGetNotes (t);
+        if (key && *key)
+            g_hash_table_insert (notes_hash, (gpointer)key, one);
+
+        key = xaccSplitGetMemo (s);
+        if (key && *key)
+            g_hash_table_insert (memo_hash, (gpointer)key, one);
+    };
 
-    gtk_widget_set_sensitive (notes_entry, edit_notes);
-    gtk_entry_set_text (GTK_ENTRY (notes_entry), xaccTransGetNotes (info->trans));
+    setup_entry (desc_entry, edit_desc, desc_hash, xaccTransGetDescription (info->trans));
+    setup_entry (notes_entry, edit_notes, notes_hash, xaccTransGetNotes (info->trans));
+    setup_entry (memo_entry, edit_memo, memo_hash, xaccSplitGetMemo (info->split));
 
-    gtk_widget_set_sensitive (memo_entry, edit_memo);
-    gtk_entry_set_text (GTK_ENTRY (memo_entry), xaccSplitGetMemo (info->split));
+    g_hash_table_destroy (desc_hash);
+    g_hash_table_destroy (notes_hash);
+    g_hash_table_destroy (memo_hash);
 
     gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (parent));
 

commit d4ff2ede955f3f0741c0c07fd72953f8482fb698
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Thu Jul 14 21:25:20 2022 +0800

    [import-main-matcher] annotate changed fields to italic
    
    and change use to .glade

diff --git a/gnucash/gtkbuilder/dialog-import.glade b/gnucash/gtkbuilder/dialog-import.glade
index f3350bc70..f7e3d001e 100644
--- a/gnucash/gtkbuilder/dialog-import.glade
+++ b/gnucash/gtkbuilder/dialog-import.glade
@@ -866,6 +866,145 @@
       <action-widget response="-7">matcher_help_close</action-widget>
     </action-widgets>
   </object>
+  <object class="GtkDialog" id="transaction_edit_dialog">
+    <property name="can-focus">False</property>
+    <property name="title" translatable="yes">Edit imported transaction details</property>
+    <property name="default-width">320</property>
+    <property name="type-hint">dialog</property>
+    <child internal-child="vbox">
+      <object class="GtkBox">
+        <property name="can-focus">False</property>
+        <property name="orientation">vertical</property>
+        <property name="spacing">2</property>
+        <child internal-child="action_area">
+          <object class="GtkButtonBox">
+            <property name="can-focus">False</property>
+            <property name="layout-style">end</property>
+            <child>
+              <object class="GtkButton" id="button1">
+                <property name="label" translatable="yes">_Cancel</property>
+                <property name="visible">True</property>
+                <property name="can-focus">True</property>
+                <property name="receives-default">True</property>
+                <property name="use-underline">True</property>
+              </object>
+              <packing>
+                <property name="expand">True</property>
+                <property name="fill">True</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkButton" id="button2">
+                <property name="label" translatable="yes">_OK</property>
+                <property name="visible">True</property>
+                <property name="can-focus">True</property>
+                <property name="receives-default">True</property>
+                <property name="use-underline">True</property>
+              </object>
+              <packing>
+                <property name="expand">True</property>
+                <property name="fill">True</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <!-- n-columns=2 n-rows=3 -->
+          <object class="GtkGrid">
+            <property name="visible">True</property>
+            <property name="can-focus">False</property>
+            <property name="row-spacing">3</property>
+            <property name="column-spacing">6</property>
+            <child>
+              <object class="GtkLabel">
+                <property name="visible">True</property>
+                <property name="can-focus">False</property>
+                <property name="halign">end</property>
+                <property name="label" translatable="yes">Description</property>
+              </object>
+              <packing>
+                <property name="left-attach">0</property>
+                <property name="top-attach">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel">
+                <property name="visible">True</property>
+                <property name="can-focus">False</property>
+                <property name="halign">end</property>
+                <property name="label" translatable="yes">Notes</property>
+              </object>
+              <packing>
+                <property name="left-attach">0</property>
+                <property name="top-attach">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel">
+                <property name="visible">True</property>
+                <property name="can-focus">False</property>
+                <property name="halign">end</property>
+                <property name="label" translatable="yes">Memo</property>
+              </object>
+              <packing>
+                <property name="left-attach">0</property>
+                <property name="top-attach">2</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkEntry" id="desc_entry">
+                <property name="visible">True</property>
+                <property name="can-focus">True</property>
+                <property name="hexpand">True</property>
+              </object>
+              <packing>
+                <property name="left-attach">1</property>
+                <property name="top-attach">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkEntry" id="notes_entry">
+                <property name="visible">True</property>
+                <property name="can-focus">True</property>
+                <property name="hexpand">True</property>
+              </object>
+              <packing>
+                <property name="left-attach">1</property>
+                <property name="top-attach">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkEntry" id="memo_entry">
+                <property name="visible">True</property>
+                <property name="can-focus">True</property>
+                <property name="hexpand">True</property>
+              </object>
+              <packing>
+                <property name="left-attach">1</property>
+                <property name="top-attach">2</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+    <action-widgets>
+      <action-widget response="-6">button1</action-widget>
+      <action-widget response="-5">button2</action-widget>
+    </action-widgets>
+  </object>
   <object class="GtkBox" id="transaction_matcher_content">
     <property name="visible">True</property>
     <property name="can-focus">False</property>
diff --git a/gnucash/import-export/import-main-matcher.c b/gnucash/import-export/import-main-matcher.c
index 6499a51ad..a9f6f4382 100644
--- a/gnucash/import-export/import-main-matcher.c
+++ b/gnucash/import-export/import-main-matcher.c
@@ -96,8 +96,10 @@ enum downloaded_cols
     DOWNLOADED_COL_AMOUNT_DOUBLE, // used only for sorting
     DOWNLOADED_COL_DESCRIPTION,
     DOWNLOADED_COL_DESCRIPTION_ORIGINAL,
+    DOWNLOADED_COL_DESCRIPTION_STYLE,
     DOWNLOADED_COL_MEMO,
     DOWNLOADED_COL_MEMO_ORIGINAL,
+    DOWNLOADED_COL_MEMO_STYLE,
     DOWNLOADED_COL_NOTES_ORIGINAL,
     DOWNLOADED_COL_ACTION_ADD,
     DOWNLOADED_COL_ACTION_CLEAR,
@@ -932,62 +934,48 @@ input_new_fields (GtkWidget *parent, RowInfo *info, GtkTreeStore *store,
                   gboolean edit_desc, gboolean edit_notes, gboolean edit_memo,
                   char **new_desc, char **new_notes, char **new_memo)
 {
-    GtkWidget *desc_entry, *notes_entry, *memo_entry, *label, *grid;
-    GtkWidget *dialog =
-        gtk_dialog_new_with_buttons ("Edit imported transaction details",
-                                     GTK_WINDOW (parent),
-                                     GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
-                                     _("_OK"), GTK_RESPONSE_ACCEPT,
-                                     _("_Cancel"), GTK_RESPONSE_REJECT,
-                                     NULL);
-    GtkWidget *content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
-    gboolean retval;
+    GtkWidget  *desc_entry, *notes_entry, *memo_entry, *label;
+    GtkWidget  *dialog;
+    GtkBuilder *builder;
+    gboolean    retval = FALSE;
+
+    builder = gtk_builder_new ();
+    gnc_builder_add_from_file (builder, "dialog-import.glade", "transaction_edit_dialog");
 
-    grid = gtk_grid_new ();
+    dialog = GTK_WIDGET(gtk_builder_get_object (builder, "transaction_edit_dialog"));
 
-    label = gtk_label_new ("Description");
-    gtk_grid_attach (GTK_GRID (grid), label, 0, 0, 1, 1);
+    desc_entry = GTK_WIDGET(gtk_builder_get_object (builder, "desc_entry"));
+    memo_entry = GTK_WIDGET(gtk_builder_get_object (builder, "memo_entry"));
+    notes_entry = GTK_WIDGET(gtk_builder_get_object (builder, "notes_entry"));
 
-    desc_entry = gtk_entry_new ();
-    gtk_widget_set_halign (desc_entry, GTK_ALIGN_START);
     gtk_widget_set_sensitive (desc_entry, edit_desc);
     gtk_entry_set_text (GTK_ENTRY (desc_entry), xaccTransGetDescription (info->trans));
-    gtk_grid_attach (GTK_GRID (grid), desc_entry, 1, 0, 1, 1);
 
-    label = gtk_label_new ("Notes");
-    gtk_grid_attach (GTK_GRID (grid), label, 0, 1, 1, 1);
-
-    notes_entry = gtk_entry_new ();
-    gtk_widget_set_halign (notes_entry, GTK_ALIGN_START);
     gtk_widget_set_sensitive (notes_entry, edit_notes);
     gtk_entry_set_text (GTK_ENTRY (notes_entry), xaccTransGetNotes (info->trans));
-    gtk_grid_attach (GTK_GRID (grid), notes_entry, 1, 1, 1, 1);
-
-    label = gtk_label_new ("Memo");
-    gtk_grid_attach (GTK_GRID (grid), label, 0, 2, 1, 1);
 
-    memo_entry = gtk_entry_new ();
-    gtk_widget_set_halign (memo_entry, GTK_ALIGN_START);
     gtk_widget_set_sensitive (memo_entry, edit_memo);
     gtk_entry_set_text (GTK_ENTRY (memo_entry), xaccSplitGetMemo (info->split));
-    gtk_grid_attach (GTK_GRID (grid), memo_entry, 1, 2, 1, 1);
 
-    gtk_container_add_with_properties (GTK_CONTAINER (content_area), grid,
-                                       "position", 1, NULL);
+    gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (parent));
 
     // run the dialog
-    gtk_widget_show_all (grid);
+    gtk_widget_show_all (dialog);
 
-    retval = (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT);
-
-    if (retval)
+    switch (gtk_dialog_run (GTK_DIALOG(dialog)))
     {
+    case GTK_RESPONSE_OK:
         *new_desc = g_strdup (gtk_entry_get_text (GTK_ENTRY (desc_entry)));
         *new_notes = g_strdup (gtk_entry_get_text (GTK_ENTRY (notes_entry)));
         *new_memo = g_strdup (gtk_entry_get_text (GTK_ENTRY (memo_entry)));
+        retval = TRUE;
+        break;
+    default:
+        break;
     }
 
     gtk_widget_destroy (dialog);
+    g_object_unref (G_OBJECT(builder));
     return retval;
 }
 
@@ -1027,8 +1015,11 @@ gnc_gen_trans_edit_fields (GtkMenuItem *menuitem, GNCImportMainMatcher *info)
             RowInfo *row = n->data;
             if (info->edit_desc)
             {
+                guint64 style = g_strcmp0 (new_desc, row->orig_desc) ?
+                    PANGO_STYLE_ITALIC : PANGO_STYLE_NORMAL;
                 gtk_tree_store_set (store, &row->iter,
                                     DOWNLOADED_COL_DESCRIPTION, new_desc,
+                                    DOWNLOADED_COL_DESCRIPTION_STYLE, style,
                                     -1);
                 xaccTransSetDescription (row->trans, new_desc);
             }
@@ -1038,8 +1029,11 @@ gnc_gen_trans_edit_fields (GtkMenuItem *menuitem, GNCImportMainMatcher *info)
 
             if (info->edit_memo)
             {
+                guint64 style = g_strcmp0 (new_memo, row->orig_memo) ?
+                    PANGO_STYLE_ITALIC : PANGO_STYLE_NORMAL;
                 gtk_tree_store_set (store, &row->iter,
                                     DOWNLOADED_COL_MEMO, new_memo,
+                                    DOWNLOADED_COL_MEMO_STYLE, style,
                                     -1);
                 xaccSplitSetMemo (row->split, new_memo);
             }
@@ -1060,7 +1054,7 @@ gnc_gen_trans_reset_edits_cb (GtkMenuItem *menuitem, GNCImportMainMatcher *info)
     GtkTreeModel *model;
     GtkTreeStore *store;
     GtkTreeSelection *selection;
-    GList *selected_rows;
+    GList *selected_rows, *row_info_list;
 
     g_return_if_fail (info != NULL);
     ENTER("gnc_gen_trans_reset_edits_cb");
@@ -1086,6 +1080,8 @@ gnc_gen_trans_reset_edits_cb (GtkMenuItem *menuitem, GNCImportMainMatcher *info)
         gtk_tree_store_set (store, &rowinfo->iter,
                             DOWNLOADED_COL_DESCRIPTION, rowinfo->orig_desc,
                             DOWNLOADED_COL_MEMO, rowinfo->orig_memo,
+                            DOWNLOADED_COL_DESCRIPTION_STYLE, PANGO_STYLE_NORMAL,
+                            DOWNLOADED_COL_MEMO_STYLE, PANGO_STYLE_NORMAL,
                             -1);
         rowinfo_free (rowinfo);
     };
@@ -1186,7 +1182,7 @@ gnc_gen_trans_view_popup_menu (GtkTreeView *treeview,
     GtkTreeModel *model;
     GtkTreeSelection *selection;
     GList *selected_rows;
-    const char *desc, *memo, *notes;
+    const char *desc = NULL, *memo = NULL, *notes = NULL;
     gboolean has_edits = FALSE;
 
     ENTER ("");
@@ -1346,6 +1342,12 @@ add_text_column (GtkTreeView *view, const gchar *title, int col_num, gboolean el
     else
         gtk_tree_view_column_set_sort_column_id (column, col_num);
 
+    if (col_num == DOWNLOADED_COL_DESCRIPTION)
+        gtk_tree_view_column_add_attribute (column, renderer, "style", DOWNLOADED_COL_DESCRIPTION_STYLE);
+
+    if (col_num == DOWNLOADED_COL_MEMO)
+        gtk_tree_view_column_add_attribute (column, renderer, "style", DOWNLOADED_COL_MEMO_STYLE);
+
     g_object_set (G_OBJECT(column),
                   "reorderable", TRUE,
                   "resizable", TRUE,
@@ -1389,8 +1391,9 @@ gnc_gen_trans_init_view (GNCImportMainMatcher *info,
     view = info->view;
     store = gtk_tree_store_new (NUM_DOWNLOADED_COLS, G_TYPE_STRING, G_TYPE_INT64,
                                 G_TYPE_STRING, G_TYPE_STRING, G_TYPE_DOUBLE,
-                                G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
-                                G_TYPE_STRING, G_TYPE_STRING, G_TYPE_BOOLEAN,
+                                G_TYPE_STRING, G_TYPE_STRING, G_TYPE_UINT64, //description stuff
+                                G_TYPE_STRING, G_TYPE_STRING, G_TYPE_UINT64, //memo stuff
+                                G_TYPE_STRING, G_TYPE_BOOLEAN,
                                 G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, G_TYPE_STRING,
                                 GDK_TYPE_PIXBUF, G_TYPE_POINTER, G_TYPE_STRING,
                                 G_TYPE_BOOLEAN);
@@ -1720,9 +1723,19 @@ update_child_row (GNCImportMatchInfo *sel_match, GtkTreeModel *model, GtkTreeIte
     ro_text = xaccPrintAmount (xaccSplitGetAmount (sel_match->split),
                                gnc_split_amount_print_info (sel_match->split, TRUE));
 
-    gtk_tree_store_set (store, &child, DOWNLOADED_COL_AMOUNT, ro_text, -1);
-    gtk_tree_store_set (store, &child, DOWNLOADED_COL_MEMO, memo, -1);
-    gtk_tree_store_set (store, &child, DOWNLOADED_COL_DESCRIPTION, desc, -1);
+    gtk_tree_store_set (store, &child,
+                        DOWNLOADED_COL_AMOUNT, ro_text,
+                        -1);
+
+    gtk_tree_store_set (store, &child,
+                        DOWNLOADED_COL_MEMO, memo,
+                        DOWNLOADED_COL_MEMO_STYLE, PANGO_STYLE_NORMAL,
+                        -1);
+
+    gtk_tree_store_set (store, &child,
+                        DOWNLOADED_COL_DESCRIPTION, desc,
+                        DOWNLOADED_COL_DESCRIPTION_STYLE, PANGO_STYLE_NORMAL,
+                        -1);
 
     gtk_tree_store_set (store, &child, DOWNLOADED_COL_ENABLE, FALSE, -1);
     g_free (text);

commit fb6091fb3e7b48488fd398d2d01e0e7e65575dfa
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Wed Jul 13 23:55:46 2022 +0800

    [import-main-matcher] 1 dialog to edit all 3 fields

diff --git a/gnucash/import-export/import-main-matcher.c b/gnucash/import-export/import-main-matcher.c
index 1ebea2504..6499a51ad 100644
--- a/gnucash/import-export/import-main-matcher.c
+++ b/gnucash/import-export/import-main-matcher.c
@@ -80,6 +80,11 @@ struct _main_matcher_info
     GSList* temp_trans_list;  // Temporary list of imported transactions
     GHashTable* acct_id_hash; // Hash table, per account, of list of transaction IDs.
     GSList* edited_accounts;  // List of accounts currently edited.
+
+    /* only when editing fields */
+    gboolean edit_desc;
+    gboolean edit_notes;
+    gboolean edit_memo;
 };
 
 enum downloaded_cols
@@ -889,13 +894,6 @@ gnc_gen_trans_assign_transfer_account_to_selection_cb (GtkMenuItem *menuitem,
     LEAVE("");
 }
 
-typedef enum
-{
-    DESCRIPTION,
-    MEMO,
-    NOTES,
-} edit_field;
-
 typedef struct
 {
     Split *split;
@@ -929,16 +927,79 @@ static RowInfo * row_get_info (gpointer row, GNCImportMainMatcher *info)
     return retval;
 }
 
+static gboolean
+input_new_fields (GtkWidget *parent, RowInfo *info, GtkTreeStore *store,
+                  gboolean edit_desc, gboolean edit_notes, gboolean edit_memo,
+                  char **new_desc, char **new_notes, char **new_memo)
+{
+    GtkWidget *desc_entry, *notes_entry, *memo_entry, *label, *grid;
+    GtkWidget *dialog =
+        gtk_dialog_new_with_buttons ("Edit imported transaction details",
+                                     GTK_WINDOW (parent),
+                                     GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
+                                     _("_OK"), GTK_RESPONSE_ACCEPT,
+                                     _("_Cancel"), GTK_RESPONSE_REJECT,
+                                     NULL);
+    GtkWidget *content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
+    gboolean retval;
+
+    grid = gtk_grid_new ();
+
+    label = gtk_label_new ("Description");
+    gtk_grid_attach (GTK_GRID (grid), label, 0, 0, 1, 1);
+
+    desc_entry = gtk_entry_new ();
+    gtk_widget_set_halign (desc_entry, GTK_ALIGN_START);
+    gtk_widget_set_sensitive (desc_entry, edit_desc);
+    gtk_entry_set_text (GTK_ENTRY (desc_entry), xaccTransGetDescription (info->trans));
+    gtk_grid_attach (GTK_GRID (grid), desc_entry, 1, 0, 1, 1);
+
+    label = gtk_label_new ("Notes");
+    gtk_grid_attach (GTK_GRID (grid), label, 0, 1, 1, 1);
+
+    notes_entry = gtk_entry_new ();
+    gtk_widget_set_halign (notes_entry, GTK_ALIGN_START);
+    gtk_widget_set_sensitive (notes_entry, edit_notes);
+    gtk_entry_set_text (GTK_ENTRY (notes_entry), xaccTransGetNotes (info->trans));
+    gtk_grid_attach (GTK_GRID (grid), notes_entry, 1, 1, 1, 1);
+
+    label = gtk_label_new ("Memo");
+    gtk_grid_attach (GTK_GRID (grid), label, 0, 2, 1, 1);
+
+    memo_entry = gtk_entry_new ();
+    gtk_widget_set_halign (memo_entry, GTK_ALIGN_START);
+    gtk_widget_set_sensitive (memo_entry, edit_memo);
+    gtk_entry_set_text (GTK_ENTRY (memo_entry), xaccSplitGetMemo (info->split));
+    gtk_grid_attach (GTK_GRID (grid), memo_entry, 1, 2, 1, 1);
+
+    gtk_container_add_with_properties (GTK_CONTAINER (content_area), grid,
+                                       "position", 1, NULL);
+
+    // run the dialog
+    gtk_widget_show_all (grid);
+
+    retval = (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT);
+
+    if (retval)
+    {
+        *new_desc = g_strdup (gtk_entry_get_text (GTK_ENTRY (desc_entry)));
+        *new_notes = g_strdup (gtk_entry_get_text (GTK_ENTRY (notes_entry)));
+        *new_memo = g_strdup (gtk_entry_get_text (GTK_ENTRY (memo_entry)));
+    }
+
+    gtk_widget_destroy (dialog);
+    return retval;
+}
+
 static void
-gnc_gen_trans_edit_fields (GtkMenuItem *menuitem, GNCImportMainMatcher *info,
-                           edit_field field)
+gnc_gen_trans_edit_fields (GtkMenuItem *menuitem, GNCImportMainMatcher *info)
 {
     GtkTreeView *treeview;
     GtkTreeSelection *selection;
     GtkTreeModel *model;
     GList *selected_rows, *row_info_list;
     GtkTreeStore* store;
-    Transaction* trans;
+    char *new_desc, *new_notes, *new_memo;
 
     g_return_if_fail (info != NULL);
     ENTER("assign_transfer_account_to_selection_cb");
@@ -956,86 +1017,42 @@ gnc_gen_trans_edit_fields (GtkMenuItem *menuitem, GNCImportMainMatcher *info,
     }
 
     row_info_list = gnc_g_list_map (selected_rows, (GncGMapFunc) row_get_info, info);
-    trans = ((RowInfo*)row_info_list->data)->trans;
 
-    switch (field)
+    if (input_new_fields (info->main_widget, row_info_list->data, store,
+                          info->edit_desc, info->edit_notes, info->edit_memo,
+                          &new_desc, &new_notes, &new_memo))
     {
-        case DESCRIPTION:
+        for (GList *n = row_info_list; n; n = g_list_next (n))
         {
-            char* new_field =
-                gnc_input_dialog_with_entry(info->main_widget, "",
-                                            _("Enter new Description"),
-                                            xaccTransGetDescription (trans));
-            if (!new_field) break;
-            for (GList *n = row_info_list; n; n = g_list_next (n))
+            RowInfo *row = n->data;
+            if (info->edit_desc)
             {
-                RowInfo *info = n->data;
-                xaccTransSetDescription (info->trans, new_field);
-                gtk_tree_store_set (store, &info->iter,
-                                    DOWNLOADED_COL_DESCRIPTION, new_field,
+                gtk_tree_store_set (store, &row->iter,
+                                    DOWNLOADED_COL_DESCRIPTION, new_desc,
                                     -1);
+                xaccTransSetDescription (row->trans, new_desc);
             }
-            g_free (new_field);
-            break;
-        }
-        case MEMO:
-        {
-            Split *first_split = xaccTransGetSplit (trans, 0);
-            char *new_field =
-                gnc_input_dialog_with_entry(info->main_widget, "",
-                                            _("Enter new Memo"),
-                                            xaccSplitGetMemo (first_split));
-            if (!new_field) break;
-            for (GList *n = row_info_list; n; n = g_list_next (n))
+
+            if (info->edit_notes)
+                xaccTransSetNotes (row->trans, new_notes);
+
+            if (info->edit_memo)
             {
-                RowInfo *info = n->data;
-                xaccSplitSetMemo (info->split, new_field);
-                gtk_tree_store_set (store, &info->iter,
-                                    DOWNLOADED_COL_MEMO, new_field,
+                gtk_tree_store_set (store, &row->iter,
+                                    DOWNLOADED_COL_MEMO, new_memo,
                                     -1);
+                xaccSplitSetMemo (row->split, new_memo);
             }
-            g_free (new_field);
-            break;
-        }
-        case NOTES:
-        {
-            char* new_field =
-                gnc_input_dialog_with_entry(info->main_widget, "",
-                                            _("Enter new Notes"),
-                                            xaccTransGetNotes (trans));
-            if (!new_field) break;
-            for (GList *n = row_info_list; n; n = g_list_next (n))
-            {
-                RowInfo *info = n->data;
-                xaccTransSetNotes (info->trans, new_field);
-            }
-            g_free (new_field);
-            break;
         }
+        g_free (new_desc);
+        g_free (new_memo);
+        g_free (new_notes);
     }
     g_list_free_full (row_info_list, (GDestroyNotify)rowinfo_free);
     g_list_free_full (selected_rows, (GDestroyNotify)gtk_tree_path_free);
     LEAVE("");
 }
 
-static void
-gnc_gen_trans_edit_description_cb (GtkMenuItem *menuitem, GNCImportMainMatcher *info)
-{
-    gnc_gen_trans_edit_fields (menuitem, info, DESCRIPTION);
-}
-
-static void
-gnc_gen_trans_edit_memo_cb (GtkMenuItem *menuitem, GNCImportMainMatcher *info)
-{
-    gnc_gen_trans_edit_fields (menuitem, info, MEMO);
-}
-
-static void
-gnc_gen_trans_edit_notes_cb (GtkMenuItem *menuitem, GNCImportMainMatcher *info)
-{
-    gnc_gen_trans_edit_fields (menuitem, info, NOTES);
-}
-
 static void
 gnc_gen_trans_reset_edits_cb (GtkMenuItem *menuitem, GNCImportMainMatcher *info)
 {
@@ -1170,7 +1187,6 @@ gnc_gen_trans_view_popup_menu (GtkTreeView *treeview,
     GtkTreeSelection *selection;
     GList *selected_rows;
     const char *desc, *memo, *notes;
-    gboolean edit_desc = TRUE, edit_notes = TRUE, edit_memo = TRUE;
     gboolean has_edits = FALSE;
 
     ENTER ("");
@@ -1187,7 +1203,14 @@ gnc_gen_trans_view_popup_menu (GtkTreeView *treeview,
     model = gtk_tree_view_get_model (treeview);
     selection = gtk_tree_view_get_selection (treeview);
     selected_rows = gtk_tree_selection_get_selected_rows (selection, &model);
-    for (GList *n = selected_rows; (edit_desc || edit_notes || edit_memo) && n;
+
+    /* initialise */
+    info->edit_desc = TRUE;
+    info->edit_notes = TRUE;
+    info->edit_memo = TRUE;
+
+    for (GList *n = selected_rows;
+         (!has_edits || info->edit_desc || info->edit_notes || info->edit_memo) && n;
          n = g_list_next(n))
     {
         RowInfo *rowinfo = row_get_info (n->data, info);
@@ -1206,33 +1229,20 @@ gnc_gen_trans_view_popup_menu (GtkTreeView *treeview,
             rowinfo_free (rowinfo);
             continue;
         }
-        if (edit_desc && g_strcmp0 (desc, xaccTransGetDescription (rowinfo->trans)))
-            edit_desc = FALSE;
-        if (edit_notes && g_strcmp0 (notes, xaccTransGetNotes (rowinfo->trans)))
-            edit_notes = FALSE;
-        if (edit_memo && g_strcmp0 (memo, xaccSplitGetMemo (rowinfo->split)))
-            edit_memo = FALSE;
+        if (info->edit_desc && g_strcmp0 (desc, xaccTransGetDescription (rowinfo->trans)))
+            info->edit_desc = FALSE;
+        if (info->edit_notes && g_strcmp0 (notes, xaccTransGetNotes (rowinfo->trans)))
+            info->edit_notes = FALSE;
+        if (info->edit_memo && g_strcmp0 (memo, xaccSplitGetMemo (rowinfo->split)))
+            info->edit_memo = FALSE;
         rowinfo_free (rowinfo);
     }
 
-    menuitem = gtk_menu_item_new_with_label (_("Edit description."));
-    gtk_widget_set_sensitive (menuitem, edit_desc);
-    g_signal_connect (menuitem, "activate",
-                      G_CALLBACK (gnc_gen_trans_edit_description_cb),
-                      info);
-    gtk_menu_shell_append (GTK_MENU_SHELL(menu), menuitem);
-
-    menuitem = gtk_menu_item_new_with_label (_("Edit memo."));
-    gtk_widget_set_sensitive (menuitem, edit_memo);
-    g_signal_connect (menuitem, "activate",
-                      G_CALLBACK (gnc_gen_trans_edit_memo_cb),
-                      info);
-    gtk_menu_shell_append (GTK_MENU_SHELL(menu), menuitem);
-
-    menuitem = gtk_menu_item_new_with_label (_("Edit notes."));
-    gtk_widget_set_sensitive (menuitem, edit_notes);
+    menuitem = gtk_menu_item_new_with_label (_("Edit description, notes, memo."));
+    gtk_widget_set_sensitive (menuitem,
+                              info->edit_desc || info->edit_notes || info->edit_memo);
     g_signal_connect (menuitem, "activate",
-                      G_CALLBACK (gnc_gen_trans_edit_notes_cb),
+                      G_CALLBACK (gnc_gen_trans_edit_fields),
                       info);
     gtk_menu_shell_append (GTK_MENU_SHELL(menu), menuitem);
 

commit 3db8c56a909d033576ea5822f0213948721f08e5
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Tue Jul 12 23:00:11 2022 +0800

    [import-main-matcher] save orig desc/notes/memo to reset edits
    
    adds menu item "Reset edits." which resets imported transactions'
    desc/notes/memo back to the original imported strings.

diff --git a/gnucash/import-export/import-main-matcher.c b/gnucash/import-export/import-main-matcher.c
index cf3ee41a4..1ebea2504 100644
--- a/gnucash/import-export/import-main-matcher.c
+++ b/gnucash/import-export/import-main-matcher.c
@@ -90,7 +90,10 @@ enum downloaded_cols
     DOWNLOADED_COL_AMOUNT,
     DOWNLOADED_COL_AMOUNT_DOUBLE, // used only for sorting
     DOWNLOADED_COL_DESCRIPTION,
+    DOWNLOADED_COL_DESCRIPTION_ORIGINAL,
     DOWNLOADED_COL_MEMO,
+    DOWNLOADED_COL_MEMO_ORIGINAL,
+    DOWNLOADED_COL_NOTES_ORIGINAL,
     DOWNLOADED_COL_ACTION_ADD,
     DOWNLOADED_COL_ACTION_CLEAR,
     DOWNLOADED_COL_ACTION_UPDATE,
@@ -898,10 +901,14 @@ typedef struct
     Split *split;
     Transaction *trans;
     GtkTreeIter iter;
+    char *orig_desc, *orig_notes, *orig_memo;
 } RowInfo;
 
 static void rowinfo_free (RowInfo* info)
 {
+    g_free (info->orig_desc);
+    g_free (info->orig_notes);
+    g_free (info->orig_memo);
     g_free (info);
 }
 
@@ -911,7 +918,12 @@ static RowInfo * row_get_info (gpointer row, GNCImportMainMatcher *info)
     GtkTreeModel *model = gtk_tree_view_get_model (info->view);
     RowInfo *retval = g_new (RowInfo, 1);
     gtk_tree_model_get_iter (model, &retval->iter, row);
-    gtk_tree_model_get (model, &retval->iter, DOWNLOADED_COL_DATA, &trans_info, -1);
+    gtk_tree_model_get (model, &retval->iter,
+                        DOWNLOADED_COL_DATA, &trans_info,
+                        DOWNLOADED_COL_DESCRIPTION_ORIGINAL, &retval->orig_desc,
+                        DOWNLOADED_COL_NOTES_ORIGINAL, &retval->orig_notes,
+                        DOWNLOADED_COL_MEMO_ORIGINAL, &retval->orig_memo,
+                        -1);
     retval->trans = gnc_import_TransInfo_get_trans (trans_info);
     retval->split = gnc_import_TransInfo_get_fsplit (trans_info);
     return retval;
@@ -1024,6 +1036,46 @@ gnc_gen_trans_edit_notes_cb (GtkMenuItem *menuitem, GNCImportMainMatcher *info)
     gnc_gen_trans_edit_fields (menuitem, info, NOTES);
 }
 
+static void
+gnc_gen_trans_reset_edits_cb (GtkMenuItem *menuitem, GNCImportMainMatcher *info)
+{
+    GtkTreeView *treeview;
+    GtkTreeModel *model;
+    GtkTreeStore *store;
+    GtkTreeSelection *selection;
+    GList *selected_rows;
+
+    g_return_if_fail (info != NULL);
+    ENTER("gnc_gen_trans_reset_edits_cb");
+
+    treeview = GTK_TREE_VIEW(info->view);
+    model = gtk_tree_view_get_model (treeview);
+    store  = GTK_TREE_STORE (model);
+    selection = gtk_tree_view_get_selection (treeview);
+    selected_rows = gtk_tree_selection_get_selected_rows (selection, &model);
+
+    if (!selected_rows)
+    {
+        LEAVE ("No selected rows");
+        return;
+    }
+
+    for (GList *n = selected_rows; n; n = g_list_next (n))
+    {
+        RowInfo *rowinfo = row_get_info (n->data, info);
+        xaccTransSetDescription (rowinfo->trans, rowinfo->orig_desc);
+        xaccTransSetNotes (rowinfo->trans, rowinfo->orig_notes);
+        xaccSplitSetMemo (rowinfo->split, rowinfo->orig_memo);
+        gtk_tree_store_set (store, &rowinfo->iter,
+                            DOWNLOADED_COL_DESCRIPTION, rowinfo->orig_desc,
+                            DOWNLOADED_COL_MEMO, rowinfo->orig_memo,
+                            -1);
+        rowinfo_free (rowinfo);
+    };
+    g_list_free_full (selected_rows, (GDestroyNotify)gtk_tree_path_free);
+    LEAVE("");
+}
+
 static void
 gnc_gen_trans_row_activated_cb (GtkTreeView *treeview,
                                 GtkTreePath *path,
@@ -1119,6 +1171,7 @@ gnc_gen_trans_view_popup_menu (GtkTreeView *treeview,
     GList *selected_rows;
     const char *desc, *memo, *notes;
     gboolean edit_desc = TRUE, edit_notes = TRUE, edit_memo = TRUE;
+    gboolean has_edits = FALSE;
 
     ENTER ("");
     menu = gtk_menu_new();
@@ -1138,6 +1191,13 @@ gnc_gen_trans_view_popup_menu (GtkTreeView *treeview,
          n = g_list_next(n))
     {
         RowInfo *rowinfo = row_get_info (n->data, info);
+
+        if (!has_edits &&
+            (g_strcmp0 (xaccSplitGetMemo (rowinfo->split), rowinfo->orig_memo) ||
+             g_strcmp0 (xaccTransGetNotes (rowinfo->trans), rowinfo->orig_notes) ||
+             g_strcmp0 (xaccTransGetDescription (rowinfo->trans), rowinfo->orig_desc)))
+            has_edits = TRUE;
+
         if (!n->prev)       /* only the first row */
         {
             desc = xaccTransGetDescription (rowinfo->trans);
@@ -1176,6 +1236,13 @@ gnc_gen_trans_view_popup_menu (GtkTreeView *treeview,
                       info);
     gtk_menu_shell_append (GTK_MENU_SHELL(menu), menuitem);
 
+    menuitem = gtk_menu_item_new_with_label (_("Reset edits."));
+    gtk_widget_set_sensitive (menuitem, has_edits);
+    g_signal_connect (menuitem, "activate",
+                      G_CALLBACK (gnc_gen_trans_reset_edits_cb),
+                      info);
+    gtk_menu_shell_append (GTK_MENU_SHELL(menu), menuitem);
+
     gtk_widget_show_all (menu);
     /* Note: event can be NULL here when called from view_onPopupMenu; */
     gtk_menu_popup_at_pointer (GTK_MENU(menu), (GdkEvent*)event);
@@ -1312,6 +1379,7 @@ gnc_gen_trans_init_view (GNCImportMainMatcher *info,
     view = info->view;
     store = gtk_tree_store_new (NUM_DOWNLOADED_COLS, G_TYPE_STRING, G_TYPE_INT64,
                                 G_TYPE_STRING, G_TYPE_STRING, G_TYPE_DOUBLE,
+                                G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
                                 G_TYPE_STRING, G_TYPE_STRING, G_TYPE_BOOLEAN,
                                 G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, G_TYPE_STRING,
                                 GDK_TYPE_PIXBUF, G_TYPE_POINTER, G_TYPE_STRING,
@@ -1726,13 +1794,22 @@ refresh_model_row (GNCImportMainMatcher *gui,
     gtk_tree_store_set (store, iter, DOWNLOADED_COL_AMOUNT, ro_text, -1);
     gtk_tree_store_set (store, iter, DOWNLOADED_COL_AMOUNT_DOUBLE, gnc_numeric_to_double (amount), -1);
 
+    /* Notes */
+    ro_text = xaccTransGetNotes (gnc_import_TransInfo_get_trans (info));
+    gtk_tree_store_set (store, iter, DOWNLOADED_COL_NOTES_ORIGINAL, ro_text, -1);
+
     /*Description*/
     ro_text = xaccTransGetDescription (gnc_import_TransInfo_get_trans (info) );
-    gtk_tree_store_set (store, iter, DOWNLOADED_COL_DESCRIPTION, ro_text, -1);
-
+    gtk_tree_store_set (store, iter,
+                        DOWNLOADED_COL_DESCRIPTION, ro_text,
+                        DOWNLOADED_COL_DESCRIPTION_ORIGINAL, ro_text,
+                        -1);
     /*Memo*/
     ro_text = xaccSplitGetMemo (split);
-    gtk_tree_store_set (store, iter, DOWNLOADED_COL_MEMO, ro_text, -1);
+    gtk_tree_store_set (store, iter,
+                        DOWNLOADED_COL_MEMO, ro_text,
+                        DOWNLOADED_COL_MEMO_ORIGINAL, ro_text,
+                        -1);
 
     /*Actions*/
 
@@ -2203,10 +2280,14 @@ query_tooltip_tree_view_cb (GtkWidget *widget, gint x, gint y,
         switch (num_col)
         {
         case DOWNLOADED_COL_DESCRIPTION:
-            gtk_tree_model_get (model, &iter, DOWNLOADED_COL_DESCRIPTION, &tooltip_text, -1);
+            gtk_tree_model_get (model, &iter,
+                                DOWNLOADED_COL_DESCRIPTION_ORIGINAL, &tooltip_text,
+                                -1);
             break;
         case DOWNLOADED_COL_MEMO:
-            gtk_tree_model_get (model, &iter, DOWNLOADED_COL_MEMO, &tooltip_text, -1);
+            gtk_tree_model_get (model, &iter,
+                                DOWNLOADED_COL_MEMO_ORIGINAL, &tooltip_text,
+                                -1);
             break;
         default:
             break;

commit 6f74d4e3ed63753f85f2c0aea78db6e468083514
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Sat Jul 9 21:36:46 2022 +0800

    [import-main-matcher] show Edit menuitem, disable if disallowed

diff --git a/gnucash/import-export/import-main-matcher.c b/gnucash/import-export/import-main-matcher.c
index 9b0395eed..cf3ee41a4 100644
--- a/gnucash/import-export/import-main-matcher.c
+++ b/gnucash/import-export/import-main-matcher.c
@@ -1114,7 +1114,6 @@ gnc_gen_trans_view_popup_menu (GtkTreeView *treeview,
                                GNCImportMainMatcher *info)
 {
     GtkWidget *menu, *menuitem;
-    GdkEventButton *event_button;
     GtkTreeModel *model;
     GtkTreeSelection *selection;
     GList *selected_rows;
@@ -1156,40 +1155,28 @@ gnc_gen_trans_view_popup_menu (GtkTreeView *treeview,
         rowinfo_free (rowinfo);
     }
 
-    if (edit_desc)
-    {
-        menuitem = gtk_menu_item_new_with_label (
-                                                 _("Edit description."));
-        g_signal_connect (menuitem, "activate",
-                          G_CALLBACK (gnc_gen_trans_edit_description_cb),
-                          info);
-        DEBUG("Callback to edit description");
-        gtk_menu_shell_append (GTK_MENU_SHELL(menu), menuitem);
-    }
+    menuitem = gtk_menu_item_new_with_label (_("Edit description."));
+    gtk_widget_set_sensitive (menuitem, edit_desc);
+    g_signal_connect (menuitem, "activate",
+                      G_CALLBACK (gnc_gen_trans_edit_description_cb),
+                      info);
+    gtk_menu_shell_append (GTK_MENU_SHELL(menu), menuitem);
 
-    if (edit_memo)
-    {
-        menuitem = gtk_menu_item_new_with_label (
-                                                 _("Edit memo."));
-        g_signal_connect (menuitem, "activate",
-                          G_CALLBACK (gnc_gen_trans_edit_memo_cb),
-                          info);
-        DEBUG("Callback to edit memo");
-        gtk_menu_shell_append (GTK_MENU_SHELL(menu), menuitem);
-    }
+    menuitem = gtk_menu_item_new_with_label (_("Edit memo."));
+    gtk_widget_set_sensitive (menuitem, edit_memo);
+    g_signal_connect (menuitem, "activate",
+                      G_CALLBACK (gnc_gen_trans_edit_memo_cb),
+                      info);
+    gtk_menu_shell_append (GTK_MENU_SHELL(menu), menuitem);
+
+    menuitem = gtk_menu_item_new_with_label (_("Edit notes."));
+    gtk_widget_set_sensitive (menuitem, edit_notes);
+    g_signal_connect (menuitem, "activate",
+                      G_CALLBACK (gnc_gen_trans_edit_notes_cb),
+                      info);
+    gtk_menu_shell_append (GTK_MENU_SHELL(menu), menuitem);
 
-    if (edit_notes)
-    {
-        menuitem = gtk_menu_item_new_with_label (
-                                                 _("Edit notes."));
-        g_signal_connect (menuitem, "activate",
-                          G_CALLBACK (gnc_gen_trans_edit_notes_cb),
-                          info);
-        DEBUG("Callback to edit notes");
-        gtk_menu_shell_append (GTK_MENU_SHELL(menu), menuitem);
-    }
     gtk_widget_show_all (menu);
-    event_button = (GdkEventButton *) event;
     /* Note: event can be NULL here when called from view_onPopupMenu; */
     gtk_menu_popup_at_pointer (GTK_MENU(menu), (GdkEvent*)event);
 

commit b10712951c4a6054ea5596323f9d84428a84fe4e
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Sat Jul 9 20:30:01 2022 +0800

    [import-main-matcher] gnc_gen_trans_view_popup_menu: show_edit_actions obsolete

diff --git a/gnucash/import-export/import-main-matcher.c b/gnucash/import-export/import-main-matcher.c
index d70029374..9b0395eed 100644
--- a/gnucash/import-export/import-main-matcher.c
+++ b/gnucash/import-export/import-main-matcher.c
@@ -130,8 +130,7 @@ static void gnc_gen_trans_assign_transfer_account_to_selection_cb (GtkMenuItem *
                                                                    GNCImportMainMatcher *info);
 static void gnc_gen_trans_view_popup_menu (GtkTreeView *treeview,
                                            GdkEvent *event,
-                                           GNCImportMainMatcher *info,
-                                           gboolean show_edit_actions);
+                                           GNCImportMainMatcher *info);
 static gboolean gnc_gen_trans_onButtonPressed_cb (GtkTreeView *treeview,
                                                   GdkEvent *event,
                                                   GNCImportMainMatcher *info);
@@ -1112,8 +1111,7 @@ gnc_gen_trans_row_changed_cb (GtkTreeSelection *selection,
 static void
 gnc_gen_trans_view_popup_menu (GtkTreeView *treeview,
                                GdkEvent *event,
-                               GNCImportMainMatcher *info,
-                               gboolean show_edit_actions)
+                               GNCImportMainMatcher *info)
 {
     GtkWidget *menu, *menuitem;
     GdkEventButton *event_button;
@@ -1227,7 +1225,7 @@ gnc_gen_trans_onButtonPressed_cb (GtkTreeView *treeview,
                 GtkTreeModel *model;
                 selected = gtk_tree_selection_get_selected_rows (selection, &model);
                 if (get_action_for_path (selected->data, model) == GNCImport_ADD)
-                    gnc_gen_trans_view_popup_menu (treeview, event, info, TRUE);
+                    gnc_gen_trans_view_popup_menu (treeview, event, info);
                 g_list_free_full (selected, (GDestroyNotify)gtk_tree_path_free);
             }
             LEAVE("return TRUE");
@@ -1248,7 +1246,7 @@ gnc_gen_trans_onPopupMenu_cb (GtkTreeView *treeview,
     selection = gtk_tree_view_get_selection (treeview);
     if (gtk_tree_selection_count_selected_rows (selection) > 0)
     {
-      gnc_gen_trans_view_popup_menu (treeview, NULL, info, TRUE);
+      gnc_gen_trans_view_popup_menu (treeview, NULL, info);
       LEAVE ("TRUE");
       return TRUE;
     }

commit fac0cde7a24c1a971993669b3d7c175cbad3aa35
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Sat Jul 9 20:24:36 2022 +0800

    [import-main-matcher] further refinement to edit multiple rows
    
    but is only enabled iff the desc/notes/memo to be renamed are
    identical.

diff --git a/gnucash/import-export/import-main-matcher.c b/gnucash/import-export/import-main-matcher.c
index 54c9b9016..d70029374 100644
--- a/gnucash/import-export/import-main-matcher.c
+++ b/gnucash/import-export/import-main-matcher.c
@@ -894,6 +894,30 @@ typedef enum
     NOTES,
 } edit_field;
 
+typedef struct
+{
+    Split *split;
+    Transaction *trans;
+    GtkTreeIter iter;
+} RowInfo;
+
+static void rowinfo_free (RowInfo* info)
+{
+    g_free (info);
+}
+
+static RowInfo * row_get_info (gpointer row, GNCImportMainMatcher *info)
+{
+    GNCImportTransInfo *trans_info;
+    GtkTreeModel *model = gtk_tree_view_get_model (info->view);
+    RowInfo *retval = g_new (RowInfo, 1);
+    gtk_tree_model_get_iter (model, &retval->iter, row);
+    gtk_tree_model_get (model, &retval->iter, DOWNLOADED_COL_DATA, &trans_info, -1);
+    retval->trans = gnc_import_TransInfo_get_trans (trans_info);
+    retval->split = gnc_import_TransInfo_get_fsplit (trans_info);
+    return retval;
+}
+
 static void
 gnc_gen_trans_edit_fields (GtkMenuItem *menuitem, GNCImportMainMatcher *info,
                            edit_field field)
@@ -901,12 +925,9 @@ gnc_gen_trans_edit_fields (GtkMenuItem *menuitem, GNCImportMainMatcher *info,
     GtkTreeView *treeview;
     GtkTreeSelection *selection;
     GtkTreeModel *model;
-    GList *selected_rows;
-    GList *refs = NULL;
+    GList *selected_rows, *row_info_list;
     GtkTreeStore* store;
-    GNCImportTransInfo *trans_info;
     Transaction* trans;
-    GtkTreeIter iter;
 
     g_return_if_fail (info != NULL);
     ENTER("assign_transfer_account_to_selection_cb");
@@ -923,18 +944,8 @@ gnc_gen_trans_edit_fields (GtkMenuItem *menuitem, GNCImportMainMatcher *info,
         return;
     }
 
-    if (selected_rows->next)
-    {
-        LEAVE ("User selected multiple rows, not supported");
-        return;
-    }
-
-    g_return_if_fail (gtk_tree_model_get_iter (model, &iter,
-                                               selected_rows->data));
-
-    gtk_tree_model_get (model, &iter, DOWNLOADED_COL_DATA,
-                        &trans_info, -1);
-    trans = gnc_import_TransInfo_get_trans (trans_info);
+    row_info_list = gnc_g_list_map (selected_rows, (GncGMapFunc) row_get_info, info);
+    trans = ((RowInfo*)row_info_list->data)->trans;
 
     switch (field)
     {
@@ -945,24 +956,33 @@ gnc_gen_trans_edit_fields (GtkMenuItem *menuitem, GNCImportMainMatcher *info,
                                             _("Enter new Description"),
                                             xaccTransGetDescription (trans));
             if (!new_field) break;
-            xaccTransSetDescription (trans, new_field);
-            gtk_tree_store_set (store, &iter, DOWNLOADED_COL_DESCRIPTION,
-                                new_field, -1);
+            for (GList *n = row_info_list; n; n = g_list_next (n))
+            {
+                RowInfo *info = n->data;
+                xaccTransSetDescription (info->trans, new_field);
+                gtk_tree_store_set (store, &info->iter,
+                                    DOWNLOADED_COL_DESCRIPTION, new_field,
+                                    -1);
+            }
             g_free (new_field);
             break;
         }
         case MEMO:
         {
-            Split *first_split =
-                gnc_import_TransInfo_get_fsplit (trans_info);
+            Split *first_split = xaccTransGetSplit (trans, 0);
             char *new_field =
                 gnc_input_dialog_with_entry(info->main_widget, "",
                                             _("Enter new Memo"),
                                             xaccSplitGetMemo (first_split));
             if (!new_field) break;
-            xaccSplitSetMemo (first_split, new_field);
-            gtk_tree_store_set (store, &iter,
-                                DOWNLOADED_COL_MEMO, new_field, -1);
+            for (GList *n = row_info_list; n; n = g_list_next (n))
+            {
+                RowInfo *info = n->data;
+                xaccSplitSetMemo (info->split, new_field);
+                gtk_tree_store_set (store, &info->iter,
+                                    DOWNLOADED_COL_MEMO, new_field,
+                                    -1);
+            }
             g_free (new_field);
             break;
         }
@@ -973,11 +993,16 @@ gnc_gen_trans_edit_fields (GtkMenuItem *menuitem, GNCImportMainMatcher *info,
                                             _("Enter new Notes"),
                                             xaccTransGetNotes (trans));
             if (!new_field) break;
-            xaccTransSetNotes (trans, new_field);
+            for (GList *n = row_info_list; n; n = g_list_next (n))
+            {
+                RowInfo *info = n->data;
+                xaccTransSetNotes (info->trans, new_field);
+            }
             g_free (new_field);
             break;
         }
     }
+    g_list_free_full (row_info_list, (GDestroyNotify)rowinfo_free);
     g_list_free_full (selected_rows, (GDestroyNotify)gtk_tree_path_free);
     LEAVE("");
 }
@@ -1092,6 +1117,11 @@ gnc_gen_trans_view_popup_menu (GtkTreeView *treeview,
 {
     GtkWidget *menu, *menuitem;
     GdkEventButton *event_button;
+    GtkTreeModel *model;
+    GtkTreeSelection *selection;
+    GList *selected_rows;
+    const char *desc, *memo, *notes;
+    gboolean edit_desc = TRUE, edit_notes = TRUE, edit_memo = TRUE;
 
     ENTER ("");
     menu = gtk_menu_new();
@@ -1104,7 +1134,31 @@ gnc_gen_trans_view_popup_menu (GtkTreeView *treeview,
     DEBUG("Callback to assign destination account to selection connected");
     gtk_menu_shell_append (GTK_MENU_SHELL(menu), menuitem);
 
-    if (show_edit_actions)
+    model = gtk_tree_view_get_model (treeview);
+    selection = gtk_tree_view_get_selection (treeview);
+    selected_rows = gtk_tree_selection_get_selected_rows (selection, &model);
+    for (GList *n = selected_rows; (edit_desc || edit_notes || edit_memo) && n;
+         n = g_list_next(n))
+    {
+        RowInfo *rowinfo = row_get_info (n->data, info);
+        if (!n->prev)       /* only the first row */
+        {
+            desc = xaccTransGetDescription (rowinfo->trans);
+            notes = xaccTransGetNotes (rowinfo->trans);
+            memo = xaccSplitGetMemo (rowinfo->split);
+            rowinfo_free (rowinfo);
+            continue;
+        }
+        if (edit_desc && g_strcmp0 (desc, xaccTransGetDescription (rowinfo->trans)))
+            edit_desc = FALSE;
+        if (edit_notes && g_strcmp0 (notes, xaccTransGetNotes (rowinfo->trans)))
+            edit_notes = FALSE;
+        if (edit_memo && g_strcmp0 (memo, xaccSplitGetMemo (rowinfo->split)))
+            edit_memo = FALSE;
+        rowinfo_free (rowinfo);
+    }
+
+    if (edit_desc)
     {
         menuitem = gtk_menu_item_new_with_label (
                                                  _("Edit description."));
@@ -1113,7 +1167,10 @@ gnc_gen_trans_view_popup_menu (GtkTreeView *treeview,
                           info);
         DEBUG("Callback to edit description");
         gtk_menu_shell_append (GTK_MENU_SHELL(menu), menuitem);
+    }
 
+    if (edit_memo)
+    {
         menuitem = gtk_menu_item_new_with_label (
                                                  _("Edit memo."));
         g_signal_connect (menuitem, "activate",
@@ -1121,7 +1178,10 @@ gnc_gen_trans_view_popup_menu (GtkTreeView *treeview,
                           info);
         DEBUG("Callback to edit memo");
         gtk_menu_shell_append (GTK_MENU_SHELL(menu), menuitem);
+    }
 
+    if (edit_notes)
+    {
         menuitem = gtk_menu_item_new_with_label (
                                                  _("Edit notes."));
         g_signal_connect (menuitem, "activate",
@@ -1135,6 +1195,7 @@ gnc_gen_trans_view_popup_menu (GtkTreeView *treeview,
     /* Note: event can be NULL here when called from view_onPopupMenu; */
     gtk_menu_popup_at_pointer (GTK_MENU(menu), (GdkEvent*)event);
 
+    g_list_free_full (selected_rows, (GDestroyNotify)gtk_tree_path_free);
     LEAVE ("");
 }
 
@@ -1160,9 +1221,7 @@ gnc_gen_trans_onButtonPressed_cb (GtkTreeView *treeview,
             // or the selected transaction is an ADD.
             selection = gtk_tree_view_get_selection (treeview);
             count = gtk_tree_selection_count_selected_rows (selection);
-            if (count > 1)
-                gnc_gen_trans_view_popup_menu (treeview, event, info, FALSE);
-            else if (count > 0)
+            if (count > 0)
             {
                 GList* selected;
                 GtkTreeModel *model;



Summary of changes:
 gnucash/gtkbuilder/dialog-import.glade      | 145 ++++++++
 gnucash/import-export/import-main-matcher.c | 548 +++++++++++++++++++++-------
 2 files changed, 562 insertions(+), 131 deletions(-)



More information about the gnucash-changes mailing list