diff --git a/src/Gui/Dialogs/DlgVersionMigrator.cpp b/src/Gui/Dialogs/DlgVersionMigrator.cpp index 3ec92021ec..e055807269 100644 --- a/src/Gui/Dialogs/DlgVersionMigrator.cpp +++ b/src/Gui/Dialogs/DlgVersionMigrator.cpp @@ -21,22 +21,22 @@ * * **************************************************************************/ -#include "PreCompiled.h" -#ifndef _PreComp_ +#include #include -#include +#include #include #include #include #include #include +#include #include +#include #include #include #include #include -#endif #include "DlgVersionMigrator.h" #include "ui_DlgVersionMigrator.h" @@ -51,56 +51,58 @@ using namespace Gui::Dialog; namespace fs = std::filesystem; - -std::set getKnownVersions() { - auto splitCommas = [](const std::string &input) { - std::set result; - std::stringstream ss(input); - std::string token; - while (std::getline(ss, token, ',')) { - result.insert(token); - } - return result; +bool isCurrentVersionKnown() +{ + std::set paths = { + App::Application::directories()->getUserAppDataDir(), + App::Application::directories()->getUserConfigPath() }; - - auto prefGroup = App::GetApplication().GetParameterGroupByPath( - "User parameter:BaseApp/Preferences/Migration"); - - // Split our comma-separated list of already-migrated-to version directories into a set for easy - // searching - std::string offeredToMigrateToVersionedConfig = - prefGroup->GetASCII("OfferedToMigrateToVersionedConfig", ""); - std::set knownVersions; - if (!offeredToMigrateToVersionedConfig.empty()) { - knownVersions = splitCommas(offeredToMigrateToVersionedConfig); - } - return knownVersions; -} - -void setKnownVersions(const std::set &knownVersions) { - auto joinCommas = [](const std::set &s) { - std::ostringstream oss; - for (auto it = s.begin(); it != s.end(); ++it) { - if (it != s.begin()) { - oss << ','; - } - oss << *it; - } - return oss.str(); - }; - - auto prefGroup = App::GetApplication().GetParameterGroupByPath( - "User parameter:BaseApp/Preferences/Migration"); - prefGroup->SetASCII("OfferedToMigrateToVersionedConfig", joinCommas(knownVersions)); -} - -void markCurrentVersionAsKnown() { int major = std::stoi(App::Application::Config()["BuildVersionMajor"]); int minor = std::stoi(App::Application::Config()["BuildVersionMinor"]); std::string currentVersionedDirName = App::ApplicationDirectories::versionStringForPath(major, minor); - std::set knownVersions = getKnownVersions(); - knownVersions.insert(currentVersionedDirName); - setKnownVersions(knownVersions); + for (auto &path : paths) { + if (App::Application::directories()->usingCurrentVersionConfig(path)) { + return true; + } + fs::path markerPath = path; + if (App::Application::directories()->isVersionedPath(path)) { + markerPath = path.parent_path(); + } + markerPath /= currentVersionedDirName + ".do_not_migrate"; + if (fs::exists(markerPath)) { + return true; + } + } + return false; +} + +void markCurrentVersionAsDoNotMigrate() { + std::set paths = { + App::Application::directories()->getUserAppDataDir(), + App::Application::directories()->getUserConfigPath() + }; + int major = std::stoi(App::Application::Config()["BuildVersionMajor"]); + int minor = std::stoi(App::Application::Config()["BuildVersionMinor"]); + std::string currentVersionedDirName = App::ApplicationDirectories::versionStringForPath(major, minor); + for (auto &path : paths) { + if (App::Application::directories()->usingCurrentVersionConfig(path)) { + // No action to take: the migration is done, so this call doesn't need to do anything + continue; + } + fs::path markerPath = path; + if (App::Application::directories()->isVersionedPath(path)) { + markerPath = path.parent_path(); + } + markerPath /= currentVersionedDirName + ".do_not_migrate"; + std::ofstream markerFile(markerPath); + if (!markerFile.is_open()) { + Base::Console().error("Unable to open marker file %s\n", Base::FileInfo::pathToString(markerPath).c_str()); + continue; + } + markerFile << "Migration to version " << currentVersionedDirName << " was declined. " + << "To request migration again, delete this file."; + markerFile.close(); + } } @@ -123,21 +125,31 @@ DlgVersionMigrator::DlgVersionMigrator(MainWindow *mw) : // NOTE: All rich-text strings are generated programmatically so that translators don't have to deal with the // markup. The two strings in the middle of the dialog are set in the UI file. - auto welcomeString = QStringLiteral("") + QObject::tr("Welcome to %1 %2.%3\n\n").arg( - programName, QString::number(major), QString::number(minor)) + QStringLiteral(""); + auto programNameString = tr("Welcome to %1 %2.%3\n\n").arg( + programName, QString::number(major), QString::number(minor)); + auto welcomeString = QStringLiteral("") + programNameString + QStringLiteral(""); - auto calculatingSizeString = QStringLiteral("") + QObject::tr("Calculating size…") + QStringLiteral(""); + auto calculatingSizeString = QStringLiteral("") + tr("Calculating size…") + QStringLiteral(""); auto shareConfigurationString = QStringLiteral("") + - QObject::tr("Share configuration between versions") + QStringLiteral(""); + tr("Share configuration between versions") + QStringLiteral(""); + setWindowTitle(programNameString); ui->welcomeLabel->setText(welcomeString); ui->sizeLabel->setText(calculatingSizeString); - ui->shareLinkLabel->setText(shareConfigurationString); - ui->shareLinkLabel->setTextInteractionFlags(Qt::TextBrowserInteraction); - connect(ui->copyButton, &QPushButton::clicked, this, &DlgVersionMigrator::migrateToCurrentVersion); - connect(ui->shareLinkLabel, &QLabel::linkActivated, this, &DlgVersionMigrator::doNotMigrate); + connect(ui->copyButton, &QPushButton::clicked, this, &DlgVersionMigrator::migrate); + connect(ui->helpButton, &QPushButton::clicked, this, &DlgVersionMigrator::help); + + // Set up the menu actions for the two hidden options + auto *menu = new QMenu(ui->menuButton); + QAction *share = menu->addAction(tr("Share configuration with previous version")); + QAction *reset = menu->addAction(tr("Use a new default configuration")); + ui->menuButton->setMenu(menu); + ui->menuButton->setPopupMode(QToolButton::InstantPopup); + ui->menuButton->setStyleSheet(QStringLiteral("QToolButton::menu-indicator { image: none; width: 0px; }")); + connect(share, &QAction::triggered, this, &DlgVersionMigrator::share); + connect(reset, &QAction::triggered, this, &DlgVersionMigrator::freshStart); } DlgVersionMigrator::~DlgVersionMigrator() = default; @@ -147,14 +159,9 @@ int DlgVersionMigrator::exec() { if (App::Application::directories()->usingCustomDirectories()) { return 0; } - std::set knownVersions = getKnownVersions(); - int major = std::stoi(App::Application::Config()["BuildVersionMajor"]); - int minor = std::stoi(App::Application::Config()["BuildVersionMinor"]); - std::string currentVersionedDirName = App::ApplicationDirectories::versionStringForPath(major, minor); - if (!knownVersions.contains(currentVersionedDirName) && !App::Application::directories()->usingCurrentVersionConfig( - App::Application::directories()->getUserAppDataDir())) { + if (!isCurrentVersionKnown()) { calculateMigrationSize(); - auto result = QDialog::exec(); + QDialog::exec(); if (sizeCalculationWorkerThread && sizeCalculationWorkerThread->isRunning()) { sizeCalculationWorkerThread->quit(); } @@ -162,6 +169,7 @@ int DlgVersionMigrator::exec() { return 0; } + class DirectorySizeCalculationWorker : public QObject { Q_OBJECT @@ -240,10 +248,9 @@ void DlgVersionMigrator::calculateMigrationSize() { sizeCalculationWorkerThread->start(); } -void DlgVersionMigrator::doNotMigrate(const QString &linkText) +void DlgVersionMigrator::share() { - Q_UNUSED(linkText); - markCurrentVersionAsKnown(); + markCurrentVersionAsDoNotMigrate(); if (sizeCalculationWorkerThread && sizeCalculationWorkerThread->isRunning()) { sizeCalculationWorkerThread->quit(); } @@ -259,10 +266,8 @@ void DlgVersionMigrator::showSizeOfMigration(uintmax_t size) { sizeCalculationWorkerThread = nullptr; // Deleted via a previously-configured deleteLater() } -void DlgVersionMigrator::migrateToCurrentVersion() { +void DlgVersionMigrator::migrate() { hide(); - auto oldKnownVersions = getKnownVersions(); - markCurrentVersionAsKnown(); // This MUST be done before the migration, or it won't get remembered auto *workerThread = new QThread(mainWindow); auto *worker = new PathMigrationWorker(); worker->moveToThread(workerThread); @@ -282,38 +287,79 @@ void DlgVersionMigrator::migrateToCurrentVersion() { migrationRunning->exec(); if (migrationRunning->result() == QDialog::Accepted) { - App::GetApplication().GetUserParameter().SaveDocument(); // Flush to disk before restarting - auto *restarting = new QMessageBox(this); - restarting->setText( - QObject::tr("Migration complete. Restarting…")); - restarting->setWindowTitle(QObject::tr("Restarting")); - restarting->setStandardButtons(QMessageBox::NoButton); - auto closeNotice = [restarting]() { - restarting->reject(); - }; - - // Insert a short delay before restart so the user can see the success message and - // knows it's a restart and not a crash... - constexpr int delayRestartMillis{2000}; - QTimer::singleShot(delayRestartMillis, closeNotice); - restarting->exec(); - - connect(qApp, &QCoreApplication::aboutToQuit, [=] { - if (getMainWindow()->close()) { - auto args = QApplication::arguments(); - args.removeFirst(); - QProcess::startDetached(QApplication::applicationFilePath(), - args, - QApplication::applicationDirPath()); - } - }); - QCoreApplication::exit(0); - _exit(0); // No really. Die. + restart(); } else { - setKnownVersions(oldKnownVersions); // Reset, we didn't migrate after all QMessageBox::critical(mainWindow, QObject::tr("Migration failed"), QObject::tr("Migration failed. See the Report View for details.")); } } + + +void DlgVersionMigrator::freshStart() +{ + // Create the versioned directories, but don't put anything in them + std::set paths = { + App::Application::directories()->getUserAppDataDir(), + App::Application::directories()->getUserConfigPath() + }; + int major = std::stoi(App::Application::Config()["BuildVersionMajor"]); + int minor = std::stoi(App::Application::Config()["BuildVersionMinor"]); + std::string currentVersionedDirName = App::ApplicationDirectories::versionStringForPath(major, minor); + for (auto &path : paths) { + if (App::Application::directories()->usingCurrentVersionConfig(path)) { + continue; + } + fs::path versionDir = path; + if (App::Application::directories()->isVersionedPath(path)) { + versionDir = path.parent_path(); + } + versionDir /= currentVersionedDirName; + if (fs::exists(versionDir)) { + continue; + } + fs::create_directory(versionDir); + } + restart(); +} + +void DlgVersionMigrator::help() +{ + auto helpPage = QStringLiteral("https://wiki.freecad.org/Version_migration"); + QDesktopServices::openUrl(QUrl(helpPage)); +} + +void DlgVersionMigrator::restart() +{ + App::GetApplication().GetUserParameter().SaveDocument(); // Flush to disk before restarting + auto *restarting = new QMessageBox(this); + restarting->setText( + QObject::tr("Migration complete. Restarting…")); + restarting->setWindowTitle(QObject::tr("Restarting")); + restarting->setStandardButtons(QMessageBox::NoButton); + auto closeNotice = [restarting]() { + restarting->reject(); + }; + + // Insert a short delay before restart so the user can see the success message and + // knows it's a restart and not a crash... + constexpr int delayRestartMillis{2000}; + QTimer::singleShot(delayRestartMillis, closeNotice); + restarting->exec(); + + connect(qApp, &QCoreApplication::aboutToQuit, [=] { + if (getMainWindow()->close()) { + auto args = QApplication::arguments(); + args.removeFirst(); + QProcess::startDetached(QApplication::applicationFilePath(), + args, + QApplication::applicationDirPath()); + } + }); + QCoreApplication::exit(0); + _exit(0); // No really. Die. +} + + + #include "DlgVersionMigrator.moc" diff --git a/src/Gui/Dialogs/DlgVersionMigrator.h b/src/Gui/Dialogs/DlgVersionMigrator.h index 1a0bdb1bc3..4de31692e5 100644 --- a/src/Gui/Dialogs/DlgVersionMigrator.h +++ b/src/Gui/Dialogs/DlgVersionMigrator.h @@ -51,13 +51,17 @@ namespace Gui { void calculateMigrationSize(); // Async -> this starts the process and immediately returns void showSizeOfMigration(uintmax_t size); - void migrateToCurrentVersion(); - void doNotMigrate(const QString &linkText); + void migrate(); + void share(); + void freshStart(); + void help(); private: MainWindow* mainWindow; QThread* sizeCalculationWorkerThread; std::unique_ptr ui; + + void restart(); }; } diff --git a/src/Gui/Dialogs/DlgVersionMigrator.ui b/src/Gui/Dialogs/DlgVersionMigrator.ui index 59196516f2..3b0006c614 100644 --- a/src/Gui/Dialogs/DlgVersionMigrator.ui +++ b/src/Gui/Dialogs/DlgVersionMigrator.ui @@ -1,6 +1,5 @@ - Gui::Dialog::DlgVersionMigrator @@ -10,7 +9,7 @@ 0 0 - 527 + 556 233 @@ -69,6 +68,13 @@ + + + + Help + + + @@ -83,20 +89,25 @@ - + - - Share configuration between versions + Copy Configuration (Recommended) - - Qt::AutoText + + true + + + false - + - Copy Configuration + + + + Qt::ToolButtonTextOnly