diff --git a/src/Gui/CMakeLists.txt b/src/Gui/CMakeLists.txt index 447056d4c5..6f0bfbbe0c 100644 --- a/src/Gui/CMakeLists.txt +++ b/src/Gui/CMakeLists.txt @@ -448,6 +448,7 @@ SET(Gui_UIC_SRCS Dialogs/DlgPropertyLink.ui Dialogs/DlgRevertToBackupConfig.ui Dialogs/DlgThemeEditor.ui + Dialogs/DlgVersionMigrator.ui PreferencePages/DlgSettings3DView.ui PreferencePages/DlgSettingsCacheDirectory.ui Dialogs/DlgSettingsColorGradient.ui @@ -564,6 +565,7 @@ SET(Dialog_CPP_SRCS Dialogs/DlgRevertToBackupConfigImp.cpp Dialogs/DlgExpressionInput.cpp Dialogs/DlgThemeEditor.cpp + Dialogs/DlgVersionMigrator.cpp TaskDlgRelocation.cpp Dialogs/DlgCheckableMessageBox.cpp TaskTransform.cpp @@ -606,6 +608,7 @@ SET(Dialog_HPP_SRCS Dialogs/DlgCheckableMessageBox.h Dialogs/DlgExpressionInput.h Dialogs/DlgThemeEditor.h + Dialogs/DlgVersionMigrator.h TaskDlgRelocation.h TaskTransform.h Dialogs/DlgUndoRedo.h @@ -652,6 +655,7 @@ SET(Dialog_SRCS Dialogs/DlgTreeWidget.ui Dialogs/DlgExpressionInput.ui Dialogs/DlgCreateNewPreferencePack.ui + Dialogs/DlgVersionMigrator.ui DownloadManager.ui DownloadItem.ui DocumentRecovery.ui @@ -1386,7 +1390,6 @@ SET(FreeCADGui_CPP_SRCS StartupProcess.cpp TransactionObject.cpp ToolHandler.cpp - VersionMigrator.cpp StyleParameters/Parser.cpp StyleParameters/ParameterManager.cpp ) @@ -1430,7 +1433,6 @@ SET(FreeCADGui_SRCS StartupProcess.h TransactionObject.h ToolHandler.h - VersionMigrator.h StyleParameters/Parser.h StyleParameters/ParameterManager.h ) diff --git a/src/Gui/VersionMigrator.cpp b/src/Gui/Dialogs/DlgVersionMigrator.cpp similarity index 69% rename from src/Gui/VersionMigrator.cpp rename to src/Gui/Dialogs/DlgVersionMigrator.cpp index 7a9be8b3aa..3ec92021ec 100644 --- a/src/Gui/VersionMigrator.cpp +++ b/src/Gui/Dialogs/DlgVersionMigrator.cpp @@ -23,8 +23,7 @@ #include "PreCompiled.h" #ifndef _PreComp_ -#include -#include +#include #include #include #include @@ -33,32 +32,26 @@ #include #include +#include #include #include #include #endif -#include "VersionMigrator.h" +#include "DlgVersionMigrator.h" +#include "ui_DlgVersionMigrator.h" -#include "MainWindow.h" +#include "../MainWindow.h" #include #include +#include "QTextBrowser" -using namespace Gui; + +using namespace Gui::Dialog; namespace fs = std::filesystem; -uintmax_t calculateDirectorySize(const fs::path &dir) { - uintmax_t size = 0; - for (auto &entry: fs::recursive_directory_iterator(dir)) { - if (fs::is_regular_file(entry.status())) { - size += fs::file_size(entry.path()); - } - } - return size; -} - std::set getKnownVersions() { auto splitCommas = [](const std::string &input) { std::set result; @@ -110,46 +103,63 @@ void markCurrentVersionAsKnown() { setKnownVersions(knownVersions); } -VersionMigrator::VersionMigrator(MainWindow *mw) : QObject(mw), mainWindow(mw) { -} -void VersionMigrator::execute() { - // If the user is running a custom directory set, there is no migration to versioned directories - if (App::Application::directories()->usingCustomDirectories()) { - return; - } +DlgVersionMigrator::DlgVersionMigrator(MainWindow *mw) : + QDialog(mw) + , mainWindow(mw) + , sizeCalculationWorkerThread(nullptr) + , ui(std::make_unique()) +{ + ui->setupUi(this); 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 = getKnownVersions(); + int major = std::stoi(App::Application::Config()["BuildVersionMajor"]); + int minor = std::stoi(App::Application::Config()["BuildVersionMinor"]); + auto programName = QString::fromStdString(App::Application::getExecutableName()); + + // 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 calculatingSizeString = QStringLiteral("") + QObject::tr("Calculating size…") + QStringLiteral(""); + + auto shareConfigurationString = QStringLiteral("") + + QObject::tr("Share configuration between versions") + QStringLiteral(""); + + 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); +} + +DlgVersionMigrator::~DlgVersionMigrator() = default; + +int DlgVersionMigrator::exec() { + // If the user is running a custom directory set, there is no migration to versioned directories + 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())) { - auto programName = QString::fromStdString(App::Application::getExecutableName()); - auto result = QMessageBox::question( - mainWindow, - QObject::tr("Welcome to %1 v%2.%3").arg(programName, QString::number(major), QString::number(minor)), - QObject::tr("Welcome to %1 v%2.%3\n\n").arg(programName, QString::number(major), QString::number(minor)) - + QObject::tr("Configuration data and addons from previous program version found. " - "Migrate the configuration to a new directory for this version? Answering 'No' will " - "continue to use the old directory. 'Yes' will copy it."), - QMessageBox::Yes | QMessageBox::No); - if (result == QMessageBox::Yes) { - confirmMigration(); - } else { - // Don't ask again for this version - markCurrentVersionAsKnown(); + if (!knownVersions.contains(currentVersionedDirName) && !App::Application::directories()->usingCurrentVersionConfig( + App::Application::directories()->getUserAppDataDir())) { + calculateMigrationSize(); + auto result = QDialog::exec(); + if (sizeCalculationWorkerThread && sizeCalculationWorkerThread->isRunning()) { + sizeCalculationWorkerThread->quit(); } } + return 0; } class DirectorySizeCalculationWorker : public QObject { @@ -216,58 +226,41 @@ Q_SIGNALS: void failed(); }; -void VersionMigrator::confirmMigration() { - auto *workerThread = new QThread(mainWindow); +void DlgVersionMigrator::calculateMigrationSize() { + sizeCalculationWorkerThread = new QThread(mainWindow); auto *worker = new DirectorySizeCalculationWorker(); - worker->moveToThread(workerThread); - connect(workerThread, &QThread::started, worker, &DirectorySizeCalculationWorker::run); + worker->moveToThread(sizeCalculationWorkerThread); + connect(sizeCalculationWorkerThread, &QThread::started, worker, &DirectorySizeCalculationWorker::run); - connect(worker, &DirectorySizeCalculationWorker::sizeFound, this, &VersionMigrator::showSizeOfMigration); - connect(worker, &DirectorySizeCalculationWorker::finished, workerThread, &QThread::quit); + connect(worker, &DirectorySizeCalculationWorker::sizeFound, this, &DlgVersionMigrator::showSizeOfMigration); + connect(worker, &DirectorySizeCalculationWorker::finished, sizeCalculationWorkerThread, &QThread::quit); connect(worker, &DirectorySizeCalculationWorker::finished, worker, &QObject::deleteLater); - connect(workerThread, &QThread::finished, workerThread, &QObject::deleteLater); + connect(sizeCalculationWorkerThread, &QThread::finished, sizeCalculationWorkerThread, &QObject::deleteLater); - auto calculatingSize = new QMessageBox(mainWindow); - calculatingSize->setWindowTitle(QObject::tr("Calculating size")); - calculatingSize->setText(QObject::tr("Calculating directory size…")); - calculatingSize->setStandardButtons(QMessageBox::Cancel); - connect(worker, &DirectorySizeCalculationWorker::sizeFound, calculatingSize, &QMessageBox::accept); - connect(calculatingSize, &QMessageBox::rejected, workerThread, &QThread::requestInterruption); - connect(calculatingSize, &QMessageBox::rejected, this, &VersionMigrator::migrationCancelled); - - workerThread->start(); - calculatingSize->exec(); + sizeCalculationWorkerThread->start(); } -void VersionMigrator::migrationCancelled() const { - auto message = QObject::tr( - "Migration cancelled. Ask again on next restart?"); - auto result = QMessageBox::question( - mainWindow, - QObject::tr("Migration cancelled"), - message, - QMessageBox::Yes | QMessageBox::No); - if (result == QMessageBox::No) { - markCurrentVersionAsKnown(); +void DlgVersionMigrator::doNotMigrate(const QString &linkText) +{ + Q_UNUSED(linkText); + markCurrentVersionAsKnown(); + if (sizeCalculationWorkerThread && sizeCalculationWorkerThread->isRunning()) { + sizeCalculationWorkerThread->quit(); } + close(); } -void VersionMigrator::showSizeOfMigration(uintmax_t size) { +void DlgVersionMigrator::showSizeOfMigration(uintmax_t size) { auto sizeString = QLocale().formattedDataSize(static_cast(size)); - auto result = QMessageBox::question( - mainWindow, - QObject::tr("Migration Size"), - QObject::tr("Migrating will copy %1 into a versioned subdirectory. Continue?").arg(sizeString), - QMessageBox::Yes | QMessageBox::No - ); - if (result == QMessageBox::Yes) { - migrateToCurrentVersion(); - } else { - migrationCancelled(); - } + auto sizeMessage = QStringLiteral("") + + QObject::tr("Estimated size of data to copy: %1").arg(sizeString) + + QStringLiteral(""); + ui->sizeLabel->setText(sizeMessage); + sizeCalculationWorkerThread = nullptr; // Deleted via a previously-configured deleteLater() } -void VersionMigrator::migrateToCurrentVersion() { +void DlgVersionMigrator::migrateToCurrentVersion() { + hide(); auto oldKnownVersions = getKnownVersions(); markCurrentVersionAsKnown(); // This MUST be done before the migration, or it won't get remembered auto *workerThread = new QThread(mainWindow); @@ -278,9 +271,9 @@ void VersionMigrator::migrateToCurrentVersion() { connect(worker, &PathMigrationWorker::finished, worker, &QObject::deleteLater); connect(workerThread, &QThread::finished, workerThread, &QObject::deleteLater); - auto migrationRunning = new QMessageBox(mainWindow); + auto migrationRunning = new QMessageBox(this); migrationRunning->setWindowTitle(QObject::tr("Migrating")); - migrationRunning->setText(QObject::tr("Migrating configuration data and addons...")); + migrationRunning->setText(QObject::tr("Migrating configuration data and addons…")); migrationRunning->setStandardButtons(QMessageBox::NoButton); connect(worker, &PathMigrationWorker::complete, migrationRunning, &QMessageBox::accept); connect(worker, &PathMigrationWorker::failed, migrationRunning, &QMessageBox::reject); @@ -290,16 +283,16 @@ void VersionMigrator::migrateToCurrentVersion() { if (migrationRunning->result() == QDialog::Accepted) { App::GetApplication().GetUserParameter().SaveDocument(); // Flush to disk before restarting - auto *restarting = new QMessageBox(mainWindow); + auto *restarting = new QMessageBox(this); restarting->setText( - QObject::tr("Migration complete. Restarting...")); + 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 + // 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); @@ -323,4 +316,4 @@ void VersionMigrator::migrateToCurrentVersion() { } } -#include "VersionMigrator.moc" +#include "DlgVersionMigrator.moc" diff --git a/src/Gui/VersionMigrator.h b/src/Gui/Dialogs/DlgVersionMigrator.h similarity index 64% rename from src/Gui/VersionMigrator.h rename to src/Gui/Dialogs/DlgVersionMigrator.h index 36d90e560b..1a0bdb1bc3 100644 --- a/src/Gui/VersionMigrator.h +++ b/src/Gui/Dialogs/DlgVersionMigrator.h @@ -21,38 +21,46 @@ * * **************************************************************************/ -#ifndef GUI_VERSIONMIGRATOR_H -#define GUI_VERSIONMIGRATOR_H +#ifndef GUI_DIALOG_VERSIONMIGRATOR_H +#define GUI_DIALOG_VERSIONMIGRATOR_H #include #include -#include +#include +#include + namespace Gui { class MainWindow; + namespace Dialog { -class GuiExport VersionMigrator : public QObject -{ - Q_OBJECT + class GuiExport DlgVersionMigrator final : public QDialog + { + Q_OBJECT -public: - explicit VersionMigrator(MainWindow *mw); - void execute(); + public: + explicit DlgVersionMigrator(MainWindow *mw); + ~DlgVersionMigrator() override; + Q_DISABLE_COPY_MOVE(DlgVersionMigrator) -protected Q_SLOTS: + int exec() override; - void confirmMigration(); - void migrationCancelled() const; - void showSizeOfMigration(uintmax_t size); - void migrateToCurrentVersion(); + protected Q_SLOTS: -private: - MainWindow* mainWindow; -}; + void calculateMigrationSize(); // Async -> this starts the process and immediately returns + void showSizeOfMigration(uintmax_t size); + void migrateToCurrentVersion(); + void doNotMigrate(const QString &linkText); + private: + MainWindow* mainWindow; + QThread* sizeCalculationWorkerThread; + std::unique_ptr ui; + }; + } } -#endif // GUI_VERSIONMIGRATOR_H +#endif // GUI_DIALOG_VERSIONMIGRATOR_H diff --git a/src/Gui/Dialogs/DlgVersionMigrator.ui b/src/Gui/Dialogs/DlgVersionMigrator.ui new file mode 100644 index 0000000000..59196516f2 --- /dev/null +++ b/src/Gui/Dialogs/DlgVersionMigrator.ui @@ -0,0 +1,109 @@ + + + + Gui::Dialog::DlgVersionMigrator + + + Qt::WindowModal + + + + 0 + 0 + 527 + 233 + + + + Dialog + + + true + + + + + + TextLabel + + + Qt::AutoText + + + + + + + Configuration data and addons from a previous program version were found. Migrate the configuration to a new directory for this version? + + + Qt::PlainText + + + true + + + + + + + Copying the configuration will ensure that any changes from the new version will not affect the previous installation. Sharing configuration between versions can cause problems and is not recommended. + + + Qt::PlainText + + + true + + + + + + + TextLabel + + + Qt::AutoText + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + Share configuration between versions + + + Qt::AutoText + + + + + + + Copy Configuration + + + + + + + + + + diff --git a/src/Gui/StartupProcess.cpp b/src/Gui/StartupProcess.cpp index 5d00a0348b..5d9974b0f0 100644 --- a/src/Gui/StartupProcess.cpp +++ b/src/Gui/StartupProcess.cpp @@ -30,8 +30,6 @@ #include #include #include -#include -#include #include #include #include @@ -53,13 +51,12 @@ #include "GuiApplication.h" #include "MainWindow.h" #include "Language/Translator.h" -#include "VersionMigrator.h" +#include "Dialogs/DlgVersionMigrator.h" #include #include using namespace Gui; -namespace fs = std::filesystem; StartupProcess::StartupProcess() = default; @@ -560,6 +557,7 @@ void StartupPostProcess::checkParameters() } void StartupPostProcess::checkVersionMigration() const { - VersionMigrator migrator(mainWindow); - migrator.execute(); + auto migrator = new Dialog::DlgVersionMigrator (mainWindow); + migrator->exec(); + migrator->deleteLater(); }