From 1949d31bb52bc76ad8f7124a21b630f2b456e0a5 Mon Sep 17 00:00:00 2001 From: Christopher Lam Date: Wed, 16 Aug 2023 20:09:47 +0800 Subject: [PATCH 1/7] [gnc-tree-container.hpp] define GncListModelContainer class - defines GncListModelContainer and several methods to iterate the list store, and appropriate setters/getters. Compatible with std::find_if etc. - GncListModelContainer::append() creates a new GncTreeIter and returns it. - GncListModelContainer::size() returns the number of rows. May be O(N). To test rows==0 use empty() instead. - GncTreeData::set_column sets data column in model - GncTreeData::set_column has a std::string function overload to access its c_str() --- gnucash/gnome-utils/CMakeLists.txt | 1 + .../gnome-utils/gnc-list-model-container.hpp | 187 ++++++++++++++++++ gnucash/gnome-utils/test/CMakeLists.txt | 18 ++ .../test/test-list-model-container.cpp | 124 ++++++++++++ 4 files changed, 330 insertions(+) create mode 100644 gnucash/gnome-utils/gnc-list-model-container.hpp create mode 100644 gnucash/gnome-utils/test/test-list-model-container.cpp diff --git a/gnucash/gnome-utils/CMakeLists.txt b/gnucash/gnome-utils/CMakeLists.txt index fb7e53c6b52..60a7994410e 100644 --- a/gnucash/gnome-utils/CMakeLists.txt +++ b/gnucash/gnome-utils/CMakeLists.txt @@ -156,6 +156,7 @@ set (gnome_utils_HEADERS gnc-gui-query.h gnc-icons.h gnc-keyring.h + gnc-list-model-container.hpp gnc-main-window.h gnc-menu-extensions.h gnc-plugin-file-history.h diff --git a/gnucash/gnome-utils/gnc-list-model-container.hpp b/gnucash/gnome-utils/gnc-list-model-container.hpp new file mode 100644 index 00000000000..ff4d4011b88 --- /dev/null +++ b/gnucash/gnome-utils/gnc-list-model-container.hpp @@ -0,0 +1,187 @@ +/********************************************************************\ + * gnc-tree-container.hpp + * * + * 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@gnu.org * +\********************************************************************/ + +#ifndef GNC_LIST_MODEL_CONTAINER_HPP +#define GNC_LIST_MODEL_CONTAINER_HPP + +#include +#include +#include +#include + +class GncListModelData +{ +public: + GncListModelData (GtkTreeModel* model, const GtkTreeIter& iter) : m_model{model}, m_iter{iter} {}; + + template + T get_column (int column) + { + gpointer rv; + gtk_tree_model_get(m_model, &m_iter, column, &rv, -1); + return static_cast(rv); + } + + GtkTreeIter& get_iter () { return m_iter; }; + + int get_column_int (int column) + { + int rv; + gtk_tree_model_get(m_model, &m_iter, column, &rv, -1); + return rv; + } + + std::string get_column_string (int column) + { + auto str = get_column(column); + std::string rv{str}; + g_free (str); + return rv; + } + + void set_columns (int unused, ...) + { + va_list var_args; + va_start (var_args, unused); + gtk_list_store_set_valist (GTK_LIST_STORE(m_model), &get_iter(), var_args); + va_end (var_args); + } + + template + void set_column (int column, T data) { gtk_list_store_set(GTK_LIST_STORE(m_model), &get_iter(), column, data, -1); } + + // overloads the template when data is a std::string + void set_column (int column, const std::string& str) { set_column (column, str.c_str()); } + + bool operator==(const GncListModelData& other) const + { + return (m_model == other.m_model) && + (m_iter.stamp == other.m_iter.stamp) && + (m_iter.user_data == other.m_iter.user_data) && + (m_iter.user_data2 == other.m_iter.user_data2) && + (m_iter.user_data3 == other.m_iter.user_data3); + } + +private: + GtkTreeModel* m_model; + GtkTreeIter m_iter; +}; + +// Custom container class +template +class GncListModelContainer +{ +public: + + // Custom iterator class + class GncListModelIter + { + public: + /* Set iterator traits queried by STL algorithms. These are + required for std::find_if etc to iterate through the + container. */ + using iterator_category = std::forward_iterator_tag; + using value_type = ModelType; + using difference_type = std::ptrdiff_t; + using pointer = ModelType*; + using reference = ModelType&; + + GncListModelIter(GtkTreeModel* model, std::optional iter) : m_model(model), m_iter(iter) {} + + GncListModelIter(GtkTreeModel* model) : m_model (model) + { + GtkTreeIter iter; + m_iter = gtk_tree_model_get_iter_first(m_model, &iter) ? std::make_optional(iter) : std::nullopt; + } + + GncListModelIter& operator++() + { + if (!m_iter.has_value()) + throw "no value, cannot increment"; + if (!gtk_tree_model_iter_next(m_model, &m_iter.value())) + m_iter = std::nullopt; + return *this; + } + + ModelType operator*() const + { + if (!m_iter.has_value()) + throw "no value, cannot dereference"; + return ModelType (m_model, *m_iter); + } + + std::unique_ptr operator->() + { + if (!m_iter.has_value()) + throw "no value, cannot dereference"; + return std::make_unique (m_model, *m_iter); + } + + bool has_value() const { return m_iter.has_value(); }; + + bool operator==(const GncListModelIter& other) const + { + if (m_model != other.m_model) + return false; + if (!m_iter.has_value() && !other.m_iter.has_value()) + return true; + if (!m_iter.has_value() || !other.m_iter.has_value()) + return false; + return (ModelType (m_model, *m_iter) == ModelType (m_model, *other.m_iter)); + } + + bool operator!=(const GncListModelIter& other) const { return !(*this == other); } + + private: + GtkTreeModel* m_model; + std::optional m_iter; + }; + + GncListModelContainer(GtkTreeModel* model) : m_model(model) + { + g_return_if_fail (GTK_IS_TREE_MODEL (m_model)); + g_object_ref (m_model); + } + + ~GncListModelContainer () { g_object_unref (m_model); } + + GncListModelIter begin() const { return GncListModelIter(m_model); }; + + GncListModelIter end() const { return GncListModelIter(m_model, std::nullopt); }; + + GncListModelIter append() + { + GtkTreeIter iter; + gtk_list_store_append (GTK_LIST_STORE(m_model), &iter); + return GncListModelIter(m_model, iter); + }; + + size_t size() const { return std::distance (begin(), end()); } + + bool empty() const { return begin() == end(); }; + + void clear() { gtk_list_store_clear (GTK_LIST_STORE (m_model)); }; + +private: + GtkTreeModel* m_model; +}; + +#endif diff --git a/gnucash/gnome-utils/test/CMakeLists.txt b/gnucash/gnome-utils/test/CMakeLists.txt index ea25c044680..474c6bc7d81 100644 --- a/gnucash/gnome-utils/test/CMakeLists.txt +++ b/gnucash/gnome-utils/test/CMakeLists.txt @@ -39,13 +39,31 @@ set(test_autoclear_LIBS gtest ) +set(test_list_model_container_SOURCES + test-list-model-container.cpp +) + +set(test_list_model_container_INCLUDE_DIRS +) + +set(test_list_model_container_LIBS + gnc-gnome-utils + gtest +) + gnc_add_test(test-autoclear "${test_autoclear_SOURCES}" test_autoclear_INCLUDE_DIRS test_autoclear_LIBS ) +gnc_add_test(test-list-model-container "${test_list_model_container_SOURCES}" + test_list_model_container_INCLUDE_DIRS + test_list_model_container_LIBS +) + gnc_add_scheme_tests(test-load-gnome-utils-module.scm) set_dist_list(test_gnome_utils_DIST CMakeLists.txt test-load-gnome-utils-module.scm + ${test_list_model_container_SOURCES} ${test_autoclear_SOURCES}) diff --git a/gnucash/gnome-utils/test/test-list-model-container.cpp b/gnucash/gnome-utils/test/test-list-model-container.cpp new file mode 100644 index 00000000000..86dfca3614d --- /dev/null +++ b/gnucash/gnome-utils/test/test-list-model-container.cpp @@ -0,0 +1,124 @@ +/******************************************************************** + * test-tree-container.cpp: * + * * + * 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, you can retrieve it from * + * https://www.gnu.org/licenses/old-licenses/gpl-2.0.html * + * or 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@gnu.org * + ********************************************************************/ + +#include "config.h" +#include +#include +#include "../gnc-list-model-container.hpp" +#include +#include + +enum { + COLUMN_STRING, + COLUMN_INT, + COLUMN_BOOLEAN, + N_COLUMNS +}; + + +TEST(GncListModelContainer, Equality) +{ + auto store1 = gtk_list_store_new (N_COLUMNS, G_TYPE_STRING, G_TYPE_INT, G_TYPE_BOOLEAN); + auto store2 = gtk_list_store_new (N_COLUMNS, G_TYPE_STRING, G_TYPE_INT, G_TYPE_BOOLEAN); + + GncListModelContainer container1{GTK_TREE_MODEL(store1)}; + GncListModelContainer container2{GTK_TREE_MODEL(store2)}; + + // these are null tests + EXPECT_TRUE (container1.begin() == container1.begin()); + EXPECT_TRUE (container1.end() == container1.end()); + + EXPECT_TRUE (container1.begin() == container1.end()); + EXPECT_TRUE (container1.size() == 0); + EXPECT_TRUE (container2.size() == 0); + EXPECT_TRUE (container1.empty()); + EXPECT_TRUE (container1.empty()); + + EXPECT_FALSE (container1.begin() == container2.begin()); + EXPECT_FALSE (container1.end() == container2.end()); + + // both containers have identical contents + container1.append()->set_column (COLUMN_STRING, "1"); + container2.append()->set_column (COLUMN_STRING, "1"); + + // the containers are now no longer empty + EXPECT_FALSE (container1.begin() == container1.end()); + EXPECT_FALSE (container2.begin() == container2.end()); + EXPECT_TRUE (container1.size() == 1); + EXPECT_TRUE (container2.size() == 1); + + // however the iterators behave as expected -- iterators from + // store1 must differ from iterators from store2 + EXPECT_FALSE (container1.begin() == container2.begin()); + EXPECT_FALSE (container1.end() == container2.end()); + + g_object_unref (store1); + g_object_unref (store2); +} + +TEST(GncListModelContainer, Basic) +{ + auto store = gtk_list_store_new (N_COLUMNS, G_TYPE_STRING, G_TYPE_INT, G_TYPE_BOOLEAN); + GncListModelContainer container{GTK_TREE_MODEL(store)}; + + // test empty + EXPECT_TRUE (container.empty()); + + for (size_t i = 0; i < 10; i++) + { + auto str = std::string("string ") + std::to_string(i); + auto iter = container.append (); + iter->set_columns (0, + COLUMN_STRING, str.c_str(), + COLUMN_INT, i, + COLUMN_BOOLEAN, (i % 2) == 0, + -1); + } + + // test non-empty + EXPECT_FALSE (container.empty()); + + // test size + EXPECT_TRUE (10 == container.size()); + + auto int_is_five = [](auto it){ return it.get_column_int(COLUMN_INT) == 5; }; + auto iter_found = std::find_if (container.begin(), container.end(), int_is_five); + EXPECT_TRUE (iter_found.has_value()); + EXPECT_EQ ("string 5", iter_found->get_column_string (COLUMN_STRING)); + + g_object_unref (store); +} + +int main(int argc, char** argv) +{ + if (gtk_init_check (nullptr, nullptr)) + std::cout << "gtk init completed!" << std::endl; + else + std::cout << "no display present!" << std::endl; + + // Initialize the Google Test framework + ::testing::InitGoogleTest(&argc, argv); + + // Run tests + return RUN_ALL_TESTS(); +} From 98fa95aecae2f3bae8ba003dedeaaec0cea55c27 Mon Sep 17 00:00:00 2001 From: Christopher Lam Date: Wed, 16 Aug 2023 20:10:57 +0800 Subject: [PATCH 2/7] [import-match-picker.cpp] use GncListModelContainer and std::find_if --- gnucash/import-export/import-match-picker.cpp | 55 ++++++++----------- 1 file changed, 23 insertions(+), 32 deletions(-) diff --git a/gnucash/import-export/import-match-picker.cpp b/gnucash/import-export/import-match-picker.cpp index 732012b698e..2426da0f9a4 100644 --- a/gnucash/import-export/import-match-picker.cpp +++ b/gnucash/import-export/import-match-picker.cpp @@ -31,12 +31,15 @@ #include #include +#include "gnc-list-model-container.hpp" #include "import-match-picker.h" #include "qof.h" #include "gnc-ui-util.h" #include "dialog-utils.h" #include "gnc-prefs.h" +#include + /********************************************************************\ * Constants * \********************************************************************/ @@ -98,32 +101,22 @@ downloaded_transaction_append(GNCImportMatchPicker * matcher, g_return_if_fail (matcher); g_return_if_fail (transaction_info); - auto found = false; auto store = GTK_LIST_STORE(gtk_tree_view_get_model(matcher->downloaded_view)); auto split = gnc_import_TransInfo_get_fsplit(transaction_info); auto trans = gnc_import_TransInfo_get_trans(transaction_info); + GncListModelContainer container{GTK_TREE_MODEL(store)}; - /* Has the transaction already been added? */ - GtkTreeIter iter; - if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter)) - { - do - { - GNCImportTransInfo *local_info; - gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, - DOWNLOADED_COL_INFO_PTR, &local_info, - -1); - if (local_info == transaction_info) - { - found = TRUE; - break; - } - } - while (gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter)); - } - if (!found) - gtk_list_store_append(store, &iter); + auto it_matches = [&transaction_info](auto it) + { return it.template get_column(DOWNLOADED_COL_INFO_PTR) == transaction_info; }; + // find the GncTreeIter whose DOWNLOADED_COL_INFO_PTR matches transaction_info + auto iter = std::find_if (container.begin(), container.end(), it_matches); + + // not found. append a new GtkTreeIter in container. + if (iter == container.end()) + iter = container.append(); + + // now iter is a GncTreeIter; iter->get_iter() is the GtkTreeIter auto account = xaccAccountGetName(xaccSplitGetAccount(split)); auto date = qof_print_date(xaccTransGetDate(trans)); auto amount = g_strdup (xaccPrintAmount(xaccSplitGetAmount(split), gnc_split_amount_print_info(split, TRUE))); @@ -137,17 +130,15 @@ downloaded_transaction_append(GNCImportMatchPicker * matcher, auto imbalance = g_strdup (xaccPrintAmount (xaccTransGetImbalanceValue(trans), gnc_commodity_print_info (xaccTransGetCurrency (trans), TRUE))); - gtk_list_store_set (store, &iter, - DOWNLOADED_COL_ACCOUNT, account, - DOWNLOADED_COL_DATE, date, - DOWNLOADED_COL_AMOUNT, amount, - DOWNLOADED_COL_DESCRIPTION, desc, - DOWNLOADED_COL_MEMO, memo, - DOWNLOADED_COL_BALANCED, imbalance, - DOWNLOADED_COL_INFO_PTR, transaction_info, - -1); - - gtk_tree_selection_select_iter (gtk_tree_view_get_selection(matcher->downloaded_view), &iter); + iter->set_column (DOWNLOADED_COL_ACCOUNT, account); + iter->set_column (DOWNLOADED_COL_DATE, date); + iter->set_column (DOWNLOADED_COL_AMOUNT, amount); + iter->set_column (DOWNLOADED_COL_DESCRIPTION, desc); + iter->set_column (DOWNLOADED_COL_MEMO, memo); + iter->set_column (DOWNLOADED_COL_BALANCED, imbalance); + iter->set_column (DOWNLOADED_COL_INFO_PTR, transaction_info); + + gtk_tree_selection_select_iter (gtk_tree_view_get_selection(matcher->downloaded_view), &iter->get_iter()); g_free (date); g_free (amount); From dac731abe69dcbf08fd5db6fee2f4463b179db32 Mon Sep 17 00:00:00 2001 From: Christopher Lam Date: Mon, 28 Aug 2023 19:54:06 +0800 Subject: [PATCH 3/7] [import-match-picker.cpp] use va_args to set all columns in 1 call --- gnucash/import-export/import-match-picker.cpp | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/gnucash/import-export/import-match-picker.cpp b/gnucash/import-export/import-match-picker.cpp index 2426da0f9a4..738578f9d65 100644 --- a/gnucash/import-export/import-match-picker.cpp +++ b/gnucash/import-export/import-match-picker.cpp @@ -130,13 +130,15 @@ downloaded_transaction_append(GNCImportMatchPicker * matcher, auto imbalance = g_strdup (xaccPrintAmount (xaccTransGetImbalanceValue(trans), gnc_commodity_print_info (xaccTransGetCurrency (trans), TRUE))); - iter->set_column (DOWNLOADED_COL_ACCOUNT, account); - iter->set_column (DOWNLOADED_COL_DATE, date); - iter->set_column (DOWNLOADED_COL_AMOUNT, amount); - iter->set_column (DOWNLOADED_COL_DESCRIPTION, desc); - iter->set_column (DOWNLOADED_COL_MEMO, memo); - iter->set_column (DOWNLOADED_COL_BALANCED, imbalance); - iter->set_column (DOWNLOADED_COL_INFO_PTR, transaction_info); + iter->set_columns (0, + DOWNLOADED_COL_ACCOUNT, account, + DOWNLOADED_COL_DATE, date, + DOWNLOADED_COL_AMOUNT, amount, + DOWNLOADED_COL_DESCRIPTION, desc, + DOWNLOADED_COL_MEMO, memo, + DOWNLOADED_COL_BALANCED, imbalance, + DOWNLOADED_COL_INFO_PTR, transaction_info, + -1); gtk_tree_selection_select_iter (gtk_tree_view_get_selection(matcher->downloaded_view), &iter->get_iter()); From 88567caf4662834399ea8e627767f6c837c851e0 Mon Sep 17 00:00:00 2001 From: Christopher Lam Date: Wed, 23 Aug 2023 18:02:05 +0800 Subject: [PATCH 4/7] [assistant-csv-trans-import.cpp] uses GncListModelContainer --- .../csv-imp/assistant-csv-trans-import.cpp | 93 ++++++------------- 1 file changed, 27 insertions(+), 66 deletions(-) diff --git a/gnucash/import-export/csv-imp/assistant-csv-trans-import.cpp b/gnucash/import-export/csv-imp/assistant-csv-trans-import.cpp index d1cb2480eaa..ab69d5537f9 100644 --- a/gnucash/import-export/csv-imp/assistant-csv-trans-import.cpp +++ b/gnucash/import-export/csv-imp/assistant-csv-trans-import.cpp @@ -57,6 +57,7 @@ #include "gnc-csv-gnumeric-popup.h" #include "go-charmap-sel.h" +#include "gnc-list-model-container.hpp" #include "gnc-imp-settings-csv-tx.hpp" #include "gnc-import-tx.hpp" #include "gnc-tokenizer-fw.hpp" @@ -894,31 +895,25 @@ void CsvImpTransAssist::preview_settings_save () { auto new_name = tx_imp->settings_name(); + GncListModelContainer tree_model_container{gtk_combo_box_get_model (settings_combo)}; /* Check if the entry text matches an already existing preset */ GtkTreeIter iter; if (!gtk_combo_box_get_active_iter (settings_combo, &iter)) { - - auto model = gtk_combo_box_get_model (settings_combo); - bool valid = gtk_tree_model_get_iter_first (model, &iter); - while (valid) + for (auto iter : tree_model_container) { // Walk through the list, reading each row - CsvTransImpSettings *preset; - gtk_tree_model_get (model, &iter, SET_GROUP, &preset, -1); - - if (preset && (preset->m_name == std::string(new_name))) + auto preset = iter.get_column(SET_GROUP); + if (preset && preset->m_name == new_name) { - auto response = gnc_ok_cancel_dialog (GTK_WINDOW (csv_imp_asst), - GTK_RESPONSE_OK, - "%s", _("Setting name already exists, overwrite?")); + auto response = gnc_ok_cancel_dialog (GTK_WINDOW (csv_imp_asst), GTK_RESPONSE_OK, + "%s", _("Setting name already exists, overwrite?")); if (response != GTK_RESPONSE_OK) return; break; } - valid = gtk_tree_model_iter_next (model, &iter); } } @@ -930,23 +925,14 @@ CsvImpTransAssist::preview_settings_save () // Update the settings store preview_populate_settings_combo(); - auto model = gtk_combo_box_get_model (settings_combo); - // Get the first entry in model - GtkTreeIter iter; - bool valid = gtk_tree_model_get_iter_first (model, &iter); - while (valid) + for (auto iter : tree_model_container) { - // Walk through the list, reading each row - gchar *name = nullptr; - gtk_tree_model_get (model, &iter, SET_NAME, &name, -1); - - if (g_strcmp0 (name, new_name.c_str()) == 0) // Set Active, the one Saved. - gtk_combo_box_set_active_iter (settings_combo, &iter); - - g_free (name); - - valid = gtk_tree_model_iter_next (model, &iter); + if (iter.get_column_string (SET_NAME) == new_name) // Set Active, the one Saved. + { + gtk_combo_box_set_active_iter (settings_combo, &iter.get_iter()); + break; + } } } else @@ -1457,7 +1443,6 @@ CsvImpTransAssist::preview_row_fill_state_cells (GtkListStore *store, GtkTreeIte GtkWidget* CsvImpTransAssist::preview_cbox_factory (GtkTreeModel* model, uint32_t colnum) { - GtkTreeIter iter; auto cbox = gtk_combo_box_new_with_model(model); /* Set up a renderer for this combobox. */ @@ -1467,18 +1452,14 @@ CsvImpTransAssist::preview_cbox_factory (GtkTreeModel* model, uint32_t colnum) gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT(cbox), renderer, "text", COL_TYPE_NAME); - auto valid = gtk_tree_model_get_iter_first (model, &iter); - while (valid) + for (auto iter : GncListModelContainer (model)) { - gint stored_col_type; - gtk_tree_model_get (model, &iter, - COL_TYPE_ID, &stored_col_type, -1); - if (stored_col_type == static_cast( tx_imp->column_types()[colnum])) + if (iter.get_column_int(COL_TYPE_ID) == static_cast( tx_imp->column_types()[colnum])) + { + gtk_combo_box_set_active_iter (GTK_COMBO_BOX(cbox), &iter.get_iter()); break; - valid = gtk_tree_model_iter_next(model, &iter); + } } - if (valid) - gtk_combo_box_set_active_iter (GTK_COMBO_BOX(cbox), &iter); g_object_set_data (G_OBJECT(cbox), "col-num", GUINT_TO_POINTER(colnum)); g_signal_connect (G_OBJECT(cbox), "changed", @@ -1787,54 +1768,34 @@ void CsvImpTransAssist::acct_match_set_accounts () static void csv_tximp_acct_match_load_mappings (GtkTreeModel *mappings_store) { - // Set iter to first entry of store - GtkTreeIter iter; - auto valid = gtk_tree_model_get_iter_first (mappings_store, &iter); - - // Walk through the store trying to match to a map - while (valid) + for (auto iter : GncListModelContainer(mappings_store)) { // Walk through the list, reading each row - Account *account = nullptr; - gchar *map_string; - gtk_tree_model_get (GTK_TREE_MODEL(mappings_store), &iter, MAPPING_STRING, &map_string, MAPPING_ACCOUNT, &account, -1); + auto account = iter.get_column(MAPPING_ACCOUNT); + auto map_string = iter.get_column_string(MAPPING_STRING); // Look for an account matching the map_string // It may already be set in the tree model. If not we try to match the map_string with // - an entry in our saved account maps // - a full name of any of our existing accounts if (account || - (account = gnc_account_imap_find_any (gnc_get_current_book(), IMAP_CAT_CSV, map_string)) || - (account = gnc_account_lookup_by_full_name (gnc_get_current_root_account(), map_string))) + (account = gnc_account_imap_find_any (gnc_get_current_book(), IMAP_CAT_CSV, map_string.c_str())) || + (account = gnc_account_lookup_by_full_name (gnc_get_current_root_account(), map_string.c_str()))) { auto fullpath = gnc_account_get_full_name (account); - gtk_list_store_set (GTK_LIST_STORE(mappings_store), &iter, MAPPING_FULLPATH, fullpath, -1); - gtk_list_store_set (GTK_LIST_STORE(mappings_store), &iter, MAPPING_ACCOUNT, account, -1); + gtk_list_store_set (GTK_LIST_STORE(mappings_store), &iter.get_iter(), MAPPING_FULLPATH, fullpath, -1); + gtk_list_store_set (GTK_LIST_STORE(mappings_store), &iter.get_iter(), MAPPING_ACCOUNT, account, -1); g_free (fullpath); } - - g_free (map_string); - valid = gtk_tree_model_iter_next (mappings_store, &iter); } } static bool csv_tximp_acct_match_check_all (GtkTreeModel *model) { - // Set iter to first entry of store - GtkTreeIter iter; - auto valid = gtk_tree_model_get_iter_first (model, &iter); - - // Walk through the store looking for nullptr accounts - while (valid) - { - Account *account; - gtk_tree_model_get (model, &iter, MAPPING_ACCOUNT, &account, -1); - if (!account) + for (auto iter : GncListModelContainer(model)) + if (!iter.get_column(MAPPING_ACCOUNT)) return false; - - valid = gtk_tree_model_iter_next (model, &iter); - } return true; } From cccf448e6cd0a5ccbefd4d5d6e69734d2b0d91cf Mon Sep 17 00:00:00 2001 From: Christopher Lam Date: Wed, 23 Aug 2023 18:02:14 +0800 Subject: [PATCH 5/7] [assistant-csv-price-import.cpp] uses GncListModelContainer --- .../csv-imp/assistant-csv-price-import.cpp | 57 ++++++------------- 1 file changed, 18 insertions(+), 39 deletions(-) diff --git a/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp b/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp index ddb0a2a1e40..8ef1ac59e02 100644 --- a/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp +++ b/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp @@ -35,6 +35,7 @@ #include #include +#include "gnc-list-model-container.hpp" #include "gnc-ui.h" #include "gnc-uri-utils.h" #include "gnc-ui-util.h" @@ -921,31 +922,25 @@ void CsvImpPriceAssist::preview_settings_save () { auto new_name = price_imp->settings_name(); + GncListModelContainer tree_model_container{gtk_combo_box_get_model (settings_combo)}; /* Check if the entry text matches an already existing preset */ GtkTreeIter iter; if (!gtk_combo_box_get_active_iter (settings_combo, &iter)) { - - auto model = gtk_combo_box_get_model (settings_combo); - bool valid = gtk_tree_model_get_iter_first (model, &iter); - while (valid) + for (auto iter : tree_model_container) { // Walk through the list, reading each row - CsvPriceImpSettings *preset; - gtk_tree_model_get (model, &iter, SET_GROUP, &preset, -1); - - if (preset && (preset->m_name == std::string(new_name))) + auto preset = iter.get_column(SET_GROUP); + if (preset && preset->m_name == new_name) { - auto response = gnc_ok_cancel_dialog (GTK_WINDOW(csv_imp_asst), - GTK_RESPONSE_OK, - "%s", _("Setting name already exists, overwrite?")); + auto response = gnc_ok_cancel_dialog (GTK_WINDOW(csv_imp_asst), GTK_RESPONSE_OK, + "%s", _("Setting name already exists, overwrite?")); if (response != GTK_RESPONSE_OK) return; break; } - valid = gtk_tree_model_iter_next (model, &iter); } } @@ -957,24 +952,13 @@ CsvImpPriceAssist::preview_settings_save () // Update the settings store preview_populate_settings_combo(); - auto model = gtk_combo_box_get_model (settings_combo); - - // Get the first entry in model - GtkTreeIter iter; - bool valid = gtk_tree_model_get_iter_first (model, &iter); - while (valid) - { - // Walk through the list, reading each row - gchar *name = nullptr; - gtk_tree_model_get (model, &iter, SET_NAME, &name, -1); - - if (g_strcmp0 (name, new_name.c_str()) == 0) // Set Active, the one Saved. - gtk_combo_box_set_active_iter (settings_combo, &iter); - g_free (name); - - valid = gtk_tree_model_iter_next (model, &iter); - } + for (auto iter : tree_model_container) + if (iter.get_column_string (SET_NAME) == new_name) // Set Active, the one Saved. + { + gtk_combo_box_set_active_iter (settings_combo, &iter.get_iter()); + break; + } } else gnc_error_dialog (GTK_WINDOW(csv_imp_asst), @@ -1516,7 +1500,6 @@ CsvImpPriceAssist::preview_row_fill_state_cells (GtkListStore *store, GtkTreeIte GtkWidget* CsvImpPriceAssist::preview_cbox_factory (GtkTreeModel* model, uint32_t colnum) { - GtkTreeIter iter; auto cbox = gtk_combo_box_new_with_model(model); /* Set up a renderer for this combobox. */ @@ -1526,18 +1509,14 @@ CsvImpPriceAssist::preview_cbox_factory (GtkTreeModel* model, uint32_t colnum) gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT(cbox), renderer, "text", COL_TYPE_NAME); - auto valid = gtk_tree_model_get_iter_first (model, &iter); - while (valid) + for (auto iter : GncListModelContainer (model)) { - gint stored_col_type; - gtk_tree_model_get (model, &iter, - COL_TYPE_ID, &stored_col_type, -1); - if (stored_col_type == static_cast( price_imp->column_types_price()[colnum])) + if (iter.get_column_int(COL_TYPE_ID) == static_cast( price_imp->column_types_price()[colnum])) + { + gtk_combo_box_set_active_iter (GTK_COMBO_BOX(cbox), &iter.get_iter()); break; - valid = gtk_tree_model_iter_next(model, &iter); + } } - if (valid) - gtk_combo_box_set_active_iter (GTK_COMBO_BOX(cbox), &iter); g_object_set_data (G_OBJECT(cbox), "col-num", GUINT_TO_POINTER(colnum)); g_signal_connect (G_OBJECT(cbox), "changed", From f2bc1e8952e277145bd8c3351fe3ed6feeeba5d3 Mon Sep 17 00:00:00 2001 From: Christopher Lam Date: Mon, 28 Aug 2023 20:45:27 +0800 Subject: [PATCH 6/7] Revert "[assistant-csv-price-import.cpp] uses GncListModelContainer" This reverts commit ec20b34a85ea2f23db2c30fd91914c0912b22745. --- .../csv-imp/assistant-csv-price-import.cpp | 57 +++++++++++++------ 1 file changed, 39 insertions(+), 18 deletions(-) diff --git a/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp b/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp index 8ef1ac59e02..ddb0a2a1e40 100644 --- a/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp +++ b/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp @@ -35,7 +35,6 @@ #include #include -#include "gnc-list-model-container.hpp" #include "gnc-ui.h" #include "gnc-uri-utils.h" #include "gnc-ui-util.h" @@ -922,25 +921,31 @@ void CsvImpPriceAssist::preview_settings_save () { auto new_name = price_imp->settings_name(); - GncListModelContainer tree_model_container{gtk_combo_box_get_model (settings_combo)}; /* Check if the entry text matches an already existing preset */ GtkTreeIter iter; if (!gtk_combo_box_get_active_iter (settings_combo, &iter)) { - for (auto iter : tree_model_container) + + auto model = gtk_combo_box_get_model (settings_combo); + bool valid = gtk_tree_model_get_iter_first (model, &iter); + while (valid) { // Walk through the list, reading each row - auto preset = iter.get_column(SET_GROUP); - if (preset && preset->m_name == new_name) + CsvPriceImpSettings *preset; + gtk_tree_model_get (model, &iter, SET_GROUP, &preset, -1); + + if (preset && (preset->m_name == std::string(new_name))) { - auto response = gnc_ok_cancel_dialog (GTK_WINDOW(csv_imp_asst), GTK_RESPONSE_OK, - "%s", _("Setting name already exists, overwrite?")); + auto response = gnc_ok_cancel_dialog (GTK_WINDOW(csv_imp_asst), + GTK_RESPONSE_OK, + "%s", _("Setting name already exists, overwrite?")); if (response != GTK_RESPONSE_OK) return; break; } + valid = gtk_tree_model_iter_next (model, &iter); } } @@ -952,13 +957,24 @@ CsvImpPriceAssist::preview_settings_save () // Update the settings store preview_populate_settings_combo(); + auto model = gtk_combo_box_get_model (settings_combo); - for (auto iter : tree_model_container) - if (iter.get_column_string (SET_NAME) == new_name) // Set Active, the one Saved. - { - gtk_combo_box_set_active_iter (settings_combo, &iter.get_iter()); - break; - } + // Get the first entry in model + GtkTreeIter iter; + bool valid = gtk_tree_model_get_iter_first (model, &iter); + while (valid) + { + // Walk through the list, reading each row + gchar *name = nullptr; + gtk_tree_model_get (model, &iter, SET_NAME, &name, -1); + + if (g_strcmp0 (name, new_name.c_str()) == 0) // Set Active, the one Saved. + gtk_combo_box_set_active_iter (settings_combo, &iter); + + g_free (name); + + valid = gtk_tree_model_iter_next (model, &iter); + } } else gnc_error_dialog (GTK_WINDOW(csv_imp_asst), @@ -1500,6 +1516,7 @@ CsvImpPriceAssist::preview_row_fill_state_cells (GtkListStore *store, GtkTreeIte GtkWidget* CsvImpPriceAssist::preview_cbox_factory (GtkTreeModel* model, uint32_t colnum) { + GtkTreeIter iter; auto cbox = gtk_combo_box_new_with_model(model); /* Set up a renderer for this combobox. */ @@ -1509,14 +1526,18 @@ CsvImpPriceAssist::preview_cbox_factory (GtkTreeModel* model, uint32_t colnum) gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT(cbox), renderer, "text", COL_TYPE_NAME); - for (auto iter : GncListModelContainer (model)) + auto valid = gtk_tree_model_get_iter_first (model, &iter); + while (valid) { - if (iter.get_column_int(COL_TYPE_ID) == static_cast( price_imp->column_types_price()[colnum])) - { - gtk_combo_box_set_active_iter (GTK_COMBO_BOX(cbox), &iter.get_iter()); + gint stored_col_type; + gtk_tree_model_get (model, &iter, + COL_TYPE_ID, &stored_col_type, -1); + if (stored_col_type == static_cast( price_imp->column_types_price()[colnum])) break; - } + valid = gtk_tree_model_iter_next(model, &iter); } + if (valid) + gtk_combo_box_set_active_iter (GTK_COMBO_BOX(cbox), &iter); g_object_set_data (G_OBJECT(cbox), "col-num", GUINT_TO_POINTER(colnum)); g_signal_connect (G_OBJECT(cbox), "changed", From c7f19c97c898ec9c52da8c70d6fce83a35e9f226 Mon Sep 17 00:00:00 2001 From: Christopher Lam Date: Mon, 28 Aug 2023 20:45:31 +0800 Subject: [PATCH 7/7] Revert "[assistant-csv-trans-import.cpp] uses GncListModelContainer" This reverts commit 96c76371241d63422767d4f36f720c7543dfad23. --- .../csv-imp/assistant-csv-trans-import.cpp | 93 +++++++++++++------ 1 file changed, 66 insertions(+), 27 deletions(-) diff --git a/gnucash/import-export/csv-imp/assistant-csv-trans-import.cpp b/gnucash/import-export/csv-imp/assistant-csv-trans-import.cpp index ab69d5537f9..d1cb2480eaa 100644 --- a/gnucash/import-export/csv-imp/assistant-csv-trans-import.cpp +++ b/gnucash/import-export/csv-imp/assistant-csv-trans-import.cpp @@ -57,7 +57,6 @@ #include "gnc-csv-gnumeric-popup.h" #include "go-charmap-sel.h" -#include "gnc-list-model-container.hpp" #include "gnc-imp-settings-csv-tx.hpp" #include "gnc-import-tx.hpp" #include "gnc-tokenizer-fw.hpp" @@ -895,25 +894,31 @@ void CsvImpTransAssist::preview_settings_save () { auto new_name = tx_imp->settings_name(); - GncListModelContainer tree_model_container{gtk_combo_box_get_model (settings_combo)}; /* Check if the entry text matches an already existing preset */ GtkTreeIter iter; if (!gtk_combo_box_get_active_iter (settings_combo, &iter)) { - for (auto iter : tree_model_container) + + auto model = gtk_combo_box_get_model (settings_combo); + bool valid = gtk_tree_model_get_iter_first (model, &iter); + while (valid) { // Walk through the list, reading each row - auto preset = iter.get_column(SET_GROUP); - if (preset && preset->m_name == new_name) + CsvTransImpSettings *preset; + gtk_tree_model_get (model, &iter, SET_GROUP, &preset, -1); + + if (preset && (preset->m_name == std::string(new_name))) { - auto response = gnc_ok_cancel_dialog (GTK_WINDOW (csv_imp_asst), GTK_RESPONSE_OK, - "%s", _("Setting name already exists, overwrite?")); + auto response = gnc_ok_cancel_dialog (GTK_WINDOW (csv_imp_asst), + GTK_RESPONSE_OK, + "%s", _("Setting name already exists, overwrite?")); if (response != GTK_RESPONSE_OK) return; break; } + valid = gtk_tree_model_iter_next (model, &iter); } } @@ -925,14 +930,23 @@ CsvImpTransAssist::preview_settings_save () // Update the settings store preview_populate_settings_combo(); + auto model = gtk_combo_box_get_model (settings_combo); - for (auto iter : tree_model_container) + // Get the first entry in model + GtkTreeIter iter; + bool valid = gtk_tree_model_get_iter_first (model, &iter); + while (valid) { - if (iter.get_column_string (SET_NAME) == new_name) // Set Active, the one Saved. - { - gtk_combo_box_set_active_iter (settings_combo, &iter.get_iter()); - break; - } + // Walk through the list, reading each row + gchar *name = nullptr; + gtk_tree_model_get (model, &iter, SET_NAME, &name, -1); + + if (g_strcmp0 (name, new_name.c_str()) == 0) // Set Active, the one Saved. + gtk_combo_box_set_active_iter (settings_combo, &iter); + + g_free (name); + + valid = gtk_tree_model_iter_next (model, &iter); } } else @@ -1443,6 +1457,7 @@ CsvImpTransAssist::preview_row_fill_state_cells (GtkListStore *store, GtkTreeIte GtkWidget* CsvImpTransAssist::preview_cbox_factory (GtkTreeModel* model, uint32_t colnum) { + GtkTreeIter iter; auto cbox = gtk_combo_box_new_with_model(model); /* Set up a renderer for this combobox. */ @@ -1452,14 +1467,18 @@ CsvImpTransAssist::preview_cbox_factory (GtkTreeModel* model, uint32_t colnum) gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT(cbox), renderer, "text", COL_TYPE_NAME); - for (auto iter : GncListModelContainer (model)) + auto valid = gtk_tree_model_get_iter_first (model, &iter); + while (valid) { - if (iter.get_column_int(COL_TYPE_ID) == static_cast( tx_imp->column_types()[colnum])) - { - gtk_combo_box_set_active_iter (GTK_COMBO_BOX(cbox), &iter.get_iter()); + gint stored_col_type; + gtk_tree_model_get (model, &iter, + COL_TYPE_ID, &stored_col_type, -1); + if (stored_col_type == static_cast( tx_imp->column_types()[colnum])) break; - } + valid = gtk_tree_model_iter_next(model, &iter); } + if (valid) + gtk_combo_box_set_active_iter (GTK_COMBO_BOX(cbox), &iter); g_object_set_data (G_OBJECT(cbox), "col-num", GUINT_TO_POINTER(colnum)); g_signal_connect (G_OBJECT(cbox), "changed", @@ -1768,34 +1787,54 @@ void CsvImpTransAssist::acct_match_set_accounts () static void csv_tximp_acct_match_load_mappings (GtkTreeModel *mappings_store) { - for (auto iter : GncListModelContainer(mappings_store)) + // Set iter to first entry of store + GtkTreeIter iter; + auto valid = gtk_tree_model_get_iter_first (mappings_store, &iter); + + // Walk through the store trying to match to a map + while (valid) { // Walk through the list, reading each row - auto account = iter.get_column(MAPPING_ACCOUNT); - auto map_string = iter.get_column_string(MAPPING_STRING); + Account *account = nullptr; + gchar *map_string; + gtk_tree_model_get (GTK_TREE_MODEL(mappings_store), &iter, MAPPING_STRING, &map_string, MAPPING_ACCOUNT, &account, -1); // Look for an account matching the map_string // It may already be set in the tree model. If not we try to match the map_string with // - an entry in our saved account maps // - a full name of any of our existing accounts if (account || - (account = gnc_account_imap_find_any (gnc_get_current_book(), IMAP_CAT_CSV, map_string.c_str())) || - (account = gnc_account_lookup_by_full_name (gnc_get_current_root_account(), map_string.c_str()))) + (account = gnc_account_imap_find_any (gnc_get_current_book(), IMAP_CAT_CSV, map_string)) || + (account = gnc_account_lookup_by_full_name (gnc_get_current_root_account(), map_string))) { auto fullpath = gnc_account_get_full_name (account); - gtk_list_store_set (GTK_LIST_STORE(mappings_store), &iter.get_iter(), MAPPING_FULLPATH, fullpath, -1); - gtk_list_store_set (GTK_LIST_STORE(mappings_store), &iter.get_iter(), MAPPING_ACCOUNT, account, -1); + gtk_list_store_set (GTK_LIST_STORE(mappings_store), &iter, MAPPING_FULLPATH, fullpath, -1); + gtk_list_store_set (GTK_LIST_STORE(mappings_store), &iter, MAPPING_ACCOUNT, account, -1); g_free (fullpath); } + + g_free (map_string); + valid = gtk_tree_model_iter_next (mappings_store, &iter); } } static bool csv_tximp_acct_match_check_all (GtkTreeModel *model) { - for (auto iter : GncListModelContainer(model)) - if (!iter.get_column(MAPPING_ACCOUNT)) + // Set iter to first entry of store + GtkTreeIter iter; + auto valid = gtk_tree_model_get_iter_first (model, &iter); + + // Walk through the store looking for nullptr accounts + while (valid) + { + Account *account; + gtk_tree_model_get (model, &iter, MAPPING_ACCOUNT, &account, -1); + if (!account) return false; + + valid = gtk_tree_model_iter_next (model, &iter); + } return true; }