From 3410a03b2291f6a095040dca442df1bf903a284c Mon Sep 17 00:00:00 2001 From: Robert Fewell <14uBobIT@gmail.com> Date: Fri, 1 Dec 2017 10:44:15 +0000 Subject: [PATCH 01/48] Add property files for the csv price importer These files are largely based on the csv transaction importer --- gnucash/import-export/csv-imp/CMakeLists.txt | 2 + gnucash/import-export/csv-imp/Makefile.am | 2 + .../import-export/csv-imp/gnc-price-props.cpp | 508 ++++++++++++++++++ .../import-export/csv-imp/gnc-price-props.hpp | 110 ++++ 4 files changed, 622 insertions(+) create mode 100644 gnucash/import-export/csv-imp/gnc-price-props.cpp create mode 100644 gnucash/import-export/csv-imp/gnc-price-props.hpp diff --git a/gnucash/import-export/csv-imp/CMakeLists.txt b/gnucash/import-export/csv-imp/CMakeLists.txt index c2a77b82fc..e2fa97fee9 100644 --- a/gnucash/import-export/csv-imp/CMakeLists.txt +++ b/gnucash/import-export/csv-imp/CMakeLists.txt @@ -19,6 +19,7 @@ SET(csv_import_SOURCES gnc-csv-trans-settings.cpp gnc-dummy-tokenizer.cpp gnc-fw-tokenizer.cpp + gnc-price-props.cpp gnc-tokenizer.cpp gnc-trans-props.cpp gnc-tx-import.cpp @@ -44,6 +45,7 @@ SET(csv_import_noinst_HEADERS gnc-csv-trans-settings.hpp gnc-dummy-tokenizer.hpp gnc-fw-tokenizer.hpp + gnc-price-props.hpp gnc-tokenizer.hpp gnc-trans-props.hpp gnc-tx-import.hpp diff --git a/gnucash/import-export/csv-imp/Makefile.am b/gnucash/import-export/csv-imp/Makefile.am index 06ef43f207..8b13d01e18 100644 --- a/gnucash/import-export/csv-imp/Makefile.am +++ b/gnucash/import-export/csv-imp/Makefile.am @@ -13,6 +13,7 @@ libgncmod_csv_import_la_SOURCES = \ gnc-csv-gnumeric-popup.c \ gnc-dummy-tokenizer.cpp \ gnc-fw-tokenizer.cpp \ + gnc-price-props.cpp \ gnc-tokenizer.cpp \ gnc-tx-import.cpp \ gnc-trans-props.cpp \ @@ -28,6 +29,7 @@ noinst_HEADERS = \ gnc-csv-gnumeric-popup.h \ gnc-dummy-tokenizer.hpp \ gnc-fw-tokenizer.hpp \ + gnc-price-props.hpp \ gnc-tokenizer.hpp \ gnc-tx-import.hpp \ gnc-trans-props.hpp \ diff --git a/gnucash/import-export/csv-imp/gnc-price-props.cpp b/gnucash/import-export/csv-imp/gnc-price-props.cpp new file mode 100644 index 0000000000..1fdcaa53a3 --- /dev/null +++ b/gnucash/import-export/csv-imp/gnc-price-props.cpp @@ -0,0 +1,508 @@ +/********************************************************************\ + * gnc-price-props.cpp - encapsulate price properties for use * + * in the csv importer * + * * + * 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 * + * * +\********************************************************************/ + +extern "C" { +#include +#if PLATFORM(WINDOWS) +#include +#endif + +#include +#include + +#include "engine-helpers.h" +#include "gnc-ui-util.h" +#include "gnc-pricedb.h" + +} + +#include +#include +#include +#include "gnc-price-props.hpp" + +G_GNUC_UNUSED static QofLogModule log_module = GNC_MOD_IMPORT; + +/* This map contains a set of strings representing the different column types. */ +std::map gnc_csv_price_col_type_strs = { + { GncPricePropType::NONE, N_("None") }, + { GncPricePropType::DATE, N_("Date") }, + { GncPricePropType::AMOUNT, N_("Amount") }, + { GncPricePropType::CURRENCY_FROM, N_("Currency From") }, + { GncPricePropType::CURRENCY_TO, N_("Currency To") }, + { GncPricePropType::SYMBOL_FROM, N_("Symbol From") }, +}; + +/* Regular expressions used to parse dates per date format */ +const char* date_regex_price[] = { + "(?:" // either y-m-d + "(?[0-9]+)[-/.' ]+" + "(?[0-9]+)[-/.' ]+" + "(?[0-9]+)" + "|" // or CCYYMMDD + "(?[0-9]{4})" + "(?[0-9]{2})" + "(?[0-9]{2})" + ")", + + "(?:" // either d-m-y + "(?[0-9]+)[-/.' ]+" + "(?[0-9]+)[-/.' ]+" + "(?[0-9]+)" + "|" // or DDMMCCYY + "(?[0-9]{2})" + "(?[0-9]{2})" + "(?[0-9]{4})" + ")", + + "(?:" // either m-d-y + "(?[0-9]+)[-/.' ]+" + "(?[0-9]+)[-/.' ]+" + "(?[0-9]+)" + "|" // or MMDDCCYY + "(?[0-9]{2})" + "(?[0-9]{2})" + "(?[0-9]{4})" + ")", + + "(?:" // either d-m(-y) + "(?[0-9]+)[-/.' ]+" + "(?[0-9]+)(?:[-/.' ]+" + "(?[0-9]+))?" + "|" // or DDMM(CCYY) + "(?[0-9]{2})" + "(?[0-9]{2})" + "(?[0-9]+)?" + ")", + + "(?:" // either m-d(-y) + "(?[0-9]+)[-/.' ]+" + "(?[0-9]+)(?:[-/.' ]+" + "(?[0-9]+))?" + "|" // or MMDD(CCYY) + "(?[0-9]{2})" + "(?[0-9]{2})" + "(?[0-9]+)?" + ")", +}; + +/** Parses a string into a date, given a format. This function + * requires only knowing the order in which the year, month and day + * appear. For example, 01-02-2003 will be parsed the same way as + * 01/02/2003. + * @param date_str The string containing a date being parsed + * @param format An index specifying a format in date_format_user + * @exception std::invalid_argument if the string can't be parsed into a date. + * @return The parsed value of date_str on success, throws on failure + */ + +time64 parse_date_price (const std::string &date_str, int format) +{ + boost::regex r(date_regex_price[format]); + boost::smatch what; + if(!boost::regex_search(date_str, what, r)) + throw std::invalid_argument (_("Value can't be parsed into a date using the selected date format.")); // regex didn't find a match + + // Attention: different behavior from 2.6.x series ! + // If date format without year was selected, the match + // should NOT have found a year. + if ((format >= 3) && (what.length("YEAR") != 0)) + throw std::invalid_argument (_("Value appears to contain a year while the selected format forbids this.")); + + auto day = std::stoi (what.str("DAY")); + auto month = std::stoi (what.str("MONTH")); + + int year; + if (format < 3) + { + /* The input dates have a year, so use that one */ + year = std::stoi (what.str("YEAR")); + + /* Handle two-digit years. */ + if (year < 100) + { + /* We allow two-digit years in the range 1969 - 2068. */ + if (year < 69) + year += 2000; + else + year += 1900; + } + } + else + { + /* The input dates don't have a year, so work with today's year. + */ + gnc_timespec2dmy(timespec_now(), nullptr, nullptr, &year); + } + + auto ts = gnc_dmy2timespec_neutral(day, month, year); + if (ts.tv_sec == INT64_MAX) + throw std::invalid_argument (_("Value can't be parsed into a date using the selected date format.")); + + return ts.tv_sec; +} + + +/** Convert str into a GncRational using the user-specified (import) currency format. + * @param str The string to be parsed + * @param currency_format The currency format to use. + * @return a GncNumeric + * @exception May throw std::invalid argument if string can't be parsed properly + */ +GncNumeric parse_amount_price (const std::string &str, int currency_format) +{ + /* If a cell is empty or just spaces return invalid amount */ + if(!boost::regex_search(str, boost::regex("[0-9]"))) + throw std::invalid_argument (_("Value doesn't appear to contain a valid number.")); + + auto expr = boost::make_u32regex("[[:Sc:]]"); + std::string str_no_symbols = boost::u32regex_replace(str, expr, ""); + + /* Convert based on user chosen currency format */ + gnc_numeric val; + char *endptr; + switch (currency_format) + { + case 0: + /* Currency locale */ + if (!(xaccParseAmount (str_no_symbols.c_str(), TRUE, &val, &endptr))) + throw std::invalid_argument (_("Value can't be parsed into a number using the selected currency format.")); + break; + case 1: + /* Currency decimal period */ + if (!(xaccParseAmountExtended (str_no_symbols.c_str(), TRUE, '-', '.', ',', "\003\003", "$+", &val, &endptr))) + throw std::invalid_argument (_("Value can't be parsed into a number using the selected currency format.")); + break; + case 2: + /* Currency decimal comma */ + if (!(xaccParseAmountExtended (str_no_symbols.c_str(), TRUE, '-', ',', '.', "\003\003", "$+", &val, &endptr))) + throw std::invalid_argument (_("Value can't be parsed into a number using the selected currency format.")); + break; + } + + return GncNumeric(val); +} + +gnc_commodity* parse_commodity_price_comm (const std::string& comm_str) +{ + if (comm_str.empty()) + return nullptr; + + auto table = gnc_commodity_table_get_table (gnc_get_current_book()); + gnc_commodity* comm = nullptr; + + /* First try commodity as a unique name. */ + if (comm_str.find("::")) + comm = gnc_commodity_table_lookup_unique (table, comm_str.c_str()); + + /* Then try mnemonic in the currency namespace */ + if (!comm) + comm = gnc_commodity_table_lookup (table, + GNC_COMMODITY_NS_CURRENCY, comm_str.c_str()); + + if (!comm) + { + /* If that fails try mnemonic in all other namespaces */ + auto namespaces = gnc_commodity_table_get_namespaces(table); + for (auto ns = namespaces; ns; ns = ns->next) + { + gchar* ns_str = (gchar*)ns->data; + if (g_utf8_collate(ns_str, GNC_COMMODITY_NS_CURRENCY) == 0) + continue; + + comm = gnc_commodity_table_lookup (table, + ns_str, comm_str.c_str()); + if (comm) + break; + } + } + + if (!comm) + throw std::invalid_argument (_("Value can't be parsed into a valid commodity.")); + else + return comm; +} + +gnc_commodity * parse_commodity_price_sym (const std::string& sym_str, bool is_currency) +{ + if (sym_str.empty()) + return nullptr; + + auto commodity_table = gnc_get_current_commodities (); + GList *namespaces; + gnc_commodity *retval = nullptr; + gnc_commodity *tmp_commodity = nullptr; + char *tmp_namespace = nullptr; + GList *commodity_list = NULL; + GList *namespace_list = gnc_commodity_table_get_namespaces (commodity_table); + + namespace_list = g_list_first (namespace_list); + while (namespace_list != NULL && retval == NULL) + { + tmp_namespace = (char*)namespace_list->data; + DEBUG("Looking at namespace %s", tmp_namespace); + commodity_list = gnc_commodity_table_get_commodities (commodity_table, tmp_namespace); + commodity_list = g_list_first (commodity_list); + while (commodity_list != NULL && retval == NULL) + { + const char* tmp_mnemonic = NULL; + tmp_commodity = (gnc_commodity*)commodity_list->data; + DEBUG("Looking at commodity %s", gnc_commodity_get_fullname (tmp_commodity)); + tmp_mnemonic = gnc_commodity_get_mnemonic (tmp_commodity); + if (g_strcmp0 (tmp_mnemonic, sym_str.c_str()) == 0) + { + retval = tmp_commodity; + DEBUG("Commodity %s%s", gnc_commodity_get_fullname (retval), " matches."); + } + commodity_list = g_list_next (commodity_list); + } + namespace_list = g_list_next (namespace_list); + } + g_list_free (commodity_list); + g_list_free (namespace_list); + + if (!retval) + throw std::invalid_argument (_("Value can't be parsed into a valid commodity.")); + else + { + if (gnc_commodity_is_currency (retval) != is_currency) + throw std::invalid_argument (_("Value parsed into an invalid commodity for column type.")); + else + return retval; + } +} + +void GncImportPrice::set (GncPricePropType prop_type, const std::string& value) +{ + try + { + // Drop any existing error for the prop_type we're about to set + m_errors.erase(prop_type); + + gnc_commodity *comm = nullptr; + switch (prop_type) + { + case GncPricePropType::DATE: + m_date = boost::none; + m_date = parse_date_price (value, m_date_format); // Throws if parsing fails + break; + + case GncPricePropType::AMOUNT: + m_amount = boost::none; + m_amount = parse_amount_price (value, m_currency_format); // Will throw if parsing fails + break; + + case GncPricePropType::CURRENCY_FROM: + m_currency_from = boost::none; + comm = parse_commodity_price_sym (value, true); // Throws if parsing fails + if (comm) + m_currency_from = comm; + break; + + case GncPricePropType::CURRENCY_TO: + m_currency_to = boost::none; + comm = parse_commodity_price_sym (value, true); // Throws if parsing fails + if (comm) + m_currency_to = comm; + break; + + case GncPricePropType::SYMBOL_FROM: + m_symbol_from = boost::none; + comm = parse_commodity_price_sym (value, false); // Throws if parsing fails + if (comm) + m_symbol_from = comm; + break; + + default: + /* Issue a warning for all other prop_types. */ + PWARN ("%d is an invalid property for a Price", static_cast(prop_type)); + break; + } + } + catch (const std::invalid_argument& e) + { + auto err_str = std::string(_(gnc_csv_price_col_type_strs[prop_type])) + + std::string(_(" could not be understood.\n")) + + e.what(); + m_errors.emplace(prop_type, err_str); + throw std::invalid_argument (err_str); + } + catch (const std::out_of_range& e) + { + auto err_str = std::string(_(gnc_csv_price_col_type_strs[prop_type])) + + std::string(_(" could not be understood.\n")) + + e.what(); + m_errors.emplace(prop_type, err_str); + throw std::invalid_argument (err_str); + } +} + +void GncImportPrice::reset (GncPricePropType prop_type) +{ + try + { + set (prop_type, std::string()); + } + catch (...) + { + // Set with an empty string will effectively clear the property + // but can also set an error for the property. Clear that error here. + m_errors.erase(prop_type); + } +} + +std::string GncImportPrice::verify_essentials (void) +{ + /* Make sure this price has the minimum required set of properties defined */ + if (m_date == boost::none) + return _("No date column."); + else if (m_amount == boost::none) + return _("No amount column."); + else if (m_currency_to == boost::none) + return _("No Currency to column."); + else if ((m_symbol_from == boost::none) && (m_currency_from == boost::none)) + return _("No from column."); + else + return std::string(); +} + +bool GncImportPrice::create_price (QofBook* book, GNCPriceDB *pdb, bool over) +{ + /* Gently refuse to create the price if the basics are not set correctly + * This should have been tested before calling this function though! + */ + auto check = verify_essentials(); + if (!check.empty()) + { + PWARN ("Refusing to create price because essentials not set properly: %s", check.c_str()); + return false; + } + + Timespec date; + timespecFromTime64 (&date, *m_date); + date.tv_nsec = 0; + +#ifdef skip +//FIXME Numeric needs changing, copied from old version... + bool rev = false; + gnc_commodity *comm_from = nullptr; + + if (m_currency_from != boost::none) // Currency Import + { + // Check for currency in reverse direction. + GNCPrice *rev_price = gnc_pricedb_lookup_day (pdb, *m_currency_to, *m_currency_from, date); + if (rev_price != nullptr) + rev = true; + gnc_price_unref (rev_price); + + // Check for price less than 1, reverse if so. + if (gnc_numeric_compare (*m_amount, gnc_numeric_create (1, 1)) != 1) + rev = true; + + comm_from = *m_currency_from; + DEBUG("Commodity from is a Currency"); + } + else + comm_from = *m_symbol_from; + + DEBUG("Date is %s, Rev is %d, Commodity from is '%s', Currency is '%s', Amount is %s", gnc_print_date (date), + rev, gnc_commodity_get_fullname (comm_from), gnc_commodity_get_fullname (*m_currency_to), + gnc_num_dbg_to_string (*m_amount) ); + + GNCPrice *old_price = nullptr; + + // Should the commodities be reversed + if (rev) + old_price = gnc_pricedb_lookup_day (pdb, *m_currency_to, comm_from, date); + else + old_price = gnc_pricedb_lookup_day (pdb, comm_from, *m_currency_to, date); + + // Should old price be over writen + if ((old_price != nullptr) && (over == true)) + { + DEBUG("Over write"); + gnc_pricedb_remove_price (pdb, old_price); + gnc_price_unref (old_price); + old_price = nullptr; + } +#endif + bool ret_val = true; +#ifdef skip + // Create the new price + if (old_price == nullptr) + { + DEBUG("Create"); + GNCPrice *price = gnc_price_create (book); + gnc_price_begin_edit (price); + + if (rev) + { + gnc_price_set_commodity (price, *m_currency_to); + gnc_price_set_currency (price, comm_from); + *m_amount = gnc_numeric_convert (gnc_numeric_invert (*m_amount), + CURRENCY_DENOM, GNC_HOW_RND_ROUND_HALF_UP); + gnc_price_set_value (price, *m_amount); + } + else + { + gnc_price_set_commodity (price, comm_from); + gnc_price_set_currency (price, *m_currency_to); + gnc_price_set_value (price, *m_amount); + } + gnc_price_set_time (price, date); + gnc_price_set_source (price, PRICE_SOURCE_USER_PRICE); +//FIXME Not sure which one gnc_price_set_source (price, PRICE_SOURCE_FQ); + gnc_price_set_typestr (price, PRICE_TYPE_LAST); + gnc_price_commit_edit (price); + + bool perr = gnc_pricedb_add_price (pdb, price); + + gnc_price_unref (price); + + if (perr == false) + throw std::invalid_argument (_("Failed to create price from selected columns.")); +//FIXME Not sure about this, should this be a PWARN + } + else + +#endif + ret_val = false; + + return ret_val; +} + +static std::string gen_err_str (std::map& errors) +{ + auto full_error = std::string(); + for (auto error : errors) + { + full_error += (full_error.empty() ? "" : "\n") + error.second; + } + return full_error; +} + +std::string GncImportPrice::errors () +{ + return gen_err_str (m_errors); +} + diff --git a/gnucash/import-export/csv-imp/gnc-price-props.hpp b/gnucash/import-export/csv-imp/gnc-price-props.hpp new file mode 100644 index 0000000000..6f1189be1a --- /dev/null +++ b/gnucash/import-export/csv-imp/gnc-price-props.hpp @@ -0,0 +1,110 @@ +/********************************************************************\ + * gnc-price-props.hpp - encapsulate price properties for use * + * in the csv importer * + * * + * 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_PRICE_PROPS_HPP +#define GNC_PRICE_PROPS_HPP + +extern "C" { +#include +#if PLATFORM(WINDOWS) +#include +#endif + +#include + +#include "gnc-commodity.h" +} + +#include +#include +#include +#include +#include + +/** Enumeration for column types. These are the different types of + * columns that can exist in a CSV/Fixed-Width file. There should be + * no two columns with the same type except for the GncPricePropType::NONE + * type. */ +enum class GncPricePropType { + NONE, + DATE, + AMOUNT, + CURRENCY_FROM, + CURRENCY_TO, + SYMBOL_FROM, + PRICE_PROPS = SYMBOL_FROM +}; + +/** Maps all column types to a string representation. + * The actual definition is in gnc-csv-imp-prices.cpp. + * Attention: that definition should be adjusted for any + * changes to enum class GncPricePropType ! */ +extern std::map gnc_csv_price_col_type_strs; + +/** Functor to check if the above map has an element of which + * the value equals name. To be used with std::find_if. + */ +struct test_price_prop_type_str +{ + test_price_prop_type_str( const char* name ) : m_name(name) {} + bool operator()( const std::pair& v ) const + { + return !g_strcmp0(v.second, m_name); + } +private: + const char *m_name; +}; + +time64 parse_date_price (const std::string &date_str, int format); +gnc_commodity* parse_commodity_price_comm (const std::string& comm_str); +gnc_commodity* parse_commodity_price_sym (const std::string& comm_str, bool is_currency); +GncNumeric parse_amount_price (const std::string &str, int currency_format); + +struct GncImportPrice +{ +public: + GncImportPrice (int date_format, int currency_format) : m_date_format{date_format}, + m_currency_format{currency_format}{}; + + void set (GncPricePropType prop_type, const std::string& value); + void set_date_format (int date_format) { m_date_format = date_format ;} + void set_currency_format (int currency_format) { m_currency_format = currency_format ;} + void reset (GncPricePropType prop_type); + std::string verify_essentials (void); + bool create_price (QofBook* book, GNCPriceDB *pdb, bool over); + std::string errors(); + +private: + int m_date_format; + int m_currency_format; + boost::optional m_date; + boost::optional m_amount; + boost::optional m_currency_from; + boost::optional m_currency_to; + boost::optional m_symbol_from; + bool created = false; + + std::map m_errors; +}; + +#endif From eb712dc7d88dc16a5c751a8fea988b35dd20da47 Mon Sep 17 00:00:00 2001 From: Robert Fewell <14uBobIT@gmail.com> Date: Fri, 1 Dec 2017 10:53:41 +0000 Subject: [PATCH 02/48] Add price import files for the csv price importer These files are largely based on the csv transaction importer and with minimum of changes to settings files. --- gnucash/import-export/csv-imp/CMakeLists.txt | 2 + gnucash/import-export/csv-imp/Makefile.am | 2 + .../csv-imp/gnc-csv-trans-settings.cpp | 8 + .../csv-imp/gnc-csv-trans-settings.hpp | 4 +- .../csv-imp/gnc-price-import.cpp | 654 ++++++++++++++++++ .../csv-imp/gnc-price-import.hpp | 160 +++++ .../import-export/csv-imp/gnc-price-props.hpp | 2 +- 7 files changed, 830 insertions(+), 2 deletions(-) create mode 100644 gnucash/import-export/csv-imp/gnc-price-import.cpp create mode 100644 gnucash/import-export/csv-imp/gnc-price-import.hpp diff --git a/gnucash/import-export/csv-imp/CMakeLists.txt b/gnucash/import-export/csv-imp/CMakeLists.txt index e2fa97fee9..0bc7d2938f 100644 --- a/gnucash/import-export/csv-imp/CMakeLists.txt +++ b/gnucash/import-export/csv-imp/CMakeLists.txt @@ -19,6 +19,7 @@ SET(csv_import_SOURCES gnc-csv-trans-settings.cpp gnc-dummy-tokenizer.cpp gnc-fw-tokenizer.cpp + gnc-price-import.cpp gnc-price-props.cpp gnc-tokenizer.cpp gnc-trans-props.cpp @@ -45,6 +46,7 @@ SET(csv_import_noinst_HEADERS gnc-csv-trans-settings.hpp gnc-dummy-tokenizer.hpp gnc-fw-tokenizer.hpp + gnc-price-import.hpp gnc-price-props.hpp gnc-tokenizer.hpp gnc-trans-props.hpp diff --git a/gnucash/import-export/csv-imp/Makefile.am b/gnucash/import-export/csv-imp/Makefile.am index 8b13d01e18..408396331a 100644 --- a/gnucash/import-export/csv-imp/Makefile.am +++ b/gnucash/import-export/csv-imp/Makefile.am @@ -13,6 +13,7 @@ libgncmod_csv_import_la_SOURCES = \ gnc-csv-gnumeric-popup.c \ gnc-dummy-tokenizer.cpp \ gnc-fw-tokenizer.cpp \ + gnc-price-import.cpp \ gnc-price-props.cpp \ gnc-tokenizer.cpp \ gnc-tx-import.cpp \ @@ -29,6 +30,7 @@ noinst_HEADERS = \ gnc-csv-gnumeric-popup.h \ gnc-dummy-tokenizer.hpp \ gnc-fw-tokenizer.hpp \ + gnc-price-import.hpp \ gnc-price-props.hpp \ gnc-tokenizer.hpp \ gnc-tx-import.hpp \ diff --git a/gnucash/import-export/csv-imp/gnc-csv-trans-settings.cpp b/gnucash/import-export/csv-imp/gnc-csv-trans-settings.cpp index 6611875380..48cfdce9d2 100644 --- a/gnucash/import-export/csv-imp/gnc-csv-trans-settings.cpp +++ b/gnucash/import-export/csv-imp/gnc-csv-trans-settings.cpp @@ -108,6 +108,14 @@ static std::shared_ptr create_int_gnc_exp_preset(void) GncTransPropType::PRICE }; + preset->m_column_types_price = { + GncPricePropType::DATE, + GncPricePropType::AMOUNT, + GncPricePropType::CURRENCY_FROM, + GncPricePropType::CURRENCY_TO, + GncPricePropType::SYMBOL_FROM + }; + return preset; } diff --git a/gnucash/import-export/csv-imp/gnc-csv-trans-settings.hpp b/gnucash/import-export/csv-imp/gnc-csv-trans-settings.hpp index 379c6600d8..93275f2da0 100644 --- a/gnucash/import-export/csv-imp/gnc-csv-trans-settings.hpp +++ b/gnucash/import-export/csv-imp/gnc-csv-trans-settings.hpp @@ -36,6 +36,7 @@ extern "C" { #include #include #include "gnc-trans-props.hpp" +#include "gnc-price-props.hpp" #include "gnc-tokenizer.hpp" /** Enumeration for separator checkbutton types. These are the @@ -93,7 +94,8 @@ std::string m_separators; // Separators for csv format Account *m_base_account; // Base account std::vector m_column_types; // The Column types in order -std::vector m_column_widths; // The Column widths +std::vector m_column_types_price; // The Column Price types in order +std::vector m_column_widths; // The Column widths bool m_load_error; // Was there an error while parsing the state file ? }; diff --git a/gnucash/import-export/csv-imp/gnc-price-import.cpp b/gnucash/import-export/csv-imp/gnc-price-import.cpp new file mode 100644 index 0000000000..09deb934e0 --- /dev/null +++ b/gnucash/import-export/csv-imp/gnc-price-import.cpp @@ -0,0 +1,654 @@ +/********************************************************************\ + * gnc-price-import.cpp - import prices from csv files * + * * + * 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 * + * * +\********************************************************************/ + +#include + +extern "C" { +#include +#if PLATFORM(WINDOWS) +#include +#endif + +#include + +#include "gnc-ui-util.h" //get book +#include "gnc-commodity.h" +#include "gnc-pricedb.h" +} + +#include +#include + +#include "gnc-price-import.hpp" +#include "gnc-price-props.hpp" +#include "gnc-csv-tokenizer.hpp" +#include "gnc-fw-tokenizer.hpp" +#include "gnc-csv-trans-settings.hpp" + +G_GNUC_UNUSED static QofLogModule log_module = GNC_MOD_IMPORT; + +const int num_date_formats_price = 5; +const gchar* date_format_user_price[] = {N_("y-m-d"), + N_("d-m-y"), + N_("m-d-y"), + N_("d-m"), + N_("m-d") + }; + +const int num_currency_formats_price = 3; +const gchar* currency_format_user_price[] = {N_("Locale"), + N_("Period: 123,456.78"), + N_("Comma: 123.456,78") + }; + + +/** Constructor for GncPriceImport. + * @return Pointer to a new GncCSvParseData + */ +GncPriceImport::GncPriceImport(GncImpFileFormat format) +{ + /* All of the data pointers are initially NULL. This is so that, if + * gnc_csv_parse_data_free is called before all of the data is + * initialized, only the data that needs to be freed is freed. */ + m_skip_errors = false; + file_format(m_settings.m_file_format = format); +} + +/** Destructor for GncPriceImport. + */ +GncPriceImport::~GncPriceImport() +{ +} + +/** Sets the file format for the file to import, which + * may cause the file to be reloaded as well if the + * previously set file format was different and a + * filename was already set. + * @param format the new format to set + * @exception std::ifstream::failure if file reloading fails + */ +void GncPriceImport::file_format(GncImpFileFormat format) +{ + if (m_tokenizer && m_settings.m_file_format == format) + return; + + auto new_encoding = std::string("UTF-8"); + auto new_imp_file = std::string(); + + // Recover common settings from old tokenizer + if (m_tokenizer) + { + new_encoding = m_tokenizer->encoding(); + new_imp_file = m_tokenizer->current_file(); + if (file_format() == GncImpFileFormat::FIXED_WIDTH) + { + auto fwtok = dynamic_cast(m_tokenizer.get()); + if (!fwtok->get_columns().empty()) + m_settings.m_column_widths = fwtok->get_columns(); + } + } + + m_settings.m_file_format = format; + m_tokenizer = gnc_tokenizer_factory(m_settings.m_file_format); + + // Set up new tokenizer with common settings + // recovered from old tokenizer + m_tokenizer->encoding(new_encoding); + load_file(new_imp_file); + + // Restore potentially previously set separators or column_widths + if ((file_format() == GncImpFileFormat::CSV) + && !m_settings.m_separators.empty()) + separators (m_settings.m_separators); + else if ((file_format() == GncImpFileFormat::FIXED_WIDTH) + && !m_settings.m_column_widths.empty()) + { + auto fwtok = dynamic_cast(m_tokenizer.get()); + fwtok->columns (m_settings.m_column_widths); + } + +} + +GncImpFileFormat GncPriceImport::file_format() +{ + return m_settings.m_file_format; +} + +void GncPriceImport::over_write (bool over) +{ + m_over_write = over; +} + +bool GncPriceImport::over_write () { return m_over_write; } + +void GncPriceImport::reset_formatted_column (std::vector& col_types) +{ + for (auto col_type: col_types) + { + auto col = std::find (m_settings.m_column_types_price.begin(), + m_settings.m_column_types_price.end(), col_type); + if (col != m_settings.m_column_types_price.end()) + set_column_type_price (col - m_settings.m_column_types_price.begin(), col_type, true); + } +} + +void GncPriceImport::currency_format (int currency_format) +{ + m_settings.m_currency_format = currency_format; + + /* Reparse all currency related columns */ + std::vector commodities = { GncPricePropType::AMOUNT }; + reset_formatted_column (commodities); +} +int GncPriceImport::currency_format () { return m_settings.m_currency_format; } + +void GncPriceImport::date_format (int date_format) +{ + m_settings.m_date_format = date_format; + + /* Reparse all date related columns */ + std::vector dates = { GncPricePropType::DATE }; + reset_formatted_column (dates); +} +int GncPriceImport::date_format () { return m_settings.m_date_format; } + +/** Converts raw file data using a new encoding. This function must be + * called after load_file only if load_file guessed + * the wrong encoding. + * @param encoding Encoding that data should be translated using + */ +void GncPriceImport::encoding (const std::string& encoding) +{ + + // TODO investigate if we can catch conversion errors and report them + if (m_tokenizer) + { + m_tokenizer->encoding(encoding); // May throw + try + { + tokenize(false); + } + catch (...) + { }; + } + + m_settings.m_encoding = encoding; +} + +std::string GncPriceImport::encoding () { return m_settings.m_encoding; } + +void GncPriceImport::update_skipped_lines(boost::optional start, boost::optional end, + boost::optional alt, boost::optional errors) +{ + if (start) + m_settings.m_skip_start_lines = *start; + if (end) + m_settings.m_skip_end_lines = *end; + if (alt) + m_settings.m_skip_alt_lines = *alt; + if (errors) + m_skip_errors = *errors; + + for (uint32_t i = 0; i < m_parsed_lines.size(); i++) + { + std::get<3>(m_parsed_lines[i]) = + ((i < skip_start_lines()) || // start rows to skip + (i >= m_parsed_lines.size() - skip_end_lines()) || // end rows to skip + (((i - skip_start_lines()) % 2 == 1) && // skip every second row... + skip_alt_lines()) || // ...if requested + (m_skip_errors && !std::get<1>(m_parsed_lines[i]).empty())); // skip lines with errors + } +} + +uint32_t GncPriceImport::skip_start_lines () { return m_settings.m_skip_start_lines; } +uint32_t GncPriceImport::skip_end_lines () { return m_settings.m_skip_end_lines; } +bool GncPriceImport::skip_alt_lines () { return m_settings.m_skip_alt_lines; } +bool GncPriceImport::skip_err_lines () { return m_skip_errors; } + +void GncPriceImport::separators (std::string separators) +{ + if (file_format() != GncImpFileFormat::CSV) + return; + + m_settings.m_separators = separators; + auto csvtok = dynamic_cast(m_tokenizer.get()); + csvtok->set_separators (separators); + +} +std::string GncPriceImport::separators () { return m_settings.m_separators; } + +void GncPriceImport::settings (const CsvTransSettings& settings) +{ + /* First apply file format as this may recreate the tokenizer */ + file_format (settings.m_file_format); + /* Only then apply the other settings */ + m_settings = settings; + encoding (m_settings.m_encoding); + + if (file_format() == GncImpFileFormat::CSV) + separators (m_settings.m_separators); + else if (file_format() == GncImpFileFormat::FIXED_WIDTH) + { + auto fwtok = dynamic_cast(m_tokenizer.get()); + fwtok->columns (m_settings.m_column_widths); + } + try + { + tokenize(false); + } + catch (...) + { }; + + /* Tokenizing will clear column types, reset them here + * based on the loaded settings. + */ + std::copy_n (settings.m_column_types_price.begin(), + std::min (m_settings.m_column_types_price.size(), settings.m_column_types_price.size()), + m_settings.m_column_types_price.begin()); + +} + +bool GncPriceImport::save_settings () +{ + + if (trans_preset_is_reserved_name (m_settings.m_name)) + return true; + + /* separators are already copied to m_settings in the separators + * function above. However this is not the case for the column + * widths in fw mode, so do this now. + */ + if (file_format() == GncImpFileFormat::FIXED_WIDTH) + { + auto fwtok = dynamic_cast(m_tokenizer.get()); + m_settings.m_column_widths = fwtok->get_columns(); + } + + return m_settings.save(); +} + +void GncPriceImport::settings_name (std::string name) { m_settings.m_name = name; } +std::string GncPriceImport::settings_name () { return m_settings.m_name; } + +/** Loads a file into a GncPriceImport. This is the first function + * that must be called after creating a new GncPriceImport. As long as + * this function didn't run successfully, the importer can't proceed. + * @param filename Name of the file that should be opened + * @exception may throw std::ifstream::failure on any io error + */ +void GncPriceImport::load_file (const std::string& filename) +{ + + /* Get the raw data first and handle an error if one occurs. */ + try + { + m_tokenizer->load_file (filename); + return; + } + catch (std::ifstream::failure& ios_err) + { + // Just log the error and pass it on the call stack for proper handling + PWARN ("Error: %s", ios_err.what()); + throw; + } +} + +/** Splits a file into cells. This requires having an encoding that + * works (see GncPriceImport::convert_encoding). Tokenizing related options + * should be set to the user's selections before calling this + * function. + * Notes: - this function must be called with guessColTypes set to true once + * before calling it with guessColTypes set to false. + * - if guessColTypes is true, all the column types will be set + * GncPricePropType::NONE right now as real guessing isn't implemented yet + * @param guessColTypes true to guess what the types of columns are based on the cell contents + * @exception std::range_error if tokenizing failed + */ +void GncPriceImport::tokenize (bool guessColTypes) +{ + if (!m_tokenizer) + return; + + uint32_t max_cols = 0; + m_tokenizer->tokenize(); + m_parsed_lines.clear(); + for (auto tokenized_line : m_tokenizer->get_tokens()) + { + m_parsed_lines.push_back (std::make_tuple (tokenized_line, std::string(), + std::make_shared(date_format(), currency_format()), + false)); + auto length = tokenized_line.size(); + if (length > max_cols) + max_cols = length; + } + + /* If it failed, generate an error. */ + if (m_parsed_lines.size() == 0) + { + throw (std::range_error ("Tokenizing failed.")); + return; + } + + m_settings.m_column_types_price.resize(max_cols, GncPricePropType::NONE); + + /* Force reinterpretation of already set columns and/or base_account */ + for (uint32_t i = 0; i < m_settings.m_column_types_price.size(); i++) + set_column_type_price (i, m_settings.m_column_types_price[i], true); + + if (guessColTypes) + { + /* Guess column_types based + * on the contents of each column. */ + /* TODO Make it actually guess. */ + } +} + + +struct ErrorListPrice +{ +public: + void add_error (std::string msg); + std::string str(); + bool empty() { return m_error.empty(); } +private: + std::string m_error; +}; + +void ErrorListPrice::add_error (std::string msg) +{ + m_error += "- " + msg + "\n"; +} + +std::string ErrorListPrice::str() +{ + return m_error.substr(0, m_error.size() - 1); +} + + +/* Test for the required minimum number of columns selected and + * the selection is consistent. + * @param An ErrorListPrice object to which all found issues are added. + */ +void GncPriceImport::verify_column_selections (ErrorListPrice& error_msg) +{ + /* Verify if a date column is selected and it's parsable. + */ + if (!check_for_column_type(GncPricePropType::DATE)) + error_msg.add_error( _("Please select a date column.")); + + /* Verify an amount column is selected. + */ + if (!check_for_column_type(GncPricePropType::AMOUNT)) + error_msg.add_error( _("Please select an amount column.")); + + /* Verify an Currency to column is selected. + */ + if (!check_for_column_type(GncPricePropType::CURRENCY_TO)) + error_msg.add_error( _("Please select a Currency to column.")); + + /* Verify at least one from column (symbol_from or currency_from) column is selected. + */ + if (!check_for_column_type(GncPricePropType::SYMBOL_FROM) && + !check_for_column_type(GncPricePropType::CURRENCY_FROM)) + error_msg.add_error( _("Please select a symbol or currency from column.")); +} + + +/* Check whether the chosen settings can successfully parse + * the import data. This will check: + * - there's at least one line selected for import + * - the minimum number of columns is selected + * - the values in the selected columns can be parsed meaningfully. + * @return An empty string if all checks passed or the reason + * verification failed otherwise. + */ +std::string GncPriceImport::verify () +{ + auto newline = std::string(); + auto error_msg = ErrorListPrice(); + + /* Check if the import file did actually contain any information */ + if (m_parsed_lines.size() == 0) + { + error_msg.add_error(_("No valid data found in the selected file. It may be empty or the selected encoding is wrong.")); + return error_msg.str(); + } + + /* Check if at least one line is selected for importing */ + auto skip_alt_offset = m_settings.m_skip_alt_lines ? 1 : 0; + if (m_settings.m_skip_start_lines + m_settings.m_skip_end_lines + skip_alt_offset >= m_parsed_lines.size()) + { + error_msg.add_error(_("No lines are selected for importing. Please reduce the number of lines to skip.")); + return error_msg.str(); + } + + verify_column_selections (error_msg); + + update_skipped_lines (boost::none, boost::none, boost::none, boost::none); + + auto have_line_errors = false; + for (auto line : m_parsed_lines) + { + if (!std::get<3>(line) && !std::get<1>(line).empty()) + { + have_line_errors = true; + break; + } + } + + if (have_line_errors) + error_msg.add_error( _("Not all fields could be parsed. Please correct the issues reported for each line or adjust the lines to skip.")); + + return error_msg.str(); +} + +/** Checks whether the parsed line contains all essential properties. + * @param parsed_line The line we are checking + * @exception std::invalid_argument in an essential property is missing + */ +static void price_properties_verify_essentials (std::vector::iterator& parsed_line) +{ + std::string error_message; + std::shared_ptr price_props; + std::tie(std::ignore, error_message, price_props, std::ignore) = *parsed_line; + + auto price_error = price_props->verify_essentials(); + + error_message.clear(); + if (!price_error.empty()) + { + error_message += price_error; + error_message += "\n"; + } + + if (!error_message.empty()) + throw std::invalid_argument(error_message); +} + +void GncPriceImport::create_price (std::vector::iterator& parsed_line) +{ + StrVec line; + std::string error_message; + std::shared_ptr price_props = nullptr; + bool skip_line = false; + std::tie(line, error_message, price_props, skip_line) = *parsed_line; + + if (skip_line) + return; + + error_message.clear(); + + /* If column parsing was successful, convert price properties into a price. */ + try + { + price_properties_verify_essentials (parsed_line); + + QofBook* book = gnc_get_current_book(); + GNCPriceDB *pdb = gnc_pricedb_get_db (book); + + /* If all went well, add this price to the list. */ + auto price_created = price_props->create_price (book, pdb, m_over_write); +//FIXME Need to look at this + if (price_created) + m_prices_added++; + else + m_prices_duplicated++; + } + catch (const std::invalid_argument& e) + { + error_message = e.what(); + PINFO("User warning: %s", error_message.c_str()); + } +} + + +/** Creates a list of prices from parsed data. The parsed data + * will first be validated. If any errors are found in lines that are marked + * for processing (ie not marked to skip) this function will + * throw an error. + * @param skip_errors true skip over lines with errors + * @exception throws std::invalid_argument if data validation or processing fails. + */ +void GncPriceImport::create_prices () +{ + /* Start with verifying the current data. */ + auto verify_result = verify(); + if (!verify_result.empty()) + throw std::invalid_argument (verify_result); + + m_prices_added = 0; + m_prices_duplicated = 0; + + /* Iterate over all parsed lines */ + for (auto parsed_lines_it = m_parsed_lines.begin(); + parsed_lines_it != m_parsed_lines.end(); + ++parsed_lines_it) + { + /* Skip current line if the user specified so */ + if ((std::get<3>(*parsed_lines_it))) + continue; + + /* Should not throw anymore, otherwise verify needs revision */ + create_price (parsed_lines_it); + } + PINFO("Number of lines is %d, added is %d, duplicates is %d", + (int)m_parsed_lines.size(), m_prices_added, m_prices_duplicated); +} + +bool +GncPriceImport::check_for_column_type (GncPricePropType type) +{ + return (std::find (m_settings.m_column_types_price.begin(), + m_settings.m_column_types_price.end(), type) + != m_settings.m_column_types_price.end()); +} + +/* A helper function intended to be called only from set_column_type_price */ +void GncPriceImport::update_price_props (uint32_t row, uint32_t col, GncPricePropType prop_type) +{ + if (prop_type == GncPricePropType::NONE) + return; /* Only deal with price related properties. */ + + auto price_props = std::make_shared (*(std::get<2>(m_parsed_lines[row])).get()); + auto value = std::string(); + + if (col < std::get<0>(m_parsed_lines[row]).size()) + value = std::get<0>(m_parsed_lines[row]).at(col); + + if (value.empty()) + price_props->reset (prop_type); + else + { + try + { + price_props->set(prop_type, value); + } + catch (const std::exception& e) + { + /* Do nothing, just prevent the exception from escalating up + * However log the error if it happens on a row that's not skipped + */ + if (!std::get<3>(m_parsed_lines[row])) + PINFO("User warning: %s", e.what()); + } + } + /* Store the result */ + std::get<2>(m_parsed_lines[row]) = price_props; +} + +void +GncPriceImport::set_column_type_price (uint32_t position, GncPricePropType type, bool force) +{ + if (position >= m_settings.m_column_types_price.size()) + return; + + auto old_type = m_settings.m_column_types_price[position]; + if ((type == old_type) && !force) + return; /* Nothing to do */ + + // Column types should be unique, so remove any previous occurrence of the new type + std::replace(m_settings.m_column_types_price.begin(), m_settings.m_column_types_price.end(), + type, GncPricePropType::NONE); + + m_settings.m_column_types_price.at (position) = type; + + /* Update the preparsed data */ + for (auto parsed_lines_it = m_parsed_lines.begin(); + parsed_lines_it != m_parsed_lines.end(); + ++parsed_lines_it) + { + /* Reset date and currency formats for each price props object + * to ensure column updates use the most recent one + */ + std::get<2>(*parsed_lines_it)->set_date_format (m_settings.m_date_format); + std::get<2>(*parsed_lines_it)->set_currency_format (m_settings.m_currency_format); + + uint32_t row = parsed_lines_it - m_parsed_lines.begin(); + + /* If the column type actually changed, first reset the property + * represented by the old column type + */ + if (old_type != type) + { + auto old_col = std::get<0>(*parsed_lines_it).size(); // Deliberately out of bounds to trigger a reset! + if ((old_type > GncPricePropType::NONE) + && (old_type <= GncPricePropType::PRICE_PROPS)) + update_price_props (row, old_col, old_type); + } + /* Then set the property represented by the new column type */ + if ((type > GncPricePropType::NONE) + && (type <= GncPricePropType::PRICE_PROPS)) + update_price_props (row, position, type); + + /* Report errors if there are any */ + auto price_errors = std::get<2>(*parsed_lines_it)->errors(); + std::get<1>(*parsed_lines_it) = + price_errors + + (price_errors.empty() ? std::string() : "\n"); + } +} + +std::vector GncPriceImport::column_types_price () +{ + return m_settings.m_column_types_price; +} + diff --git a/gnucash/import-export/csv-imp/gnc-price-import.hpp b/gnucash/import-export/csv-imp/gnc-price-import.hpp new file mode 100644 index 0000000000..0898215b29 --- /dev/null +++ b/gnucash/import-export/csv-imp/gnc-price-import.hpp @@ -0,0 +1,160 @@ +/********************************************************************\ + * gnc-price-import.hpp - import prices from csv files * + * * + * 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 * +\********************************************************************/ + +/** @file + @brief Class to import prices from CSV or fixed width files + * + gnc-price-import.hpp + @author Copyright (c) 2015 Geert Janssens + @author Copyright (c) 2017 Robert Fewell + */ + +#ifndef GNC_PRICE_IMPORT_HPP +#define GNC_PRICE_IMPORT_HPP + +extern "C" { +#include "config.h" + +} + +#include +#include +#include +#include + +#include "gnc-tokenizer.hpp" +#include "gnc-price-props.hpp" +#include "gnc-csv-trans-settings.hpp" +#include + +/* A set of currency formats that the user sees. */ +extern const int num_currency_formats; +extern const gchar* currency_format_user[]; + +/* A set of date formats that the user sees. */ +extern const int num_date_formats; +extern const gchar* date_format_user[]; + +/** Tuple to hold + * - a tokenized line of input + * - an optional error string + * - a struct to hold user selected properties for a price */ +using parse_line_t = std::tuple, + bool>; +struct ErrorListPrice; + +/** The actual PriceImport class + * It's intended to use in the following sequence of actions: + * - set a file format + * - load a file + * - optionally convert it's encoding + * - parse the file into lines, which in turn are split up in columns + * the result of this step can be queried from tokenizer + * - the user should now map the columns to types, which is stored in column_types + * - last step is convert the mapped columns into a list of transactions + * - this list will then be passed on the the generic importer for further processing */ +class GncPriceImport +{ +public: + // Constructor - Destructor + GncPriceImport(GncImpFileFormat format = GncImpFileFormat::UNKNOWN); + ~GncPriceImport(); + + void file_format(GncImpFileFormat format); + GncImpFileFormat file_format(); + + void over_write (bool over); + bool over_write (); + + void currency_format (int currency_format); + int currency_format (); + + void date_format (int date_format); + int date_format (); + + void encoding (const std::string& encoding); + std::string encoding (); + + void update_skipped_lines (boost::optional start, boost::optional end, + boost::optional alt, boost::optional errors); + uint32_t skip_start_lines (); + uint32_t skip_end_lines (); + bool skip_alt_lines (); + bool skip_err_lines (); + + void separators (std::string separators); + std::string separators (); + + void settings (const CsvTransSettings& settings); + bool save_settings (); + + void settings_name (std::string name); + std::string settings_name (); + + + void load_file (const std::string& filename); + + void tokenize (bool guessColTypes); + + std::string verify(); + + /** This function will attempt to convert all tokenized lines into + * prices using the column types the user has set. + */ + void create_prices (); + bool check_for_column_type (GncPricePropType type); + void set_column_type_price (uint32_t position, GncPricePropType type, bool force = false); + std::vector column_types_price (); + + std::unique_ptr m_tokenizer; /**< Will handle file loading/encoding conversion/splitting into fields */ + std::vector m_parsed_lines; /**< source file parsed into a two-dimensional array of strings. + Per line also holds possible error messages and objects with extracted + price properties. */ + int m_prices_added; + int m_prices_duplicated; + +private: + /** A helper function used by create_prices. It will attempt + * to convert a single tokenized line into a price using + * the column types the user has set. + */ + void create_price (std::vector::iterator& parsed_line); + + void verify_column_selections (ErrorListPrice& error_msg); + + /* Internal helper function to force reparsing of columns subject to format changes */ + void reset_formatted_column (std::vector& col_types); + + /* Two internal helper functions that should only be called from within + * set_column_type_price for consistency (otherwise error messages may not be (re)set) + */ + void update_price_props (uint32_t row, uint32_t col, GncPricePropType prop_type); + + struct CsvTranSettings; + CsvTransSettings m_settings; + bool m_skip_errors; + bool m_over_write; +}; + + +#endif diff --git a/gnucash/import-export/csv-imp/gnc-price-props.hpp b/gnucash/import-export/csv-imp/gnc-price-props.hpp index 6f1189be1a..d1012805f1 100644 --- a/gnucash/import-export/csv-imp/gnc-price-props.hpp +++ b/gnucash/import-export/csv-imp/gnc-price-props.hpp @@ -31,7 +31,7 @@ extern "C" { #endif #include - +#include "gnc-pricedb.h" #include "gnc-commodity.h" } From 4fe77a577ae429c0407868a62805bd4095c87ca8 Mon Sep 17 00:00:00 2001 From: Robert Fewell <14uBobIT@gmail.com> Date: Fri, 1 Dec 2017 10:55:29 +0000 Subject: [PATCH 03/48] Rename function gnc_csv_price_col_type_strs to gnc_price_col_type_strs --- gnucash/import-export/csv-imp/gnc-price-props.cpp | 6 +++--- gnucash/import-export/csv-imp/gnc-price-props.hpp | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/gnucash/import-export/csv-imp/gnc-price-props.cpp b/gnucash/import-export/csv-imp/gnc-price-props.cpp index 1fdcaa53a3..a39f178851 100644 --- a/gnucash/import-export/csv-imp/gnc-price-props.cpp +++ b/gnucash/import-export/csv-imp/gnc-price-props.cpp @@ -44,7 +44,7 @@ extern "C" { G_GNUC_UNUSED static QofLogModule log_module = GNC_MOD_IMPORT; /* This map contains a set of strings representing the different column types. */ -std::map gnc_csv_price_col_type_strs = { +std::map gnc_price_col_type_strs = { { GncPricePropType::NONE, N_("None") }, { GncPricePropType::DATE, N_("Date") }, { GncPricePropType::AMOUNT, N_("Amount") }, @@ -341,7 +341,7 @@ void GncImportPrice::set (GncPricePropType prop_type, const std::string& value) } catch (const std::invalid_argument& e) { - auto err_str = std::string(_(gnc_csv_price_col_type_strs[prop_type])) + + auto err_str = std::string(_(gnc_price_col_type_strs[prop_type])) + std::string(_(" could not be understood.\n")) + e.what(); m_errors.emplace(prop_type, err_str); @@ -349,7 +349,7 @@ void GncImportPrice::set (GncPricePropType prop_type, const std::string& value) } catch (const std::out_of_range& e) { - auto err_str = std::string(_(gnc_csv_price_col_type_strs[prop_type])) + + auto err_str = std::string(_(gnc_price_col_type_strs[prop_type])) + std::string(_(" could not be understood.\n")) + e.what(); m_errors.emplace(prop_type, err_str); diff --git a/gnucash/import-export/csv-imp/gnc-price-props.hpp b/gnucash/import-export/csv-imp/gnc-price-props.hpp index d1012805f1..57083e9a4b 100644 --- a/gnucash/import-export/csv-imp/gnc-price-props.hpp +++ b/gnucash/import-export/csv-imp/gnc-price-props.hpp @@ -56,10 +56,10 @@ enum class GncPricePropType { }; /** Maps all column types to a string representation. - * The actual definition is in gnc-csv-imp-prices.cpp. + * The actual definition is in gnc-price-props.cpp. * Attention: that definition should be adjusted for any * changes to enum class GncPricePropType ! */ -extern std::map gnc_csv_price_col_type_strs; +extern std::map gnc_price_col_type_strs; /** Functor to check if the above map has an element of which * the value equals name. To be used with std::find_if. From 393b8a126d0dfd7236800fd2705eb8d62deeaf98 Mon Sep 17 00:00:00 2001 From: Robert Fewell <14uBobIT@gmail.com> Date: Fri, 1 Dec 2017 11:01:16 +0000 Subject: [PATCH 04/48] Add CSV Price importer assistant files These file are largely based on the csv transaction importer. They are just the start for subsequent changes. --- gnucash/import-export/csv-imp/CMakeLists.txt | 4 +- gnucash/import-export/csv-imp/Makefile.am | 3 + .../csv-imp/assistant-csv-price-import.cpp | 1621 +++++++++++++++++ .../csv-imp/assistant-csv-price-import.glade | 1001 ++++++++++ .../csv-imp/assistant-csv-price-import.h | 36 + .../csv-imp/gnc-plugin-csv-import-ui.xml | 1 + .../csv-imp/gnc-plugin-csv-import.c | 14 + 7 files changed, 2679 insertions(+), 1 deletion(-) create mode 100644 gnucash/import-export/csv-imp/assistant-csv-price-import.cpp create mode 100644 gnucash/import-export/csv-imp/assistant-csv-price-import.glade create mode 100644 gnucash/import-export/csv-imp/assistant-csv-price-import.h diff --git a/gnucash/import-export/csv-imp/CMakeLists.txt b/gnucash/import-export/csv-imp/CMakeLists.txt index 0bc7d2938f..44a14cacec 100644 --- a/gnucash/import-export/csv-imp/CMakeLists.txt +++ b/gnucash/import-export/csv-imp/CMakeLists.txt @@ -10,6 +10,7 @@ SET(csv_import_remote_SOURCES SET(csv_import_SOURCES gncmod-csv-import.c assistant-csv-account-import.c + assistant-csv-price-import.cpp assistant-csv-trans-import.cpp gnc-plugin-csv-import.c csv-account-import.c @@ -37,6 +38,7 @@ SET(csv_import_remote_HEADERS SET(csv_import_noinst_HEADERS assistant-csv-account-import.h + assistant-csv-price-import.h assistant-csv-trans-import.h gnc-plugin-csv-import.h csv-account-import.h @@ -88,7 +90,7 @@ INSTALL(TARGETS gncmod-csv-import # No headers to install SET(csv_import_GLADE assistant-csv-account-import.glade - assistant-csv-trans-import.glade) + assistant-csv-price-import.glade assistant-csv-trans-import.glade) INSTALL(FILES ${csv_import_GLADE} DESTINATION ${CMAKE_INSTALL_DATADIR}/gnucash/gtkbuilder) diff --git a/gnucash/import-export/csv-imp/Makefile.am b/gnucash/import-export/csv-imp/Makefile.am index 408396331a..4b473de869 100644 --- a/gnucash/import-export/csv-imp/Makefile.am +++ b/gnucash/import-export/csv-imp/Makefile.am @@ -5,6 +5,7 @@ pkglib_LTLIBRARIES=libgncmod-csv-import.la libgncmod_csv_import_la_SOURCES = \ gncmod-csv-import.c \ assistant-csv-account-import.c \ + assistant-csv-price-import.cpp \ assistant-csv-trans-import.cpp \ gnc-plugin-csv-import.c \ csv-account-import.c \ @@ -22,6 +23,7 @@ libgncmod_csv_import_la_SOURCES = \ noinst_HEADERS = \ assistant-csv-account-import.h \ + assistant-csv-price-import.h \ assistant-csv-trans-import.h \ gnc-plugin-csv-import.h \ csv-account-import.h \ @@ -79,6 +81,7 @@ ui_DATA = \ gtkbuilderdir = ${GNC_GTKBUILDER_DIR} gtkbuilder_DATA = \ assistant-csv-account-import.glade \ + assistant-csv-price-import.glade \ assistant-csv-trans-import.glade EXTRA_DIST = $(ui_DATA) $(gtkbuilder_DATA) CMakeLists.txt diff --git a/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp b/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp new file mode 100644 index 0000000000..a6ba349ff4 --- /dev/null +++ b/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp @@ -0,0 +1,1621 @@ +/*******************************************************************\ + * assistant-csv-price-import.c -- An assistant for importing * + * Prices from a file. * + * * + * Copyright (C) 2017 Robert Fewell * + * * + * 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 * +\********************************************************************/ +/** @file assistant-csv-price-import.cpp + @brief CSV Import Assistant + @author Copyright (c) 2016 Geert Janssens + @author Copyright (c) 2017 Robert Fewell +*/ + +#include + +extern "C" +{ +#include "config.h" + +#include +#include +#include + +#include "gnc-ui.h" +#include "gnc-uri-utils.h" +#include "gnc-ui-util.h" +#include "dialog-utils.h" + +#include "gnc-component-manager.h" + +#include "gnc-state.h" + +#include "assistant-csv-price-import.h" + +#include "gnc-csv-gnumeric-popup.h" +#include "go-charmap-sel.h" +} + +#include "gnc-csv-trans-settings.hpp" +#include "gnc-price-import.hpp" +#include "gnc-fw-tokenizer.hpp" +#include "gnc-csv-tokenizer.hpp" + +#define MIN_COL_WIDTH 70 +#define GNC_PREFS_GROUP "dialogs.import.csv" +#define ASSISTANT_CSV_IMPORT_PRICE_CM_CLASS "assistant-csv-price-import" + +/* This static indicates the debugging module that this .o belongs to. */ +static QofLogModule log_module = GNC_MOD_ASSISTANT; + +class CsvImpPriceAssist +{ +public: + CsvImpPriceAssist (); + + void assist_prepare_cb (GtkWidget *page); + void assist_file_page_prepare (); + void assist_preview_page_prepare (); + void assist_confirm_page_prepare (); + void assist_summary_page_prepare (); + void assist_finish (bool canceled); + void assist_compmgr_close (); + + void file_confirm_cb (); + + void preview_settings_delete (); + void preview_settings_save (); + void preview_settings_name (GtkEntry* entry); + void preview_settings_load (); + void preview_update_skipped_rows (); + void preview_over_write (bool over); + void preview_update_separators (GtkWidget* widget); + void preview_update_file_format (); + void preview_update_account (); + void preview_update_encoding (const char* encoding); + void preview_update_date_format (); + void preview_update_currency_format (); + void preview_update_col_type (GtkComboBox* cbox); + void preview_update_fw_columns (GtkTreeView* treeview, GdkEventButton* event); + + void preview_populate_settings_combo(); + void preview_handle_save_del_sensitivity (GtkComboBox* combo); + void preview_split_column (int col, int offset); + void preview_refresh_table (); + void preview_refresh (); + void preview_validate_settings (); + + friend gboolean + fixed_context_menu_handler_price (GnumericPopupMenuElement const *element, + gpointer userdata); +private: + /* helper functions to manage the context menu for fixed with columns */ + uint32_t get_new_col_rel_pos (GtkTreeViewColumn *tcol, int dx); + void fixed_context_menu (GdkEventButton *event, int col, int dx); + /* helper function to calculate row colors for the preview table (to visualize status) */ + void preview_row_fill_state_cells (GtkListStore *store, GtkTreeIter *iter, + std::string& err_msg, bool skip); + /* helper function to create preview header cell combo boxes listing available column types */ + GtkWidget* preview_cbox_factory (GtkTreeModel* model, uint32_t colnum); + /* helper function to set rendering parameters for preview data columns */ + void preview_style_column (uint32_t col_num, GtkTreeModel* model); + + GtkAssistant *csv_imp_asst; + + GtkWidget *file_page; /**< Assistant file page widget */ + GtkWidget *file_chooser; /**< The widget for the file chooser */ + std::string m_file_name; /**< The import file name */ + + GtkWidget *preview_page; /**< Assistant preview page widget */ + GtkComboBox *settings_combo; /**< The Settings Combo */ + GtkWidget *save_button; /**< The Save Settings button */ + GtkWidget *del_button; /**< The Delete Settings button */ + + GtkWidget *combo_hbox; /**< The Settings Combo hbox */ + GtkSpinButton *start_row_spin; /**< The widget for the start row spinner */ + GtkSpinButton *end_row_spin; /**< The widget for the end row spinner */ + GtkWidget *skip_alt_rows_button; /**< The widget for Skip alternate rows from start row */ + GtkWidget *skip_errors_button; /**< The widget for Skip error rows*/ + GtkWidget *csv_button; /**< The widget for the CSV button */ + GtkWidget *fixed_button; /**< The widget for the Fixed Width button */ + GtkWidget *over_write_cbutton; /**< The widget for Price Over Write */ + GOCharmapSel *encselector; /**< The widget for selecting the encoding */ + GtkWidget *separator_table; /**< Container for the separator checkboxes */ + GtkCheckButton *sep_button[SEP_NUM_OF_TYPES]; /**< Checkbuttons for common separators */ + GtkWidget *fw_instructions_hbox; /**< Container for fixed-width instructions */ + GtkCheckButton *custom_cbutton; /**< The checkbutton for a custom separator */ + GtkEntry *custom_entry; /**< The entry for custom separators */ + GtkComboBoxText *date_format_combo; /**< The Combo Text widget for selecting the date format */ + GtkComboBoxText *currency_format_combo; /**< The Combo Text widget for selecting the currency format */ + GtkTreeView *treeview; /**< The treeview containing the data */ + GtkLabel *instructions_label; /**< The instructions label */ + GtkImage *instructions_image; /**< The instructions image */ + bool encoding_selected_called; /**< Before encoding_selected is first called, this is false. + * error lines, instead of all the file data. */ + int fixed_context_col; /**< The number of the column the user has clicked */ + int fixed_context_offset; /**< The offset (in characters) in the column + * the user has clicked */ + + GtkWidget *confirm_page; /**< Assistant confirm page widget */ + + GtkWidget *summary_page; /**< Assistant summary page widget */ + GtkWidget *summary_label; /**< The summary text */ + + std::unique_ptr price_imp; /**< The actual data we are previewing */ +}; + + +/******************************************************* + * Assistant call back functions + *******************************************************/ + +extern "C" +{ +void csv_price_imp_assist_prepare_cb (GtkAssistant *assistant, GtkWidget *page, CsvImpPriceAssist* info); +void csv_price_imp_assist_destroy_cb (GtkWidget *object, CsvImpPriceAssist* info); +void csv_price_imp_assist_cancel_cb (GtkAssistant *gtkassistant, CsvImpPriceAssist* info); +void csv_price_imp_assist_close_cb (GtkAssistant *gtkassistant, CsvImpPriceAssist* info); +void csv_price_imp_assist_finish_cb (GtkAssistant *gtkassistant, CsvImpPriceAssist* info); +void csv_price_imp_file_confirm_cb (GtkWidget *button, CsvImpPriceAssist *info); +void csv_price_imp_preview_del_settings_cb (GtkWidget *button, CsvImpPriceAssist *info); +void csv_price_imp_preview_save_settings_cb (GtkWidget *button, CsvImpPriceAssist *info); +void csv_price_imp_preview_settings_sel_changed_cb (GtkComboBox *combo, CsvImpPriceAssist *info); +void csv_price_imp_preview_settings_text_inserted_cb (GtkEditable *entry, gchar *new_text, + gint new_text_length, gint *position, CsvImpPriceAssist *info); +void csv_price_imp_preview_settings_text_changed_cb (GtkEntry *entry, CsvImpPriceAssist *info); +void csv_price_imp_preview_srow_cb (GtkSpinButton *spin, CsvImpPriceAssist *info); +void csv_price_imp_preview_erow_cb (GtkSpinButton *spin, CsvImpPriceAssist *info); +void csv_price_imp_preview_skiprows_cb (GtkToggleButton *checkbox, CsvImpPriceAssist *info); +void csv_price_imp_preview_skiperrors_cb (GtkToggleButton *checkbox, CsvImpPriceAssist *info); +void csv_price_imp_preview_overwrite_cb (GtkToggleButton *checkbox, CsvImpPriceAssist *info); +void csv_price_imp_preview_sep_button_cb (GtkWidget* widget, CsvImpPriceAssist* info); +void csv_price_imp_preview_sep_fixed_sel_cb (GtkToggleButton* csv_button, CsvImpPriceAssist* info); +void csv_price_imp_preview_acct_sel_cb (GtkWidget* widget, CsvImpPriceAssist* info); +void csv_price_imp_preview_enc_sel_cb (GOCharmapSel* selector, const char* encoding, + CsvImpPriceAssist* info); +} + +void +csv_price_imp_assist_prepare_cb (GtkAssistant *assistant, GtkWidget *page, + CsvImpPriceAssist* info) +{ + info->assist_prepare_cb(page); +} + +void +csv_price_imp_assist_destroy_cb (GtkWidget *object, CsvImpPriceAssist* info) +{ + gnc_unregister_gui_component_by_data (ASSISTANT_CSV_IMPORT_PRICE_CM_CLASS, info); + delete info; +} + +void +csv_price_imp_assist_cancel_cb (GtkAssistant *assistant, CsvImpPriceAssist* info) +{ + info->assist_finish (true); + gnc_close_gui_component_by_data (ASSISTANT_CSV_IMPORT_PRICE_CM_CLASS, info); +} + +void +csv_price_imp_assist_close_cb (GtkAssistant *assistant, CsvImpPriceAssist* info) +{ + gnc_close_gui_component_by_data (ASSISTANT_CSV_IMPORT_PRICE_CM_CLASS, info); +} + +void +csv_price_imp_assist_finish_cb (GtkAssistant *assistant, CsvImpPriceAssist* info) +{ + info->assist_finish (false); +} + +void csv_price_imp_file_confirm_cb (GtkWidget *button, CsvImpPriceAssist *info) +{ + info->file_confirm_cb(); +} + +void csv_price_imp_preview_del_settings_cb (GtkWidget *button, CsvImpPriceAssist *info) +{ + info->preview_settings_delete(); +} + +void csv_price_imp_preview_save_settings_cb (GtkWidget *button, CsvImpPriceAssist *info) +{ + info->preview_settings_save(); +} + +void csv_price_imp_preview_settings_sel_changed_cb (GtkComboBox *combo, CsvImpPriceAssist *info) +{ + info->preview_settings_load(); +} + +void +csv_price_imp_preview_settings_text_inserted_cb (GtkEditable *entry, gchar *new_text, + gint new_text_length, gint *position, CsvImpPriceAssist *info) +{ + if (!new_text) + return; + + /* Prevent entering [], which are invalid characters in key files */ + auto base_txt = std::string (new_text); + auto mod_txt = base_txt; + std::replace (mod_txt.begin(), mod_txt.end(), '[', '('); + std::replace (mod_txt.begin(), mod_txt.end(), ']', ')'); + if (base_txt == mod_txt) + return; + g_signal_handlers_block_by_func (entry, (gpointer) csv_price_imp_preview_settings_text_inserted_cb, info); + gtk_editable_insert_text (entry, mod_txt.c_str(), mod_txt.size() , position); + g_signal_handlers_unblock_by_func (entry, (gpointer) csv_price_imp_preview_settings_text_inserted_cb, info); + + g_signal_stop_emission_by_name (entry, "insert_text"); +} + +void +csv_price_imp_preview_settings_text_changed_cb (GtkEntry *entry, CsvImpPriceAssist *info) +{ + info->preview_settings_name(entry); +} + +void csv_price_imp_preview_srow_cb (GtkSpinButton *spin, CsvImpPriceAssist *info) +{ + info->preview_update_skipped_rows(); +} + +void csv_price_imp_preview_erow_cb (GtkSpinButton *spin, CsvImpPriceAssist *info) +{ + info->preview_update_skipped_rows(); +} + +void csv_price_imp_preview_skiprows_cb (GtkToggleButton *checkbox, CsvImpPriceAssist *info) +{ + info->preview_update_skipped_rows(); +} + +void csv_price_imp_preview_skiperrors_cb (GtkToggleButton *checkbox, CsvImpPriceAssist *info) +{ + info->preview_update_skipped_rows(); +} + +void csv_price_imp_preview_overwrite_cb (GtkToggleButton *checkbox, CsvImpPriceAssist *info) +{ + info->preview_over_write (gtk_toggle_button_get_active (checkbox)); +} + +void csv_price_imp_preview_sep_button_cb (GtkWidget* widget, CsvImpPriceAssist* info) +{ + info->preview_update_separators(widget); +} + +void csv_price_imp_preview_sep_fixed_sel_cb (GtkToggleButton* csv_button, CsvImpPriceAssist* info) +{ + info->preview_update_file_format(); +} + +void csv_price_imp_preview_acct_sel_cb (GtkWidget* widget, CsvImpPriceAssist* info) +{ + info->preview_update_account(); +} + +void csv_price_imp_preview_enc_sel_cb (GOCharmapSel* selector, const char* encoding, + CsvImpPriceAssist* info) +{ + info->preview_update_encoding(encoding); +} + +static void csv_price_imp_preview_date_fmt_sel_cb (GtkComboBox* format_selector, CsvImpPriceAssist* info) +{ + info->preview_update_date_format(); +} + +static void csv_price_imp_preview_currency_fmt_sel_cb (GtkComboBox* format_selector, CsvImpPriceAssist* info) +{ + info->preview_update_currency_format(); +} + +void csv_price_imp_preview_col_type_changed_cb (GtkComboBox* cbox, CsvImpPriceAssist* info) +{ + info->preview_update_col_type (cbox); +} + +gboolean +csv_price_imp_preview_treeview_clicked_cb (GtkTreeView* treeview, GdkEventButton* event, + CsvImpPriceAssist* info) +{ + info->preview_update_fw_columns(treeview, event); + return false; +} + + +/******************************************************* + * Assistant Constructor + *******************************************************/ +CsvImpPriceAssist::CsvImpPriceAssist () +{ + auto builder = gtk_builder_new(); + gnc_builder_add_from_file (builder , "assistant-csv-price-import.glade", "start_row_adj"); + gnc_builder_add_from_file (builder , "assistant-csv-price-import.glade", "end_row_adj"); + gnc_builder_add_from_file (builder , "assistant-csv-price-import.glade", "CSV Price Assistant"); + csv_imp_asst = GTK_ASSISTANT(gtk_builder_get_object (builder, "CSV Price Assistant")); + + /* Enable buttons on all page. */ + gtk_assistant_set_page_complete (csv_imp_asst, + GTK_WIDGET(gtk_builder_get_object (builder, "start_page")), + true); + gtk_assistant_set_page_complete (csv_imp_asst, + GTK_WIDGET(gtk_builder_get_object (builder, "file_page")), + false); + gtk_assistant_set_page_complete (csv_imp_asst, + GTK_WIDGET(gtk_builder_get_object (builder, "preview_page")), + false); + gtk_assistant_set_page_complete (csv_imp_asst, + GTK_WIDGET(gtk_builder_get_object (builder, "confirm_page")), + true); + gtk_assistant_set_page_complete (csv_imp_asst, + GTK_WIDGET(gtk_builder_get_object (builder, "summary_page")), + true); + + /* File chooser Page */ + file_page = GTK_WIDGET(gtk_builder_get_object (builder, "file_page")); + file_chooser = gtk_file_chooser_widget_new (GTK_FILE_CHOOSER_ACTION_OPEN); + g_signal_connect (G_OBJECT(file_chooser), "file-activated", + G_CALLBACK(csv_price_imp_file_confirm_cb), this); + auto button = gtk_button_new_from_stock (GTK_STOCK_OK); + gtk_widget_set_size_request (button, 100, -1); + gtk_widget_show (button); + auto h_box = gtk_hbox_new (TRUE, 0); + gtk_box_pack_start (GTK_BOX(h_box), button, FALSE, FALSE, 0); + gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER(file_chooser), h_box); + g_signal_connect (G_OBJECT(button), "clicked", + G_CALLBACK(csv_price_imp_file_confirm_cb), this); + + auto box = GTK_WIDGET(gtk_builder_get_object (builder, "file_page")); + gtk_box_pack_start (GTK_BOX(box), file_chooser, TRUE, TRUE, 6); + gtk_widget_show (file_chooser); + + /* Preview Settings Page */ + { + preview_page = GTK_WIDGET(gtk_builder_get_object (builder, "preview_page")); + + // Add Settings combo + auto settings_store = gtk_list_store_new (2, G_TYPE_POINTER, G_TYPE_STRING); + settings_combo = GTK_COMBO_BOX(gtk_combo_box_new_with_model_and_entry (GTK_TREE_MODEL(settings_store))); + gtk_combo_box_set_entry_text_column (GTK_COMBO_BOX(settings_combo), SET_NAME); + gtk_combo_box_set_active (GTK_COMBO_BOX(settings_combo), 0); + + combo_hbox = GTK_WIDGET(gtk_builder_get_object (builder, "combo_hbox")); + gtk_box_pack_start (GTK_BOX(combo_hbox), GTK_WIDGET(settings_combo), true, true, 6); + gtk_widget_show (GTK_WIDGET(settings_combo)); + + g_signal_connect (G_OBJECT(settings_combo), "changed", + G_CALLBACK(csv_price_imp_preview_settings_sel_changed_cb), this); + + // Additionally connect to the changed signal of the embedded GtkEntry + auto emb_entry = gtk_bin_get_child (GTK_BIN (settings_combo)); + g_signal_connect (G_OBJECT(emb_entry), "changed", + G_CALLBACK(csv_price_imp_preview_settings_text_changed_cb), this); + g_signal_connect (G_OBJECT(emb_entry), "insert-text", + G_CALLBACK(csv_price_imp_preview_settings_text_inserted_cb), this); + + // Add Save Settings button + save_button = GTK_WIDGET(gtk_builder_get_object (builder, "save_settings")); + + // Add Delete Settings button + del_button = GTK_WIDGET(gtk_builder_get_object (builder, "delete_settings")); + + /* The table containing the separator configuration widgets */ + start_row_spin = GTK_SPIN_BUTTON(gtk_builder_get_object (builder, "start_row")); + end_row_spin = GTK_SPIN_BUTTON(gtk_builder_get_object (builder, "end_row")); + skip_alt_rows_button = GTK_WIDGET(gtk_builder_get_object (builder, "skip_rows")); + skip_errors_button = GTK_WIDGET(gtk_builder_get_object (builder, "skip_errors_button")); + over_write_cbutton = GTK_WIDGET(gtk_builder_get_object (builder, "over_write_button")); + separator_table = GTK_WIDGET(gtk_builder_get_object (builder, "separator_table")); + fw_instructions_hbox = GTK_WIDGET(gtk_builder_get_object (builder, "fw_instructions_hbox")); + + /* Load the separator buttons from the glade builder file into the + * sep_buttons array. */ + const char* sep_button_names[] = { + "space_cbutton", + "tab_cbutton", + "comma_cbutton", + "colon_cbutton", + "semicolon_cbutton", + "hyphen_cbutton" + }; + for (int i = 0; i < SEP_NUM_OF_TYPES; i++) + sep_button[i] + = (GtkCheckButton*)GTK_WIDGET(gtk_builder_get_object (builder, sep_button_names[i])); + + /* Load and connect the custom separator checkbutton in the same way + * as the other separator buttons. */ + custom_cbutton + = (GtkCheckButton*)GTK_WIDGET(gtk_builder_get_object (builder, "custom_cbutton")); + + /* Load the entry for the custom separator entry. Connect it to the + * sep_button_clicked event handler as well. */ + custom_entry = (GtkEntry*)GTK_WIDGET(gtk_builder_get_object (builder, "custom_entry")); + + /* Create the encoding selector widget and add it to the assistant */ + encselector = GO_CHARMAP_SEL(go_charmap_sel_new(GO_CHARMAP_SEL_TO_UTF8)); + /* Connect the selector to the encoding_selected event handler. */ + g_signal_connect (G_OBJECT(encselector), "charmap_changed", + G_CALLBACK(csv_price_imp_preview_enc_sel_cb), this); + + auto encoding_container = GTK_CONTAINER(gtk_builder_get_object (builder, "encoding_container")); + gtk_container_add (encoding_container, GTK_WIDGET(encselector)); + gtk_widget_show_all (GTK_WIDGET(encoding_container)); + + /* The instructions label and image */ + instructions_label = GTK_LABEL(gtk_builder_get_object (builder, "instructions_label")); + instructions_image = GTK_IMAGE(gtk_builder_get_object (builder, "instructions_image")); + + /* Add in the date format combo box and hook it up to an event handler. */ + date_format_combo = GTK_COMBO_BOX_TEXT(gtk_combo_box_text_new()); + for (int i = 0; i < num_date_formats; i++) + { + gtk_combo_box_text_append_text (date_format_combo, _(date_format_user[i])); + } + gtk_combo_box_set_active (GTK_COMBO_BOX(date_format_combo), 0); + g_signal_connect (G_OBJECT(date_format_combo), "changed", + G_CALLBACK(csv_price_imp_preview_date_fmt_sel_cb), this); + + /* Add it to the assistant. */ + auto date_format_container = GTK_CONTAINER(gtk_builder_get_object (builder, "date_format_container")); + gtk_container_add (date_format_container, GTK_WIDGET(date_format_combo)); + gtk_widget_show_all (GTK_WIDGET(date_format_container)); + + /* Add in the currency format combo box and hook it up to an event handler. */ + currency_format_combo = GTK_COMBO_BOX_TEXT(gtk_combo_box_text_new()); + for (int i = 0; i < num_currency_formats; i++) + { + gtk_combo_box_text_append_text (currency_format_combo, _(currency_format_user[i])); + } + /* Default will the locale */ + gtk_combo_box_set_active (GTK_COMBO_BOX(currency_format_combo), 0); + g_signal_connect (G_OBJECT(currency_format_combo), "changed", + G_CALLBACK(csv_price_imp_preview_currency_fmt_sel_cb), this); + + /* Add it to the assistant. */ + auto currency_format_container = GTK_CONTAINER(gtk_builder_get_object (builder, "currency_format_container")); + gtk_container_add (currency_format_container, GTK_WIDGET(currency_format_combo)); + gtk_widget_show_all (GTK_WIDGET(currency_format_container)); + + /* Connect the CSV/Fixed-Width radio button event handler. */ + csv_button = GTK_WIDGET(gtk_builder_get_object (builder, "csv_button")); + fixed_button = GTK_WIDGET(gtk_builder_get_object (builder, "fixed_button")); + + /* Load the data treeview and connect it to its resizing event handler. */ + treeview = (GtkTreeView*)GTK_WIDGET(gtk_builder_get_object (builder, "treeview")); + gtk_tree_view_set_headers_clickable (treeview, true); + + /* This is true only after encoding_selected is called, so we must + * set it initially to false. */ + encoding_selected_called = false; + } + + /* Confirm Page */ + confirm_page = GTK_WIDGET(gtk_builder_get_object (builder, "confirm_page")); + + /* Summary Page */ + summary_page = GTK_WIDGET(gtk_builder_get_object (builder, "summary_page")); + summary_label = GTK_WIDGET(gtk_builder_get_object (builder, "summary_label")); + + gnc_restore_window_size (GNC_PREFS_GROUP, GTK_WINDOW(csv_imp_asst)); + + gtk_builder_connect_signals (builder, this); + g_object_unref (G_OBJECT(builder)); + + gtk_widget_show_all (GTK_WIDGET(csv_imp_asst)); + gnc_window_adjust_for_screen (GTK_WINDOW(csv_imp_asst)); +} + +/************************************************** + * Code related to the file chooser page + **************************************************/ + +/* csv_price_imp_file_confirm_cb + * + * call back for ok button in file chooser widget + */ +void +CsvImpPriceAssist::file_confirm_cb () +{ + auto file_name = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(file_chooser)); + if (!file_name) + return; + + auto filepath = gnc_uri_get_path (file_name); + auto starting_dir = g_path_get_dirname (filepath); + + m_file_name = file_name; + gnc_set_default_directory (GNC_PREFS_GROUP, starting_dir); + + DEBUG("file_name selected is %s", m_file_name.c_str()); + DEBUG("starting directory is %s", starting_dir); + + g_free (filepath); + g_free (file_name); + g_free (starting_dir); + + /* Load the file into parse_data. */ + price_imp = std::unique_ptr(new GncPriceImport); + /* Assume data is CSV. User can later override to Fixed Width if needed */ + try + { + price_imp->file_format (GncImpFileFormat::CSV); + price_imp->load_file (m_file_name); + price_imp->tokenize (true); + } + catch (std::ifstream::failure& e) + { + /* File loading failed ... */ + gnc_error_dialog (GTK_WIDGET(csv_imp_asst), "%s", e.what()); + return; + } + catch (std::range_error &e) + { + /* Parsing failed ... */ + gnc_error_dialog (GTK_WIDGET(csv_imp_asst), "%s", e.what()); + return; + } + /* Get settings store and populate */ + preview_populate_settings_combo(); + gtk_combo_box_set_active (settings_combo, 0); + + auto num = gtk_assistant_get_current_page (csv_imp_asst); + gtk_assistant_set_current_page (csv_imp_asst, num + 1); +} + + +/************************************************** + * Code related to the preview page + **************************************************/ + +/* Set the available presets in the settings combo box + */ +void CsvImpPriceAssist::preview_populate_settings_combo() +{ + // Clear the list store + auto model = gtk_combo_box_get_model (settings_combo); + gtk_list_store_clear (GTK_LIST_STORE(model)); + + // Append the default entry +//FIXME get_trans_presets ???? + auto presets = get_trans_presets (); + for (auto preset : presets) + { + GtkTreeIter iter; + gtk_list_store_append (GTK_LIST_STORE(model), &iter); + /* FIXME we store the raw pointer to the preset, while it's + * managed by a shared pointer. This is dangerous because + * when the shared pointer goes out of scope, our pointer will dangle. + * For now this is safe, because the shared pointers in this case are + * long-lived, but this may need refactoring. + */ + gtk_list_store_set (GTK_LIST_STORE(model), &iter, SET_GROUP, preset.get(), SET_NAME, preset->m_name.c_str(), -1); + } +} + +/* Enable or disable the save and delete settings buttons + * depending on what is selected and entered as settings name + */ +void CsvImpPriceAssist::preview_handle_save_del_sensitivity (GtkComboBox* combo) +{ + GtkTreeIter iter; + auto can_delete = false; + auto can_save = false; + auto entry = gtk_bin_get_child (GTK_BIN(combo)); + auto entry_text = gtk_entry_get_text (GTK_ENTRY(entry)); + /* Handle sensitivity of the delete and save button */ + if (gtk_combo_box_get_active_iter (combo, &iter)) + { + CsvTransSettings *preset; + GtkTreeModel *model = gtk_combo_box_get_model (combo); + gtk_tree_model_get (model, &iter, SET_GROUP, &preset, -1); +//FIXME + if (preset && !trans_preset_is_reserved_name (preset->m_name)) + { + /* Current preset is not read_only, so buttons can be enabled */ + can_delete = true; + can_save = true; + } + } +//FIXME + else if (entry_text && (strlen (entry_text) > 0) && + !trans_preset_is_reserved_name (std::string(entry_text))) + can_save = true; + + gtk_widget_set_sensitive (save_button, can_save); + gtk_widget_set_sensitive (del_button, can_delete); +} + +void +CsvImpPriceAssist::preview_settings_name (GtkEntry* entry) +{ + auto text = gtk_entry_get_text (entry); + if (text) + price_imp->settings_name(text); + + auto combo = gtk_widget_get_parent (GTK_WIDGET(entry)); + preview_handle_save_del_sensitivity (GTK_COMBO_BOX(combo)); +} + +/* Use selected preset to configure the import. Triggered when + * a preset is selected in the settings combo. + */ +void +CsvImpPriceAssist::preview_settings_load () +{ + // Get the Active Selection + GtkTreeIter iter; + if (!gtk_combo_box_get_active_iter (settings_combo, &iter)) + return; + + CsvTransSettings *preset = nullptr; + auto model = gtk_combo_box_get_model (settings_combo); + gtk_tree_model_get (model, &iter, SET_GROUP, &preset, -1); + + if (!preset) + return; + + price_imp->settings (*preset); + if (preset->m_load_error) + gnc_error_dialog (GTK_WIDGET(csv_imp_asst), + "%s", _("There were problems reading some saved settings, continuing to load.\n" + "Please review and save again.")); + + preview_refresh (); + preview_handle_save_del_sensitivity (settings_combo); +} + +/* Callback to delete a settings entry + */ +void +CsvImpPriceAssist::preview_settings_delete () +{ + // Get the Active Selection + GtkTreeIter iter; + if (!gtk_combo_box_get_active_iter (settings_combo, &iter)) + return; + + CsvTransSettings *preset = nullptr; + auto model = gtk_combo_box_get_model (settings_combo); + gtk_tree_model_get (model, &iter, SET_GROUP, &preset, -1); + + auto response = gnc_ok_cancel_dialog (GTK_WIDGET(csv_imp_asst), + GTK_RESPONSE_CANCEL, + "%s", _("Delete the Import Settings.")); + if (response == GTK_RESPONSE_OK) + { + preset->remove(); + preview_populate_settings_combo(); + gtk_combo_box_set_active (settings_combo, 0); // Default + preview_refresh (); // Reset the widgets + } +} + +/* Callback to save the current settings to the gnucash state file. + */ +void +CsvImpPriceAssist::preview_settings_save () +{ + auto title = _("Save the Import Settings."); + auto new_name = price_imp->settings_name(); + + /* 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) + { + // Walk through the list, reading each row + CsvTransSettings *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_WIDGET(csv_imp_asst), + GTK_RESPONSE_OK, + "%s", _("Setting name already exists, over write?")); + if (response != GTK_RESPONSE_OK) + return; + + break; + } + valid = gtk_tree_model_iter_next (model, &iter); + } + } + + /* All checks passed, let's save this preset */ + if (!price_imp->save_settings()) + { + gnc_info_dialog (GTK_WIDGET(csv_imp_asst), + "%s", _("The settings have been saved.")); + + // 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); + } + } + else + gnc_error_dialog (GTK_WIDGET(csv_imp_asst), + "%s", _("There was a problem saving the settings, please try again.")); +} + +/* Callback triggered when user adjusts skip start lines + */ +void CsvImpPriceAssist::preview_update_skipped_rows () +{ + /* Update skip rows in the parser */ + price_imp->update_skipped_lines (gtk_spin_button_get_value_as_int (start_row_spin), + gtk_spin_button_get_value_as_int (end_row_spin), + gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(skip_alt_rows_button)), + gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(skip_errors_button))); + + /* And adjust maximum number of lines that can be skipped at each end accordingly */ + auto adj = gtk_spin_button_get_adjustment (end_row_spin); + gtk_adjustment_set_upper (adj, price_imp->m_parsed_lines.size() + - price_imp->skip_start_lines() -1); + + adj = gtk_spin_button_get_adjustment (start_row_spin); + gtk_adjustment_set_upper (adj, price_imp->m_parsed_lines.size() + - price_imp->skip_end_lines() - 1); + + preview_refresh_table (); +} + +void CsvImpPriceAssist::preview_over_write (bool over) +{ + price_imp->over_write (over); +} + +/** Event handler for separator changes. This function is called + * whenever one of the widgets for configuring the separators (the + * separator checkbuttons or the custom separator entry) is + * changed. + * @param widget The widget that was changed + * @param info The data that is being configured + */ +void CsvImpPriceAssist::preview_update_separators (GtkWidget* widget) +{ + /* Only manipulate separator characters if the currently open file is + * csv separated. */ + if (price_imp->file_format() != GncImpFileFormat::CSV) + return; + + /* Add the corresponding characters to checked_separators for each + * button that is checked. */ + auto checked_separators = std::string(); + const auto stock_sep_chars = std::string (" \t,:;-"); + for (int i = 0; i < SEP_NUM_OF_TYPES; i++) + { + if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(sep_button[i]))) + checked_separators += stock_sep_chars[i]; + } + + /* Add the custom separator if the user checked its button. */ + if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(custom_cbutton))) + { + auto custom_sep = gtk_entry_get_text (custom_entry); + if (custom_sep[0] != '\0') /* Don't add a blank separator (bad things will happen!). */ + checked_separators += custom_sep; + } + + /* Set the parse options using the checked_separators list. */ + price_imp->separators (checked_separators); + + /* Parse the data using the new options. We don't want to reguess + * the column types because we want to leave the user's + * configurations intact. */ + try + { + price_imp->tokenize (false); + preview_refresh_table (); + } + catch (std::range_error &e) + { + /* Warn the user there was a problem and try to undo what caused + * the error. (This will cause a reparsing and ideally a usable + * configuration.) */ + gnc_error_dialog (GTK_WIDGET(csv_imp_asst), "Error in parsing"); + /* If we're here because the user changed the file format, we should just wait for the user + * to update the configuration */ + if (!widget) + return; + /* If the user changed the custom separator, erase that custom separator. */ + if (widget == GTK_WIDGET(custom_entry)) + gtk_entry_set_text (GTK_ENTRY(widget), ""); + /* If the user checked a checkbutton, toggle that checkbutton back. */ + else + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(widget), + !gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(widget))); + return; + } +} + +/** Event handler for clicking one of the format type radio + * buttons. This occurs if the format (Fixed-Width or CSV) is changed. + * @param csv_button The "Separated" radio button + * @param info The display of the data being imported + */ +void CsvImpPriceAssist::preview_update_file_format () +{ + /* Set the parsing type correctly. */ + try + { + if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(csv_button))) + { + price_imp->file_format (GncImpFileFormat::CSV); + g_signal_handlers_disconnect_by_func(G_OBJECT(treeview), + (gpointer)csv_price_imp_preview_treeview_clicked_cb, (gpointer)this); + gtk_widget_set_visible (separator_table, true); + gtk_widget_set_visible (fw_instructions_hbox, false); + } + else + { + price_imp->file_format (GncImpFileFormat::FIXED_WIDTH); + /* Enable context menu for adding/removing columns. */ + g_signal_connect (G_OBJECT(treeview), "button-press-event", + G_CALLBACK(csv_price_imp_preview_treeview_clicked_cb), (gpointer)this); + gtk_widget_set_visible (separator_table, false); + gtk_widget_set_visible (fw_instructions_hbox, true); + + } + price_imp->tokenize (false); + preview_refresh_table (); + } + catch (std::range_error &e) + { + /* Parsing failed ... */ + gnc_error_dialog (nullptr, "%s", e.what()); + return; + } + catch (...) + { + // FIXME Handle file loading errors (possibly thrown by file_format above) + PWARN("Got an error during file loading"); + } +} + +/** Event handler for a new encoding. This is called when the user + * selects a new encoding; the data is reparsed and shown to the + * user. + * @param selector The widget the user uses to select a new encoding + * @param encoding The encoding that the user selected + */ +void +CsvImpPriceAssist::preview_update_encoding (const char* encoding) +{ + /* This gets called twice every time a new encoding is selected. The + * second call actually passes the correct data; thus, we only do + * something the second time this is called. */ + + /* If this is the second time the function is called ... */ + if (encoding_selected_called) + { + std::string previous_encoding = price_imp->m_tokenizer->encoding(); + /* Try converting the new encoding and reparsing. */ + try + { + price_imp->encoding (encoding); + preview_refresh_table (); + } + catch (...) + { + /* If it fails, change back to the old encoding. */ + gnc_error_dialog (nullptr, "%s", _("Invalid encoding selected")); + go_charmap_sel_set_encoding (encselector, previous_encoding.c_str()); + } + } + encoding_selected_called = !encoding_selected_called; +} + +void +CsvImpPriceAssist::preview_update_date_format () +{ + price_imp->date_format (gtk_combo_box_get_active (GTK_COMBO_BOX(date_format_combo))); + preview_refresh_table (); +} + +void +CsvImpPriceAssist::preview_update_currency_format () +{ + price_imp->currency_format (gtk_combo_box_get_active (GTK_COMBO_BOX(currency_format_combo))); + preview_refresh_table (); +} + +gboolean +csv_imp_preview_queue_rebuild_table (CsvImpPriceAssist *assist) +{ + assist->preview_refresh_table (); + return false; +} + +/* Internally used enum to access the columns in the comboboxes + * the user can click to set a type for each column of the data + */ +enum PreviewHeaderComboCols { COL_TYPE_NAME, COL_TYPE_ID }; +/* Internally used enum to access the first two (fixed) columns + * in the model used to display the prased data. + */ +enum PreviewDataTableCols { + PREV_COL_FCOLOR, + PREV_COL_BCOLOR, + PREV_COL_STRIKE, + PREV_COL_ERROR, + PREV_COL_ERR_ICON, + PREV_N_FIXED_COLS }; + +/** Event handler for the user selecting a new column type. When the + * user selects a new column type, that column's text must be changed + * to the selection, and any other columns containing that selection + * must be changed to "None" because we don't allow duplicates. + * @param renderer The renderer of the column the user changed + * @param path There is only 1 row in info->ctreeview, so this is always 0. + * @param new_text The text the user selected + * @param info The display of the data being imported + */ +void CsvImpPriceAssist::preview_update_col_type (GtkComboBox* cbox) +{ + /* Get the new text */ + GtkTreeIter iter; + auto model = gtk_combo_box_get_model (cbox); + gtk_combo_box_get_active_iter (cbox, &iter); + auto new_col_type = GncPricePropType::NONE; + gtk_tree_model_get (model, &iter, COL_TYPE_ID, &new_col_type, -1); + + auto col_num = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT(cbox), "col-num")); + price_imp->set_column_type_price (col_num, new_col_type); + + /* Delay rebuilding our data table to avoid critical warnings due to + * pending events still acting on them after this event is processed. + */ + g_idle_add ((GSourceFunc)csv_imp_preview_queue_rebuild_table, this); +} + +/*======================================================================*/ +/*================== Beginning of Gnumeric Code ========================*/ + +/* The following is code copied from Gnumeric 1.7.8 licensed under the + * GNU General Public License version 2 and/or version 3. It is from the file + * gnumeric/gnucash/dialogs/dialog-stf-fixed-page.c, and it has been + * modified slightly to work within GnuCash. */ + +/* + * Copyright 2001 Almer S. Tigelaar + * Copyright 2003 Morten Welinder + * + * 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, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +enum +{ + CONTEXT_STF_IMPORT_MERGE_LEFT = 1, + CONTEXT_STF_IMPORT_MERGE_RIGHT = 2, + CONTEXT_STF_IMPORT_SPLIT = 3, + CONTEXT_STF_IMPORT_WIDEN = 4, + CONTEXT_STF_IMPORT_NARROW = 5 +}; + +static GnumericPopupMenuElement const popup_elements[] = +{ + { + N_("Merge with column on _left"), GTK_STOCK_REMOVE, + 0, 1 << CONTEXT_STF_IMPORT_MERGE_LEFT, CONTEXT_STF_IMPORT_MERGE_LEFT + }, + { + N_("Merge with column on _right"), GTK_STOCK_REMOVE, + 0, 1 << CONTEXT_STF_IMPORT_MERGE_RIGHT, CONTEXT_STF_IMPORT_MERGE_RIGHT + }, + { "", nullptr, 0, 0, 0 }, + { + N_("_Split this column"), nullptr, + 0, 1 << CONTEXT_STF_IMPORT_SPLIT, CONTEXT_STF_IMPORT_SPLIT + }, + { "", nullptr, 0, 0, 0 }, + { + N_("_Widen this column"), GTK_STOCK_GO_FORWARD, + 0, 1 << CONTEXT_STF_IMPORT_WIDEN, CONTEXT_STF_IMPORT_WIDEN + }, + { + N_("_Narrow this column"), GTK_STOCK_GO_BACK, + 0, 1 << CONTEXT_STF_IMPORT_NARROW, CONTEXT_STF_IMPORT_NARROW + }, + { nullptr, nullptr, 0, 0, 0 }, +}; + +uint32_t CsvImpPriceAssist::get_new_col_rel_pos (GtkTreeViewColumn *tcol, int dx) +{ + auto renderers = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT(tcol)); + auto cell = GTK_CELL_RENDERER(renderers->data); + g_list_free (renderers); + PangoFontDescription *font_desc; + g_object_get (G_OBJECT(cell), "font_desc", &font_desc, nullptr); + + PangoLayout *layout = gtk_widget_create_pango_layout (GTK_WIDGET(treeview), "x"); + pango_layout_set_font_description (layout, font_desc); + int width; + pango_layout_get_pixel_size (layout, &width, nullptr); + if (width < 1) width = 1; + uint32_t charindex = (dx + width / 2) / width; + g_object_unref (layout); + pango_font_description_free (font_desc); + + return charindex; +} + +gboolean +fixed_context_menu_handler_price (GnumericPopupMenuElement const *element, + gpointer userdata) +{ + auto info = (CsvImpPriceAssist*)userdata; + auto fwtok = dynamic_cast(info->price_imp->m_tokenizer.get()); + + switch (element->index) + { + case CONTEXT_STF_IMPORT_MERGE_LEFT: + fwtok->col_delete (info->fixed_context_col - 1); + break; + case CONTEXT_STF_IMPORT_MERGE_RIGHT: + fwtok->col_delete (info->fixed_context_col); + break; + case CONTEXT_STF_IMPORT_SPLIT: + fwtok->col_split (info->fixed_context_col, info->fixed_context_offset); + break; + case CONTEXT_STF_IMPORT_WIDEN: + fwtok->col_widen (info->fixed_context_col); + break; + case CONTEXT_STF_IMPORT_NARROW: + fwtok->col_narrow (info->fixed_context_col); + break; + default: + ; /* Nothing */ + } + + try + { + info->price_imp->tokenize (false); + } + catch(std::range_error& e) + { + gnc_error_dialog (nullptr, "%s", e.what()); + return false; + } + info->preview_refresh_table (); + return true; +} + +void +CsvImpPriceAssist::fixed_context_menu (GdkEventButton *event, + int col, int offset) +{ + auto fwtok = dynamic_cast(price_imp->m_tokenizer.get()); + fixed_context_col = col; + fixed_context_offset = offset; + + int sensitivity_filter = 0; + if (!fwtok->col_can_delete (col - 1)) + sensitivity_filter |= (1 << CONTEXT_STF_IMPORT_MERGE_LEFT); + if (!fwtok->col_can_delete (col)) + sensitivity_filter |= (1 << CONTEXT_STF_IMPORT_MERGE_RIGHT); + if (!fwtok->col_can_split (col, offset)) + sensitivity_filter |= (1 << CONTEXT_STF_IMPORT_SPLIT); + if (!fwtok->col_can_widen (col)) + sensitivity_filter |= (1 << CONTEXT_STF_IMPORT_WIDEN); + if (!fwtok->col_can_narrow (col)) + sensitivity_filter |= (1 << CONTEXT_STF_IMPORT_NARROW); + + gnumeric_create_popup_menu (popup_elements, &fixed_context_menu_handler_price, + this, 0, + sensitivity_filter, event); +} + +/*===================== End of Gnumeric Code ===========================*/ +/*======================================================================*/ +void +CsvImpPriceAssist::preview_split_column (int col, int offset) +{ + auto fwtok = dynamic_cast(price_imp->m_tokenizer.get()); + fwtok->col_split (col, offset); + try + { + price_imp->tokenize (false); + } + catch (std::range_error& e) + { + gnc_error_dialog (nullptr, "%s", e.what()); + return; + } + preview_refresh_table(); +} + +/** Event handler for clicking on column headers. This function is + * called whenever the user clicks on column headers in + * preview->treeview to modify columns when in fixed-width mode. + * @param button The button at the top of a column of the treeview + * @param event The event that happened (where the user clicked) + * @param info The data being configured + * @returns true if further processing of this even should stop, false + * if other event handlers can have a go at this as well + */ +void +CsvImpPriceAssist::preview_update_fw_columns (GtkTreeView* treeview, GdkEventButton* event) +{ + /* Nothing to do if this was not triggered on our treeview body */ + if (event->window != gtk_tree_view_get_bin_window (treeview)) + return; + + /* Find the column that was clicked. */ + GtkTreeViewColumn *tcol = nullptr; + int cell_x = 0; + auto success = gtk_tree_view_get_path_at_pos (treeview, + (int)event->x, (int)event->y, + nullptr, &tcol, &cell_x, nullptr); + if (!success) + return; + + /* Stop if no column found in this treeview (-1) or + * if column is the error messages column (0) */ + auto tcol_list = gtk_tree_view_get_columns(treeview); + auto tcol_num = g_list_index (tcol_list, tcol); + g_list_free (tcol_list); + if (tcol_num <= 0) + return; + + /* Data columns in the treeview are offset by one + * because the first column is the error column + */ + auto dcol = tcol_num - 1; + auto offset = get_new_col_rel_pos (tcol, cell_x); + if (event->type == GDK_2BUTTON_PRESS && event->button == 1) + /* Double clicks can split columns. */ + preview_split_column (dcol, offset); + else if (event->type == GDK_BUTTON_PRESS && event->button == 3) + /* Right clicking brings up a context menu. */ + fixed_context_menu (event, dcol, offset); +} + +/* Convert state info (errors/skipped) in visual feedback to decorate the preview table */ +void +CsvImpPriceAssist::preview_row_fill_state_cells (GtkListStore *store, GtkTreeIter *iter, + std::string& err_msg, bool skip) +{ + /* Extract error status for all non-skipped lines */ + const char *c_err_msg = nullptr; + const char *icon_name = nullptr; + const char *fcolor = nullptr; + const char *bcolor = nullptr; + if (!skip && !err_msg.empty()) + { + fcolor = "black"; + bcolor = "pink"; + c_err_msg = err_msg.c_str(); + icon_name = GTK_STOCK_DIALOG_ERROR; + } + gtk_list_store_set (store, iter, + PREV_COL_FCOLOR, fcolor, + PREV_COL_BCOLOR, bcolor, + PREV_COL_STRIKE, skip, + PREV_COL_ERROR, c_err_msg, + PREV_COL_ERR_ICON, icon_name, -1); +} + +/* Helper function that creates a combo_box using a model + * with valid column types and selects the given column type + */ +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. */ + auto renderer = gtk_cell_renderer_text_new(); + gtk_cell_layout_pack_start (GTK_CELL_LAYOUT(cbox), + renderer, true); + 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) + { + 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", + G_CALLBACK(csv_price_imp_preview_col_type_changed_cb), (gpointer)this); + + gtk_widget_show (cbox); + return cbox; +} + +void +CsvImpPriceAssist::preview_style_column (uint32_t col_num, GtkTreeModel* model) +{ + auto col = gtk_tree_view_get_column (treeview, col_num); + auto renderer = static_cast(gtk_tree_view_column_get_cell_renderers(col)->data); + + /* First column -the error status column- is rendered differently */ + if (col_num == 0) + { + gtk_tree_view_column_set_attributes (col, renderer, + "stock-id", PREV_COL_ERR_ICON, + "cell-background", PREV_COL_BCOLOR, nullptr); + g_object_set (G_OBJECT(renderer), "stock-size", GTK_ICON_SIZE_MENU, nullptr); + g_object_set (G_OBJECT(col), "sizing", GTK_TREE_VIEW_COLUMN_FIXED, + "fixed-width", 20, nullptr); + gtk_tree_view_column_set_resizable (col, false); + } + else + { + gtk_tree_view_column_set_attributes (col, renderer, + "foreground", PREV_COL_FCOLOR, + "background", PREV_COL_BCOLOR, + "strikethrough", PREV_COL_STRIKE, + "text", col_num + PREV_N_FIXED_COLS -1, nullptr); + + /* We want a monospace font fixed-width data is properly displayed. */ + g_object_set (G_OBJECT(renderer), "family", "monospace", nullptr); + + /* Add a combobox to select column types as column header. Each uses the same + * common model for the dropdown list. The selected value is taken + * from the column_types vector. */ + auto cbox = preview_cbox_factory (GTK_TREE_MODEL(model), col_num - 1); + gtk_tree_view_column_set_widget (col, cbox); + + /* Enable resizing of the columns. */ + gtk_tree_view_column_set_resizable (col, true); + gtk_tree_view_column_set_clickable (col, true); + } +} + +/* Helper to create a shared store for the header comboboxes in the preview treeview. + * It holds the possible column types */ +GtkTreeModel* +make_column_header_model_price (void) +{ + auto combostore = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT); + for (auto col_type : gnc_price_col_type_strs) + { + GtkTreeIter iter; + gtk_list_store_append (combostore, &iter); + gtk_list_store_set (combostore, &iter, + COL_TYPE_NAME, _(col_type.second), + COL_TYPE_ID, static_cast(col_type.first), -1); + } + return GTK_TREE_MODEL(combostore); +} + +/* Updates the preview treeview to show the data as parsed based on the user's + * import parameters. + */ +void CsvImpPriceAssist::preview_refresh_table () +{ + preview_validate_settings (); + + /* Create a new liststore to hold status and data from the file being imported. + The first columns hold status information (row-color, row-errors, row-error-icon,... + All following columns represent the tokenized data as strings. */ + auto ncols = PREV_N_FIXED_COLS + price_imp->column_types_price().size(); + auto model_col_types = g_new (GType, ncols); + model_col_types[PREV_COL_FCOLOR] = G_TYPE_STRING; + model_col_types[PREV_COL_BCOLOR] = G_TYPE_STRING; + model_col_types[PREV_COL_ERROR] = G_TYPE_STRING; + model_col_types[PREV_COL_ERR_ICON] = G_TYPE_STRING; + model_col_types[PREV_COL_STRIKE] = G_TYPE_BOOLEAN; + for (guint i = PREV_N_FIXED_COLS; i < ncols; i++) + model_col_types[i] = G_TYPE_STRING; + auto store = gtk_list_store_newv (ncols, model_col_types); + g_free (model_col_types); + + /* Fill the data liststore with data from importer object. */ + for (auto parse_line : price_imp->m_parsed_lines) + { + /* Fill the state cells */ + GtkTreeIter iter; + gtk_list_store_append (store, &iter); + preview_row_fill_state_cells (store, &iter, + std::get<1>(parse_line), std::get<3>(parse_line)); + + /* Fill the data cells. */ + for (auto cell_str_it = std::get<0>(parse_line).cbegin(); cell_str_it != std::get<0>(parse_line).cend(); cell_str_it++) + { + uint32_t pos = PREV_N_FIXED_COLS + cell_str_it - std::get<0>(parse_line).cbegin(); + gtk_list_store_set (store, &iter, pos, cell_str_it->c_str(), -1); + } + } + gtk_tree_view_set_model (treeview, GTK_TREE_MODEL(store)); + gtk_tree_view_set_tooltip_column (treeview, PREV_COL_ERROR); + + /* Adjust treeview to go with the just created model. This consists of adding + * or removing columns and resetting any parameters related to how + * the columns and data should be rendered. + */ + + /* Start with counting the current number of columns (ntcols) + * we have in the treeview */ + auto columns = gtk_tree_view_get_columns (treeview); + auto ntcols = g_list_length(columns); + g_list_free (columns); + + /* Drop redundant columns if the model has less data columns than the new model + * ntcols = n° of columns in treeview (1 error column + x data columns) + * ncols = n° of columns in model (fixed state columns + x data columns) + */ + while (ntcols > ncols - PREV_N_FIXED_COLS + 1) + { + auto col = gtk_tree_view_get_column (treeview, ntcols - 1); + gtk_tree_view_column_clear (col); + ntcols = gtk_tree_view_remove_column(treeview, col); + } + + /* Insert columns if the model has more data columns than the treeview. */ + while (ntcols < ncols - PREV_N_FIXED_COLS + 1) + { + /* Default cell renderer is text, except for the first (error) column */ + auto renderer = gtk_cell_renderer_text_new(); + if (ntcols == 0) + renderer = gtk_cell_renderer_pixbuf_new(); // Error column uses an icon + auto col = gtk_tree_view_column_new (); + gtk_tree_view_column_pack_start (col, renderer, false); + ntcols = gtk_tree_view_append_column (treeview, col); + } + + /* Reset column attributes as they are undefined after recreating the model */ + auto combostore = make_column_header_model_price (); + for (uint32_t i = 0; i < ntcols; i++) + preview_style_column (i, combostore); + + /* Release our reference for the stores to allow proper memory management. */ + g_object_unref (store); + g_object_unref (combostore); + + /* Make the things actually appear. */ + gtk_widget_show_all (GTK_WIDGET(treeview)); +} + +/* Update the preview page based on the current state of the importer. + * Should be called when settings are changed. + */ +void +CsvImpPriceAssist::preview_refresh () +{ + // Set start row + auto adj = gtk_spin_button_get_adjustment (start_row_spin); + gtk_adjustment_set_upper (adj, price_imp->m_parsed_lines.size()); + gtk_spin_button_set_value (start_row_spin, + price_imp->skip_start_lines()); + + // Set end row + adj = gtk_spin_button_get_adjustment (end_row_spin); + gtk_adjustment_set_upper (adj, price_imp->m_parsed_lines.size()); + gtk_spin_button_set_value (end_row_spin, + price_imp->skip_end_lines()); + + // Set Alternate rows + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(skip_alt_rows_button), + price_imp->skip_alt_lines()); + + // Set over-write indicator + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(over_write_cbutton), + price_imp->over_write()); + + // Set Import Format + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(csv_button), + (price_imp->file_format() == GncImpFileFormat::CSV)); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(fixed_button), + (price_imp->file_format() != GncImpFileFormat::CSV)); + + // This section deals with the combo's and character encoding + gtk_combo_box_set_active (GTK_COMBO_BOX(date_format_combo), + price_imp->date_format()); + gtk_combo_box_set_active (GTK_COMBO_BOX(currency_format_combo), + price_imp->currency_format()); + go_charmap_sel_set_encoding (encselector, price_imp->encoding().c_str()); + + // Handle separator checkboxes and custom field, only relevant if the file format is csv + if (price_imp->file_format() == GncImpFileFormat::CSV) + { + auto separators = price_imp->separators(); + const auto stock_sep_chars = std::string (" \t,:;-"); + for (int i = 0; i < SEP_NUM_OF_TYPES; i++) + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(sep_button[i]), + separators.find (stock_sep_chars[i]) != std::string::npos); + + // If there are any other separators in the separators string, + // add them as custom separators + auto pos = separators.find_first_of (stock_sep_chars); + while (!separators.empty() && pos != std::string::npos) + { + separators.erase(pos); + pos = separators.find_first_of (stock_sep_chars); + } + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(custom_cbutton), + !separators.empty()); + gtk_entry_set_text (GTK_ENTRY(custom_entry), separators.c_str()); + } + // Repopulate the parsed data table + g_idle_add ((GSourceFunc)csv_imp_preview_queue_rebuild_table, this); +} + +/* Check if all selected data can be parsed sufficiently to continue + */ +void CsvImpPriceAssist::preview_validate_settings () +{ + /* Allow the user to proceed only if there are no inconsistencies in the settings */ + auto error_msg = price_imp->verify(); + gtk_assistant_set_page_complete (csv_imp_asst, preview_page, error_msg.empty()); + gtk_label_set_markup(GTK_LABEL(instructions_label), error_msg.c_str()); + gtk_widget_set_visible (GTK_WIDGET(instructions_image), !error_msg.empty()); +} + +/******************************************************* + * Assistant page prepare functions + *******************************************************/ + +void +CsvImpPriceAssist::assist_file_page_prepare () +{ + /* Set the default directory */ + auto starting_dir = gnc_get_default_directory (GNC_PREFS_GROUP); + if (starting_dir) + { + gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER(file_chooser), starting_dir); + g_free (starting_dir); + } +} + +void +CsvImpPriceAssist::assist_preview_page_prepare () +{ + /* Disable the Forward Assistant Button */ + gtk_assistant_set_page_complete (csv_imp_asst, preview_page, false); + + /* Load the data into the treeview. */ + preview_refresh_table (); +} + +void +CsvImpPriceAssist::assist_confirm_page_prepare () +{ +} + +void +CsvImpPriceAssist::assist_summary_page_prepare () +{ + auto text = std::string(""); + text += _("The prices were imported from the file '") + m_file_name + "'."; + text += _("\n\nThe number of Prices added was ") + std::to_string(price_imp->m_prices_added); + text += _(" and ") + std::to_string(price_imp->m_prices_duplicated); + text += _(" were duplicated."); + text += ""; + + gtk_label_set_markup (GTK_LABEL(summary_label), text.c_str()); +} + +void +CsvImpPriceAssist::assist_prepare_cb (GtkWidget *page) +{ + if (page == file_page) + assist_file_page_prepare (); + else if (page == preview_page) + assist_preview_page_prepare (); + else if (page == confirm_page) + assist_confirm_page_prepare (); + else if (page == summary_page) + assist_summary_page_prepare (); +} + +void +CsvImpPriceAssist::assist_finish (bool canceled) +{ + /* Start the import */ +//FIXME Apply button +g_print("Finish\n"); +// if (canceled || price_imp->m_transactions.empty()) +// gnc_gen_trans_list_delete (gnc_csv_importer_gui); +// else +// gnc_gen_trans_assist_start (gnc_csv_importer_gui); + + +//FIXME Cancel comes here to, check when nothing set, goes to catch below also + + /* Create prices from the parsed data */ + try + { + price_imp->create_prices (); + } + catch (const std::invalid_argument& err) + { + /* Oops! This shouldn't happen when using the import assistant ! + * Inform the user and go back to the preview page. + */ + gnc_error_dialog (GTK_WIDGET(csv_imp_asst), + _("An unexpected error has occurred while creating prices. Please report this as a bug.\n\n" + "Error message:\n%s"), err.what()); + gtk_assistant_set_current_page (csv_imp_asst, 2); + } +} + +void +CsvImpPriceAssist::assist_compmgr_close () +{ + gnc_save_window_size (GNC_PREFS_GROUP, GTK_WINDOW(csv_imp_asst)); + gtk_widget_destroy (GTK_WIDGET(csv_imp_asst)); +} + +static void +csv_price_imp_close_handler (gpointer user_data) +{ + auto info = (CsvImpPriceAssist*)user_data; + info->assist_compmgr_close(); +} + +/********************************************************************\ + * gnc_file_csv_price_import * + * opens up a assistant to import prices. * + * * + * Args: none * + * Return: nothing * +\********************************************************************/ +void +gnc_file_csv_price_import(void) +{ + auto info = new CsvImpPriceAssist; + gnc_register_gui_component (ASSISTANT_CSV_IMPORT_PRICE_CM_CLASS, + nullptr, csv_price_imp_close_handler, + info); +} diff --git a/gnucash/import-export/csv-imp/assistant-csv-price-import.glade b/gnucash/import-export/csv-imp/assistant-csv-price-import.glade new file mode 100644 index 0000000000..705e8274aa --- /dev/null +++ b/gnucash/import-export/csv-imp/assistant-csv-price-import.glade @@ -0,0 +1,1001 @@ + + + + + + 1000 + 1 + 10 + + + 1000 + 1 + 10 + + + False + 12 + CSV Price Import + 400 + 500 + + + + + + + + True + False + This assistant will help you import Prices from a CSV file. + +There is a minimum number of columns that have to be present for a successful import, for Stock prices these are Date, Amount, Symbol From and for Currency they are Date, Amount, Currency From and Currency To. + +Various options exist for specifying the delimiter as well as a fixed width option. With the fixed width option, double click on the bar above the displayed rows to set the column width. + +Examples are "RR.L","21/11/2016",5.345,"GBP" and "USD","2016-11-21",1.56,"GBP" + +There is an option for specifying the start row, end row and an option to skip alternate rows beginning from the start row which can be used if you have some header text. Also there is an option to over write existing prices for that day if required. + +On the preview page you can Load and Save the settings. To save the settings, select a previously saved entry or replace the text and press the Save Settings button. + +This operation is not reversable, so make sure you have a working backup. + +Click on 'Forward' to proceed or 'Cancel' to Abort Import. + True + + + intro + True + + + + + True + False + 12 + + + True + False + +Select location and file name for the Import, then click 'OK'... + + True + + + False + False + 0 + + + + + Select File for Import + + + + + True + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 12 + 2 + + + True + False + 2 + 2 + 5 + 5 + + + True + False + 0 + in + + + True + False + 5 + 5 + 5 + + + True + False + + + True + True + True + Delete Settings + + + + True + False + gtk-delete + + + + + False + False + end + 0 + + + + + True + True + True + Save Settings + + + + True + False + gtk-save + + + + + False + False + end + 2 + + + + + + + + + True + False + <b>Load and Save Settings</b> + True + False + + + + + GTK_FILL + GTK_FILL + + + + + True + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 0 + none + + + True + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 5 + 5 + + + True + False + + + True + False + 2 + 2 + + + Separators + True + True + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + True + True + + + + + + Fixed-Width + True + True + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + True + csv_button + + + 1 + 2 + + + + + True + False + + + 2 + 1 + 2 + GTK_FILL + + + + + False + False + 0 + + + + + True + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 3 + 3 + 3 + + + Space + True + True + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + True + + + + GTK_FILL + GTK_FILL + + + + + Tab + True + True + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + True + + + + 1 + 2 + GTK_FILL + GTK_FILL + + + + + Comma (,) + True + True + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + True + True + + + + 2 + 3 + GTK_FILL + GTK_FILL + + + + + Colon (:) + True + True + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + True + + + + 1 + 2 + GTK_FILL + GTK_FILL + + + + + Semicolon (;) + True + True + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + True + + + + 1 + 2 + 1 + 2 + GTK_FILL + GTK_FILL + + + + + Hyphen (-) + True + True + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + True + + + + 2 + 3 + 1 + 2 + GTK_FILL + GTK_FILL + + + + + Custom + True + True + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + True + + + + 2 + 3 + GTK_FILL + GTK_FILL + + + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + + True + False + False + True + True + + + + 1 + 3 + 2 + 3 + GTK_FILL + GTK_FILL + + + + + False + False + 1 + + + + + False + True + + + True + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 0 + gtk-dialog-info + + + False + True + 2 + 0 + + + + + True + False + 2 + 2 + + + True + False + 0 + 5 + + True + True + + + + + True + False + 0 + Double-click anywhere on the table below to insert a column break + True + True + + + 1 + 2 + + + + + + True + False + 0 + 5 + + True + True + + + 1 + 2 + + + + + True + False + 0 + Right-click anywhere in a column to modify it (widen, narrow, merge) + True + True + + + 1 + 2 + 1 + 2 + + + + + + True + True + 1 + + + + + False + False + 2 + + + + + True + False + 2 + + + True + False + + + GTK_FILL + + + + + Allow existing prices to be over written. + True + True + False + Normally prices are not over written, select this to change that. + True + + + + 1 + 2 + + + + + False + False + 3 + + + + + + + + + True + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + <b>File Format</b> + True + + + + + 1 + 2 + GTK_FILL + GTK_FILL + + + + + True + False + 0 + none + + + True + False + 5 + 5 + + + True + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + + + True + False + 6 + 2 + 5 + 5 + + + True + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 0 + + + 1 + 2 + 1 + 2 + GTK_FILL + + + + + True + False + 0 + Date Format + + + 1 + 2 + GTK_SHRINK | GTK_FILL + + + + + True + False + 0 + + + 1 + 2 + 2 + 3 + GTK_FILL + + + + + True + False + 0 + Currency Format + + + 2 + 3 + GTK_SHRINK | GTK_FILL + + + + + True + False + 0 + Encoding + + + GTK_SHRINK | GTK_FILL + + + + + True + False + 0 + + + 1 + 2 + GTK_FILL + + + + + True + False + 0 + Leading Lines to Skip + + + 4 + 5 + GTK_FILL + + + + + True + False + 0 + Trailing Lines to Skip + + + 5 + 6 + GTK_FILL + + + + + True + False + + + True + True + + True + False + False + True + True + start_row_adj + True + + + + False + False + 0 + + + + + 1 + 2 + 4 + 5 + + + + + True + False + + + True + True + + True + False + False + True + True + end_row_adj + True + + + + False + False + 0 + + + + + 1 + 2 + 5 + 6 + + + + + True + False + + + 2 + 3 + 4 + + + + + False + True + 0 + + + + + Skip alternate lines + True + True + False + Starting from the first line that is actually imported every second line will be skipped. This option will take the leading lines to skip into account as well. +For example +* if 'Leading Lines to Skip' is set to 3, the first line to import will be line 4. Lines 5, 7, 9,... will be skipped. +* if 'Leading Lines to Skip' is set to 4, the first line to import will be line 5. Lines 6, 8, 10,... will be skipped. + True + + + + False + True + 1 + + + + + + + + + True + False + <b>Miscellaneous</b> + True + + + + + 1 + 2 + 1 + 2 + GTK_FILL + GTK_FILL + + + + + False + False + 0 + + + + + True + True + automatic + automatic + + + True + False + + + True + False + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + False + both + + + False + True + 0 + + + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + both + + + True + True + 1 + + + + + + + + + True + True + 1 + + + + + True + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + + + True + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 0 + gtk-dialog-info + + + False + True + 2 + 0 + + + + + True + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 0 + Select the type of each column to import. + + + True + True + 1 + + + + + False + False + 5 + 2 + + + + + True + False + + + Skip Errors + True + False + 1 + right + True + + + + False + False + end + 0 + + + + + False + False + 3 + + + + + intro + True + + + + + True + False + 12 + + + True + False + + + True + False + Press Apply to add Prices. +Cancel to abort. + center + True + + + + + True + False + 0 + + + + + confirm + Import Prices Now + + + + + True + False + 12 + + + True + False + label + True + True + + + True + True + 0 + + + + + summary + Import Summary + True + + + + diff --git a/gnucash/import-export/csv-imp/assistant-csv-price-import.h b/gnucash/import-export/csv-imp/assistant-csv-price-import.h new file mode 100644 index 0000000000..213b622e2e --- /dev/null +++ b/gnucash/import-export/csv-imp/assistant-csv-price-import.h @@ -0,0 +1,36 @@ +/*******************************************************************\ + * assistant-csv-price-import.h -- An assistant for importing * + * Prices from a file. * + * * + * Copyright (C) 2017 Robert Fewell * + * * + * 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 * +\********************************************************************/ +/** @file assistant-csv-price-import.h + @brief CSV Import Assistant + @author Copyright (c) 2017 Robert Fewell +*/ +#ifndef GNC_ASSISTANT_CSV_IMPORT_PRICE_H +#define GNC_ASSISTANT_CSV_IMPORT_PRICE_H + + +/** The gnc_file_csv_price_import() will let the user import the + * commodity prices from a file. + */ +void gnc_file_csv_price_import (void); +#endif diff --git a/gnucash/import-export/csv-imp/gnc-plugin-csv-import-ui.xml b/gnucash/import-export/csv-imp/gnc-plugin-csv-import-ui.xml index c119a648cb..bdd3043cb4 100644 --- a/gnucash/import-export/csv-imp/gnc-plugin-csv-import-ui.xml +++ b/gnucash/import-export/csv-imp/gnc-plugin-csv-import-ui.xml @@ -5,6 +5,7 @@ + diff --git a/gnucash/import-export/csv-imp/gnc-plugin-csv-import.c b/gnucash/import-export/csv-imp/gnc-plugin-csv-import.c index 25954beb9d..c14092d1c9 100644 --- a/gnucash/import-export/csv-imp/gnc-plugin-csv-import.c +++ b/gnucash/import-export/csv-imp/gnc-plugin-csv-import.c @@ -30,6 +30,7 @@ #include "assistant-csv-account-import.h" #include "assistant-csv-trans-import.h" +#include "assistant-csv-price-import.h" static void gnc_plugin_csv_import_class_init (GncPluginCsvImportClass *klass); static void gnc_plugin_csv_import_init (GncPluginCsvImport *plugin); @@ -38,6 +39,7 @@ static void gnc_plugin_csv_import_finalize (GObject *object); /* Command callbacks */ static void gnc_plugin_csv_import_tree_cmd (GtkAction *action, GncMainWindowActionData *data); static void gnc_plugin_csv_import_trans_cmd (GtkAction *action, GncMainWindowActionData *data); +static void gnc_plugin_csv_import_price_cmd (GtkAction *action, GncMainWindowActionData *data); #define PLUGIN_ACTIONS_NAME "gnc-plugin-csv-import-actions" #define PLUGIN_UI_FILENAME "gnc-plugin-csv-import-ui.xml" @@ -54,6 +56,11 @@ static GtkActionEntry gnc_plugin_actions [] = N_("Import Transactions from a CSV file"), G_CALLBACK (gnc_plugin_csv_import_trans_cmd) }, + { + "CsvImportPriceAction", GTK_STOCK_CONVERT, N_("Import _Prices from a CSV file..."), NULL, + N_("Import Prices from a CSV file"), + G_CALLBACK (gnc_plugin_csv_import_price_cmd) + }, }; static guint gnc_plugin_n_actions = G_N_ELEMENTS (gnc_plugin_actions); @@ -157,6 +164,13 @@ gnc_plugin_csv_import_trans_cmd (GtkAction *action, gnc_file_csv_trans_import (); } +static void +gnc_plugin_csv_import_price_cmd (GtkAction *action, + GncMainWindowActionData *data) +{ + gnc_file_csv_price_import (); +} + /************************************************************ * Plugin Bootstrapping * ************************************************************/ From cf90b8cb47a6dfe38e870310534fa1a1c0e294b7 Mon Sep 17 00:00:00 2001 From: Robert Fewell <14uBobIT@gmail.com> Date: Fri, 1 Dec 2017 11:03:07 +0000 Subject: [PATCH 05/48] Remove not required account update --- .../import-export/csv-imp/assistant-csv-price-import.cpp | 6 ------ 1 file changed, 6 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 a6ba349ff4..6c4279a813 100644 --- a/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp +++ b/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp @@ -87,7 +87,6 @@ public: void preview_over_write (bool over); void preview_update_separators (GtkWidget* widget); void preview_update_file_format (); - void preview_update_account (); void preview_update_encoding (const char* encoding); void preview_update_date_format (); void preview_update_currency_format (); @@ -306,11 +305,6 @@ void csv_price_imp_preview_sep_fixed_sel_cb (GtkToggleButton* csv_button, CsvImp info->preview_update_file_format(); } -void csv_price_imp_preview_acct_sel_cb (GtkWidget* widget, CsvImpPriceAssist* info) -{ - info->preview_update_account(); -} - void csv_price_imp_preview_enc_sel_cb (GOCharmapSel* selector, const char* encoding, CsvImpPriceAssist* info) { From 1435813f0208e69aac24705f93f03d10b9cab90b Mon Sep 17 00:00:00 2001 From: Robert Fewell <14uBobIT@gmail.com> Date: Fri, 1 Dec 2017 11:04:49 +0000 Subject: [PATCH 06/48] Some text changes --- gnucash/import-export/csv-imp/gnc-price-import.cpp | 2 +- gnucash/import-export/csv-imp/gnc-price-import.hpp | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/gnucash/import-export/csv-imp/gnc-price-import.cpp b/gnucash/import-export/csv-imp/gnc-price-import.cpp index 09deb934e0..5437237ef1 100644 --- a/gnucash/import-export/csv-imp/gnc-price-import.cpp +++ b/gnucash/import-export/csv-imp/gnc-price-import.cpp @@ -400,7 +400,7 @@ void GncPriceImport::verify_column_selections (ErrorListPrice& error_msg) if (!check_for_column_type(GncPricePropType::AMOUNT)) error_msg.add_error( _("Please select an amount column.")); - /* Verify an Currency to column is selected. + /* Verify a Currency to column is selected. */ if (!check_for_column_type(GncPricePropType::CURRENCY_TO)) error_msg.add_error( _("Please select a Currency to column.")); diff --git a/gnucash/import-export/csv-imp/gnc-price-import.hpp b/gnucash/import-export/csv-imp/gnc-price-import.hpp index 0898215b29..60cdc27807 100644 --- a/gnucash/import-export/csv-imp/gnc-price-import.hpp +++ b/gnucash/import-export/csv-imp/gnc-price-import.hpp @@ -68,11 +68,10 @@ struct ErrorListPrice; * - set a file format * - load a file * - optionally convert it's encoding - * - parse the file into lines, which in turn are split up in columns + * - parse the file into lines, which in turn are split up in to columns * the result of this step can be queried from tokenizer * - the user should now map the columns to types, which is stored in column_types - * - last step is convert the mapped columns into a list of transactions - * - this list will then be passed on the the generic importer for further processing */ + * - last step is convert the mapped columns into a list of prices to add */ class GncPriceImport { public: From bf0c3853ac0d9a2f57acfd5dac61c69a583ff23b Mon Sep 17 00:00:00 2001 From: Robert Fewell <14uBobIT@gmail.com> Date: Fri, 1 Dec 2017 11:25:06 +0000 Subject: [PATCH 07/48] Add option to specify Commodity from and Currency to for whole file Added two combo's to allow user to specify a Commodity from and Currency to for the whole file. Also reduced the property types to four and aligned all the commodity and currency variables. --- .../csv-imp/assistant-csv-price-import.cpp | 202 +++++++++++++++++- .../csv-imp/assistant-csv-price-import.glade | 150 ++++++++++++- .../csv-imp/gnc-price-import.cpp | 118 ++++++++-- .../csv-imp/gnc-price-import.hpp | 9 +- .../import-export/csv-imp/gnc-price-props.cpp | 120 +++++------ .../import-export/csv-imp/gnc-price-props.hpp | 23 +- 6 files changed, 525 insertions(+), 97 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 6c4279a813..120367a58e 100644 --- a/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp +++ b/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp @@ -90,6 +90,8 @@ public: void preview_update_encoding (const char* encoding); void preview_update_date_format (); void preview_update_currency_format (); + void preview_update_currency (); + void preview_update_commodity (); void preview_update_col_type (GtkComboBox* cbox); void preview_update_fw_columns (GtkTreeView* treeview, GdkEventButton* event); @@ -134,6 +136,8 @@ private: GtkWidget *csv_button; /**< The widget for the CSV button */ GtkWidget *fixed_button; /**< The widget for the Fixed Width button */ GtkWidget *over_write_cbutton; /**< The widget for Price Over Write */ + GtkWidget *commodity_selector; /**< The widget for commodity combo box */ + GtkWidget *currency_selector; /**< The widget for currency combo box */ GOCharmapSel *encselector; /**< The widget for selecting the encoding */ GtkWidget *separator_table; /**< Container for the separator checkboxes */ GtkCheckButton *sep_button[SEP_NUM_OF_TYPES]; /**< Checkbuttons for common separators */ @@ -321,6 +325,16 @@ static void csv_price_imp_preview_currency_fmt_sel_cb (GtkComboBox* format_selec info->preview_update_currency_format(); } +static void csv_price_imp_preview_currency_sel_cb (GtkComboBox* currency_selector, CsvImpPriceAssist* info) +{ + info->preview_update_currency(); +} + +static void csv_price_imp_preview_commodity_sel_cb (GtkComboBox* commodity_selector, CsvImpPriceAssist* info) +{ + info->preview_update_commodity(); +} + void csv_price_imp_preview_col_type_changed_cb (GtkComboBox* cbox, CsvImpPriceAssist* info) { info->preview_update_col_type (cbox); @@ -334,6 +348,126 @@ csv_price_imp_preview_treeview_clicked_cb (GtkTreeView* treeview, GdkEventButton return false; } +static +gnc_commodity *get_commodity_from_combo (GtkComboBox *combo) +{ + GtkTreeModel *model, *sort_model; + GtkTreeIter iter, siter; + gchar *string; + gnc_commodity *comm; + + if (!gtk_combo_box_get_active_iter (combo, &siter)) + return nullptr; + + sort_model = gtk_combo_box_get_model (combo); + model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT(sort_model)); + + gtk_tree_model_sort_convert_iter_to_child_iter (GTK_TREE_MODEL_SORT(sort_model), + &iter, &siter); + + gtk_tree_model_get (GTK_TREE_MODEL (model), &iter, 0, &string, 2, &comm, -1); + + PINFO("Commodity string is %s", string); + + g_free (string); + return comm; +} + +static void +set_commodity_for_combo (GtkComboBox *combo, gnc_commodity *comm) +{ + GtkTreeModel *model, *sort_model; + GtkTreeIter iter, siter; + gnc_commodity *model_comm; + gboolean valid; + + sort_model = gtk_combo_box_get_model (combo); + model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT(sort_model)); + valid = gtk_tree_model_get_iter_first (model, &iter); + + while (valid) + { + gtk_tree_model_get (model, &iter, 2, &model_comm, -1); + if (model_comm == comm) + { + if (gtk_tree_model_sort_convert_child_iter_to_iter (GTK_TREE_MODEL_SORT(sort_model), &siter, &iter)) + { + gtk_combo_box_set_active_iter (combo, &siter); + return; + } + } + /* Make iter point to the next row in the list store */ + valid = gtk_tree_model_iter_next (model, &iter); + } + // Not found, set it to first iter + valid = gtk_tree_model_get_iter_first (model, &iter); + if (gtk_tree_model_sort_convert_child_iter_to_iter (GTK_TREE_MODEL_SORT(sort_model), &siter, &iter)) + gtk_combo_box_set_active_iter (combo, &siter); +} + +static +GtkTreeModel *get_model (bool all_commodity) +{ + GtkTreeModel *store, *model; + const gnc_commodity_table *commodity_table = gnc_get_current_commodities (); + gnc_commodity *tmp_commodity = nullptr; + char *tmp_namespace = nullptr; + GList *commodity_list = nullptr; + GList *namespace_list = gnc_commodity_table_get_namespaces (commodity_table); + GtkTreeIter iter; + + store = GTK_TREE_MODEL(gtk_list_store_new (3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER)); + model = gtk_tree_model_sort_new_with_model (store); + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (model), + 0, GTK_SORT_ASCENDING); + + gtk_list_store_append (GTK_LIST_STORE(store), &iter); + gtk_list_store_set (GTK_LIST_STORE(store), &iter, 0, " ", 1, nullptr, -1); + + namespace_list = g_list_first (namespace_list); + while (namespace_list != nullptr) + { + tmp_namespace = (char*)namespace_list->data; + DEBUG("Looking at namespace %s", tmp_namespace); + + /* Hide the template entry */ + if (g_utf8_collate (tmp_namespace, "template" ) != 0) + { + if ((g_utf8_collate (tmp_namespace, GNC_COMMODITY_NS_CURRENCY ) == 0) || (all_commodity == true)) + { + commodity_list = gnc_commodity_table_get_commodities (commodity_table, tmp_namespace); + commodity_list = g_list_first (commodity_list); + while (commodity_list != nullptr) + { + gchar *name_str; + gchar *save_str; + gchar *settings_str; + tmp_commodity = (gnc_commodity*)commodity_list->data; + DEBUG("Looking at commodity %s", gnc_commodity_get_fullname (tmp_commodity)); + + name_str = g_strconcat (tmp_namespace, " : (", gnc_commodity_get_mnemonic (tmp_commodity), + ") ", gnc_commodity_get_fullname (tmp_commodity), nullptr); + + settings_str = g_strconcat (tmp_namespace, "::", gnc_commodity_get_mnemonic (tmp_commodity), nullptr); + DEBUG("Name string is %s, Save string is %s", name_str, settings_str); + + gtk_list_store_append (GTK_LIST_STORE(store), &iter); + gtk_list_store_set (GTK_LIST_STORE(store), &iter, 0, name_str, 1, settings_str, 2, tmp_commodity, -1); + + g_free (name_str); + g_free (settings_str); + commodity_list = g_list_next (commodity_list); + } + } + } + namespace_list = g_list_next (namespace_list); + } + g_list_free (commodity_list); + g_list_free (namespace_list); + + return model; +} + /******************************************************* * Assistant Constructor @@ -343,6 +477,8 @@ CsvImpPriceAssist::CsvImpPriceAssist () auto builder = gtk_builder_new(); gnc_builder_add_from_file (builder , "assistant-csv-price-import.glade", "start_row_adj"); gnc_builder_add_from_file (builder , "assistant-csv-price-import.glade", "end_row_adj"); + gnc_builder_add_from_file (builder , "assistant-csv-price-import.glade", "liststore1"); + gnc_builder_add_from_file (builder , "assistant-csv-price-import.glade", "liststore2"); gnc_builder_add_from_file (builder , "assistant-csv-price-import.glade", "CSV Price Assistant"); csv_imp_asst = GTK_ASSISTANT(gtk_builder_get_object (builder, "CSV Price Assistant")); @@ -453,6 +589,18 @@ CsvImpPriceAssist::CsvImpPriceAssist () gtk_container_add (encoding_container, GTK_WIDGET(encselector)); gtk_widget_show_all (GTK_WIDGET(encoding_container)); + /* Add commodity selection widget */ + commodity_selector = GTK_WIDGET(gtk_builder_get_object (builder, "commodity_cbox")); + gtk_combo_box_set_model (GTK_COMBO_BOX(commodity_selector), get_model (true)); + g_signal_connect(G_OBJECT(commodity_selector), "changed", + G_CALLBACK(csv_price_imp_preview_commodity_sel_cb), this); + + /* Add currency selection widget */ + currency_selector = GTK_WIDGET(gtk_builder_get_object (builder, "currency_cbox")); + gtk_combo_box_set_model (GTK_COMBO_BOX(currency_selector), get_model (false)); + g_signal_connect(G_OBJECT(currency_selector), "changed", + G_CALLBACK(csv_price_imp_preview_currency_sel_cb), this); + /* The instructions label and image */ instructions_label = GTK_LABEL(gtk_builder_get_object (builder, "instructions_label")); instructions_image = GTK_IMAGE(gtk_builder_get_object (builder, "instructions_image")); @@ -570,6 +718,9 @@ CsvImpPriceAssist::file_confirm_cb () preview_populate_settings_combo(); gtk_combo_box_set_active (settings_combo, 0); + // set over_write to false as default + price_imp->over_write (false); + auto num = gtk_assistant_get_current_page (csv_imp_asst); gtk_assistant_set_current_page (csv_imp_asst, num + 1); } @@ -951,6 +1102,22 @@ CsvImpPriceAssist::preview_update_currency_format () preview_refresh_table (); } +void +CsvImpPriceAssist::preview_update_currency () +{ + gnc_commodity *comm = get_commodity_from_combo (GTK_COMBO_BOX(currency_selector)); + price_imp->to_currency (comm); + preview_refresh_table (); +} + +void +CsvImpPriceAssist::preview_update_commodity () +{ + gnc_commodity *comm = get_commodity_from_combo (GTK_COMBO_BOX(commodity_selector)); + price_imp->from_commodity (comm); + preview_refresh_table (); +} + gboolean csv_imp_preview_queue_rebuild_table (CsvImpPriceAssist *assist) { @@ -1413,6 +1580,28 @@ void CsvImpPriceAssist::preview_refresh_table () for (uint32_t i = 0; i < ntcols; i++) preview_style_column (i, combostore); + auto column_types = price_imp->column_types_price(); + + // look for a commodity column, clear the commdoity combo + auto col_type_comm = std::find (column_types.begin(), + column_types.end(), GncPricePropType::FROM_COMMODITY); + if (col_type_comm != column_types.end()) + { + g_signal_handlers_block_by_func (commodity_selector, (gpointer) csv_price_imp_preview_commodity_sel_cb, this); + set_commodity_for_combo (GTK_COMBO_BOX(commodity_selector), nullptr); + g_signal_handlers_unblock_by_func (commodity_selector, (gpointer) csv_price_imp_preview_commodity_sel_cb, this); + } + + // look for a currency column, clear the currency combo + auto col_type_curr = std::find (column_types.begin(), + column_types.end(), GncPricePropType::TO_CURRENCY); + if (col_type_curr != column_types.end()) + { + g_signal_handlers_block_by_func (currency_selector, (gpointer) csv_price_imp_preview_currency_sel_cb, this); + set_commodity_for_combo (GTK_COMBO_BOX(currency_selector), nullptr); + g_signal_handlers_unblock_by_func (currency_selector, (gpointer) csv_price_imp_preview_currency_sel_cb, this); + } + /* Release our reference for the stores to allow proper memory management. */ g_object_unref (store); g_object_unref (combostore); @@ -1460,6 +1649,13 @@ CsvImpPriceAssist::preview_refresh () price_imp->currency_format()); go_charmap_sel_set_encoding (encselector, price_imp->encoding().c_str()); + // Set the commodity and currency combos + set_commodity_for_combo(GTK_COMBO_BOX(commodity_selector), + price_imp->from_commodity()); + + set_commodity_for_combo(GTK_COMBO_BOX(currency_selector), + price_imp->to_currency()); + // Handle separator checkboxes and custom field, only relevant if the file format is csv if (price_imp->file_format() == GncImpFileFormat::CSV) { @@ -1533,9 +1729,9 @@ CsvImpPriceAssist::assist_summary_page_prepare () auto text = std::string(""); text += _("The prices were imported from the file '") + m_file_name + "'."; text += _("\n\nThe number of Prices added was ") + std::to_string(price_imp->m_prices_added); - text += _(" and ") + std::to_string(price_imp->m_prices_duplicated); - text += _(" were duplicated."); - text += ""; + text += _(", duplicated was ") + std::to_string(price_imp->m_prices_duplicated); + text += _(" and replaced was ") + std::to_string(price_imp->m_prices_replaced); + text += "."; gtk_label_set_markup (GTK_LABEL(summary_label), text.c_str()); } diff --git a/gnucash/import-export/csv-imp/assistant-csv-price-import.glade b/gnucash/import-export/csv-imp/assistant-csv-price-import.glade index 705e8274aa..764b1967a2 100644 --- a/gnucash/import-export/csv-imp/assistant-csv-price-import.glade +++ b/gnucash/import-export/csv-imp/assistant-csv-price-import.glade @@ -7,6 +7,26 @@ 1 10 + + + + + + + + + + + + + + + + + + + + 1000 1 @@ -515,7 +535,7 @@ Select location and file name for the Import, then click 'OK'... True True False - Normally prices are not over written, select this to change that. + Normally prices are not over written, select this to change that. This setting is not saved. True @@ -804,6 +824,9 @@ For example GTK_FILL + + + False @@ -811,6 +834,125 @@ For example 0 + + + True + False + + + True + False + 0 + none + + + True + False + 5 + 5 + 5 + + + True + False + + + True + False + liststore1 + + + + 0 + + + + + False + False + 0 + + + + + + + + + True + False + <b>Commodity From</b> + True + + + + + False + False + 0 + + + + + True + False + 0 + none + + + True + False + 5 + 5 + 5 + + + True + False + + + True + False + liststore1 + + + + 0 + + + + + False + False + 0 + + + + + + + + + True + False + <b>Currency To</b> + True + + + + + False + False + 2 + + + + + False + False + 1 + + True @@ -860,7 +1002,7 @@ For example True True - 1 + 2 @@ -902,7 +1044,7 @@ For example False False 5 - 2 + 3 @@ -930,7 +1072,7 @@ For example False False - 3 + 4 diff --git a/gnucash/import-export/csv-imp/gnc-price-import.cpp b/gnucash/import-export/csv-imp/gnc-price-import.cpp index 5437237ef1..e7bb72a0e5 100644 --- a/gnucash/import-export/csv-imp/gnc-price-import.cpp +++ b/gnucash/import-export/csv-imp/gnc-price-import.cpp @@ -125,7 +125,6 @@ void GncPriceImport::file_format(GncImpFileFormat format) auto fwtok = dynamic_cast(m_tokenizer.get()); fwtok->columns (m_settings.m_column_widths); } - } GncImpFileFormat GncPriceImport::file_format() @@ -140,6 +139,50 @@ void GncPriceImport::over_write (bool over) bool GncPriceImport::over_write () { return m_over_write; } +/** Sets a from commodity. This is the commodity all import data relates to. + * When a from commodity is set, there can't be any from columns selected + * in the import data. + * @param from_commodity Pointer to a commodity or NULL. + */ +void GncPriceImport::from_commodity (gnc_commodity* from_commodity) +{ + m_settings.m_from_commodity = from_commodity; + + if (m_settings.m_from_commodity) + { + auto col_type = std::find (m_settings.m_column_types_price.begin(), + m_settings.m_column_types_price.end(), GncPricePropType::FROM_COMMODITY); + + if (col_type != m_settings.m_column_types_price.end()) + set_column_type_price (col_type -m_settings.m_column_types_price.begin(), + GncPricePropType::NONE); + } +} + +gnc_commodity *GncPriceImport::from_commodity () { return m_settings.m_from_commodity; } + +/** Sets a to currency. This is the to currency all import data relates to. + * When a to currency is set, there can't be any to currency columns selected + * in the import data. + * @param to_currency Pointer to a commodity or NULL. + */ +void GncPriceImport::to_currency (gnc_commodity* to_currency) +{ + m_settings.m_to_currency = to_currency; + + if (m_settings.m_to_currency) + { + auto col_type = std::find (m_settings.m_column_types_price.begin(), + m_settings.m_column_types_price.end(), GncPricePropType::TO_CURRENCY); + + if (col_type != m_settings.m_column_types_price.end()) + set_column_type_price (col_type -m_settings.m_column_types_price.begin(), + GncPricePropType::NONE); + } +} + +gnc_commodity *GncPriceImport::to_currency () { return m_settings.m_to_currency; } + void GncPriceImport::reset_formatted_column (std::vector& col_types) { for (auto col_type: col_types) @@ -241,6 +284,8 @@ void GncPriceImport::settings (const CsvTransSettings& settings) /* First apply file format as this may recreate the tokenizer */ file_format (settings.m_file_format); /* Only then apply the other settings */ + from_commodity (m_settings.m_from_commodity); + to_currency (m_settings.m_to_currency); m_settings = settings; encoding (m_settings.m_encoding); @@ -402,14 +447,19 @@ void GncPriceImport::verify_column_selections (ErrorListPrice& error_msg) /* Verify a Currency to column is selected. */ - if (!check_for_column_type(GncPricePropType::CURRENCY_TO)) - error_msg.add_error( _("Please select a Currency to column.")); + if (!check_for_column_type(GncPricePropType::TO_CURRENCY)) + { + if (!m_settings.m_to_currency) + error_msg.add_error( _("Please select a Currency to column or set a Currency in the Currency To field.")); + } - /* Verify at least one from column (symbol_from or currency_from) column is selected. + /* Verify a Commodity from column is selected. */ - if (!check_for_column_type(GncPricePropType::SYMBOL_FROM) && - !check_for_column_type(GncPricePropType::CURRENCY_FROM)) - error_msg.add_error( _("Please select a symbol or currency from column.")); + if (!check_for_column_type(GncPricePropType::FROM_COMMODITY)) + { + if (!m_settings.m_from_commodity) + error_msg.add_error( _("Please select a Commodity from column or set a Commodity in the Commodity From field.")); + } } @@ -497,6 +547,40 @@ void GncPriceImport::create_price (std::vector::iterator& parsed_l error_message.clear(); + // Add a CURRENCY_TO property with the default currency to if no currency to column was set by the user + auto line_to_currency = price_props->get_to_currency(); + if (!line_to_currency) + { + if (m_settings.m_to_currency) + price_props->set_to_currency(m_settings.m_to_currency); + else + { + // Oops - the user didn't select an Account column *and* we didn't get a default value either! + // Note if you get here this suggests a bug in the code! + error_message = _("No Currency to column selected and no default Currency specified either.\n" + "This should never happen. Please report this as a bug."); + PINFO("User warning: %s", error_message.c_str()); + throw std::invalid_argument(error_message); + } + } + + // Add a COMMODITY_FROM property with the default commodity from if no commodity from column was set by the user + auto line_from_commodity = price_props->get_from_commodity(); + if (!line_from_commodity) + { + if (m_settings.m_from_commodity) + price_props->set_from_commodity(m_settings.m_from_commodity); + else + { + // Oops - the user didn't select an Account column *and* we didn't get a default value either! + // Note if you get here this suggests a bug in the code! + error_message = _("No Commodity from column selected and no default Commodity specified either.\n" + "This should never happen. Please report this as a bug."); + PINFO("User warning: %s", error_message.c_str()); + throw std::invalid_argument(error_message); + } + } + /* If column parsing was successful, convert price properties into a price. */ try { @@ -507,11 +591,12 @@ void GncPriceImport::create_price (std::vector::iterator& parsed_l /* If all went well, add this price to the list. */ auto price_created = price_props->create_price (book, pdb, m_over_write); -//FIXME Need to look at this - if (price_created) + if (price_created == ADDED) m_prices_added++; - else + else if (price_created == DUPLICATED) m_prices_duplicated++; + else if (price_created == REPLACED) + m_prices_replaced++; } catch (const std::invalid_argument& e) { @@ -537,6 +622,7 @@ void GncPriceImport::create_prices () m_prices_added = 0; m_prices_duplicated = 0; + m_prices_replaced = 0; /* Iterate over all parsed lines */ for (auto parsed_lines_it = m_parsed_lines.begin(); @@ -550,8 +636,8 @@ void GncPriceImport::create_prices () /* Should not throw anymore, otherwise verify needs revision */ create_price (parsed_lines_it); } - PINFO("Number of lines is %d, added is %d, duplicates is %d", - (int)m_parsed_lines.size(), m_prices_added, m_prices_duplicated); + PINFO("Number of lines is %d, added %d, duplicated %d, replaced %d", + (int)m_parsed_lines.size(), m_prices_added, m_prices_duplicated, m_prices_replaced); } bool @@ -611,6 +697,14 @@ GncPriceImport::set_column_type_price (uint32_t position, GncPricePropType type, m_settings.m_column_types_price.at (position) = type; + // If the user has set a Commodity from column, we can't have a commodity from default set + if (type == GncPricePropType::FROM_COMMODITY) + from_commodity (nullptr); + + // If the user has set a Currency to column, we can't have a currency to default set + if (type == GncPricePropType::TO_CURRENCY) + to_currency (nullptr); + /* Update the preparsed data */ for (auto parsed_lines_it = m_parsed_lines.begin(); parsed_lines_it != m_parsed_lines.end(); diff --git a/gnucash/import-export/csv-imp/gnc-price-import.hpp b/gnucash/import-export/csv-imp/gnc-price-import.hpp index 60cdc27807..809464eaea 100644 --- a/gnucash/import-export/csv-imp/gnc-price-import.hpp +++ b/gnucash/import-export/csv-imp/gnc-price-import.hpp @@ -32,7 +32,7 @@ extern "C" { #include "config.h" - +#include "gnc-commodity.h" } #include @@ -85,6 +85,12 @@ public: void over_write (bool over); bool over_write (); + void from_commodity (gnc_commodity *from_commodity); + gnc_commodity *from_commodity (); + + void to_currency (gnc_commodity *to_currency); + gnc_commodity *to_currency (); + void currency_format (int currency_format); int currency_format (); @@ -131,6 +137,7 @@ public: price properties. */ int m_prices_added; int m_prices_duplicated; + int m_prices_replaced; private: /** A helper function used by create_prices. It will attempt diff --git a/gnucash/import-export/csv-imp/gnc-price-props.cpp b/gnucash/import-export/csv-imp/gnc-price-props.cpp index a39f178851..e6f983e525 100644 --- a/gnucash/import-export/csv-imp/gnc-price-props.cpp +++ b/gnucash/import-export/csv-imp/gnc-price-props.cpp @@ -32,8 +32,6 @@ extern "C" { #include "engine-helpers.h" #include "gnc-ui-util.h" -#include "gnc-pricedb.h" - } #include @@ -48,9 +46,8 @@ std::map gnc_price_col_type_strs = { { GncPricePropType::NONE, N_("None") }, { GncPricePropType::DATE, N_("Date") }, { GncPricePropType::AMOUNT, N_("Amount") }, - { GncPricePropType::CURRENCY_FROM, N_("Currency From") }, - { GncPricePropType::CURRENCY_TO, N_("Currency To") }, - { GncPricePropType::SYMBOL_FROM, N_("Symbol From") }, + { GncPricePropType::FROM_COMMODITY, N_("Commodity From") }, + { GncPricePropType::TO_CURRENCY, N_("Currency To") }, }; /* Regular expressions used to parse dates per date format */ @@ -243,6 +240,7 @@ gnc_commodity* parse_commodity_price_comm (const std::string& comm_str) return comm; } +//FIXME can we change above to do below gnc_commodity * parse_commodity_price_sym (const std::string& sym_str, bool is_currency) { if (sym_str.empty()) @@ -285,8 +283,8 @@ gnc_commodity * parse_commodity_price_sym (const std::string& sym_str, bool is_c throw std::invalid_argument (_("Value can't be parsed into a valid commodity.")); else { - if (gnc_commodity_is_currency (retval) != is_currency) - throw std::invalid_argument (_("Value parsed into an invalid commodity for column type.")); + if ((is_currency == true) && (gnc_commodity_is_currency (retval) != true)) + throw std::invalid_argument (_("Value parsed into an invalid currency for currency column type.")); else return retval; } @@ -312,25 +310,18 @@ void GncImportPrice::set (GncPricePropType prop_type, const std::string& value) m_amount = parse_amount_price (value, m_currency_format); // Will throw if parsing fails break; - case GncPricePropType::CURRENCY_FROM: - m_currency_from = boost::none; - comm = parse_commodity_price_sym (value, true); // Throws if parsing fails - if (comm) - m_currency_from = comm; - break; - - case GncPricePropType::CURRENCY_TO: - m_currency_to = boost::none; - comm = parse_commodity_price_sym (value, true); // Throws if parsing fails - if (comm) - m_currency_to = comm; - break; - - case GncPricePropType::SYMBOL_FROM: - m_symbol_from = boost::none; + case GncPricePropType::FROM_COMMODITY: + m_from_commodity = boost::none; comm = parse_commodity_price_sym (value, false); // Throws if parsing fails if (comm) - m_symbol_from = comm; + m_from_commodity = comm; + break; + + case GncPricePropType::TO_CURRENCY: + m_to_currency = boost::none; + comm = parse_commodity_price_sym (value, true); // Throws if parsing fails + if (comm) + m_to_currency = comm; break; default: @@ -378,15 +369,15 @@ std::string GncImportPrice::verify_essentials (void) return _("No date column."); else if (m_amount == boost::none) return _("No amount column."); - else if (m_currency_to == boost::none) + else if (m_to_currency == boost::none) return _("No Currency to column."); - else if ((m_symbol_from == boost::none) && (m_currency_from == boost::none)) - return _("No from column."); + else if (m_from_commodity == boost::none) + return _("No Commodity from column."); else return std::string(); } -bool GncImportPrice::create_price (QofBook* book, GNCPriceDB *pdb, bool over) +Result GncImportPrice::create_price (QofBook* book, GNCPriceDB *pdb, bool over) { /* Gently refuse to create the price if the basics are not set correctly * This should have been tested before calling this function though! @@ -395,47 +386,40 @@ bool GncImportPrice::create_price (QofBook* book, GNCPriceDB *pdb, bool over) if (!check.empty()) { PWARN ("Refusing to create price because essentials not set properly: %s", check.c_str()); - return false; + return FAILED; } Timespec date; timespecFromTime64 (&date, *m_date); date.tv_nsec = 0; -#ifdef skip -//FIXME Numeric needs changing, copied from old version... bool rev = false; - gnc_commodity *comm_from = nullptr; + auto amount = *m_amount; - if (m_currency_from != boost::none) // Currency Import + GNCPrice *old_price = gnc_pricedb_lookup_day (pdb, *m_from_commodity, *m_to_currency, date); + + if (gnc_commodity_is_currency (*m_from_commodity)) // Currency Import { // Check for currency in reverse direction. - GNCPrice *rev_price = gnc_pricedb_lookup_day (pdb, *m_currency_to, *m_currency_from, date); - if (rev_price != nullptr) - rev = true; - gnc_price_unref (rev_price); + if (old_price != nullptr) + { + // Check for price in reverse direction. + if (gnc_commodity_equiv (gnc_price_get_currency (old_price), *m_from_commodity)) + rev = true; + + DEBUG("Commodity from is a Currency"); + } // Check for price less than 1, reverse if so. - if (gnc_numeric_compare (*m_amount, gnc_numeric_create (1, 1)) != 1) + if (*m_amount < GncNumeric(1,1)) rev = true; - comm_from = *m_currency_from; - DEBUG("Commodity from is a Currency"); } - else - comm_from = *m_symbol_from; - DEBUG("Date is %s, Rev is %d, Commodity from is '%s', Currency is '%s', Amount is %s", gnc_print_date (date), - rev, gnc_commodity_get_fullname (comm_from), gnc_commodity_get_fullname (*m_currency_to), - gnc_num_dbg_to_string (*m_amount) ); + rev, gnc_commodity_get_fullname (*m_from_commodity), gnc_commodity_get_fullname (*m_to_currency), + amount.to_string().c_str()); - GNCPrice *old_price = nullptr; - - // Should the commodities be reversed - if (rev) - old_price = gnc_pricedb_lookup_day (pdb, *m_currency_to, comm_from, date); - else - old_price = gnc_pricedb_lookup_day (pdb, comm_from, *m_currency_to, date); + Result ret_val = ADDED; // Should old price be over writen if ((old_price != nullptr) && (over == true)) @@ -444,31 +428,29 @@ bool GncImportPrice::create_price (QofBook* book, GNCPriceDB *pdb, bool over) gnc_pricedb_remove_price (pdb, old_price); gnc_price_unref (old_price); old_price = nullptr; + ret_val = REPLACED; } -#endif - bool ret_val = true; -#ifdef skip + // Create the new price if (old_price == nullptr) { DEBUG("Create"); GNCPrice *price = gnc_price_create (book); gnc_price_begin_edit (price); - if (rev) { - gnc_price_set_commodity (price, *m_currency_to); - gnc_price_set_currency (price, comm_from); - *m_amount = gnc_numeric_convert (gnc_numeric_invert (*m_amount), - CURRENCY_DENOM, GNC_HOW_RND_ROUND_HALF_UP); - gnc_price_set_value (price, *m_amount); + amount = amount.inv(); //invert the amount + gnc_price_set_commodity (price, *m_to_currency); + gnc_price_set_currency (price, *m_from_commodity); } else { - gnc_price_set_commodity (price, comm_from); - gnc_price_set_currency (price, *m_currency_to); - gnc_price_set_value (price, *m_amount); + gnc_price_set_commodity (price, *m_from_commodity); + gnc_price_set_currency (price, *m_to_currency); } + auto amount_conv = amount.convert(CURRENCY_DENOM); + gnc_price_set_value (price, static_cast(amount_conv)); + gnc_price_set_time (price, date); gnc_price_set_source (price, PRICE_SOURCE_USER_PRICE); //FIXME Not sure which one gnc_price_set_source (price, PRICE_SOURCE_FQ); @@ -479,15 +461,15 @@ bool GncImportPrice::create_price (QofBook* book, GNCPriceDB *pdb, bool over) gnc_price_unref (price); - if (perr == false) + if (perr == false) throw std::invalid_argument (_("Failed to create price from selected columns.")); //FIXME Not sure about this, should this be a PWARN } else - -#endif - ret_val = false; - + { + gnc_price_unref (old_price); + ret_val = DUPLICATED; + } return ret_val; } diff --git a/gnucash/import-export/csv-imp/gnc-price-props.hpp b/gnucash/import-export/csv-imp/gnc-price-props.hpp index 57083e9a4b..a8550ea1e3 100644 --- a/gnucash/import-export/csv-imp/gnc-price-props.hpp +++ b/gnucash/import-export/csv-imp/gnc-price-props.hpp @@ -49,12 +49,13 @@ enum class GncPricePropType { NONE, DATE, AMOUNT, - CURRENCY_FROM, - CURRENCY_TO, - SYMBOL_FROM, - PRICE_PROPS = SYMBOL_FROM + FROM_COMMODITY, + TO_CURRENCY, + PRICE_PROPS = TO_CURRENCY }; +enum Result { FAILED, ADDED, DUPLICATED, REPLACED }; + /** Maps all column types to a string representation. * The actual definition is in gnc-price-props.cpp. * Attention: that definition should be adjusted for any @@ -91,7 +92,14 @@ public: void set_currency_format (int currency_format) { m_currency_format = currency_format ;} void reset (GncPricePropType prop_type); std::string verify_essentials (void); - bool create_price (QofBook* book, GNCPriceDB *pdb, bool over); + Result create_price (QofBook* book, GNCPriceDB *pdb, bool over); + + gnc_commodity* get_from_commodity () { if (m_from_commodity) return *m_from_commodity; else return nullptr; } + void set_from_commodity (gnc_commodity* comm) { if (comm) m_from_commodity = comm; else m_from_commodity = boost::none; } + + gnc_commodity* get_to_currency () { if (m_to_currency) return *m_to_currency; else return nullptr; } + void set_to_currency (gnc_commodity* curr) { if (curr) m_to_currency = curr; else m_to_currency = boost::none; } + std::string errors(); private: @@ -99,9 +107,8 @@ private: int m_currency_format; boost::optional m_date; boost::optional m_amount; - boost::optional m_currency_from; - boost::optional m_currency_to; - boost::optional m_symbol_from; + boost::optional m_from_commodity; + boost::optional m_to_currency; bool created = false; std::map m_errors; From cd4b5a31006a36d2fdbd8f2f1ef8839a4c83bfea Mon Sep 17 00:00:00 2001 From: Robert Fewell <14uBobIT@gmail.com> Date: Fri, 1 Dec 2017 11:30:16 +0000 Subject: [PATCH 08/48] Made changes to preset column types to align with other changes These changes are to align with the changes to column types and also the basic setup of the new commodity from and currency to combo's. More changes will follow to make the saving and loading work properly. --- .../csv-imp/gnc-csv-trans-settings.cpp | 29 +++++++++++++++++-- .../csv-imp/gnc-csv-trans-settings.hpp | 8 +++-- 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/gnucash/import-export/csv-imp/gnc-csv-trans-settings.cpp b/gnucash/import-export/csv-imp/gnc-csv-trans-settings.cpp index 48cfdce9d2..5235a4ccf3 100644 --- a/gnucash/import-export/csv-imp/gnc-csv-trans-settings.cpp +++ b/gnucash/import-export/csv-imp/gnc-csv-trans-settings.cpp @@ -63,6 +63,8 @@ const std::string gnc_exp{N_("GnuCash Export Format")}; #define CSV_COL_TYPES "ColumnTypes" #define CSV_COL_WIDTHS "ColumnWidths" #define CSV_ACCOUNT "BaseAccount" +#define CSV_TO_CURR "PriceToCurrency" +#define CSV_FROM_COMM "PriceFromCommodity" G_GNUC_UNUSED static QofLogModule log_module = GNC_MOD_IMPORT; @@ -111,9 +113,8 @@ static std::shared_ptr create_int_gnc_exp_preset(void) preset->m_column_types_price = { GncPricePropType::DATE, GncPricePropType::AMOUNT, - GncPricePropType::CURRENCY_FROM, - GncPricePropType::CURRENCY_TO, - GncPricePropType::SYMBOL_FROM + GncPricePropType::FROM_COMMODITY, + GncPricePropType::TO_CURRENCY, }; return preset; @@ -266,6 +267,22 @@ CsvTransSettings::load (void) if (key_char) g_free (key_char); + key_char = g_key_file_get_string (keyfile, group.c_str(), CSV_TO_CURR, &key_error); + if (key_char && *key_char != '\0') +//FIXME m_to_currency = gnc_account_lookup_by_full_name (gnc_get_current_root_account(), key_char); + m_to_currency = nullptr; + m_load_error |= handle_load_error (&key_error, group); + if (key_char) + g_free (key_char); + + key_char = g_key_file_get_string (keyfile, group.c_str(), CSV_FROM_COMM, &key_error); + if (key_char && *key_char != '\0') +//FIXME m_from_commodity = gnc_account_lookup_by_full_name (gnc_get_current_root_account(), key_char); + m_from_commodity = nullptr; + m_load_error |= handle_load_error (&key_error, group); + if (key_char) + g_free (key_char); + m_column_types.clear(); gsize list_len; gchar** col_types_str = g_key_file_get_string_list (keyfile, group.c_str(), CSV_COL_TYPES, @@ -361,6 +378,12 @@ CsvTransSettings::save (void) if (m_base_account) g_key_file_set_string (keyfile, group.c_str(), CSV_ACCOUNT, gnc_account_get_full_name(m_base_account)); + if (m_to_currency) + g_key_file_set_string (keyfile, group.c_str(), CSV_TO_CURR, gnc_commodity_get_fullname(m_to_currency)); + + if (m_from_commodity) + g_key_file_set_string (keyfile, group.c_str(), CSV_FROM_COMM, gnc_commodity_get_fullname(m_from_commodity)); + std::vector col_types_str; for (auto col_type : m_column_types) col_types_str.push_back(gnc_csv_col_type_strs[col_type]); diff --git a/gnucash/import-export/csv-imp/gnc-csv-trans-settings.hpp b/gnucash/import-export/csv-imp/gnc-csv-trans-settings.hpp index 93275f2da0..dae837dfb8 100644 --- a/gnucash/import-export/csv-imp/gnc-csv-trans-settings.hpp +++ b/gnucash/import-export/csv-imp/gnc-csv-trans-settings.hpp @@ -31,6 +31,7 @@ extern "C" { #include #include "Account.h" +#include "gnc-commodity.h" } #include @@ -53,8 +54,8 @@ struct CsvTransSettings CsvTransSettings() : m_file_format (GncImpFileFormat::CSV), m_encoding {"UTF-8"}, m_multi_split (false), m_date_format {0}, m_currency_format {0}, m_skip_start_lines{0}, m_skip_end_lines{0}, m_skip_alt_lines (false), - m_separators {","}, m_base_account {nullptr}, - m_load_error {false} { } + m_separators {","}, m_base_account {nullptr}, m_to_currency {nullptr}, + m_from_commodity {nullptr}, m_load_error {false} { } /** Save the gathered widget properties to a key File. * @@ -97,6 +98,9 @@ std::vector m_column_types; // The Column types in order std::vector m_column_types_price; // The Column Price types in order std::vector m_column_widths; // The Column widths +gnc_commodity *m_to_currency; // Price To Currency +gnc_commodity *m_from_commodity; // Price From Commodity + bool m_load_error; // Was there an error while parsing the state file ? }; From 4d75259cb41902581bcffaf71ca2742164e635c2 Mon Sep 17 00:00:00 2001 From: Robert Fewell <14uBobIT@gmail.com> Date: Fri, 1 Dec 2017 11:32:48 +0000 Subject: [PATCH 09/48] Remove duplicated function --- .../import-export/csv-imp/gnc-price-props.cpp | 60 +++---------------- .../import-export/csv-imp/gnc-price-props.hpp | 1 - 2 files changed, 7 insertions(+), 54 deletions(-) diff --git a/gnucash/import-export/csv-imp/gnc-price-props.cpp b/gnucash/import-export/csv-imp/gnc-price-props.cpp index e6f983e525..d9293e13dd 100644 --- a/gnucash/import-export/csv-imp/gnc-price-props.cpp +++ b/gnucash/import-export/csv-imp/gnc-price-props.cpp @@ -240,56 +240,6 @@ gnc_commodity* parse_commodity_price_comm (const std::string& comm_str) return comm; } -//FIXME can we change above to do below -gnc_commodity * parse_commodity_price_sym (const std::string& sym_str, bool is_currency) -{ - if (sym_str.empty()) - return nullptr; - - auto commodity_table = gnc_get_current_commodities (); - GList *namespaces; - gnc_commodity *retval = nullptr; - gnc_commodity *tmp_commodity = nullptr; - char *tmp_namespace = nullptr; - GList *commodity_list = NULL; - GList *namespace_list = gnc_commodity_table_get_namespaces (commodity_table); - - namespace_list = g_list_first (namespace_list); - while (namespace_list != NULL && retval == NULL) - { - tmp_namespace = (char*)namespace_list->data; - DEBUG("Looking at namespace %s", tmp_namespace); - commodity_list = gnc_commodity_table_get_commodities (commodity_table, tmp_namespace); - commodity_list = g_list_first (commodity_list); - while (commodity_list != NULL && retval == NULL) - { - const char* tmp_mnemonic = NULL; - tmp_commodity = (gnc_commodity*)commodity_list->data; - DEBUG("Looking at commodity %s", gnc_commodity_get_fullname (tmp_commodity)); - tmp_mnemonic = gnc_commodity_get_mnemonic (tmp_commodity); - if (g_strcmp0 (tmp_mnemonic, sym_str.c_str()) == 0) - { - retval = tmp_commodity; - DEBUG("Commodity %s%s", gnc_commodity_get_fullname (retval), " matches."); - } - commodity_list = g_list_next (commodity_list); - } - namespace_list = g_list_next (namespace_list); - } - g_list_free (commodity_list); - g_list_free (namespace_list); - - if (!retval) - throw std::invalid_argument (_("Value can't be parsed into a valid commodity.")); - else - { - if ((is_currency == true) && (gnc_commodity_is_currency (retval) != true)) - throw std::invalid_argument (_("Value parsed into an invalid currency for currency column type.")); - else - return retval; - } -} - void GncImportPrice::set (GncPricePropType prop_type, const std::string& value) { try @@ -307,21 +257,25 @@ void GncImportPrice::set (GncPricePropType prop_type, const std::string& value) case GncPricePropType::AMOUNT: m_amount = boost::none; - m_amount = parse_amount_price (value, m_currency_format); // Will throw if parsing fails + m_amount = parse_amount_price (value, m_currency_format); // Throws if parsing fails break; case GncPricePropType::FROM_COMMODITY: m_from_commodity = boost::none; - comm = parse_commodity_price_sym (value, false); // Throws if parsing fails + comm = parse_commodity_price_comm (value); // Throws if parsing fails if (comm) m_from_commodity = comm; break; case GncPricePropType::TO_CURRENCY: m_to_currency = boost::none; - comm = parse_commodity_price_sym (value, true); // Throws if parsing fails + comm = parse_commodity_price_comm (value); // Throws if parsing fails if (comm) + { + if (gnc_commodity_is_currency (comm) != true) + throw std::invalid_argument (_("Value parsed into an invalid currency for a currency column type.")); m_to_currency = comm; + } break; default: diff --git a/gnucash/import-export/csv-imp/gnc-price-props.hpp b/gnucash/import-export/csv-imp/gnc-price-props.hpp index a8550ea1e3..9ae485323e 100644 --- a/gnucash/import-export/csv-imp/gnc-price-props.hpp +++ b/gnucash/import-export/csv-imp/gnc-price-props.hpp @@ -78,7 +78,6 @@ private: time64 parse_date_price (const std::string &date_str, int format); gnc_commodity* parse_commodity_price_comm (const std::string& comm_str); -gnc_commodity* parse_commodity_price_sym (const std::string& comm_str, bool is_currency); GncNumeric parse_amount_price (const std::string &str, int currency_format); struct GncImportPrice From 5578da11860737109f678443b9bf8b4e7084b2dd Mon Sep 17 00:00:00 2001 From: Robert Fewell <14uBobIT@gmail.com> Date: Fri, 1 Dec 2017 11:34:21 +0000 Subject: [PATCH 10/48] Fix some errors in conversion of some function names Some function names did not get converted to a price equivalent and reorder some statements. --- .../import-export/csv-imp/assistant-csv-price-import.cpp | 8 ++++---- gnucash/import-export/csv-imp/gnc-price-import.cpp | 2 +- gnucash/import-export/csv-imp/gnc-price-import.hpp | 8 ++++---- gnucash/import-export/csv-imp/gnc-price-props.hpp | 1 - 4 files changed, 9 insertions(+), 10 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 120367a58e..2d2cfddc4c 100644 --- a/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp +++ b/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp @@ -607,9 +607,9 @@ CsvImpPriceAssist::CsvImpPriceAssist () /* Add in the date format combo box and hook it up to an event handler. */ date_format_combo = GTK_COMBO_BOX_TEXT(gtk_combo_box_text_new()); - for (int i = 0; i < num_date_formats; i++) + for (int i = 0; i < num_date_formats_price; i++) { - gtk_combo_box_text_append_text (date_format_combo, _(date_format_user[i])); + gtk_combo_box_text_append_text (date_format_combo, _(date_format_user_price[i])); } gtk_combo_box_set_active (GTK_COMBO_BOX(date_format_combo), 0); g_signal_connect (G_OBJECT(date_format_combo), "changed", @@ -622,9 +622,9 @@ CsvImpPriceAssist::CsvImpPriceAssist () /* Add in the currency format combo box and hook it up to an event handler. */ currency_format_combo = GTK_COMBO_BOX_TEXT(gtk_combo_box_text_new()); - for (int i = 0; i < num_currency_formats; i++) + for (int i = 0; i < num_currency_formats_price; i++) { - gtk_combo_box_text_append_text (currency_format_combo, _(currency_format_user[i])); + gtk_combo_box_text_append_text (currency_format_combo, _(currency_format_user_price[i])); } /* Default will the locale */ gtk_combo_box_set_active (GTK_COMBO_BOX(currency_format_combo), 0); diff --git a/gnucash/import-export/csv-imp/gnc-price-import.cpp b/gnucash/import-export/csv-imp/gnc-price-import.cpp index e7bb72a0e5..38a3cbcb57 100644 --- a/gnucash/import-export/csv-imp/gnc-price-import.cpp +++ b/gnucash/import-export/csv-imp/gnc-price-import.cpp @@ -284,9 +284,9 @@ void GncPriceImport::settings (const CsvTransSettings& settings) /* First apply file format as this may recreate the tokenizer */ file_format (settings.m_file_format); /* Only then apply the other settings */ + m_settings = settings; from_commodity (m_settings.m_from_commodity); to_currency (m_settings.m_to_currency); - m_settings = settings; encoding (m_settings.m_encoding); if (file_format() == GncImpFileFormat::CSV) diff --git a/gnucash/import-export/csv-imp/gnc-price-import.hpp b/gnucash/import-export/csv-imp/gnc-price-import.hpp index 809464eaea..be91941a86 100644 --- a/gnucash/import-export/csv-imp/gnc-price-import.hpp +++ b/gnucash/import-export/csv-imp/gnc-price-import.hpp @@ -46,12 +46,12 @@ extern "C" { #include /* A set of currency formats that the user sees. */ -extern const int num_currency_formats; -extern const gchar* currency_format_user[]; +extern const int num_currency_formats_price; +extern const gchar* currency_format_user_price[]; /* A set of date formats that the user sees. */ -extern const int num_date_formats; -extern const gchar* date_format_user[]; +extern const int num_date_formats_price; +extern const gchar* date_format_user_price[]; /** Tuple to hold * - a tokenized line of input diff --git a/gnucash/import-export/csv-imp/gnc-price-props.hpp b/gnucash/import-export/csv-imp/gnc-price-props.hpp index 9ae485323e..1d44ff6ae0 100644 --- a/gnucash/import-export/csv-imp/gnc-price-props.hpp +++ b/gnucash/import-export/csv-imp/gnc-price-props.hpp @@ -90,7 +90,6 @@ public: void set_date_format (int date_format) { m_date_format = date_format ;} void set_currency_format (int currency_format) { m_currency_format = currency_format ;} void reset (GncPricePropType prop_type); - std::string verify_essentials (void); Result create_price (QofBook* book, GNCPriceDB *pdb, bool over); gnc_commodity* get_from_commodity () { if (m_from_commodity) return *m_from_commodity; else return nullptr; } From a996c02ef7da224a0a5962cb50985c1dd887df50 Mon Sep 17 00:00:00 2001 From: Robert Fewell <14uBobIT@gmail.com> Date: Fri, 1 Dec 2017 11:43:14 +0000 Subject: [PATCH 11/48] Change the settings file to save and load price settings. Added a setting type to distinguish between TRANS and PRICE settings so it can load a specific settings type and added the price save and load options. --- .../csv-imp/assistant-csv-price-import.cpp | 4 +- .../csv-imp/assistant-csv-trans-import.cpp | 4 +- .../csv-imp/gnc-csv-trans-settings.cpp | 204 +++++++++++------- .../csv-imp/gnc-csv-trans-settings.hpp | 26 ++- 4 files changed, 145 insertions(+), 93 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 2d2cfddc4c..e3a5ae3614 100644 --- a/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp +++ b/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp @@ -64,6 +64,8 @@ extern "C" /* This static indicates the debugging module that this .o belongs to. */ static QofLogModule log_module = GNC_MOD_ASSISTANT; +const std::string settings_type = "PRICE"; + class CsvImpPriceAssist { public: @@ -740,7 +742,7 @@ void CsvImpPriceAssist::preview_populate_settings_combo() // Append the default entry //FIXME get_trans_presets ???? - auto presets = get_trans_presets (); + auto presets = get_trans_presets (settings_type); for (auto preset : presets) { GtkTreeIter iter; 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 d52b676a6e..e3d10ccd7b 100644 --- a/gnucash/import-export/csv-imp/assistant-csv-trans-import.cpp +++ b/gnucash/import-export/csv-imp/assistant-csv-trans-import.cpp @@ -70,6 +70,8 @@ extern "C" /* This static indicates the debugging module that this .o belongs to. */ static QofLogModule log_module = GNC_MOD_ASSISTANT; +const std::string settings_type = "TRANS"; + class CsvImpTransAssist { public: @@ -677,7 +679,7 @@ void CsvImpTransAssist::preview_populate_settings_combo() // Append the default entry - auto presets = get_trans_presets (); + auto presets = get_trans_presets (settings_type); for (auto preset : presets) { GtkTreeIter iter; diff --git a/gnucash/import-export/csv-imp/gnc-csv-trans-settings.cpp b/gnucash/import-export/csv-imp/gnc-csv-trans-settings.cpp index 5235a4ccf3..73ab2a59a6 100644 --- a/gnucash/import-export/csv-imp/gnc-csv-trans-settings.cpp +++ b/gnucash/import-export/csv-imp/gnc-csv-trans-settings.cpp @@ -41,7 +41,7 @@ extern "C" #include "gnc-ui-util.h" } -const std::string csv_group_prefix{"CSV - "}; +const std::string csv_group_prefix{"CSV-"}; const std::string no_settings{N_("No Settings")}; const std::string gnc_exp{N_("GnuCash Export Format")}; #define CSV_NAME "Name" @@ -70,10 +70,11 @@ G_GNUC_UNUSED static QofLogModule log_module = GNC_MOD_IMPORT; preset_vec presets; -static std::shared_ptr create_int_no_preset(void) +static std::shared_ptr create_int_no_preset(const std::string& set_type) { auto preset = std::make_shared(); preset->m_name = no_settings; + preset->m_settings_type = set_type; return preset; } @@ -109,14 +110,6 @@ static std::shared_ptr create_int_gnc_exp_preset(void) GncTransPropType::REC_DATE, GncTransPropType::PRICE }; - - preset->m_column_types_price = { - GncPricePropType::DATE, - GncPricePropType::AMOUNT, - GncPricePropType::FROM_COMMODITY, - GncPricePropType::TO_CURRENCY, - }; - return preset; } @@ -124,8 +117,9 @@ static std::shared_ptr create_int_gnc_exp_preset(void) * find * * find all settings entries in the state key file + * based on settings type. **************************************************/ -const preset_vec& get_trans_presets (void) +const preset_vec& get_trans_presets (const std::string& set_type) { // Search all Groups in the state key file for ones starting with prefix @@ -138,11 +132,12 @@ const preset_vec& get_trans_presets (void) for (gsize i=0; i < grouplength; i++) { auto group = std::string(groups[i]); - auto pos = group.find(csv_group_prefix); + auto gp = csv_group_prefix + set_type + " - "; + auto pos = group.find(gp); if (pos == std::string::npos) continue; - preset_names.push_back(group.substr(csv_group_prefix.size())); + preset_names.push_back(group.substr(gp.size())); } // string array from the state file is no longer needed now. g_strfreev (groups); @@ -154,18 +149,20 @@ const preset_vec& get_trans_presets (void) presets.clear(); /* Start with the internally generated ones */ - presets.push_back(create_int_no_preset()); - presets.push_back(create_int_gnc_exp_preset()); + presets.push_back(create_int_no_preset(set_type)); + + if (set_type.compare("TRANS") == 0) + presets.push_back(create_int_gnc_exp_preset()); /* Then add all the ones we found in the state file */ for (auto preset_name : preset_names) { auto preset = std::make_shared(); + preset->m_settings_type = set_type; preset->m_name = preset_name; preset->load(); presets.push_back(preset); } - return presets; } @@ -215,7 +212,7 @@ CsvTransSettings::load (void) GError *key_error = nullptr; m_load_error = false; - auto group = csv_group_prefix + m_name; + auto group = csv_group_prefix + m_settings_type + " - " + m_name; auto keyfile = gnc_state_get_current (); m_skip_start_lines = g_key_file_get_integer (keyfile, group.c_str(), CSV_SKIP_START, &key_error); @@ -260,56 +257,82 @@ CsvTransSettings::load (void) if (key_char) g_free (key_char); - key_char = g_key_file_get_string (keyfile, group.c_str(), CSV_ACCOUNT, &key_error); - if (key_char && *key_char != '\0') - m_base_account = gnc_account_lookup_by_full_name (gnc_get_current_root_account(), key_char); - m_load_error |= handle_load_error (&key_error, group); - if (key_char) - g_free (key_char); - - key_char = g_key_file_get_string (keyfile, group.c_str(), CSV_TO_CURR, &key_error); - if (key_char && *key_char != '\0') -//FIXME m_to_currency = gnc_account_lookup_by_full_name (gnc_get_current_root_account(), key_char); - m_to_currency = nullptr; - m_load_error |= handle_load_error (&key_error, group); - if (key_char) - g_free (key_char); - - key_char = g_key_file_get_string (keyfile, group.c_str(), CSV_FROM_COMM, &key_error); - if (key_char && *key_char != '\0') -//FIXME m_from_commodity = gnc_account_lookup_by_full_name (gnc_get_current_root_account(), key_char); - m_from_commodity = nullptr; - m_load_error |= handle_load_error (&key_error, group); - if (key_char) - g_free (key_char); - - m_column_types.clear(); gsize list_len; - gchar** col_types_str = g_key_file_get_string_list (keyfile, group.c_str(), CSV_COL_TYPES, - &list_len, &key_error); - for (uint32_t i = 0; i < list_len; i++) + + // Transactions + if (m_settings_type.compare("TRANS") == 0) { - auto col_types_it = std::find_if (gnc_csv_col_type_strs.begin(), - gnc_csv_col_type_strs.end(), test_prop_type_str (col_types_str[i])); - if (col_types_it != gnc_csv_col_type_strs.end()) + key_char = g_key_file_get_string (keyfile, group.c_str(), CSV_ACCOUNT, &key_error); + if (key_char && *key_char != '\0') + m_base_account = gnc_account_lookup_by_full_name (gnc_get_current_root_account(), key_char); + m_load_error |= handle_load_error (&key_error, group); + if (key_char) + g_free (key_char); + + m_column_types.clear(); + gchar** col_types_str = g_key_file_get_string_list (keyfile, group.c_str(), CSV_COL_TYPES, + &list_len, &key_error); + for (uint32_t i = 0; i < list_len; i++) { - /* Found a valid column type. Now check whether it is allowed - * in the selected mode (two-split vs multi-split) */ - auto prop = sanitize_trans_prop (col_types_it->first, m_multi_split); - m_column_types.push_back(prop); - if (prop != col_types_it->first) - PWARN("Found column type '%s', but this is blacklisted when multi-split mode is %s. " - "Inserting column type 'NONE' instead'.", - col_types_it->second, m_multi_split ? "enabled" : "disabled"); + auto col_types_it = std::find_if (gnc_csv_col_type_strs.begin(), + gnc_csv_col_type_strs.end(), test_prop_type_str (col_types_str[i])); + if (col_types_it != gnc_csv_col_type_strs.end()) + { + /* Found a valid column type. Now check whether it is allowed + * in the selected mode (two-split vs multi-split) */ + auto prop = sanitize_trans_prop (col_types_it->first, m_multi_split); + m_column_types.push_back(prop); + if (prop != col_types_it->first) + PWARN("Found column type '%s', but this is blacklisted when multi-split mode is %s. " + "Inserting column type 'NONE' instead'.", + col_types_it->second, m_multi_split ? "enabled" : "disabled"); + } + else + PWARN("Found invalid column type '%s'. Inserting column type 'NONE' instead'.", + col_types_str[i]); } - else - PWARN("Found invalid column type '%s'. Inserting column type 'NONE' instead'.", - col_types_str[i]); - + if (col_types_str) + g_strfreev (col_types_str); } - if (col_types_str) - g_strfreev (col_types_str); + + // Price + if (m_settings_type.compare("PRICE") == 0) + { + key_char = g_key_file_get_string (keyfile, group.c_str(), CSV_TO_CURR, &key_error); + if (key_char && *key_char != '\0') + m_to_currency = parse_commodity_price_comm (key_char); + m_load_error |= handle_load_error (&key_error, group); + if (key_char) + g_free (key_char); + key_char = g_key_file_get_string (keyfile, group.c_str(), CSV_FROM_COMM, &key_error); + if (key_char && *key_char != '\0') + m_from_commodity = parse_commodity_price_comm (key_char); + m_load_error |= handle_load_error (&key_error, group); + if (key_char) + g_free (key_char); + + m_column_types.clear(); + gchar** col_types_str_price = g_key_file_get_string_list (keyfile, group.c_str(), CSV_COL_TYPES, + &list_len, &key_error); + for (uint32_t i = 0; i < list_len; i++) + { + auto col_types_it = std::find_if (gnc_price_col_type_strs.begin(), + gnc_price_col_type_strs.end(), test_price_prop_type_str (col_types_str_price[i])); + if (col_types_it != gnc_price_col_type_strs.end()) + { + // Found a valid column type + m_column_types_price.push_back(col_types_it->first); + } + else + PWARN("Found invalid column type '%s'. Inserting column type 'NONE' instead'.", + col_types_str_price[i]); + } + if (col_types_str_price) + g_strfreev (col_types_str_price); + } + + // Widths m_column_widths.clear(); gint *col_widths_int = g_key_file_get_integer_list (keyfile, group.c_str(), CSV_COL_WIDTHS, &list_len, &key_error); @@ -347,14 +370,15 @@ CsvTransSettings::save (void) } auto keyfile = gnc_state_get_current (); - auto group = csv_group_prefix + m_name; + auto group = csv_group_prefix + m_settings_type + " - " + m_name; // Drop previous saved settings with this name g_key_file_remove_group (keyfile, group.c_str(), nullptr); // Start Saving the settings + // Common g_key_file_set_string (keyfile, group.c_str(), CSV_NAME, m_name.c_str()); - g_key_file_set_boolean (keyfile, group.c_str(), CSV_MULTI_SPLIT, m_multi_split); + g_key_file_set_integer (keyfile, group.c_str(), CSV_SKIP_START, m_skip_start_lines); g_key_file_set_integer (keyfile, group.c_str(), CSV_SKIP_END, m_skip_end_lines); g_key_file_set_boolean (keyfile, group.c_str(), CSV_SKIP_ALT, m_skip_alt_lines); @@ -375,27 +399,45 @@ CsvTransSettings::save (void) g_key_file_set_integer (keyfile, group.c_str(), CSV_CURRENCY, m_currency_format); g_key_file_set_string (keyfile, group.c_str(), CSV_ENCODING, m_encoding.c_str()); - if (m_base_account) - g_key_file_set_string (keyfile, group.c_str(), CSV_ACCOUNT, gnc_account_get_full_name(m_base_account)); - - if (m_to_currency) - g_key_file_set_string (keyfile, group.c_str(), CSV_TO_CURR, gnc_commodity_get_fullname(m_to_currency)); - - if (m_from_commodity) - g_key_file_set_string (keyfile, group.c_str(), CSV_FROM_COMM, gnc_commodity_get_fullname(m_from_commodity)); - - std::vector col_types_str; - for (auto col_type : m_column_types) - col_types_str.push_back(gnc_csv_col_type_strs[col_type]); - - if (!col_types_str.empty()) - g_key_file_set_string_list (keyfile, group.c_str(), CSV_COL_TYPES, - col_types_str.data(), col_types_str.size()); - if (!m_column_widths.empty()) g_key_file_set_integer_list (keyfile, group.c_str(), CSV_COL_WIDTHS, (gint*)(m_column_widths.data()), m_column_widths.size()); + // Transaction + if (m_settings_type.compare("TRANS") == 0) + { + g_key_file_set_boolean (keyfile, group.c_str(), CSV_MULTI_SPLIT, m_multi_split); + + if (m_base_account) + g_key_file_set_string (keyfile, group.c_str(), CSV_ACCOUNT, gnc_account_get_full_name(m_base_account)); + + std::vector col_types_str; + for (auto col_type : m_column_types) + col_types_str.push_back(gnc_csv_col_type_strs[col_type]); + + if (!col_types_str.empty()) + g_key_file_set_string_list (keyfile, group.c_str(), CSV_COL_TYPES, + col_types_str.data(), col_types_str.size()); + } + + // Price + if (m_settings_type.compare("PRICE") == 0) + { + if (m_to_currency) + g_key_file_set_string (keyfile, group.c_str(), CSV_TO_CURR, gnc_commodity_get_mnemonic(m_to_currency)); + + if (m_from_commodity) + g_key_file_set_string (keyfile, group.c_str(), CSV_FROM_COMM, gnc_commodity_get_mnemonic(m_from_commodity)); + + std::vector col_types_str_price; + for (auto col_type : m_column_types_price) + col_types_str_price.push_back(gnc_price_col_type_strs[col_type]); + + if (!col_types_str_price.empty()) + g_key_file_set_string_list (keyfile, group.c_str(), CSV_COL_TYPES, + col_types_str_price.data(), col_types_str_price.size()); + } + // Do a test read of encoding GError *key_error = nullptr; bool error = false; @@ -425,7 +467,7 @@ CsvTransSettings::remove (void) return; auto keyfile = gnc_state_get_current (); - auto group = csv_group_prefix + m_name; + auto group = csv_group_prefix + m_settings_type + " - " + m_name; g_key_file_remove_group (keyfile, group.c_str(), nullptr); } diff --git a/gnucash/import-export/csv-imp/gnc-csv-trans-settings.hpp b/gnucash/import-export/csv-imp/gnc-csv-trans-settings.hpp index dae837dfb8..7df293c546 100644 --- a/gnucash/import-export/csv-imp/gnc-csv-trans-settings.hpp +++ b/gnucash/import-export/csv-imp/gnc-csv-trans-settings.hpp @@ -54,8 +54,8 @@ struct CsvTransSettings CsvTransSettings() : m_file_format (GncImpFileFormat::CSV), m_encoding {"UTF-8"}, m_multi_split (false), m_date_format {0}, m_currency_format {0}, m_skip_start_lines{0}, m_skip_end_lines{0}, m_skip_alt_lines (false), - m_separators {","}, m_base_account {nullptr}, m_to_currency {nullptr}, - m_from_commodity {nullptr}, m_load_error {false} { } + m_separators {","}, m_load_error {false}, m_base_account {nullptr}, + m_from_commodity {nullptr}, m_to_currency {nullptr} { } /** Save the gathered widget properties to a key File. * @@ -81,7 +81,9 @@ void remove (void); */ bool read_only (void); +std::string m_settings_type; // Settings Type, TRANS, PRICE etc. +// Common Settings std::string m_name; // Name given to this preset by the user GncImpFileFormat m_file_format; // CSV import Format std::string m_encoding; // File encoding @@ -92,16 +94,17 @@ uint32_t m_skip_start_lines; // Number of header rows to skip uint32_t m_skip_end_lines; // Number of footer rows to skip bool m_skip_alt_lines; // Skip alternate rows std::string m_separators; // Separators for csv format - -Account *m_base_account; // Base account -std::vector m_column_types; // The Column types in order -std::vector m_column_types_price; // The Column Price types in order +bool m_load_error; // Was there an error while parsing the state file ? std::vector m_column_widths; // The Column widths -gnc_commodity *m_to_currency; // Price To Currency -gnc_commodity *m_from_commodity; // Price From Commodity +// Transaction Settings +Account *m_base_account; // Base account +std::vector m_column_types; // The Column types in order -bool m_load_error; // Was there an error while parsing the state file ? +// Price Settings +gnc_commodity *m_from_commodity; // Price From Commodity +gnc_commodity *m_to_currency; // Price To Currency +std::vector m_column_types_price; // The Price Column types in order }; using preset_vec = std::vector>; @@ -109,9 +112,12 @@ using preset_vec = std::vector>; * - one or more internally defined presets * - all preset found in the state key file. * + * @param set_type The type of setting stored in the + * key file, TRANS, PRICE, etc. + * * @return a reference to the populated vector. */ -const preset_vec& get_trans_presets (void); +const preset_vec& get_trans_presets (const std::string& set_type); /** Check whether name can be used as a preset name. * The names of the internal presets are considered reserved. From b8bbdb2ad54e2eaf55b774b9954ab4f99775eea6 Mon Sep 17 00:00:00 2001 From: Robert Fewell <14uBobIT@gmail.com> Date: Fri, 1 Dec 2017 11:44:38 +0000 Subject: [PATCH 12/48] Minor changes and tidy up --- .../csv-imp/assistant-csv-price-import.cpp | 26 ++++++------------- 1 file changed, 8 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 e3a5ae3614..43f2d8acee 100644 --- a/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp +++ b/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp @@ -76,7 +76,7 @@ public: void assist_preview_page_prepare (); void assist_confirm_page_prepare (); void assist_summary_page_prepare (); - void assist_finish (bool canceled); + void assist_finish (); void assist_compmgr_close (); void file_confirm_cb (); @@ -213,7 +213,6 @@ csv_price_imp_assist_destroy_cb (GtkWidget *object, CsvImpPriceAssist* info) void csv_price_imp_assist_cancel_cb (GtkAssistant *assistant, CsvImpPriceAssist* info) { - info->assist_finish (true); gnc_close_gui_component_by_data (ASSISTANT_CSV_IMPORT_PRICE_CM_CLASS, info); } @@ -226,7 +225,7 @@ csv_price_imp_assist_close_cb (GtkAssistant *assistant, CsvImpPriceAssist* info) void csv_price_imp_assist_finish_cb (GtkAssistant *assistant, CsvImpPriceAssist* info) { - info->assist_finish (false); + info->assist_finish (); } void csv_price_imp_file_confirm_cb (GtkWidget *button, CsvImpPriceAssist *info) @@ -356,7 +355,7 @@ gnc_commodity *get_commodity_from_combo (GtkComboBox *combo) GtkTreeModel *model, *sort_model; GtkTreeIter iter, siter; gchar *string; - gnc_commodity *comm; + gnc_commodity *comm; if (!gtk_combo_box_get_active_iter (combo, &siter)) return nullptr; @@ -741,7 +740,6 @@ void CsvImpPriceAssist::preview_populate_settings_combo() gtk_list_store_clear (GTK_LIST_STORE(model)); // Append the default entry -//FIXME get_trans_presets ???? auto presets = get_trans_presets (settings_type); for (auto preset : presets) { @@ -773,7 +771,7 @@ void CsvImpPriceAssist::preview_handle_save_del_sensitivity (GtkComboBox* combo) CsvTransSettings *preset; GtkTreeModel *model = gtk_combo_box_get_model (combo); gtk_tree_model_get (model, &iter, SET_GROUP, &preset, -1); -//FIXME + if (preset && !trans_preset_is_reserved_name (preset->m_name)) { /* Current preset is not read_only, so buttons can be enabled */ @@ -781,7 +779,6 @@ void CsvImpPriceAssist::preview_handle_save_del_sensitivity (GtkComboBox* combo) can_save = true; } } -//FIXME else if (entry_text && (strlen (entry_text) > 0) && !trans_preset_is_reserved_name (std::string(entry_text))) can_save = true; @@ -944,6 +941,8 @@ void CsvImpPriceAssist::preview_update_skipped_rows () preview_refresh_table (); } +/* Callback triggered when user clicks on Over Write option + */ void CsvImpPriceAssist::preview_over_write (bool over) { price_imp->over_write (over); @@ -1723,6 +1722,7 @@ CsvImpPriceAssist::assist_preview_page_prepare () void CsvImpPriceAssist::assist_confirm_page_prepare () { + /* Confirm Page */ } void @@ -1752,19 +1752,9 @@ CsvImpPriceAssist::assist_prepare_cb (GtkWidget *page) } void -CsvImpPriceAssist::assist_finish (bool canceled) +CsvImpPriceAssist::assist_finish () { /* Start the import */ -//FIXME Apply button -g_print("Finish\n"); -// if (canceled || price_imp->m_transactions.empty()) -// gnc_gen_trans_list_delete (gnc_csv_importer_gui); -// else -// gnc_gen_trans_assist_start (gnc_csv_importer_gui); - - -//FIXME Cancel comes here to, check when nothing set, goes to catch below also - /* Create prices from the parsed data */ try { From 9debe91e990ba00c68a09449d6a4c22646faae66 Mon Sep 17 00:00:00 2001 From: Robert Fewell <14uBobIT@gmail.com> Date: Fri, 1 Dec 2017 11:47:12 +0000 Subject: [PATCH 13/48] Change the way commodity and currency combo's are shown. Use commodity print name to show in the combo's and use a hidden field to sort the list grouping by namespace. Also alter the way these settings are saved. --- .../csv-imp/assistant-csv-price-import.cpp | 23 ++++++++----------- .../csv-imp/gnc-csv-trans-settings.cpp | 16 ++++++++++--- 2 files changed, 23 insertions(+), 16 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 43f2d8acee..075f46389b 100644 --- a/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp +++ b/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp @@ -419,11 +419,11 @@ GtkTreeModel *get_model (bool all_commodity) store = GTK_TREE_MODEL(gtk_list_store_new (3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER)); model = gtk_tree_model_sort_new_with_model (store); - gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (model), - 0, GTK_SORT_ASCENDING); + // set sort to sort on second string, first string will be shown + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (model), 1, GTK_SORT_ASCENDING); gtk_list_store_append (GTK_LIST_STORE(store), &iter); - gtk_list_store_set (GTK_LIST_STORE(store), &iter, 0, " ", 1, nullptr, -1); + gtk_list_store_set (GTK_LIST_STORE(store), &iter, 0, " ", 1, " ", 2, nullptr, -1); namespace_list = g_list_first (namespace_list); while (namespace_list != nullptr) @@ -440,23 +440,20 @@ GtkTreeModel *get_model (bool all_commodity) commodity_list = g_list_first (commodity_list); while (commodity_list != nullptr) { - gchar *name_str; - gchar *save_str; - gchar *settings_str; + const gchar *name_str; + gchar *sort_str; tmp_commodity = (gnc_commodity*)commodity_list->data; DEBUG("Looking at commodity %s", gnc_commodity_get_fullname (tmp_commodity)); - name_str = g_strconcat (tmp_namespace, " : (", gnc_commodity_get_mnemonic (tmp_commodity), - ") ", gnc_commodity_get_fullname (tmp_commodity), nullptr); + name_str = gnc_commodity_get_printname (tmp_commodity); - settings_str = g_strconcat (tmp_namespace, "::", gnc_commodity_get_mnemonic (tmp_commodity), nullptr); - DEBUG("Name string is %s, Save string is %s", name_str, settings_str); + sort_str = g_strconcat (tmp_namespace, "::", gnc_commodity_get_mnemonic (tmp_commodity), nullptr); + DEBUG("Name string is %s, Sort string is %s", name_str, sort_str); gtk_list_store_append (GTK_LIST_STORE(store), &iter); - gtk_list_store_set (GTK_LIST_STORE(store), &iter, 0, name_str, 1, settings_str, 2, tmp_commodity, -1); + gtk_list_store_set (GTK_LIST_STORE(store), &iter, 0, name_str, 1, sort_str, 2, tmp_commodity, -1); - g_free (name_str); - g_free (settings_str); + g_free (sort_str); commodity_list = g_list_next (commodity_list); } } diff --git a/gnucash/import-export/csv-imp/gnc-csv-trans-settings.cpp b/gnucash/import-export/csv-imp/gnc-csv-trans-settings.cpp index 73ab2a59a6..3bbc9847ba 100644 --- a/gnucash/import-export/csv-imp/gnc-csv-trans-settings.cpp +++ b/gnucash/import-export/csv-imp/gnc-csv-trans-settings.cpp @@ -294,7 +294,7 @@ CsvTransSettings::load (void) if (col_types_str) g_strfreev (col_types_str); } - + // Price if (m_settings_type.compare("PRICE") == 0) { @@ -424,10 +424,20 @@ CsvTransSettings::save (void) if (m_settings_type.compare("PRICE") == 0) { if (m_to_currency) - g_key_file_set_string (keyfile, group.c_str(), CSV_TO_CURR, gnc_commodity_get_mnemonic(m_to_currency)); + { + auto unique_name = g_strconcat (gnc_commodity_get_namespace (m_to_currency), "::", + gnc_commodity_get_mnemonic (m_to_currency), nullptr); + g_key_file_set_string (keyfile, group.c_str(), CSV_TO_CURR, unique_name); + g_free (unique_name); + } if (m_from_commodity) - g_key_file_set_string (keyfile, group.c_str(), CSV_FROM_COMM, gnc_commodity_get_mnemonic(m_from_commodity)); + { + auto unique_name = g_strconcat (gnc_commodity_get_namespace (m_from_commodity), "::", + gnc_commodity_get_mnemonic (m_from_commodity), nullptr); + g_key_file_set_string (keyfile, group.c_str(), CSV_FROM_COMM, unique_name); + g_free (unique_name); + } std::vector col_types_str_price; for (auto col_type : m_column_types_price) From 339fbaa587f49c91ed329b0f3a558a4c9ecc2bbb Mon Sep 17 00:00:00 2001 From: Robert Fewell <14uBobIT@gmail.com> Date: Fri, 1 Dec 2017 11:49:28 +0000 Subject: [PATCH 14/48] Various changes to comments in source files and displayed text. --- .../csv-imp/assistant-csv-price-import.cpp | 10 +++--- .../csv-imp/assistant-csv-price-import.glade | 18 +++++++--- .../csv-imp/gnc-price-import.cpp | 35 +++++++------------ .../import-export/csv-imp/gnc-price-props.cpp | 12 ++++--- .../import-export/csv-imp/gnc-price-props.hpp | 1 + 5 files changed, 40 insertions(+), 36 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 075f46389b..14a8ffaa65 100644 --- a/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp +++ b/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp @@ -434,7 +434,7 @@ GtkTreeModel *get_model (bool all_commodity) /* Hide the template entry */ if (g_utf8_collate (tmp_namespace, "template" ) != 0) { - if ((g_utf8_collate (tmp_namespace, GNC_COMMODITY_NS_CURRENCY ) == 0) || (all_commodity == true)) + if ((g_utf8_collate (tmp_namespace, GNC_COMMODITY_NS_CURRENCY ) == 0) || (all_commodity == true)) { commodity_list = gnc_commodity_table_get_commodities (commodity_table, tmp_namespace); commodity_list = g_list_first (commodity_list); @@ -1727,10 +1727,10 @@ CsvImpPriceAssist::assist_summary_page_prepare () { auto text = std::string(""); text += _("The prices were imported from the file '") + m_file_name + "'."; - text += _("\n\nThe number of Prices added was ") + std::to_string(price_imp->m_prices_added); - text += _(", duplicated was ") + std::to_string(price_imp->m_prices_duplicated); - text += _(" and replaced was ") + std::to_string(price_imp->m_prices_replaced); - text += "."; + text += _("\n\nThere were ") + std::to_string(price_imp->m_prices_added); + text += _(" Prices added, ") + std::to_string(price_imp->m_prices_duplicated); + text += _(" duplicated and ") + std::to_string(price_imp->m_prices_replaced); + text += _(" replaced."); gtk_label_set_markup (GTK_LABEL(summary_label), text.c_str()); } diff --git a/gnucash/import-export/csv-imp/assistant-csv-price-import.glade b/gnucash/import-export/csv-imp/assistant-csv-price-import.glade index 764b1967a2..33db2669c5 100644 --- a/gnucash/import-export/csv-imp/assistant-csv-price-import.glade +++ b/gnucash/import-export/csv-imp/assistant-csv-price-import.glade @@ -43,21 +43,27 @@ + + + + + + True False This assistant will help you import Prices from a CSV file. -There is a minimum number of columns that have to be present for a successful import, for Stock prices these are Date, Amount, Symbol From and for Currency they are Date, Amount, Currency From and Currency To. +There is a minimum number of columns that have to be present for a successful import, these are Date, Amount, Commodity From and Currency To. If all entries are for the same Commodity / Currency then you can select them and then the columns will be Date and Amount. -Various options exist for specifying the delimiter as well as a fixed width option. With the fixed width option, double click on the bar above the displayed rows to set the column width. +Various options exist for specifying the delimiter as well as a fixed width option. With the fixed width option, double click on the table of rows displayed to set a column width, then right mouse to change if required. Examples are "RR.L","21/11/2016",5.345,"GBP" and "USD","2016-11-21",1.56,"GBP" There is an option for specifying the start row, end row and an option to skip alternate rows beginning from the start row which can be used if you have some header text. Also there is an option to over write existing prices for that day if required. -On the preview page you can Load and Save the settings. To save the settings, select a previously saved entry or replace the text and press the Save Settings button. +Lastly, for repeated imports the preview page has buttons to Load and Save the settings. To save the settings, tweak the settings to your preferences (optionally starting from an existing preset), then (optionally change the settings name and press the Save Settings button. Note you can't save to built-in presets. This operation is not reversable, so make sure you have a working backup. @@ -536,6 +542,7 @@ Select location and file name for the Import, then click 'OK'... True False Normally prices are not over written, select this to change that. This setting is not saved. + False True @@ -1094,8 +1101,9 @@ For example True False - Press Apply to add Prices. -Cancel to abort. + <b>Press Apply to add the Prices. +Cancel to abort.</b> + True center True diff --git a/gnucash/import-export/csv-imp/gnc-price-import.cpp b/gnucash/import-export/csv-imp/gnc-price-import.cpp index 38a3cbcb57..d5d3862ad7 100644 --- a/gnucash/import-export/csv-imp/gnc-price-import.cpp +++ b/gnucash/import-export/csv-imp/gnc-price-import.cpp @@ -62,7 +62,7 @@ const gchar* currency_format_user_price[] = {N_("Locale"), /** Constructor for GncPriceImport. - * @return Pointer to a new GncCSvParseData + * @return Pointer to a new GncCsvParseData */ GncPriceImport::GncPriceImport(GncImpFileFormat format) { @@ -142,7 +142,7 @@ bool GncPriceImport::over_write () { return m_over_write; } /** Sets a from commodity. This is the commodity all import data relates to. * When a from commodity is set, there can't be any from columns selected * in the import data. - * @param from_commodity Pointer to a commodity or NULL. + * @param from_commodity pointer to a commodity or NULL. */ void GncPriceImport::from_commodity (gnc_commodity* from_commodity) { @@ -164,7 +164,7 @@ gnc_commodity *GncPriceImport::from_commodity () { return m_settings.m_from_comm /** Sets a to currency. This is the to currency all import data relates to. * When a to currency is set, there can't be any to currency columns selected * in the import data. - * @param to_currency Pointer to a commodity or NULL. + * @param to_currency pointer to a commodity or NULL. */ void GncPriceImport::to_currency (gnc_commodity* to_currency) { @@ -221,7 +221,6 @@ int GncPriceImport::date_format () { return m_settings.m_date_format; } */ void GncPriceImport::encoding (const std::string& encoding) { - // TODO investigate if we can catch conversion errors and report them if (m_tokenizer) { @@ -309,12 +308,10 @@ void GncPriceImport::settings (const CsvTransSettings& settings) std::copy_n (settings.m_column_types_price.begin(), std::min (m_settings.m_column_types_price.size(), settings.m_column_types_price.size()), m_settings.m_column_types_price.begin()); - } bool GncPriceImport::save_settings () { - if (trans_preset_is_reserved_name (m_settings.m_name)) return true; @@ -327,7 +324,6 @@ bool GncPriceImport::save_settings () auto fwtok = dynamic_cast(m_tokenizer.get()); m_settings.m_column_widths = fwtok->get_columns(); } - return m_settings.save(); } @@ -342,7 +338,6 @@ std::string GncPriceImport::settings_name () { return m_settings.m_name; } */ void GncPriceImport::load_file (const std::string& filename) { - /* Get the raw data first and handle an error if one occurs. */ try { @@ -407,7 +402,6 @@ void GncPriceImport::tokenize (bool guessColTypes) } } - struct ErrorListPrice { public: @@ -428,7 +422,6 @@ std::string ErrorListPrice::str() return m_error.substr(0, m_error.size() - 1); } - /* Test for the required minimum number of columns selected and * the selection is consistent. * @param An ErrorListPrice object to which all found issues are added. @@ -450,7 +443,7 @@ void GncPriceImport::verify_column_selections (ErrorListPrice& error_msg) if (!check_for_column_type(GncPricePropType::TO_CURRENCY)) { if (!m_settings.m_to_currency) - error_msg.add_error( _("Please select a Currency to column or set a Currency in the Currency To field.")); + error_msg.add_error( _("Please select a 'Currency to' column or set a Currency in the 'Currency To' field.")); } /* Verify a Commodity from column is selected. @@ -458,11 +451,10 @@ void GncPriceImport::verify_column_selections (ErrorListPrice& error_msg) if (!check_for_column_type(GncPricePropType::FROM_COMMODITY)) { if (!m_settings.m_from_commodity) - error_msg.add_error( _("Please select a Commodity from column or set a Commodity in the Commodity From field.")); + error_msg.add_error( _("Please select a 'Commodity from' column or set a Commodity in the 'Commodity From' field.")); } } - /* Check whether the chosen settings can successfully parse * the import data. This will check: * - there's at least one line selected for import @@ -547,7 +539,7 @@ void GncPriceImport::create_price (std::vector::iterator& parsed_l error_message.clear(); - // Add a CURRENCY_TO property with the default currency to if no currency to column was set by the user + // Add a CURRENCY_TO property with the selected 'currency to' if no 'currency to' column was set by the user auto line_to_currency = price_props->get_to_currency(); if (!line_to_currency) { @@ -555,16 +547,16 @@ void GncPriceImport::create_price (std::vector::iterator& parsed_l price_props->set_to_currency(m_settings.m_to_currency); else { - // Oops - the user didn't select an Account column *and* we didn't get a default value either! + // Oops - the user didn't select a 'currency to' column *and* we didn't get a selected value either! // Note if you get here this suggests a bug in the code! - error_message = _("No Currency to column selected and no default Currency specified either.\n" + error_message = _("No 'Currency to' column selected and no selected Currency specified either.\n" "This should never happen. Please report this as a bug."); PINFO("User warning: %s", error_message.c_str()); throw std::invalid_argument(error_message); } } - // Add a COMMODITY_FROM property with the default commodity from if no commodity from column was set by the user + // Add a COMMODITY_FROM property with the selected 'commodity from' if no 'commodity from' column was set by the user auto line_from_commodity = price_props->get_from_commodity(); if (!line_from_commodity) { @@ -572,9 +564,9 @@ void GncPriceImport::create_price (std::vector::iterator& parsed_l price_props->set_from_commodity(m_settings.m_from_commodity); else { - // Oops - the user didn't select an Account column *and* we didn't get a default value either! + // Oops - the user didn't select a 'commodity from' column *and* we didn't get a selected value either! // Note if you get here this suggests a bug in the code! - error_message = _("No Commodity from column selected and no default Commodity specified either.\n" + error_message = _("No 'Commodity from' column selected and no selected Commodity specified either.\n" "This should never happen. Please report this as a bug."); PINFO("User warning: %s", error_message.c_str()); throw std::invalid_argument(error_message); @@ -605,7 +597,6 @@ void GncPriceImport::create_price (std::vector::iterator& parsed_l } } - /** Creates a list of prices from parsed data. The parsed data * will first be validated. If any errors are found in lines that are marked * for processing (ie not marked to skip) this function will @@ -697,11 +688,11 @@ GncPriceImport::set_column_type_price (uint32_t position, GncPricePropType type, m_settings.m_column_types_price.at (position) = type; - // If the user has set a Commodity from column, we can't have a commodity from default set + // If the user has set a 'commodity from' column, we can't have a commodity from selected if (type == GncPricePropType::FROM_COMMODITY) from_commodity (nullptr); - // If the user has set a Currency to column, we can't have a currency to default set + // If the user has set a 'currency to' column, we can't have a currency to selected if (type == GncPricePropType::TO_CURRENCY) to_currency (nullptr); diff --git a/gnucash/import-export/csv-imp/gnc-price-props.cpp b/gnucash/import-export/csv-imp/gnc-price-props.cpp index d9293e13dd..2deffb3660 100644 --- a/gnucash/import-export/csv-imp/gnc-price-props.cpp +++ b/gnucash/import-export/csv-imp/gnc-price-props.cpp @@ -160,7 +160,7 @@ time64 parse_date_price (const std::string &date_str, int format) } -/** Convert str into a GncRational using the user-specified (import) currency format. +/** Convert str into a GncNumeric using the user-specified (import) currency format. * @param str The string to be parsed * @param currency_format The currency format to use. * @return a GncNumeric @@ -200,6 +200,11 @@ GncNumeric parse_amount_price (const std::string &str, int currency_format) return GncNumeric(val); } +/** Convert comm_str into a gnc_commodity. + * @param comm_str The string to be parsed + * @return a gnc_commodity + * @exception May throw std::invalid argument if string can't be parsed properly + */ gnc_commodity* parse_commodity_price_comm (const std::string& comm_str) { if (comm_str.empty()) @@ -324,9 +329,9 @@ std::string GncImportPrice::verify_essentials (void) else if (m_amount == boost::none) return _("No amount column."); else if (m_to_currency == boost::none) - return _("No Currency to column."); + return _("No 'Currency to' column."); else if (m_from_commodity == boost::none) - return _("No Commodity from column."); + return _("No 'Commodity from' column."); else return std::string(); } @@ -417,7 +422,6 @@ Result GncImportPrice::create_price (QofBook* book, GNCPriceDB *pdb, bool over) if (perr == false) throw std::invalid_argument (_("Failed to create price from selected columns.")); -//FIXME Not sure about this, should this be a PWARN } else { diff --git a/gnucash/import-export/csv-imp/gnc-price-props.hpp b/gnucash/import-export/csv-imp/gnc-price-props.hpp index 1d44ff6ae0..9ae485323e 100644 --- a/gnucash/import-export/csv-imp/gnc-price-props.hpp +++ b/gnucash/import-export/csv-imp/gnc-price-props.hpp @@ -90,6 +90,7 @@ public: void set_date_format (int date_format) { m_date_format = date_format ;} void set_currency_format (int currency_format) { m_currency_format = currency_format ;} void reset (GncPricePropType prop_type); + std::string verify_essentials (void); Result create_price (QofBook* book, GNCPriceDB *pdb, bool over); gnc_commodity* get_from_commodity () { if (m_from_commodity) return *m_from_commodity; else return nullptr; } From b1becf3dd4548f6244ce21bf62a1dcaf34b75518 Mon Sep 17 00:00:00 2001 From: Robert Fewell <14uBobIT@gmail.com> Date: Fri, 1 Dec 2017 11:51:07 +0000 Subject: [PATCH 15/48] Add a test for from_commodity not being the same as to_currency --- gnucash/import-export/csv-imp/gnc-price-props.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/gnucash/import-export/csv-imp/gnc-price-props.cpp b/gnucash/import-export/csv-imp/gnc-price-props.cpp index 2deffb3660..0ea21f9773 100644 --- a/gnucash/import-export/csv-imp/gnc-price-props.cpp +++ b/gnucash/import-export/csv-imp/gnc-price-props.cpp @@ -269,7 +269,11 @@ void GncImportPrice::set (GncPricePropType prop_type, const std::string& value) m_from_commodity = boost::none; comm = parse_commodity_price_comm (value); // Throws if parsing fails if (comm) + { + if (m_to_currency == comm) + throw std::invalid_argument (_("'Commodity From' can not be the same as 'Currency To' column type.")); m_from_commodity = comm; + } break; case GncPricePropType::TO_CURRENCY: @@ -277,6 +281,8 @@ void GncImportPrice::set (GncPricePropType prop_type, const std::string& value) comm = parse_commodity_price_comm (value); // Throws if parsing fails if (comm) { + if (m_from_commodity == comm) + throw std::invalid_argument (_("'Currency To' can not be the same as 'Commodity From' column type.")); if (gnc_commodity_is_currency (comm) != true) throw std::invalid_argument (_("Value parsed into an invalid currency for a currency column type.")); m_to_currency = comm; @@ -332,6 +338,8 @@ std::string GncImportPrice::verify_essentials (void) return _("No 'Currency to' column."); else if (m_from_commodity == boost::none) return _("No 'Commodity from' column."); + else if (gnc_commodity_equal (*m_from_commodity, *m_to_currency)) + return _("'Commodity from' can not be the same as 'Currency to'."); else return std::string(); } From 6c11cfad852efe67aa8863685e15b9db1079036a Mon Sep 17 00:00:00 2001 From: Robert Fewell <14uBobIT@gmail.com> Date: Fri, 1 Dec 2017 11:52:48 +0000 Subject: [PATCH 16/48] Add the ability to test from_commodity and to_currency being the same. To cover all combinations we need to test across the combo's and also the table entries when appropriate columns are set. Also need to force a reparse if any of the options change. --- .../csv-imp/assistant-csv-price-import.cpp | 37 +++++++++++++- .../csv-imp/gnc-price-import.cpp | 49 ++++++++++++++----- 2 files changed, 72 insertions(+), 14 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 14a8ffaa65..21126ea5b4 100644 --- a/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp +++ b/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp @@ -94,6 +94,7 @@ public: void preview_update_currency_format (); void preview_update_currency (); void preview_update_commodity (); + void preview_reparse_col_type (GncPricePropType type); void preview_update_col_type (GtkComboBox* cbox); void preview_update_fw_columns (GtkTreeView* treeview, GdkEventButton* event); @@ -1138,6 +1139,22 @@ enum PreviewDataTableCols { PREV_COL_ERR_ICON, PREV_N_FIXED_COLS }; + +void +CsvImpPriceAssist::preview_reparse_col_type (GncPricePropType type) +{ + auto column_types = price_imp->column_types_price(); + + // look for column type and force a reparse + auto col_type = std::find (column_types.begin(), + column_types.end(), type); + if (col_type != column_types.end()) + { + price_imp->set_column_type_price (col_type -column_types.begin(), + type, true); + } +} + /** Event handler for the user selecting a new column type. When the * user selects a new column type, that column's text must be changed * to the selection, and any other columns containing that selection @@ -1157,8 +1174,26 @@ void CsvImpPriceAssist::preview_update_col_type (GtkComboBox* cbox) gtk_tree_model_get (model, &iter, COL_TYPE_ID, &new_col_type, -1); auto col_num = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT(cbox), "col-num")); + + auto column_types = price_imp->column_types_price(); + auto old_col_type = column_types.at(col_num); + price_imp->set_column_type_price (col_num, new_col_type); + // if old_col_type is TO_CURRENCY, force a reparse of commodity + if (old_col_type == GncPricePropType::TO_CURRENCY) + { + // look for a from_commodity column to reparse + preview_reparse_col_type (GncPricePropType::FROM_COMMODITY); + } + + // if old_col_type is FROM_COMMODITY, force a reparse of currency + if (old_col_type == GncPricePropType::FROM_COMMODITY) + { + // look for a to_currency column to reparse + preview_reparse_col_type (GncPricePropType::TO_CURRENCY); + } + /* Delay rebuilding our data table to avoid critical warnings due to * pending events still acting on them after this event is processed. */ @@ -1580,7 +1615,7 @@ void CsvImpPriceAssist::preview_refresh_table () auto column_types = price_imp->column_types_price(); - // look for a commodity column, clear the commdoity combo + // look for a commodity column, clear the commodity combo auto col_type_comm = std::find (column_types.begin(), column_types.end(), GncPricePropType::FROM_COMMODITY); if (col_type_comm != column_types.end()) diff --git a/gnucash/import-export/csv-imp/gnc-price-import.cpp b/gnucash/import-export/csv-imp/gnc-price-import.cpp index d5d3862ad7..44a17b913e 100644 --- a/gnucash/import-export/csv-imp/gnc-price-import.cpp +++ b/gnucash/import-export/csv-imp/gnc-price-import.cpp @@ -136,7 +136,6 @@ void GncPriceImport::over_write (bool over) { m_over_write = over; } - bool GncPriceImport::over_write () { return m_over_write; } /** Sets a from commodity. This is the commodity all import data relates to. @@ -147,18 +146,20 @@ bool GncPriceImport::over_write () { return m_over_write; } void GncPriceImport::from_commodity (gnc_commodity* from_commodity) { m_settings.m_from_commodity = from_commodity; - if (m_settings.m_from_commodity) { - auto col_type = std::find (m_settings.m_column_types_price.begin(), + auto col_type_comm = std::find (m_settings.m_column_types_price.begin(), m_settings.m_column_types_price.end(), GncPricePropType::FROM_COMMODITY); - if (col_type != m_settings.m_column_types_price.end()) - set_column_type_price (col_type -m_settings.m_column_types_price.begin(), + if (col_type_comm != m_settings.m_column_types_price.end()) + set_column_type_price (col_type_comm -m_settings.m_column_types_price.begin(), GncPricePropType::NONE); + + // force a refresh of the to_currency if the from_commodity is changed + std::vector commodities = { GncPricePropType::TO_CURRENCY }; + reset_formatted_column (commodities); } } - gnc_commodity *GncPriceImport::from_commodity () { return m_settings.m_from_commodity; } /** Sets a to currency. This is the to currency all import data relates to. @@ -169,18 +170,20 @@ gnc_commodity *GncPriceImport::from_commodity () { return m_settings.m_from_comm void GncPriceImport::to_currency (gnc_commodity* to_currency) { m_settings.m_to_currency = to_currency; - if (m_settings.m_to_currency) { - auto col_type = std::find (m_settings.m_column_types_price.begin(), + auto col_type_currency = std::find (m_settings.m_column_types_price.begin(), m_settings.m_column_types_price.end(), GncPricePropType::TO_CURRENCY); - if (col_type != m_settings.m_column_types_price.end()) - set_column_type_price (col_type -m_settings.m_column_types_price.begin(), + if (col_type_currency != m_settings.m_column_types_price.end()) + set_column_type_price (col_type_currency -m_settings.m_column_types_price.begin(), GncPricePropType::NONE); + + // force a refresh of the from_commodity if the to_currency is changed + std::vector commodities = { GncPricePropType::FROM_COMMODITY }; + reset_formatted_column (commodities); } } - gnc_commodity *GncPriceImport::to_currency () { return m_settings.m_to_currency; } void GncPriceImport::reset_formatted_column (std::vector& col_types) @@ -453,6 +456,14 @@ void GncPriceImport::verify_column_selections (ErrorListPrice& error_msg) if (!m_settings.m_from_commodity) error_msg.add_error( _("Please select a 'Commodity from' column or set a Commodity in the 'Commodity From' field.")); } + + /* Verify a 'Commodity from' does not equal 'Currency to'. + */ + if ((m_settings.m_to_currency) && (m_settings.m_from_commodity)) + { + if (gnc_commodity_equal (m_settings.m_to_currency, m_settings.m_from_commodity)) + error_msg.add_error( _("'Commodity From' can not be the same as 'Currency To'.")); + } } /* Check whether the chosen settings can successfully parse @@ -539,7 +550,7 @@ void GncPriceImport::create_price (std::vector::iterator& parsed_l error_message.clear(); - // Add a CURRENCY_TO property with the selected 'currency to' if no 'currency to' column was set by the user + // Add a TO_CURRENCY property with the selected 'currency to' if no 'currency to' column was set by the user auto line_to_currency = price_props->get_to_currency(); if (!line_to_currency) { @@ -556,7 +567,7 @@ void GncPriceImport::create_price (std::vector::iterator& parsed_l } } - // Add a COMMODITY_FROM property with the selected 'commodity from' if no 'commodity from' column was set by the user + // Add a FROM_COMMODITY property with the selected 'commodity from' if no 'commodity from' column was set by the user auto line_from_commodity = price_props->get_from_commodity(); if (!line_from_commodity) { @@ -657,6 +668,18 @@ void GncPriceImport::update_price_props (uint32_t row, uint32_t col, GncPricePro { try { + // set the from_commodity based on combo so we can test for same. + if (prop_type == GncPricePropType::TO_CURRENCY) + { + if (m_settings.m_from_commodity) + price_props->set_from_commodity (m_settings.m_from_commodity); + } + // set the to_currency based on combo so we can test for same. + if (prop_type == GncPricePropType::FROM_COMMODITY) + { + if (m_settings.m_to_currency) + price_props->set_to_currency (m_settings.m_to_currency); + } price_props->set(prop_type, value); } catch (const std::exception& e) From c1a9464511b4bc0587a9038a214be3b8390da49f Mon Sep 17 00:00:00 2001 From: Robert Fewell <14uBobIT@gmail.com> Date: Fri, 1 Dec 2017 11:56:25 +0000 Subject: [PATCH 17/48] Reorder the create price procedure. --- .../import-export/csv-imp/gnc-price-props.cpp | 44 +++++++++---------- 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/gnucash/import-export/csv-imp/gnc-price-props.cpp b/gnucash/import-export/csv-imp/gnc-price-props.cpp index 0ea21f9773..539f316851 100644 --- a/gnucash/import-export/csv-imp/gnc-price-props.cpp +++ b/gnucash/import-export/csv-imp/gnc-price-props.cpp @@ -362,32 +362,10 @@ Result GncImportPrice::create_price (QofBook* book, GNCPriceDB *pdb, bool over) bool rev = false; auto amount = *m_amount; + Result ret_val = ADDED; GNCPrice *old_price = gnc_pricedb_lookup_day (pdb, *m_from_commodity, *m_to_currency, date); - if (gnc_commodity_is_currency (*m_from_commodity)) // Currency Import - { - // Check for currency in reverse direction. - if (old_price != nullptr) - { - // Check for price in reverse direction. - if (gnc_commodity_equiv (gnc_price_get_currency (old_price), *m_from_commodity)) - rev = true; - - DEBUG("Commodity from is a Currency"); - } - - // Check for price less than 1, reverse if so. - if (*m_amount < GncNumeric(1,1)) - rev = true; - - } - DEBUG("Date is %s, Rev is %d, Commodity from is '%s', Currency is '%s', Amount is %s", gnc_print_date (date), - rev, gnc_commodity_get_fullname (*m_from_commodity), gnc_commodity_get_fullname (*m_to_currency), - amount.to_string().c_str()); - - Result ret_val = ADDED; - // Should old price be over writen if ((old_price != nullptr) && (over == true)) { @@ -398,6 +376,26 @@ Result GncImportPrice::create_price (QofBook* book, GNCPriceDB *pdb, bool over) ret_val = REPLACED; } + if (gnc_commodity_is_currency (*m_from_commodity)) // Currency Import + { + // Check for currency in reverse direction. + if (old_price != nullptr) + { + // Check for price in reverse direction. + if (gnc_commodity_equiv (gnc_price_get_currency (old_price), *m_from_commodity)) + rev = true; + } + DEBUG("Commodity from is a Currency"); + + // Check for price less than 1, reverse if so. + if (*m_amount < GncNumeric(1,1)) + rev = true; + + } + DEBUG("Date is %s, Rev is %d, Commodity from is '%s', Currency is '%s', Amount is %s", gnc_print_date (date), + rev, gnc_commodity_get_fullname (*m_from_commodity), gnc_commodity_get_fullname (*m_to_currency), + amount.to_string().c_str()); + // Create the new price if (old_price == nullptr) { From c3b54ab05493a04621d1068dce57cc92daff5645 Mon Sep 17 00:00:00 2001 From: Robert Fewell <14uBobIT@gmail.com> Date: Fri, 1 Dec 2017 12:03:14 +0000 Subject: [PATCH 18/48] Rename gnc-csv-trans-settings.* to gnc-csv-import-settings.* Change the name of the import settings files as they do not just deal with transactions and all associated required changes. --- gnucash/import-export/csv-imp/CMakeLists.txt | 4 +-- gnucash/import-export/csv-imp/Makefile.am | 4 +-- .../csv-imp/assistant-csv-price-import.cpp | 16 ++++----- .../csv-imp/assistant-csv-trans-import.cpp | 16 ++++----- ...ttings.cpp => gnc-csv-import-settings.cpp} | 34 +++++++++---------- ...ttings.hpp => gnc-csv-import-settings.hpp} | 20 +++++------ .../csv-imp/gnc-price-import.cpp | 6 ++-- .../csv-imp/gnc-price-import.hpp | 6 ++-- .../import-export/csv-imp/gnc-tx-import.cpp | 6 ++-- .../import-export/csv-imp/gnc-tx-import.hpp | 6 ++-- 10 files changed, 59 insertions(+), 59 deletions(-) rename gnucash/import-export/csv-imp/{gnc-csv-trans-settings.cpp => gnc-csv-import-settings.cpp} (95%) rename gnucash/import-export/csv-imp/{gnc-csv-trans-settings.hpp => gnc-csv-import-settings.hpp} (90%) diff --git a/gnucash/import-export/csv-imp/CMakeLists.txt b/gnucash/import-export/csv-imp/CMakeLists.txt index 44a14cacec..65082be1c7 100644 --- a/gnucash/import-export/csv-imp/CMakeLists.txt +++ b/gnucash/import-export/csv-imp/CMakeLists.txt @@ -17,7 +17,7 @@ SET(csv_import_SOURCES gnc-csv-account-map.c gnc-csv-gnumeric-popup.c gnc-csv-tokenizer.cpp - gnc-csv-trans-settings.cpp + gnc-csv-import-settings.cpp gnc-dummy-tokenizer.cpp gnc-fw-tokenizer.cpp gnc-price-import.cpp @@ -45,7 +45,7 @@ SET(csv_import_noinst_HEADERS gnc-csv-account-map.h gnc-csv-gnumeric-popup.h gnc-csv-tokenizer.hpp - gnc-csv-trans-settings.hpp + gnc-csv-import-settings.hpp gnc-dummy-tokenizer.hpp gnc-fw-tokenizer.hpp gnc-price-import.hpp diff --git a/gnucash/import-export/csv-imp/Makefile.am b/gnucash/import-export/csv-imp/Makefile.am index 4b473de869..f1b501f7ea 100644 --- a/gnucash/import-export/csv-imp/Makefile.am +++ b/gnucash/import-export/csv-imp/Makefile.am @@ -19,7 +19,7 @@ libgncmod_csv_import_la_SOURCES = \ gnc-tokenizer.cpp \ gnc-tx-import.cpp \ gnc-trans-props.cpp \ - gnc-csv-trans-settings.cpp + gnc-csv-import-settings.cpp noinst_HEADERS = \ assistant-csv-account-import.h \ @@ -37,7 +37,7 @@ noinst_HEADERS = \ gnc-tokenizer.hpp \ gnc-tx-import.hpp \ gnc-trans-props.hpp \ - gnc-csv-trans-settings.hpp + gnc-csv-import-settings.hpp libgncmod_csv_import_la_LDFLAGS = -avoid-version 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 21126ea5b4..0fa6273d4b 100644 --- a/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp +++ b/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp @@ -52,7 +52,7 @@ extern "C" #include "go-charmap-sel.h" } -#include "gnc-csv-trans-settings.hpp" +#include "gnc-csv-import-settings.hpp" #include "gnc-price-import.hpp" #include "gnc-fw-tokenizer.hpp" #include "gnc-csv-tokenizer.hpp" @@ -738,7 +738,7 @@ void CsvImpPriceAssist::preview_populate_settings_combo() gtk_list_store_clear (GTK_LIST_STORE(model)); // Append the default entry - auto presets = get_trans_presets (settings_type); + auto presets = get_import_presets (settings_type); for (auto preset : presets) { GtkTreeIter iter; @@ -766,11 +766,11 @@ void CsvImpPriceAssist::preview_handle_save_del_sensitivity (GtkComboBox* combo) /* Handle sensitivity of the delete and save button */ if (gtk_combo_box_get_active_iter (combo, &iter)) { - CsvTransSettings *preset; + CsvImportSettings *preset; GtkTreeModel *model = gtk_combo_box_get_model (combo); gtk_tree_model_get (model, &iter, SET_GROUP, &preset, -1); - if (preset && !trans_preset_is_reserved_name (preset->m_name)) + if (preset && !preset_is_reserved_name (preset->m_name)) { /* Current preset is not read_only, so buttons can be enabled */ can_delete = true; @@ -778,7 +778,7 @@ void CsvImpPriceAssist::preview_handle_save_del_sensitivity (GtkComboBox* combo) } } else if (entry_text && (strlen (entry_text) > 0) && - !trans_preset_is_reserved_name (std::string(entry_text))) + !preset_is_reserved_name (std::string(entry_text))) can_save = true; gtk_widget_set_sensitive (save_button, can_save); @@ -807,7 +807,7 @@ CsvImpPriceAssist::preview_settings_load () if (!gtk_combo_box_get_active_iter (settings_combo, &iter)) return; - CsvTransSettings *preset = nullptr; + CsvImportSettings *preset = nullptr; auto model = gtk_combo_box_get_model (settings_combo); gtk_tree_model_get (model, &iter, SET_GROUP, &preset, -1); @@ -834,7 +834,7 @@ CsvImpPriceAssist::preview_settings_delete () if (!gtk_combo_box_get_active_iter (settings_combo, &iter)) return; - CsvTransSettings *preset = nullptr; + CsvImportSettings *preset = nullptr; auto model = gtk_combo_box_get_model (settings_combo); gtk_tree_model_get (model, &iter, SET_GROUP, &preset, -1); @@ -868,7 +868,7 @@ CsvImpPriceAssist::preview_settings_save () while (valid) { // Walk through the list, reading each row - CsvTransSettings *preset; + CsvImportSettings *preset; gtk_tree_model_get (model, &iter, SET_GROUP, &preset, -1); if (preset && (preset->m_name == std::string(new_name))) 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 e3d10ccd7b..3512aba4be 100644 --- a/gnucash/import-export/csv-imp/assistant-csv-trans-import.cpp +++ b/gnucash/import-export/csv-imp/assistant-csv-trans-import.cpp @@ -58,7 +58,7 @@ extern "C" #include "go-charmap-sel.h" } -#include "gnc-csv-trans-settings.hpp" +#include "gnc-csv-import-settings.hpp" #include "gnc-tx-import.hpp" #include "gnc-fw-tokenizer.hpp" #include "gnc-csv-tokenizer.hpp" @@ -679,7 +679,7 @@ void CsvImpTransAssist::preview_populate_settings_combo() // Append the default entry - auto presets = get_trans_presets (settings_type); + auto presets = get_import_presets (settings_type); for (auto preset : presets) { GtkTreeIter iter; @@ -707,11 +707,11 @@ void CsvImpTransAssist::preview_handle_save_del_sensitivity (GtkComboBox* combo) /* Handle sensitivity of the delete and save button */ if (gtk_combo_box_get_active_iter (combo, &iter)) { - CsvTransSettings *preset; + CsvImportSettings *preset; GtkTreeModel *model = gtk_combo_box_get_model (combo); gtk_tree_model_get (model, &iter, SET_GROUP, &preset, -1); - if (preset && !trans_preset_is_reserved_name (preset->m_name)) + if (preset && !preset_is_reserved_name (preset->m_name)) { /* Current preset is not read_only, so buttons can be enabled */ can_delete = true; @@ -719,7 +719,7 @@ void CsvImpTransAssist::preview_handle_save_del_sensitivity (GtkComboBox* combo) } } else if (entry_text && (strlen (entry_text) > 0) && - !trans_preset_is_reserved_name (std::string(entry_text))) + !preset_is_reserved_name (std::string(entry_text))) can_save = true; gtk_widget_set_sensitive (save_button, can_save); @@ -752,7 +752,7 @@ CsvImpTransAssist::preview_settings_load () if (!gtk_combo_box_get_active_iter (settings_combo, &iter)) return; - CsvTransSettings *preset = nullptr; + CsvImportSettings *preset = nullptr; auto model = gtk_combo_box_get_model (settings_combo); gtk_tree_model_get (model, &iter, SET_GROUP, &preset, -1); @@ -779,7 +779,7 @@ CsvImpTransAssist::preview_settings_delete () if (!gtk_combo_box_get_active_iter (settings_combo, &iter)) return; - CsvTransSettings *preset = nullptr; + CsvImportSettings *preset = nullptr; auto model = gtk_combo_box_get_model (settings_combo); gtk_tree_model_get (model, &iter, SET_GROUP, &preset, -1); @@ -812,7 +812,7 @@ CsvImpTransAssist::preview_settings_save () while (valid) { // Walk through the list, reading each row - CsvTransSettings *preset; + CsvImportSettings *preset; gtk_tree_model_get (model, &iter, SET_GROUP, &preset, -1); if (preset && (preset->m_name == std::string(new_name))) diff --git a/gnucash/import-export/csv-imp/gnc-csv-trans-settings.cpp b/gnucash/import-export/csv-imp/gnc-csv-import-settings.cpp similarity index 95% rename from gnucash/import-export/csv-imp/gnc-csv-trans-settings.cpp rename to gnucash/import-export/csv-imp/gnc-csv-import-settings.cpp index 3bbc9847ba..0096c5397e 100644 --- a/gnucash/import-export/csv-imp/gnc-csv-trans-settings.cpp +++ b/gnucash/import-export/csv-imp/gnc-csv-import-settings.cpp @@ -1,5 +1,5 @@ /*******************************************************************\ - * gnc-csv-trans-settings.c -- Save and Load CSV Import Settings * + * gnc-csv-import-settings.c -- Save and Load CSV Import Settings * * * * Copyright (C) 2014 Robert Fewell * * * @@ -20,13 +20,13 @@ * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * * Boston, MA 02110-1301, USA gnu@gnu.org * \********************************************************************/ -/** @file gnc-csv-trans-settings.c +/** @file gnc-csv-import-settings.c @brief CSV Import Settings @author Copyright (c) 2014 Robert Fewell @author Copyright (c) 2016 Geert Janssens */ -#include "gnc-csv-trans-settings.hpp" +#include "gnc-csv-import-settings.hpp" #include extern "C" @@ -70,18 +70,18 @@ G_GNUC_UNUSED static QofLogModule log_module = GNC_MOD_IMPORT; preset_vec presets; -static std::shared_ptr create_int_no_preset(const std::string& set_type) +static std::shared_ptr create_int_no_preset(const std::string& set_type) { - auto preset = std::make_shared(); + auto preset = std::make_shared(); preset->m_name = no_settings; preset->m_settings_type = set_type; return preset; } -static std::shared_ptr create_int_gnc_exp_preset(void) +static std::shared_ptr create_int_gnc_exp_preset(void) { - auto preset = std::make_shared(); + auto preset = std::make_shared(); preset->m_name = gnc_exp; preset->m_skip_start_lines = 1; preset->m_multi_split = true; @@ -119,7 +119,7 @@ static std::shared_ptr create_int_gnc_exp_preset(void) * find all settings entries in the state key file * based on settings type. **************************************************/ -const preset_vec& get_trans_presets (const std::string& set_type) +const preset_vec& get_import_presets (const std::string& set_type) { // Search all Groups in the state key file for ones starting with prefix @@ -157,7 +157,7 @@ const preset_vec& get_trans_presets (const std::string& set_type) /* Then add all the ones we found in the state file */ for (auto preset_name : preset_names) { - auto preset = std::make_shared(); + auto preset = std::make_shared(); preset->m_settings_type = set_type; preset->m_name = preset_name; preset->load(); @@ -166,7 +166,7 @@ const preset_vec& get_trans_presets (const std::string& set_type) return presets; } -bool trans_preset_is_reserved_name (const std::string& name) +bool preset_is_reserved_name (const std::string& name) { return ((name == no_settings) || (name == _(no_settings.c_str())) || @@ -205,9 +205,9 @@ handle_load_error (GError **key_error, const std::string& group) * load the settings from a state key file **************************************************/ bool -CsvTransSettings::load (void) +CsvImportSettings::load (void) { - if (trans_preset_is_reserved_name (m_name)) + if (preset_is_reserved_name (m_name)) return true; GError *key_error = nullptr; @@ -355,9 +355,9 @@ CsvTransSettings::load (void) * save settings to a key file **************************************************/ bool -CsvTransSettings::save (void) +CsvImportSettings::save (void) { - if (trans_preset_is_reserved_name (m_name)) + if (preset_is_reserved_name (m_name)) { PWARN ("Ignoring attempt to save to reserved name '%s'", m_name.c_str()); return true; @@ -471,9 +471,9 @@ CsvTransSettings::save (void) } void -CsvTransSettings::remove (void) +CsvImportSettings::remove (void) { - if (trans_preset_is_reserved_name (m_name)) + if (preset_is_reserved_name (m_name)) return; auto keyfile = gnc_state_get_current (); @@ -483,7 +483,7 @@ CsvTransSettings::remove (void) bool -CsvTransSettings::read_only (void) +CsvImportSettings::read_only (void) { return ((m_name == no_settings) || (m_name == _(no_settings.c_str())) || diff --git a/gnucash/import-export/csv-imp/gnc-csv-trans-settings.hpp b/gnucash/import-export/csv-imp/gnc-csv-import-settings.hpp similarity index 90% rename from gnucash/import-export/csv-imp/gnc-csv-trans-settings.hpp rename to gnucash/import-export/csv-imp/gnc-csv-import-settings.hpp index 7df293c546..e6ce4d4f22 100644 --- a/gnucash/import-export/csv-imp/gnc-csv-trans-settings.hpp +++ b/gnucash/import-export/csv-imp/gnc-csv-import-settings.hpp @@ -1,5 +1,5 @@ /*******************************************************************\ - * gnc-csv-trans-settings.h -- Save and Load CSV Import Settings * + * gnc-csv-import-settings.h -- Save and Load CSV Import Settings * * * * Copyright (C) 2014 Robert Fewell * * * @@ -20,13 +20,13 @@ * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * * Boston, MA 02110-1301, USA gnu@gnu.org * \********************************************************************/ -/** @file gnc-csv-trans-settings.h +/** @file gnc-csv-import-settings.h @brief CSV Import Settings @author Copyright (c) 2014 Robert Fewell @author Copyright (c) 2016 Geert Janssens */ -#ifndef GNC_CSV_TRANS_SETTINGS_H -#define GNC_CSV_TRANS_SETTINGS_H +#ifndef GNC_CSV_IMPORT_SETTINGS_H +#define GNC_CSV_IMPORT_SETTINGS_H extern "C" { #include @@ -49,9 +49,9 @@ enum SEP_BUTTON_TYPES {SEP_SPACE, SEP_TAB, SEP_COMMA, SEP_COLON, SEP_SEMICOLON, /** Enumeration for the settings combo's */ enum SETTINGS_COL {SET_GROUP, SET_NAME}; -struct CsvTransSettings +struct CsvImportSettings { - CsvTransSettings() : m_file_format (GncImpFileFormat::CSV), m_encoding {"UTF-8"}, + CsvImportSettings() : m_file_format (GncImpFileFormat::CSV), m_encoding {"UTF-8"}, m_multi_split (false), m_date_format {0}, m_currency_format {0}, m_skip_start_lines{0}, m_skip_end_lines{0}, m_skip_alt_lines (false), m_separators {","}, m_load_error {false}, m_base_account {nullptr}, @@ -107,8 +107,8 @@ gnc_commodity *m_to_currency; // Price To Currency std::vector m_column_types_price; // The Price Column types in order }; -using preset_vec = std::vector>; -/** Creates a vector of CsvTransSettings which combines +using preset_vec = std::vector>; +/** Creates a vector of CsvImportSettings which combines * - one or more internally defined presets * - all preset found in the state key file. * @@ -117,12 +117,12 @@ using preset_vec = std::vector>; * * @return a reference to the populated vector. */ -const preset_vec& get_trans_presets (const std::string& set_type); +const preset_vec& get_import_presets (const std::string& set_type); /** Check whether name can be used as a preset name. * The names of the internal presets are considered reserved. * A preset with such a name should not be saved or deleted. */ -bool trans_preset_is_reserved_name (const std::string& name); +bool preset_is_reserved_name (const std::string& name); #endif diff --git a/gnucash/import-export/csv-imp/gnc-price-import.cpp b/gnucash/import-export/csv-imp/gnc-price-import.cpp index 44a17b913e..838c9473cb 100644 --- a/gnucash/import-export/csv-imp/gnc-price-import.cpp +++ b/gnucash/import-export/csv-imp/gnc-price-import.cpp @@ -42,7 +42,7 @@ extern "C" { #include "gnc-price-props.hpp" #include "gnc-csv-tokenizer.hpp" #include "gnc-fw-tokenizer.hpp" -#include "gnc-csv-trans-settings.hpp" +#include "gnc-csv-import-settings.hpp" G_GNUC_UNUSED static QofLogModule log_module = GNC_MOD_IMPORT; @@ -281,7 +281,7 @@ void GncPriceImport::separators (std::string separators) } std::string GncPriceImport::separators () { return m_settings.m_separators; } -void GncPriceImport::settings (const CsvTransSettings& settings) +void GncPriceImport::settings (const CsvImportSettings& settings) { /* First apply file format as this may recreate the tokenizer */ file_format (settings.m_file_format); @@ -315,7 +315,7 @@ void GncPriceImport::settings (const CsvTransSettings& settings) bool GncPriceImport::save_settings () { - if (trans_preset_is_reserved_name (m_settings.m_name)) + if (preset_is_reserved_name (m_settings.m_name)) return true; /* separators are already copied to m_settings in the separators diff --git a/gnucash/import-export/csv-imp/gnc-price-import.hpp b/gnucash/import-export/csv-imp/gnc-price-import.hpp index be91941a86..9959aaaa51 100644 --- a/gnucash/import-export/csv-imp/gnc-price-import.hpp +++ b/gnucash/import-export/csv-imp/gnc-price-import.hpp @@ -42,7 +42,7 @@ extern "C" { #include "gnc-tokenizer.hpp" #include "gnc-price-props.hpp" -#include "gnc-csv-trans-settings.hpp" +#include "gnc-csv-import-settings.hpp" #include /* A set of currency formats that the user sees. */ @@ -110,7 +110,7 @@ public: void separators (std::string separators); std::string separators (); - void settings (const CsvTransSettings& settings); + void settings (const CsvImportSettings& settings); bool save_settings (); void settings_name (std::string name); @@ -157,7 +157,7 @@ private: void update_price_props (uint32_t row, uint32_t col, GncPricePropType prop_type); struct CsvTranSettings; - CsvTransSettings m_settings; + CsvImportSettings m_settings; bool m_skip_errors; bool m_over_write; }; diff --git a/gnucash/import-export/csv-imp/gnc-tx-import.cpp b/gnucash/import-export/csv-imp/gnc-tx-import.cpp index daa85b4d98..eddbd753ef 100644 --- a/gnucash/import-export/csv-imp/gnc-tx-import.cpp +++ b/gnucash/import-export/csv-imp/gnc-tx-import.cpp @@ -39,7 +39,7 @@ extern "C" { #include "gnc-trans-props.hpp" #include "gnc-csv-tokenizer.hpp" #include "gnc-fw-tokenizer.hpp" -#include "gnc-csv-trans-settings.hpp" +#include "gnc-csv-import-settings.hpp" G_GNUC_UNUSED static QofLogModule log_module = GNC_MOD_IMPORT; @@ -293,7 +293,7 @@ void GncTxImport::separators (std::string separators) } std::string GncTxImport::separators () { return m_settings.m_separators; } -void GncTxImport::settings (const CsvTransSettings& settings) +void GncTxImport::settings (const CsvImportSettings& settings) { /* First apply file format as this may recreate the tokenizer */ file_format (settings.m_file_format); @@ -329,7 +329,7 @@ void GncTxImport::settings (const CsvTransSettings& settings) bool GncTxImport::save_settings () { - if (trans_preset_is_reserved_name (m_settings.m_name)) + if (preset_is_reserved_name (m_settings.m_name)) return true; /* separators are already copied to m_settings in the separators diff --git a/gnucash/import-export/csv-imp/gnc-tx-import.hpp b/gnucash/import-export/csv-imp/gnc-tx-import.hpp index 0c14a7489d..87e4ea4cd1 100644 --- a/gnucash/import-export/csv-imp/gnc-tx-import.hpp +++ b/gnucash/import-export/csv-imp/gnc-tx-import.hpp @@ -43,7 +43,7 @@ extern "C" { #include "gnc-tokenizer.hpp" #include "gnc-trans-props.hpp" -#include "gnc-csv-trans-settings.hpp" +#include "gnc-csv-import-settings.hpp" #include @@ -136,7 +136,7 @@ public: void separators (std::string separators); std::string separators (); - void settings (const CsvTransSettings& settings); + void settings (const CsvImportSettings& settings); bool save_settings (); void settings_name (std::string name); @@ -190,7 +190,7 @@ private: void update_pre_split_props (uint32_t row, uint32_t col, GncTransPropType prop_type); struct CsvTranSettings; - CsvTransSettings m_settings; + CsvImportSettings m_settings; bool m_skip_errors; bool m_req_mapped_accts; From b94b2f8ac25926f5afdc6988f952b52b998d31a8 Mon Sep 17 00:00:00 2001 From: Robert Fewell <14uBobIT@gmail.com> Date: Fri, 1 Dec 2017 12:04:51 +0000 Subject: [PATCH 19/48] Pot file changes for new files and settings rename --- po/POTFILES.in | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/po/POTFILES.in b/po/POTFILES.in index bced61f5a2..7f74af4d66 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -287,17 +287,21 @@ gnucash/import-export/csv-exp/gnc-plugin-csv-export.c [type: gettext/gsettings]gnucash/import-export/csv-exp/gschemas/org.gnucash.dialogs.export.csv.gschema.xml.in.in gnucash/import-export/csv-imp/assistant-csv-account-import.c gnucash/import-export/csv-imp/assistant-csv-account-import.glade +gnucash/import-export/csv-imp/assistant-csv-price-import.cpp +gnucash/import-export/csv-imp/assistant-csv-price-import.glade gnucash/import-export/csv-imp/assistant-csv-trans-import.cpp gnucash/import-export/csv-imp/assistant-csv-trans-import.glade gnucash/import-export/csv-imp/csv-account-import.c gnucash/import-export/csv-imp/gnc-csv-account-map.c gnucash/import-export/csv-imp/gnc-csv-gnumeric-popup.c gnucash/import-export/csv-imp/gnc-csv-tokenizer.cpp -gnucash/import-export/csv-imp/gnc-csv-trans-settings.cpp +gnucash/import-export/csv-imp/gnc-csv-import-settings.cpp gnucash/import-export/csv-imp/gnc-dummy-tokenizer.cpp gnucash/import-export/csv-imp/gnc-fw-tokenizer.cpp gnucash/import-export/csv-imp/gncmod-csv-import.c gnucash/import-export/csv-imp/gnc-plugin-csv-import.c +gnucash/import-export/csv-imp/gnc-price-import.cpp +gnucash/import-export/csv-imp/gnc-price-props.cpp gnucash/import-export/csv-imp/gnc-tokenizer.cpp gnucash/import-export/csv-imp/gnc-trans-props.cpp gnucash/import-export/csv-imp/gnc-tx-import.cpp From 5b02021550dd71dd3de643a387b4c3034e6758a7 Mon Sep 17 00:00:00 2001 From: Robert Fewell <14uBobIT@gmail.com> Date: Mon, 4 Dec 2017 14:25:02 +0000 Subject: [PATCH 20/48] Make changes for Gtk3 compatibility --- .../csv-imp/assistant-csv-price-import.cpp | 24 +- .../csv-imp/assistant-csv-price-import.glade | 261 ++++++++---------- .../csv-imp/gnc-plugin-csv-import.c | 2 +- 3 files changed, 131 insertions(+), 156 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 0fa6273d4b..af810cb2b2 100644 --- a/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp +++ b/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp @@ -503,10 +503,12 @@ CsvImpPriceAssist::CsvImpPriceAssist () file_chooser = gtk_file_chooser_widget_new (GTK_FILE_CHOOSER_ACTION_OPEN); g_signal_connect (G_OBJECT(file_chooser), "file-activated", G_CALLBACK(csv_price_imp_file_confirm_cb), this); - auto button = gtk_button_new_from_stock (GTK_STOCK_OK); + auto button = gtk_button_new_with_label (_("OK")); gtk_widget_set_size_request (button, 100, -1); gtk_widget_show (button); - auto h_box = gtk_hbox_new (TRUE, 0); + auto h_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); + gtk_box_set_homogeneous (GTK_BOX (h_box), TRUE); + gtk_widget_set_hexpand (GTK_WIDGET(h_box), TRUE); gtk_box_pack_start (GTK_BOX(h_box), button, FALSE, FALSE, 0); gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER(file_chooser), h_box); g_signal_connect (G_OBJECT(button), "clicked", @@ -792,7 +794,9 @@ CsvImpPriceAssist::preview_settings_name (GtkEntry* entry) if (text) price_imp->settings_name(text); - auto combo = gtk_widget_get_parent (GTK_WIDGET(entry)); + auto box = gtk_widget_get_parent (GTK_WIDGET(entry)); + auto combo = gtk_widget_get_parent (GTK_WIDGET(box)); + preview_handle_save_del_sensitivity (GTK_COMBO_BOX(combo)); } @@ -1239,11 +1243,11 @@ enum static GnumericPopupMenuElement const popup_elements[] = { { - N_("Merge with column on _left"), GTK_STOCK_REMOVE, + N_("Merge with column on _left"), "list-remove", 0, 1 << CONTEXT_STF_IMPORT_MERGE_LEFT, CONTEXT_STF_IMPORT_MERGE_LEFT }, { - N_("Merge with column on _right"), GTK_STOCK_REMOVE, + N_("Merge with column on _right"), "list-remove", 0, 1 << CONTEXT_STF_IMPORT_MERGE_RIGHT, CONTEXT_STF_IMPORT_MERGE_RIGHT }, { "", nullptr, 0, 0, 0 }, @@ -1253,11 +1257,11 @@ static GnumericPopupMenuElement const popup_elements[] = }, { "", nullptr, 0, 0, 0 }, { - N_("_Widen this column"), GTK_STOCK_GO_FORWARD, + N_("_Widen this column"), "go-next", 0, 1 << CONTEXT_STF_IMPORT_WIDEN, CONTEXT_STF_IMPORT_WIDEN }, { - N_("_Narrow this column"), GTK_STOCK_GO_BACK, + N_("_Narrow this column"), "go-previous", 0, 1 << CONTEXT_STF_IMPORT_NARROW, CONTEXT_STF_IMPORT_NARROW }, { nullptr, nullptr, 0, 0, 0 }, @@ -1429,7 +1433,7 @@ CsvImpPriceAssist::preview_row_fill_state_cells (GtkListStore *store, GtkTreeIte fcolor = "black"; bcolor = "pink"; c_err_msg = err_msg.c_str(); - icon_name = GTK_STOCK_DIALOG_ERROR; + icon_name = "dialog-error"; } gtk_list_store_set (store, iter, PREV_COL_FCOLOR, fcolor, @@ -1480,13 +1484,13 @@ void CsvImpPriceAssist::preview_style_column (uint32_t col_num, GtkTreeModel* model) { auto col = gtk_tree_view_get_column (treeview, col_num); - auto renderer = static_cast(gtk_tree_view_column_get_cell_renderers(col)->data); + auto renderer = static_cast(gtk_cell_layout_get_cells(GTK_CELL_LAYOUT(col))->data); /* First column -the error status column- is rendered differently */ if (col_num == 0) { gtk_tree_view_column_set_attributes (col, renderer, - "stock-id", PREV_COL_ERR_ICON, + "icon-name", PREV_COL_ERR_ICON, "cell-background", PREV_COL_BCOLOR, nullptr); g_object_set (G_OBJECT(renderer), "stock-size", GTK_ICON_SIZE_MENU, nullptr); g_object_set (G_OBJECT(col), "sizing", GTK_TREE_VIEW_COLUMN_FIXED, diff --git a/gnucash/import-export/csv-imp/assistant-csv-price-import.glade b/gnucash/import-export/csv-imp/assistant-csv-price-import.glade index 33db2669c5..b1ba9c0a8c 100644 --- a/gnucash/import-export/csv-imp/assistant-csv-price-import.glade +++ b/gnucash/import-export/csv-imp/assistant-csv-price-import.glade @@ -1,7 +1,7 @@ + - - + 1000 1 @@ -38,11 +38,11 @@ CSV Price Import 400 500 + + - - @@ -76,10 +76,11 @@ Click on 'Forward' to proceed or 'Cancel' to Abort Import. - + True False 12 + vertical True @@ -101,20 +102,19 @@ Select location and file name for the Import, then click 'OK'... - + True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 12 + vertical 2 - + True False - 2 - 2 - 5 5 + 5 True @@ -129,7 +129,7 @@ Select location and file name for the Import, then click 'OK'... 5 5 - + True False @@ -143,7 +143,7 @@ Select location and file name for the Import, then click 'OK'... True False - gtk-delete + edit-delete @@ -165,7 +165,7 @@ Select location and file name for the Import, then click 'OK'... True False - gtk-save + document-save @@ -184,6 +184,7 @@ Select location and file name for the Import, then click 'OK'... True False + start <b>Load and Save Settings</b> True False @@ -191,8 +192,8 @@ Select location and file name for the Import, then click 'OK'... - GTK_FILL - GTK_FILL + 0 + 0 @@ -210,15 +211,14 @@ Select location and file name for the Import, then click 'OK'... 5 5 - + True False + vertical - + True False - 2 - 2 Separators @@ -226,10 +226,15 @@ Select location and file name for the Import, then click 'OK'... True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + start True True + + 0 + 0 + @@ -238,24 +243,24 @@ Select location and file name for the Import, then click 'OK'... True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + start True csv_button 1 - 2 + 0 - + True False - 2 + 0 1 - 2 - GTK_FILL + 2 @@ -266,12 +271,10 @@ Select location and file name for the Import, then click 'OK'... - + True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - 3 - 3 3 @@ -280,12 +283,13 @@ Select location and file name for the Import, then click 'OK'... True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + start True - GTK_FILL - GTK_FILL + 0 + 0 @@ -295,14 +299,13 @@ Select location and file name for the Import, then click 'OK'... True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + start True 1 - 2 - GTK_FILL - GTK_FILL + 0 @@ -312,15 +315,14 @@ Select location and file name for the Import, then click 'OK'... True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + start True True 2 - 3 - GTK_FILL - GTK_FILL + 0 @@ -330,14 +332,13 @@ Select location and file name for the Import, then click 'OK'... True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + start True + 2 1 - 2 - GTK_FILL - GTK_FILL @@ -347,16 +348,13 @@ Select location and file name for the Import, then click 'OK'... True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + start True - 1 - 2 + 0 1 - 2 - GTK_FILL - GTK_FILL @@ -366,16 +364,13 @@ Select location and file name for the Import, then click 'OK'... True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + start True - 2 - 3 + 1 1 - 2 - GTK_FILL - GTK_FILL @@ -385,14 +380,13 @@ Select location and file name for the Import, then click 'OK'... True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + start True + 0 2 - 3 - GTK_FILL - GTK_FILL @@ -401,22 +395,18 @@ Select location and file name for the Import, then click 'OK'... True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - True False False - True - True 1 - 3 2 - 3 - GTK_FILL - GTK_FILL + + + False @@ -425,7 +415,7 @@ Select location and file name for the Import, then click 'OK'... - + False True @@ -433,8 +423,7 @@ Select location and file name for the Import, then click 'OK'... True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - 0 - gtk-dialog-info + dialog-information False @@ -444,67 +433,61 @@ Select location and file name for the Import, then click 'OK'... - + True False - 2 - 2 True False - 0 - 5 True True + + 0 + 0 + True False - 0 + start Double-click anywhere on the table below to insert a column break True True 1 - 2 - + 0 True False - 0 - 5 True True + 0 1 - 2 True False - 0 + start Right-click anywhere in a column to modify it (widen, narrow, merge) True True 1 - 2 1 - 2 - @@ -522,17 +505,17 @@ Select location and file name for the Import, then click 'OK'... - + True False - 2 - + True False - GTK_FILL + 0 + 0 @@ -540,15 +523,15 @@ Select location and file name for the Import, then click 'OK'... Allow existing prices to be over written. True True + False False Normally prices are not over written, select this to change that. This setting is not saved. - False True + 0 1 - 2 @@ -567,16 +550,15 @@ Select location and file name for the Import, then click 'OK'... True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + start <b>File Format</b> True + 0 1 - 2 - GTK_FILL - GTK_FILL @@ -592,18 +574,17 @@ Select location and file name for the Import, then click 'OK'... 5 5 - + True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + vertical - + True False - 6 - 2 - 5 5 + 5 True @@ -613,23 +594,19 @@ Select location and file name for the Import, then click 'OK'... 1 - 2 1 - 2 - GTK_FILL True False - 0 + start Date Format + 0 1 - 2 - GTK_SHRINK | GTK_FILL @@ -640,34 +617,31 @@ Select location and file name for the Import, then click 'OK'... 1 - 2 2 - 3 - GTK_FILL True False - 0 + start Currency Format + 0 2 - 3 - GTK_SHRINK | GTK_FILL True False - 0 + start Encoding - GTK_SHRINK | GTK_FILL + 0 + 0 @@ -678,38 +652,35 @@ Select location and file name for the Import, then click 'OK'... 1 - 2 - GTK_FILL + 0 True False - 0 + start Leading Lines to Skip + 0 4 - 5 - GTK_FILL True False - 0 + start Trailing Lines to Skip + 0 5 - 6 - GTK_FILL - + True False @@ -717,11 +688,8 @@ Select location and file name for the Import, then click 'OK'... True True - True False False - True - True start_row_adj True @@ -735,13 +703,11 @@ Select location and file name for the Import, then click 'OK'... 1 - 2 4 - 5 - + True False @@ -749,11 +715,8 @@ Select location and file name for the Import, then click 'OK'... True True - True False False - True - True end_row_adj True @@ -767,20 +730,18 @@ Select location and file name for the Import, then click 'OK'... 1 - 2 5 - 6 - + True False - 2 + 0 3 - 4 + 2 @@ -800,6 +761,7 @@ Select location and file name for the Import, then click 'OK'... For example * if 'Leading Lines to Skip' is set to 3, the first line to import will be line 4. Lines 5, 7, 9,... will be skipped. * if 'Leading Lines to Skip' is set to 4, the first line to import will be line 5. Lines 6, 8, 10,... will be skipped. + start True @@ -817,6 +779,7 @@ For example True False + start <b>Miscellaneous</b> True @@ -824,11 +787,7 @@ For example 1 - 2 1 - 2 - GTK_FILL - GTK_FILL @@ -842,7 +801,7 @@ For example - + True False @@ -859,7 +818,7 @@ For example 5 5 - + True False @@ -913,7 +872,7 @@ For example 5 5 - + True False @@ -964,16 +923,15 @@ For example True True - automatic - automatic True False - + True False + vertical True @@ -981,6 +939,9 @@ For example GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK False both + + + False @@ -994,6 +955,9 @@ For example True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK both + + + True @@ -1013,7 +977,7 @@ For example - + True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK @@ -1022,8 +986,7 @@ For example True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - 0 - gtk-dialog-info + dialog-information False @@ -1037,7 +1000,7 @@ For example True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - 0 + start Select the type of each column to import. @@ -1055,7 +1018,7 @@ For example - + True False @@ -1063,7 +1026,6 @@ For example Skip Errors True False - 1 right True @@ -1089,10 +1051,11 @@ For example - + True False 12 + vertical True @@ -1122,10 +1085,11 @@ Cancel to abort.</b> - + True False 12 + vertical True @@ -1147,5 +1111,12 @@ Cancel to abort.</b> True + + + False + + + + diff --git a/gnucash/import-export/csv-imp/gnc-plugin-csv-import.c b/gnucash/import-export/csv-imp/gnc-plugin-csv-import.c index c14092d1c9..3cecf66fe8 100644 --- a/gnucash/import-export/csv-imp/gnc-plugin-csv-import.c +++ b/gnucash/import-export/csv-imp/gnc-plugin-csv-import.c @@ -57,7 +57,7 @@ static GtkActionEntry gnc_plugin_actions [] = G_CALLBACK (gnc_plugin_csv_import_trans_cmd) }, { - "CsvImportPriceAction", GTK_STOCK_CONVERT, N_("Import _Prices from a CSV file..."), NULL, + "CsvImportPriceAction", "go-previous", N_("Import _Prices from a CSV file..."), NULL, N_("Import Prices from a CSV file"), G_CALLBACK (gnc_plugin_csv_import_price_cmd) }, From db079b55404dfa1c04533b28d6d0ecd38da10689 Mon Sep 17 00:00:00 2001 From: Robert Fewell <14uBobIT@gmail.com> Date: Thu, 7 Dec 2017 11:17:14 +0000 Subject: [PATCH 21/48] Replace date parse function with one from gnc_datetime --- .../csv-imp/assistant-csv-price-import.cpp | 6 +- .../csv-imp/gnc-price-import.cpp | 8 -- .../csv-imp/gnc-price-import.hpp | 4 - .../import-export/csv-imp/gnc-price-props.cpp | 114 +----------------- .../import-export/csv-imp/gnc-price-props.hpp | 4 +- 5 files changed, 6 insertions(+), 130 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 af810cb2b2..fbc563c82a 100644 --- a/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp +++ b/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp @@ -608,10 +608,8 @@ CsvImpPriceAssist::CsvImpPriceAssist () /* Add in the date format combo box and hook it up to an event handler. */ date_format_combo = GTK_COMBO_BOX_TEXT(gtk_combo_box_text_new()); - for (int i = 0; i < num_date_formats_price; i++) - { - gtk_combo_box_text_append_text (date_format_combo, _(date_format_user_price[i])); - } + for (auto& date_fmt : GncDate::c_formats) + gtk_combo_box_text_append_text (date_format_combo, _(date_fmt.m_fmt.c_str())); gtk_combo_box_set_active (GTK_COMBO_BOX(date_format_combo), 0); g_signal_connect (G_OBJECT(date_format_combo), "changed", G_CALLBACK(csv_price_imp_preview_date_fmt_sel_cb), this); diff --git a/gnucash/import-export/csv-imp/gnc-price-import.cpp b/gnucash/import-export/csv-imp/gnc-price-import.cpp index 838c9473cb..705d37ccb4 100644 --- a/gnucash/import-export/csv-imp/gnc-price-import.cpp +++ b/gnucash/import-export/csv-imp/gnc-price-import.cpp @@ -46,14 +46,6 @@ extern "C" { G_GNUC_UNUSED static QofLogModule log_module = GNC_MOD_IMPORT; -const int num_date_formats_price = 5; -const gchar* date_format_user_price[] = {N_("y-m-d"), - N_("d-m-y"), - N_("m-d-y"), - N_("d-m"), - N_("m-d") - }; - const int num_currency_formats_price = 3; const gchar* currency_format_user_price[] = {N_("Locale"), N_("Period: 123,456.78"), diff --git a/gnucash/import-export/csv-imp/gnc-price-import.hpp b/gnucash/import-export/csv-imp/gnc-price-import.hpp index 9959aaaa51..fd8ebedf81 100644 --- a/gnucash/import-export/csv-imp/gnc-price-import.hpp +++ b/gnucash/import-export/csv-imp/gnc-price-import.hpp @@ -49,10 +49,6 @@ extern "C" { extern const int num_currency_formats_price; extern const gchar* currency_format_user_price[]; -/* A set of date formats that the user sees. */ -extern const int num_date_formats_price; -extern const gchar* date_format_user_price[]; - /** Tuple to hold * - a tokenized line of input * - an optional error string diff --git a/gnucash/import-export/csv-imp/gnc-price-props.cpp b/gnucash/import-export/csv-imp/gnc-price-props.cpp index 539f316851..1ccd6e50af 100644 --- a/gnucash/import-export/csv-imp/gnc-price-props.cpp +++ b/gnucash/import-export/csv-imp/gnc-price-props.cpp @@ -50,116 +50,6 @@ std::map gnc_price_col_type_strs = { { GncPricePropType::TO_CURRENCY, N_("Currency To") }, }; -/* Regular expressions used to parse dates per date format */ -const char* date_regex_price[] = { - "(?:" // either y-m-d - "(?[0-9]+)[-/.' ]+" - "(?[0-9]+)[-/.' ]+" - "(?[0-9]+)" - "|" // or CCYYMMDD - "(?[0-9]{4})" - "(?[0-9]{2})" - "(?[0-9]{2})" - ")", - - "(?:" // either d-m-y - "(?[0-9]+)[-/.' ]+" - "(?[0-9]+)[-/.' ]+" - "(?[0-9]+)" - "|" // or DDMMCCYY - "(?[0-9]{2})" - "(?[0-9]{2})" - "(?[0-9]{4})" - ")", - - "(?:" // either m-d-y - "(?[0-9]+)[-/.' ]+" - "(?[0-9]+)[-/.' ]+" - "(?[0-9]+)" - "|" // or MMDDCCYY - "(?[0-9]{2})" - "(?[0-9]{2})" - "(?[0-9]{4})" - ")", - - "(?:" // either d-m(-y) - "(?[0-9]+)[-/.' ]+" - "(?[0-9]+)(?:[-/.' ]+" - "(?[0-9]+))?" - "|" // or DDMM(CCYY) - "(?[0-9]{2})" - "(?[0-9]{2})" - "(?[0-9]+)?" - ")", - - "(?:" // either m-d(-y) - "(?[0-9]+)[-/.' ]+" - "(?[0-9]+)(?:[-/.' ]+" - "(?[0-9]+))?" - "|" // or MMDD(CCYY) - "(?[0-9]{2})" - "(?[0-9]{2})" - "(?[0-9]+)?" - ")", -}; - -/** Parses a string into a date, given a format. This function - * requires only knowing the order in which the year, month and day - * appear. For example, 01-02-2003 will be parsed the same way as - * 01/02/2003. - * @param date_str The string containing a date being parsed - * @param format An index specifying a format in date_format_user - * @exception std::invalid_argument if the string can't be parsed into a date. - * @return The parsed value of date_str on success, throws on failure - */ - -time64 parse_date_price (const std::string &date_str, int format) -{ - boost::regex r(date_regex_price[format]); - boost::smatch what; - if(!boost::regex_search(date_str, what, r)) - throw std::invalid_argument (_("Value can't be parsed into a date using the selected date format.")); // regex didn't find a match - - // Attention: different behavior from 2.6.x series ! - // If date format without year was selected, the match - // should NOT have found a year. - if ((format >= 3) && (what.length("YEAR") != 0)) - throw std::invalid_argument (_("Value appears to contain a year while the selected format forbids this.")); - - auto day = std::stoi (what.str("DAY")); - auto month = std::stoi (what.str("MONTH")); - - int year; - if (format < 3) - { - /* The input dates have a year, so use that one */ - year = std::stoi (what.str("YEAR")); - - /* Handle two-digit years. */ - if (year < 100) - { - /* We allow two-digit years in the range 1969 - 2068. */ - if (year < 69) - year += 2000; - else - year += 1900; - } - } - else - { - /* The input dates don't have a year, so work with today's year. - */ - gnc_timespec2dmy(timespec_now(), nullptr, nullptr, &year); - } - - auto ts = gnc_dmy2timespec_neutral(day, month, year); - if (ts.tv_sec == INT64_MAX) - throw std::invalid_argument (_("Value can't be parsed into a date using the selected date format.")); - - return ts.tv_sec; -} - - /** Convert str into a GncNumeric using the user-specified (import) currency format. * @param str The string to be parsed * @param currency_format The currency format to use. @@ -257,7 +147,7 @@ void GncImportPrice::set (GncPricePropType prop_type, const std::string& value) { case GncPricePropType::DATE: m_date = boost::none; - m_date = parse_date_price (value, m_date_format); // Throws if parsing fails + m_date = GncDate(value, GncDate::c_formats[m_date_format].m_fmt); // Throws if parsing fails break; case GncPricePropType::AMOUNT: @@ -357,7 +247,7 @@ Result GncImportPrice::create_price (QofBook* book, GNCPriceDB *pdb, bool over) } Timespec date; - timespecFromTime64 (&date, *m_date); + timespecFromTime64 (&date, static_cast(GncDateTime(*m_date, DayPart::neutral))); date.tv_nsec = 0; bool rev = false; diff --git a/gnucash/import-export/csv-imp/gnc-price-props.hpp b/gnucash/import-export/csv-imp/gnc-price-props.hpp index 9ae485323e..77e39dc689 100644 --- a/gnucash/import-export/csv-imp/gnc-price-props.hpp +++ b/gnucash/import-export/csv-imp/gnc-price-props.hpp @@ -39,6 +39,7 @@ extern "C" { #include #include #include +#include #include /** Enumeration for column types. These are the different types of @@ -76,7 +77,6 @@ private: const char *m_name; }; -time64 parse_date_price (const std::string &date_str, int format); gnc_commodity* parse_commodity_price_comm (const std::string& comm_str); GncNumeric parse_amount_price (const std::string &str, int currency_format); @@ -104,7 +104,7 @@ public: private: int m_date_format; int m_currency_format; - boost::optional m_date; + boost::optional m_date; boost::optional m_amount; boost::optional m_from_commodity; boost::optional m_to_currency; From 66da4ae37404dc46a6ee1a81289cd2b8fd506383 Mon Sep 17 00:00:00 2001 From: Robert Fewell <14uBobIT@gmail.com> Date: Thu, 7 Dec 2017 11:19:18 +0000 Subject: [PATCH 22/48] Add a test for empty values Some csv values are allowed to be empty based on options selected so add a test for this otherwise all values are required. --- .../import-export/csv-imp/gnc-price-import.cpp | 18 +++++++++++------- .../import-export/csv-imp/gnc-price-props.cpp | 9 +++++++-- .../import-export/csv-imp/gnc-price-props.hpp | 2 +- 3 files changed, 19 insertions(+), 10 deletions(-) diff --git a/gnucash/import-export/csv-imp/gnc-price-import.cpp b/gnucash/import-export/csv-imp/gnc-price-import.cpp index 705d37ccb4..61c876de90 100644 --- a/gnucash/import-export/csv-imp/gnc-price-import.cpp +++ b/gnucash/import-export/csv-imp/gnc-price-import.cpp @@ -649,15 +649,13 @@ void GncPriceImport::update_price_props (uint32_t row, uint32_t col, GncPricePro return; /* Only deal with price related properties. */ auto price_props = std::make_shared (*(std::get<2>(m_parsed_lines[row])).get()); - auto value = std::string(); - if (col < std::get<0>(m_parsed_lines[row]).size()) - value = std::get<0>(m_parsed_lines[row]).at(col); - - if (value.empty()) - price_props->reset (prop_type); + if (col >= std::get<0>(m_parsed_lines[row]).size()) + price_props->reset (prop_type); //reset errors else { + auto value = std::get<0>(m_parsed_lines[row]).at(col); + bool enable_test_empty = true; try { // set the from_commodity based on combo so we can test for same. @@ -665,14 +663,20 @@ void GncPriceImport::update_price_props (uint32_t row, uint32_t col, GncPricePro { if (m_settings.m_from_commodity) price_props->set_from_commodity (m_settings.m_from_commodity); + + if (m_settings.m_to_currency) + enable_test_empty = false; } // set the to_currency based on combo so we can test for same. if (prop_type == GncPricePropType::FROM_COMMODITY) { if (m_settings.m_to_currency) price_props->set_to_currency (m_settings.m_to_currency); + + if (m_settings.m_from_commodity) + enable_test_empty = false; } - price_props->set(prop_type, value); + price_props->set(prop_type, value, enable_test_empty); } catch (const std::exception& e) { diff --git a/gnucash/import-export/csv-imp/gnc-price-props.cpp b/gnucash/import-export/csv-imp/gnc-price-props.cpp index 1ccd6e50af..151bd38ad2 100644 --- a/gnucash/import-export/csv-imp/gnc-price-props.cpp +++ b/gnucash/import-export/csv-imp/gnc-price-props.cpp @@ -135,13 +135,17 @@ gnc_commodity* parse_commodity_price_comm (const std::string& comm_str) return comm; } -void GncImportPrice::set (GncPricePropType prop_type, const std::string& value) +void GncImportPrice::set (GncPricePropType prop_type, const std::string& value, bool enable_test_empty) { try { // Drop any existing error for the prop_type we're about to set m_errors.erase(prop_type); + // conditional test for empty values + if (value.empty() && enable_test_empty) + throw std::invalid_argument (_("Column value can not be empty.")); + gnc_commodity *comm = nullptr; switch (prop_type) { @@ -207,7 +211,8 @@ void GncImportPrice::reset (GncPricePropType prop_type) { try { - set (prop_type, std::string()); + // set enable_test_empty to false to allow empty values + set (prop_type, std::string(), false); } catch (...) { diff --git a/gnucash/import-export/csv-imp/gnc-price-props.hpp b/gnucash/import-export/csv-imp/gnc-price-props.hpp index 77e39dc689..3a358612af 100644 --- a/gnucash/import-export/csv-imp/gnc-price-props.hpp +++ b/gnucash/import-export/csv-imp/gnc-price-props.hpp @@ -86,7 +86,7 @@ public: GncImportPrice (int date_format, int currency_format) : m_date_format{date_format}, m_currency_format{currency_format}{}; - void set (GncPricePropType prop_type, const std::string& value); + void set (GncPricePropType prop_type, const std::string& value, bool enable_test_empty); void set_date_format (int date_format) { m_date_format = date_format ;} void set_currency_format (int currency_format) { m_currency_format = currency_format ;} void reset (GncPricePropType prop_type); From 17d8d424a0a6266a9790797d88452f2c356f3e0d Mon Sep 17 00:00:00 2001 From: Robert Fewell <14uBobIT@gmail.com> Date: Fri, 1 Dec 2017 10:44:15 +0000 Subject: [PATCH 23/48] Add property files for the csv price importer These files are largely based on the csv transaction importer --- gnucash/import-export/csv-imp/CMakeLists.txt | 2 + gnucash/import-export/csv-imp/Makefile.am | 2 + .../import-export/csv-imp/gnc-price-props.cpp | 508 ++++++++++++++++++ .../import-export/csv-imp/gnc-price-props.hpp | 110 ++++ 4 files changed, 622 insertions(+) create mode 100644 gnucash/import-export/csv-imp/gnc-price-props.cpp create mode 100644 gnucash/import-export/csv-imp/gnc-price-props.hpp diff --git a/gnucash/import-export/csv-imp/CMakeLists.txt b/gnucash/import-export/csv-imp/CMakeLists.txt index c2a77b82fc..e2fa97fee9 100644 --- a/gnucash/import-export/csv-imp/CMakeLists.txt +++ b/gnucash/import-export/csv-imp/CMakeLists.txt @@ -19,6 +19,7 @@ SET(csv_import_SOURCES gnc-csv-trans-settings.cpp gnc-dummy-tokenizer.cpp gnc-fw-tokenizer.cpp + gnc-price-props.cpp gnc-tokenizer.cpp gnc-trans-props.cpp gnc-tx-import.cpp @@ -44,6 +45,7 @@ SET(csv_import_noinst_HEADERS gnc-csv-trans-settings.hpp gnc-dummy-tokenizer.hpp gnc-fw-tokenizer.hpp + gnc-price-props.hpp gnc-tokenizer.hpp gnc-trans-props.hpp gnc-tx-import.hpp diff --git a/gnucash/import-export/csv-imp/Makefile.am b/gnucash/import-export/csv-imp/Makefile.am index 06ef43f207..8b13d01e18 100644 --- a/gnucash/import-export/csv-imp/Makefile.am +++ b/gnucash/import-export/csv-imp/Makefile.am @@ -13,6 +13,7 @@ libgncmod_csv_import_la_SOURCES = \ gnc-csv-gnumeric-popup.c \ gnc-dummy-tokenizer.cpp \ gnc-fw-tokenizer.cpp \ + gnc-price-props.cpp \ gnc-tokenizer.cpp \ gnc-tx-import.cpp \ gnc-trans-props.cpp \ @@ -28,6 +29,7 @@ noinst_HEADERS = \ gnc-csv-gnumeric-popup.h \ gnc-dummy-tokenizer.hpp \ gnc-fw-tokenizer.hpp \ + gnc-price-props.hpp \ gnc-tokenizer.hpp \ gnc-tx-import.hpp \ gnc-trans-props.hpp \ diff --git a/gnucash/import-export/csv-imp/gnc-price-props.cpp b/gnucash/import-export/csv-imp/gnc-price-props.cpp new file mode 100644 index 0000000000..1fdcaa53a3 --- /dev/null +++ b/gnucash/import-export/csv-imp/gnc-price-props.cpp @@ -0,0 +1,508 @@ +/********************************************************************\ + * gnc-price-props.cpp - encapsulate price properties for use * + * in the csv importer * + * * + * 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 * + * * +\********************************************************************/ + +extern "C" { +#include +#if PLATFORM(WINDOWS) +#include +#endif + +#include +#include + +#include "engine-helpers.h" +#include "gnc-ui-util.h" +#include "gnc-pricedb.h" + +} + +#include +#include +#include +#include "gnc-price-props.hpp" + +G_GNUC_UNUSED static QofLogModule log_module = GNC_MOD_IMPORT; + +/* This map contains a set of strings representing the different column types. */ +std::map gnc_csv_price_col_type_strs = { + { GncPricePropType::NONE, N_("None") }, + { GncPricePropType::DATE, N_("Date") }, + { GncPricePropType::AMOUNT, N_("Amount") }, + { GncPricePropType::CURRENCY_FROM, N_("Currency From") }, + { GncPricePropType::CURRENCY_TO, N_("Currency To") }, + { GncPricePropType::SYMBOL_FROM, N_("Symbol From") }, +}; + +/* Regular expressions used to parse dates per date format */ +const char* date_regex_price[] = { + "(?:" // either y-m-d + "(?[0-9]+)[-/.' ]+" + "(?[0-9]+)[-/.' ]+" + "(?[0-9]+)" + "|" // or CCYYMMDD + "(?[0-9]{4})" + "(?[0-9]{2})" + "(?[0-9]{2})" + ")", + + "(?:" // either d-m-y + "(?[0-9]+)[-/.' ]+" + "(?[0-9]+)[-/.' ]+" + "(?[0-9]+)" + "|" // or DDMMCCYY + "(?[0-9]{2})" + "(?[0-9]{2})" + "(?[0-9]{4})" + ")", + + "(?:" // either m-d-y + "(?[0-9]+)[-/.' ]+" + "(?[0-9]+)[-/.' ]+" + "(?[0-9]+)" + "|" // or MMDDCCYY + "(?[0-9]{2})" + "(?[0-9]{2})" + "(?[0-9]{4})" + ")", + + "(?:" // either d-m(-y) + "(?[0-9]+)[-/.' ]+" + "(?[0-9]+)(?:[-/.' ]+" + "(?[0-9]+))?" + "|" // or DDMM(CCYY) + "(?[0-9]{2})" + "(?[0-9]{2})" + "(?[0-9]+)?" + ")", + + "(?:" // either m-d(-y) + "(?[0-9]+)[-/.' ]+" + "(?[0-9]+)(?:[-/.' ]+" + "(?[0-9]+))?" + "|" // or MMDD(CCYY) + "(?[0-9]{2})" + "(?[0-9]{2})" + "(?[0-9]+)?" + ")", +}; + +/** Parses a string into a date, given a format. This function + * requires only knowing the order in which the year, month and day + * appear. For example, 01-02-2003 will be parsed the same way as + * 01/02/2003. + * @param date_str The string containing a date being parsed + * @param format An index specifying a format in date_format_user + * @exception std::invalid_argument if the string can't be parsed into a date. + * @return The parsed value of date_str on success, throws on failure + */ + +time64 parse_date_price (const std::string &date_str, int format) +{ + boost::regex r(date_regex_price[format]); + boost::smatch what; + if(!boost::regex_search(date_str, what, r)) + throw std::invalid_argument (_("Value can't be parsed into a date using the selected date format.")); // regex didn't find a match + + // Attention: different behavior from 2.6.x series ! + // If date format without year was selected, the match + // should NOT have found a year. + if ((format >= 3) && (what.length("YEAR") != 0)) + throw std::invalid_argument (_("Value appears to contain a year while the selected format forbids this.")); + + auto day = std::stoi (what.str("DAY")); + auto month = std::stoi (what.str("MONTH")); + + int year; + if (format < 3) + { + /* The input dates have a year, so use that one */ + year = std::stoi (what.str("YEAR")); + + /* Handle two-digit years. */ + if (year < 100) + { + /* We allow two-digit years in the range 1969 - 2068. */ + if (year < 69) + year += 2000; + else + year += 1900; + } + } + else + { + /* The input dates don't have a year, so work with today's year. + */ + gnc_timespec2dmy(timespec_now(), nullptr, nullptr, &year); + } + + auto ts = gnc_dmy2timespec_neutral(day, month, year); + if (ts.tv_sec == INT64_MAX) + throw std::invalid_argument (_("Value can't be parsed into a date using the selected date format.")); + + return ts.tv_sec; +} + + +/** Convert str into a GncRational using the user-specified (import) currency format. + * @param str The string to be parsed + * @param currency_format The currency format to use. + * @return a GncNumeric + * @exception May throw std::invalid argument if string can't be parsed properly + */ +GncNumeric parse_amount_price (const std::string &str, int currency_format) +{ + /* If a cell is empty or just spaces return invalid amount */ + if(!boost::regex_search(str, boost::regex("[0-9]"))) + throw std::invalid_argument (_("Value doesn't appear to contain a valid number.")); + + auto expr = boost::make_u32regex("[[:Sc:]]"); + std::string str_no_symbols = boost::u32regex_replace(str, expr, ""); + + /* Convert based on user chosen currency format */ + gnc_numeric val; + char *endptr; + switch (currency_format) + { + case 0: + /* Currency locale */ + if (!(xaccParseAmount (str_no_symbols.c_str(), TRUE, &val, &endptr))) + throw std::invalid_argument (_("Value can't be parsed into a number using the selected currency format.")); + break; + case 1: + /* Currency decimal period */ + if (!(xaccParseAmountExtended (str_no_symbols.c_str(), TRUE, '-', '.', ',', "\003\003", "$+", &val, &endptr))) + throw std::invalid_argument (_("Value can't be parsed into a number using the selected currency format.")); + break; + case 2: + /* Currency decimal comma */ + if (!(xaccParseAmountExtended (str_no_symbols.c_str(), TRUE, '-', ',', '.', "\003\003", "$+", &val, &endptr))) + throw std::invalid_argument (_("Value can't be parsed into a number using the selected currency format.")); + break; + } + + return GncNumeric(val); +} + +gnc_commodity* parse_commodity_price_comm (const std::string& comm_str) +{ + if (comm_str.empty()) + return nullptr; + + auto table = gnc_commodity_table_get_table (gnc_get_current_book()); + gnc_commodity* comm = nullptr; + + /* First try commodity as a unique name. */ + if (comm_str.find("::")) + comm = gnc_commodity_table_lookup_unique (table, comm_str.c_str()); + + /* Then try mnemonic in the currency namespace */ + if (!comm) + comm = gnc_commodity_table_lookup (table, + GNC_COMMODITY_NS_CURRENCY, comm_str.c_str()); + + if (!comm) + { + /* If that fails try mnemonic in all other namespaces */ + auto namespaces = gnc_commodity_table_get_namespaces(table); + for (auto ns = namespaces; ns; ns = ns->next) + { + gchar* ns_str = (gchar*)ns->data; + if (g_utf8_collate(ns_str, GNC_COMMODITY_NS_CURRENCY) == 0) + continue; + + comm = gnc_commodity_table_lookup (table, + ns_str, comm_str.c_str()); + if (comm) + break; + } + } + + if (!comm) + throw std::invalid_argument (_("Value can't be parsed into a valid commodity.")); + else + return comm; +} + +gnc_commodity * parse_commodity_price_sym (const std::string& sym_str, bool is_currency) +{ + if (sym_str.empty()) + return nullptr; + + auto commodity_table = gnc_get_current_commodities (); + GList *namespaces; + gnc_commodity *retval = nullptr; + gnc_commodity *tmp_commodity = nullptr; + char *tmp_namespace = nullptr; + GList *commodity_list = NULL; + GList *namespace_list = gnc_commodity_table_get_namespaces (commodity_table); + + namespace_list = g_list_first (namespace_list); + while (namespace_list != NULL && retval == NULL) + { + tmp_namespace = (char*)namespace_list->data; + DEBUG("Looking at namespace %s", tmp_namespace); + commodity_list = gnc_commodity_table_get_commodities (commodity_table, tmp_namespace); + commodity_list = g_list_first (commodity_list); + while (commodity_list != NULL && retval == NULL) + { + const char* tmp_mnemonic = NULL; + tmp_commodity = (gnc_commodity*)commodity_list->data; + DEBUG("Looking at commodity %s", gnc_commodity_get_fullname (tmp_commodity)); + tmp_mnemonic = gnc_commodity_get_mnemonic (tmp_commodity); + if (g_strcmp0 (tmp_mnemonic, sym_str.c_str()) == 0) + { + retval = tmp_commodity; + DEBUG("Commodity %s%s", gnc_commodity_get_fullname (retval), " matches."); + } + commodity_list = g_list_next (commodity_list); + } + namespace_list = g_list_next (namespace_list); + } + g_list_free (commodity_list); + g_list_free (namespace_list); + + if (!retval) + throw std::invalid_argument (_("Value can't be parsed into a valid commodity.")); + else + { + if (gnc_commodity_is_currency (retval) != is_currency) + throw std::invalid_argument (_("Value parsed into an invalid commodity for column type.")); + else + return retval; + } +} + +void GncImportPrice::set (GncPricePropType prop_type, const std::string& value) +{ + try + { + // Drop any existing error for the prop_type we're about to set + m_errors.erase(prop_type); + + gnc_commodity *comm = nullptr; + switch (prop_type) + { + case GncPricePropType::DATE: + m_date = boost::none; + m_date = parse_date_price (value, m_date_format); // Throws if parsing fails + break; + + case GncPricePropType::AMOUNT: + m_amount = boost::none; + m_amount = parse_amount_price (value, m_currency_format); // Will throw if parsing fails + break; + + case GncPricePropType::CURRENCY_FROM: + m_currency_from = boost::none; + comm = parse_commodity_price_sym (value, true); // Throws if parsing fails + if (comm) + m_currency_from = comm; + break; + + case GncPricePropType::CURRENCY_TO: + m_currency_to = boost::none; + comm = parse_commodity_price_sym (value, true); // Throws if parsing fails + if (comm) + m_currency_to = comm; + break; + + case GncPricePropType::SYMBOL_FROM: + m_symbol_from = boost::none; + comm = parse_commodity_price_sym (value, false); // Throws if parsing fails + if (comm) + m_symbol_from = comm; + break; + + default: + /* Issue a warning for all other prop_types. */ + PWARN ("%d is an invalid property for a Price", static_cast(prop_type)); + break; + } + } + catch (const std::invalid_argument& e) + { + auto err_str = std::string(_(gnc_csv_price_col_type_strs[prop_type])) + + std::string(_(" could not be understood.\n")) + + e.what(); + m_errors.emplace(prop_type, err_str); + throw std::invalid_argument (err_str); + } + catch (const std::out_of_range& e) + { + auto err_str = std::string(_(gnc_csv_price_col_type_strs[prop_type])) + + std::string(_(" could not be understood.\n")) + + e.what(); + m_errors.emplace(prop_type, err_str); + throw std::invalid_argument (err_str); + } +} + +void GncImportPrice::reset (GncPricePropType prop_type) +{ + try + { + set (prop_type, std::string()); + } + catch (...) + { + // Set with an empty string will effectively clear the property + // but can also set an error for the property. Clear that error here. + m_errors.erase(prop_type); + } +} + +std::string GncImportPrice::verify_essentials (void) +{ + /* Make sure this price has the minimum required set of properties defined */ + if (m_date == boost::none) + return _("No date column."); + else if (m_amount == boost::none) + return _("No amount column."); + else if (m_currency_to == boost::none) + return _("No Currency to column."); + else if ((m_symbol_from == boost::none) && (m_currency_from == boost::none)) + return _("No from column."); + else + return std::string(); +} + +bool GncImportPrice::create_price (QofBook* book, GNCPriceDB *pdb, bool over) +{ + /* Gently refuse to create the price if the basics are not set correctly + * This should have been tested before calling this function though! + */ + auto check = verify_essentials(); + if (!check.empty()) + { + PWARN ("Refusing to create price because essentials not set properly: %s", check.c_str()); + return false; + } + + Timespec date; + timespecFromTime64 (&date, *m_date); + date.tv_nsec = 0; + +#ifdef skip +//FIXME Numeric needs changing, copied from old version... + bool rev = false; + gnc_commodity *comm_from = nullptr; + + if (m_currency_from != boost::none) // Currency Import + { + // Check for currency in reverse direction. + GNCPrice *rev_price = gnc_pricedb_lookup_day (pdb, *m_currency_to, *m_currency_from, date); + if (rev_price != nullptr) + rev = true; + gnc_price_unref (rev_price); + + // Check for price less than 1, reverse if so. + if (gnc_numeric_compare (*m_amount, gnc_numeric_create (1, 1)) != 1) + rev = true; + + comm_from = *m_currency_from; + DEBUG("Commodity from is a Currency"); + } + else + comm_from = *m_symbol_from; + + DEBUG("Date is %s, Rev is %d, Commodity from is '%s', Currency is '%s', Amount is %s", gnc_print_date (date), + rev, gnc_commodity_get_fullname (comm_from), gnc_commodity_get_fullname (*m_currency_to), + gnc_num_dbg_to_string (*m_amount) ); + + GNCPrice *old_price = nullptr; + + // Should the commodities be reversed + if (rev) + old_price = gnc_pricedb_lookup_day (pdb, *m_currency_to, comm_from, date); + else + old_price = gnc_pricedb_lookup_day (pdb, comm_from, *m_currency_to, date); + + // Should old price be over writen + if ((old_price != nullptr) && (over == true)) + { + DEBUG("Over write"); + gnc_pricedb_remove_price (pdb, old_price); + gnc_price_unref (old_price); + old_price = nullptr; + } +#endif + bool ret_val = true; +#ifdef skip + // Create the new price + if (old_price == nullptr) + { + DEBUG("Create"); + GNCPrice *price = gnc_price_create (book); + gnc_price_begin_edit (price); + + if (rev) + { + gnc_price_set_commodity (price, *m_currency_to); + gnc_price_set_currency (price, comm_from); + *m_amount = gnc_numeric_convert (gnc_numeric_invert (*m_amount), + CURRENCY_DENOM, GNC_HOW_RND_ROUND_HALF_UP); + gnc_price_set_value (price, *m_amount); + } + else + { + gnc_price_set_commodity (price, comm_from); + gnc_price_set_currency (price, *m_currency_to); + gnc_price_set_value (price, *m_amount); + } + gnc_price_set_time (price, date); + gnc_price_set_source (price, PRICE_SOURCE_USER_PRICE); +//FIXME Not sure which one gnc_price_set_source (price, PRICE_SOURCE_FQ); + gnc_price_set_typestr (price, PRICE_TYPE_LAST); + gnc_price_commit_edit (price); + + bool perr = gnc_pricedb_add_price (pdb, price); + + gnc_price_unref (price); + + if (perr == false) + throw std::invalid_argument (_("Failed to create price from selected columns.")); +//FIXME Not sure about this, should this be a PWARN + } + else + +#endif + ret_val = false; + + return ret_val; +} + +static std::string gen_err_str (std::map& errors) +{ + auto full_error = std::string(); + for (auto error : errors) + { + full_error += (full_error.empty() ? "" : "\n") + error.second; + } + return full_error; +} + +std::string GncImportPrice::errors () +{ + return gen_err_str (m_errors); +} + diff --git a/gnucash/import-export/csv-imp/gnc-price-props.hpp b/gnucash/import-export/csv-imp/gnc-price-props.hpp new file mode 100644 index 0000000000..6f1189be1a --- /dev/null +++ b/gnucash/import-export/csv-imp/gnc-price-props.hpp @@ -0,0 +1,110 @@ +/********************************************************************\ + * gnc-price-props.hpp - encapsulate price properties for use * + * in the csv importer * + * * + * 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_PRICE_PROPS_HPP +#define GNC_PRICE_PROPS_HPP + +extern "C" { +#include +#if PLATFORM(WINDOWS) +#include +#endif + +#include + +#include "gnc-commodity.h" +} + +#include +#include +#include +#include +#include + +/** Enumeration for column types. These are the different types of + * columns that can exist in a CSV/Fixed-Width file. There should be + * no two columns with the same type except for the GncPricePropType::NONE + * type. */ +enum class GncPricePropType { + NONE, + DATE, + AMOUNT, + CURRENCY_FROM, + CURRENCY_TO, + SYMBOL_FROM, + PRICE_PROPS = SYMBOL_FROM +}; + +/** Maps all column types to a string representation. + * The actual definition is in gnc-csv-imp-prices.cpp. + * Attention: that definition should be adjusted for any + * changes to enum class GncPricePropType ! */ +extern std::map gnc_csv_price_col_type_strs; + +/** Functor to check if the above map has an element of which + * the value equals name. To be used with std::find_if. + */ +struct test_price_prop_type_str +{ + test_price_prop_type_str( const char* name ) : m_name(name) {} + bool operator()( const std::pair& v ) const + { + return !g_strcmp0(v.second, m_name); + } +private: + const char *m_name; +}; + +time64 parse_date_price (const std::string &date_str, int format); +gnc_commodity* parse_commodity_price_comm (const std::string& comm_str); +gnc_commodity* parse_commodity_price_sym (const std::string& comm_str, bool is_currency); +GncNumeric parse_amount_price (const std::string &str, int currency_format); + +struct GncImportPrice +{ +public: + GncImportPrice (int date_format, int currency_format) : m_date_format{date_format}, + m_currency_format{currency_format}{}; + + void set (GncPricePropType prop_type, const std::string& value); + void set_date_format (int date_format) { m_date_format = date_format ;} + void set_currency_format (int currency_format) { m_currency_format = currency_format ;} + void reset (GncPricePropType prop_type); + std::string verify_essentials (void); + bool create_price (QofBook* book, GNCPriceDB *pdb, bool over); + std::string errors(); + +private: + int m_date_format; + int m_currency_format; + boost::optional m_date; + boost::optional m_amount; + boost::optional m_currency_from; + boost::optional m_currency_to; + boost::optional m_symbol_from; + bool created = false; + + std::map m_errors; +}; + +#endif From bec1fbd1a08cbd1c0c408df5e38a6343aa9dccd8 Mon Sep 17 00:00:00 2001 From: Robert Fewell <14uBobIT@gmail.com> Date: Fri, 1 Dec 2017 10:53:41 +0000 Subject: [PATCH 24/48] Add price import files for the csv price importer These files are largely based on the csv transaction importer and with minimum of changes to settings files. --- gnucash/import-export/csv-imp/CMakeLists.txt | 2 + gnucash/import-export/csv-imp/Makefile.am | 2 + .../csv-imp/gnc-csv-trans-settings.cpp | 8 + .../csv-imp/gnc-csv-trans-settings.hpp | 4 +- .../csv-imp/gnc-price-import.cpp | 654 ++++++++++++++++++ .../csv-imp/gnc-price-import.hpp | 160 +++++ .../import-export/csv-imp/gnc-price-props.hpp | 2 +- 7 files changed, 830 insertions(+), 2 deletions(-) create mode 100644 gnucash/import-export/csv-imp/gnc-price-import.cpp create mode 100644 gnucash/import-export/csv-imp/gnc-price-import.hpp diff --git a/gnucash/import-export/csv-imp/CMakeLists.txt b/gnucash/import-export/csv-imp/CMakeLists.txt index e2fa97fee9..0bc7d2938f 100644 --- a/gnucash/import-export/csv-imp/CMakeLists.txt +++ b/gnucash/import-export/csv-imp/CMakeLists.txt @@ -19,6 +19,7 @@ SET(csv_import_SOURCES gnc-csv-trans-settings.cpp gnc-dummy-tokenizer.cpp gnc-fw-tokenizer.cpp + gnc-price-import.cpp gnc-price-props.cpp gnc-tokenizer.cpp gnc-trans-props.cpp @@ -45,6 +46,7 @@ SET(csv_import_noinst_HEADERS gnc-csv-trans-settings.hpp gnc-dummy-tokenizer.hpp gnc-fw-tokenizer.hpp + gnc-price-import.hpp gnc-price-props.hpp gnc-tokenizer.hpp gnc-trans-props.hpp diff --git a/gnucash/import-export/csv-imp/Makefile.am b/gnucash/import-export/csv-imp/Makefile.am index 8b13d01e18..408396331a 100644 --- a/gnucash/import-export/csv-imp/Makefile.am +++ b/gnucash/import-export/csv-imp/Makefile.am @@ -13,6 +13,7 @@ libgncmod_csv_import_la_SOURCES = \ gnc-csv-gnumeric-popup.c \ gnc-dummy-tokenizer.cpp \ gnc-fw-tokenizer.cpp \ + gnc-price-import.cpp \ gnc-price-props.cpp \ gnc-tokenizer.cpp \ gnc-tx-import.cpp \ @@ -29,6 +30,7 @@ noinst_HEADERS = \ gnc-csv-gnumeric-popup.h \ gnc-dummy-tokenizer.hpp \ gnc-fw-tokenizer.hpp \ + gnc-price-import.hpp \ gnc-price-props.hpp \ gnc-tokenizer.hpp \ gnc-tx-import.hpp \ diff --git a/gnucash/import-export/csv-imp/gnc-csv-trans-settings.cpp b/gnucash/import-export/csv-imp/gnc-csv-trans-settings.cpp index 6611875380..48cfdce9d2 100644 --- a/gnucash/import-export/csv-imp/gnc-csv-trans-settings.cpp +++ b/gnucash/import-export/csv-imp/gnc-csv-trans-settings.cpp @@ -108,6 +108,14 @@ static std::shared_ptr create_int_gnc_exp_preset(void) GncTransPropType::PRICE }; + preset->m_column_types_price = { + GncPricePropType::DATE, + GncPricePropType::AMOUNT, + GncPricePropType::CURRENCY_FROM, + GncPricePropType::CURRENCY_TO, + GncPricePropType::SYMBOL_FROM + }; + return preset; } diff --git a/gnucash/import-export/csv-imp/gnc-csv-trans-settings.hpp b/gnucash/import-export/csv-imp/gnc-csv-trans-settings.hpp index 379c6600d8..93275f2da0 100644 --- a/gnucash/import-export/csv-imp/gnc-csv-trans-settings.hpp +++ b/gnucash/import-export/csv-imp/gnc-csv-trans-settings.hpp @@ -36,6 +36,7 @@ extern "C" { #include #include #include "gnc-trans-props.hpp" +#include "gnc-price-props.hpp" #include "gnc-tokenizer.hpp" /** Enumeration for separator checkbutton types. These are the @@ -93,7 +94,8 @@ std::string m_separators; // Separators for csv format Account *m_base_account; // Base account std::vector m_column_types; // The Column types in order -std::vector m_column_widths; // The Column widths +std::vector m_column_types_price; // The Column Price types in order +std::vector m_column_widths; // The Column widths bool m_load_error; // Was there an error while parsing the state file ? }; diff --git a/gnucash/import-export/csv-imp/gnc-price-import.cpp b/gnucash/import-export/csv-imp/gnc-price-import.cpp new file mode 100644 index 0000000000..09deb934e0 --- /dev/null +++ b/gnucash/import-export/csv-imp/gnc-price-import.cpp @@ -0,0 +1,654 @@ +/********************************************************************\ + * gnc-price-import.cpp - import prices from csv files * + * * + * 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 * + * * +\********************************************************************/ + +#include + +extern "C" { +#include +#if PLATFORM(WINDOWS) +#include +#endif + +#include + +#include "gnc-ui-util.h" //get book +#include "gnc-commodity.h" +#include "gnc-pricedb.h" +} + +#include +#include + +#include "gnc-price-import.hpp" +#include "gnc-price-props.hpp" +#include "gnc-csv-tokenizer.hpp" +#include "gnc-fw-tokenizer.hpp" +#include "gnc-csv-trans-settings.hpp" + +G_GNUC_UNUSED static QofLogModule log_module = GNC_MOD_IMPORT; + +const int num_date_formats_price = 5; +const gchar* date_format_user_price[] = {N_("y-m-d"), + N_("d-m-y"), + N_("m-d-y"), + N_("d-m"), + N_("m-d") + }; + +const int num_currency_formats_price = 3; +const gchar* currency_format_user_price[] = {N_("Locale"), + N_("Period: 123,456.78"), + N_("Comma: 123.456,78") + }; + + +/** Constructor for GncPriceImport. + * @return Pointer to a new GncCSvParseData + */ +GncPriceImport::GncPriceImport(GncImpFileFormat format) +{ + /* All of the data pointers are initially NULL. This is so that, if + * gnc_csv_parse_data_free is called before all of the data is + * initialized, only the data that needs to be freed is freed. */ + m_skip_errors = false; + file_format(m_settings.m_file_format = format); +} + +/** Destructor for GncPriceImport. + */ +GncPriceImport::~GncPriceImport() +{ +} + +/** Sets the file format for the file to import, which + * may cause the file to be reloaded as well if the + * previously set file format was different and a + * filename was already set. + * @param format the new format to set + * @exception std::ifstream::failure if file reloading fails + */ +void GncPriceImport::file_format(GncImpFileFormat format) +{ + if (m_tokenizer && m_settings.m_file_format == format) + return; + + auto new_encoding = std::string("UTF-8"); + auto new_imp_file = std::string(); + + // Recover common settings from old tokenizer + if (m_tokenizer) + { + new_encoding = m_tokenizer->encoding(); + new_imp_file = m_tokenizer->current_file(); + if (file_format() == GncImpFileFormat::FIXED_WIDTH) + { + auto fwtok = dynamic_cast(m_tokenizer.get()); + if (!fwtok->get_columns().empty()) + m_settings.m_column_widths = fwtok->get_columns(); + } + } + + m_settings.m_file_format = format; + m_tokenizer = gnc_tokenizer_factory(m_settings.m_file_format); + + // Set up new tokenizer with common settings + // recovered from old tokenizer + m_tokenizer->encoding(new_encoding); + load_file(new_imp_file); + + // Restore potentially previously set separators or column_widths + if ((file_format() == GncImpFileFormat::CSV) + && !m_settings.m_separators.empty()) + separators (m_settings.m_separators); + else if ((file_format() == GncImpFileFormat::FIXED_WIDTH) + && !m_settings.m_column_widths.empty()) + { + auto fwtok = dynamic_cast(m_tokenizer.get()); + fwtok->columns (m_settings.m_column_widths); + } + +} + +GncImpFileFormat GncPriceImport::file_format() +{ + return m_settings.m_file_format; +} + +void GncPriceImport::over_write (bool over) +{ + m_over_write = over; +} + +bool GncPriceImport::over_write () { return m_over_write; } + +void GncPriceImport::reset_formatted_column (std::vector& col_types) +{ + for (auto col_type: col_types) + { + auto col = std::find (m_settings.m_column_types_price.begin(), + m_settings.m_column_types_price.end(), col_type); + if (col != m_settings.m_column_types_price.end()) + set_column_type_price (col - m_settings.m_column_types_price.begin(), col_type, true); + } +} + +void GncPriceImport::currency_format (int currency_format) +{ + m_settings.m_currency_format = currency_format; + + /* Reparse all currency related columns */ + std::vector commodities = { GncPricePropType::AMOUNT }; + reset_formatted_column (commodities); +} +int GncPriceImport::currency_format () { return m_settings.m_currency_format; } + +void GncPriceImport::date_format (int date_format) +{ + m_settings.m_date_format = date_format; + + /* Reparse all date related columns */ + std::vector dates = { GncPricePropType::DATE }; + reset_formatted_column (dates); +} +int GncPriceImport::date_format () { return m_settings.m_date_format; } + +/** Converts raw file data using a new encoding. This function must be + * called after load_file only if load_file guessed + * the wrong encoding. + * @param encoding Encoding that data should be translated using + */ +void GncPriceImport::encoding (const std::string& encoding) +{ + + // TODO investigate if we can catch conversion errors and report them + if (m_tokenizer) + { + m_tokenizer->encoding(encoding); // May throw + try + { + tokenize(false); + } + catch (...) + { }; + } + + m_settings.m_encoding = encoding; +} + +std::string GncPriceImport::encoding () { return m_settings.m_encoding; } + +void GncPriceImport::update_skipped_lines(boost::optional start, boost::optional end, + boost::optional alt, boost::optional errors) +{ + if (start) + m_settings.m_skip_start_lines = *start; + if (end) + m_settings.m_skip_end_lines = *end; + if (alt) + m_settings.m_skip_alt_lines = *alt; + if (errors) + m_skip_errors = *errors; + + for (uint32_t i = 0; i < m_parsed_lines.size(); i++) + { + std::get<3>(m_parsed_lines[i]) = + ((i < skip_start_lines()) || // start rows to skip + (i >= m_parsed_lines.size() - skip_end_lines()) || // end rows to skip + (((i - skip_start_lines()) % 2 == 1) && // skip every second row... + skip_alt_lines()) || // ...if requested + (m_skip_errors && !std::get<1>(m_parsed_lines[i]).empty())); // skip lines with errors + } +} + +uint32_t GncPriceImport::skip_start_lines () { return m_settings.m_skip_start_lines; } +uint32_t GncPriceImport::skip_end_lines () { return m_settings.m_skip_end_lines; } +bool GncPriceImport::skip_alt_lines () { return m_settings.m_skip_alt_lines; } +bool GncPriceImport::skip_err_lines () { return m_skip_errors; } + +void GncPriceImport::separators (std::string separators) +{ + if (file_format() != GncImpFileFormat::CSV) + return; + + m_settings.m_separators = separators; + auto csvtok = dynamic_cast(m_tokenizer.get()); + csvtok->set_separators (separators); + +} +std::string GncPriceImport::separators () { return m_settings.m_separators; } + +void GncPriceImport::settings (const CsvTransSettings& settings) +{ + /* First apply file format as this may recreate the tokenizer */ + file_format (settings.m_file_format); + /* Only then apply the other settings */ + m_settings = settings; + encoding (m_settings.m_encoding); + + if (file_format() == GncImpFileFormat::CSV) + separators (m_settings.m_separators); + else if (file_format() == GncImpFileFormat::FIXED_WIDTH) + { + auto fwtok = dynamic_cast(m_tokenizer.get()); + fwtok->columns (m_settings.m_column_widths); + } + try + { + tokenize(false); + } + catch (...) + { }; + + /* Tokenizing will clear column types, reset them here + * based on the loaded settings. + */ + std::copy_n (settings.m_column_types_price.begin(), + std::min (m_settings.m_column_types_price.size(), settings.m_column_types_price.size()), + m_settings.m_column_types_price.begin()); + +} + +bool GncPriceImport::save_settings () +{ + + if (trans_preset_is_reserved_name (m_settings.m_name)) + return true; + + /* separators are already copied to m_settings in the separators + * function above. However this is not the case for the column + * widths in fw mode, so do this now. + */ + if (file_format() == GncImpFileFormat::FIXED_WIDTH) + { + auto fwtok = dynamic_cast(m_tokenizer.get()); + m_settings.m_column_widths = fwtok->get_columns(); + } + + return m_settings.save(); +} + +void GncPriceImport::settings_name (std::string name) { m_settings.m_name = name; } +std::string GncPriceImport::settings_name () { return m_settings.m_name; } + +/** Loads a file into a GncPriceImport. This is the first function + * that must be called after creating a new GncPriceImport. As long as + * this function didn't run successfully, the importer can't proceed. + * @param filename Name of the file that should be opened + * @exception may throw std::ifstream::failure on any io error + */ +void GncPriceImport::load_file (const std::string& filename) +{ + + /* Get the raw data first and handle an error if one occurs. */ + try + { + m_tokenizer->load_file (filename); + return; + } + catch (std::ifstream::failure& ios_err) + { + // Just log the error and pass it on the call stack for proper handling + PWARN ("Error: %s", ios_err.what()); + throw; + } +} + +/** Splits a file into cells. This requires having an encoding that + * works (see GncPriceImport::convert_encoding). Tokenizing related options + * should be set to the user's selections before calling this + * function. + * Notes: - this function must be called with guessColTypes set to true once + * before calling it with guessColTypes set to false. + * - if guessColTypes is true, all the column types will be set + * GncPricePropType::NONE right now as real guessing isn't implemented yet + * @param guessColTypes true to guess what the types of columns are based on the cell contents + * @exception std::range_error if tokenizing failed + */ +void GncPriceImport::tokenize (bool guessColTypes) +{ + if (!m_tokenizer) + return; + + uint32_t max_cols = 0; + m_tokenizer->tokenize(); + m_parsed_lines.clear(); + for (auto tokenized_line : m_tokenizer->get_tokens()) + { + m_parsed_lines.push_back (std::make_tuple (tokenized_line, std::string(), + std::make_shared(date_format(), currency_format()), + false)); + auto length = tokenized_line.size(); + if (length > max_cols) + max_cols = length; + } + + /* If it failed, generate an error. */ + if (m_parsed_lines.size() == 0) + { + throw (std::range_error ("Tokenizing failed.")); + return; + } + + m_settings.m_column_types_price.resize(max_cols, GncPricePropType::NONE); + + /* Force reinterpretation of already set columns and/or base_account */ + for (uint32_t i = 0; i < m_settings.m_column_types_price.size(); i++) + set_column_type_price (i, m_settings.m_column_types_price[i], true); + + if (guessColTypes) + { + /* Guess column_types based + * on the contents of each column. */ + /* TODO Make it actually guess. */ + } +} + + +struct ErrorListPrice +{ +public: + void add_error (std::string msg); + std::string str(); + bool empty() { return m_error.empty(); } +private: + std::string m_error; +}; + +void ErrorListPrice::add_error (std::string msg) +{ + m_error += "- " + msg + "\n"; +} + +std::string ErrorListPrice::str() +{ + return m_error.substr(0, m_error.size() - 1); +} + + +/* Test for the required minimum number of columns selected and + * the selection is consistent. + * @param An ErrorListPrice object to which all found issues are added. + */ +void GncPriceImport::verify_column_selections (ErrorListPrice& error_msg) +{ + /* Verify if a date column is selected and it's parsable. + */ + if (!check_for_column_type(GncPricePropType::DATE)) + error_msg.add_error( _("Please select a date column.")); + + /* Verify an amount column is selected. + */ + if (!check_for_column_type(GncPricePropType::AMOUNT)) + error_msg.add_error( _("Please select an amount column.")); + + /* Verify an Currency to column is selected. + */ + if (!check_for_column_type(GncPricePropType::CURRENCY_TO)) + error_msg.add_error( _("Please select a Currency to column.")); + + /* Verify at least one from column (symbol_from or currency_from) column is selected. + */ + if (!check_for_column_type(GncPricePropType::SYMBOL_FROM) && + !check_for_column_type(GncPricePropType::CURRENCY_FROM)) + error_msg.add_error( _("Please select a symbol or currency from column.")); +} + + +/* Check whether the chosen settings can successfully parse + * the import data. This will check: + * - there's at least one line selected for import + * - the minimum number of columns is selected + * - the values in the selected columns can be parsed meaningfully. + * @return An empty string if all checks passed or the reason + * verification failed otherwise. + */ +std::string GncPriceImport::verify () +{ + auto newline = std::string(); + auto error_msg = ErrorListPrice(); + + /* Check if the import file did actually contain any information */ + if (m_parsed_lines.size() == 0) + { + error_msg.add_error(_("No valid data found in the selected file. It may be empty or the selected encoding is wrong.")); + return error_msg.str(); + } + + /* Check if at least one line is selected for importing */ + auto skip_alt_offset = m_settings.m_skip_alt_lines ? 1 : 0; + if (m_settings.m_skip_start_lines + m_settings.m_skip_end_lines + skip_alt_offset >= m_parsed_lines.size()) + { + error_msg.add_error(_("No lines are selected for importing. Please reduce the number of lines to skip.")); + return error_msg.str(); + } + + verify_column_selections (error_msg); + + update_skipped_lines (boost::none, boost::none, boost::none, boost::none); + + auto have_line_errors = false; + for (auto line : m_parsed_lines) + { + if (!std::get<3>(line) && !std::get<1>(line).empty()) + { + have_line_errors = true; + break; + } + } + + if (have_line_errors) + error_msg.add_error( _("Not all fields could be parsed. Please correct the issues reported for each line or adjust the lines to skip.")); + + return error_msg.str(); +} + +/** Checks whether the parsed line contains all essential properties. + * @param parsed_line The line we are checking + * @exception std::invalid_argument in an essential property is missing + */ +static void price_properties_verify_essentials (std::vector::iterator& parsed_line) +{ + std::string error_message; + std::shared_ptr price_props; + std::tie(std::ignore, error_message, price_props, std::ignore) = *parsed_line; + + auto price_error = price_props->verify_essentials(); + + error_message.clear(); + if (!price_error.empty()) + { + error_message += price_error; + error_message += "\n"; + } + + if (!error_message.empty()) + throw std::invalid_argument(error_message); +} + +void GncPriceImport::create_price (std::vector::iterator& parsed_line) +{ + StrVec line; + std::string error_message; + std::shared_ptr price_props = nullptr; + bool skip_line = false; + std::tie(line, error_message, price_props, skip_line) = *parsed_line; + + if (skip_line) + return; + + error_message.clear(); + + /* If column parsing was successful, convert price properties into a price. */ + try + { + price_properties_verify_essentials (parsed_line); + + QofBook* book = gnc_get_current_book(); + GNCPriceDB *pdb = gnc_pricedb_get_db (book); + + /* If all went well, add this price to the list. */ + auto price_created = price_props->create_price (book, pdb, m_over_write); +//FIXME Need to look at this + if (price_created) + m_prices_added++; + else + m_prices_duplicated++; + } + catch (const std::invalid_argument& e) + { + error_message = e.what(); + PINFO("User warning: %s", error_message.c_str()); + } +} + + +/** Creates a list of prices from parsed data. The parsed data + * will first be validated. If any errors are found in lines that are marked + * for processing (ie not marked to skip) this function will + * throw an error. + * @param skip_errors true skip over lines with errors + * @exception throws std::invalid_argument if data validation or processing fails. + */ +void GncPriceImport::create_prices () +{ + /* Start with verifying the current data. */ + auto verify_result = verify(); + if (!verify_result.empty()) + throw std::invalid_argument (verify_result); + + m_prices_added = 0; + m_prices_duplicated = 0; + + /* Iterate over all parsed lines */ + for (auto parsed_lines_it = m_parsed_lines.begin(); + parsed_lines_it != m_parsed_lines.end(); + ++parsed_lines_it) + { + /* Skip current line if the user specified so */ + if ((std::get<3>(*parsed_lines_it))) + continue; + + /* Should not throw anymore, otherwise verify needs revision */ + create_price (parsed_lines_it); + } + PINFO("Number of lines is %d, added is %d, duplicates is %d", + (int)m_parsed_lines.size(), m_prices_added, m_prices_duplicated); +} + +bool +GncPriceImport::check_for_column_type (GncPricePropType type) +{ + return (std::find (m_settings.m_column_types_price.begin(), + m_settings.m_column_types_price.end(), type) + != m_settings.m_column_types_price.end()); +} + +/* A helper function intended to be called only from set_column_type_price */ +void GncPriceImport::update_price_props (uint32_t row, uint32_t col, GncPricePropType prop_type) +{ + if (prop_type == GncPricePropType::NONE) + return; /* Only deal with price related properties. */ + + auto price_props = std::make_shared (*(std::get<2>(m_parsed_lines[row])).get()); + auto value = std::string(); + + if (col < std::get<0>(m_parsed_lines[row]).size()) + value = std::get<0>(m_parsed_lines[row]).at(col); + + if (value.empty()) + price_props->reset (prop_type); + else + { + try + { + price_props->set(prop_type, value); + } + catch (const std::exception& e) + { + /* Do nothing, just prevent the exception from escalating up + * However log the error if it happens on a row that's not skipped + */ + if (!std::get<3>(m_parsed_lines[row])) + PINFO("User warning: %s", e.what()); + } + } + /* Store the result */ + std::get<2>(m_parsed_lines[row]) = price_props; +} + +void +GncPriceImport::set_column_type_price (uint32_t position, GncPricePropType type, bool force) +{ + if (position >= m_settings.m_column_types_price.size()) + return; + + auto old_type = m_settings.m_column_types_price[position]; + if ((type == old_type) && !force) + return; /* Nothing to do */ + + // Column types should be unique, so remove any previous occurrence of the new type + std::replace(m_settings.m_column_types_price.begin(), m_settings.m_column_types_price.end(), + type, GncPricePropType::NONE); + + m_settings.m_column_types_price.at (position) = type; + + /* Update the preparsed data */ + for (auto parsed_lines_it = m_parsed_lines.begin(); + parsed_lines_it != m_parsed_lines.end(); + ++parsed_lines_it) + { + /* Reset date and currency formats for each price props object + * to ensure column updates use the most recent one + */ + std::get<2>(*parsed_lines_it)->set_date_format (m_settings.m_date_format); + std::get<2>(*parsed_lines_it)->set_currency_format (m_settings.m_currency_format); + + uint32_t row = parsed_lines_it - m_parsed_lines.begin(); + + /* If the column type actually changed, first reset the property + * represented by the old column type + */ + if (old_type != type) + { + auto old_col = std::get<0>(*parsed_lines_it).size(); // Deliberately out of bounds to trigger a reset! + if ((old_type > GncPricePropType::NONE) + && (old_type <= GncPricePropType::PRICE_PROPS)) + update_price_props (row, old_col, old_type); + } + /* Then set the property represented by the new column type */ + if ((type > GncPricePropType::NONE) + && (type <= GncPricePropType::PRICE_PROPS)) + update_price_props (row, position, type); + + /* Report errors if there are any */ + auto price_errors = std::get<2>(*parsed_lines_it)->errors(); + std::get<1>(*parsed_lines_it) = + price_errors + + (price_errors.empty() ? std::string() : "\n"); + } +} + +std::vector GncPriceImport::column_types_price () +{ + return m_settings.m_column_types_price; +} + diff --git a/gnucash/import-export/csv-imp/gnc-price-import.hpp b/gnucash/import-export/csv-imp/gnc-price-import.hpp new file mode 100644 index 0000000000..0898215b29 --- /dev/null +++ b/gnucash/import-export/csv-imp/gnc-price-import.hpp @@ -0,0 +1,160 @@ +/********************************************************************\ + * gnc-price-import.hpp - import prices from csv files * + * * + * 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 * +\********************************************************************/ + +/** @file + @brief Class to import prices from CSV or fixed width files + * + gnc-price-import.hpp + @author Copyright (c) 2015 Geert Janssens + @author Copyright (c) 2017 Robert Fewell + */ + +#ifndef GNC_PRICE_IMPORT_HPP +#define GNC_PRICE_IMPORT_HPP + +extern "C" { +#include "config.h" + +} + +#include +#include +#include +#include + +#include "gnc-tokenizer.hpp" +#include "gnc-price-props.hpp" +#include "gnc-csv-trans-settings.hpp" +#include + +/* A set of currency formats that the user sees. */ +extern const int num_currency_formats; +extern const gchar* currency_format_user[]; + +/* A set of date formats that the user sees. */ +extern const int num_date_formats; +extern const gchar* date_format_user[]; + +/** Tuple to hold + * - a tokenized line of input + * - an optional error string + * - a struct to hold user selected properties for a price */ +using parse_line_t = std::tuple, + bool>; +struct ErrorListPrice; + +/** The actual PriceImport class + * It's intended to use in the following sequence of actions: + * - set a file format + * - load a file + * - optionally convert it's encoding + * - parse the file into lines, which in turn are split up in columns + * the result of this step can be queried from tokenizer + * - the user should now map the columns to types, which is stored in column_types + * - last step is convert the mapped columns into a list of transactions + * - this list will then be passed on the the generic importer for further processing */ +class GncPriceImport +{ +public: + // Constructor - Destructor + GncPriceImport(GncImpFileFormat format = GncImpFileFormat::UNKNOWN); + ~GncPriceImport(); + + void file_format(GncImpFileFormat format); + GncImpFileFormat file_format(); + + void over_write (bool over); + bool over_write (); + + void currency_format (int currency_format); + int currency_format (); + + void date_format (int date_format); + int date_format (); + + void encoding (const std::string& encoding); + std::string encoding (); + + void update_skipped_lines (boost::optional start, boost::optional end, + boost::optional alt, boost::optional errors); + uint32_t skip_start_lines (); + uint32_t skip_end_lines (); + bool skip_alt_lines (); + bool skip_err_lines (); + + void separators (std::string separators); + std::string separators (); + + void settings (const CsvTransSettings& settings); + bool save_settings (); + + void settings_name (std::string name); + std::string settings_name (); + + + void load_file (const std::string& filename); + + void tokenize (bool guessColTypes); + + std::string verify(); + + /** This function will attempt to convert all tokenized lines into + * prices using the column types the user has set. + */ + void create_prices (); + bool check_for_column_type (GncPricePropType type); + void set_column_type_price (uint32_t position, GncPricePropType type, bool force = false); + std::vector column_types_price (); + + std::unique_ptr m_tokenizer; /**< Will handle file loading/encoding conversion/splitting into fields */ + std::vector m_parsed_lines; /**< source file parsed into a two-dimensional array of strings. + Per line also holds possible error messages and objects with extracted + price properties. */ + int m_prices_added; + int m_prices_duplicated; + +private: + /** A helper function used by create_prices. It will attempt + * to convert a single tokenized line into a price using + * the column types the user has set. + */ + void create_price (std::vector::iterator& parsed_line); + + void verify_column_selections (ErrorListPrice& error_msg); + + /* Internal helper function to force reparsing of columns subject to format changes */ + void reset_formatted_column (std::vector& col_types); + + /* Two internal helper functions that should only be called from within + * set_column_type_price for consistency (otherwise error messages may not be (re)set) + */ + void update_price_props (uint32_t row, uint32_t col, GncPricePropType prop_type); + + struct CsvTranSettings; + CsvTransSettings m_settings; + bool m_skip_errors; + bool m_over_write; +}; + + +#endif diff --git a/gnucash/import-export/csv-imp/gnc-price-props.hpp b/gnucash/import-export/csv-imp/gnc-price-props.hpp index 6f1189be1a..d1012805f1 100644 --- a/gnucash/import-export/csv-imp/gnc-price-props.hpp +++ b/gnucash/import-export/csv-imp/gnc-price-props.hpp @@ -31,7 +31,7 @@ extern "C" { #endif #include - +#include "gnc-pricedb.h" #include "gnc-commodity.h" } From 404bc1e329bddb6726cab3577f98c49ef7294ffa Mon Sep 17 00:00:00 2001 From: Robert Fewell <14uBobIT@gmail.com> Date: Fri, 1 Dec 2017 10:55:29 +0000 Subject: [PATCH 25/48] Rename function gnc_csv_price_col_type_strs to gnc_price_col_type_strs --- gnucash/import-export/csv-imp/gnc-price-props.cpp | 6 +++--- gnucash/import-export/csv-imp/gnc-price-props.hpp | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/gnucash/import-export/csv-imp/gnc-price-props.cpp b/gnucash/import-export/csv-imp/gnc-price-props.cpp index 1fdcaa53a3..a39f178851 100644 --- a/gnucash/import-export/csv-imp/gnc-price-props.cpp +++ b/gnucash/import-export/csv-imp/gnc-price-props.cpp @@ -44,7 +44,7 @@ extern "C" { G_GNUC_UNUSED static QofLogModule log_module = GNC_MOD_IMPORT; /* This map contains a set of strings representing the different column types. */ -std::map gnc_csv_price_col_type_strs = { +std::map gnc_price_col_type_strs = { { GncPricePropType::NONE, N_("None") }, { GncPricePropType::DATE, N_("Date") }, { GncPricePropType::AMOUNT, N_("Amount") }, @@ -341,7 +341,7 @@ void GncImportPrice::set (GncPricePropType prop_type, const std::string& value) } catch (const std::invalid_argument& e) { - auto err_str = std::string(_(gnc_csv_price_col_type_strs[prop_type])) + + auto err_str = std::string(_(gnc_price_col_type_strs[prop_type])) + std::string(_(" could not be understood.\n")) + e.what(); m_errors.emplace(prop_type, err_str); @@ -349,7 +349,7 @@ void GncImportPrice::set (GncPricePropType prop_type, const std::string& value) } catch (const std::out_of_range& e) { - auto err_str = std::string(_(gnc_csv_price_col_type_strs[prop_type])) + + auto err_str = std::string(_(gnc_price_col_type_strs[prop_type])) + std::string(_(" could not be understood.\n")) + e.what(); m_errors.emplace(prop_type, err_str); diff --git a/gnucash/import-export/csv-imp/gnc-price-props.hpp b/gnucash/import-export/csv-imp/gnc-price-props.hpp index d1012805f1..57083e9a4b 100644 --- a/gnucash/import-export/csv-imp/gnc-price-props.hpp +++ b/gnucash/import-export/csv-imp/gnc-price-props.hpp @@ -56,10 +56,10 @@ enum class GncPricePropType { }; /** Maps all column types to a string representation. - * The actual definition is in gnc-csv-imp-prices.cpp. + * The actual definition is in gnc-price-props.cpp. * Attention: that definition should be adjusted for any * changes to enum class GncPricePropType ! */ -extern std::map gnc_csv_price_col_type_strs; +extern std::map gnc_price_col_type_strs; /** Functor to check if the above map has an element of which * the value equals name. To be used with std::find_if. From 3cfa9d05bad517db632679ffa6127dd34066d58e Mon Sep 17 00:00:00 2001 From: Robert Fewell <14uBobIT@gmail.com> Date: Fri, 1 Dec 2017 11:01:16 +0000 Subject: [PATCH 26/48] Add CSV Price importer assistant files These file are largely based on the csv transaction importer. They are just the start for subsequent changes. --- gnucash/import-export/csv-imp/CMakeLists.txt | 4 +- gnucash/import-export/csv-imp/Makefile.am | 3 + .../csv-imp/assistant-csv-price-import.cpp | 1621 +++++++++++++++++ .../csv-imp/assistant-csv-price-import.glade | 1001 ++++++++++ .../csv-imp/assistant-csv-price-import.h | 36 + .../csv-imp/gnc-plugin-csv-import-ui.xml | 1 + .../csv-imp/gnc-plugin-csv-import.c | 14 + 7 files changed, 2679 insertions(+), 1 deletion(-) create mode 100644 gnucash/import-export/csv-imp/assistant-csv-price-import.cpp create mode 100644 gnucash/import-export/csv-imp/assistant-csv-price-import.glade create mode 100644 gnucash/import-export/csv-imp/assistant-csv-price-import.h diff --git a/gnucash/import-export/csv-imp/CMakeLists.txt b/gnucash/import-export/csv-imp/CMakeLists.txt index 0bc7d2938f..44a14cacec 100644 --- a/gnucash/import-export/csv-imp/CMakeLists.txt +++ b/gnucash/import-export/csv-imp/CMakeLists.txt @@ -10,6 +10,7 @@ SET(csv_import_remote_SOURCES SET(csv_import_SOURCES gncmod-csv-import.c assistant-csv-account-import.c + assistant-csv-price-import.cpp assistant-csv-trans-import.cpp gnc-plugin-csv-import.c csv-account-import.c @@ -37,6 +38,7 @@ SET(csv_import_remote_HEADERS SET(csv_import_noinst_HEADERS assistant-csv-account-import.h + assistant-csv-price-import.h assistant-csv-trans-import.h gnc-plugin-csv-import.h csv-account-import.h @@ -88,7 +90,7 @@ INSTALL(TARGETS gncmod-csv-import # No headers to install SET(csv_import_GLADE assistant-csv-account-import.glade - assistant-csv-trans-import.glade) + assistant-csv-price-import.glade assistant-csv-trans-import.glade) INSTALL(FILES ${csv_import_GLADE} DESTINATION ${CMAKE_INSTALL_DATADIR}/gnucash/gtkbuilder) diff --git a/gnucash/import-export/csv-imp/Makefile.am b/gnucash/import-export/csv-imp/Makefile.am index 408396331a..4b473de869 100644 --- a/gnucash/import-export/csv-imp/Makefile.am +++ b/gnucash/import-export/csv-imp/Makefile.am @@ -5,6 +5,7 @@ pkglib_LTLIBRARIES=libgncmod-csv-import.la libgncmod_csv_import_la_SOURCES = \ gncmod-csv-import.c \ assistant-csv-account-import.c \ + assistant-csv-price-import.cpp \ assistant-csv-trans-import.cpp \ gnc-plugin-csv-import.c \ csv-account-import.c \ @@ -22,6 +23,7 @@ libgncmod_csv_import_la_SOURCES = \ noinst_HEADERS = \ assistant-csv-account-import.h \ + assistant-csv-price-import.h \ assistant-csv-trans-import.h \ gnc-plugin-csv-import.h \ csv-account-import.h \ @@ -79,6 +81,7 @@ ui_DATA = \ gtkbuilderdir = ${GNC_GTKBUILDER_DIR} gtkbuilder_DATA = \ assistant-csv-account-import.glade \ + assistant-csv-price-import.glade \ assistant-csv-trans-import.glade EXTRA_DIST = $(ui_DATA) $(gtkbuilder_DATA) CMakeLists.txt diff --git a/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp b/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp new file mode 100644 index 0000000000..a6ba349ff4 --- /dev/null +++ b/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp @@ -0,0 +1,1621 @@ +/*******************************************************************\ + * assistant-csv-price-import.c -- An assistant for importing * + * Prices from a file. * + * * + * Copyright (C) 2017 Robert Fewell * + * * + * 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 * +\********************************************************************/ +/** @file assistant-csv-price-import.cpp + @brief CSV Import Assistant + @author Copyright (c) 2016 Geert Janssens + @author Copyright (c) 2017 Robert Fewell +*/ + +#include + +extern "C" +{ +#include "config.h" + +#include +#include +#include + +#include "gnc-ui.h" +#include "gnc-uri-utils.h" +#include "gnc-ui-util.h" +#include "dialog-utils.h" + +#include "gnc-component-manager.h" + +#include "gnc-state.h" + +#include "assistant-csv-price-import.h" + +#include "gnc-csv-gnumeric-popup.h" +#include "go-charmap-sel.h" +} + +#include "gnc-csv-trans-settings.hpp" +#include "gnc-price-import.hpp" +#include "gnc-fw-tokenizer.hpp" +#include "gnc-csv-tokenizer.hpp" + +#define MIN_COL_WIDTH 70 +#define GNC_PREFS_GROUP "dialogs.import.csv" +#define ASSISTANT_CSV_IMPORT_PRICE_CM_CLASS "assistant-csv-price-import" + +/* This static indicates the debugging module that this .o belongs to. */ +static QofLogModule log_module = GNC_MOD_ASSISTANT; + +class CsvImpPriceAssist +{ +public: + CsvImpPriceAssist (); + + void assist_prepare_cb (GtkWidget *page); + void assist_file_page_prepare (); + void assist_preview_page_prepare (); + void assist_confirm_page_prepare (); + void assist_summary_page_prepare (); + void assist_finish (bool canceled); + void assist_compmgr_close (); + + void file_confirm_cb (); + + void preview_settings_delete (); + void preview_settings_save (); + void preview_settings_name (GtkEntry* entry); + void preview_settings_load (); + void preview_update_skipped_rows (); + void preview_over_write (bool over); + void preview_update_separators (GtkWidget* widget); + void preview_update_file_format (); + void preview_update_account (); + void preview_update_encoding (const char* encoding); + void preview_update_date_format (); + void preview_update_currency_format (); + void preview_update_col_type (GtkComboBox* cbox); + void preview_update_fw_columns (GtkTreeView* treeview, GdkEventButton* event); + + void preview_populate_settings_combo(); + void preview_handle_save_del_sensitivity (GtkComboBox* combo); + void preview_split_column (int col, int offset); + void preview_refresh_table (); + void preview_refresh (); + void preview_validate_settings (); + + friend gboolean + fixed_context_menu_handler_price (GnumericPopupMenuElement const *element, + gpointer userdata); +private: + /* helper functions to manage the context menu for fixed with columns */ + uint32_t get_new_col_rel_pos (GtkTreeViewColumn *tcol, int dx); + void fixed_context_menu (GdkEventButton *event, int col, int dx); + /* helper function to calculate row colors for the preview table (to visualize status) */ + void preview_row_fill_state_cells (GtkListStore *store, GtkTreeIter *iter, + std::string& err_msg, bool skip); + /* helper function to create preview header cell combo boxes listing available column types */ + GtkWidget* preview_cbox_factory (GtkTreeModel* model, uint32_t colnum); + /* helper function to set rendering parameters for preview data columns */ + void preview_style_column (uint32_t col_num, GtkTreeModel* model); + + GtkAssistant *csv_imp_asst; + + GtkWidget *file_page; /**< Assistant file page widget */ + GtkWidget *file_chooser; /**< The widget for the file chooser */ + std::string m_file_name; /**< The import file name */ + + GtkWidget *preview_page; /**< Assistant preview page widget */ + GtkComboBox *settings_combo; /**< The Settings Combo */ + GtkWidget *save_button; /**< The Save Settings button */ + GtkWidget *del_button; /**< The Delete Settings button */ + + GtkWidget *combo_hbox; /**< The Settings Combo hbox */ + GtkSpinButton *start_row_spin; /**< The widget for the start row spinner */ + GtkSpinButton *end_row_spin; /**< The widget for the end row spinner */ + GtkWidget *skip_alt_rows_button; /**< The widget for Skip alternate rows from start row */ + GtkWidget *skip_errors_button; /**< The widget for Skip error rows*/ + GtkWidget *csv_button; /**< The widget for the CSV button */ + GtkWidget *fixed_button; /**< The widget for the Fixed Width button */ + GtkWidget *over_write_cbutton; /**< The widget for Price Over Write */ + GOCharmapSel *encselector; /**< The widget for selecting the encoding */ + GtkWidget *separator_table; /**< Container for the separator checkboxes */ + GtkCheckButton *sep_button[SEP_NUM_OF_TYPES]; /**< Checkbuttons for common separators */ + GtkWidget *fw_instructions_hbox; /**< Container for fixed-width instructions */ + GtkCheckButton *custom_cbutton; /**< The checkbutton for a custom separator */ + GtkEntry *custom_entry; /**< The entry for custom separators */ + GtkComboBoxText *date_format_combo; /**< The Combo Text widget for selecting the date format */ + GtkComboBoxText *currency_format_combo; /**< The Combo Text widget for selecting the currency format */ + GtkTreeView *treeview; /**< The treeview containing the data */ + GtkLabel *instructions_label; /**< The instructions label */ + GtkImage *instructions_image; /**< The instructions image */ + bool encoding_selected_called; /**< Before encoding_selected is first called, this is false. + * error lines, instead of all the file data. */ + int fixed_context_col; /**< The number of the column the user has clicked */ + int fixed_context_offset; /**< The offset (in characters) in the column + * the user has clicked */ + + GtkWidget *confirm_page; /**< Assistant confirm page widget */ + + GtkWidget *summary_page; /**< Assistant summary page widget */ + GtkWidget *summary_label; /**< The summary text */ + + std::unique_ptr price_imp; /**< The actual data we are previewing */ +}; + + +/******************************************************* + * Assistant call back functions + *******************************************************/ + +extern "C" +{ +void csv_price_imp_assist_prepare_cb (GtkAssistant *assistant, GtkWidget *page, CsvImpPriceAssist* info); +void csv_price_imp_assist_destroy_cb (GtkWidget *object, CsvImpPriceAssist* info); +void csv_price_imp_assist_cancel_cb (GtkAssistant *gtkassistant, CsvImpPriceAssist* info); +void csv_price_imp_assist_close_cb (GtkAssistant *gtkassistant, CsvImpPriceAssist* info); +void csv_price_imp_assist_finish_cb (GtkAssistant *gtkassistant, CsvImpPriceAssist* info); +void csv_price_imp_file_confirm_cb (GtkWidget *button, CsvImpPriceAssist *info); +void csv_price_imp_preview_del_settings_cb (GtkWidget *button, CsvImpPriceAssist *info); +void csv_price_imp_preview_save_settings_cb (GtkWidget *button, CsvImpPriceAssist *info); +void csv_price_imp_preview_settings_sel_changed_cb (GtkComboBox *combo, CsvImpPriceAssist *info); +void csv_price_imp_preview_settings_text_inserted_cb (GtkEditable *entry, gchar *new_text, + gint new_text_length, gint *position, CsvImpPriceAssist *info); +void csv_price_imp_preview_settings_text_changed_cb (GtkEntry *entry, CsvImpPriceAssist *info); +void csv_price_imp_preview_srow_cb (GtkSpinButton *spin, CsvImpPriceAssist *info); +void csv_price_imp_preview_erow_cb (GtkSpinButton *spin, CsvImpPriceAssist *info); +void csv_price_imp_preview_skiprows_cb (GtkToggleButton *checkbox, CsvImpPriceAssist *info); +void csv_price_imp_preview_skiperrors_cb (GtkToggleButton *checkbox, CsvImpPriceAssist *info); +void csv_price_imp_preview_overwrite_cb (GtkToggleButton *checkbox, CsvImpPriceAssist *info); +void csv_price_imp_preview_sep_button_cb (GtkWidget* widget, CsvImpPriceAssist* info); +void csv_price_imp_preview_sep_fixed_sel_cb (GtkToggleButton* csv_button, CsvImpPriceAssist* info); +void csv_price_imp_preview_acct_sel_cb (GtkWidget* widget, CsvImpPriceAssist* info); +void csv_price_imp_preview_enc_sel_cb (GOCharmapSel* selector, const char* encoding, + CsvImpPriceAssist* info); +} + +void +csv_price_imp_assist_prepare_cb (GtkAssistant *assistant, GtkWidget *page, + CsvImpPriceAssist* info) +{ + info->assist_prepare_cb(page); +} + +void +csv_price_imp_assist_destroy_cb (GtkWidget *object, CsvImpPriceAssist* info) +{ + gnc_unregister_gui_component_by_data (ASSISTANT_CSV_IMPORT_PRICE_CM_CLASS, info); + delete info; +} + +void +csv_price_imp_assist_cancel_cb (GtkAssistant *assistant, CsvImpPriceAssist* info) +{ + info->assist_finish (true); + gnc_close_gui_component_by_data (ASSISTANT_CSV_IMPORT_PRICE_CM_CLASS, info); +} + +void +csv_price_imp_assist_close_cb (GtkAssistant *assistant, CsvImpPriceAssist* info) +{ + gnc_close_gui_component_by_data (ASSISTANT_CSV_IMPORT_PRICE_CM_CLASS, info); +} + +void +csv_price_imp_assist_finish_cb (GtkAssistant *assistant, CsvImpPriceAssist* info) +{ + info->assist_finish (false); +} + +void csv_price_imp_file_confirm_cb (GtkWidget *button, CsvImpPriceAssist *info) +{ + info->file_confirm_cb(); +} + +void csv_price_imp_preview_del_settings_cb (GtkWidget *button, CsvImpPriceAssist *info) +{ + info->preview_settings_delete(); +} + +void csv_price_imp_preview_save_settings_cb (GtkWidget *button, CsvImpPriceAssist *info) +{ + info->preview_settings_save(); +} + +void csv_price_imp_preview_settings_sel_changed_cb (GtkComboBox *combo, CsvImpPriceAssist *info) +{ + info->preview_settings_load(); +} + +void +csv_price_imp_preview_settings_text_inserted_cb (GtkEditable *entry, gchar *new_text, + gint new_text_length, gint *position, CsvImpPriceAssist *info) +{ + if (!new_text) + return; + + /* Prevent entering [], which are invalid characters in key files */ + auto base_txt = std::string (new_text); + auto mod_txt = base_txt; + std::replace (mod_txt.begin(), mod_txt.end(), '[', '('); + std::replace (mod_txt.begin(), mod_txt.end(), ']', ')'); + if (base_txt == mod_txt) + return; + g_signal_handlers_block_by_func (entry, (gpointer) csv_price_imp_preview_settings_text_inserted_cb, info); + gtk_editable_insert_text (entry, mod_txt.c_str(), mod_txt.size() , position); + g_signal_handlers_unblock_by_func (entry, (gpointer) csv_price_imp_preview_settings_text_inserted_cb, info); + + g_signal_stop_emission_by_name (entry, "insert_text"); +} + +void +csv_price_imp_preview_settings_text_changed_cb (GtkEntry *entry, CsvImpPriceAssist *info) +{ + info->preview_settings_name(entry); +} + +void csv_price_imp_preview_srow_cb (GtkSpinButton *spin, CsvImpPriceAssist *info) +{ + info->preview_update_skipped_rows(); +} + +void csv_price_imp_preview_erow_cb (GtkSpinButton *spin, CsvImpPriceAssist *info) +{ + info->preview_update_skipped_rows(); +} + +void csv_price_imp_preview_skiprows_cb (GtkToggleButton *checkbox, CsvImpPriceAssist *info) +{ + info->preview_update_skipped_rows(); +} + +void csv_price_imp_preview_skiperrors_cb (GtkToggleButton *checkbox, CsvImpPriceAssist *info) +{ + info->preview_update_skipped_rows(); +} + +void csv_price_imp_preview_overwrite_cb (GtkToggleButton *checkbox, CsvImpPriceAssist *info) +{ + info->preview_over_write (gtk_toggle_button_get_active (checkbox)); +} + +void csv_price_imp_preview_sep_button_cb (GtkWidget* widget, CsvImpPriceAssist* info) +{ + info->preview_update_separators(widget); +} + +void csv_price_imp_preview_sep_fixed_sel_cb (GtkToggleButton* csv_button, CsvImpPriceAssist* info) +{ + info->preview_update_file_format(); +} + +void csv_price_imp_preview_acct_sel_cb (GtkWidget* widget, CsvImpPriceAssist* info) +{ + info->preview_update_account(); +} + +void csv_price_imp_preview_enc_sel_cb (GOCharmapSel* selector, const char* encoding, + CsvImpPriceAssist* info) +{ + info->preview_update_encoding(encoding); +} + +static void csv_price_imp_preview_date_fmt_sel_cb (GtkComboBox* format_selector, CsvImpPriceAssist* info) +{ + info->preview_update_date_format(); +} + +static void csv_price_imp_preview_currency_fmt_sel_cb (GtkComboBox* format_selector, CsvImpPriceAssist* info) +{ + info->preview_update_currency_format(); +} + +void csv_price_imp_preview_col_type_changed_cb (GtkComboBox* cbox, CsvImpPriceAssist* info) +{ + info->preview_update_col_type (cbox); +} + +gboolean +csv_price_imp_preview_treeview_clicked_cb (GtkTreeView* treeview, GdkEventButton* event, + CsvImpPriceAssist* info) +{ + info->preview_update_fw_columns(treeview, event); + return false; +} + + +/******************************************************* + * Assistant Constructor + *******************************************************/ +CsvImpPriceAssist::CsvImpPriceAssist () +{ + auto builder = gtk_builder_new(); + gnc_builder_add_from_file (builder , "assistant-csv-price-import.glade", "start_row_adj"); + gnc_builder_add_from_file (builder , "assistant-csv-price-import.glade", "end_row_adj"); + gnc_builder_add_from_file (builder , "assistant-csv-price-import.glade", "CSV Price Assistant"); + csv_imp_asst = GTK_ASSISTANT(gtk_builder_get_object (builder, "CSV Price Assistant")); + + /* Enable buttons on all page. */ + gtk_assistant_set_page_complete (csv_imp_asst, + GTK_WIDGET(gtk_builder_get_object (builder, "start_page")), + true); + gtk_assistant_set_page_complete (csv_imp_asst, + GTK_WIDGET(gtk_builder_get_object (builder, "file_page")), + false); + gtk_assistant_set_page_complete (csv_imp_asst, + GTK_WIDGET(gtk_builder_get_object (builder, "preview_page")), + false); + gtk_assistant_set_page_complete (csv_imp_asst, + GTK_WIDGET(gtk_builder_get_object (builder, "confirm_page")), + true); + gtk_assistant_set_page_complete (csv_imp_asst, + GTK_WIDGET(gtk_builder_get_object (builder, "summary_page")), + true); + + /* File chooser Page */ + file_page = GTK_WIDGET(gtk_builder_get_object (builder, "file_page")); + file_chooser = gtk_file_chooser_widget_new (GTK_FILE_CHOOSER_ACTION_OPEN); + g_signal_connect (G_OBJECT(file_chooser), "file-activated", + G_CALLBACK(csv_price_imp_file_confirm_cb), this); + auto button = gtk_button_new_from_stock (GTK_STOCK_OK); + gtk_widget_set_size_request (button, 100, -1); + gtk_widget_show (button); + auto h_box = gtk_hbox_new (TRUE, 0); + gtk_box_pack_start (GTK_BOX(h_box), button, FALSE, FALSE, 0); + gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER(file_chooser), h_box); + g_signal_connect (G_OBJECT(button), "clicked", + G_CALLBACK(csv_price_imp_file_confirm_cb), this); + + auto box = GTK_WIDGET(gtk_builder_get_object (builder, "file_page")); + gtk_box_pack_start (GTK_BOX(box), file_chooser, TRUE, TRUE, 6); + gtk_widget_show (file_chooser); + + /* Preview Settings Page */ + { + preview_page = GTK_WIDGET(gtk_builder_get_object (builder, "preview_page")); + + // Add Settings combo + auto settings_store = gtk_list_store_new (2, G_TYPE_POINTER, G_TYPE_STRING); + settings_combo = GTK_COMBO_BOX(gtk_combo_box_new_with_model_and_entry (GTK_TREE_MODEL(settings_store))); + gtk_combo_box_set_entry_text_column (GTK_COMBO_BOX(settings_combo), SET_NAME); + gtk_combo_box_set_active (GTK_COMBO_BOX(settings_combo), 0); + + combo_hbox = GTK_WIDGET(gtk_builder_get_object (builder, "combo_hbox")); + gtk_box_pack_start (GTK_BOX(combo_hbox), GTK_WIDGET(settings_combo), true, true, 6); + gtk_widget_show (GTK_WIDGET(settings_combo)); + + g_signal_connect (G_OBJECT(settings_combo), "changed", + G_CALLBACK(csv_price_imp_preview_settings_sel_changed_cb), this); + + // Additionally connect to the changed signal of the embedded GtkEntry + auto emb_entry = gtk_bin_get_child (GTK_BIN (settings_combo)); + g_signal_connect (G_OBJECT(emb_entry), "changed", + G_CALLBACK(csv_price_imp_preview_settings_text_changed_cb), this); + g_signal_connect (G_OBJECT(emb_entry), "insert-text", + G_CALLBACK(csv_price_imp_preview_settings_text_inserted_cb), this); + + // Add Save Settings button + save_button = GTK_WIDGET(gtk_builder_get_object (builder, "save_settings")); + + // Add Delete Settings button + del_button = GTK_WIDGET(gtk_builder_get_object (builder, "delete_settings")); + + /* The table containing the separator configuration widgets */ + start_row_spin = GTK_SPIN_BUTTON(gtk_builder_get_object (builder, "start_row")); + end_row_spin = GTK_SPIN_BUTTON(gtk_builder_get_object (builder, "end_row")); + skip_alt_rows_button = GTK_WIDGET(gtk_builder_get_object (builder, "skip_rows")); + skip_errors_button = GTK_WIDGET(gtk_builder_get_object (builder, "skip_errors_button")); + over_write_cbutton = GTK_WIDGET(gtk_builder_get_object (builder, "over_write_button")); + separator_table = GTK_WIDGET(gtk_builder_get_object (builder, "separator_table")); + fw_instructions_hbox = GTK_WIDGET(gtk_builder_get_object (builder, "fw_instructions_hbox")); + + /* Load the separator buttons from the glade builder file into the + * sep_buttons array. */ + const char* sep_button_names[] = { + "space_cbutton", + "tab_cbutton", + "comma_cbutton", + "colon_cbutton", + "semicolon_cbutton", + "hyphen_cbutton" + }; + for (int i = 0; i < SEP_NUM_OF_TYPES; i++) + sep_button[i] + = (GtkCheckButton*)GTK_WIDGET(gtk_builder_get_object (builder, sep_button_names[i])); + + /* Load and connect the custom separator checkbutton in the same way + * as the other separator buttons. */ + custom_cbutton + = (GtkCheckButton*)GTK_WIDGET(gtk_builder_get_object (builder, "custom_cbutton")); + + /* Load the entry for the custom separator entry. Connect it to the + * sep_button_clicked event handler as well. */ + custom_entry = (GtkEntry*)GTK_WIDGET(gtk_builder_get_object (builder, "custom_entry")); + + /* Create the encoding selector widget and add it to the assistant */ + encselector = GO_CHARMAP_SEL(go_charmap_sel_new(GO_CHARMAP_SEL_TO_UTF8)); + /* Connect the selector to the encoding_selected event handler. */ + g_signal_connect (G_OBJECT(encselector), "charmap_changed", + G_CALLBACK(csv_price_imp_preview_enc_sel_cb), this); + + auto encoding_container = GTK_CONTAINER(gtk_builder_get_object (builder, "encoding_container")); + gtk_container_add (encoding_container, GTK_WIDGET(encselector)); + gtk_widget_show_all (GTK_WIDGET(encoding_container)); + + /* The instructions label and image */ + instructions_label = GTK_LABEL(gtk_builder_get_object (builder, "instructions_label")); + instructions_image = GTK_IMAGE(gtk_builder_get_object (builder, "instructions_image")); + + /* Add in the date format combo box and hook it up to an event handler. */ + date_format_combo = GTK_COMBO_BOX_TEXT(gtk_combo_box_text_new()); + for (int i = 0; i < num_date_formats; i++) + { + gtk_combo_box_text_append_text (date_format_combo, _(date_format_user[i])); + } + gtk_combo_box_set_active (GTK_COMBO_BOX(date_format_combo), 0); + g_signal_connect (G_OBJECT(date_format_combo), "changed", + G_CALLBACK(csv_price_imp_preview_date_fmt_sel_cb), this); + + /* Add it to the assistant. */ + auto date_format_container = GTK_CONTAINER(gtk_builder_get_object (builder, "date_format_container")); + gtk_container_add (date_format_container, GTK_WIDGET(date_format_combo)); + gtk_widget_show_all (GTK_WIDGET(date_format_container)); + + /* Add in the currency format combo box and hook it up to an event handler. */ + currency_format_combo = GTK_COMBO_BOX_TEXT(gtk_combo_box_text_new()); + for (int i = 0; i < num_currency_formats; i++) + { + gtk_combo_box_text_append_text (currency_format_combo, _(currency_format_user[i])); + } + /* Default will the locale */ + gtk_combo_box_set_active (GTK_COMBO_BOX(currency_format_combo), 0); + g_signal_connect (G_OBJECT(currency_format_combo), "changed", + G_CALLBACK(csv_price_imp_preview_currency_fmt_sel_cb), this); + + /* Add it to the assistant. */ + auto currency_format_container = GTK_CONTAINER(gtk_builder_get_object (builder, "currency_format_container")); + gtk_container_add (currency_format_container, GTK_WIDGET(currency_format_combo)); + gtk_widget_show_all (GTK_WIDGET(currency_format_container)); + + /* Connect the CSV/Fixed-Width radio button event handler. */ + csv_button = GTK_WIDGET(gtk_builder_get_object (builder, "csv_button")); + fixed_button = GTK_WIDGET(gtk_builder_get_object (builder, "fixed_button")); + + /* Load the data treeview and connect it to its resizing event handler. */ + treeview = (GtkTreeView*)GTK_WIDGET(gtk_builder_get_object (builder, "treeview")); + gtk_tree_view_set_headers_clickable (treeview, true); + + /* This is true only after encoding_selected is called, so we must + * set it initially to false. */ + encoding_selected_called = false; + } + + /* Confirm Page */ + confirm_page = GTK_WIDGET(gtk_builder_get_object (builder, "confirm_page")); + + /* Summary Page */ + summary_page = GTK_WIDGET(gtk_builder_get_object (builder, "summary_page")); + summary_label = GTK_WIDGET(gtk_builder_get_object (builder, "summary_label")); + + gnc_restore_window_size (GNC_PREFS_GROUP, GTK_WINDOW(csv_imp_asst)); + + gtk_builder_connect_signals (builder, this); + g_object_unref (G_OBJECT(builder)); + + gtk_widget_show_all (GTK_WIDGET(csv_imp_asst)); + gnc_window_adjust_for_screen (GTK_WINDOW(csv_imp_asst)); +} + +/************************************************** + * Code related to the file chooser page + **************************************************/ + +/* csv_price_imp_file_confirm_cb + * + * call back for ok button in file chooser widget + */ +void +CsvImpPriceAssist::file_confirm_cb () +{ + auto file_name = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(file_chooser)); + if (!file_name) + return; + + auto filepath = gnc_uri_get_path (file_name); + auto starting_dir = g_path_get_dirname (filepath); + + m_file_name = file_name; + gnc_set_default_directory (GNC_PREFS_GROUP, starting_dir); + + DEBUG("file_name selected is %s", m_file_name.c_str()); + DEBUG("starting directory is %s", starting_dir); + + g_free (filepath); + g_free (file_name); + g_free (starting_dir); + + /* Load the file into parse_data. */ + price_imp = std::unique_ptr(new GncPriceImport); + /* Assume data is CSV. User can later override to Fixed Width if needed */ + try + { + price_imp->file_format (GncImpFileFormat::CSV); + price_imp->load_file (m_file_name); + price_imp->tokenize (true); + } + catch (std::ifstream::failure& e) + { + /* File loading failed ... */ + gnc_error_dialog (GTK_WIDGET(csv_imp_asst), "%s", e.what()); + return; + } + catch (std::range_error &e) + { + /* Parsing failed ... */ + gnc_error_dialog (GTK_WIDGET(csv_imp_asst), "%s", e.what()); + return; + } + /* Get settings store and populate */ + preview_populate_settings_combo(); + gtk_combo_box_set_active (settings_combo, 0); + + auto num = gtk_assistant_get_current_page (csv_imp_asst); + gtk_assistant_set_current_page (csv_imp_asst, num + 1); +} + + +/************************************************** + * Code related to the preview page + **************************************************/ + +/* Set the available presets in the settings combo box + */ +void CsvImpPriceAssist::preview_populate_settings_combo() +{ + // Clear the list store + auto model = gtk_combo_box_get_model (settings_combo); + gtk_list_store_clear (GTK_LIST_STORE(model)); + + // Append the default entry +//FIXME get_trans_presets ???? + auto presets = get_trans_presets (); + for (auto preset : presets) + { + GtkTreeIter iter; + gtk_list_store_append (GTK_LIST_STORE(model), &iter); + /* FIXME we store the raw pointer to the preset, while it's + * managed by a shared pointer. This is dangerous because + * when the shared pointer goes out of scope, our pointer will dangle. + * For now this is safe, because the shared pointers in this case are + * long-lived, but this may need refactoring. + */ + gtk_list_store_set (GTK_LIST_STORE(model), &iter, SET_GROUP, preset.get(), SET_NAME, preset->m_name.c_str(), -1); + } +} + +/* Enable or disable the save and delete settings buttons + * depending on what is selected and entered as settings name + */ +void CsvImpPriceAssist::preview_handle_save_del_sensitivity (GtkComboBox* combo) +{ + GtkTreeIter iter; + auto can_delete = false; + auto can_save = false; + auto entry = gtk_bin_get_child (GTK_BIN(combo)); + auto entry_text = gtk_entry_get_text (GTK_ENTRY(entry)); + /* Handle sensitivity of the delete and save button */ + if (gtk_combo_box_get_active_iter (combo, &iter)) + { + CsvTransSettings *preset; + GtkTreeModel *model = gtk_combo_box_get_model (combo); + gtk_tree_model_get (model, &iter, SET_GROUP, &preset, -1); +//FIXME + if (preset && !trans_preset_is_reserved_name (preset->m_name)) + { + /* Current preset is not read_only, so buttons can be enabled */ + can_delete = true; + can_save = true; + } + } +//FIXME + else if (entry_text && (strlen (entry_text) > 0) && + !trans_preset_is_reserved_name (std::string(entry_text))) + can_save = true; + + gtk_widget_set_sensitive (save_button, can_save); + gtk_widget_set_sensitive (del_button, can_delete); +} + +void +CsvImpPriceAssist::preview_settings_name (GtkEntry* entry) +{ + auto text = gtk_entry_get_text (entry); + if (text) + price_imp->settings_name(text); + + auto combo = gtk_widget_get_parent (GTK_WIDGET(entry)); + preview_handle_save_del_sensitivity (GTK_COMBO_BOX(combo)); +} + +/* Use selected preset to configure the import. Triggered when + * a preset is selected in the settings combo. + */ +void +CsvImpPriceAssist::preview_settings_load () +{ + // Get the Active Selection + GtkTreeIter iter; + if (!gtk_combo_box_get_active_iter (settings_combo, &iter)) + return; + + CsvTransSettings *preset = nullptr; + auto model = gtk_combo_box_get_model (settings_combo); + gtk_tree_model_get (model, &iter, SET_GROUP, &preset, -1); + + if (!preset) + return; + + price_imp->settings (*preset); + if (preset->m_load_error) + gnc_error_dialog (GTK_WIDGET(csv_imp_asst), + "%s", _("There were problems reading some saved settings, continuing to load.\n" + "Please review and save again.")); + + preview_refresh (); + preview_handle_save_del_sensitivity (settings_combo); +} + +/* Callback to delete a settings entry + */ +void +CsvImpPriceAssist::preview_settings_delete () +{ + // Get the Active Selection + GtkTreeIter iter; + if (!gtk_combo_box_get_active_iter (settings_combo, &iter)) + return; + + CsvTransSettings *preset = nullptr; + auto model = gtk_combo_box_get_model (settings_combo); + gtk_tree_model_get (model, &iter, SET_GROUP, &preset, -1); + + auto response = gnc_ok_cancel_dialog (GTK_WIDGET(csv_imp_asst), + GTK_RESPONSE_CANCEL, + "%s", _("Delete the Import Settings.")); + if (response == GTK_RESPONSE_OK) + { + preset->remove(); + preview_populate_settings_combo(); + gtk_combo_box_set_active (settings_combo, 0); // Default + preview_refresh (); // Reset the widgets + } +} + +/* Callback to save the current settings to the gnucash state file. + */ +void +CsvImpPriceAssist::preview_settings_save () +{ + auto title = _("Save the Import Settings."); + auto new_name = price_imp->settings_name(); + + /* 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) + { + // Walk through the list, reading each row + CsvTransSettings *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_WIDGET(csv_imp_asst), + GTK_RESPONSE_OK, + "%s", _("Setting name already exists, over write?")); + if (response != GTK_RESPONSE_OK) + return; + + break; + } + valid = gtk_tree_model_iter_next (model, &iter); + } + } + + /* All checks passed, let's save this preset */ + if (!price_imp->save_settings()) + { + gnc_info_dialog (GTK_WIDGET(csv_imp_asst), + "%s", _("The settings have been saved.")); + + // 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); + } + } + else + gnc_error_dialog (GTK_WIDGET(csv_imp_asst), + "%s", _("There was a problem saving the settings, please try again.")); +} + +/* Callback triggered when user adjusts skip start lines + */ +void CsvImpPriceAssist::preview_update_skipped_rows () +{ + /* Update skip rows in the parser */ + price_imp->update_skipped_lines (gtk_spin_button_get_value_as_int (start_row_spin), + gtk_spin_button_get_value_as_int (end_row_spin), + gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(skip_alt_rows_button)), + gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(skip_errors_button))); + + /* And adjust maximum number of lines that can be skipped at each end accordingly */ + auto adj = gtk_spin_button_get_adjustment (end_row_spin); + gtk_adjustment_set_upper (adj, price_imp->m_parsed_lines.size() + - price_imp->skip_start_lines() -1); + + adj = gtk_spin_button_get_adjustment (start_row_spin); + gtk_adjustment_set_upper (adj, price_imp->m_parsed_lines.size() + - price_imp->skip_end_lines() - 1); + + preview_refresh_table (); +} + +void CsvImpPriceAssist::preview_over_write (bool over) +{ + price_imp->over_write (over); +} + +/** Event handler for separator changes. This function is called + * whenever one of the widgets for configuring the separators (the + * separator checkbuttons or the custom separator entry) is + * changed. + * @param widget The widget that was changed + * @param info The data that is being configured + */ +void CsvImpPriceAssist::preview_update_separators (GtkWidget* widget) +{ + /* Only manipulate separator characters if the currently open file is + * csv separated. */ + if (price_imp->file_format() != GncImpFileFormat::CSV) + return; + + /* Add the corresponding characters to checked_separators for each + * button that is checked. */ + auto checked_separators = std::string(); + const auto stock_sep_chars = std::string (" \t,:;-"); + for (int i = 0; i < SEP_NUM_OF_TYPES; i++) + { + if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(sep_button[i]))) + checked_separators += stock_sep_chars[i]; + } + + /* Add the custom separator if the user checked its button. */ + if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(custom_cbutton))) + { + auto custom_sep = gtk_entry_get_text (custom_entry); + if (custom_sep[0] != '\0') /* Don't add a blank separator (bad things will happen!). */ + checked_separators += custom_sep; + } + + /* Set the parse options using the checked_separators list. */ + price_imp->separators (checked_separators); + + /* Parse the data using the new options. We don't want to reguess + * the column types because we want to leave the user's + * configurations intact. */ + try + { + price_imp->tokenize (false); + preview_refresh_table (); + } + catch (std::range_error &e) + { + /* Warn the user there was a problem and try to undo what caused + * the error. (This will cause a reparsing and ideally a usable + * configuration.) */ + gnc_error_dialog (GTK_WIDGET(csv_imp_asst), "Error in parsing"); + /* If we're here because the user changed the file format, we should just wait for the user + * to update the configuration */ + if (!widget) + return; + /* If the user changed the custom separator, erase that custom separator. */ + if (widget == GTK_WIDGET(custom_entry)) + gtk_entry_set_text (GTK_ENTRY(widget), ""); + /* If the user checked a checkbutton, toggle that checkbutton back. */ + else + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(widget), + !gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(widget))); + return; + } +} + +/** Event handler for clicking one of the format type radio + * buttons. This occurs if the format (Fixed-Width or CSV) is changed. + * @param csv_button The "Separated" radio button + * @param info The display of the data being imported + */ +void CsvImpPriceAssist::preview_update_file_format () +{ + /* Set the parsing type correctly. */ + try + { + if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(csv_button))) + { + price_imp->file_format (GncImpFileFormat::CSV); + g_signal_handlers_disconnect_by_func(G_OBJECT(treeview), + (gpointer)csv_price_imp_preview_treeview_clicked_cb, (gpointer)this); + gtk_widget_set_visible (separator_table, true); + gtk_widget_set_visible (fw_instructions_hbox, false); + } + else + { + price_imp->file_format (GncImpFileFormat::FIXED_WIDTH); + /* Enable context menu for adding/removing columns. */ + g_signal_connect (G_OBJECT(treeview), "button-press-event", + G_CALLBACK(csv_price_imp_preview_treeview_clicked_cb), (gpointer)this); + gtk_widget_set_visible (separator_table, false); + gtk_widget_set_visible (fw_instructions_hbox, true); + + } + price_imp->tokenize (false); + preview_refresh_table (); + } + catch (std::range_error &e) + { + /* Parsing failed ... */ + gnc_error_dialog (nullptr, "%s", e.what()); + return; + } + catch (...) + { + // FIXME Handle file loading errors (possibly thrown by file_format above) + PWARN("Got an error during file loading"); + } +} + +/** Event handler for a new encoding. This is called when the user + * selects a new encoding; the data is reparsed and shown to the + * user. + * @param selector The widget the user uses to select a new encoding + * @param encoding The encoding that the user selected + */ +void +CsvImpPriceAssist::preview_update_encoding (const char* encoding) +{ + /* This gets called twice every time a new encoding is selected. The + * second call actually passes the correct data; thus, we only do + * something the second time this is called. */ + + /* If this is the second time the function is called ... */ + if (encoding_selected_called) + { + std::string previous_encoding = price_imp->m_tokenizer->encoding(); + /* Try converting the new encoding and reparsing. */ + try + { + price_imp->encoding (encoding); + preview_refresh_table (); + } + catch (...) + { + /* If it fails, change back to the old encoding. */ + gnc_error_dialog (nullptr, "%s", _("Invalid encoding selected")); + go_charmap_sel_set_encoding (encselector, previous_encoding.c_str()); + } + } + encoding_selected_called = !encoding_selected_called; +} + +void +CsvImpPriceAssist::preview_update_date_format () +{ + price_imp->date_format (gtk_combo_box_get_active (GTK_COMBO_BOX(date_format_combo))); + preview_refresh_table (); +} + +void +CsvImpPriceAssist::preview_update_currency_format () +{ + price_imp->currency_format (gtk_combo_box_get_active (GTK_COMBO_BOX(currency_format_combo))); + preview_refresh_table (); +} + +gboolean +csv_imp_preview_queue_rebuild_table (CsvImpPriceAssist *assist) +{ + assist->preview_refresh_table (); + return false; +} + +/* Internally used enum to access the columns in the comboboxes + * the user can click to set a type for each column of the data + */ +enum PreviewHeaderComboCols { COL_TYPE_NAME, COL_TYPE_ID }; +/* Internally used enum to access the first two (fixed) columns + * in the model used to display the prased data. + */ +enum PreviewDataTableCols { + PREV_COL_FCOLOR, + PREV_COL_BCOLOR, + PREV_COL_STRIKE, + PREV_COL_ERROR, + PREV_COL_ERR_ICON, + PREV_N_FIXED_COLS }; + +/** Event handler for the user selecting a new column type. When the + * user selects a new column type, that column's text must be changed + * to the selection, and any other columns containing that selection + * must be changed to "None" because we don't allow duplicates. + * @param renderer The renderer of the column the user changed + * @param path There is only 1 row in info->ctreeview, so this is always 0. + * @param new_text The text the user selected + * @param info The display of the data being imported + */ +void CsvImpPriceAssist::preview_update_col_type (GtkComboBox* cbox) +{ + /* Get the new text */ + GtkTreeIter iter; + auto model = gtk_combo_box_get_model (cbox); + gtk_combo_box_get_active_iter (cbox, &iter); + auto new_col_type = GncPricePropType::NONE; + gtk_tree_model_get (model, &iter, COL_TYPE_ID, &new_col_type, -1); + + auto col_num = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT(cbox), "col-num")); + price_imp->set_column_type_price (col_num, new_col_type); + + /* Delay rebuilding our data table to avoid critical warnings due to + * pending events still acting on them after this event is processed. + */ + g_idle_add ((GSourceFunc)csv_imp_preview_queue_rebuild_table, this); +} + +/*======================================================================*/ +/*================== Beginning of Gnumeric Code ========================*/ + +/* The following is code copied from Gnumeric 1.7.8 licensed under the + * GNU General Public License version 2 and/or version 3. It is from the file + * gnumeric/gnucash/dialogs/dialog-stf-fixed-page.c, and it has been + * modified slightly to work within GnuCash. */ + +/* + * Copyright 2001 Almer S. Tigelaar + * Copyright 2003 Morten Welinder + * + * 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, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +enum +{ + CONTEXT_STF_IMPORT_MERGE_LEFT = 1, + CONTEXT_STF_IMPORT_MERGE_RIGHT = 2, + CONTEXT_STF_IMPORT_SPLIT = 3, + CONTEXT_STF_IMPORT_WIDEN = 4, + CONTEXT_STF_IMPORT_NARROW = 5 +}; + +static GnumericPopupMenuElement const popup_elements[] = +{ + { + N_("Merge with column on _left"), GTK_STOCK_REMOVE, + 0, 1 << CONTEXT_STF_IMPORT_MERGE_LEFT, CONTEXT_STF_IMPORT_MERGE_LEFT + }, + { + N_("Merge with column on _right"), GTK_STOCK_REMOVE, + 0, 1 << CONTEXT_STF_IMPORT_MERGE_RIGHT, CONTEXT_STF_IMPORT_MERGE_RIGHT + }, + { "", nullptr, 0, 0, 0 }, + { + N_("_Split this column"), nullptr, + 0, 1 << CONTEXT_STF_IMPORT_SPLIT, CONTEXT_STF_IMPORT_SPLIT + }, + { "", nullptr, 0, 0, 0 }, + { + N_("_Widen this column"), GTK_STOCK_GO_FORWARD, + 0, 1 << CONTEXT_STF_IMPORT_WIDEN, CONTEXT_STF_IMPORT_WIDEN + }, + { + N_("_Narrow this column"), GTK_STOCK_GO_BACK, + 0, 1 << CONTEXT_STF_IMPORT_NARROW, CONTEXT_STF_IMPORT_NARROW + }, + { nullptr, nullptr, 0, 0, 0 }, +}; + +uint32_t CsvImpPriceAssist::get_new_col_rel_pos (GtkTreeViewColumn *tcol, int dx) +{ + auto renderers = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT(tcol)); + auto cell = GTK_CELL_RENDERER(renderers->data); + g_list_free (renderers); + PangoFontDescription *font_desc; + g_object_get (G_OBJECT(cell), "font_desc", &font_desc, nullptr); + + PangoLayout *layout = gtk_widget_create_pango_layout (GTK_WIDGET(treeview), "x"); + pango_layout_set_font_description (layout, font_desc); + int width; + pango_layout_get_pixel_size (layout, &width, nullptr); + if (width < 1) width = 1; + uint32_t charindex = (dx + width / 2) / width; + g_object_unref (layout); + pango_font_description_free (font_desc); + + return charindex; +} + +gboolean +fixed_context_menu_handler_price (GnumericPopupMenuElement const *element, + gpointer userdata) +{ + auto info = (CsvImpPriceAssist*)userdata; + auto fwtok = dynamic_cast(info->price_imp->m_tokenizer.get()); + + switch (element->index) + { + case CONTEXT_STF_IMPORT_MERGE_LEFT: + fwtok->col_delete (info->fixed_context_col - 1); + break; + case CONTEXT_STF_IMPORT_MERGE_RIGHT: + fwtok->col_delete (info->fixed_context_col); + break; + case CONTEXT_STF_IMPORT_SPLIT: + fwtok->col_split (info->fixed_context_col, info->fixed_context_offset); + break; + case CONTEXT_STF_IMPORT_WIDEN: + fwtok->col_widen (info->fixed_context_col); + break; + case CONTEXT_STF_IMPORT_NARROW: + fwtok->col_narrow (info->fixed_context_col); + break; + default: + ; /* Nothing */ + } + + try + { + info->price_imp->tokenize (false); + } + catch(std::range_error& e) + { + gnc_error_dialog (nullptr, "%s", e.what()); + return false; + } + info->preview_refresh_table (); + return true; +} + +void +CsvImpPriceAssist::fixed_context_menu (GdkEventButton *event, + int col, int offset) +{ + auto fwtok = dynamic_cast(price_imp->m_tokenizer.get()); + fixed_context_col = col; + fixed_context_offset = offset; + + int sensitivity_filter = 0; + if (!fwtok->col_can_delete (col - 1)) + sensitivity_filter |= (1 << CONTEXT_STF_IMPORT_MERGE_LEFT); + if (!fwtok->col_can_delete (col)) + sensitivity_filter |= (1 << CONTEXT_STF_IMPORT_MERGE_RIGHT); + if (!fwtok->col_can_split (col, offset)) + sensitivity_filter |= (1 << CONTEXT_STF_IMPORT_SPLIT); + if (!fwtok->col_can_widen (col)) + sensitivity_filter |= (1 << CONTEXT_STF_IMPORT_WIDEN); + if (!fwtok->col_can_narrow (col)) + sensitivity_filter |= (1 << CONTEXT_STF_IMPORT_NARROW); + + gnumeric_create_popup_menu (popup_elements, &fixed_context_menu_handler_price, + this, 0, + sensitivity_filter, event); +} + +/*===================== End of Gnumeric Code ===========================*/ +/*======================================================================*/ +void +CsvImpPriceAssist::preview_split_column (int col, int offset) +{ + auto fwtok = dynamic_cast(price_imp->m_tokenizer.get()); + fwtok->col_split (col, offset); + try + { + price_imp->tokenize (false); + } + catch (std::range_error& e) + { + gnc_error_dialog (nullptr, "%s", e.what()); + return; + } + preview_refresh_table(); +} + +/** Event handler for clicking on column headers. This function is + * called whenever the user clicks on column headers in + * preview->treeview to modify columns when in fixed-width mode. + * @param button The button at the top of a column of the treeview + * @param event The event that happened (where the user clicked) + * @param info The data being configured + * @returns true if further processing of this even should stop, false + * if other event handlers can have a go at this as well + */ +void +CsvImpPriceAssist::preview_update_fw_columns (GtkTreeView* treeview, GdkEventButton* event) +{ + /* Nothing to do if this was not triggered on our treeview body */ + if (event->window != gtk_tree_view_get_bin_window (treeview)) + return; + + /* Find the column that was clicked. */ + GtkTreeViewColumn *tcol = nullptr; + int cell_x = 0; + auto success = gtk_tree_view_get_path_at_pos (treeview, + (int)event->x, (int)event->y, + nullptr, &tcol, &cell_x, nullptr); + if (!success) + return; + + /* Stop if no column found in this treeview (-1) or + * if column is the error messages column (0) */ + auto tcol_list = gtk_tree_view_get_columns(treeview); + auto tcol_num = g_list_index (tcol_list, tcol); + g_list_free (tcol_list); + if (tcol_num <= 0) + return; + + /* Data columns in the treeview are offset by one + * because the first column is the error column + */ + auto dcol = tcol_num - 1; + auto offset = get_new_col_rel_pos (tcol, cell_x); + if (event->type == GDK_2BUTTON_PRESS && event->button == 1) + /* Double clicks can split columns. */ + preview_split_column (dcol, offset); + else if (event->type == GDK_BUTTON_PRESS && event->button == 3) + /* Right clicking brings up a context menu. */ + fixed_context_menu (event, dcol, offset); +} + +/* Convert state info (errors/skipped) in visual feedback to decorate the preview table */ +void +CsvImpPriceAssist::preview_row_fill_state_cells (GtkListStore *store, GtkTreeIter *iter, + std::string& err_msg, bool skip) +{ + /* Extract error status for all non-skipped lines */ + const char *c_err_msg = nullptr; + const char *icon_name = nullptr; + const char *fcolor = nullptr; + const char *bcolor = nullptr; + if (!skip && !err_msg.empty()) + { + fcolor = "black"; + bcolor = "pink"; + c_err_msg = err_msg.c_str(); + icon_name = GTK_STOCK_DIALOG_ERROR; + } + gtk_list_store_set (store, iter, + PREV_COL_FCOLOR, fcolor, + PREV_COL_BCOLOR, bcolor, + PREV_COL_STRIKE, skip, + PREV_COL_ERROR, c_err_msg, + PREV_COL_ERR_ICON, icon_name, -1); +} + +/* Helper function that creates a combo_box using a model + * with valid column types and selects the given column type + */ +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. */ + auto renderer = gtk_cell_renderer_text_new(); + gtk_cell_layout_pack_start (GTK_CELL_LAYOUT(cbox), + renderer, true); + 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) + { + 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", + G_CALLBACK(csv_price_imp_preview_col_type_changed_cb), (gpointer)this); + + gtk_widget_show (cbox); + return cbox; +} + +void +CsvImpPriceAssist::preview_style_column (uint32_t col_num, GtkTreeModel* model) +{ + auto col = gtk_tree_view_get_column (treeview, col_num); + auto renderer = static_cast(gtk_tree_view_column_get_cell_renderers(col)->data); + + /* First column -the error status column- is rendered differently */ + if (col_num == 0) + { + gtk_tree_view_column_set_attributes (col, renderer, + "stock-id", PREV_COL_ERR_ICON, + "cell-background", PREV_COL_BCOLOR, nullptr); + g_object_set (G_OBJECT(renderer), "stock-size", GTK_ICON_SIZE_MENU, nullptr); + g_object_set (G_OBJECT(col), "sizing", GTK_TREE_VIEW_COLUMN_FIXED, + "fixed-width", 20, nullptr); + gtk_tree_view_column_set_resizable (col, false); + } + else + { + gtk_tree_view_column_set_attributes (col, renderer, + "foreground", PREV_COL_FCOLOR, + "background", PREV_COL_BCOLOR, + "strikethrough", PREV_COL_STRIKE, + "text", col_num + PREV_N_FIXED_COLS -1, nullptr); + + /* We want a monospace font fixed-width data is properly displayed. */ + g_object_set (G_OBJECT(renderer), "family", "monospace", nullptr); + + /* Add a combobox to select column types as column header. Each uses the same + * common model for the dropdown list. The selected value is taken + * from the column_types vector. */ + auto cbox = preview_cbox_factory (GTK_TREE_MODEL(model), col_num - 1); + gtk_tree_view_column_set_widget (col, cbox); + + /* Enable resizing of the columns. */ + gtk_tree_view_column_set_resizable (col, true); + gtk_tree_view_column_set_clickable (col, true); + } +} + +/* Helper to create a shared store for the header comboboxes in the preview treeview. + * It holds the possible column types */ +GtkTreeModel* +make_column_header_model_price (void) +{ + auto combostore = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT); + for (auto col_type : gnc_price_col_type_strs) + { + GtkTreeIter iter; + gtk_list_store_append (combostore, &iter); + gtk_list_store_set (combostore, &iter, + COL_TYPE_NAME, _(col_type.second), + COL_TYPE_ID, static_cast(col_type.first), -1); + } + return GTK_TREE_MODEL(combostore); +} + +/* Updates the preview treeview to show the data as parsed based on the user's + * import parameters. + */ +void CsvImpPriceAssist::preview_refresh_table () +{ + preview_validate_settings (); + + /* Create a new liststore to hold status and data from the file being imported. + The first columns hold status information (row-color, row-errors, row-error-icon,... + All following columns represent the tokenized data as strings. */ + auto ncols = PREV_N_FIXED_COLS + price_imp->column_types_price().size(); + auto model_col_types = g_new (GType, ncols); + model_col_types[PREV_COL_FCOLOR] = G_TYPE_STRING; + model_col_types[PREV_COL_BCOLOR] = G_TYPE_STRING; + model_col_types[PREV_COL_ERROR] = G_TYPE_STRING; + model_col_types[PREV_COL_ERR_ICON] = G_TYPE_STRING; + model_col_types[PREV_COL_STRIKE] = G_TYPE_BOOLEAN; + for (guint i = PREV_N_FIXED_COLS; i < ncols; i++) + model_col_types[i] = G_TYPE_STRING; + auto store = gtk_list_store_newv (ncols, model_col_types); + g_free (model_col_types); + + /* Fill the data liststore with data from importer object. */ + for (auto parse_line : price_imp->m_parsed_lines) + { + /* Fill the state cells */ + GtkTreeIter iter; + gtk_list_store_append (store, &iter); + preview_row_fill_state_cells (store, &iter, + std::get<1>(parse_line), std::get<3>(parse_line)); + + /* Fill the data cells. */ + for (auto cell_str_it = std::get<0>(parse_line).cbegin(); cell_str_it != std::get<0>(parse_line).cend(); cell_str_it++) + { + uint32_t pos = PREV_N_FIXED_COLS + cell_str_it - std::get<0>(parse_line).cbegin(); + gtk_list_store_set (store, &iter, pos, cell_str_it->c_str(), -1); + } + } + gtk_tree_view_set_model (treeview, GTK_TREE_MODEL(store)); + gtk_tree_view_set_tooltip_column (treeview, PREV_COL_ERROR); + + /* Adjust treeview to go with the just created model. This consists of adding + * or removing columns and resetting any parameters related to how + * the columns and data should be rendered. + */ + + /* Start with counting the current number of columns (ntcols) + * we have in the treeview */ + auto columns = gtk_tree_view_get_columns (treeview); + auto ntcols = g_list_length(columns); + g_list_free (columns); + + /* Drop redundant columns if the model has less data columns than the new model + * ntcols = n° of columns in treeview (1 error column + x data columns) + * ncols = n° of columns in model (fixed state columns + x data columns) + */ + while (ntcols > ncols - PREV_N_FIXED_COLS + 1) + { + auto col = gtk_tree_view_get_column (treeview, ntcols - 1); + gtk_tree_view_column_clear (col); + ntcols = gtk_tree_view_remove_column(treeview, col); + } + + /* Insert columns if the model has more data columns than the treeview. */ + while (ntcols < ncols - PREV_N_FIXED_COLS + 1) + { + /* Default cell renderer is text, except for the first (error) column */ + auto renderer = gtk_cell_renderer_text_new(); + if (ntcols == 0) + renderer = gtk_cell_renderer_pixbuf_new(); // Error column uses an icon + auto col = gtk_tree_view_column_new (); + gtk_tree_view_column_pack_start (col, renderer, false); + ntcols = gtk_tree_view_append_column (treeview, col); + } + + /* Reset column attributes as they are undefined after recreating the model */ + auto combostore = make_column_header_model_price (); + for (uint32_t i = 0; i < ntcols; i++) + preview_style_column (i, combostore); + + /* Release our reference for the stores to allow proper memory management. */ + g_object_unref (store); + g_object_unref (combostore); + + /* Make the things actually appear. */ + gtk_widget_show_all (GTK_WIDGET(treeview)); +} + +/* Update the preview page based on the current state of the importer. + * Should be called when settings are changed. + */ +void +CsvImpPriceAssist::preview_refresh () +{ + // Set start row + auto adj = gtk_spin_button_get_adjustment (start_row_spin); + gtk_adjustment_set_upper (adj, price_imp->m_parsed_lines.size()); + gtk_spin_button_set_value (start_row_spin, + price_imp->skip_start_lines()); + + // Set end row + adj = gtk_spin_button_get_adjustment (end_row_spin); + gtk_adjustment_set_upper (adj, price_imp->m_parsed_lines.size()); + gtk_spin_button_set_value (end_row_spin, + price_imp->skip_end_lines()); + + // Set Alternate rows + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(skip_alt_rows_button), + price_imp->skip_alt_lines()); + + // Set over-write indicator + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(over_write_cbutton), + price_imp->over_write()); + + // Set Import Format + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(csv_button), + (price_imp->file_format() == GncImpFileFormat::CSV)); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(fixed_button), + (price_imp->file_format() != GncImpFileFormat::CSV)); + + // This section deals with the combo's and character encoding + gtk_combo_box_set_active (GTK_COMBO_BOX(date_format_combo), + price_imp->date_format()); + gtk_combo_box_set_active (GTK_COMBO_BOX(currency_format_combo), + price_imp->currency_format()); + go_charmap_sel_set_encoding (encselector, price_imp->encoding().c_str()); + + // Handle separator checkboxes and custom field, only relevant if the file format is csv + if (price_imp->file_format() == GncImpFileFormat::CSV) + { + auto separators = price_imp->separators(); + const auto stock_sep_chars = std::string (" \t,:;-"); + for (int i = 0; i < SEP_NUM_OF_TYPES; i++) + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(sep_button[i]), + separators.find (stock_sep_chars[i]) != std::string::npos); + + // If there are any other separators in the separators string, + // add them as custom separators + auto pos = separators.find_first_of (stock_sep_chars); + while (!separators.empty() && pos != std::string::npos) + { + separators.erase(pos); + pos = separators.find_first_of (stock_sep_chars); + } + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(custom_cbutton), + !separators.empty()); + gtk_entry_set_text (GTK_ENTRY(custom_entry), separators.c_str()); + } + // Repopulate the parsed data table + g_idle_add ((GSourceFunc)csv_imp_preview_queue_rebuild_table, this); +} + +/* Check if all selected data can be parsed sufficiently to continue + */ +void CsvImpPriceAssist::preview_validate_settings () +{ + /* Allow the user to proceed only if there are no inconsistencies in the settings */ + auto error_msg = price_imp->verify(); + gtk_assistant_set_page_complete (csv_imp_asst, preview_page, error_msg.empty()); + gtk_label_set_markup(GTK_LABEL(instructions_label), error_msg.c_str()); + gtk_widget_set_visible (GTK_WIDGET(instructions_image), !error_msg.empty()); +} + +/******************************************************* + * Assistant page prepare functions + *******************************************************/ + +void +CsvImpPriceAssist::assist_file_page_prepare () +{ + /* Set the default directory */ + auto starting_dir = gnc_get_default_directory (GNC_PREFS_GROUP); + if (starting_dir) + { + gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER(file_chooser), starting_dir); + g_free (starting_dir); + } +} + +void +CsvImpPriceAssist::assist_preview_page_prepare () +{ + /* Disable the Forward Assistant Button */ + gtk_assistant_set_page_complete (csv_imp_asst, preview_page, false); + + /* Load the data into the treeview. */ + preview_refresh_table (); +} + +void +CsvImpPriceAssist::assist_confirm_page_prepare () +{ +} + +void +CsvImpPriceAssist::assist_summary_page_prepare () +{ + auto text = std::string(""); + text += _("The prices were imported from the file '") + m_file_name + "'."; + text += _("\n\nThe number of Prices added was ") + std::to_string(price_imp->m_prices_added); + text += _(" and ") + std::to_string(price_imp->m_prices_duplicated); + text += _(" were duplicated."); + text += ""; + + gtk_label_set_markup (GTK_LABEL(summary_label), text.c_str()); +} + +void +CsvImpPriceAssist::assist_prepare_cb (GtkWidget *page) +{ + if (page == file_page) + assist_file_page_prepare (); + else if (page == preview_page) + assist_preview_page_prepare (); + else if (page == confirm_page) + assist_confirm_page_prepare (); + else if (page == summary_page) + assist_summary_page_prepare (); +} + +void +CsvImpPriceAssist::assist_finish (bool canceled) +{ + /* Start the import */ +//FIXME Apply button +g_print("Finish\n"); +// if (canceled || price_imp->m_transactions.empty()) +// gnc_gen_trans_list_delete (gnc_csv_importer_gui); +// else +// gnc_gen_trans_assist_start (gnc_csv_importer_gui); + + +//FIXME Cancel comes here to, check when nothing set, goes to catch below also + + /* Create prices from the parsed data */ + try + { + price_imp->create_prices (); + } + catch (const std::invalid_argument& err) + { + /* Oops! This shouldn't happen when using the import assistant ! + * Inform the user and go back to the preview page. + */ + gnc_error_dialog (GTK_WIDGET(csv_imp_asst), + _("An unexpected error has occurred while creating prices. Please report this as a bug.\n\n" + "Error message:\n%s"), err.what()); + gtk_assistant_set_current_page (csv_imp_asst, 2); + } +} + +void +CsvImpPriceAssist::assist_compmgr_close () +{ + gnc_save_window_size (GNC_PREFS_GROUP, GTK_WINDOW(csv_imp_asst)); + gtk_widget_destroy (GTK_WIDGET(csv_imp_asst)); +} + +static void +csv_price_imp_close_handler (gpointer user_data) +{ + auto info = (CsvImpPriceAssist*)user_data; + info->assist_compmgr_close(); +} + +/********************************************************************\ + * gnc_file_csv_price_import * + * opens up a assistant to import prices. * + * * + * Args: none * + * Return: nothing * +\********************************************************************/ +void +gnc_file_csv_price_import(void) +{ + auto info = new CsvImpPriceAssist; + gnc_register_gui_component (ASSISTANT_CSV_IMPORT_PRICE_CM_CLASS, + nullptr, csv_price_imp_close_handler, + info); +} diff --git a/gnucash/import-export/csv-imp/assistant-csv-price-import.glade b/gnucash/import-export/csv-imp/assistant-csv-price-import.glade new file mode 100644 index 0000000000..705e8274aa --- /dev/null +++ b/gnucash/import-export/csv-imp/assistant-csv-price-import.glade @@ -0,0 +1,1001 @@ + + + + + + 1000 + 1 + 10 + + + 1000 + 1 + 10 + + + False + 12 + CSV Price Import + 400 + 500 + + + + + + + + True + False + This assistant will help you import Prices from a CSV file. + +There is a minimum number of columns that have to be present for a successful import, for Stock prices these are Date, Amount, Symbol From and for Currency they are Date, Amount, Currency From and Currency To. + +Various options exist for specifying the delimiter as well as a fixed width option. With the fixed width option, double click on the bar above the displayed rows to set the column width. + +Examples are "RR.L","21/11/2016",5.345,"GBP" and "USD","2016-11-21",1.56,"GBP" + +There is an option for specifying the start row, end row and an option to skip alternate rows beginning from the start row which can be used if you have some header text. Also there is an option to over write existing prices for that day if required. + +On the preview page you can Load and Save the settings. To save the settings, select a previously saved entry or replace the text and press the Save Settings button. + +This operation is not reversable, so make sure you have a working backup. + +Click on 'Forward' to proceed or 'Cancel' to Abort Import. + True + + + intro + True + + + + + True + False + 12 + + + True + False + +Select location and file name for the Import, then click 'OK'... + + True + + + False + False + 0 + + + + + Select File for Import + + + + + True + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 12 + 2 + + + True + False + 2 + 2 + 5 + 5 + + + True + False + 0 + in + + + True + False + 5 + 5 + 5 + + + True + False + + + True + True + True + Delete Settings + + + + True + False + gtk-delete + + + + + False + False + end + 0 + + + + + True + True + True + Save Settings + + + + True + False + gtk-save + + + + + False + False + end + 2 + + + + + + + + + True + False + <b>Load and Save Settings</b> + True + False + + + + + GTK_FILL + GTK_FILL + + + + + True + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 0 + none + + + True + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 5 + 5 + + + True + False + + + True + False + 2 + 2 + + + Separators + True + True + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + True + True + + + + + + Fixed-Width + True + True + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + True + csv_button + + + 1 + 2 + + + + + True + False + + + 2 + 1 + 2 + GTK_FILL + + + + + False + False + 0 + + + + + True + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 3 + 3 + 3 + + + Space + True + True + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + True + + + + GTK_FILL + GTK_FILL + + + + + Tab + True + True + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + True + + + + 1 + 2 + GTK_FILL + GTK_FILL + + + + + Comma (,) + True + True + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + True + True + + + + 2 + 3 + GTK_FILL + GTK_FILL + + + + + Colon (:) + True + True + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + True + + + + 1 + 2 + GTK_FILL + GTK_FILL + + + + + Semicolon (;) + True + True + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + True + + + + 1 + 2 + 1 + 2 + GTK_FILL + GTK_FILL + + + + + Hyphen (-) + True + True + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + True + + + + 2 + 3 + 1 + 2 + GTK_FILL + GTK_FILL + + + + + Custom + True + True + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + True + + + + 2 + 3 + GTK_FILL + GTK_FILL + + + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + + True + False + False + True + True + + + + 1 + 3 + 2 + 3 + GTK_FILL + GTK_FILL + + + + + False + False + 1 + + + + + False + True + + + True + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 0 + gtk-dialog-info + + + False + True + 2 + 0 + + + + + True + False + 2 + 2 + + + True + False + 0 + 5 + + True + True + + + + + True + False + 0 + Double-click anywhere on the table below to insert a column break + True + True + + + 1 + 2 + + + + + + True + False + 0 + 5 + + True + True + + + 1 + 2 + + + + + True + False + 0 + Right-click anywhere in a column to modify it (widen, narrow, merge) + True + True + + + 1 + 2 + 1 + 2 + + + + + + True + True + 1 + + + + + False + False + 2 + + + + + True + False + 2 + + + True + False + + + GTK_FILL + + + + + Allow existing prices to be over written. + True + True + False + Normally prices are not over written, select this to change that. + True + + + + 1 + 2 + + + + + False + False + 3 + + + + + + + + + True + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + <b>File Format</b> + True + + + + + 1 + 2 + GTK_FILL + GTK_FILL + + + + + True + False + 0 + none + + + True + False + 5 + 5 + + + True + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + + + True + False + 6 + 2 + 5 + 5 + + + True + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 0 + + + 1 + 2 + 1 + 2 + GTK_FILL + + + + + True + False + 0 + Date Format + + + 1 + 2 + GTK_SHRINK | GTK_FILL + + + + + True + False + 0 + + + 1 + 2 + 2 + 3 + GTK_FILL + + + + + True + False + 0 + Currency Format + + + 2 + 3 + GTK_SHRINK | GTK_FILL + + + + + True + False + 0 + Encoding + + + GTK_SHRINK | GTK_FILL + + + + + True + False + 0 + + + 1 + 2 + GTK_FILL + + + + + True + False + 0 + Leading Lines to Skip + + + 4 + 5 + GTK_FILL + + + + + True + False + 0 + Trailing Lines to Skip + + + 5 + 6 + GTK_FILL + + + + + True + False + + + True + True + + True + False + False + True + True + start_row_adj + True + + + + False + False + 0 + + + + + 1 + 2 + 4 + 5 + + + + + True + False + + + True + True + + True + False + False + True + True + end_row_adj + True + + + + False + False + 0 + + + + + 1 + 2 + 5 + 6 + + + + + True + False + + + 2 + 3 + 4 + + + + + False + True + 0 + + + + + Skip alternate lines + True + True + False + Starting from the first line that is actually imported every second line will be skipped. This option will take the leading lines to skip into account as well. +For example +* if 'Leading Lines to Skip' is set to 3, the first line to import will be line 4. Lines 5, 7, 9,... will be skipped. +* if 'Leading Lines to Skip' is set to 4, the first line to import will be line 5. Lines 6, 8, 10,... will be skipped. + True + + + + False + True + 1 + + + + + + + + + True + False + <b>Miscellaneous</b> + True + + + + + 1 + 2 + 1 + 2 + GTK_FILL + GTK_FILL + + + + + False + False + 0 + + + + + True + True + automatic + automatic + + + True + False + + + True + False + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + False + both + + + False + True + 0 + + + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + both + + + True + True + 1 + + + + + + + + + True + True + 1 + + + + + True + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + + + True + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 0 + gtk-dialog-info + + + False + True + 2 + 0 + + + + + True + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 0 + Select the type of each column to import. + + + True + True + 1 + + + + + False + False + 5 + 2 + + + + + True + False + + + Skip Errors + True + False + 1 + right + True + + + + False + False + end + 0 + + + + + False + False + 3 + + + + + intro + True + + + + + True + False + 12 + + + True + False + + + True + False + Press Apply to add Prices. +Cancel to abort. + center + True + + + + + True + False + 0 + + + + + confirm + Import Prices Now + + + + + True + False + 12 + + + True + False + label + True + True + + + True + True + 0 + + + + + summary + Import Summary + True + + + + diff --git a/gnucash/import-export/csv-imp/assistant-csv-price-import.h b/gnucash/import-export/csv-imp/assistant-csv-price-import.h new file mode 100644 index 0000000000..213b622e2e --- /dev/null +++ b/gnucash/import-export/csv-imp/assistant-csv-price-import.h @@ -0,0 +1,36 @@ +/*******************************************************************\ + * assistant-csv-price-import.h -- An assistant for importing * + * Prices from a file. * + * * + * Copyright (C) 2017 Robert Fewell * + * * + * 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 * +\********************************************************************/ +/** @file assistant-csv-price-import.h + @brief CSV Import Assistant + @author Copyright (c) 2017 Robert Fewell +*/ +#ifndef GNC_ASSISTANT_CSV_IMPORT_PRICE_H +#define GNC_ASSISTANT_CSV_IMPORT_PRICE_H + + +/** The gnc_file_csv_price_import() will let the user import the + * commodity prices from a file. + */ +void gnc_file_csv_price_import (void); +#endif diff --git a/gnucash/import-export/csv-imp/gnc-plugin-csv-import-ui.xml b/gnucash/import-export/csv-imp/gnc-plugin-csv-import-ui.xml index c119a648cb..bdd3043cb4 100644 --- a/gnucash/import-export/csv-imp/gnc-plugin-csv-import-ui.xml +++ b/gnucash/import-export/csv-imp/gnc-plugin-csv-import-ui.xml @@ -5,6 +5,7 @@ + diff --git a/gnucash/import-export/csv-imp/gnc-plugin-csv-import.c b/gnucash/import-export/csv-imp/gnc-plugin-csv-import.c index 25954beb9d..c14092d1c9 100644 --- a/gnucash/import-export/csv-imp/gnc-plugin-csv-import.c +++ b/gnucash/import-export/csv-imp/gnc-plugin-csv-import.c @@ -30,6 +30,7 @@ #include "assistant-csv-account-import.h" #include "assistant-csv-trans-import.h" +#include "assistant-csv-price-import.h" static void gnc_plugin_csv_import_class_init (GncPluginCsvImportClass *klass); static void gnc_plugin_csv_import_init (GncPluginCsvImport *plugin); @@ -38,6 +39,7 @@ static void gnc_plugin_csv_import_finalize (GObject *object); /* Command callbacks */ static void gnc_plugin_csv_import_tree_cmd (GtkAction *action, GncMainWindowActionData *data); static void gnc_plugin_csv_import_trans_cmd (GtkAction *action, GncMainWindowActionData *data); +static void gnc_plugin_csv_import_price_cmd (GtkAction *action, GncMainWindowActionData *data); #define PLUGIN_ACTIONS_NAME "gnc-plugin-csv-import-actions" #define PLUGIN_UI_FILENAME "gnc-plugin-csv-import-ui.xml" @@ -54,6 +56,11 @@ static GtkActionEntry gnc_plugin_actions [] = N_("Import Transactions from a CSV file"), G_CALLBACK (gnc_plugin_csv_import_trans_cmd) }, + { + "CsvImportPriceAction", GTK_STOCK_CONVERT, N_("Import _Prices from a CSV file..."), NULL, + N_("Import Prices from a CSV file"), + G_CALLBACK (gnc_plugin_csv_import_price_cmd) + }, }; static guint gnc_plugin_n_actions = G_N_ELEMENTS (gnc_plugin_actions); @@ -157,6 +164,13 @@ gnc_plugin_csv_import_trans_cmd (GtkAction *action, gnc_file_csv_trans_import (); } +static void +gnc_plugin_csv_import_price_cmd (GtkAction *action, + GncMainWindowActionData *data) +{ + gnc_file_csv_price_import (); +} + /************************************************************ * Plugin Bootstrapping * ************************************************************/ From f2c78102e09c55ed1bb3243c35c87bf3293e3808 Mon Sep 17 00:00:00 2001 From: Robert Fewell <14uBobIT@gmail.com> Date: Fri, 1 Dec 2017 11:03:07 +0000 Subject: [PATCH 27/48] Remove not required account update --- .../import-export/csv-imp/assistant-csv-price-import.cpp | 6 ------ 1 file changed, 6 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 a6ba349ff4..6c4279a813 100644 --- a/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp +++ b/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp @@ -87,7 +87,6 @@ public: void preview_over_write (bool over); void preview_update_separators (GtkWidget* widget); void preview_update_file_format (); - void preview_update_account (); void preview_update_encoding (const char* encoding); void preview_update_date_format (); void preview_update_currency_format (); @@ -306,11 +305,6 @@ void csv_price_imp_preview_sep_fixed_sel_cb (GtkToggleButton* csv_button, CsvImp info->preview_update_file_format(); } -void csv_price_imp_preview_acct_sel_cb (GtkWidget* widget, CsvImpPriceAssist* info) -{ - info->preview_update_account(); -} - void csv_price_imp_preview_enc_sel_cb (GOCharmapSel* selector, const char* encoding, CsvImpPriceAssist* info) { From 3279973329885cc63a6c7b8fb1060aacd4f2cfec Mon Sep 17 00:00:00 2001 From: Robert Fewell <14uBobIT@gmail.com> Date: Fri, 1 Dec 2017 11:04:49 +0000 Subject: [PATCH 28/48] Some text changes --- gnucash/import-export/csv-imp/gnc-price-import.cpp | 2 +- gnucash/import-export/csv-imp/gnc-price-import.hpp | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/gnucash/import-export/csv-imp/gnc-price-import.cpp b/gnucash/import-export/csv-imp/gnc-price-import.cpp index 09deb934e0..5437237ef1 100644 --- a/gnucash/import-export/csv-imp/gnc-price-import.cpp +++ b/gnucash/import-export/csv-imp/gnc-price-import.cpp @@ -400,7 +400,7 @@ void GncPriceImport::verify_column_selections (ErrorListPrice& error_msg) if (!check_for_column_type(GncPricePropType::AMOUNT)) error_msg.add_error( _("Please select an amount column.")); - /* Verify an Currency to column is selected. + /* Verify a Currency to column is selected. */ if (!check_for_column_type(GncPricePropType::CURRENCY_TO)) error_msg.add_error( _("Please select a Currency to column.")); diff --git a/gnucash/import-export/csv-imp/gnc-price-import.hpp b/gnucash/import-export/csv-imp/gnc-price-import.hpp index 0898215b29..60cdc27807 100644 --- a/gnucash/import-export/csv-imp/gnc-price-import.hpp +++ b/gnucash/import-export/csv-imp/gnc-price-import.hpp @@ -68,11 +68,10 @@ struct ErrorListPrice; * - set a file format * - load a file * - optionally convert it's encoding - * - parse the file into lines, which in turn are split up in columns + * - parse the file into lines, which in turn are split up in to columns * the result of this step can be queried from tokenizer * - the user should now map the columns to types, which is stored in column_types - * - last step is convert the mapped columns into a list of transactions - * - this list will then be passed on the the generic importer for further processing */ + * - last step is convert the mapped columns into a list of prices to add */ class GncPriceImport { public: From 8f3e175fb2d3b665e597527e52cbe1cf2e242528 Mon Sep 17 00:00:00 2001 From: Robert Fewell <14uBobIT@gmail.com> Date: Fri, 1 Dec 2017 11:25:06 +0000 Subject: [PATCH 29/48] Add option to specify Commodity from and Currency to for whole file Added two combo's to allow user to specify a Commodity from and Currency to for the whole file. Also reduced the property types to four and aligned all the commodity and currency variables. --- .../csv-imp/assistant-csv-price-import.cpp | 202 +++++++++++++++++- .../csv-imp/assistant-csv-price-import.glade | 150 ++++++++++++- .../csv-imp/gnc-price-import.cpp | 118 ++++++++-- .../csv-imp/gnc-price-import.hpp | 9 +- .../import-export/csv-imp/gnc-price-props.cpp | 120 +++++------ .../import-export/csv-imp/gnc-price-props.hpp | 23 +- 6 files changed, 525 insertions(+), 97 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 6c4279a813..120367a58e 100644 --- a/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp +++ b/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp @@ -90,6 +90,8 @@ public: void preview_update_encoding (const char* encoding); void preview_update_date_format (); void preview_update_currency_format (); + void preview_update_currency (); + void preview_update_commodity (); void preview_update_col_type (GtkComboBox* cbox); void preview_update_fw_columns (GtkTreeView* treeview, GdkEventButton* event); @@ -134,6 +136,8 @@ private: GtkWidget *csv_button; /**< The widget for the CSV button */ GtkWidget *fixed_button; /**< The widget for the Fixed Width button */ GtkWidget *over_write_cbutton; /**< The widget for Price Over Write */ + GtkWidget *commodity_selector; /**< The widget for commodity combo box */ + GtkWidget *currency_selector; /**< The widget for currency combo box */ GOCharmapSel *encselector; /**< The widget for selecting the encoding */ GtkWidget *separator_table; /**< Container for the separator checkboxes */ GtkCheckButton *sep_button[SEP_NUM_OF_TYPES]; /**< Checkbuttons for common separators */ @@ -321,6 +325,16 @@ static void csv_price_imp_preview_currency_fmt_sel_cb (GtkComboBox* format_selec info->preview_update_currency_format(); } +static void csv_price_imp_preview_currency_sel_cb (GtkComboBox* currency_selector, CsvImpPriceAssist* info) +{ + info->preview_update_currency(); +} + +static void csv_price_imp_preview_commodity_sel_cb (GtkComboBox* commodity_selector, CsvImpPriceAssist* info) +{ + info->preview_update_commodity(); +} + void csv_price_imp_preview_col_type_changed_cb (GtkComboBox* cbox, CsvImpPriceAssist* info) { info->preview_update_col_type (cbox); @@ -334,6 +348,126 @@ csv_price_imp_preview_treeview_clicked_cb (GtkTreeView* treeview, GdkEventButton return false; } +static +gnc_commodity *get_commodity_from_combo (GtkComboBox *combo) +{ + GtkTreeModel *model, *sort_model; + GtkTreeIter iter, siter; + gchar *string; + gnc_commodity *comm; + + if (!gtk_combo_box_get_active_iter (combo, &siter)) + return nullptr; + + sort_model = gtk_combo_box_get_model (combo); + model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT(sort_model)); + + gtk_tree_model_sort_convert_iter_to_child_iter (GTK_TREE_MODEL_SORT(sort_model), + &iter, &siter); + + gtk_tree_model_get (GTK_TREE_MODEL (model), &iter, 0, &string, 2, &comm, -1); + + PINFO("Commodity string is %s", string); + + g_free (string); + return comm; +} + +static void +set_commodity_for_combo (GtkComboBox *combo, gnc_commodity *comm) +{ + GtkTreeModel *model, *sort_model; + GtkTreeIter iter, siter; + gnc_commodity *model_comm; + gboolean valid; + + sort_model = gtk_combo_box_get_model (combo); + model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT(sort_model)); + valid = gtk_tree_model_get_iter_first (model, &iter); + + while (valid) + { + gtk_tree_model_get (model, &iter, 2, &model_comm, -1); + if (model_comm == comm) + { + if (gtk_tree_model_sort_convert_child_iter_to_iter (GTK_TREE_MODEL_SORT(sort_model), &siter, &iter)) + { + gtk_combo_box_set_active_iter (combo, &siter); + return; + } + } + /* Make iter point to the next row in the list store */ + valid = gtk_tree_model_iter_next (model, &iter); + } + // Not found, set it to first iter + valid = gtk_tree_model_get_iter_first (model, &iter); + if (gtk_tree_model_sort_convert_child_iter_to_iter (GTK_TREE_MODEL_SORT(sort_model), &siter, &iter)) + gtk_combo_box_set_active_iter (combo, &siter); +} + +static +GtkTreeModel *get_model (bool all_commodity) +{ + GtkTreeModel *store, *model; + const gnc_commodity_table *commodity_table = gnc_get_current_commodities (); + gnc_commodity *tmp_commodity = nullptr; + char *tmp_namespace = nullptr; + GList *commodity_list = nullptr; + GList *namespace_list = gnc_commodity_table_get_namespaces (commodity_table); + GtkTreeIter iter; + + store = GTK_TREE_MODEL(gtk_list_store_new (3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER)); + model = gtk_tree_model_sort_new_with_model (store); + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (model), + 0, GTK_SORT_ASCENDING); + + gtk_list_store_append (GTK_LIST_STORE(store), &iter); + gtk_list_store_set (GTK_LIST_STORE(store), &iter, 0, " ", 1, nullptr, -1); + + namespace_list = g_list_first (namespace_list); + while (namespace_list != nullptr) + { + tmp_namespace = (char*)namespace_list->data; + DEBUG("Looking at namespace %s", tmp_namespace); + + /* Hide the template entry */ + if (g_utf8_collate (tmp_namespace, "template" ) != 0) + { + if ((g_utf8_collate (tmp_namespace, GNC_COMMODITY_NS_CURRENCY ) == 0) || (all_commodity == true)) + { + commodity_list = gnc_commodity_table_get_commodities (commodity_table, tmp_namespace); + commodity_list = g_list_first (commodity_list); + while (commodity_list != nullptr) + { + gchar *name_str; + gchar *save_str; + gchar *settings_str; + tmp_commodity = (gnc_commodity*)commodity_list->data; + DEBUG("Looking at commodity %s", gnc_commodity_get_fullname (tmp_commodity)); + + name_str = g_strconcat (tmp_namespace, " : (", gnc_commodity_get_mnemonic (tmp_commodity), + ") ", gnc_commodity_get_fullname (tmp_commodity), nullptr); + + settings_str = g_strconcat (tmp_namespace, "::", gnc_commodity_get_mnemonic (tmp_commodity), nullptr); + DEBUG("Name string is %s, Save string is %s", name_str, settings_str); + + gtk_list_store_append (GTK_LIST_STORE(store), &iter); + gtk_list_store_set (GTK_LIST_STORE(store), &iter, 0, name_str, 1, settings_str, 2, tmp_commodity, -1); + + g_free (name_str); + g_free (settings_str); + commodity_list = g_list_next (commodity_list); + } + } + } + namespace_list = g_list_next (namespace_list); + } + g_list_free (commodity_list); + g_list_free (namespace_list); + + return model; +} + /******************************************************* * Assistant Constructor @@ -343,6 +477,8 @@ CsvImpPriceAssist::CsvImpPriceAssist () auto builder = gtk_builder_new(); gnc_builder_add_from_file (builder , "assistant-csv-price-import.glade", "start_row_adj"); gnc_builder_add_from_file (builder , "assistant-csv-price-import.glade", "end_row_adj"); + gnc_builder_add_from_file (builder , "assistant-csv-price-import.glade", "liststore1"); + gnc_builder_add_from_file (builder , "assistant-csv-price-import.glade", "liststore2"); gnc_builder_add_from_file (builder , "assistant-csv-price-import.glade", "CSV Price Assistant"); csv_imp_asst = GTK_ASSISTANT(gtk_builder_get_object (builder, "CSV Price Assistant")); @@ -453,6 +589,18 @@ CsvImpPriceAssist::CsvImpPriceAssist () gtk_container_add (encoding_container, GTK_WIDGET(encselector)); gtk_widget_show_all (GTK_WIDGET(encoding_container)); + /* Add commodity selection widget */ + commodity_selector = GTK_WIDGET(gtk_builder_get_object (builder, "commodity_cbox")); + gtk_combo_box_set_model (GTK_COMBO_BOX(commodity_selector), get_model (true)); + g_signal_connect(G_OBJECT(commodity_selector), "changed", + G_CALLBACK(csv_price_imp_preview_commodity_sel_cb), this); + + /* Add currency selection widget */ + currency_selector = GTK_WIDGET(gtk_builder_get_object (builder, "currency_cbox")); + gtk_combo_box_set_model (GTK_COMBO_BOX(currency_selector), get_model (false)); + g_signal_connect(G_OBJECT(currency_selector), "changed", + G_CALLBACK(csv_price_imp_preview_currency_sel_cb), this); + /* The instructions label and image */ instructions_label = GTK_LABEL(gtk_builder_get_object (builder, "instructions_label")); instructions_image = GTK_IMAGE(gtk_builder_get_object (builder, "instructions_image")); @@ -570,6 +718,9 @@ CsvImpPriceAssist::file_confirm_cb () preview_populate_settings_combo(); gtk_combo_box_set_active (settings_combo, 0); + // set over_write to false as default + price_imp->over_write (false); + auto num = gtk_assistant_get_current_page (csv_imp_asst); gtk_assistant_set_current_page (csv_imp_asst, num + 1); } @@ -951,6 +1102,22 @@ CsvImpPriceAssist::preview_update_currency_format () preview_refresh_table (); } +void +CsvImpPriceAssist::preview_update_currency () +{ + gnc_commodity *comm = get_commodity_from_combo (GTK_COMBO_BOX(currency_selector)); + price_imp->to_currency (comm); + preview_refresh_table (); +} + +void +CsvImpPriceAssist::preview_update_commodity () +{ + gnc_commodity *comm = get_commodity_from_combo (GTK_COMBO_BOX(commodity_selector)); + price_imp->from_commodity (comm); + preview_refresh_table (); +} + gboolean csv_imp_preview_queue_rebuild_table (CsvImpPriceAssist *assist) { @@ -1413,6 +1580,28 @@ void CsvImpPriceAssist::preview_refresh_table () for (uint32_t i = 0; i < ntcols; i++) preview_style_column (i, combostore); + auto column_types = price_imp->column_types_price(); + + // look for a commodity column, clear the commdoity combo + auto col_type_comm = std::find (column_types.begin(), + column_types.end(), GncPricePropType::FROM_COMMODITY); + if (col_type_comm != column_types.end()) + { + g_signal_handlers_block_by_func (commodity_selector, (gpointer) csv_price_imp_preview_commodity_sel_cb, this); + set_commodity_for_combo (GTK_COMBO_BOX(commodity_selector), nullptr); + g_signal_handlers_unblock_by_func (commodity_selector, (gpointer) csv_price_imp_preview_commodity_sel_cb, this); + } + + // look for a currency column, clear the currency combo + auto col_type_curr = std::find (column_types.begin(), + column_types.end(), GncPricePropType::TO_CURRENCY); + if (col_type_curr != column_types.end()) + { + g_signal_handlers_block_by_func (currency_selector, (gpointer) csv_price_imp_preview_currency_sel_cb, this); + set_commodity_for_combo (GTK_COMBO_BOX(currency_selector), nullptr); + g_signal_handlers_unblock_by_func (currency_selector, (gpointer) csv_price_imp_preview_currency_sel_cb, this); + } + /* Release our reference for the stores to allow proper memory management. */ g_object_unref (store); g_object_unref (combostore); @@ -1460,6 +1649,13 @@ CsvImpPriceAssist::preview_refresh () price_imp->currency_format()); go_charmap_sel_set_encoding (encselector, price_imp->encoding().c_str()); + // Set the commodity and currency combos + set_commodity_for_combo(GTK_COMBO_BOX(commodity_selector), + price_imp->from_commodity()); + + set_commodity_for_combo(GTK_COMBO_BOX(currency_selector), + price_imp->to_currency()); + // Handle separator checkboxes and custom field, only relevant if the file format is csv if (price_imp->file_format() == GncImpFileFormat::CSV) { @@ -1533,9 +1729,9 @@ CsvImpPriceAssist::assist_summary_page_prepare () auto text = std::string(""); text += _("The prices were imported from the file '") + m_file_name + "'."; text += _("\n\nThe number of Prices added was ") + std::to_string(price_imp->m_prices_added); - text += _(" and ") + std::to_string(price_imp->m_prices_duplicated); - text += _(" were duplicated."); - text += ""; + text += _(", duplicated was ") + std::to_string(price_imp->m_prices_duplicated); + text += _(" and replaced was ") + std::to_string(price_imp->m_prices_replaced); + text += "."; gtk_label_set_markup (GTK_LABEL(summary_label), text.c_str()); } diff --git a/gnucash/import-export/csv-imp/assistant-csv-price-import.glade b/gnucash/import-export/csv-imp/assistant-csv-price-import.glade index 705e8274aa..764b1967a2 100644 --- a/gnucash/import-export/csv-imp/assistant-csv-price-import.glade +++ b/gnucash/import-export/csv-imp/assistant-csv-price-import.glade @@ -7,6 +7,26 @@ 1 10 + + + + + + + + + + + + + + + + + + + + 1000 1 @@ -515,7 +535,7 @@ Select location and file name for the Import, then click 'OK'... True True False - Normally prices are not over written, select this to change that. + Normally prices are not over written, select this to change that. This setting is not saved. True @@ -804,6 +824,9 @@ For example GTK_FILL + + + False @@ -811,6 +834,125 @@ For example 0 + + + True + False + + + True + False + 0 + none + + + True + False + 5 + 5 + 5 + + + True + False + + + True + False + liststore1 + + + + 0 + + + + + False + False + 0 + + + + + + + + + True + False + <b>Commodity From</b> + True + + + + + False + False + 0 + + + + + True + False + 0 + none + + + True + False + 5 + 5 + 5 + + + True + False + + + True + False + liststore1 + + + + 0 + + + + + False + False + 0 + + + + + + + + + True + False + <b>Currency To</b> + True + + + + + False + False + 2 + + + + + False + False + 1 + + True @@ -860,7 +1002,7 @@ For example True True - 1 + 2 @@ -902,7 +1044,7 @@ For example False False 5 - 2 + 3 @@ -930,7 +1072,7 @@ For example False False - 3 + 4 diff --git a/gnucash/import-export/csv-imp/gnc-price-import.cpp b/gnucash/import-export/csv-imp/gnc-price-import.cpp index 5437237ef1..e7bb72a0e5 100644 --- a/gnucash/import-export/csv-imp/gnc-price-import.cpp +++ b/gnucash/import-export/csv-imp/gnc-price-import.cpp @@ -125,7 +125,6 @@ void GncPriceImport::file_format(GncImpFileFormat format) auto fwtok = dynamic_cast(m_tokenizer.get()); fwtok->columns (m_settings.m_column_widths); } - } GncImpFileFormat GncPriceImport::file_format() @@ -140,6 +139,50 @@ void GncPriceImport::over_write (bool over) bool GncPriceImport::over_write () { return m_over_write; } +/** Sets a from commodity. This is the commodity all import data relates to. + * When a from commodity is set, there can't be any from columns selected + * in the import data. + * @param from_commodity Pointer to a commodity or NULL. + */ +void GncPriceImport::from_commodity (gnc_commodity* from_commodity) +{ + m_settings.m_from_commodity = from_commodity; + + if (m_settings.m_from_commodity) + { + auto col_type = std::find (m_settings.m_column_types_price.begin(), + m_settings.m_column_types_price.end(), GncPricePropType::FROM_COMMODITY); + + if (col_type != m_settings.m_column_types_price.end()) + set_column_type_price (col_type -m_settings.m_column_types_price.begin(), + GncPricePropType::NONE); + } +} + +gnc_commodity *GncPriceImport::from_commodity () { return m_settings.m_from_commodity; } + +/** Sets a to currency. This is the to currency all import data relates to. + * When a to currency is set, there can't be any to currency columns selected + * in the import data. + * @param to_currency Pointer to a commodity or NULL. + */ +void GncPriceImport::to_currency (gnc_commodity* to_currency) +{ + m_settings.m_to_currency = to_currency; + + if (m_settings.m_to_currency) + { + auto col_type = std::find (m_settings.m_column_types_price.begin(), + m_settings.m_column_types_price.end(), GncPricePropType::TO_CURRENCY); + + if (col_type != m_settings.m_column_types_price.end()) + set_column_type_price (col_type -m_settings.m_column_types_price.begin(), + GncPricePropType::NONE); + } +} + +gnc_commodity *GncPriceImport::to_currency () { return m_settings.m_to_currency; } + void GncPriceImport::reset_formatted_column (std::vector& col_types) { for (auto col_type: col_types) @@ -241,6 +284,8 @@ void GncPriceImport::settings (const CsvTransSettings& settings) /* First apply file format as this may recreate the tokenizer */ file_format (settings.m_file_format); /* Only then apply the other settings */ + from_commodity (m_settings.m_from_commodity); + to_currency (m_settings.m_to_currency); m_settings = settings; encoding (m_settings.m_encoding); @@ -402,14 +447,19 @@ void GncPriceImport::verify_column_selections (ErrorListPrice& error_msg) /* Verify a Currency to column is selected. */ - if (!check_for_column_type(GncPricePropType::CURRENCY_TO)) - error_msg.add_error( _("Please select a Currency to column.")); + if (!check_for_column_type(GncPricePropType::TO_CURRENCY)) + { + if (!m_settings.m_to_currency) + error_msg.add_error( _("Please select a Currency to column or set a Currency in the Currency To field.")); + } - /* Verify at least one from column (symbol_from or currency_from) column is selected. + /* Verify a Commodity from column is selected. */ - if (!check_for_column_type(GncPricePropType::SYMBOL_FROM) && - !check_for_column_type(GncPricePropType::CURRENCY_FROM)) - error_msg.add_error( _("Please select a symbol or currency from column.")); + if (!check_for_column_type(GncPricePropType::FROM_COMMODITY)) + { + if (!m_settings.m_from_commodity) + error_msg.add_error( _("Please select a Commodity from column or set a Commodity in the Commodity From field.")); + } } @@ -497,6 +547,40 @@ void GncPriceImport::create_price (std::vector::iterator& parsed_l error_message.clear(); + // Add a CURRENCY_TO property with the default currency to if no currency to column was set by the user + auto line_to_currency = price_props->get_to_currency(); + if (!line_to_currency) + { + if (m_settings.m_to_currency) + price_props->set_to_currency(m_settings.m_to_currency); + else + { + // Oops - the user didn't select an Account column *and* we didn't get a default value either! + // Note if you get here this suggests a bug in the code! + error_message = _("No Currency to column selected and no default Currency specified either.\n" + "This should never happen. Please report this as a bug."); + PINFO("User warning: %s", error_message.c_str()); + throw std::invalid_argument(error_message); + } + } + + // Add a COMMODITY_FROM property with the default commodity from if no commodity from column was set by the user + auto line_from_commodity = price_props->get_from_commodity(); + if (!line_from_commodity) + { + if (m_settings.m_from_commodity) + price_props->set_from_commodity(m_settings.m_from_commodity); + else + { + // Oops - the user didn't select an Account column *and* we didn't get a default value either! + // Note if you get here this suggests a bug in the code! + error_message = _("No Commodity from column selected and no default Commodity specified either.\n" + "This should never happen. Please report this as a bug."); + PINFO("User warning: %s", error_message.c_str()); + throw std::invalid_argument(error_message); + } + } + /* If column parsing was successful, convert price properties into a price. */ try { @@ -507,11 +591,12 @@ void GncPriceImport::create_price (std::vector::iterator& parsed_l /* If all went well, add this price to the list. */ auto price_created = price_props->create_price (book, pdb, m_over_write); -//FIXME Need to look at this - if (price_created) + if (price_created == ADDED) m_prices_added++; - else + else if (price_created == DUPLICATED) m_prices_duplicated++; + else if (price_created == REPLACED) + m_prices_replaced++; } catch (const std::invalid_argument& e) { @@ -537,6 +622,7 @@ void GncPriceImport::create_prices () m_prices_added = 0; m_prices_duplicated = 0; + m_prices_replaced = 0; /* Iterate over all parsed lines */ for (auto parsed_lines_it = m_parsed_lines.begin(); @@ -550,8 +636,8 @@ void GncPriceImport::create_prices () /* Should not throw anymore, otherwise verify needs revision */ create_price (parsed_lines_it); } - PINFO("Number of lines is %d, added is %d, duplicates is %d", - (int)m_parsed_lines.size(), m_prices_added, m_prices_duplicated); + PINFO("Number of lines is %d, added %d, duplicated %d, replaced %d", + (int)m_parsed_lines.size(), m_prices_added, m_prices_duplicated, m_prices_replaced); } bool @@ -611,6 +697,14 @@ GncPriceImport::set_column_type_price (uint32_t position, GncPricePropType type, m_settings.m_column_types_price.at (position) = type; + // If the user has set a Commodity from column, we can't have a commodity from default set + if (type == GncPricePropType::FROM_COMMODITY) + from_commodity (nullptr); + + // If the user has set a Currency to column, we can't have a currency to default set + if (type == GncPricePropType::TO_CURRENCY) + to_currency (nullptr); + /* Update the preparsed data */ for (auto parsed_lines_it = m_parsed_lines.begin(); parsed_lines_it != m_parsed_lines.end(); diff --git a/gnucash/import-export/csv-imp/gnc-price-import.hpp b/gnucash/import-export/csv-imp/gnc-price-import.hpp index 60cdc27807..809464eaea 100644 --- a/gnucash/import-export/csv-imp/gnc-price-import.hpp +++ b/gnucash/import-export/csv-imp/gnc-price-import.hpp @@ -32,7 +32,7 @@ extern "C" { #include "config.h" - +#include "gnc-commodity.h" } #include @@ -85,6 +85,12 @@ public: void over_write (bool over); bool over_write (); + void from_commodity (gnc_commodity *from_commodity); + gnc_commodity *from_commodity (); + + void to_currency (gnc_commodity *to_currency); + gnc_commodity *to_currency (); + void currency_format (int currency_format); int currency_format (); @@ -131,6 +137,7 @@ public: price properties. */ int m_prices_added; int m_prices_duplicated; + int m_prices_replaced; private: /** A helper function used by create_prices. It will attempt diff --git a/gnucash/import-export/csv-imp/gnc-price-props.cpp b/gnucash/import-export/csv-imp/gnc-price-props.cpp index a39f178851..e6f983e525 100644 --- a/gnucash/import-export/csv-imp/gnc-price-props.cpp +++ b/gnucash/import-export/csv-imp/gnc-price-props.cpp @@ -32,8 +32,6 @@ extern "C" { #include "engine-helpers.h" #include "gnc-ui-util.h" -#include "gnc-pricedb.h" - } #include @@ -48,9 +46,8 @@ std::map gnc_price_col_type_strs = { { GncPricePropType::NONE, N_("None") }, { GncPricePropType::DATE, N_("Date") }, { GncPricePropType::AMOUNT, N_("Amount") }, - { GncPricePropType::CURRENCY_FROM, N_("Currency From") }, - { GncPricePropType::CURRENCY_TO, N_("Currency To") }, - { GncPricePropType::SYMBOL_FROM, N_("Symbol From") }, + { GncPricePropType::FROM_COMMODITY, N_("Commodity From") }, + { GncPricePropType::TO_CURRENCY, N_("Currency To") }, }; /* Regular expressions used to parse dates per date format */ @@ -243,6 +240,7 @@ gnc_commodity* parse_commodity_price_comm (const std::string& comm_str) return comm; } +//FIXME can we change above to do below gnc_commodity * parse_commodity_price_sym (const std::string& sym_str, bool is_currency) { if (sym_str.empty()) @@ -285,8 +283,8 @@ gnc_commodity * parse_commodity_price_sym (const std::string& sym_str, bool is_c throw std::invalid_argument (_("Value can't be parsed into a valid commodity.")); else { - if (gnc_commodity_is_currency (retval) != is_currency) - throw std::invalid_argument (_("Value parsed into an invalid commodity for column type.")); + if ((is_currency == true) && (gnc_commodity_is_currency (retval) != true)) + throw std::invalid_argument (_("Value parsed into an invalid currency for currency column type.")); else return retval; } @@ -312,25 +310,18 @@ void GncImportPrice::set (GncPricePropType prop_type, const std::string& value) m_amount = parse_amount_price (value, m_currency_format); // Will throw if parsing fails break; - case GncPricePropType::CURRENCY_FROM: - m_currency_from = boost::none; - comm = parse_commodity_price_sym (value, true); // Throws if parsing fails - if (comm) - m_currency_from = comm; - break; - - case GncPricePropType::CURRENCY_TO: - m_currency_to = boost::none; - comm = parse_commodity_price_sym (value, true); // Throws if parsing fails - if (comm) - m_currency_to = comm; - break; - - case GncPricePropType::SYMBOL_FROM: - m_symbol_from = boost::none; + case GncPricePropType::FROM_COMMODITY: + m_from_commodity = boost::none; comm = parse_commodity_price_sym (value, false); // Throws if parsing fails if (comm) - m_symbol_from = comm; + m_from_commodity = comm; + break; + + case GncPricePropType::TO_CURRENCY: + m_to_currency = boost::none; + comm = parse_commodity_price_sym (value, true); // Throws if parsing fails + if (comm) + m_to_currency = comm; break; default: @@ -378,15 +369,15 @@ std::string GncImportPrice::verify_essentials (void) return _("No date column."); else if (m_amount == boost::none) return _("No amount column."); - else if (m_currency_to == boost::none) + else if (m_to_currency == boost::none) return _("No Currency to column."); - else if ((m_symbol_from == boost::none) && (m_currency_from == boost::none)) - return _("No from column."); + else if (m_from_commodity == boost::none) + return _("No Commodity from column."); else return std::string(); } -bool GncImportPrice::create_price (QofBook* book, GNCPriceDB *pdb, bool over) +Result GncImportPrice::create_price (QofBook* book, GNCPriceDB *pdb, bool over) { /* Gently refuse to create the price if the basics are not set correctly * This should have been tested before calling this function though! @@ -395,47 +386,40 @@ bool GncImportPrice::create_price (QofBook* book, GNCPriceDB *pdb, bool over) if (!check.empty()) { PWARN ("Refusing to create price because essentials not set properly: %s", check.c_str()); - return false; + return FAILED; } Timespec date; timespecFromTime64 (&date, *m_date); date.tv_nsec = 0; -#ifdef skip -//FIXME Numeric needs changing, copied from old version... bool rev = false; - gnc_commodity *comm_from = nullptr; + auto amount = *m_amount; - if (m_currency_from != boost::none) // Currency Import + GNCPrice *old_price = gnc_pricedb_lookup_day (pdb, *m_from_commodity, *m_to_currency, date); + + if (gnc_commodity_is_currency (*m_from_commodity)) // Currency Import { // Check for currency in reverse direction. - GNCPrice *rev_price = gnc_pricedb_lookup_day (pdb, *m_currency_to, *m_currency_from, date); - if (rev_price != nullptr) - rev = true; - gnc_price_unref (rev_price); + if (old_price != nullptr) + { + // Check for price in reverse direction. + if (gnc_commodity_equiv (gnc_price_get_currency (old_price), *m_from_commodity)) + rev = true; + + DEBUG("Commodity from is a Currency"); + } // Check for price less than 1, reverse if so. - if (gnc_numeric_compare (*m_amount, gnc_numeric_create (1, 1)) != 1) + if (*m_amount < GncNumeric(1,1)) rev = true; - comm_from = *m_currency_from; - DEBUG("Commodity from is a Currency"); } - else - comm_from = *m_symbol_from; - DEBUG("Date is %s, Rev is %d, Commodity from is '%s', Currency is '%s', Amount is %s", gnc_print_date (date), - rev, gnc_commodity_get_fullname (comm_from), gnc_commodity_get_fullname (*m_currency_to), - gnc_num_dbg_to_string (*m_amount) ); + rev, gnc_commodity_get_fullname (*m_from_commodity), gnc_commodity_get_fullname (*m_to_currency), + amount.to_string().c_str()); - GNCPrice *old_price = nullptr; - - // Should the commodities be reversed - if (rev) - old_price = gnc_pricedb_lookup_day (pdb, *m_currency_to, comm_from, date); - else - old_price = gnc_pricedb_lookup_day (pdb, comm_from, *m_currency_to, date); + Result ret_val = ADDED; // Should old price be over writen if ((old_price != nullptr) && (over == true)) @@ -444,31 +428,29 @@ bool GncImportPrice::create_price (QofBook* book, GNCPriceDB *pdb, bool over) gnc_pricedb_remove_price (pdb, old_price); gnc_price_unref (old_price); old_price = nullptr; + ret_val = REPLACED; } -#endif - bool ret_val = true; -#ifdef skip + // Create the new price if (old_price == nullptr) { DEBUG("Create"); GNCPrice *price = gnc_price_create (book); gnc_price_begin_edit (price); - if (rev) { - gnc_price_set_commodity (price, *m_currency_to); - gnc_price_set_currency (price, comm_from); - *m_amount = gnc_numeric_convert (gnc_numeric_invert (*m_amount), - CURRENCY_DENOM, GNC_HOW_RND_ROUND_HALF_UP); - gnc_price_set_value (price, *m_amount); + amount = amount.inv(); //invert the amount + gnc_price_set_commodity (price, *m_to_currency); + gnc_price_set_currency (price, *m_from_commodity); } else { - gnc_price_set_commodity (price, comm_from); - gnc_price_set_currency (price, *m_currency_to); - gnc_price_set_value (price, *m_amount); + gnc_price_set_commodity (price, *m_from_commodity); + gnc_price_set_currency (price, *m_to_currency); } + auto amount_conv = amount.convert(CURRENCY_DENOM); + gnc_price_set_value (price, static_cast(amount_conv)); + gnc_price_set_time (price, date); gnc_price_set_source (price, PRICE_SOURCE_USER_PRICE); //FIXME Not sure which one gnc_price_set_source (price, PRICE_SOURCE_FQ); @@ -479,15 +461,15 @@ bool GncImportPrice::create_price (QofBook* book, GNCPriceDB *pdb, bool over) gnc_price_unref (price); - if (perr == false) + if (perr == false) throw std::invalid_argument (_("Failed to create price from selected columns.")); //FIXME Not sure about this, should this be a PWARN } else - -#endif - ret_val = false; - + { + gnc_price_unref (old_price); + ret_val = DUPLICATED; + } return ret_val; } diff --git a/gnucash/import-export/csv-imp/gnc-price-props.hpp b/gnucash/import-export/csv-imp/gnc-price-props.hpp index 57083e9a4b..a8550ea1e3 100644 --- a/gnucash/import-export/csv-imp/gnc-price-props.hpp +++ b/gnucash/import-export/csv-imp/gnc-price-props.hpp @@ -49,12 +49,13 @@ enum class GncPricePropType { NONE, DATE, AMOUNT, - CURRENCY_FROM, - CURRENCY_TO, - SYMBOL_FROM, - PRICE_PROPS = SYMBOL_FROM + FROM_COMMODITY, + TO_CURRENCY, + PRICE_PROPS = TO_CURRENCY }; +enum Result { FAILED, ADDED, DUPLICATED, REPLACED }; + /** Maps all column types to a string representation. * The actual definition is in gnc-price-props.cpp. * Attention: that definition should be adjusted for any @@ -91,7 +92,14 @@ public: void set_currency_format (int currency_format) { m_currency_format = currency_format ;} void reset (GncPricePropType prop_type); std::string verify_essentials (void); - bool create_price (QofBook* book, GNCPriceDB *pdb, bool over); + Result create_price (QofBook* book, GNCPriceDB *pdb, bool over); + + gnc_commodity* get_from_commodity () { if (m_from_commodity) return *m_from_commodity; else return nullptr; } + void set_from_commodity (gnc_commodity* comm) { if (comm) m_from_commodity = comm; else m_from_commodity = boost::none; } + + gnc_commodity* get_to_currency () { if (m_to_currency) return *m_to_currency; else return nullptr; } + void set_to_currency (gnc_commodity* curr) { if (curr) m_to_currency = curr; else m_to_currency = boost::none; } + std::string errors(); private: @@ -99,9 +107,8 @@ private: int m_currency_format; boost::optional m_date; boost::optional m_amount; - boost::optional m_currency_from; - boost::optional m_currency_to; - boost::optional m_symbol_from; + boost::optional m_from_commodity; + boost::optional m_to_currency; bool created = false; std::map m_errors; From 1e31db74d1143b3a7cd7fe39f1e42a2f935c04d7 Mon Sep 17 00:00:00 2001 From: Robert Fewell <14uBobIT@gmail.com> Date: Fri, 1 Dec 2017 11:30:16 +0000 Subject: [PATCH 30/48] Made changes to preset column types to align with other changes These changes are to align with the changes to column types and also the basic setup of the new commodity from and currency to combo's. More changes will follow to make the saving and loading work properly. --- .../csv-imp/gnc-csv-trans-settings.cpp | 29 +++++++++++++++++-- .../csv-imp/gnc-csv-trans-settings.hpp | 8 +++-- 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/gnucash/import-export/csv-imp/gnc-csv-trans-settings.cpp b/gnucash/import-export/csv-imp/gnc-csv-trans-settings.cpp index 48cfdce9d2..5235a4ccf3 100644 --- a/gnucash/import-export/csv-imp/gnc-csv-trans-settings.cpp +++ b/gnucash/import-export/csv-imp/gnc-csv-trans-settings.cpp @@ -63,6 +63,8 @@ const std::string gnc_exp{N_("GnuCash Export Format")}; #define CSV_COL_TYPES "ColumnTypes" #define CSV_COL_WIDTHS "ColumnWidths" #define CSV_ACCOUNT "BaseAccount" +#define CSV_TO_CURR "PriceToCurrency" +#define CSV_FROM_COMM "PriceFromCommodity" G_GNUC_UNUSED static QofLogModule log_module = GNC_MOD_IMPORT; @@ -111,9 +113,8 @@ static std::shared_ptr create_int_gnc_exp_preset(void) preset->m_column_types_price = { GncPricePropType::DATE, GncPricePropType::AMOUNT, - GncPricePropType::CURRENCY_FROM, - GncPricePropType::CURRENCY_TO, - GncPricePropType::SYMBOL_FROM + GncPricePropType::FROM_COMMODITY, + GncPricePropType::TO_CURRENCY, }; return preset; @@ -266,6 +267,22 @@ CsvTransSettings::load (void) if (key_char) g_free (key_char); + key_char = g_key_file_get_string (keyfile, group.c_str(), CSV_TO_CURR, &key_error); + if (key_char && *key_char != '\0') +//FIXME m_to_currency = gnc_account_lookup_by_full_name (gnc_get_current_root_account(), key_char); + m_to_currency = nullptr; + m_load_error |= handle_load_error (&key_error, group); + if (key_char) + g_free (key_char); + + key_char = g_key_file_get_string (keyfile, group.c_str(), CSV_FROM_COMM, &key_error); + if (key_char && *key_char != '\0') +//FIXME m_from_commodity = gnc_account_lookup_by_full_name (gnc_get_current_root_account(), key_char); + m_from_commodity = nullptr; + m_load_error |= handle_load_error (&key_error, group); + if (key_char) + g_free (key_char); + m_column_types.clear(); gsize list_len; gchar** col_types_str = g_key_file_get_string_list (keyfile, group.c_str(), CSV_COL_TYPES, @@ -361,6 +378,12 @@ CsvTransSettings::save (void) if (m_base_account) g_key_file_set_string (keyfile, group.c_str(), CSV_ACCOUNT, gnc_account_get_full_name(m_base_account)); + if (m_to_currency) + g_key_file_set_string (keyfile, group.c_str(), CSV_TO_CURR, gnc_commodity_get_fullname(m_to_currency)); + + if (m_from_commodity) + g_key_file_set_string (keyfile, group.c_str(), CSV_FROM_COMM, gnc_commodity_get_fullname(m_from_commodity)); + std::vector col_types_str; for (auto col_type : m_column_types) col_types_str.push_back(gnc_csv_col_type_strs[col_type]); diff --git a/gnucash/import-export/csv-imp/gnc-csv-trans-settings.hpp b/gnucash/import-export/csv-imp/gnc-csv-trans-settings.hpp index 93275f2da0..dae837dfb8 100644 --- a/gnucash/import-export/csv-imp/gnc-csv-trans-settings.hpp +++ b/gnucash/import-export/csv-imp/gnc-csv-trans-settings.hpp @@ -31,6 +31,7 @@ extern "C" { #include #include "Account.h" +#include "gnc-commodity.h" } #include @@ -53,8 +54,8 @@ struct CsvTransSettings CsvTransSettings() : m_file_format (GncImpFileFormat::CSV), m_encoding {"UTF-8"}, m_multi_split (false), m_date_format {0}, m_currency_format {0}, m_skip_start_lines{0}, m_skip_end_lines{0}, m_skip_alt_lines (false), - m_separators {","}, m_base_account {nullptr}, - m_load_error {false} { } + m_separators {","}, m_base_account {nullptr}, m_to_currency {nullptr}, + m_from_commodity {nullptr}, m_load_error {false} { } /** Save the gathered widget properties to a key File. * @@ -97,6 +98,9 @@ std::vector m_column_types; // The Column types in order std::vector m_column_types_price; // The Column Price types in order std::vector m_column_widths; // The Column widths +gnc_commodity *m_to_currency; // Price To Currency +gnc_commodity *m_from_commodity; // Price From Commodity + bool m_load_error; // Was there an error while parsing the state file ? }; From 16845c3a2096906bf1a72eb30eae997745b39c3a Mon Sep 17 00:00:00 2001 From: Robert Fewell <14uBobIT@gmail.com> Date: Fri, 1 Dec 2017 11:32:48 +0000 Subject: [PATCH 31/48] Remove duplicated function --- .../import-export/csv-imp/gnc-price-props.cpp | 60 +++---------------- .../import-export/csv-imp/gnc-price-props.hpp | 1 - 2 files changed, 7 insertions(+), 54 deletions(-) diff --git a/gnucash/import-export/csv-imp/gnc-price-props.cpp b/gnucash/import-export/csv-imp/gnc-price-props.cpp index e6f983e525..d9293e13dd 100644 --- a/gnucash/import-export/csv-imp/gnc-price-props.cpp +++ b/gnucash/import-export/csv-imp/gnc-price-props.cpp @@ -240,56 +240,6 @@ gnc_commodity* parse_commodity_price_comm (const std::string& comm_str) return comm; } -//FIXME can we change above to do below -gnc_commodity * parse_commodity_price_sym (const std::string& sym_str, bool is_currency) -{ - if (sym_str.empty()) - return nullptr; - - auto commodity_table = gnc_get_current_commodities (); - GList *namespaces; - gnc_commodity *retval = nullptr; - gnc_commodity *tmp_commodity = nullptr; - char *tmp_namespace = nullptr; - GList *commodity_list = NULL; - GList *namespace_list = gnc_commodity_table_get_namespaces (commodity_table); - - namespace_list = g_list_first (namespace_list); - while (namespace_list != NULL && retval == NULL) - { - tmp_namespace = (char*)namespace_list->data; - DEBUG("Looking at namespace %s", tmp_namespace); - commodity_list = gnc_commodity_table_get_commodities (commodity_table, tmp_namespace); - commodity_list = g_list_first (commodity_list); - while (commodity_list != NULL && retval == NULL) - { - const char* tmp_mnemonic = NULL; - tmp_commodity = (gnc_commodity*)commodity_list->data; - DEBUG("Looking at commodity %s", gnc_commodity_get_fullname (tmp_commodity)); - tmp_mnemonic = gnc_commodity_get_mnemonic (tmp_commodity); - if (g_strcmp0 (tmp_mnemonic, sym_str.c_str()) == 0) - { - retval = tmp_commodity; - DEBUG("Commodity %s%s", gnc_commodity_get_fullname (retval), " matches."); - } - commodity_list = g_list_next (commodity_list); - } - namespace_list = g_list_next (namespace_list); - } - g_list_free (commodity_list); - g_list_free (namespace_list); - - if (!retval) - throw std::invalid_argument (_("Value can't be parsed into a valid commodity.")); - else - { - if ((is_currency == true) && (gnc_commodity_is_currency (retval) != true)) - throw std::invalid_argument (_("Value parsed into an invalid currency for currency column type.")); - else - return retval; - } -} - void GncImportPrice::set (GncPricePropType prop_type, const std::string& value) { try @@ -307,21 +257,25 @@ void GncImportPrice::set (GncPricePropType prop_type, const std::string& value) case GncPricePropType::AMOUNT: m_amount = boost::none; - m_amount = parse_amount_price (value, m_currency_format); // Will throw if parsing fails + m_amount = parse_amount_price (value, m_currency_format); // Throws if parsing fails break; case GncPricePropType::FROM_COMMODITY: m_from_commodity = boost::none; - comm = parse_commodity_price_sym (value, false); // Throws if parsing fails + comm = parse_commodity_price_comm (value); // Throws if parsing fails if (comm) m_from_commodity = comm; break; case GncPricePropType::TO_CURRENCY: m_to_currency = boost::none; - comm = parse_commodity_price_sym (value, true); // Throws if parsing fails + comm = parse_commodity_price_comm (value); // Throws if parsing fails if (comm) + { + if (gnc_commodity_is_currency (comm) != true) + throw std::invalid_argument (_("Value parsed into an invalid currency for a currency column type.")); m_to_currency = comm; + } break; default: diff --git a/gnucash/import-export/csv-imp/gnc-price-props.hpp b/gnucash/import-export/csv-imp/gnc-price-props.hpp index a8550ea1e3..9ae485323e 100644 --- a/gnucash/import-export/csv-imp/gnc-price-props.hpp +++ b/gnucash/import-export/csv-imp/gnc-price-props.hpp @@ -78,7 +78,6 @@ private: time64 parse_date_price (const std::string &date_str, int format); gnc_commodity* parse_commodity_price_comm (const std::string& comm_str); -gnc_commodity* parse_commodity_price_sym (const std::string& comm_str, bool is_currency); GncNumeric parse_amount_price (const std::string &str, int currency_format); struct GncImportPrice From 3c18b8063459a2ef2634c854c065764cbaa50bf7 Mon Sep 17 00:00:00 2001 From: Robert Fewell <14uBobIT@gmail.com> Date: Fri, 1 Dec 2017 11:34:21 +0000 Subject: [PATCH 32/48] Fix some errors in conversion of some function names Some function names did not get converted to a price equivalent and reorder some statements. --- .../import-export/csv-imp/assistant-csv-price-import.cpp | 8 ++++---- gnucash/import-export/csv-imp/gnc-price-import.cpp | 2 +- gnucash/import-export/csv-imp/gnc-price-import.hpp | 8 ++++---- gnucash/import-export/csv-imp/gnc-price-props.hpp | 1 - 4 files changed, 9 insertions(+), 10 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 120367a58e..2d2cfddc4c 100644 --- a/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp +++ b/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp @@ -607,9 +607,9 @@ CsvImpPriceAssist::CsvImpPriceAssist () /* Add in the date format combo box and hook it up to an event handler. */ date_format_combo = GTK_COMBO_BOX_TEXT(gtk_combo_box_text_new()); - for (int i = 0; i < num_date_formats; i++) + for (int i = 0; i < num_date_formats_price; i++) { - gtk_combo_box_text_append_text (date_format_combo, _(date_format_user[i])); + gtk_combo_box_text_append_text (date_format_combo, _(date_format_user_price[i])); } gtk_combo_box_set_active (GTK_COMBO_BOX(date_format_combo), 0); g_signal_connect (G_OBJECT(date_format_combo), "changed", @@ -622,9 +622,9 @@ CsvImpPriceAssist::CsvImpPriceAssist () /* Add in the currency format combo box and hook it up to an event handler. */ currency_format_combo = GTK_COMBO_BOX_TEXT(gtk_combo_box_text_new()); - for (int i = 0; i < num_currency_formats; i++) + for (int i = 0; i < num_currency_formats_price; i++) { - gtk_combo_box_text_append_text (currency_format_combo, _(currency_format_user[i])); + gtk_combo_box_text_append_text (currency_format_combo, _(currency_format_user_price[i])); } /* Default will the locale */ gtk_combo_box_set_active (GTK_COMBO_BOX(currency_format_combo), 0); diff --git a/gnucash/import-export/csv-imp/gnc-price-import.cpp b/gnucash/import-export/csv-imp/gnc-price-import.cpp index e7bb72a0e5..38a3cbcb57 100644 --- a/gnucash/import-export/csv-imp/gnc-price-import.cpp +++ b/gnucash/import-export/csv-imp/gnc-price-import.cpp @@ -284,9 +284,9 @@ void GncPriceImport::settings (const CsvTransSettings& settings) /* First apply file format as this may recreate the tokenizer */ file_format (settings.m_file_format); /* Only then apply the other settings */ + m_settings = settings; from_commodity (m_settings.m_from_commodity); to_currency (m_settings.m_to_currency); - m_settings = settings; encoding (m_settings.m_encoding); if (file_format() == GncImpFileFormat::CSV) diff --git a/gnucash/import-export/csv-imp/gnc-price-import.hpp b/gnucash/import-export/csv-imp/gnc-price-import.hpp index 809464eaea..be91941a86 100644 --- a/gnucash/import-export/csv-imp/gnc-price-import.hpp +++ b/gnucash/import-export/csv-imp/gnc-price-import.hpp @@ -46,12 +46,12 @@ extern "C" { #include /* A set of currency formats that the user sees. */ -extern const int num_currency_formats; -extern const gchar* currency_format_user[]; +extern const int num_currency_formats_price; +extern const gchar* currency_format_user_price[]; /* A set of date formats that the user sees. */ -extern const int num_date_formats; -extern const gchar* date_format_user[]; +extern const int num_date_formats_price; +extern const gchar* date_format_user_price[]; /** Tuple to hold * - a tokenized line of input diff --git a/gnucash/import-export/csv-imp/gnc-price-props.hpp b/gnucash/import-export/csv-imp/gnc-price-props.hpp index 9ae485323e..1d44ff6ae0 100644 --- a/gnucash/import-export/csv-imp/gnc-price-props.hpp +++ b/gnucash/import-export/csv-imp/gnc-price-props.hpp @@ -90,7 +90,6 @@ public: void set_date_format (int date_format) { m_date_format = date_format ;} void set_currency_format (int currency_format) { m_currency_format = currency_format ;} void reset (GncPricePropType prop_type); - std::string verify_essentials (void); Result create_price (QofBook* book, GNCPriceDB *pdb, bool over); gnc_commodity* get_from_commodity () { if (m_from_commodity) return *m_from_commodity; else return nullptr; } From 3a3c2cba9c55df5cb8ac0dfd9ee34801d353243f Mon Sep 17 00:00:00 2001 From: Robert Fewell <14uBobIT@gmail.com> Date: Fri, 1 Dec 2017 11:43:14 +0000 Subject: [PATCH 33/48] Change the settings file to save and load price settings. Added a setting type to distinguish between TRANS and PRICE settings so it can load a specific settings type and added the price save and load options. --- .../csv-imp/assistant-csv-price-import.cpp | 4 +- .../csv-imp/assistant-csv-trans-import.cpp | 4 +- .../csv-imp/gnc-csv-trans-settings.cpp | 204 +++++++++++------- .../csv-imp/gnc-csv-trans-settings.hpp | 26 ++- 4 files changed, 145 insertions(+), 93 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 2d2cfddc4c..e3a5ae3614 100644 --- a/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp +++ b/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp @@ -64,6 +64,8 @@ extern "C" /* This static indicates the debugging module that this .o belongs to. */ static QofLogModule log_module = GNC_MOD_ASSISTANT; +const std::string settings_type = "PRICE"; + class CsvImpPriceAssist { public: @@ -740,7 +742,7 @@ void CsvImpPriceAssist::preview_populate_settings_combo() // Append the default entry //FIXME get_trans_presets ???? - auto presets = get_trans_presets (); + auto presets = get_trans_presets (settings_type); for (auto preset : presets) { GtkTreeIter iter; 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 9402f38e94..07091f0bdc 100644 --- a/gnucash/import-export/csv-imp/assistant-csv-trans-import.cpp +++ b/gnucash/import-export/csv-imp/assistant-csv-trans-import.cpp @@ -70,6 +70,8 @@ extern "C" /* This static indicates the debugging module that this .o belongs to. */ static QofLogModule log_module = GNC_MOD_ASSISTANT; +const std::string settings_type = "TRANS"; + class CsvImpTransAssist { public: @@ -677,7 +679,7 @@ void CsvImpTransAssist::preview_populate_settings_combo() // Append the default entry - auto presets = get_trans_presets (); + auto presets = get_trans_presets (settings_type); for (auto preset : presets) { GtkTreeIter iter; diff --git a/gnucash/import-export/csv-imp/gnc-csv-trans-settings.cpp b/gnucash/import-export/csv-imp/gnc-csv-trans-settings.cpp index 5235a4ccf3..73ab2a59a6 100644 --- a/gnucash/import-export/csv-imp/gnc-csv-trans-settings.cpp +++ b/gnucash/import-export/csv-imp/gnc-csv-trans-settings.cpp @@ -41,7 +41,7 @@ extern "C" #include "gnc-ui-util.h" } -const std::string csv_group_prefix{"CSV - "}; +const std::string csv_group_prefix{"CSV-"}; const std::string no_settings{N_("No Settings")}; const std::string gnc_exp{N_("GnuCash Export Format")}; #define CSV_NAME "Name" @@ -70,10 +70,11 @@ G_GNUC_UNUSED static QofLogModule log_module = GNC_MOD_IMPORT; preset_vec presets; -static std::shared_ptr create_int_no_preset(void) +static std::shared_ptr create_int_no_preset(const std::string& set_type) { auto preset = std::make_shared(); preset->m_name = no_settings; + preset->m_settings_type = set_type; return preset; } @@ -109,14 +110,6 @@ static std::shared_ptr create_int_gnc_exp_preset(void) GncTransPropType::REC_DATE, GncTransPropType::PRICE }; - - preset->m_column_types_price = { - GncPricePropType::DATE, - GncPricePropType::AMOUNT, - GncPricePropType::FROM_COMMODITY, - GncPricePropType::TO_CURRENCY, - }; - return preset; } @@ -124,8 +117,9 @@ static std::shared_ptr create_int_gnc_exp_preset(void) * find * * find all settings entries in the state key file + * based on settings type. **************************************************/ -const preset_vec& get_trans_presets (void) +const preset_vec& get_trans_presets (const std::string& set_type) { // Search all Groups in the state key file for ones starting with prefix @@ -138,11 +132,12 @@ const preset_vec& get_trans_presets (void) for (gsize i=0; i < grouplength; i++) { auto group = std::string(groups[i]); - auto pos = group.find(csv_group_prefix); + auto gp = csv_group_prefix + set_type + " - "; + auto pos = group.find(gp); if (pos == std::string::npos) continue; - preset_names.push_back(group.substr(csv_group_prefix.size())); + preset_names.push_back(group.substr(gp.size())); } // string array from the state file is no longer needed now. g_strfreev (groups); @@ -154,18 +149,20 @@ const preset_vec& get_trans_presets (void) presets.clear(); /* Start with the internally generated ones */ - presets.push_back(create_int_no_preset()); - presets.push_back(create_int_gnc_exp_preset()); + presets.push_back(create_int_no_preset(set_type)); + + if (set_type.compare("TRANS") == 0) + presets.push_back(create_int_gnc_exp_preset()); /* Then add all the ones we found in the state file */ for (auto preset_name : preset_names) { auto preset = std::make_shared(); + preset->m_settings_type = set_type; preset->m_name = preset_name; preset->load(); presets.push_back(preset); } - return presets; } @@ -215,7 +212,7 @@ CsvTransSettings::load (void) GError *key_error = nullptr; m_load_error = false; - auto group = csv_group_prefix + m_name; + auto group = csv_group_prefix + m_settings_type + " - " + m_name; auto keyfile = gnc_state_get_current (); m_skip_start_lines = g_key_file_get_integer (keyfile, group.c_str(), CSV_SKIP_START, &key_error); @@ -260,56 +257,82 @@ CsvTransSettings::load (void) if (key_char) g_free (key_char); - key_char = g_key_file_get_string (keyfile, group.c_str(), CSV_ACCOUNT, &key_error); - if (key_char && *key_char != '\0') - m_base_account = gnc_account_lookup_by_full_name (gnc_get_current_root_account(), key_char); - m_load_error |= handle_load_error (&key_error, group); - if (key_char) - g_free (key_char); - - key_char = g_key_file_get_string (keyfile, group.c_str(), CSV_TO_CURR, &key_error); - if (key_char && *key_char != '\0') -//FIXME m_to_currency = gnc_account_lookup_by_full_name (gnc_get_current_root_account(), key_char); - m_to_currency = nullptr; - m_load_error |= handle_load_error (&key_error, group); - if (key_char) - g_free (key_char); - - key_char = g_key_file_get_string (keyfile, group.c_str(), CSV_FROM_COMM, &key_error); - if (key_char && *key_char != '\0') -//FIXME m_from_commodity = gnc_account_lookup_by_full_name (gnc_get_current_root_account(), key_char); - m_from_commodity = nullptr; - m_load_error |= handle_load_error (&key_error, group); - if (key_char) - g_free (key_char); - - m_column_types.clear(); gsize list_len; - gchar** col_types_str = g_key_file_get_string_list (keyfile, group.c_str(), CSV_COL_TYPES, - &list_len, &key_error); - for (uint32_t i = 0; i < list_len; i++) + + // Transactions + if (m_settings_type.compare("TRANS") == 0) { - auto col_types_it = std::find_if (gnc_csv_col_type_strs.begin(), - gnc_csv_col_type_strs.end(), test_prop_type_str (col_types_str[i])); - if (col_types_it != gnc_csv_col_type_strs.end()) + key_char = g_key_file_get_string (keyfile, group.c_str(), CSV_ACCOUNT, &key_error); + if (key_char && *key_char != '\0') + m_base_account = gnc_account_lookup_by_full_name (gnc_get_current_root_account(), key_char); + m_load_error |= handle_load_error (&key_error, group); + if (key_char) + g_free (key_char); + + m_column_types.clear(); + gchar** col_types_str = g_key_file_get_string_list (keyfile, group.c_str(), CSV_COL_TYPES, + &list_len, &key_error); + for (uint32_t i = 0; i < list_len; i++) { - /* Found a valid column type. Now check whether it is allowed - * in the selected mode (two-split vs multi-split) */ - auto prop = sanitize_trans_prop (col_types_it->first, m_multi_split); - m_column_types.push_back(prop); - if (prop != col_types_it->first) - PWARN("Found column type '%s', but this is blacklisted when multi-split mode is %s. " - "Inserting column type 'NONE' instead'.", - col_types_it->second, m_multi_split ? "enabled" : "disabled"); + auto col_types_it = std::find_if (gnc_csv_col_type_strs.begin(), + gnc_csv_col_type_strs.end(), test_prop_type_str (col_types_str[i])); + if (col_types_it != gnc_csv_col_type_strs.end()) + { + /* Found a valid column type. Now check whether it is allowed + * in the selected mode (two-split vs multi-split) */ + auto prop = sanitize_trans_prop (col_types_it->first, m_multi_split); + m_column_types.push_back(prop); + if (prop != col_types_it->first) + PWARN("Found column type '%s', but this is blacklisted when multi-split mode is %s. " + "Inserting column type 'NONE' instead'.", + col_types_it->second, m_multi_split ? "enabled" : "disabled"); + } + else + PWARN("Found invalid column type '%s'. Inserting column type 'NONE' instead'.", + col_types_str[i]); } - else - PWARN("Found invalid column type '%s'. Inserting column type 'NONE' instead'.", - col_types_str[i]); - + if (col_types_str) + g_strfreev (col_types_str); } - if (col_types_str) - g_strfreev (col_types_str); + + // Price + if (m_settings_type.compare("PRICE") == 0) + { + key_char = g_key_file_get_string (keyfile, group.c_str(), CSV_TO_CURR, &key_error); + if (key_char && *key_char != '\0') + m_to_currency = parse_commodity_price_comm (key_char); + m_load_error |= handle_load_error (&key_error, group); + if (key_char) + g_free (key_char); + key_char = g_key_file_get_string (keyfile, group.c_str(), CSV_FROM_COMM, &key_error); + if (key_char && *key_char != '\0') + m_from_commodity = parse_commodity_price_comm (key_char); + m_load_error |= handle_load_error (&key_error, group); + if (key_char) + g_free (key_char); + + m_column_types.clear(); + gchar** col_types_str_price = g_key_file_get_string_list (keyfile, group.c_str(), CSV_COL_TYPES, + &list_len, &key_error); + for (uint32_t i = 0; i < list_len; i++) + { + auto col_types_it = std::find_if (gnc_price_col_type_strs.begin(), + gnc_price_col_type_strs.end(), test_price_prop_type_str (col_types_str_price[i])); + if (col_types_it != gnc_price_col_type_strs.end()) + { + // Found a valid column type + m_column_types_price.push_back(col_types_it->first); + } + else + PWARN("Found invalid column type '%s'. Inserting column type 'NONE' instead'.", + col_types_str_price[i]); + } + if (col_types_str_price) + g_strfreev (col_types_str_price); + } + + // Widths m_column_widths.clear(); gint *col_widths_int = g_key_file_get_integer_list (keyfile, group.c_str(), CSV_COL_WIDTHS, &list_len, &key_error); @@ -347,14 +370,15 @@ CsvTransSettings::save (void) } auto keyfile = gnc_state_get_current (); - auto group = csv_group_prefix + m_name; + auto group = csv_group_prefix + m_settings_type + " - " + m_name; // Drop previous saved settings with this name g_key_file_remove_group (keyfile, group.c_str(), nullptr); // Start Saving the settings + // Common g_key_file_set_string (keyfile, group.c_str(), CSV_NAME, m_name.c_str()); - g_key_file_set_boolean (keyfile, group.c_str(), CSV_MULTI_SPLIT, m_multi_split); + g_key_file_set_integer (keyfile, group.c_str(), CSV_SKIP_START, m_skip_start_lines); g_key_file_set_integer (keyfile, group.c_str(), CSV_SKIP_END, m_skip_end_lines); g_key_file_set_boolean (keyfile, group.c_str(), CSV_SKIP_ALT, m_skip_alt_lines); @@ -375,27 +399,45 @@ CsvTransSettings::save (void) g_key_file_set_integer (keyfile, group.c_str(), CSV_CURRENCY, m_currency_format); g_key_file_set_string (keyfile, group.c_str(), CSV_ENCODING, m_encoding.c_str()); - if (m_base_account) - g_key_file_set_string (keyfile, group.c_str(), CSV_ACCOUNT, gnc_account_get_full_name(m_base_account)); - - if (m_to_currency) - g_key_file_set_string (keyfile, group.c_str(), CSV_TO_CURR, gnc_commodity_get_fullname(m_to_currency)); - - if (m_from_commodity) - g_key_file_set_string (keyfile, group.c_str(), CSV_FROM_COMM, gnc_commodity_get_fullname(m_from_commodity)); - - std::vector col_types_str; - for (auto col_type : m_column_types) - col_types_str.push_back(gnc_csv_col_type_strs[col_type]); - - if (!col_types_str.empty()) - g_key_file_set_string_list (keyfile, group.c_str(), CSV_COL_TYPES, - col_types_str.data(), col_types_str.size()); - if (!m_column_widths.empty()) g_key_file_set_integer_list (keyfile, group.c_str(), CSV_COL_WIDTHS, (gint*)(m_column_widths.data()), m_column_widths.size()); + // Transaction + if (m_settings_type.compare("TRANS") == 0) + { + g_key_file_set_boolean (keyfile, group.c_str(), CSV_MULTI_SPLIT, m_multi_split); + + if (m_base_account) + g_key_file_set_string (keyfile, group.c_str(), CSV_ACCOUNT, gnc_account_get_full_name(m_base_account)); + + std::vector col_types_str; + for (auto col_type : m_column_types) + col_types_str.push_back(gnc_csv_col_type_strs[col_type]); + + if (!col_types_str.empty()) + g_key_file_set_string_list (keyfile, group.c_str(), CSV_COL_TYPES, + col_types_str.data(), col_types_str.size()); + } + + // Price + if (m_settings_type.compare("PRICE") == 0) + { + if (m_to_currency) + g_key_file_set_string (keyfile, group.c_str(), CSV_TO_CURR, gnc_commodity_get_mnemonic(m_to_currency)); + + if (m_from_commodity) + g_key_file_set_string (keyfile, group.c_str(), CSV_FROM_COMM, gnc_commodity_get_mnemonic(m_from_commodity)); + + std::vector col_types_str_price; + for (auto col_type : m_column_types_price) + col_types_str_price.push_back(gnc_price_col_type_strs[col_type]); + + if (!col_types_str_price.empty()) + g_key_file_set_string_list (keyfile, group.c_str(), CSV_COL_TYPES, + col_types_str_price.data(), col_types_str_price.size()); + } + // Do a test read of encoding GError *key_error = nullptr; bool error = false; @@ -425,7 +467,7 @@ CsvTransSettings::remove (void) return; auto keyfile = gnc_state_get_current (); - auto group = csv_group_prefix + m_name; + auto group = csv_group_prefix + m_settings_type + " - " + m_name; g_key_file_remove_group (keyfile, group.c_str(), nullptr); } diff --git a/gnucash/import-export/csv-imp/gnc-csv-trans-settings.hpp b/gnucash/import-export/csv-imp/gnc-csv-trans-settings.hpp index dae837dfb8..7df293c546 100644 --- a/gnucash/import-export/csv-imp/gnc-csv-trans-settings.hpp +++ b/gnucash/import-export/csv-imp/gnc-csv-trans-settings.hpp @@ -54,8 +54,8 @@ struct CsvTransSettings CsvTransSettings() : m_file_format (GncImpFileFormat::CSV), m_encoding {"UTF-8"}, m_multi_split (false), m_date_format {0}, m_currency_format {0}, m_skip_start_lines{0}, m_skip_end_lines{0}, m_skip_alt_lines (false), - m_separators {","}, m_base_account {nullptr}, m_to_currency {nullptr}, - m_from_commodity {nullptr}, m_load_error {false} { } + m_separators {","}, m_load_error {false}, m_base_account {nullptr}, + m_from_commodity {nullptr}, m_to_currency {nullptr} { } /** Save the gathered widget properties to a key File. * @@ -81,7 +81,9 @@ void remove (void); */ bool read_only (void); +std::string m_settings_type; // Settings Type, TRANS, PRICE etc. +// Common Settings std::string m_name; // Name given to this preset by the user GncImpFileFormat m_file_format; // CSV import Format std::string m_encoding; // File encoding @@ -92,16 +94,17 @@ uint32_t m_skip_start_lines; // Number of header rows to skip uint32_t m_skip_end_lines; // Number of footer rows to skip bool m_skip_alt_lines; // Skip alternate rows std::string m_separators; // Separators for csv format - -Account *m_base_account; // Base account -std::vector m_column_types; // The Column types in order -std::vector m_column_types_price; // The Column Price types in order +bool m_load_error; // Was there an error while parsing the state file ? std::vector m_column_widths; // The Column widths -gnc_commodity *m_to_currency; // Price To Currency -gnc_commodity *m_from_commodity; // Price From Commodity +// Transaction Settings +Account *m_base_account; // Base account +std::vector m_column_types; // The Column types in order -bool m_load_error; // Was there an error while parsing the state file ? +// Price Settings +gnc_commodity *m_from_commodity; // Price From Commodity +gnc_commodity *m_to_currency; // Price To Currency +std::vector m_column_types_price; // The Price Column types in order }; using preset_vec = std::vector>; @@ -109,9 +112,12 @@ using preset_vec = std::vector>; * - one or more internally defined presets * - all preset found in the state key file. * + * @param set_type The type of setting stored in the + * key file, TRANS, PRICE, etc. + * * @return a reference to the populated vector. */ -const preset_vec& get_trans_presets (void); +const preset_vec& get_trans_presets (const std::string& set_type); /** Check whether name can be used as a preset name. * The names of the internal presets are considered reserved. From caba8c433021673d630b4f7459ada4290c2798ef Mon Sep 17 00:00:00 2001 From: Robert Fewell <14uBobIT@gmail.com> Date: Fri, 1 Dec 2017 11:44:38 +0000 Subject: [PATCH 34/48] Minor changes and tidy up --- .../csv-imp/assistant-csv-price-import.cpp | 26 ++++++------------- 1 file changed, 8 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 e3a5ae3614..43f2d8acee 100644 --- a/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp +++ b/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp @@ -76,7 +76,7 @@ public: void assist_preview_page_prepare (); void assist_confirm_page_prepare (); void assist_summary_page_prepare (); - void assist_finish (bool canceled); + void assist_finish (); void assist_compmgr_close (); void file_confirm_cb (); @@ -213,7 +213,6 @@ csv_price_imp_assist_destroy_cb (GtkWidget *object, CsvImpPriceAssist* info) void csv_price_imp_assist_cancel_cb (GtkAssistant *assistant, CsvImpPriceAssist* info) { - info->assist_finish (true); gnc_close_gui_component_by_data (ASSISTANT_CSV_IMPORT_PRICE_CM_CLASS, info); } @@ -226,7 +225,7 @@ csv_price_imp_assist_close_cb (GtkAssistant *assistant, CsvImpPriceAssist* info) void csv_price_imp_assist_finish_cb (GtkAssistant *assistant, CsvImpPriceAssist* info) { - info->assist_finish (false); + info->assist_finish (); } void csv_price_imp_file_confirm_cb (GtkWidget *button, CsvImpPriceAssist *info) @@ -356,7 +355,7 @@ gnc_commodity *get_commodity_from_combo (GtkComboBox *combo) GtkTreeModel *model, *sort_model; GtkTreeIter iter, siter; gchar *string; - gnc_commodity *comm; + gnc_commodity *comm; if (!gtk_combo_box_get_active_iter (combo, &siter)) return nullptr; @@ -741,7 +740,6 @@ void CsvImpPriceAssist::preview_populate_settings_combo() gtk_list_store_clear (GTK_LIST_STORE(model)); // Append the default entry -//FIXME get_trans_presets ???? auto presets = get_trans_presets (settings_type); for (auto preset : presets) { @@ -773,7 +771,7 @@ void CsvImpPriceAssist::preview_handle_save_del_sensitivity (GtkComboBox* combo) CsvTransSettings *preset; GtkTreeModel *model = gtk_combo_box_get_model (combo); gtk_tree_model_get (model, &iter, SET_GROUP, &preset, -1); -//FIXME + if (preset && !trans_preset_is_reserved_name (preset->m_name)) { /* Current preset is not read_only, so buttons can be enabled */ @@ -781,7 +779,6 @@ void CsvImpPriceAssist::preview_handle_save_del_sensitivity (GtkComboBox* combo) can_save = true; } } -//FIXME else if (entry_text && (strlen (entry_text) > 0) && !trans_preset_is_reserved_name (std::string(entry_text))) can_save = true; @@ -944,6 +941,8 @@ void CsvImpPriceAssist::preview_update_skipped_rows () preview_refresh_table (); } +/* Callback triggered when user clicks on Over Write option + */ void CsvImpPriceAssist::preview_over_write (bool over) { price_imp->over_write (over); @@ -1723,6 +1722,7 @@ CsvImpPriceAssist::assist_preview_page_prepare () void CsvImpPriceAssist::assist_confirm_page_prepare () { + /* Confirm Page */ } void @@ -1752,19 +1752,9 @@ CsvImpPriceAssist::assist_prepare_cb (GtkWidget *page) } void -CsvImpPriceAssist::assist_finish (bool canceled) +CsvImpPriceAssist::assist_finish () { /* Start the import */ -//FIXME Apply button -g_print("Finish\n"); -// if (canceled || price_imp->m_transactions.empty()) -// gnc_gen_trans_list_delete (gnc_csv_importer_gui); -// else -// gnc_gen_trans_assist_start (gnc_csv_importer_gui); - - -//FIXME Cancel comes here to, check when nothing set, goes to catch below also - /* Create prices from the parsed data */ try { From 1bb2d1dc3890236aaa9f996e84723555f5c334e9 Mon Sep 17 00:00:00 2001 From: Robert Fewell <14uBobIT@gmail.com> Date: Fri, 1 Dec 2017 11:47:12 +0000 Subject: [PATCH 35/48] Change the way commodity and currency combo's are shown. Use commodity print name to show in the combo's and use a hidden field to sort the list grouping by namespace. Also alter the way these settings are saved. --- .../csv-imp/assistant-csv-price-import.cpp | 23 ++++++++----------- .../csv-imp/gnc-csv-trans-settings.cpp | 16 ++++++++++--- 2 files changed, 23 insertions(+), 16 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 43f2d8acee..075f46389b 100644 --- a/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp +++ b/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp @@ -419,11 +419,11 @@ GtkTreeModel *get_model (bool all_commodity) store = GTK_TREE_MODEL(gtk_list_store_new (3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER)); model = gtk_tree_model_sort_new_with_model (store); - gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (model), - 0, GTK_SORT_ASCENDING); + // set sort to sort on second string, first string will be shown + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (model), 1, GTK_SORT_ASCENDING); gtk_list_store_append (GTK_LIST_STORE(store), &iter); - gtk_list_store_set (GTK_LIST_STORE(store), &iter, 0, " ", 1, nullptr, -1); + gtk_list_store_set (GTK_LIST_STORE(store), &iter, 0, " ", 1, " ", 2, nullptr, -1); namespace_list = g_list_first (namespace_list); while (namespace_list != nullptr) @@ -440,23 +440,20 @@ GtkTreeModel *get_model (bool all_commodity) commodity_list = g_list_first (commodity_list); while (commodity_list != nullptr) { - gchar *name_str; - gchar *save_str; - gchar *settings_str; + const gchar *name_str; + gchar *sort_str; tmp_commodity = (gnc_commodity*)commodity_list->data; DEBUG("Looking at commodity %s", gnc_commodity_get_fullname (tmp_commodity)); - name_str = g_strconcat (tmp_namespace, " : (", gnc_commodity_get_mnemonic (tmp_commodity), - ") ", gnc_commodity_get_fullname (tmp_commodity), nullptr); + name_str = gnc_commodity_get_printname (tmp_commodity); - settings_str = g_strconcat (tmp_namespace, "::", gnc_commodity_get_mnemonic (tmp_commodity), nullptr); - DEBUG("Name string is %s, Save string is %s", name_str, settings_str); + sort_str = g_strconcat (tmp_namespace, "::", gnc_commodity_get_mnemonic (tmp_commodity), nullptr); + DEBUG("Name string is %s, Sort string is %s", name_str, sort_str); gtk_list_store_append (GTK_LIST_STORE(store), &iter); - gtk_list_store_set (GTK_LIST_STORE(store), &iter, 0, name_str, 1, settings_str, 2, tmp_commodity, -1); + gtk_list_store_set (GTK_LIST_STORE(store), &iter, 0, name_str, 1, sort_str, 2, tmp_commodity, -1); - g_free (name_str); - g_free (settings_str); + g_free (sort_str); commodity_list = g_list_next (commodity_list); } } diff --git a/gnucash/import-export/csv-imp/gnc-csv-trans-settings.cpp b/gnucash/import-export/csv-imp/gnc-csv-trans-settings.cpp index 73ab2a59a6..3bbc9847ba 100644 --- a/gnucash/import-export/csv-imp/gnc-csv-trans-settings.cpp +++ b/gnucash/import-export/csv-imp/gnc-csv-trans-settings.cpp @@ -294,7 +294,7 @@ CsvTransSettings::load (void) if (col_types_str) g_strfreev (col_types_str); } - + // Price if (m_settings_type.compare("PRICE") == 0) { @@ -424,10 +424,20 @@ CsvTransSettings::save (void) if (m_settings_type.compare("PRICE") == 0) { if (m_to_currency) - g_key_file_set_string (keyfile, group.c_str(), CSV_TO_CURR, gnc_commodity_get_mnemonic(m_to_currency)); + { + auto unique_name = g_strconcat (gnc_commodity_get_namespace (m_to_currency), "::", + gnc_commodity_get_mnemonic (m_to_currency), nullptr); + g_key_file_set_string (keyfile, group.c_str(), CSV_TO_CURR, unique_name); + g_free (unique_name); + } if (m_from_commodity) - g_key_file_set_string (keyfile, group.c_str(), CSV_FROM_COMM, gnc_commodity_get_mnemonic(m_from_commodity)); + { + auto unique_name = g_strconcat (gnc_commodity_get_namespace (m_from_commodity), "::", + gnc_commodity_get_mnemonic (m_from_commodity), nullptr); + g_key_file_set_string (keyfile, group.c_str(), CSV_FROM_COMM, unique_name); + g_free (unique_name); + } std::vector col_types_str_price; for (auto col_type : m_column_types_price) From 61f860bcfcfb793a61d75fc48b7b1be47b6ae056 Mon Sep 17 00:00:00 2001 From: Robert Fewell <14uBobIT@gmail.com> Date: Fri, 1 Dec 2017 11:49:28 +0000 Subject: [PATCH 36/48] Various changes to comments in source files and displayed text. --- .../csv-imp/assistant-csv-price-import.cpp | 10 +++--- .../csv-imp/assistant-csv-price-import.glade | 18 +++++++--- .../csv-imp/gnc-price-import.cpp | 35 +++++++------------ .../import-export/csv-imp/gnc-price-props.cpp | 12 ++++--- .../import-export/csv-imp/gnc-price-props.hpp | 1 + 5 files changed, 40 insertions(+), 36 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 075f46389b..14a8ffaa65 100644 --- a/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp +++ b/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp @@ -434,7 +434,7 @@ GtkTreeModel *get_model (bool all_commodity) /* Hide the template entry */ if (g_utf8_collate (tmp_namespace, "template" ) != 0) { - if ((g_utf8_collate (tmp_namespace, GNC_COMMODITY_NS_CURRENCY ) == 0) || (all_commodity == true)) + if ((g_utf8_collate (tmp_namespace, GNC_COMMODITY_NS_CURRENCY ) == 0) || (all_commodity == true)) { commodity_list = gnc_commodity_table_get_commodities (commodity_table, tmp_namespace); commodity_list = g_list_first (commodity_list); @@ -1727,10 +1727,10 @@ CsvImpPriceAssist::assist_summary_page_prepare () { auto text = std::string(""); text += _("The prices were imported from the file '") + m_file_name + "'."; - text += _("\n\nThe number of Prices added was ") + std::to_string(price_imp->m_prices_added); - text += _(", duplicated was ") + std::to_string(price_imp->m_prices_duplicated); - text += _(" and replaced was ") + std::to_string(price_imp->m_prices_replaced); - text += "."; + text += _("\n\nThere were ") + std::to_string(price_imp->m_prices_added); + text += _(" Prices added, ") + std::to_string(price_imp->m_prices_duplicated); + text += _(" duplicated and ") + std::to_string(price_imp->m_prices_replaced); + text += _(" replaced."); gtk_label_set_markup (GTK_LABEL(summary_label), text.c_str()); } diff --git a/gnucash/import-export/csv-imp/assistant-csv-price-import.glade b/gnucash/import-export/csv-imp/assistant-csv-price-import.glade index 764b1967a2..33db2669c5 100644 --- a/gnucash/import-export/csv-imp/assistant-csv-price-import.glade +++ b/gnucash/import-export/csv-imp/assistant-csv-price-import.glade @@ -43,21 +43,27 @@ + + + + + + True False This assistant will help you import Prices from a CSV file. -There is a minimum number of columns that have to be present for a successful import, for Stock prices these are Date, Amount, Symbol From and for Currency they are Date, Amount, Currency From and Currency To. +There is a minimum number of columns that have to be present for a successful import, these are Date, Amount, Commodity From and Currency To. If all entries are for the same Commodity / Currency then you can select them and then the columns will be Date and Amount. -Various options exist for specifying the delimiter as well as a fixed width option. With the fixed width option, double click on the bar above the displayed rows to set the column width. +Various options exist for specifying the delimiter as well as a fixed width option. With the fixed width option, double click on the table of rows displayed to set a column width, then right mouse to change if required. Examples are "RR.L","21/11/2016",5.345,"GBP" and "USD","2016-11-21",1.56,"GBP" There is an option for specifying the start row, end row and an option to skip alternate rows beginning from the start row which can be used if you have some header text. Also there is an option to over write existing prices for that day if required. -On the preview page you can Load and Save the settings. To save the settings, select a previously saved entry or replace the text and press the Save Settings button. +Lastly, for repeated imports the preview page has buttons to Load and Save the settings. To save the settings, tweak the settings to your preferences (optionally starting from an existing preset), then (optionally change the settings name and press the Save Settings button. Note you can't save to built-in presets. This operation is not reversable, so make sure you have a working backup. @@ -536,6 +542,7 @@ Select location and file name for the Import, then click 'OK'... True False Normally prices are not over written, select this to change that. This setting is not saved. + False True @@ -1094,8 +1101,9 @@ For example True False - Press Apply to add Prices. -Cancel to abort. + <b>Press Apply to add the Prices. +Cancel to abort.</b> + True center True diff --git a/gnucash/import-export/csv-imp/gnc-price-import.cpp b/gnucash/import-export/csv-imp/gnc-price-import.cpp index 38a3cbcb57..d5d3862ad7 100644 --- a/gnucash/import-export/csv-imp/gnc-price-import.cpp +++ b/gnucash/import-export/csv-imp/gnc-price-import.cpp @@ -62,7 +62,7 @@ const gchar* currency_format_user_price[] = {N_("Locale"), /** Constructor for GncPriceImport. - * @return Pointer to a new GncCSvParseData + * @return Pointer to a new GncCsvParseData */ GncPriceImport::GncPriceImport(GncImpFileFormat format) { @@ -142,7 +142,7 @@ bool GncPriceImport::over_write () { return m_over_write; } /** Sets a from commodity. This is the commodity all import data relates to. * When a from commodity is set, there can't be any from columns selected * in the import data. - * @param from_commodity Pointer to a commodity or NULL. + * @param from_commodity pointer to a commodity or NULL. */ void GncPriceImport::from_commodity (gnc_commodity* from_commodity) { @@ -164,7 +164,7 @@ gnc_commodity *GncPriceImport::from_commodity () { return m_settings.m_from_comm /** Sets a to currency. This is the to currency all import data relates to. * When a to currency is set, there can't be any to currency columns selected * in the import data. - * @param to_currency Pointer to a commodity or NULL. + * @param to_currency pointer to a commodity or NULL. */ void GncPriceImport::to_currency (gnc_commodity* to_currency) { @@ -221,7 +221,6 @@ int GncPriceImport::date_format () { return m_settings.m_date_format; } */ void GncPriceImport::encoding (const std::string& encoding) { - // TODO investigate if we can catch conversion errors and report them if (m_tokenizer) { @@ -309,12 +308,10 @@ void GncPriceImport::settings (const CsvTransSettings& settings) std::copy_n (settings.m_column_types_price.begin(), std::min (m_settings.m_column_types_price.size(), settings.m_column_types_price.size()), m_settings.m_column_types_price.begin()); - } bool GncPriceImport::save_settings () { - if (trans_preset_is_reserved_name (m_settings.m_name)) return true; @@ -327,7 +324,6 @@ bool GncPriceImport::save_settings () auto fwtok = dynamic_cast(m_tokenizer.get()); m_settings.m_column_widths = fwtok->get_columns(); } - return m_settings.save(); } @@ -342,7 +338,6 @@ std::string GncPriceImport::settings_name () { return m_settings.m_name; } */ void GncPriceImport::load_file (const std::string& filename) { - /* Get the raw data first and handle an error if one occurs. */ try { @@ -407,7 +402,6 @@ void GncPriceImport::tokenize (bool guessColTypes) } } - struct ErrorListPrice { public: @@ -428,7 +422,6 @@ std::string ErrorListPrice::str() return m_error.substr(0, m_error.size() - 1); } - /* Test for the required minimum number of columns selected and * the selection is consistent. * @param An ErrorListPrice object to which all found issues are added. @@ -450,7 +443,7 @@ void GncPriceImport::verify_column_selections (ErrorListPrice& error_msg) if (!check_for_column_type(GncPricePropType::TO_CURRENCY)) { if (!m_settings.m_to_currency) - error_msg.add_error( _("Please select a Currency to column or set a Currency in the Currency To field.")); + error_msg.add_error( _("Please select a 'Currency to' column or set a Currency in the 'Currency To' field.")); } /* Verify a Commodity from column is selected. @@ -458,11 +451,10 @@ void GncPriceImport::verify_column_selections (ErrorListPrice& error_msg) if (!check_for_column_type(GncPricePropType::FROM_COMMODITY)) { if (!m_settings.m_from_commodity) - error_msg.add_error( _("Please select a Commodity from column or set a Commodity in the Commodity From field.")); + error_msg.add_error( _("Please select a 'Commodity from' column or set a Commodity in the 'Commodity From' field.")); } } - /* Check whether the chosen settings can successfully parse * the import data. This will check: * - there's at least one line selected for import @@ -547,7 +539,7 @@ void GncPriceImport::create_price (std::vector::iterator& parsed_l error_message.clear(); - // Add a CURRENCY_TO property with the default currency to if no currency to column was set by the user + // Add a CURRENCY_TO property with the selected 'currency to' if no 'currency to' column was set by the user auto line_to_currency = price_props->get_to_currency(); if (!line_to_currency) { @@ -555,16 +547,16 @@ void GncPriceImport::create_price (std::vector::iterator& parsed_l price_props->set_to_currency(m_settings.m_to_currency); else { - // Oops - the user didn't select an Account column *and* we didn't get a default value either! + // Oops - the user didn't select a 'currency to' column *and* we didn't get a selected value either! // Note if you get here this suggests a bug in the code! - error_message = _("No Currency to column selected and no default Currency specified either.\n" + error_message = _("No 'Currency to' column selected and no selected Currency specified either.\n" "This should never happen. Please report this as a bug."); PINFO("User warning: %s", error_message.c_str()); throw std::invalid_argument(error_message); } } - // Add a COMMODITY_FROM property with the default commodity from if no commodity from column was set by the user + // Add a COMMODITY_FROM property with the selected 'commodity from' if no 'commodity from' column was set by the user auto line_from_commodity = price_props->get_from_commodity(); if (!line_from_commodity) { @@ -572,9 +564,9 @@ void GncPriceImport::create_price (std::vector::iterator& parsed_l price_props->set_from_commodity(m_settings.m_from_commodity); else { - // Oops - the user didn't select an Account column *and* we didn't get a default value either! + // Oops - the user didn't select a 'commodity from' column *and* we didn't get a selected value either! // Note if you get here this suggests a bug in the code! - error_message = _("No Commodity from column selected and no default Commodity specified either.\n" + error_message = _("No 'Commodity from' column selected and no selected Commodity specified either.\n" "This should never happen. Please report this as a bug."); PINFO("User warning: %s", error_message.c_str()); throw std::invalid_argument(error_message); @@ -605,7 +597,6 @@ void GncPriceImport::create_price (std::vector::iterator& parsed_l } } - /** Creates a list of prices from parsed data. The parsed data * will first be validated. If any errors are found in lines that are marked * for processing (ie not marked to skip) this function will @@ -697,11 +688,11 @@ GncPriceImport::set_column_type_price (uint32_t position, GncPricePropType type, m_settings.m_column_types_price.at (position) = type; - // If the user has set a Commodity from column, we can't have a commodity from default set + // If the user has set a 'commodity from' column, we can't have a commodity from selected if (type == GncPricePropType::FROM_COMMODITY) from_commodity (nullptr); - // If the user has set a Currency to column, we can't have a currency to default set + // If the user has set a 'currency to' column, we can't have a currency to selected if (type == GncPricePropType::TO_CURRENCY) to_currency (nullptr); diff --git a/gnucash/import-export/csv-imp/gnc-price-props.cpp b/gnucash/import-export/csv-imp/gnc-price-props.cpp index d9293e13dd..2deffb3660 100644 --- a/gnucash/import-export/csv-imp/gnc-price-props.cpp +++ b/gnucash/import-export/csv-imp/gnc-price-props.cpp @@ -160,7 +160,7 @@ time64 parse_date_price (const std::string &date_str, int format) } -/** Convert str into a GncRational using the user-specified (import) currency format. +/** Convert str into a GncNumeric using the user-specified (import) currency format. * @param str The string to be parsed * @param currency_format The currency format to use. * @return a GncNumeric @@ -200,6 +200,11 @@ GncNumeric parse_amount_price (const std::string &str, int currency_format) return GncNumeric(val); } +/** Convert comm_str into a gnc_commodity. + * @param comm_str The string to be parsed + * @return a gnc_commodity + * @exception May throw std::invalid argument if string can't be parsed properly + */ gnc_commodity* parse_commodity_price_comm (const std::string& comm_str) { if (comm_str.empty()) @@ -324,9 +329,9 @@ std::string GncImportPrice::verify_essentials (void) else if (m_amount == boost::none) return _("No amount column."); else if (m_to_currency == boost::none) - return _("No Currency to column."); + return _("No 'Currency to' column."); else if (m_from_commodity == boost::none) - return _("No Commodity from column."); + return _("No 'Commodity from' column."); else return std::string(); } @@ -417,7 +422,6 @@ Result GncImportPrice::create_price (QofBook* book, GNCPriceDB *pdb, bool over) if (perr == false) throw std::invalid_argument (_("Failed to create price from selected columns.")); -//FIXME Not sure about this, should this be a PWARN } else { diff --git a/gnucash/import-export/csv-imp/gnc-price-props.hpp b/gnucash/import-export/csv-imp/gnc-price-props.hpp index 1d44ff6ae0..9ae485323e 100644 --- a/gnucash/import-export/csv-imp/gnc-price-props.hpp +++ b/gnucash/import-export/csv-imp/gnc-price-props.hpp @@ -90,6 +90,7 @@ public: void set_date_format (int date_format) { m_date_format = date_format ;} void set_currency_format (int currency_format) { m_currency_format = currency_format ;} void reset (GncPricePropType prop_type); + std::string verify_essentials (void); Result create_price (QofBook* book, GNCPriceDB *pdb, bool over); gnc_commodity* get_from_commodity () { if (m_from_commodity) return *m_from_commodity; else return nullptr; } From aff1c0c5a66e3d6861b25216066157754e4300d1 Mon Sep 17 00:00:00 2001 From: Robert Fewell <14uBobIT@gmail.com> Date: Fri, 1 Dec 2017 11:51:07 +0000 Subject: [PATCH 37/48] Add a test for from_commodity not being the same as to_currency --- gnucash/import-export/csv-imp/gnc-price-props.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/gnucash/import-export/csv-imp/gnc-price-props.cpp b/gnucash/import-export/csv-imp/gnc-price-props.cpp index 2deffb3660..0ea21f9773 100644 --- a/gnucash/import-export/csv-imp/gnc-price-props.cpp +++ b/gnucash/import-export/csv-imp/gnc-price-props.cpp @@ -269,7 +269,11 @@ void GncImportPrice::set (GncPricePropType prop_type, const std::string& value) m_from_commodity = boost::none; comm = parse_commodity_price_comm (value); // Throws if parsing fails if (comm) + { + if (m_to_currency == comm) + throw std::invalid_argument (_("'Commodity From' can not be the same as 'Currency To' column type.")); m_from_commodity = comm; + } break; case GncPricePropType::TO_CURRENCY: @@ -277,6 +281,8 @@ void GncImportPrice::set (GncPricePropType prop_type, const std::string& value) comm = parse_commodity_price_comm (value); // Throws if parsing fails if (comm) { + if (m_from_commodity == comm) + throw std::invalid_argument (_("'Currency To' can not be the same as 'Commodity From' column type.")); if (gnc_commodity_is_currency (comm) != true) throw std::invalid_argument (_("Value parsed into an invalid currency for a currency column type.")); m_to_currency = comm; @@ -332,6 +338,8 @@ std::string GncImportPrice::verify_essentials (void) return _("No 'Currency to' column."); else if (m_from_commodity == boost::none) return _("No 'Commodity from' column."); + else if (gnc_commodity_equal (*m_from_commodity, *m_to_currency)) + return _("'Commodity from' can not be the same as 'Currency to'."); else return std::string(); } From 62bbe4f951dbc8dbb9a1ac0b522e0fd33276e4b8 Mon Sep 17 00:00:00 2001 From: Robert Fewell <14uBobIT@gmail.com> Date: Fri, 1 Dec 2017 11:52:48 +0000 Subject: [PATCH 38/48] Add the ability to test from_commodity and to_currency being the same. To cover all combinations we need to test across the combo's and also the table entries when appropriate columns are set. Also need to force a reparse if any of the options change. --- .../csv-imp/assistant-csv-price-import.cpp | 37 +++++++++++++- .../csv-imp/gnc-price-import.cpp | 49 ++++++++++++++----- 2 files changed, 72 insertions(+), 14 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 14a8ffaa65..21126ea5b4 100644 --- a/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp +++ b/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp @@ -94,6 +94,7 @@ public: void preview_update_currency_format (); void preview_update_currency (); void preview_update_commodity (); + void preview_reparse_col_type (GncPricePropType type); void preview_update_col_type (GtkComboBox* cbox); void preview_update_fw_columns (GtkTreeView* treeview, GdkEventButton* event); @@ -1138,6 +1139,22 @@ enum PreviewDataTableCols { PREV_COL_ERR_ICON, PREV_N_FIXED_COLS }; + +void +CsvImpPriceAssist::preview_reparse_col_type (GncPricePropType type) +{ + auto column_types = price_imp->column_types_price(); + + // look for column type and force a reparse + auto col_type = std::find (column_types.begin(), + column_types.end(), type); + if (col_type != column_types.end()) + { + price_imp->set_column_type_price (col_type -column_types.begin(), + type, true); + } +} + /** Event handler for the user selecting a new column type. When the * user selects a new column type, that column's text must be changed * to the selection, and any other columns containing that selection @@ -1157,8 +1174,26 @@ void CsvImpPriceAssist::preview_update_col_type (GtkComboBox* cbox) gtk_tree_model_get (model, &iter, COL_TYPE_ID, &new_col_type, -1); auto col_num = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT(cbox), "col-num")); + + auto column_types = price_imp->column_types_price(); + auto old_col_type = column_types.at(col_num); + price_imp->set_column_type_price (col_num, new_col_type); + // if old_col_type is TO_CURRENCY, force a reparse of commodity + if (old_col_type == GncPricePropType::TO_CURRENCY) + { + // look for a from_commodity column to reparse + preview_reparse_col_type (GncPricePropType::FROM_COMMODITY); + } + + // if old_col_type is FROM_COMMODITY, force a reparse of currency + if (old_col_type == GncPricePropType::FROM_COMMODITY) + { + // look for a to_currency column to reparse + preview_reparse_col_type (GncPricePropType::TO_CURRENCY); + } + /* Delay rebuilding our data table to avoid critical warnings due to * pending events still acting on them after this event is processed. */ @@ -1580,7 +1615,7 @@ void CsvImpPriceAssist::preview_refresh_table () auto column_types = price_imp->column_types_price(); - // look for a commodity column, clear the commdoity combo + // look for a commodity column, clear the commodity combo auto col_type_comm = std::find (column_types.begin(), column_types.end(), GncPricePropType::FROM_COMMODITY); if (col_type_comm != column_types.end()) diff --git a/gnucash/import-export/csv-imp/gnc-price-import.cpp b/gnucash/import-export/csv-imp/gnc-price-import.cpp index d5d3862ad7..44a17b913e 100644 --- a/gnucash/import-export/csv-imp/gnc-price-import.cpp +++ b/gnucash/import-export/csv-imp/gnc-price-import.cpp @@ -136,7 +136,6 @@ void GncPriceImport::over_write (bool over) { m_over_write = over; } - bool GncPriceImport::over_write () { return m_over_write; } /** Sets a from commodity. This is the commodity all import data relates to. @@ -147,18 +146,20 @@ bool GncPriceImport::over_write () { return m_over_write; } void GncPriceImport::from_commodity (gnc_commodity* from_commodity) { m_settings.m_from_commodity = from_commodity; - if (m_settings.m_from_commodity) { - auto col_type = std::find (m_settings.m_column_types_price.begin(), + auto col_type_comm = std::find (m_settings.m_column_types_price.begin(), m_settings.m_column_types_price.end(), GncPricePropType::FROM_COMMODITY); - if (col_type != m_settings.m_column_types_price.end()) - set_column_type_price (col_type -m_settings.m_column_types_price.begin(), + if (col_type_comm != m_settings.m_column_types_price.end()) + set_column_type_price (col_type_comm -m_settings.m_column_types_price.begin(), GncPricePropType::NONE); + + // force a refresh of the to_currency if the from_commodity is changed + std::vector commodities = { GncPricePropType::TO_CURRENCY }; + reset_formatted_column (commodities); } } - gnc_commodity *GncPriceImport::from_commodity () { return m_settings.m_from_commodity; } /** Sets a to currency. This is the to currency all import data relates to. @@ -169,18 +170,20 @@ gnc_commodity *GncPriceImport::from_commodity () { return m_settings.m_from_comm void GncPriceImport::to_currency (gnc_commodity* to_currency) { m_settings.m_to_currency = to_currency; - if (m_settings.m_to_currency) { - auto col_type = std::find (m_settings.m_column_types_price.begin(), + auto col_type_currency = std::find (m_settings.m_column_types_price.begin(), m_settings.m_column_types_price.end(), GncPricePropType::TO_CURRENCY); - if (col_type != m_settings.m_column_types_price.end()) - set_column_type_price (col_type -m_settings.m_column_types_price.begin(), + if (col_type_currency != m_settings.m_column_types_price.end()) + set_column_type_price (col_type_currency -m_settings.m_column_types_price.begin(), GncPricePropType::NONE); + + // force a refresh of the from_commodity if the to_currency is changed + std::vector commodities = { GncPricePropType::FROM_COMMODITY }; + reset_formatted_column (commodities); } } - gnc_commodity *GncPriceImport::to_currency () { return m_settings.m_to_currency; } void GncPriceImport::reset_formatted_column (std::vector& col_types) @@ -453,6 +456,14 @@ void GncPriceImport::verify_column_selections (ErrorListPrice& error_msg) if (!m_settings.m_from_commodity) error_msg.add_error( _("Please select a 'Commodity from' column or set a Commodity in the 'Commodity From' field.")); } + + /* Verify a 'Commodity from' does not equal 'Currency to'. + */ + if ((m_settings.m_to_currency) && (m_settings.m_from_commodity)) + { + if (gnc_commodity_equal (m_settings.m_to_currency, m_settings.m_from_commodity)) + error_msg.add_error( _("'Commodity From' can not be the same as 'Currency To'.")); + } } /* Check whether the chosen settings can successfully parse @@ -539,7 +550,7 @@ void GncPriceImport::create_price (std::vector::iterator& parsed_l error_message.clear(); - // Add a CURRENCY_TO property with the selected 'currency to' if no 'currency to' column was set by the user + // Add a TO_CURRENCY property with the selected 'currency to' if no 'currency to' column was set by the user auto line_to_currency = price_props->get_to_currency(); if (!line_to_currency) { @@ -556,7 +567,7 @@ void GncPriceImport::create_price (std::vector::iterator& parsed_l } } - // Add a COMMODITY_FROM property with the selected 'commodity from' if no 'commodity from' column was set by the user + // Add a FROM_COMMODITY property with the selected 'commodity from' if no 'commodity from' column was set by the user auto line_from_commodity = price_props->get_from_commodity(); if (!line_from_commodity) { @@ -657,6 +668,18 @@ void GncPriceImport::update_price_props (uint32_t row, uint32_t col, GncPricePro { try { + // set the from_commodity based on combo so we can test for same. + if (prop_type == GncPricePropType::TO_CURRENCY) + { + if (m_settings.m_from_commodity) + price_props->set_from_commodity (m_settings.m_from_commodity); + } + // set the to_currency based on combo so we can test for same. + if (prop_type == GncPricePropType::FROM_COMMODITY) + { + if (m_settings.m_to_currency) + price_props->set_to_currency (m_settings.m_to_currency); + } price_props->set(prop_type, value); } catch (const std::exception& e) From 644a0aa06f09c49a99dd013f25c74af999f4fc46 Mon Sep 17 00:00:00 2001 From: Robert Fewell <14uBobIT@gmail.com> Date: Fri, 1 Dec 2017 11:56:25 +0000 Subject: [PATCH 39/48] Reorder the create price procedure. --- .../import-export/csv-imp/gnc-price-props.cpp | 44 +++++++++---------- 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/gnucash/import-export/csv-imp/gnc-price-props.cpp b/gnucash/import-export/csv-imp/gnc-price-props.cpp index 0ea21f9773..539f316851 100644 --- a/gnucash/import-export/csv-imp/gnc-price-props.cpp +++ b/gnucash/import-export/csv-imp/gnc-price-props.cpp @@ -362,32 +362,10 @@ Result GncImportPrice::create_price (QofBook* book, GNCPriceDB *pdb, bool over) bool rev = false; auto amount = *m_amount; + Result ret_val = ADDED; GNCPrice *old_price = gnc_pricedb_lookup_day (pdb, *m_from_commodity, *m_to_currency, date); - if (gnc_commodity_is_currency (*m_from_commodity)) // Currency Import - { - // Check for currency in reverse direction. - if (old_price != nullptr) - { - // Check for price in reverse direction. - if (gnc_commodity_equiv (gnc_price_get_currency (old_price), *m_from_commodity)) - rev = true; - - DEBUG("Commodity from is a Currency"); - } - - // Check for price less than 1, reverse if so. - if (*m_amount < GncNumeric(1,1)) - rev = true; - - } - DEBUG("Date is %s, Rev is %d, Commodity from is '%s', Currency is '%s', Amount is %s", gnc_print_date (date), - rev, gnc_commodity_get_fullname (*m_from_commodity), gnc_commodity_get_fullname (*m_to_currency), - amount.to_string().c_str()); - - Result ret_val = ADDED; - // Should old price be over writen if ((old_price != nullptr) && (over == true)) { @@ -398,6 +376,26 @@ Result GncImportPrice::create_price (QofBook* book, GNCPriceDB *pdb, bool over) ret_val = REPLACED; } + if (gnc_commodity_is_currency (*m_from_commodity)) // Currency Import + { + // Check for currency in reverse direction. + if (old_price != nullptr) + { + // Check for price in reverse direction. + if (gnc_commodity_equiv (gnc_price_get_currency (old_price), *m_from_commodity)) + rev = true; + } + DEBUG("Commodity from is a Currency"); + + // Check for price less than 1, reverse if so. + if (*m_amount < GncNumeric(1,1)) + rev = true; + + } + DEBUG("Date is %s, Rev is %d, Commodity from is '%s', Currency is '%s', Amount is %s", gnc_print_date (date), + rev, gnc_commodity_get_fullname (*m_from_commodity), gnc_commodity_get_fullname (*m_to_currency), + amount.to_string().c_str()); + // Create the new price if (old_price == nullptr) { From 71bf7d01fdd1b044723daec5cfbba2cb17c061bf Mon Sep 17 00:00:00 2001 From: Robert Fewell <14uBobIT@gmail.com> Date: Fri, 1 Dec 2017 12:03:14 +0000 Subject: [PATCH 40/48] Rename gnc-csv-trans-settings.* to gnc-csv-import-settings.* Change the name of the import settings files as they do not just deal with transactions and all associated required changes. --- gnucash/import-export/csv-imp/CMakeLists.txt | 4 +-- gnucash/import-export/csv-imp/Makefile.am | 4 +-- .../csv-imp/assistant-csv-price-import.cpp | 16 ++++----- .../csv-imp/assistant-csv-trans-import.cpp | 16 ++++----- ...ttings.cpp => gnc-csv-import-settings.cpp} | 34 +++++++++---------- ...ttings.hpp => gnc-csv-import-settings.hpp} | 20 +++++------ .../csv-imp/gnc-price-import.cpp | 6 ++-- .../csv-imp/gnc-price-import.hpp | 6 ++-- .../import-export/csv-imp/gnc-tx-import.cpp | 6 ++-- .../import-export/csv-imp/gnc-tx-import.hpp | 6 ++-- 10 files changed, 59 insertions(+), 59 deletions(-) rename gnucash/import-export/csv-imp/{gnc-csv-trans-settings.cpp => gnc-csv-import-settings.cpp} (95%) rename gnucash/import-export/csv-imp/{gnc-csv-trans-settings.hpp => gnc-csv-import-settings.hpp} (90%) diff --git a/gnucash/import-export/csv-imp/CMakeLists.txt b/gnucash/import-export/csv-imp/CMakeLists.txt index 44a14cacec..65082be1c7 100644 --- a/gnucash/import-export/csv-imp/CMakeLists.txt +++ b/gnucash/import-export/csv-imp/CMakeLists.txt @@ -17,7 +17,7 @@ SET(csv_import_SOURCES gnc-csv-account-map.c gnc-csv-gnumeric-popup.c gnc-csv-tokenizer.cpp - gnc-csv-trans-settings.cpp + gnc-csv-import-settings.cpp gnc-dummy-tokenizer.cpp gnc-fw-tokenizer.cpp gnc-price-import.cpp @@ -45,7 +45,7 @@ SET(csv_import_noinst_HEADERS gnc-csv-account-map.h gnc-csv-gnumeric-popup.h gnc-csv-tokenizer.hpp - gnc-csv-trans-settings.hpp + gnc-csv-import-settings.hpp gnc-dummy-tokenizer.hpp gnc-fw-tokenizer.hpp gnc-price-import.hpp diff --git a/gnucash/import-export/csv-imp/Makefile.am b/gnucash/import-export/csv-imp/Makefile.am index 4b473de869..f1b501f7ea 100644 --- a/gnucash/import-export/csv-imp/Makefile.am +++ b/gnucash/import-export/csv-imp/Makefile.am @@ -19,7 +19,7 @@ libgncmod_csv_import_la_SOURCES = \ gnc-tokenizer.cpp \ gnc-tx-import.cpp \ gnc-trans-props.cpp \ - gnc-csv-trans-settings.cpp + gnc-csv-import-settings.cpp noinst_HEADERS = \ assistant-csv-account-import.h \ @@ -37,7 +37,7 @@ noinst_HEADERS = \ gnc-tokenizer.hpp \ gnc-tx-import.hpp \ gnc-trans-props.hpp \ - gnc-csv-trans-settings.hpp + gnc-csv-import-settings.hpp libgncmod_csv_import_la_LDFLAGS = -avoid-version 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 21126ea5b4..0fa6273d4b 100644 --- a/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp +++ b/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp @@ -52,7 +52,7 @@ extern "C" #include "go-charmap-sel.h" } -#include "gnc-csv-trans-settings.hpp" +#include "gnc-csv-import-settings.hpp" #include "gnc-price-import.hpp" #include "gnc-fw-tokenizer.hpp" #include "gnc-csv-tokenizer.hpp" @@ -738,7 +738,7 @@ void CsvImpPriceAssist::preview_populate_settings_combo() gtk_list_store_clear (GTK_LIST_STORE(model)); // Append the default entry - auto presets = get_trans_presets (settings_type); + auto presets = get_import_presets (settings_type); for (auto preset : presets) { GtkTreeIter iter; @@ -766,11 +766,11 @@ void CsvImpPriceAssist::preview_handle_save_del_sensitivity (GtkComboBox* combo) /* Handle sensitivity of the delete and save button */ if (gtk_combo_box_get_active_iter (combo, &iter)) { - CsvTransSettings *preset; + CsvImportSettings *preset; GtkTreeModel *model = gtk_combo_box_get_model (combo); gtk_tree_model_get (model, &iter, SET_GROUP, &preset, -1); - if (preset && !trans_preset_is_reserved_name (preset->m_name)) + if (preset && !preset_is_reserved_name (preset->m_name)) { /* Current preset is not read_only, so buttons can be enabled */ can_delete = true; @@ -778,7 +778,7 @@ void CsvImpPriceAssist::preview_handle_save_del_sensitivity (GtkComboBox* combo) } } else if (entry_text && (strlen (entry_text) > 0) && - !trans_preset_is_reserved_name (std::string(entry_text))) + !preset_is_reserved_name (std::string(entry_text))) can_save = true; gtk_widget_set_sensitive (save_button, can_save); @@ -807,7 +807,7 @@ CsvImpPriceAssist::preview_settings_load () if (!gtk_combo_box_get_active_iter (settings_combo, &iter)) return; - CsvTransSettings *preset = nullptr; + CsvImportSettings *preset = nullptr; auto model = gtk_combo_box_get_model (settings_combo); gtk_tree_model_get (model, &iter, SET_GROUP, &preset, -1); @@ -834,7 +834,7 @@ CsvImpPriceAssist::preview_settings_delete () if (!gtk_combo_box_get_active_iter (settings_combo, &iter)) return; - CsvTransSettings *preset = nullptr; + CsvImportSettings *preset = nullptr; auto model = gtk_combo_box_get_model (settings_combo); gtk_tree_model_get (model, &iter, SET_GROUP, &preset, -1); @@ -868,7 +868,7 @@ CsvImpPriceAssist::preview_settings_save () while (valid) { // Walk through the list, reading each row - CsvTransSettings *preset; + CsvImportSettings *preset; gtk_tree_model_get (model, &iter, SET_GROUP, &preset, -1); if (preset && (preset->m_name == std::string(new_name))) 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 07091f0bdc..41779c63cd 100644 --- a/gnucash/import-export/csv-imp/assistant-csv-trans-import.cpp +++ b/gnucash/import-export/csv-imp/assistant-csv-trans-import.cpp @@ -58,7 +58,7 @@ extern "C" #include "go-charmap-sel.h" } -#include "gnc-csv-trans-settings.hpp" +#include "gnc-csv-import-settings.hpp" #include "gnc-tx-import.hpp" #include "gnc-fw-tokenizer.hpp" #include "gnc-csv-tokenizer.hpp" @@ -679,7 +679,7 @@ void CsvImpTransAssist::preview_populate_settings_combo() // Append the default entry - auto presets = get_trans_presets (settings_type); + auto presets = get_import_presets (settings_type); for (auto preset : presets) { GtkTreeIter iter; @@ -707,11 +707,11 @@ void CsvImpTransAssist::preview_handle_save_del_sensitivity (GtkComboBox* combo) /* Handle sensitivity of the delete and save button */ if (gtk_combo_box_get_active_iter (combo, &iter)) { - CsvTransSettings *preset; + CsvImportSettings *preset; GtkTreeModel *model = gtk_combo_box_get_model (combo); gtk_tree_model_get (model, &iter, SET_GROUP, &preset, -1); - if (preset && !trans_preset_is_reserved_name (preset->m_name)) + if (preset && !preset_is_reserved_name (preset->m_name)) { /* Current preset is not read_only, so buttons can be enabled */ can_delete = true; @@ -719,7 +719,7 @@ void CsvImpTransAssist::preview_handle_save_del_sensitivity (GtkComboBox* combo) } } else if (entry_text && (strlen (entry_text) > 0) && - !trans_preset_is_reserved_name (std::string(entry_text))) + !preset_is_reserved_name (std::string(entry_text))) can_save = true; gtk_widget_set_sensitive (save_button, can_save); @@ -752,7 +752,7 @@ CsvImpTransAssist::preview_settings_load () if (!gtk_combo_box_get_active_iter (settings_combo, &iter)) return; - CsvTransSettings *preset = nullptr; + CsvImportSettings *preset = nullptr; auto model = gtk_combo_box_get_model (settings_combo); gtk_tree_model_get (model, &iter, SET_GROUP, &preset, -1); @@ -779,7 +779,7 @@ CsvImpTransAssist::preview_settings_delete () if (!gtk_combo_box_get_active_iter (settings_combo, &iter)) return; - CsvTransSettings *preset = nullptr; + CsvImportSettings *preset = nullptr; auto model = gtk_combo_box_get_model (settings_combo); gtk_tree_model_get (model, &iter, SET_GROUP, &preset, -1); @@ -812,7 +812,7 @@ CsvImpTransAssist::preview_settings_save () while (valid) { // Walk through the list, reading each row - CsvTransSettings *preset; + CsvImportSettings *preset; gtk_tree_model_get (model, &iter, SET_GROUP, &preset, -1); if (preset && (preset->m_name == std::string(new_name))) diff --git a/gnucash/import-export/csv-imp/gnc-csv-trans-settings.cpp b/gnucash/import-export/csv-imp/gnc-csv-import-settings.cpp similarity index 95% rename from gnucash/import-export/csv-imp/gnc-csv-trans-settings.cpp rename to gnucash/import-export/csv-imp/gnc-csv-import-settings.cpp index 3bbc9847ba..0096c5397e 100644 --- a/gnucash/import-export/csv-imp/gnc-csv-trans-settings.cpp +++ b/gnucash/import-export/csv-imp/gnc-csv-import-settings.cpp @@ -1,5 +1,5 @@ /*******************************************************************\ - * gnc-csv-trans-settings.c -- Save and Load CSV Import Settings * + * gnc-csv-import-settings.c -- Save and Load CSV Import Settings * * * * Copyright (C) 2014 Robert Fewell * * * @@ -20,13 +20,13 @@ * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * * Boston, MA 02110-1301, USA gnu@gnu.org * \********************************************************************/ -/** @file gnc-csv-trans-settings.c +/** @file gnc-csv-import-settings.c @brief CSV Import Settings @author Copyright (c) 2014 Robert Fewell @author Copyright (c) 2016 Geert Janssens */ -#include "gnc-csv-trans-settings.hpp" +#include "gnc-csv-import-settings.hpp" #include extern "C" @@ -70,18 +70,18 @@ G_GNUC_UNUSED static QofLogModule log_module = GNC_MOD_IMPORT; preset_vec presets; -static std::shared_ptr create_int_no_preset(const std::string& set_type) +static std::shared_ptr create_int_no_preset(const std::string& set_type) { - auto preset = std::make_shared(); + auto preset = std::make_shared(); preset->m_name = no_settings; preset->m_settings_type = set_type; return preset; } -static std::shared_ptr create_int_gnc_exp_preset(void) +static std::shared_ptr create_int_gnc_exp_preset(void) { - auto preset = std::make_shared(); + auto preset = std::make_shared(); preset->m_name = gnc_exp; preset->m_skip_start_lines = 1; preset->m_multi_split = true; @@ -119,7 +119,7 @@ static std::shared_ptr create_int_gnc_exp_preset(void) * find all settings entries in the state key file * based on settings type. **************************************************/ -const preset_vec& get_trans_presets (const std::string& set_type) +const preset_vec& get_import_presets (const std::string& set_type) { // Search all Groups in the state key file for ones starting with prefix @@ -157,7 +157,7 @@ const preset_vec& get_trans_presets (const std::string& set_type) /* Then add all the ones we found in the state file */ for (auto preset_name : preset_names) { - auto preset = std::make_shared(); + auto preset = std::make_shared(); preset->m_settings_type = set_type; preset->m_name = preset_name; preset->load(); @@ -166,7 +166,7 @@ const preset_vec& get_trans_presets (const std::string& set_type) return presets; } -bool trans_preset_is_reserved_name (const std::string& name) +bool preset_is_reserved_name (const std::string& name) { return ((name == no_settings) || (name == _(no_settings.c_str())) || @@ -205,9 +205,9 @@ handle_load_error (GError **key_error, const std::string& group) * load the settings from a state key file **************************************************/ bool -CsvTransSettings::load (void) +CsvImportSettings::load (void) { - if (trans_preset_is_reserved_name (m_name)) + if (preset_is_reserved_name (m_name)) return true; GError *key_error = nullptr; @@ -355,9 +355,9 @@ CsvTransSettings::load (void) * save settings to a key file **************************************************/ bool -CsvTransSettings::save (void) +CsvImportSettings::save (void) { - if (trans_preset_is_reserved_name (m_name)) + if (preset_is_reserved_name (m_name)) { PWARN ("Ignoring attempt to save to reserved name '%s'", m_name.c_str()); return true; @@ -471,9 +471,9 @@ CsvTransSettings::save (void) } void -CsvTransSettings::remove (void) +CsvImportSettings::remove (void) { - if (trans_preset_is_reserved_name (m_name)) + if (preset_is_reserved_name (m_name)) return; auto keyfile = gnc_state_get_current (); @@ -483,7 +483,7 @@ CsvTransSettings::remove (void) bool -CsvTransSettings::read_only (void) +CsvImportSettings::read_only (void) { return ((m_name == no_settings) || (m_name == _(no_settings.c_str())) || diff --git a/gnucash/import-export/csv-imp/gnc-csv-trans-settings.hpp b/gnucash/import-export/csv-imp/gnc-csv-import-settings.hpp similarity index 90% rename from gnucash/import-export/csv-imp/gnc-csv-trans-settings.hpp rename to gnucash/import-export/csv-imp/gnc-csv-import-settings.hpp index 7df293c546..e6ce4d4f22 100644 --- a/gnucash/import-export/csv-imp/gnc-csv-trans-settings.hpp +++ b/gnucash/import-export/csv-imp/gnc-csv-import-settings.hpp @@ -1,5 +1,5 @@ /*******************************************************************\ - * gnc-csv-trans-settings.h -- Save and Load CSV Import Settings * + * gnc-csv-import-settings.h -- Save and Load CSV Import Settings * * * * Copyright (C) 2014 Robert Fewell * * * @@ -20,13 +20,13 @@ * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * * Boston, MA 02110-1301, USA gnu@gnu.org * \********************************************************************/ -/** @file gnc-csv-trans-settings.h +/** @file gnc-csv-import-settings.h @brief CSV Import Settings @author Copyright (c) 2014 Robert Fewell @author Copyright (c) 2016 Geert Janssens */ -#ifndef GNC_CSV_TRANS_SETTINGS_H -#define GNC_CSV_TRANS_SETTINGS_H +#ifndef GNC_CSV_IMPORT_SETTINGS_H +#define GNC_CSV_IMPORT_SETTINGS_H extern "C" { #include @@ -49,9 +49,9 @@ enum SEP_BUTTON_TYPES {SEP_SPACE, SEP_TAB, SEP_COMMA, SEP_COLON, SEP_SEMICOLON, /** Enumeration for the settings combo's */ enum SETTINGS_COL {SET_GROUP, SET_NAME}; -struct CsvTransSettings +struct CsvImportSettings { - CsvTransSettings() : m_file_format (GncImpFileFormat::CSV), m_encoding {"UTF-8"}, + CsvImportSettings() : m_file_format (GncImpFileFormat::CSV), m_encoding {"UTF-8"}, m_multi_split (false), m_date_format {0}, m_currency_format {0}, m_skip_start_lines{0}, m_skip_end_lines{0}, m_skip_alt_lines (false), m_separators {","}, m_load_error {false}, m_base_account {nullptr}, @@ -107,8 +107,8 @@ gnc_commodity *m_to_currency; // Price To Currency std::vector m_column_types_price; // The Price Column types in order }; -using preset_vec = std::vector>; -/** Creates a vector of CsvTransSettings which combines +using preset_vec = std::vector>; +/** Creates a vector of CsvImportSettings which combines * - one or more internally defined presets * - all preset found in the state key file. * @@ -117,12 +117,12 @@ using preset_vec = std::vector>; * * @return a reference to the populated vector. */ -const preset_vec& get_trans_presets (const std::string& set_type); +const preset_vec& get_import_presets (const std::string& set_type); /** Check whether name can be used as a preset name. * The names of the internal presets are considered reserved. * A preset with such a name should not be saved or deleted. */ -bool trans_preset_is_reserved_name (const std::string& name); +bool preset_is_reserved_name (const std::string& name); #endif diff --git a/gnucash/import-export/csv-imp/gnc-price-import.cpp b/gnucash/import-export/csv-imp/gnc-price-import.cpp index 44a17b913e..838c9473cb 100644 --- a/gnucash/import-export/csv-imp/gnc-price-import.cpp +++ b/gnucash/import-export/csv-imp/gnc-price-import.cpp @@ -42,7 +42,7 @@ extern "C" { #include "gnc-price-props.hpp" #include "gnc-csv-tokenizer.hpp" #include "gnc-fw-tokenizer.hpp" -#include "gnc-csv-trans-settings.hpp" +#include "gnc-csv-import-settings.hpp" G_GNUC_UNUSED static QofLogModule log_module = GNC_MOD_IMPORT; @@ -281,7 +281,7 @@ void GncPriceImport::separators (std::string separators) } std::string GncPriceImport::separators () { return m_settings.m_separators; } -void GncPriceImport::settings (const CsvTransSettings& settings) +void GncPriceImport::settings (const CsvImportSettings& settings) { /* First apply file format as this may recreate the tokenizer */ file_format (settings.m_file_format); @@ -315,7 +315,7 @@ void GncPriceImport::settings (const CsvTransSettings& settings) bool GncPriceImport::save_settings () { - if (trans_preset_is_reserved_name (m_settings.m_name)) + if (preset_is_reserved_name (m_settings.m_name)) return true; /* separators are already copied to m_settings in the separators diff --git a/gnucash/import-export/csv-imp/gnc-price-import.hpp b/gnucash/import-export/csv-imp/gnc-price-import.hpp index be91941a86..9959aaaa51 100644 --- a/gnucash/import-export/csv-imp/gnc-price-import.hpp +++ b/gnucash/import-export/csv-imp/gnc-price-import.hpp @@ -42,7 +42,7 @@ extern "C" { #include "gnc-tokenizer.hpp" #include "gnc-price-props.hpp" -#include "gnc-csv-trans-settings.hpp" +#include "gnc-csv-import-settings.hpp" #include /* A set of currency formats that the user sees. */ @@ -110,7 +110,7 @@ public: void separators (std::string separators); std::string separators (); - void settings (const CsvTransSettings& settings); + void settings (const CsvImportSettings& settings); bool save_settings (); void settings_name (std::string name); @@ -157,7 +157,7 @@ private: void update_price_props (uint32_t row, uint32_t col, GncPricePropType prop_type); struct CsvTranSettings; - CsvTransSettings m_settings; + CsvImportSettings m_settings; bool m_skip_errors; bool m_over_write; }; diff --git a/gnucash/import-export/csv-imp/gnc-tx-import.cpp b/gnucash/import-export/csv-imp/gnc-tx-import.cpp index daa85b4d98..eddbd753ef 100644 --- a/gnucash/import-export/csv-imp/gnc-tx-import.cpp +++ b/gnucash/import-export/csv-imp/gnc-tx-import.cpp @@ -39,7 +39,7 @@ extern "C" { #include "gnc-trans-props.hpp" #include "gnc-csv-tokenizer.hpp" #include "gnc-fw-tokenizer.hpp" -#include "gnc-csv-trans-settings.hpp" +#include "gnc-csv-import-settings.hpp" G_GNUC_UNUSED static QofLogModule log_module = GNC_MOD_IMPORT; @@ -293,7 +293,7 @@ void GncTxImport::separators (std::string separators) } std::string GncTxImport::separators () { return m_settings.m_separators; } -void GncTxImport::settings (const CsvTransSettings& settings) +void GncTxImport::settings (const CsvImportSettings& settings) { /* First apply file format as this may recreate the tokenizer */ file_format (settings.m_file_format); @@ -329,7 +329,7 @@ void GncTxImport::settings (const CsvTransSettings& settings) bool GncTxImport::save_settings () { - if (trans_preset_is_reserved_name (m_settings.m_name)) + if (preset_is_reserved_name (m_settings.m_name)) return true; /* separators are already copied to m_settings in the separators diff --git a/gnucash/import-export/csv-imp/gnc-tx-import.hpp b/gnucash/import-export/csv-imp/gnc-tx-import.hpp index 0c14a7489d..87e4ea4cd1 100644 --- a/gnucash/import-export/csv-imp/gnc-tx-import.hpp +++ b/gnucash/import-export/csv-imp/gnc-tx-import.hpp @@ -43,7 +43,7 @@ extern "C" { #include "gnc-tokenizer.hpp" #include "gnc-trans-props.hpp" -#include "gnc-csv-trans-settings.hpp" +#include "gnc-csv-import-settings.hpp" #include @@ -136,7 +136,7 @@ public: void separators (std::string separators); std::string separators (); - void settings (const CsvTransSettings& settings); + void settings (const CsvImportSettings& settings); bool save_settings (); void settings_name (std::string name); @@ -190,7 +190,7 @@ private: void update_pre_split_props (uint32_t row, uint32_t col, GncTransPropType prop_type); struct CsvTranSettings; - CsvTransSettings m_settings; + CsvImportSettings m_settings; bool m_skip_errors; bool m_req_mapped_accts; From 1aa3601e01cc07748d7f4a70e3f9be06af3a18e3 Mon Sep 17 00:00:00 2001 From: Robert Fewell <14uBobIT@gmail.com> Date: Fri, 1 Dec 2017 12:04:51 +0000 Subject: [PATCH 41/48] Pot file changes for new files and settings rename --- po/POTFILES.in | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/po/POTFILES.in b/po/POTFILES.in index bced61f5a2..7f74af4d66 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -287,17 +287,21 @@ gnucash/import-export/csv-exp/gnc-plugin-csv-export.c [type: gettext/gsettings]gnucash/import-export/csv-exp/gschemas/org.gnucash.dialogs.export.csv.gschema.xml.in.in gnucash/import-export/csv-imp/assistant-csv-account-import.c gnucash/import-export/csv-imp/assistant-csv-account-import.glade +gnucash/import-export/csv-imp/assistant-csv-price-import.cpp +gnucash/import-export/csv-imp/assistant-csv-price-import.glade gnucash/import-export/csv-imp/assistant-csv-trans-import.cpp gnucash/import-export/csv-imp/assistant-csv-trans-import.glade gnucash/import-export/csv-imp/csv-account-import.c gnucash/import-export/csv-imp/gnc-csv-account-map.c gnucash/import-export/csv-imp/gnc-csv-gnumeric-popup.c gnucash/import-export/csv-imp/gnc-csv-tokenizer.cpp -gnucash/import-export/csv-imp/gnc-csv-trans-settings.cpp +gnucash/import-export/csv-imp/gnc-csv-import-settings.cpp gnucash/import-export/csv-imp/gnc-dummy-tokenizer.cpp gnucash/import-export/csv-imp/gnc-fw-tokenizer.cpp gnucash/import-export/csv-imp/gncmod-csv-import.c gnucash/import-export/csv-imp/gnc-plugin-csv-import.c +gnucash/import-export/csv-imp/gnc-price-import.cpp +gnucash/import-export/csv-imp/gnc-price-props.cpp gnucash/import-export/csv-imp/gnc-tokenizer.cpp gnucash/import-export/csv-imp/gnc-trans-props.cpp gnucash/import-export/csv-imp/gnc-tx-import.cpp From ee2f301789b34d289743a1dd5bad4d1d07b09ba5 Mon Sep 17 00:00:00 2001 From: Robert Fewell <14uBobIT@gmail.com> Date: Mon, 4 Dec 2017 14:25:02 +0000 Subject: [PATCH 42/48] Make changes for Gtk3 compatibility --- .../csv-imp/assistant-csv-price-import.cpp | 24 +- .../csv-imp/assistant-csv-price-import.glade | 261 ++++++++---------- .../csv-imp/gnc-plugin-csv-import.c | 2 +- 3 files changed, 131 insertions(+), 156 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 0fa6273d4b..af810cb2b2 100644 --- a/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp +++ b/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp @@ -503,10 +503,12 @@ CsvImpPriceAssist::CsvImpPriceAssist () file_chooser = gtk_file_chooser_widget_new (GTK_FILE_CHOOSER_ACTION_OPEN); g_signal_connect (G_OBJECT(file_chooser), "file-activated", G_CALLBACK(csv_price_imp_file_confirm_cb), this); - auto button = gtk_button_new_from_stock (GTK_STOCK_OK); + auto button = gtk_button_new_with_label (_("OK")); gtk_widget_set_size_request (button, 100, -1); gtk_widget_show (button); - auto h_box = gtk_hbox_new (TRUE, 0); + auto h_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); + gtk_box_set_homogeneous (GTK_BOX (h_box), TRUE); + gtk_widget_set_hexpand (GTK_WIDGET(h_box), TRUE); gtk_box_pack_start (GTK_BOX(h_box), button, FALSE, FALSE, 0); gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER(file_chooser), h_box); g_signal_connect (G_OBJECT(button), "clicked", @@ -792,7 +794,9 @@ CsvImpPriceAssist::preview_settings_name (GtkEntry* entry) if (text) price_imp->settings_name(text); - auto combo = gtk_widget_get_parent (GTK_WIDGET(entry)); + auto box = gtk_widget_get_parent (GTK_WIDGET(entry)); + auto combo = gtk_widget_get_parent (GTK_WIDGET(box)); + preview_handle_save_del_sensitivity (GTK_COMBO_BOX(combo)); } @@ -1239,11 +1243,11 @@ enum static GnumericPopupMenuElement const popup_elements[] = { { - N_("Merge with column on _left"), GTK_STOCK_REMOVE, + N_("Merge with column on _left"), "list-remove", 0, 1 << CONTEXT_STF_IMPORT_MERGE_LEFT, CONTEXT_STF_IMPORT_MERGE_LEFT }, { - N_("Merge with column on _right"), GTK_STOCK_REMOVE, + N_("Merge with column on _right"), "list-remove", 0, 1 << CONTEXT_STF_IMPORT_MERGE_RIGHT, CONTEXT_STF_IMPORT_MERGE_RIGHT }, { "", nullptr, 0, 0, 0 }, @@ -1253,11 +1257,11 @@ static GnumericPopupMenuElement const popup_elements[] = }, { "", nullptr, 0, 0, 0 }, { - N_("_Widen this column"), GTK_STOCK_GO_FORWARD, + N_("_Widen this column"), "go-next", 0, 1 << CONTEXT_STF_IMPORT_WIDEN, CONTEXT_STF_IMPORT_WIDEN }, { - N_("_Narrow this column"), GTK_STOCK_GO_BACK, + N_("_Narrow this column"), "go-previous", 0, 1 << CONTEXT_STF_IMPORT_NARROW, CONTEXT_STF_IMPORT_NARROW }, { nullptr, nullptr, 0, 0, 0 }, @@ -1429,7 +1433,7 @@ CsvImpPriceAssist::preview_row_fill_state_cells (GtkListStore *store, GtkTreeIte fcolor = "black"; bcolor = "pink"; c_err_msg = err_msg.c_str(); - icon_name = GTK_STOCK_DIALOG_ERROR; + icon_name = "dialog-error"; } gtk_list_store_set (store, iter, PREV_COL_FCOLOR, fcolor, @@ -1480,13 +1484,13 @@ void CsvImpPriceAssist::preview_style_column (uint32_t col_num, GtkTreeModel* model) { auto col = gtk_tree_view_get_column (treeview, col_num); - auto renderer = static_cast(gtk_tree_view_column_get_cell_renderers(col)->data); + auto renderer = static_cast(gtk_cell_layout_get_cells(GTK_CELL_LAYOUT(col))->data); /* First column -the error status column- is rendered differently */ if (col_num == 0) { gtk_tree_view_column_set_attributes (col, renderer, - "stock-id", PREV_COL_ERR_ICON, + "icon-name", PREV_COL_ERR_ICON, "cell-background", PREV_COL_BCOLOR, nullptr); g_object_set (G_OBJECT(renderer), "stock-size", GTK_ICON_SIZE_MENU, nullptr); g_object_set (G_OBJECT(col), "sizing", GTK_TREE_VIEW_COLUMN_FIXED, diff --git a/gnucash/import-export/csv-imp/assistant-csv-price-import.glade b/gnucash/import-export/csv-imp/assistant-csv-price-import.glade index 33db2669c5..b1ba9c0a8c 100644 --- a/gnucash/import-export/csv-imp/assistant-csv-price-import.glade +++ b/gnucash/import-export/csv-imp/assistant-csv-price-import.glade @@ -1,7 +1,7 @@ + - - + 1000 1 @@ -38,11 +38,11 @@ CSV Price Import 400 500 + + - - @@ -76,10 +76,11 @@ Click on 'Forward' to proceed or 'Cancel' to Abort Import. - + True False 12 + vertical True @@ -101,20 +102,19 @@ Select location and file name for the Import, then click 'OK'... - + True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 12 + vertical 2 - + True False - 2 - 2 - 5 5 + 5 True @@ -129,7 +129,7 @@ Select location and file name for the Import, then click 'OK'... 5 5 - + True False @@ -143,7 +143,7 @@ Select location and file name for the Import, then click 'OK'... True False - gtk-delete + edit-delete @@ -165,7 +165,7 @@ Select location and file name for the Import, then click 'OK'... True False - gtk-save + document-save @@ -184,6 +184,7 @@ Select location and file name for the Import, then click 'OK'... True False + start <b>Load and Save Settings</b> True False @@ -191,8 +192,8 @@ Select location and file name for the Import, then click 'OK'... - GTK_FILL - GTK_FILL + 0 + 0 @@ -210,15 +211,14 @@ Select location and file name for the Import, then click 'OK'... 5 5 - + True False + vertical - + True False - 2 - 2 Separators @@ -226,10 +226,15 @@ Select location and file name for the Import, then click 'OK'... True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + start True True + + 0 + 0 + @@ -238,24 +243,24 @@ Select location and file name for the Import, then click 'OK'... True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + start True csv_button 1 - 2 + 0 - + True False - 2 + 0 1 - 2 - GTK_FILL + 2 @@ -266,12 +271,10 @@ Select location and file name for the Import, then click 'OK'... - + True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - 3 - 3 3 @@ -280,12 +283,13 @@ Select location and file name for the Import, then click 'OK'... True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + start True - GTK_FILL - GTK_FILL + 0 + 0 @@ -295,14 +299,13 @@ Select location and file name for the Import, then click 'OK'... True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + start True 1 - 2 - GTK_FILL - GTK_FILL + 0 @@ -312,15 +315,14 @@ Select location and file name for the Import, then click 'OK'... True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + start True True 2 - 3 - GTK_FILL - GTK_FILL + 0 @@ -330,14 +332,13 @@ Select location and file name for the Import, then click 'OK'... True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + start True + 2 1 - 2 - GTK_FILL - GTK_FILL @@ -347,16 +348,13 @@ Select location and file name for the Import, then click 'OK'... True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + start True - 1 - 2 + 0 1 - 2 - GTK_FILL - GTK_FILL @@ -366,16 +364,13 @@ Select location and file name for the Import, then click 'OK'... True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + start True - 2 - 3 + 1 1 - 2 - GTK_FILL - GTK_FILL @@ -385,14 +380,13 @@ Select location and file name for the Import, then click 'OK'... True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + start True + 0 2 - 3 - GTK_FILL - GTK_FILL @@ -401,22 +395,18 @@ Select location and file name for the Import, then click 'OK'... True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - True False False - True - True 1 - 3 2 - 3 - GTK_FILL - GTK_FILL + + + False @@ -425,7 +415,7 @@ Select location and file name for the Import, then click 'OK'... - + False True @@ -433,8 +423,7 @@ Select location and file name for the Import, then click 'OK'... True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - 0 - gtk-dialog-info + dialog-information False @@ -444,67 +433,61 @@ Select location and file name for the Import, then click 'OK'... - + True False - 2 - 2 True False - 0 - 5 True True + + 0 + 0 + True False - 0 + start Double-click anywhere on the table below to insert a column break True True 1 - 2 - + 0 True False - 0 - 5 True True + 0 1 - 2 True False - 0 + start Right-click anywhere in a column to modify it (widen, narrow, merge) True True 1 - 2 1 - 2 - @@ -522,17 +505,17 @@ Select location and file name for the Import, then click 'OK'... - + True False - 2 - + True False - GTK_FILL + 0 + 0 @@ -540,15 +523,15 @@ Select location and file name for the Import, then click 'OK'... Allow existing prices to be over written. True True + False False Normally prices are not over written, select this to change that. This setting is not saved. - False True + 0 1 - 2 @@ -567,16 +550,15 @@ Select location and file name for the Import, then click 'OK'... True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + start <b>File Format</b> True + 0 1 - 2 - GTK_FILL - GTK_FILL @@ -592,18 +574,17 @@ Select location and file name for the Import, then click 'OK'... 5 5 - + True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + vertical - + True False - 6 - 2 - 5 5 + 5 True @@ -613,23 +594,19 @@ Select location and file name for the Import, then click 'OK'... 1 - 2 1 - 2 - GTK_FILL True False - 0 + start Date Format + 0 1 - 2 - GTK_SHRINK | GTK_FILL @@ -640,34 +617,31 @@ Select location and file name for the Import, then click 'OK'... 1 - 2 2 - 3 - GTK_FILL True False - 0 + start Currency Format + 0 2 - 3 - GTK_SHRINK | GTK_FILL True False - 0 + start Encoding - GTK_SHRINK | GTK_FILL + 0 + 0 @@ -678,38 +652,35 @@ Select location and file name for the Import, then click 'OK'... 1 - 2 - GTK_FILL + 0 True False - 0 + start Leading Lines to Skip + 0 4 - 5 - GTK_FILL True False - 0 + start Trailing Lines to Skip + 0 5 - 6 - GTK_FILL - + True False @@ -717,11 +688,8 @@ Select location and file name for the Import, then click 'OK'... True True - True False False - True - True start_row_adj True @@ -735,13 +703,11 @@ Select location and file name for the Import, then click 'OK'... 1 - 2 4 - 5 - + True False @@ -749,11 +715,8 @@ Select location and file name for the Import, then click 'OK'... True True - True False False - True - True end_row_adj True @@ -767,20 +730,18 @@ Select location and file name for the Import, then click 'OK'... 1 - 2 5 - 6 - + True False - 2 + 0 3 - 4 + 2 @@ -800,6 +761,7 @@ Select location and file name for the Import, then click 'OK'... For example * if 'Leading Lines to Skip' is set to 3, the first line to import will be line 4. Lines 5, 7, 9,... will be skipped. * if 'Leading Lines to Skip' is set to 4, the first line to import will be line 5. Lines 6, 8, 10,... will be skipped. + start True @@ -817,6 +779,7 @@ For example True False + start <b>Miscellaneous</b> True @@ -824,11 +787,7 @@ For example 1 - 2 1 - 2 - GTK_FILL - GTK_FILL @@ -842,7 +801,7 @@ For example - + True False @@ -859,7 +818,7 @@ For example 5 5 - + True False @@ -913,7 +872,7 @@ For example 5 5 - + True False @@ -964,16 +923,15 @@ For example True True - automatic - automatic True False - + True False + vertical True @@ -981,6 +939,9 @@ For example GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK False both + + + False @@ -994,6 +955,9 @@ For example True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK both + + + True @@ -1013,7 +977,7 @@ For example - + True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK @@ -1022,8 +986,7 @@ For example True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - 0 - gtk-dialog-info + dialog-information False @@ -1037,7 +1000,7 @@ For example True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - 0 + start Select the type of each column to import. @@ -1055,7 +1018,7 @@ For example - + True False @@ -1063,7 +1026,6 @@ For example Skip Errors True False - 1 right True @@ -1089,10 +1051,11 @@ For example - + True False 12 + vertical True @@ -1122,10 +1085,11 @@ Cancel to abort.</b> - + True False 12 + vertical True @@ -1147,5 +1111,12 @@ Cancel to abort.</b> True + + + False + + + + diff --git a/gnucash/import-export/csv-imp/gnc-plugin-csv-import.c b/gnucash/import-export/csv-imp/gnc-plugin-csv-import.c index c14092d1c9..3cecf66fe8 100644 --- a/gnucash/import-export/csv-imp/gnc-plugin-csv-import.c +++ b/gnucash/import-export/csv-imp/gnc-plugin-csv-import.c @@ -57,7 +57,7 @@ static GtkActionEntry gnc_plugin_actions [] = G_CALLBACK (gnc_plugin_csv_import_trans_cmd) }, { - "CsvImportPriceAction", GTK_STOCK_CONVERT, N_("Import _Prices from a CSV file..."), NULL, + "CsvImportPriceAction", "go-previous", N_("Import _Prices from a CSV file..."), NULL, N_("Import Prices from a CSV file"), G_CALLBACK (gnc_plugin_csv_import_price_cmd) }, From 16714a8c5b7e08eb727c905fce1582f68fffd5c2 Mon Sep 17 00:00:00 2001 From: Robert Fewell <14uBobIT@gmail.com> Date: Thu, 7 Dec 2017 11:17:14 +0000 Subject: [PATCH 43/48] Replace date parse function with one from gnc_datetime --- .../csv-imp/assistant-csv-price-import.cpp | 6 +- .../csv-imp/gnc-price-import.cpp | 8 -- .../csv-imp/gnc-price-import.hpp | 4 - .../import-export/csv-imp/gnc-price-props.cpp | 114 +----------------- .../import-export/csv-imp/gnc-price-props.hpp | 4 +- 5 files changed, 6 insertions(+), 130 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 af810cb2b2..fbc563c82a 100644 --- a/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp +++ b/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp @@ -608,10 +608,8 @@ CsvImpPriceAssist::CsvImpPriceAssist () /* Add in the date format combo box and hook it up to an event handler. */ date_format_combo = GTK_COMBO_BOX_TEXT(gtk_combo_box_text_new()); - for (int i = 0; i < num_date_formats_price; i++) - { - gtk_combo_box_text_append_text (date_format_combo, _(date_format_user_price[i])); - } + for (auto& date_fmt : GncDate::c_formats) + gtk_combo_box_text_append_text (date_format_combo, _(date_fmt.m_fmt.c_str())); gtk_combo_box_set_active (GTK_COMBO_BOX(date_format_combo), 0); g_signal_connect (G_OBJECT(date_format_combo), "changed", G_CALLBACK(csv_price_imp_preview_date_fmt_sel_cb), this); diff --git a/gnucash/import-export/csv-imp/gnc-price-import.cpp b/gnucash/import-export/csv-imp/gnc-price-import.cpp index 838c9473cb..705d37ccb4 100644 --- a/gnucash/import-export/csv-imp/gnc-price-import.cpp +++ b/gnucash/import-export/csv-imp/gnc-price-import.cpp @@ -46,14 +46,6 @@ extern "C" { G_GNUC_UNUSED static QofLogModule log_module = GNC_MOD_IMPORT; -const int num_date_formats_price = 5; -const gchar* date_format_user_price[] = {N_("y-m-d"), - N_("d-m-y"), - N_("m-d-y"), - N_("d-m"), - N_("m-d") - }; - const int num_currency_formats_price = 3; const gchar* currency_format_user_price[] = {N_("Locale"), N_("Period: 123,456.78"), diff --git a/gnucash/import-export/csv-imp/gnc-price-import.hpp b/gnucash/import-export/csv-imp/gnc-price-import.hpp index 9959aaaa51..fd8ebedf81 100644 --- a/gnucash/import-export/csv-imp/gnc-price-import.hpp +++ b/gnucash/import-export/csv-imp/gnc-price-import.hpp @@ -49,10 +49,6 @@ extern "C" { extern const int num_currency_formats_price; extern const gchar* currency_format_user_price[]; -/* A set of date formats that the user sees. */ -extern const int num_date_formats_price; -extern const gchar* date_format_user_price[]; - /** Tuple to hold * - a tokenized line of input * - an optional error string diff --git a/gnucash/import-export/csv-imp/gnc-price-props.cpp b/gnucash/import-export/csv-imp/gnc-price-props.cpp index 539f316851..1ccd6e50af 100644 --- a/gnucash/import-export/csv-imp/gnc-price-props.cpp +++ b/gnucash/import-export/csv-imp/gnc-price-props.cpp @@ -50,116 +50,6 @@ std::map gnc_price_col_type_strs = { { GncPricePropType::TO_CURRENCY, N_("Currency To") }, }; -/* Regular expressions used to parse dates per date format */ -const char* date_regex_price[] = { - "(?:" // either y-m-d - "(?[0-9]+)[-/.' ]+" - "(?[0-9]+)[-/.' ]+" - "(?[0-9]+)" - "|" // or CCYYMMDD - "(?[0-9]{4})" - "(?[0-9]{2})" - "(?[0-9]{2})" - ")", - - "(?:" // either d-m-y - "(?[0-9]+)[-/.' ]+" - "(?[0-9]+)[-/.' ]+" - "(?[0-9]+)" - "|" // or DDMMCCYY - "(?[0-9]{2})" - "(?[0-9]{2})" - "(?[0-9]{4})" - ")", - - "(?:" // either m-d-y - "(?[0-9]+)[-/.' ]+" - "(?[0-9]+)[-/.' ]+" - "(?[0-9]+)" - "|" // or MMDDCCYY - "(?[0-9]{2})" - "(?[0-9]{2})" - "(?[0-9]{4})" - ")", - - "(?:" // either d-m(-y) - "(?[0-9]+)[-/.' ]+" - "(?[0-9]+)(?:[-/.' ]+" - "(?[0-9]+))?" - "|" // or DDMM(CCYY) - "(?[0-9]{2})" - "(?[0-9]{2})" - "(?[0-9]+)?" - ")", - - "(?:" // either m-d(-y) - "(?[0-9]+)[-/.' ]+" - "(?[0-9]+)(?:[-/.' ]+" - "(?[0-9]+))?" - "|" // or MMDD(CCYY) - "(?[0-9]{2})" - "(?[0-9]{2})" - "(?[0-9]+)?" - ")", -}; - -/** Parses a string into a date, given a format. This function - * requires only knowing the order in which the year, month and day - * appear. For example, 01-02-2003 will be parsed the same way as - * 01/02/2003. - * @param date_str The string containing a date being parsed - * @param format An index specifying a format in date_format_user - * @exception std::invalid_argument if the string can't be parsed into a date. - * @return The parsed value of date_str on success, throws on failure - */ - -time64 parse_date_price (const std::string &date_str, int format) -{ - boost::regex r(date_regex_price[format]); - boost::smatch what; - if(!boost::regex_search(date_str, what, r)) - throw std::invalid_argument (_("Value can't be parsed into a date using the selected date format.")); // regex didn't find a match - - // Attention: different behavior from 2.6.x series ! - // If date format without year was selected, the match - // should NOT have found a year. - if ((format >= 3) && (what.length("YEAR") != 0)) - throw std::invalid_argument (_("Value appears to contain a year while the selected format forbids this.")); - - auto day = std::stoi (what.str("DAY")); - auto month = std::stoi (what.str("MONTH")); - - int year; - if (format < 3) - { - /* The input dates have a year, so use that one */ - year = std::stoi (what.str("YEAR")); - - /* Handle two-digit years. */ - if (year < 100) - { - /* We allow two-digit years in the range 1969 - 2068. */ - if (year < 69) - year += 2000; - else - year += 1900; - } - } - else - { - /* The input dates don't have a year, so work with today's year. - */ - gnc_timespec2dmy(timespec_now(), nullptr, nullptr, &year); - } - - auto ts = gnc_dmy2timespec_neutral(day, month, year); - if (ts.tv_sec == INT64_MAX) - throw std::invalid_argument (_("Value can't be parsed into a date using the selected date format.")); - - return ts.tv_sec; -} - - /** Convert str into a GncNumeric using the user-specified (import) currency format. * @param str The string to be parsed * @param currency_format The currency format to use. @@ -257,7 +147,7 @@ void GncImportPrice::set (GncPricePropType prop_type, const std::string& value) { case GncPricePropType::DATE: m_date = boost::none; - m_date = parse_date_price (value, m_date_format); // Throws if parsing fails + m_date = GncDate(value, GncDate::c_formats[m_date_format].m_fmt); // Throws if parsing fails break; case GncPricePropType::AMOUNT: @@ -357,7 +247,7 @@ Result GncImportPrice::create_price (QofBook* book, GNCPriceDB *pdb, bool over) } Timespec date; - timespecFromTime64 (&date, *m_date); + timespecFromTime64 (&date, static_cast(GncDateTime(*m_date, DayPart::neutral))); date.tv_nsec = 0; bool rev = false; diff --git a/gnucash/import-export/csv-imp/gnc-price-props.hpp b/gnucash/import-export/csv-imp/gnc-price-props.hpp index 9ae485323e..77e39dc689 100644 --- a/gnucash/import-export/csv-imp/gnc-price-props.hpp +++ b/gnucash/import-export/csv-imp/gnc-price-props.hpp @@ -39,6 +39,7 @@ extern "C" { #include #include #include +#include #include /** Enumeration for column types. These are the different types of @@ -76,7 +77,6 @@ private: const char *m_name; }; -time64 parse_date_price (const std::string &date_str, int format); gnc_commodity* parse_commodity_price_comm (const std::string& comm_str); GncNumeric parse_amount_price (const std::string &str, int currency_format); @@ -104,7 +104,7 @@ public: private: int m_date_format; int m_currency_format; - boost::optional m_date; + boost::optional m_date; boost::optional m_amount; boost::optional m_from_commodity; boost::optional m_to_currency; From 288563c25e39be4c5a87f924d928be6689e00357 Mon Sep 17 00:00:00 2001 From: Robert Fewell <14uBobIT@gmail.com> Date: Thu, 7 Dec 2017 11:19:18 +0000 Subject: [PATCH 44/48] Add a test for empty values Some csv values are allowed to be empty based on options selected so add a test for this otherwise all values are required. --- .../import-export/csv-imp/gnc-price-import.cpp | 18 +++++++++++------- .../import-export/csv-imp/gnc-price-props.cpp | 9 +++++++-- .../import-export/csv-imp/gnc-price-props.hpp | 2 +- 3 files changed, 19 insertions(+), 10 deletions(-) diff --git a/gnucash/import-export/csv-imp/gnc-price-import.cpp b/gnucash/import-export/csv-imp/gnc-price-import.cpp index 705d37ccb4..61c876de90 100644 --- a/gnucash/import-export/csv-imp/gnc-price-import.cpp +++ b/gnucash/import-export/csv-imp/gnc-price-import.cpp @@ -649,15 +649,13 @@ void GncPriceImport::update_price_props (uint32_t row, uint32_t col, GncPricePro return; /* Only deal with price related properties. */ auto price_props = std::make_shared (*(std::get<2>(m_parsed_lines[row])).get()); - auto value = std::string(); - if (col < std::get<0>(m_parsed_lines[row]).size()) - value = std::get<0>(m_parsed_lines[row]).at(col); - - if (value.empty()) - price_props->reset (prop_type); + if (col >= std::get<0>(m_parsed_lines[row]).size()) + price_props->reset (prop_type); //reset errors else { + auto value = std::get<0>(m_parsed_lines[row]).at(col); + bool enable_test_empty = true; try { // set the from_commodity based on combo so we can test for same. @@ -665,14 +663,20 @@ void GncPriceImport::update_price_props (uint32_t row, uint32_t col, GncPricePro { if (m_settings.m_from_commodity) price_props->set_from_commodity (m_settings.m_from_commodity); + + if (m_settings.m_to_currency) + enable_test_empty = false; } // set the to_currency based on combo so we can test for same. if (prop_type == GncPricePropType::FROM_COMMODITY) { if (m_settings.m_to_currency) price_props->set_to_currency (m_settings.m_to_currency); + + if (m_settings.m_from_commodity) + enable_test_empty = false; } - price_props->set(prop_type, value); + price_props->set(prop_type, value, enable_test_empty); } catch (const std::exception& e) { diff --git a/gnucash/import-export/csv-imp/gnc-price-props.cpp b/gnucash/import-export/csv-imp/gnc-price-props.cpp index 1ccd6e50af..151bd38ad2 100644 --- a/gnucash/import-export/csv-imp/gnc-price-props.cpp +++ b/gnucash/import-export/csv-imp/gnc-price-props.cpp @@ -135,13 +135,17 @@ gnc_commodity* parse_commodity_price_comm (const std::string& comm_str) return comm; } -void GncImportPrice::set (GncPricePropType prop_type, const std::string& value) +void GncImportPrice::set (GncPricePropType prop_type, const std::string& value, bool enable_test_empty) { try { // Drop any existing error for the prop_type we're about to set m_errors.erase(prop_type); + // conditional test for empty values + if (value.empty() && enable_test_empty) + throw std::invalid_argument (_("Column value can not be empty.")); + gnc_commodity *comm = nullptr; switch (prop_type) { @@ -207,7 +211,8 @@ void GncImportPrice::reset (GncPricePropType prop_type) { try { - set (prop_type, std::string()); + // set enable_test_empty to false to allow empty values + set (prop_type, std::string(), false); } catch (...) { diff --git a/gnucash/import-export/csv-imp/gnc-price-props.hpp b/gnucash/import-export/csv-imp/gnc-price-props.hpp index 77e39dc689..3a358612af 100644 --- a/gnucash/import-export/csv-imp/gnc-price-props.hpp +++ b/gnucash/import-export/csv-imp/gnc-price-props.hpp @@ -86,7 +86,7 @@ public: GncImportPrice (int date_format, int currency_format) : m_date_format{date_format}, m_currency_format{currency_format}{}; - void set (GncPricePropType prop_type, const std::string& value); + void set (GncPricePropType prop_type, const std::string& value, bool enable_test_empty); void set_date_format (int date_format) { m_date_format = date_format ;} void set_currency_format (int currency_format) { m_currency_format = currency_format ;} void reset (GncPricePropType prop_type); From 8ee6783b4b3e65cfaace37a5a4f9d4b31f574495 Mon Sep 17 00:00:00 2001 From: Robert Fewell <14uBobIT@gmail.com> Date: Sun, 10 Dec 2017 11:58:09 +0000 Subject: [PATCH 45/48] Replace magic numbers used in std::get... with values from enum --- .../csv-imp/assistant-csv-price-import.cpp | 6 ++-- .../csv-imp/gnc-price-import.cpp | 29 +++++++++---------- .../csv-imp/gnc-price-import.hpp | 12 ++++++++ 3 files changed, 29 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 fbc563c82a..398a52cb04 100644 --- a/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp +++ b/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp @@ -1564,12 +1564,12 @@ void CsvImpPriceAssist::preview_refresh_table () GtkTreeIter iter; gtk_list_store_append (store, &iter); preview_row_fill_state_cells (store, &iter, - std::get<1>(parse_line), std::get<3>(parse_line)); + std::get(parse_line), std::get(parse_line)); /* Fill the data cells. */ - for (auto cell_str_it = std::get<0>(parse_line).cbegin(); cell_str_it != std::get<0>(parse_line).cend(); cell_str_it++) + for (auto cell_str_it = std::get(parse_line).cbegin(); cell_str_it != std::get(parse_line).cend(); cell_str_it++) { - uint32_t pos = PREV_N_FIXED_COLS + cell_str_it - std::get<0>(parse_line).cbegin(); + uint32_t pos = PREV_N_FIXED_COLS + cell_str_it - std::get(parse_line).cbegin(); gtk_list_store_set (store, &iter, pos, cell_str_it->c_str(), -1); } } diff --git a/gnucash/import-export/csv-imp/gnc-price-import.cpp b/gnucash/import-export/csv-imp/gnc-price-import.cpp index 61c876de90..df91261fc1 100644 --- a/gnucash/import-export/csv-imp/gnc-price-import.cpp +++ b/gnucash/import-export/csv-imp/gnc-price-import.cpp @@ -54,7 +54,6 @@ const gchar* currency_format_user_price[] = {N_("Locale"), /** Constructor for GncPriceImport. - * @return Pointer to a new GncCsvParseData */ GncPriceImport::GncPriceImport(GncImpFileFormat format) { @@ -247,12 +246,12 @@ void GncPriceImport::update_skipped_lines(boost::optional start, boost for (uint32_t i = 0; i < m_parsed_lines.size(); i++) { - std::get<3>(m_parsed_lines[i]) = + std::get(m_parsed_lines[i]) = ((i < skip_start_lines()) || // start rows to skip (i >= m_parsed_lines.size() - skip_end_lines()) || // end rows to skip (((i - skip_start_lines()) % 2 == 1) && // skip every second row... skip_alt_lines()) || // ...if requested - (m_skip_errors && !std::get<1>(m_parsed_lines[i]).empty())); // skip lines with errors + (m_skip_errors && !std::get(m_parsed_lines[i]).empty())); // skip lines with errors } } @@ -493,7 +492,7 @@ std::string GncPriceImport::verify () auto have_line_errors = false; for (auto line : m_parsed_lines) { - if (!std::get<3>(line) && !std::get<1>(line).empty()) + if (!std::get(line) && !std::get(line).empty()) { have_line_errors = true; break; @@ -624,7 +623,7 @@ void GncPriceImport::create_prices () ++parsed_lines_it) { /* Skip current line if the user specified so */ - if ((std::get<3>(*parsed_lines_it))) + if ((std::get(*parsed_lines_it))) continue; /* Should not throw anymore, otherwise verify needs revision */ @@ -648,13 +647,13 @@ void GncPriceImport::update_price_props (uint32_t row, uint32_t col, GncPricePro if (prop_type == GncPricePropType::NONE) return; /* Only deal with price related properties. */ - auto price_props = std::make_shared (*(std::get<2>(m_parsed_lines[row])).get()); + auto price_props = std::make_shared (*(std::get(m_parsed_lines[row])).get()); - if (col >= std::get<0>(m_parsed_lines[row]).size()) + if (col >= std::get(m_parsed_lines[row]).size()) price_props->reset (prop_type); //reset errors else { - auto value = std::get<0>(m_parsed_lines[row]).at(col); + auto value = std::get(m_parsed_lines[row]).at(col); bool enable_test_empty = true; try { @@ -683,12 +682,12 @@ void GncPriceImport::update_price_props (uint32_t row, uint32_t col, GncPricePro /* Do nothing, just prevent the exception from escalating up * However log the error if it happens on a row that's not skipped */ - if (!std::get<3>(m_parsed_lines[row])) + if (!std::get(m_parsed_lines[row])) PINFO("User warning: %s", e.what()); } } /* Store the result */ - std::get<2>(m_parsed_lines[row]) = price_props; + std::get(m_parsed_lines[row]) = price_props; } void @@ -723,8 +722,8 @@ GncPriceImport::set_column_type_price (uint32_t position, GncPricePropType type, /* Reset date and currency formats for each price props object * to ensure column updates use the most recent one */ - std::get<2>(*parsed_lines_it)->set_date_format (m_settings.m_date_format); - std::get<2>(*parsed_lines_it)->set_currency_format (m_settings.m_currency_format); + std::get(*parsed_lines_it)->set_date_format (m_settings.m_date_format); + std::get(*parsed_lines_it)->set_currency_format (m_settings.m_currency_format); uint32_t row = parsed_lines_it - m_parsed_lines.begin(); @@ -733,7 +732,7 @@ GncPriceImport::set_column_type_price (uint32_t position, GncPricePropType type, */ if (old_type != type) { - auto old_col = std::get<0>(*parsed_lines_it).size(); // Deliberately out of bounds to trigger a reset! + auto old_col = std::get(*parsed_lines_it).size(); // Deliberately out of bounds to trigger a reset! if ((old_type > GncPricePropType::NONE) && (old_type <= GncPricePropType::PRICE_PROPS)) update_price_props (row, old_col, old_type); @@ -744,8 +743,8 @@ GncPriceImport::set_column_type_price (uint32_t position, GncPricePropType type, update_price_props (row, position, type); /* Report errors if there are any */ - auto price_errors = std::get<2>(*parsed_lines_it)->errors(); - std::get<1>(*parsed_lines_it) = + auto price_errors = std::get(*parsed_lines_it)->errors(); + std::get(*parsed_lines_it) = price_errors + (price_errors.empty() ? std::string() : "\n"); } diff --git a/gnucash/import-export/csv-imp/gnc-price-import.hpp b/gnucash/import-export/csv-imp/gnc-price-import.hpp index fd8ebedf81..360859d7ee 100644 --- a/gnucash/import-export/csv-imp/gnc-price-import.hpp +++ b/gnucash/import-export/csv-imp/gnc-price-import.hpp @@ -49,6 +49,18 @@ extern "C" { extern const int num_currency_formats_price; extern const gchar* currency_format_user_price[]; +/** An enum describing the columns found in a parse_line_t. Currently these are: + * - a tokenized line of input + * - an optional error string + * - a struct to hold user selected properties for a price + * - a boolean to mark the line as skipped by error and/or user or not */ +enum parse_line_cols { + PL_INPUT, + PL_ERROR, + PL_PREPRICE, + PL_SKIP +}; + /** Tuple to hold * - a tokenized line of input * - an optional error string From 0534ba4f8b9a69d39a524be282b2bcd737594f33 Mon Sep 17 00:00:00 2001 From: Robert Fewell <14uBobIT@gmail.com> Date: Sun, 10 Dec 2017 11:59:40 +0000 Subject: [PATCH 46/48] Update file with changes for transient dialog changes --- .../csv-imp/assistant-csv-price-import.cpp | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 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 398a52cb04..8bb371ba34 100644 --- a/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp +++ b/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp @@ -704,13 +704,13 @@ CsvImpPriceAssist::file_confirm_cb () catch (std::ifstream::failure& e) { /* File loading failed ... */ - gnc_error_dialog (GTK_WIDGET(csv_imp_asst), "%s", e.what()); + gnc_error_dialog (GTK_WINDOW(csv_imp_asst), "%s", e.what()); return; } catch (std::range_error &e) { /* Parsing failed ... */ - gnc_error_dialog (GTK_WIDGET(csv_imp_asst), "%s", e.what()); + gnc_error_dialog (GTK_WINDOW(csv_imp_asst), "%s", e.what()); return; } /* Get settings store and populate */ @@ -818,7 +818,7 @@ CsvImpPriceAssist::preview_settings_load () price_imp->settings (*preset); if (preset->m_load_error) - gnc_error_dialog (GTK_WIDGET(csv_imp_asst), + gnc_error_dialog (GTK_WINDOW(csv_imp_asst), "%s", _("There were problems reading some saved settings, continuing to load.\n" "Please review and save again.")); @@ -840,7 +840,7 @@ CsvImpPriceAssist::preview_settings_delete () auto model = gtk_combo_box_get_model (settings_combo); gtk_tree_model_get (model, &iter, SET_GROUP, &preset, -1); - auto response = gnc_ok_cancel_dialog (GTK_WIDGET(csv_imp_asst), + auto response = gnc_ok_cancel_dialog (GTK_WINDOW(csv_imp_asst), GTK_RESPONSE_CANCEL, "%s", _("Delete the Import Settings.")); if (response == GTK_RESPONSE_OK) @@ -875,7 +875,7 @@ CsvImpPriceAssist::preview_settings_save () if (preset && (preset->m_name == std::string(new_name))) { - auto response = gnc_ok_cancel_dialog (GTK_WIDGET(csv_imp_asst), + auto response = gnc_ok_cancel_dialog (GTK_WINDOW(csv_imp_asst), GTK_RESPONSE_OK, "%s", _("Setting name already exists, over write?")); if (response != GTK_RESPONSE_OK) @@ -890,7 +890,7 @@ CsvImpPriceAssist::preview_settings_save () /* All checks passed, let's save this preset */ if (!price_imp->save_settings()) { - gnc_info_dialog (GTK_WIDGET(csv_imp_asst), + gnc_info_dialog (GTK_WINDOW(csv_imp_asst), "%s", _("The settings have been saved.")); // Update the settings store @@ -915,7 +915,7 @@ CsvImpPriceAssist::preview_settings_save () } } else - gnc_error_dialog (GTK_WIDGET(csv_imp_asst), + gnc_error_dialog (GTK_WINDOW(csv_imp_asst), "%s", _("There was a problem saving the settings, please try again.")); } @@ -996,7 +996,7 @@ void CsvImpPriceAssist::preview_update_separators (GtkWidget* widget) /* Warn the user there was a problem and try to undo what caused * the error. (This will cause a reparsing and ideally a usable * configuration.) */ - gnc_error_dialog (GTK_WIDGET(csv_imp_asst), "Error in parsing"); + gnc_error_dialog (GTK_WINDOW(csv_imp_asst), "Error in parsing"); /* If we're here because the user changed the file format, we should just wait for the user * to update the configuration */ if (!widget) @@ -1046,7 +1046,7 @@ void CsvImpPriceAssist::preview_update_file_format () catch (std::range_error &e) { /* Parsing failed ... */ - gnc_error_dialog (nullptr, "%s", e.what()); + gnc_error_dialog (GTK_WINDOW (csv_imp_asst), "%s", e.what()); return; } catch (...) @@ -1082,7 +1082,7 @@ CsvImpPriceAssist::preview_update_encoding (const char* encoding) catch (...) { /* If it fails, change back to the old encoding. */ - gnc_error_dialog (nullptr, "%s", _("Invalid encoding selected")); + gnc_error_dialog (GTK_WINDOW (csv_imp_asst), "%s", _("Invalid encoding selected")); go_charmap_sel_set_encoding (encselector, previous_encoding.c_str()); } } @@ -1319,7 +1319,7 @@ fixed_context_menu_handler_price (GnumericPopupMenuElement const *element, } catch(std::range_error& e) { - gnc_error_dialog (nullptr, "%s", e.what()); + gnc_error_dialog (GTK_WINDOW (info->csv_imp_asst), "%s", e.what()); return false; } info->preview_refresh_table (); @@ -1364,7 +1364,7 @@ CsvImpPriceAssist::preview_split_column (int col, int offset) } catch (std::range_error& e) { - gnc_error_dialog (nullptr, "%s", e.what()); + gnc_error_dialog (GTK_WINDOW (csv_imp_asst), "%s", e.what()); return; } preview_refresh_table(); @@ -1799,7 +1799,7 @@ CsvImpPriceAssist::assist_finish () /* Oops! This shouldn't happen when using the import assistant ! * Inform the user and go back to the preview page. */ - gnc_error_dialog (GTK_WIDGET(csv_imp_asst), + gnc_error_dialog (GTK_WINDOW(csv_imp_asst), _("An unexpected error has occurred while creating prices. Please report this as a bug.\n\n" "Error message:\n%s"), err.what()); gtk_assistant_set_current_page (csv_imp_asst, 2); From ba5ca5bd551a6bf4f8066a4533a44e792fbe09d1 Mon Sep 17 00:00:00 2001 From: Robert Fewell <14uBobIT@gmail.com> Date: Mon, 11 Dec 2017 09:53:20 +0000 Subject: [PATCH 47/48] Remove surplus statement --- gnucash/import-export/csv-imp/gnc-price-import.hpp | 1 - 1 file changed, 1 deletion(-) diff --git a/gnucash/import-export/csv-imp/gnc-price-import.hpp b/gnucash/import-export/csv-imp/gnc-price-import.hpp index 360859d7ee..c91943f847 100644 --- a/gnucash/import-export/csv-imp/gnc-price-import.hpp +++ b/gnucash/import-export/csv-imp/gnc-price-import.hpp @@ -164,7 +164,6 @@ private: */ void update_price_props (uint32_t row, uint32_t col, GncPricePropType prop_type); - struct CsvTranSettings; CsvImportSettings m_settings; bool m_skip_errors; bool m_over_write; From 5aa84e13d610525410b031031960a7830d806e59 Mon Sep 17 00:00:00 2001 From: Robert Fewell <14uBobIT@gmail.com> Date: Thu, 14 Dec 2017 20:37:12 +0000 Subject: [PATCH 48/48] Change the way the import settings are handled --- gnucash/import-export/csv-imp/CMakeLists.txt | 4 + gnucash/import-export/csv-imp/Makefile.am | 8 +- .../csv-imp/assistant-csv-price-import.cpp | 14 +- .../csv-imp/assistant-csv-trans-import.cpp | 17 +- .../csv-imp/gnc-csv-import-settings.cpp | 318 ++---------------- .../csv-imp/gnc-csv-import-settings.hpp | 61 ++-- .../csv-imp/gnc-csv-price-import-settings.cpp | 258 ++++++++++++++ .../csv-imp/gnc-csv-price-import-settings.hpp | 79 +++++ .../csv-imp/gnc-csv-trans-import-settings.cpp | 262 +++++++++++++++ .../csv-imp/gnc-csv-trans-import-settings.hpp | 79 +++++ .../csv-imp/gnc-price-import.cpp | 4 +- .../csv-imp/gnc-price-import.hpp | 6 +- .../import-export/csv-imp/gnc-tx-import.cpp | 4 +- .../import-export/csv-imp/gnc-tx-import.hpp | 8 +- 14 files changed, 771 insertions(+), 351 deletions(-) create mode 100644 gnucash/import-export/csv-imp/gnc-csv-price-import-settings.cpp create mode 100644 gnucash/import-export/csv-imp/gnc-csv-price-import-settings.hpp create mode 100644 gnucash/import-export/csv-imp/gnc-csv-trans-import-settings.cpp create mode 100644 gnucash/import-export/csv-imp/gnc-csv-trans-import-settings.hpp diff --git a/gnucash/import-export/csv-imp/CMakeLists.txt b/gnucash/import-export/csv-imp/CMakeLists.txt index 65082be1c7..03c6195388 100644 --- a/gnucash/import-export/csv-imp/CMakeLists.txt +++ b/gnucash/import-export/csv-imp/CMakeLists.txt @@ -18,6 +18,8 @@ SET(csv_import_SOURCES gnc-csv-gnumeric-popup.c gnc-csv-tokenizer.cpp gnc-csv-import-settings.cpp + gnc-csv-price-import-settings.cpp + gnc-csv-trans-import-settings.cpp gnc-dummy-tokenizer.cpp gnc-fw-tokenizer.cpp gnc-price-import.cpp @@ -46,6 +48,8 @@ SET(csv_import_noinst_HEADERS gnc-csv-gnumeric-popup.h gnc-csv-tokenizer.hpp gnc-csv-import-settings.hpp + gnc-csv-price-import-settings.hpp + gnc-csv-trans-import-settings.hpp gnc-dummy-tokenizer.hpp gnc-fw-tokenizer.hpp gnc-price-import.hpp diff --git a/gnucash/import-export/csv-imp/Makefile.am b/gnucash/import-export/csv-imp/Makefile.am index f1b501f7ea..b4ab800b87 100644 --- a/gnucash/import-export/csv-imp/Makefile.am +++ b/gnucash/import-export/csv-imp/Makefile.am @@ -19,7 +19,9 @@ libgncmod_csv_import_la_SOURCES = \ gnc-tokenizer.cpp \ gnc-tx-import.cpp \ gnc-trans-props.cpp \ - gnc-csv-import-settings.cpp + gnc-csv-import-settings.cpp \ + gnc-csv-price-import-settings.cpp \ + gnc-csv-trans-import-settings.cpp noinst_HEADERS = \ assistant-csv-account-import.h \ @@ -37,7 +39,9 @@ noinst_HEADERS = \ gnc-tokenizer.hpp \ gnc-tx-import.hpp \ gnc-trans-props.hpp \ - gnc-csv-import-settings.hpp + gnc-csv-import-settings.hpp \ + gnc-csv-price-import-settings.hpp \ + gnc-csv-trans-import-settings.hpp libgncmod_csv_import_la_LDFLAGS = -avoid-version 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 8bb371ba34..091123302a 100644 --- a/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp +++ b/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp @@ -52,7 +52,7 @@ extern "C" #include "go-charmap-sel.h" } -#include "gnc-csv-import-settings.hpp" +#include "gnc-csv-price-import-settings.hpp" #include "gnc-price-import.hpp" #include "gnc-fw-tokenizer.hpp" #include "gnc-csv-tokenizer.hpp" @@ -64,8 +64,6 @@ extern "C" /* This static indicates the debugging module that this .o belongs to. */ static QofLogModule log_module = GNC_MOD_ASSISTANT; -const std::string settings_type = "PRICE"; - class CsvImpPriceAssist { public: @@ -738,7 +736,7 @@ void CsvImpPriceAssist::preview_populate_settings_combo() gtk_list_store_clear (GTK_LIST_STORE(model)); // Append the default entry - auto presets = get_import_presets (settings_type); + auto presets = get_import_presets_price (); for (auto preset : presets) { GtkTreeIter iter; @@ -766,7 +764,7 @@ void CsvImpPriceAssist::preview_handle_save_del_sensitivity (GtkComboBox* combo) /* Handle sensitivity of the delete and save button */ if (gtk_combo_box_get_active_iter (combo, &iter)) { - CsvImportSettings *preset; + CsvPriceImpSettings *preset; GtkTreeModel *model = gtk_combo_box_get_model (combo); gtk_tree_model_get (model, &iter, SET_GROUP, &preset, -1); @@ -809,7 +807,7 @@ CsvImpPriceAssist::preview_settings_load () if (!gtk_combo_box_get_active_iter (settings_combo, &iter)) return; - CsvImportSettings *preset = nullptr; + CsvPriceImpSettings *preset = nullptr; auto model = gtk_combo_box_get_model (settings_combo); gtk_tree_model_get (model, &iter, SET_GROUP, &preset, -1); @@ -836,7 +834,7 @@ CsvImpPriceAssist::preview_settings_delete () if (!gtk_combo_box_get_active_iter (settings_combo, &iter)) return; - CsvImportSettings *preset = nullptr; + CsvPriceImpSettings *preset = nullptr; auto model = gtk_combo_box_get_model (settings_combo); gtk_tree_model_get (model, &iter, SET_GROUP, &preset, -1); @@ -870,7 +868,7 @@ CsvImpPriceAssist::preview_settings_save () while (valid) { // Walk through the list, reading each row - CsvImportSettings *preset; + CsvPriceImpSettings *preset; gtk_tree_model_get (model, &iter, SET_GROUP, &preset, -1); if (preset && (preset->m_name == std::string(new_name))) 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 41779c63cd..5347393aa8 100644 --- a/gnucash/import-export/csv-imp/assistant-csv-trans-import.cpp +++ b/gnucash/import-export/csv-imp/assistant-csv-trans-import.cpp @@ -58,7 +58,7 @@ extern "C" #include "go-charmap-sel.h" } -#include "gnc-csv-import-settings.hpp" +#include "gnc-csv-trans-import-settings.hpp" #include "gnc-tx-import.hpp" #include "gnc-fw-tokenizer.hpp" #include "gnc-csv-tokenizer.hpp" @@ -70,8 +70,6 @@ extern "C" /* This static indicates the debugging module that this .o belongs to. */ static QofLogModule log_module = GNC_MOD_ASSISTANT; -const std::string settings_type = "TRANS"; - class CsvImpTransAssist { public: @@ -678,8 +676,7 @@ void CsvImpTransAssist::preview_populate_settings_combo() gtk_list_store_clear (GTK_LIST_STORE(model)); // Append the default entry - - auto presets = get_import_presets (settings_type); + auto presets = get_import_presets_trans (); for (auto preset : presets) { GtkTreeIter iter; @@ -707,7 +704,7 @@ void CsvImpTransAssist::preview_handle_save_del_sensitivity (GtkComboBox* combo) /* Handle sensitivity of the delete and save button */ if (gtk_combo_box_get_active_iter (combo, &iter)) { - CsvImportSettings *preset; + CsvTransImpSettings *preset; GtkTreeModel *model = gtk_combo_box_get_model (combo); gtk_tree_model_get (model, &iter, SET_GROUP, &preset, -1); @@ -736,7 +733,7 @@ CsvImpTransAssist::preview_settings_name (GtkEntry* entry) auto box = gtk_widget_get_parent (GTK_WIDGET(entry)); auto combo = gtk_widget_get_parent (GTK_WIDGET(box)); - + preview_handle_save_del_sensitivity (GTK_COMBO_BOX(combo)); } @@ -752,7 +749,7 @@ CsvImpTransAssist::preview_settings_load () if (!gtk_combo_box_get_active_iter (settings_combo, &iter)) return; - CsvImportSettings *preset = nullptr; + CsvTransImpSettings *preset = nullptr; auto model = gtk_combo_box_get_model (settings_combo); gtk_tree_model_get (model, &iter, SET_GROUP, &preset, -1); @@ -779,7 +776,7 @@ CsvImpTransAssist::preview_settings_delete () if (!gtk_combo_box_get_active_iter (settings_combo, &iter)) return; - CsvImportSettings *preset = nullptr; + CsvTransImpSettings *preset = nullptr; auto model = gtk_combo_box_get_model (settings_combo); gtk_tree_model_get (model, &iter, SET_GROUP, &preset, -1); @@ -812,7 +809,7 @@ CsvImpTransAssist::preview_settings_save () while (valid) { // Walk through the list, reading each row - CsvImportSettings *preset; + CsvTransImpSettings *preset; gtk_tree_model_get (model, &iter, SET_GROUP, &preset, -1); if (preset && (preset->m_name == std::string(new_name))) diff --git a/gnucash/import-export/csv-imp/gnc-csv-import-settings.cpp b/gnucash/import-export/csv-imp/gnc-csv-import-settings.cpp index 0096c5397e..7c5e362bfd 100644 --- a/gnucash/import-export/csv-imp/gnc-csv-import-settings.cpp +++ b/gnucash/import-export/csv-imp/gnc-csv-import-settings.cpp @@ -1,5 +1,5 @@ /*******************************************************************\ - * gnc-csv-import-settings.c -- Save and Load CSV Import Settings * + * gnc-csv-import-settings.cpp -- Save and Load CSV Import Settings * * * * Copyright (C) 2014 Robert Fewell * * * @@ -20,7 +20,7 @@ * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * * Boston, MA 02110-1301, USA gnu@gnu.org * \********************************************************************/ -/** @file gnc-csv-import-settings.c +/** @file gnc-csv-import-settings.cpp @brief CSV Import Settings @author Copyright (c) 2014 Robert Fewell @author Copyright (c) 2016 Geert Janssens @@ -44,12 +44,12 @@ extern "C" const std::string csv_group_prefix{"CSV-"}; const std::string no_settings{N_("No Settings")}; const std::string gnc_exp{N_("GnuCash Export Format")}; + #define CSV_NAME "Name" #define CSV_FORMAT "CsvFormat" #define CSV_SKIP_ALT "SkipAltLines" #define CSV_SKIP_START "SkipStartLines" #define CSV_SKIP_END "SkipEndLines" -#define CSV_MULTI_SPLIT "MultiSplit" #define CSV_SEP "Separators" @@ -60,121 +60,10 @@ const std::string gnc_exp{N_("GnuCash Export Format")}; #define CSV_CURRENCY "CurrencyFormat" #define CSV_ENCODING "Encoding" -#define CSV_COL_TYPES "ColumnTypes" #define CSV_COL_WIDTHS "ColumnWidths" -#define CSV_ACCOUNT "BaseAccount" -#define CSV_TO_CURR "PriceToCurrency" -#define CSV_FROM_COMM "PriceFromCommodity" G_GNUC_UNUSED static QofLogModule log_module = GNC_MOD_IMPORT; -preset_vec presets; - -static std::shared_ptr create_int_no_preset(const std::string& set_type) -{ - auto preset = std::make_shared(); - preset->m_name = no_settings; - preset->m_settings_type = set_type; - - return preset; -} - -static std::shared_ptr create_int_gnc_exp_preset(void) -{ - auto preset = std::make_shared(); - preset->m_name = gnc_exp; - preset->m_skip_start_lines = 1; - preset->m_multi_split = true; - - /* FIXME date and currency format should still be aligned with export format! - * That's currently hard to do, because the export uses whatever the user - * had set as global preference. - preset->date_active = 0; - preset->currency_active = 0; - */ - preset->m_column_types = { - GncTransPropType::DATE, - GncTransPropType::UNIQUE_ID, - GncTransPropType::NUM, - GncTransPropType::DESCRIPTION, - GncTransPropType::NOTES, - GncTransPropType::COMMODITY, - GncTransPropType::VOID_REASON, - GncTransPropType::ACTION, - GncTransPropType::MEMO, - GncTransPropType::ACCOUNT, - GncTransPropType::NONE, - GncTransPropType::NONE, - GncTransPropType::DEPOSIT, - GncTransPropType::REC_STATE, - GncTransPropType::REC_DATE, - GncTransPropType::PRICE - }; - return preset; -} - -/************************************************** - * find - * - * find all settings entries in the state key file - * based on settings type. - **************************************************/ -const preset_vec& get_import_presets (const std::string& set_type) -{ - - // Search all Groups in the state key file for ones starting with prefix - auto preset_names = std::vector(); - auto keyfile = gnc_state_get_current (); - gsize grouplength; - gchar **groups = g_key_file_get_groups (keyfile, &grouplength); - - /* Start by building a sorted list of candidate presets as found in the state file */ - for (gsize i=0; i < grouplength; i++) - { - auto group = std::string(groups[i]); - auto gp = csv_group_prefix + set_type + " - "; - auto pos = group.find(gp); - if (pos == std::string::npos) - continue; - - preset_names.push_back(group.substr(gp.size())); - } - // string array from the state file is no longer needed now. - g_strfreev (groups); - - /* We want our settings to appear sorted alphabetically to the user */ - std::sort(preset_names.begin(), preset_names.end()); - - /* Now add each preset to our global list */ - presets.clear(); - - /* Start with the internally generated ones */ - presets.push_back(create_int_no_preset(set_type)); - - if (set_type.compare("TRANS") == 0) - presets.push_back(create_int_gnc_exp_preset()); - - /* Then add all the ones we found in the state file */ - for (auto preset_name : preset_names) - { - auto preset = std::make_shared(); - preset->m_settings_type = set_type; - preset->m_name = preset_name; - preset->load(); - presets.push_back(preset); - } - return presets; -} - -bool preset_is_reserved_name (const std::string& name) -{ - return ((name == no_settings) || - (name == _(no_settings.c_str())) || - (name == gnc_exp) || - (name == _(gnc_exp.c_str()))); -} - - /************************************************** * handle_load_error * @@ -182,7 +71,7 @@ bool preset_is_reserved_name (const std::string& name) * ignore key-not-found errors though. We'll just * use a default value and go on. **************************************************/ -static bool +bool handle_load_error (GError **key_error, const std::string& group) { if (!*key_error) @@ -199,17 +88,37 @@ handle_load_error (GError **key_error, const std::string& group) return true; } +bool preset_is_reserved_name (const std::string& name) +{ + return ((name == no_settings) || + (name == _(no_settings.c_str())) || + (name == gnc_exp) || + (name == _(gnc_exp.c_str()))); +} + +std::string get_no_settings (void) +{ + return no_settings; +} + +std::string get_gnc_exp (void) +{ + return gnc_exp; +} + +std::string get_prefix (void) +{ + return csv_group_prefix; +} + /************************************************** - * load + * load_common * * load the settings from a state key file **************************************************/ bool -CsvImportSettings::load (void) +CsvImportSettings::load_common (void) { - if (preset_is_reserved_name (m_name)) - return true; - GError *key_error = nullptr; m_load_error = false; auto group = csv_group_prefix + m_settings_type + " - " + m_name; @@ -224,9 +133,6 @@ CsvImportSettings::load (void) m_skip_alt_lines = g_key_file_get_boolean (keyfile, group.c_str(), CSV_SKIP_ALT, &key_error); m_load_error |= handle_load_error (&key_error, group); - m_multi_split = g_key_file_get_boolean (keyfile, group.c_str(), CSV_MULTI_SPLIT, &key_error); - m_load_error |= handle_load_error (&key_error, group); - auto csv_format = g_key_file_get_boolean (keyfile, group.c_str(), CSV_FORMAT, &key_error); if (key_error) csv_format = true; // default to true, but above command will return false in case of error m_load_error |= handle_load_error (&key_error, group); @@ -257,82 +163,8 @@ CsvImportSettings::load (void) if (key_char) g_free (key_char); - gsize list_len; - - // Transactions - if (m_settings_type.compare("TRANS") == 0) - { - key_char = g_key_file_get_string (keyfile, group.c_str(), CSV_ACCOUNT, &key_error); - if (key_char && *key_char != '\0') - m_base_account = gnc_account_lookup_by_full_name (gnc_get_current_root_account(), key_char); - m_load_error |= handle_load_error (&key_error, group); - if (key_char) - g_free (key_char); - - m_column_types.clear(); - gchar** col_types_str = g_key_file_get_string_list (keyfile, group.c_str(), CSV_COL_TYPES, - &list_len, &key_error); - for (uint32_t i = 0; i < list_len; i++) - { - auto col_types_it = std::find_if (gnc_csv_col_type_strs.begin(), - gnc_csv_col_type_strs.end(), test_prop_type_str (col_types_str[i])); - if (col_types_it != gnc_csv_col_type_strs.end()) - { - /* Found a valid column type. Now check whether it is allowed - * in the selected mode (two-split vs multi-split) */ - auto prop = sanitize_trans_prop (col_types_it->first, m_multi_split); - m_column_types.push_back(prop); - if (prop != col_types_it->first) - PWARN("Found column type '%s', but this is blacklisted when multi-split mode is %s. " - "Inserting column type 'NONE' instead'.", - col_types_it->second, m_multi_split ? "enabled" : "disabled"); - } - else - PWARN("Found invalid column type '%s'. Inserting column type 'NONE' instead'.", - col_types_str[i]); - } - if (col_types_str) - g_strfreev (col_types_str); - } - - // Price - if (m_settings_type.compare("PRICE") == 0) - { - key_char = g_key_file_get_string (keyfile, group.c_str(), CSV_TO_CURR, &key_error); - if (key_char && *key_char != '\0') - m_to_currency = parse_commodity_price_comm (key_char); - m_load_error |= handle_load_error (&key_error, group); - if (key_char) - g_free (key_char); - - key_char = g_key_file_get_string (keyfile, group.c_str(), CSV_FROM_COMM, &key_error); - if (key_char && *key_char != '\0') - m_from_commodity = parse_commodity_price_comm (key_char); - m_load_error |= handle_load_error (&key_error, group); - if (key_char) - g_free (key_char); - - m_column_types.clear(); - gchar** col_types_str_price = g_key_file_get_string_list (keyfile, group.c_str(), CSV_COL_TYPES, - &list_len, &key_error); - for (uint32_t i = 0; i < list_len; i++) - { - auto col_types_it = std::find_if (gnc_price_col_type_strs.begin(), - gnc_price_col_type_strs.end(), test_price_prop_type_str (col_types_str_price[i])); - if (col_types_it != gnc_price_col_type_strs.end()) - { - // Found a valid column type - m_column_types_price.push_back(col_types_it->first); - } - else - PWARN("Found invalid column type '%s'. Inserting column type 'NONE' instead'.", - col_types_str_price[i]); - } - if (col_types_str_price) - g_strfreev (col_types_str_price); - } - // Widths + gsize list_len; m_column_widths.clear(); gint *col_widths_int = g_key_file_get_integer_list (keyfile, group.c_str(), CSV_COL_WIDTHS, &list_len, &key_error); @@ -348,35 +180,18 @@ CsvImportSettings::load (void) return m_load_error; } - /************************************************** - * save + * save_common * * save settings to a key file **************************************************/ bool -CsvImportSettings::save (void) +CsvImportSettings::save_common (void) { - if (preset_is_reserved_name (m_name)) - { - PWARN ("Ignoring attempt to save to reserved name '%s'", m_name.c_str()); - return true; - } - - if ((m_name.find('[') != std::string::npos)) - { - PWARN ("Name '%s' contains invalid characters '[]'. Refusing to save", m_name.c_str()); - return true; - } - auto keyfile = gnc_state_get_current (); auto group = csv_group_prefix + m_settings_type + " - " + m_name; - // Drop previous saved settings with this name - g_key_file_remove_group (keyfile, group.c_str(), nullptr); - - // Start Saving the settings - // Common + // Start Saving the Common settings g_key_file_set_string (keyfile, group.c_str(), CSV_NAME, m_name.c_str()); g_key_file_set_integer (keyfile, group.c_str(), CSV_SKIP_START, m_skip_start_lines); @@ -394,8 +209,7 @@ CsvImportSettings::save (void) [&cmt_ss, &fmt_num](const GncDateFormat& fmt) { cmt_ss << fmt_num++ << ": '" << fmt.m_fmt << "', "; }); auto cmt = cmt_ss.str().substr(0, static_cast(cmt_ss.tellp()) - 2); - g_key_file_set_comment (keyfile, group.c_str(), CSV_DATE, - cmt.c_str(), nullptr); + g_key_file_set_comment (keyfile, group.c_str(), CSV_DATE, cmt.c_str(), nullptr); g_key_file_set_integer (keyfile, group.c_str(), CSV_CURRENCY, m_currency_format); g_key_file_set_string (keyfile, group.c_str(), CSV_ENCODING, m_encoding.c_str()); @@ -403,51 +217,6 @@ CsvImportSettings::save (void) g_key_file_set_integer_list (keyfile, group.c_str(), CSV_COL_WIDTHS, (gint*)(m_column_widths.data()), m_column_widths.size()); - // Transaction - if (m_settings_type.compare("TRANS") == 0) - { - g_key_file_set_boolean (keyfile, group.c_str(), CSV_MULTI_SPLIT, m_multi_split); - - if (m_base_account) - g_key_file_set_string (keyfile, group.c_str(), CSV_ACCOUNT, gnc_account_get_full_name(m_base_account)); - - std::vector col_types_str; - for (auto col_type : m_column_types) - col_types_str.push_back(gnc_csv_col_type_strs[col_type]); - - if (!col_types_str.empty()) - g_key_file_set_string_list (keyfile, group.c_str(), CSV_COL_TYPES, - col_types_str.data(), col_types_str.size()); - } - - // Price - if (m_settings_type.compare("PRICE") == 0) - { - if (m_to_currency) - { - auto unique_name = g_strconcat (gnc_commodity_get_namespace (m_to_currency), "::", - gnc_commodity_get_mnemonic (m_to_currency), nullptr); - g_key_file_set_string (keyfile, group.c_str(), CSV_TO_CURR, unique_name); - g_free (unique_name); - } - - if (m_from_commodity) - { - auto unique_name = g_strconcat (gnc_commodity_get_namespace (m_from_commodity), "::", - gnc_commodity_get_mnemonic (m_from_commodity), nullptr); - g_key_file_set_string (keyfile, group.c_str(), CSV_FROM_COMM, unique_name); - g_free (unique_name); - } - - std::vector col_types_str_price; - for (auto col_type : m_column_types_price) - col_types_str_price.push_back(gnc_price_col_type_strs[col_type]); - - if (!col_types_str_price.empty()) - g_key_file_set_string_list (keyfile, group.c_str(), CSV_COL_TYPES, - col_types_str_price.data(), col_types_str_price.size()); - } - // Do a test read of encoding GError *key_error = nullptr; bool error = false; @@ -460,33 +229,20 @@ CsvImportSettings::save (void) { if (key_error) { - g_warning ("Error reading group %s key %s: %s", group.c_str(), CSV_COL_TYPES, key_error->message); + g_warning ("Error reading group %s key %s: %s", group.c_str(), CSV_ENCODING, key_error->message); g_error_free (key_error); } else - g_warning ("Error comparing group %s key %s: '%s' and '%s'", group.c_str(), CSV_COL_TYPES, enc_str.c_str(), group.c_str()); + g_warning ("Error comparing group %s key %s: '%s' and '%s'", group.c_str(), CSV_ENCODING, enc_str.c_str(), group.c_str()); error = true; } return error; } void -CsvImportSettings::remove (void) +CsvImportSettings::remove_common (void) { - if (preset_is_reserved_name (m_name)) - return; - auto keyfile = gnc_state_get_current (); auto group = csv_group_prefix + m_settings_type + " - " + m_name; g_key_file_remove_group (keyfile, group.c_str(), nullptr); } - - -bool -CsvImportSettings::read_only (void) -{ - return ((m_name == no_settings) || - (m_name == _(no_settings.c_str())) || - (m_name == gnc_exp) || - (m_name == _(gnc_exp.c_str()))); -} diff --git a/gnucash/import-export/csv-imp/gnc-csv-import-settings.hpp b/gnucash/import-export/csv-imp/gnc-csv-import-settings.hpp index e6ce4d4f22..6102fed144 100644 --- a/gnucash/import-export/csv-imp/gnc-csv-import-settings.hpp +++ b/gnucash/import-export/csv-imp/gnc-csv-import-settings.hpp @@ -1,5 +1,5 @@ /*******************************************************************\ - * gnc-csv-import-settings.h -- Save and Load CSV Import Settings * + * gnc-csv-import-settings.hpp -- Save and Load CSV Import Settings * * * * Copyright (C) 2014 Robert Fewell * * * @@ -20,7 +20,7 @@ * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * * Boston, MA 02110-1301, USA gnu@gnu.org * \********************************************************************/ -/** @file gnc-csv-import-settings.h +/** @file gnc-csv-import-settings.hpp @brief CSV Import Settings @author Copyright (c) 2014 Robert Fewell @author Copyright (c) 2016 Geert Janssens @@ -36,8 +36,8 @@ extern "C" { #include #include -#include "gnc-trans-props.hpp" -#include "gnc-price-props.hpp" +#include +#include #include "gnc-tokenizer.hpp" /** Enumeration for separator checkbutton types. These are the @@ -52,34 +52,25 @@ enum SETTINGS_COL {SET_GROUP, SET_NAME}; struct CsvImportSettings { CsvImportSettings() : m_file_format (GncImpFileFormat::CSV), m_encoding {"UTF-8"}, - m_multi_split (false), m_date_format {0}, m_currency_format {0}, + m_date_format {0}, m_currency_format {0}, m_skip_start_lines{0}, m_skip_end_lines{0}, m_skip_alt_lines (false), - m_separators {","}, m_load_error {false}, m_base_account {nullptr}, - m_from_commodity {nullptr}, m_to_currency {nullptr} { } + m_separators {","}, m_load_error {false} { } /** Save the gathered widget properties to a key File. * * @return true if there was a problem in saving. */ -bool save (void); +bool save_common (void); /** Load the widget properties from a key File. * * @return true if there was a problem. */ -bool load (void); +bool load_common (void); /** Remove the preset from the state file. */ -void remove (void); - -/** Check whether the user is allowed to save (over) or delete this preset or not. - * The internally generated presets are read-only. The others - * can be saved to the state file or deleted. - * - * @return true if there was a problem. - */ -bool read_only (void); +void remove_common (void); std::string m_settings_type; // Settings Type, TRANS, PRICE etc. @@ -87,7 +78,6 @@ std::string m_settings_type; // Settings Type, TRANS, PRICE etc std::string m_name; // Name given to this preset by the user GncImpFileFormat m_file_format; // CSV import Format std::string m_encoding; // File encoding -bool m_multi_split; // Assume multiple lines per transaction int m_date_format; // Date Active id int m_currency_format; // Currency Active id uint32_t m_skip_start_lines; // Number of header rows to skip @@ -96,28 +86,11 @@ bool m_skip_alt_lines; // Skip alternate rows std::string m_separators; // Separators for csv format bool m_load_error; // Was there an error while parsing the state file ? std::vector m_column_widths; // The Column widths - -// Transaction Settings -Account *m_base_account; // Base account -std::vector m_column_types; // The Column types in order - -// Price Settings -gnc_commodity *m_from_commodity; // Price From Commodity -gnc_commodity *m_to_currency; // Price To Currency -std::vector m_column_types_price; // The Price Column types in order }; -using preset_vec = std::vector>; -/** Creates a vector of CsvImportSettings which combines - * - one or more internally defined presets - * - all preset found in the state key file. - * - * @param set_type The type of setting stored in the - * key file, TRANS, PRICE, etc. - * - * @return a reference to the populated vector. - */ -const preset_vec& get_import_presets (const std::string& set_type); +std::string get_no_settings (void); +std::string get_gnc_exp (void); +std::string get_prefix (void); /** Check whether name can be used as a preset name. * The names of the internal presets are considered reserved. @@ -125,4 +98,14 @@ const preset_vec& get_import_presets (const std::string& set_type); */ bool preset_is_reserved_name (const std::string& name); +/************************************************** + * handle_load_error + * + * record possible errors in the log file + * ignore key-not-found errors though. We'll just + * use a default value and go on. + **************************************************/ +bool +handle_load_error (GError **key_error, const std::string& group); + #endif diff --git a/gnucash/import-export/csv-imp/gnc-csv-price-import-settings.cpp b/gnucash/import-export/csv-imp/gnc-csv-price-import-settings.cpp new file mode 100644 index 0000000000..8ebe9eba6b --- /dev/null +++ b/gnucash/import-export/csv-imp/gnc-csv-price-import-settings.cpp @@ -0,0 +1,258 @@ +/*******************************************************************\ + * gnc-csv-price-import-settings.cpp -- Price CSV Import Settings * + * * + * Copyright (C) 2017 Robert Fewell * + * * + * 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 * +\********************************************************************/ +/** @file gnc-csv-price-import-settings.cpp + @brief CSV Import Settings + @author Copyright (c) 2014 Robert Fewell + @author Copyright (c) 2016 Geert Janssens +*/ + +#include "gnc-csv-import-settings.hpp" +#include "gnc-csv-price-import-settings.hpp" +#include + +extern "C" +{ +#include + +#include +#include + +#include "Account.h" +#include "gnc-state.h" +#include "gnc-ui-util.h" +} + +const std::string settings_type{"PRICE"}; + +#define CSV_COL_TYPES "ColumnTypes" + +#define CSV_TO_CURR "PriceToCurrency" +#define CSV_FROM_COMM "PriceFromCommodity" + +G_GNUC_UNUSED static QofLogModule log_module = GNC_MOD_IMPORT; + +preset_vec_price presets_price; + +static std::shared_ptr create_int_no_preset(void) +{ + auto preset = std::make_shared(); + preset->m_name = get_no_settings(); + preset->m_settings_type = settings_type; + + return preset; +} + +static std::shared_ptr create_int_gnc_exp_preset(void) +{ + auto preset = std::make_shared(); + preset->m_name = get_gnc_exp(); + preset->m_skip_start_lines = 1; + + /* FIXME date and currency format should still be aligned with export format! + * That's currently hard to do, because the export uses whatever the user + * had set as global preference. + preset->date_active = 0; + preset->currency_active = 0; + */ + preset->m_column_types_price = { + GncPricePropType::DATE, + GncPricePropType::AMOUNT, + GncPricePropType::FROM_COMMODITY, + GncPricePropType::TO_CURRENCY + }; + return preset; +} + +/************************************************** + * find + * + * find all settings entries in the state key file + * based on settings type. + **************************************************/ +const preset_vec_price& get_import_presets_price (void) +{ + // Search all Groups in the state key file for ones starting with prefix + auto preset_names = std::vector(); + auto keyfile = gnc_state_get_current (); + gsize grouplength; + gchar **groups = g_key_file_get_groups (keyfile, &grouplength); + + /* Start by building a sorted list of candidate presets as found in the state file */ + for (gsize i=0; i < grouplength; i++) + { + auto group = std::string(groups[i]); + auto gp = get_prefix() + settings_type + " - "; + auto pos = group.find(gp); + if (pos == std::string::npos) + continue; + + preset_names.push_back(group.substr(gp.size())); + } + // string array from the state file is no longer needed now. + g_strfreev (groups); + + /* We want our settings to appear sorted alphabetically to the user */ + std::sort(preset_names.begin(), preset_names.end()); + + /* Now add each preset to our global list */ + presets_price.clear(); + + /* Start with the internally generated ones */ + presets_price.push_back(create_int_no_preset()); + //presets_price.push_back(create_int_gnc_exp_preset()); // Not Required + + /* Then add all the ones we found in the state file */ + for (auto preset_name : preset_names) + { + auto preset = std::make_shared(); + preset->m_settings_type = settings_type; + preset->m_name = preset_name; + preset->load(); + presets_price.push_back(preset); + } + return presets_price; +} + +/************************************************** + * load + * + * load the settings from a state key file + **************************************************/ +bool +CsvPriceImpSettings::load (void) +{ + if (preset_is_reserved_name (m_name)) + return true; + + GError *key_error = nullptr; + m_load_error = false; + auto keyfile = gnc_state_get_current (); + auto group = get_prefix() + m_settings_type + " - " + m_name; + + // Start Loading the settings + m_load_error = load_common(); // load the common settings + + gchar *key_char = g_key_file_get_string (keyfile, group.c_str(), CSV_TO_CURR, &key_error); + if (key_char && *key_char != '\0') + m_to_currency = parse_commodity_price_comm (key_char); + m_load_error |= handle_load_error (&key_error, group); + if (key_char) + g_free (key_char); + + key_char = g_key_file_get_string (keyfile, group.c_str(), CSV_FROM_COMM, &key_error); + if (key_char && *key_char != '\0') + m_from_commodity = parse_commodity_price_comm (key_char); + m_load_error |= handle_load_error (&key_error, group); + if (key_char) + g_free (key_char); + + gsize list_len; + m_column_types_price.clear(); + gchar** col_types_str_price = g_key_file_get_string_list (keyfile, group.c_str(), CSV_COL_TYPES, + &list_len, &key_error); + for (uint32_t i = 0; i < list_len; i++) + { + auto col_types_it = std::find_if (gnc_price_col_type_strs.begin(), + gnc_price_col_type_strs.end(), test_price_prop_type_str (col_types_str_price[i])); + if (col_types_it != gnc_price_col_type_strs.end()) + { + // Found a valid column type + m_column_types_price.push_back(col_types_it->first); + } + else + PWARN("Found invalid column type '%s'. Inserting column type 'NONE' instead'.", + col_types_str_price[i]); + } + if (col_types_str_price) + g_strfreev (col_types_str_price); + + return m_load_error; +} + +/************************************************** + * save + * + * save settings to a key file + **************************************************/ +bool +CsvPriceImpSettings::save (void) +{ + if (preset_is_reserved_name (m_name)) + { + PWARN ("Ignoring attempt to save to reserved name '%s'", m_name.c_str()); + return true; + } + + if ((m_name.find('[') != std::string::npos)) + { + PWARN ("Name '%s' contains invalid characters '[]'. Refusing to save", m_name.c_str()); + return true; + } + + auto keyfile = gnc_state_get_current (); + auto group = get_prefix() + m_settings_type + " - " + m_name; + + // Drop previous saved settings with this name + g_key_file_remove_group (keyfile, group.c_str(), nullptr); + + // Start Saving the settings + bool error = save_common(); // save the common settings + + if (error) + return error; + + if (m_to_currency) + { + auto unique_name = g_strconcat (gnc_commodity_get_namespace (m_to_currency), "::", + gnc_commodity_get_mnemonic (m_to_currency), nullptr); + g_key_file_set_string (keyfile, group.c_str(), CSV_TO_CURR, unique_name); + g_free (unique_name); + } + + if (m_from_commodity) + { + auto unique_name = g_strconcat (gnc_commodity_get_namespace (m_from_commodity), "::", + gnc_commodity_get_mnemonic (m_from_commodity), nullptr); + g_key_file_set_string (keyfile, group.c_str(), CSV_FROM_COMM, unique_name); + g_free (unique_name); + } + + std::vector col_types_str_price; + for (auto col_type : m_column_types_price) + col_types_str_price.push_back(gnc_price_col_type_strs[col_type]); + + if (!col_types_str_price.empty()) + g_key_file_set_string_list (keyfile, group.c_str(), CSV_COL_TYPES, + col_types_str_price.data(), col_types_str_price.size()); + + return error; +} + +void +CsvPriceImpSettings::remove (void) +{ + if (preset_is_reserved_name (m_name)) + return; + + remove_common(); +} diff --git a/gnucash/import-export/csv-imp/gnc-csv-price-import-settings.hpp b/gnucash/import-export/csv-imp/gnc-csv-price-import-settings.hpp new file mode 100644 index 0000000000..e0d52b480e --- /dev/null +++ b/gnucash/import-export/csv-imp/gnc-csv-price-import-settings.hpp @@ -0,0 +1,79 @@ +/*******************************************************************\ + * gnc-csv-price-import-settings.hpp -- Price CSV Import Settings * + * * + * Copyright (C) 2017 Robert Fewell * + * * + * 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 * +\********************************************************************/ +/** @file gnc-csv-price-import-settings.hpp + @brief CSV Import Settings + @author Copyright (c) 2014 Robert Fewell + @author Copyright (c) 2016 Geert Janssens +*/ +#ifndef GNC_CSV_PRICE_IMPORT_SETTINGS_H +#define GNC_CSV_PRICE_IMPORT_SETTINGS_H + +extern "C" { +#include +#include "Account.h" +#include "gnc-commodity.h" +} + +#include +#include +#include "gnc-price-props.hpp" +#include "gnc-tokenizer.hpp" +#include "gnc-csv-import-settings.hpp" + +struct CsvPriceImpSettings : public CsvImportSettings +{ + CsvPriceImpSettings() : m_from_commodity {nullptr}, m_to_currency {nullptr} { } + +/** Save the gathered widget properties to a key File. + * + * @return true if there was a problem in saving. + */ +bool save (void); + +/** Load the widget properties from a key File. + * + * @return true if there was a problem. + */ +bool load (void); + +/** Remove the preset from the state file. + */ +void remove (void); + +// Price Settings +gnc_commodity *m_from_commodity; // Price From Commodity +gnc_commodity *m_to_currency; // Price To Currency +std::vector m_column_types_price; // The Price Column types in order +}; + +using preset_vec_price = std::vector>; + +/** Creates a vector of CsvPriceImpSettings which combines + * - one or more internally defined presets + * - all preset found in the state key file. + * + * @return a reference to the populated vector. + */ +const preset_vec_price& get_import_presets_price (void); + +#endif diff --git a/gnucash/import-export/csv-imp/gnc-csv-trans-import-settings.cpp b/gnucash/import-export/csv-imp/gnc-csv-trans-import-settings.cpp new file mode 100644 index 0000000000..17ac2b79c3 --- /dev/null +++ b/gnucash/import-export/csv-imp/gnc-csv-trans-import-settings.cpp @@ -0,0 +1,262 @@ +/*******************************************************************\ + * gnc-csv-trans-import-settings.cpp -- Trans CSV Import Settings * + * * + * Copyright (C) 2014 Robert Fewell * + * * + * 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 * +\********************************************************************/ +/** @file gnc-csv-trans-import-settings.cpp + @brief CSV Import Settings + @author Copyright (c) 2014 Robert Fewell + @author Copyright (c) 2016 Geert Janssens +*/ + +#include "gnc-csv-import-settings.hpp" +#include "gnc-csv-trans-import-settings.hpp" +#include + +extern "C" +{ +#include + +#include +#include + +#include "Account.h" +#include "gnc-state.h" +#include "gnc-ui-util.h" +} + +const std::string settings_type{"TRANS"}; + +#define CSV_COL_TYPES "ColumnTypes" + +#define CSV_ACCOUNT "BaseAccount" +#define CSV_MULTI_SPLIT "MultiSplit" + +G_GNUC_UNUSED static QofLogModule log_module = GNC_MOD_IMPORT; + +preset_vec_trans presets_trans; + +static std::shared_ptr create_int_no_preset(void) +{ + auto preset = std::make_shared(); + preset->m_name = get_no_settings(); + preset->m_settings_type = settings_type; + + return preset; +} + +static std::shared_ptr create_int_gnc_exp_preset(void) +{ + auto preset = std::make_shared(); + preset->m_name = get_gnc_exp(); + preset->m_skip_start_lines = 1; + preset->m_multi_split = true; + + /* FIXME date and currency format should still be aligned with export format! + * That's currently hard to do, because the export uses whatever the user + * had set as global preference. + preset->date_active = 0; + preset->currency_active = 0; + */ + preset->m_column_types = { + GncTransPropType::DATE, + GncTransPropType::UNIQUE_ID, + GncTransPropType::NUM, + GncTransPropType::DESCRIPTION, + GncTransPropType::NOTES, + GncTransPropType::COMMODITY, + GncTransPropType::VOID_REASON, + GncTransPropType::ACTION, + GncTransPropType::MEMO, + GncTransPropType::ACCOUNT, + GncTransPropType::NONE, + GncTransPropType::NONE, + GncTransPropType::DEPOSIT, + GncTransPropType::REC_STATE, + GncTransPropType::REC_DATE, + GncTransPropType::PRICE + }; + return preset; +} + +/************************************************** + * find + * + * find all settings entries in the state key file + * based on settings type. + **************************************************/ +const preset_vec_trans& get_import_presets_trans (void) +{ + // Search all Groups in the state key file for ones starting with prefix + auto preset_names = std::vector(); + auto keyfile = gnc_state_get_current (); + gsize grouplength; + gchar **groups = g_key_file_get_groups (keyfile, &grouplength); + + /* Start by building a sorted list of candidate presets as found in the state file */ + for (gsize i=0; i < grouplength; i++) + { + auto group = std::string(groups[i]); + auto gp = get_prefix() + settings_type + " - "; + auto pos = group.find(gp); + if (pos == std::string::npos) + continue; + + preset_names.push_back(group.substr(gp.size())); + } + // string array from the state file is no longer needed now. + g_strfreev (groups); + + /* We want our settings to appear sorted alphabetically to the user */ + std::sort(preset_names.begin(), preset_names.end()); + + /* Now add each preset to our global list */ + presets_trans.clear(); + + /* Start with the internally generated ones */ + presets_trans.push_back(create_int_no_preset()); + presets_trans.push_back(create_int_gnc_exp_preset()); + + /* Then add all the ones we found in the state file */ + for (auto preset_name : preset_names) + { + auto preset = std::make_shared(); + preset->m_settings_type = settings_type; + preset->m_name = preset_name; + preset->load(); + presets_trans.push_back(preset); + } + return presets_trans; +} + +/************************************************** + * load + * + * load the settings from a state key file + **************************************************/ +bool +CsvTransImpSettings::load (void) +{ + if (preset_is_reserved_name (m_name)) + return true; + + GError *key_error = nullptr; + m_load_error = false; + auto keyfile = gnc_state_get_current (); + auto group = get_prefix() + m_settings_type + " - " + m_name; + + // Start Loading the settings + m_load_error = load_common(); // load the common settings + + m_multi_split = g_key_file_get_boolean (keyfile, group.c_str(), CSV_MULTI_SPLIT, &key_error); + m_load_error |= handle_load_error (&key_error, group); + + gchar *key_char = g_key_file_get_string (keyfile, group.c_str(), CSV_ACCOUNT, &key_error); + if (key_char && *key_char != '\0') + m_base_account = gnc_account_lookup_by_full_name (gnc_get_current_root_account(), key_char); + m_load_error |= handle_load_error (&key_error, group); + if (key_char) + g_free (key_char); + + gsize list_len; + m_column_types.clear(); + gchar** col_types_str = g_key_file_get_string_list (keyfile, group.c_str(), CSV_COL_TYPES, + &list_len, &key_error); + for (uint32_t i = 0; i < list_len; i++) + { + auto col_types_it = std::find_if (gnc_csv_col_type_strs.begin(), + gnc_csv_col_type_strs.end(), test_prop_type_str (col_types_str[i])); + if (col_types_it != gnc_csv_col_type_strs.end()) + { + /* Found a valid column type. Now check whether it is allowed + * in the selected mode (two-split vs multi-split) */ + auto prop = sanitize_trans_prop (col_types_it->first, m_multi_split); + m_column_types.push_back(prop); + if (prop != col_types_it->first) + PWARN("Found column type '%s', but this is blacklisted when multi-split mode is %s. " + "Inserting column type 'NONE' instead'.", + col_types_it->second, m_multi_split ? "enabled" : "disabled"); + } + else + PWARN("Found invalid column type '%s'. Inserting column type 'NONE' instead'.", + col_types_str[i]); + } + if (col_types_str) + g_strfreev (col_types_str); + + return m_load_error; +} + +/************************************************** + * save + * + * save settings to a key file + **************************************************/ +bool +CsvTransImpSettings::save (void) +{ + if (preset_is_reserved_name (m_name)) + { + PWARN ("Ignoring attempt to save to reserved name '%s'", m_name.c_str()); + return true; + } + + if ((m_name.find('[') != std::string::npos)) + { + PWARN ("Name '%s' contains invalid characters '[]'. Refusing to save", m_name.c_str()); + return true; + } + + auto keyfile = gnc_state_get_current (); + auto group = get_prefix() + m_settings_type + " - " + m_name; + + // Drop previous saved settings with this name + g_key_file_remove_group (keyfile, group.c_str(), nullptr); + + // Start Saving the settings + bool error = save_common(); // save the common settings + + if (error) + return error; + + g_key_file_set_boolean (keyfile, group.c_str(), CSV_MULTI_SPLIT, m_multi_split); + + if (m_base_account) + g_key_file_set_string (keyfile, group.c_str(), CSV_ACCOUNT, gnc_account_get_full_name(m_base_account)); + + std::vector col_types_str; + for (auto col_type : m_column_types) + col_types_str.push_back(gnc_csv_col_type_strs[col_type]); + + if (!col_types_str.empty()) + g_key_file_set_string_list (keyfile, group.c_str(), CSV_COL_TYPES, + col_types_str.data(), col_types_str.size()); + + return error; +} + +void +CsvTransImpSettings::remove (void) +{ + if (preset_is_reserved_name (m_name)) + return; + + remove_common(); +} diff --git a/gnucash/import-export/csv-imp/gnc-csv-trans-import-settings.hpp b/gnucash/import-export/csv-imp/gnc-csv-trans-import-settings.hpp new file mode 100644 index 0000000000..c93ef3a7ce --- /dev/null +++ b/gnucash/import-export/csv-imp/gnc-csv-trans-import-settings.hpp @@ -0,0 +1,79 @@ +/*******************************************************************\ + * gnc-csv-trans-import-settings.hpp -- Trans CSV Import Settings * + * * + * Copyright (C) 2017 Robert Fewell * + * * + * 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 * +\********************************************************************/ +/** @file gnc-csv-trans-import-settings.hpp + @brief CSV Import Settings + @author Copyright (c) 2014 Robert Fewell + @author Copyright (c) 2016 Geert Janssens +*/ +#ifndef GNC_CSV_TRANS_IMPORT_SETTINGS_H +#define GNC_CSV_TRANS_IMPORT_SETTINGS_H + +extern "C" { +#include +#include "Account.h" +#include "gnc-commodity.h" +} + +#include +#include +#include "gnc-trans-props.hpp" +#include "gnc-tokenizer.hpp" +#include "gnc-csv-import-settings.hpp" + +struct CsvTransImpSettings : public CsvImportSettings +{ + CsvTransImpSettings() : m_base_account {nullptr}, m_multi_split (false) { } + +/** Save the gathered widget properties to a key File. + * + * @return true if there was a problem in saving. + */ +bool save (void); + +/** Load the widget properties from a key File. + * + * @return true if there was a problem. + */ +bool load (void); + +/** Remove the preset from the state file. + */ +void remove (void); + +// Transaction Settings +Account *m_base_account; // Base account +bool m_multi_split; // Assume multiple lines per transaction +std::vector m_column_types; // The Column types in order +}; + +using preset_vec_trans = std::vector>; + +/** Creates a vector of CsvTransImpSettings which combines + * - one or more internally defined presets + * - all preset found in the state key file. + * + * @return a reference to the populated vector. + */ +const preset_vec_trans& get_import_presets_trans (void); + +#endif diff --git a/gnucash/import-export/csv-imp/gnc-price-import.cpp b/gnucash/import-export/csv-imp/gnc-price-import.cpp index df91261fc1..0bc1ead8a1 100644 --- a/gnucash/import-export/csv-imp/gnc-price-import.cpp +++ b/gnucash/import-export/csv-imp/gnc-price-import.cpp @@ -42,7 +42,7 @@ extern "C" { #include "gnc-price-props.hpp" #include "gnc-csv-tokenizer.hpp" #include "gnc-fw-tokenizer.hpp" -#include "gnc-csv-import-settings.hpp" +#include "gnc-csv-price-import-settings.hpp" G_GNUC_UNUSED static QofLogModule log_module = GNC_MOD_IMPORT; @@ -272,7 +272,7 @@ void GncPriceImport::separators (std::string separators) } std::string GncPriceImport::separators () { return m_settings.m_separators; } -void GncPriceImport::settings (const CsvImportSettings& settings) +void GncPriceImport::settings (const CsvPriceImpSettings& settings) { /* First apply file format as this may recreate the tokenizer */ file_format (settings.m_file_format); diff --git a/gnucash/import-export/csv-imp/gnc-price-import.hpp b/gnucash/import-export/csv-imp/gnc-price-import.hpp index c91943f847..7d181ec168 100644 --- a/gnucash/import-export/csv-imp/gnc-price-import.hpp +++ b/gnucash/import-export/csv-imp/gnc-price-import.hpp @@ -42,7 +42,7 @@ extern "C" { #include "gnc-tokenizer.hpp" #include "gnc-price-props.hpp" -#include "gnc-csv-import-settings.hpp" +#include "gnc-csv-price-import-settings.hpp" #include /* A set of currency formats that the user sees. */ @@ -118,7 +118,7 @@ public: void separators (std::string separators); std::string separators (); - void settings (const CsvImportSettings& settings); + void settings (const CsvPriceImpSettings& settings); bool save_settings (); void settings_name (std::string name); @@ -164,7 +164,7 @@ private: */ void update_price_props (uint32_t row, uint32_t col, GncPricePropType prop_type); - CsvImportSettings m_settings; + CsvPriceImpSettings m_settings; bool m_skip_errors; bool m_over_write; }; diff --git a/gnucash/import-export/csv-imp/gnc-tx-import.cpp b/gnucash/import-export/csv-imp/gnc-tx-import.cpp index eddbd753ef..a43f29c543 100644 --- a/gnucash/import-export/csv-imp/gnc-tx-import.cpp +++ b/gnucash/import-export/csv-imp/gnc-tx-import.cpp @@ -39,7 +39,7 @@ extern "C" { #include "gnc-trans-props.hpp" #include "gnc-csv-tokenizer.hpp" #include "gnc-fw-tokenizer.hpp" -#include "gnc-csv-import-settings.hpp" +#include "gnc-csv-trans-import-settings.hpp" G_GNUC_UNUSED static QofLogModule log_module = GNC_MOD_IMPORT; @@ -293,7 +293,7 @@ void GncTxImport::separators (std::string separators) } std::string GncTxImport::separators () { return m_settings.m_separators; } -void GncTxImport::settings (const CsvImportSettings& settings) +void GncTxImport::settings (const CsvTransImpSettings& settings) { /* First apply file format as this may recreate the tokenizer */ file_format (settings.m_file_format); diff --git a/gnucash/import-export/csv-imp/gnc-tx-import.hpp b/gnucash/import-export/csv-imp/gnc-tx-import.hpp index 87e4ea4cd1..4fc339cbb3 100644 --- a/gnucash/import-export/csv-imp/gnc-tx-import.hpp +++ b/gnucash/import-export/csv-imp/gnc-tx-import.hpp @@ -43,7 +43,7 @@ extern "C" { #include "gnc-tokenizer.hpp" #include "gnc-trans-props.hpp" -#include "gnc-csv-import-settings.hpp" +#include "gnc-csv-trans-import-settings.hpp" #include @@ -136,7 +136,7 @@ public: void separators (std::string separators); std::string separators (); - void settings (const CsvImportSettings& settings); + void settings (const CsvTransImpSettings& settings); bool save_settings (); void settings_name (std::string name); @@ -189,8 +189,8 @@ private: void update_pre_trans_props (uint32_t row, uint32_t col, GncTransPropType prop_type); void update_pre_split_props (uint32_t row, uint32_t col, GncTransPropType prop_type); - struct CsvTranSettings; - CsvImportSettings m_settings; + struct CsvTranImpSettings; //FIXME do we need this line + CsvTransImpSettings m_settings; bool m_skip_errors; bool m_req_mapped_accts;