diff --git a/gnucash/gnucash-bin.c b/gnucash/gnucash-bin.c index 398ec2351c..1517b2bbe4 100644 --- a/gnucash/gnucash-bin.c +++ b/gnucash/gnucash-bin.c @@ -148,7 +148,6 @@ static GOptionEntry options[] = { NULL } }; -static gboolean userdata_migrated = FALSE; static gchar *userdata_migration_msg = NULL; static void @@ -387,11 +386,6 @@ load_user_config(void) { /* Don't continue adding to this list. When 2.0 rolls around bump the 1.4 (unnumbered) files off the list. */ - static const gchar *user_config_files[] = - { - "config-2.0.user", "config-1.8.user", "config-1.6.user", - "config.user", NULL - }; static const gchar *saved_report_files[] = { SAVED_REPORTS_FILE, SAVED_REPORTS_FILE_OLD_REV, NULL @@ -404,7 +398,14 @@ load_user_config(void) else is_user_config_loaded = TRUE; update_message("loading user configuration"); - try_load_config_array(user_config_files); + { + gchar *config_filename; + config_filename = g_build_filename (gnc_userconfig_dir (), + "config-user.scm", (char *)NULL); + gfec_try_load(config_filename); + g_free(config_filename); + } + update_message("loading saved reports"); try_load_config_array(saved_report_files); update_message("loading stylesheets"); @@ -647,7 +648,7 @@ inner_main (void *closure, int argc, char **argv) gnc_ui_new_user_dialog(); } - if (userdata_migrated) + if (userdata_migration_msg) { GtkWidget *dialog = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_INFO, @@ -657,6 +658,7 @@ inner_main (void *closure, int argc, char **argv) gnc_destroy_splash_screen(); gtk_dialog_run(GTK_DIALOG(dialog)); gtk_widget_destroy (dialog); + g_free (userdata_migration_msg); } /* Ensure temporary preferences are temporary */ gnc_prefs_reset_group (GNC_PREFS_GROUP_WARNINGS_TEMP); @@ -703,7 +705,8 @@ gnc_log_init() { gchar *log_config_filename; - log_config_filename = gnc_build_userdata_path("log.conf"); + log_config_filename = g_build_filename (gnc_userconfig_dir (), + "log.conf", (char *)NULL); if (g_file_test(log_config_filename, G_FILE_TEST_EXISTS)) qof_log_parse_log_config(log_config_filename); g_free(log_config_filename); @@ -784,15 +787,8 @@ main(int argc, char ** argv) /* Make sure gnucash' user data directory is properly set up This must be done before any guile code is called as that would fail the migration message */ - userdata_migrated = gnc_filepath_init(); - /* Translators: the message below will be completed with two directory names. */ - userdata_migration_msg = g_strdup_printf ( - _("Notice\n\nYour gnucash metadata has been migrated.\n\n" - "Old location: %s%s\n" - "New location: %s\n\n" - "If you no longer intend to run " PACKAGE_NAME " 2.6.x or older on this system you can safely remove the old directory."), - g_get_home_dir(), G_DIR_SEPARATOR_S ".gnucash", gnc_userdata_dir()); - if (userdata_migrated) + userdata_migration_msg = gnc_filepath_init(); + if (userdata_migration_msg) g_print("\n\n%s\n", userdata_migration_msg); gnc_log_init(); diff --git a/libgnucash/core-utils/gnc-filepath-utils.cpp b/libgnucash/core-utils/gnc-filepath-utils.cpp index 3e956eaf1f..ecf4db9b26 100644 --- a/libgnucash/core-utils/gnc-filepath-utils.cpp +++ b/libgnucash/core-utils/gnc-filepath-utils.cpp @@ -65,6 +65,10 @@ extern "C" { } #include +#include +#include + + #if PLATFORM(WINDOWS) #include @@ -73,6 +77,8 @@ extern "C" { namespace bfs = boost::filesystem; namespace bst = boost::system; +namespace bl = boost::locale; +using namespace boost::locale; /** * Scrubs a filename by changing "strange" chars (e.g. those that are not @@ -319,6 +325,7 @@ gnc_path_find_localized_html_file (const gchar *file_name) /* ====================================================================== */ static auto gnc_userdata_home = bfs::path(); +static auto gnc_userconfig_home = bfs::path(); static auto build_dir = bfs::path(); static bool dir_is_descendant (const bfs::path& path, const bfs::path& base) @@ -580,6 +587,141 @@ get_userconfig_home(void) return userconfig_home; } +static std::string migrate_gnc_datahome() +{ + auto success = false; + // Specify location of dictionaries + auto old_dir = bfs::path(g_get_home_dir()) / ".gnucash"; + + bl::generator gen; + gen.add_messages_path(gnc_path_get_datadir()); + gen.add_messages_domain(PACKAGE); + +// std::locale::global(gen("")); + auto migration_msg = std::stringstream (); + migration_msg.imbue(gen("")); + + if (copy_recursive (old_dir, + gnc_userdata_home)) + { + /* Step 1: copy directory $HOME/.gnucash to $GNC_DATA_HOME */ + + /* Translators: the message below will be completed with two directory names. */ + migration_msg + << translate ("Notice") << std::endl << std::endl + << translate ("Your gnucash metadata has been migrated.") << std::endl << std::endl + << translate ("Old location:") << " " << old_dir.string() << std::endl + << translate ("New location:") << " " << gnc_userdata_home.string() << std::endl << std::endl + // Translators {1} will be replaced with the package name (typically Gnucash) at runtime + << bl::format (translate ("If you no longer intend to run {1} 2.6.x or older on this system you can safely remove the old directory.")) + % PACKAGE_NAME; + + /* Step 2: move user editable config files from $GNC_DATA_HOME to GNC_CONFIG_HOME + These files are: + - log.conf + - the most recent of "config-2.0.user", "config-1.8.user", "config-1.6.user", + "config.user" + Note: we'll also rename config.user to config-user.scm to make it more clear what + this file is expecting custom scm code to load at run time + + This is only done if not Windows or OS X, because on those platforms + gnc_userconfig_home and gnc_userdata_home are the same.*/ +#if !defined G_OS_WIN32 && !defined MAC_INTEGRATION + auto failed = std::vector{}; + auto succeeded = std::vector{}; + + auto oldlogpath = gnc_userdata_home / "log.conf"; + auto newlogpath = gnc_userconfig_home / "log.conf"; + try + { + if (bfs::exists (oldlogpath) && gnc_validate_directory (gnc_userconfig_home)) + { + bfs::rename (oldlogpath, newlogpath); + succeeded.emplace_back ("log.conf"); + } + } + catch (const bfs::filesystem_error& ex) + { + failed.emplace_back ("log.conf"); + } + + auto user_config_files = std::vector + { + "config.user", "config-1.6.user", + "config-1.8.user", "config-2.0.user" + }; + auto newconfpath = gnc_userconfig_home / "config-user.scm"; + auto conf_exist_vec = std::vector {}; + auto final_rename = std::string(); + for (auto conf_file : user_config_files) + { + auto oldconfpath = gnc_userdata_home / conf_file; + try + { + if (bfs::exists (oldconfpath) && gnc_validate_directory (gnc_userconfig_home)) + { + bfs::rename (oldconfpath, newconfpath); + /* Translators: this string refers to a file name that gets renamed */ + final_rename = conf_file + " (" + _("Renamed to:") + " config-user.scm)"; + conf_exist_vec.emplace_back (conf_file); + } + } + catch (const bfs::filesystem_error& ex) + { + failed.emplace_back (conf_file); + } + } + if (!final_rename.empty()) + succeeded.emplace_back (final_rename); + /* The last element in conf_exist_vec would be the same as final_rename. + * This will be reported in the succeeded vector, so don't + * report it again as removed */ + if (!conf_exist_vec.empty()) + conf_exist_vec.pop_back(); + + /* Step 3: inform the user of additional changes */ + if (!succeeded.empty() || !conf_exist_vec.empty() || !failed.empty()) + migration_msg << std::endl << std::endl + << translate ("In addition:"); + + if (!succeeded.empty()) + { + migration_msg << std::endl << std::endl + << bl::format (translate ("The following file has been moved to {1} instead:", + "The following files have been moved to {1} instead:", + succeeded.size())) % gnc_userconfig_home.string().c_str() + << std::endl; + for (auto success_file : succeeded) + migration_msg << "- " << success_file << std::endl; + } + if (!conf_exist_vec.empty()) + { + migration_msg << std::endl << std::endl + << translate ("The following file has been removed instead:", + "The following files have been removed instead:", + conf_exist_vec.size()) + << std::endl; + for (auto rem_file : conf_exist_vec) + migration_msg << "- " << rem_file << std::endl; + } + if (!failed.empty()) + { + migration_msg << std::endl << std::endl + << bl::format (translate ("The following file could not be moved to {1}:", + "The following files could not be moved to {1}:", + failed.size())) % gnc_userconfig_home.string().c_str() + << std::endl; + for (auto failed_file : failed) + migration_msg << "- " << failed_file << std::endl; + } +#endif + + } + + return migration_msg.str (); +} + + #if defined G_OS_WIN32 ||defined MAC_INTEGRATION constexpr auto path_package = PACKAGE_NAME; #else @@ -587,9 +729,14 @@ constexpr auto path_package = PACKAGE; #endif -gboolean +char * gnc_filepath_init (void) { + // Initialize the user's config directory for gnucash + gnc_userconfig_home = get_userconfig_home() / path_package; + + + // Initialize the user's data directory for gnucash auto gnc_userdata_home_exists = false; auto have_valid_userdata_home = false; @@ -660,10 +807,11 @@ gnc_filepath_init (void) } } - auto migrated = FALSE; + /* Run migration code before creating the default directories + If migrating, these default directories are copied instead of created. */ + auto migration_notice = std::string (); if (!gnc_userdata_home_exists) - migrated = copy_recursive (bfs::path (g_get_home_dir()) / ".gnucash", - gnc_userdata_home); + migration_notice = migrate_gnc_datahome(); /* Try to create the standard subdirectories for gnucash' user data */ try @@ -678,7 +826,7 @@ gnc_filepath_init (void) "(Error: %s)", ex.what()); } - return migrated; + return migration_notice.empty() ? NULL : g_strdup (migration_notice.c_str()); } /** @fn const gchar * gnc_userdata_dir () @@ -744,8 +892,10 @@ gnc_userdata_dir (void) const gchar * gnc_userconfig_dir (void) { - auto config_path = get_userconfig_home() / path_package; - return g_strdup(config_path.string().c_str()); + if (gnc_userdata_home.empty()) + gnc_filepath_init(); + + return gnc_userconfig_home.string().c_str(); } static const bfs::path& diff --git a/libgnucash/core-utils/gnc-filepath-utils.h b/libgnucash/core-utils/gnc-filepath-utils.h index f327446bcf..3e04efc954 100644 --- a/libgnucash/core-utils/gnc-filepath-utils.h +++ b/libgnucash/core-utils/gnc-filepath-utils.h @@ -91,9 +91,9 @@ gchar *gnc_path_find_localized_html_file (const gchar *file_name); * function will also try to copy files from $HOME/.gnucash * to there if that old location exists. * - * @return whether files got copied from the old location. + * @return a migration message when files got copied from the old location, NULL otherwise */ -gboolean gnc_filepath_init (void); +char * gnc_filepath_init (void); const gchar *gnc_userdata_dir (void); gchar *gnc_build_userdata_path (const gchar *filename); diff --git a/po/CMakeLists.txt b/po/CMakeLists.txt index aaa69f807d..0f7ed912b2 100644 --- a/po/CMakeLists.txt +++ b/po/CMakeLists.txt @@ -155,6 +155,8 @@ IF(BUILDING_FROM_VCS) find_program(INTLTOOL_EXTRACT NAMES intltool-extract) find_program(XGETTEXT xgettext) + configure_file (${CMAKE_CURRENT_SOURCE_DIR}/Makevars + ${CMAKE_CURRENT_BINARY_DIR}/Makevars COPYONLY) IF (${INTLTOOL_EXTRACT} STREQUAL "INTLTOOL_EXTRACT-NOTFOUND") MESSAGE(FATAL_ERROR "Can't find the 'intltool-extract' program.") diff --git a/po/Makevars b/po/Makevars index e7a9833eac..13fee66eba 100644 --- a/po/Makevars +++ b/po/Makevars @@ -8,7 +8,13 @@ subdir = po top_builddir = .. # These options get passed to xgettext. -XGETTEXT_OPTIONS = --keyword=_ --keyword=N_ --keyword=Q_ +XGETTEXT_OPTIONS = --keyword=_ --keyword=N_ --keyword=Q_ \ + --keyword=translate:1,1t --keyword=translate:1c,2,2t \ + --keyword=translate:1,2,3t --keyword=translate:1c,2,3,4t +# The two lines below are also for boost::locale. I haven't added them (yet) as +# we should first investigate whether they interfere with plain C gettext keywords +# --keyword=gettext:1 --keyword=pgettext:1c,2 \ +# --keyword=ngettext:1,2 --keyword=npgettext:1c,2,3 \ # This is the copyright holder that gets inserted into the header of the # $(DOMAIN).pot file. Set this to the copyright holder of the surrounding diff --git a/po/gnucash-pot.cmake b/po/gnucash-pot.cmake index 25d80929f3..8ac951e68c 100644 --- a/po/gnucash-pot.cmake +++ b/po/gnucash-pot.cmake @@ -7,7 +7,7 @@ set(ENV{INTLTOOL_EXTRACT} ${INTLTOOL_EXTRACT}) set(ENV{XGETTEXT} ${XGETTEXT}) set(ENV{srcdir} ${PO_SRC_DIR}) execute_process( - COMMAND ${PERL} ${INTLTOOL_UPDATE} --gettext-package ${PACKAGE} --pot + COMMAND ${PERL} ${INTLTOOL_UPDATE} -x --gettext-package ${PACKAGE} --pot WORKING_DIRECTORY ${PO_BIN_DIR} RESULT_VARIABLE GNUCASH_POT_RESULT )