diff options
Diffstat (limited to 'src/libs/installer/packagemanagergui.cpp')
-rw-r--r-- | src/libs/installer/packagemanagergui.cpp | 421 |
1 files changed, 271 insertions, 150 deletions
diff --git a/src/libs/installer/packagemanagergui.cpp b/src/libs/installer/packagemanagergui.cpp index 67a2123af..1f0462eea 100644 --- a/src/libs/installer/packagemanagergui.cpp +++ b/src/libs/installer/packagemanagergui.cpp @@ -1,6 +1,6 @@ /************************************************************************** ** -** Copyright (C) 2021 The Qt Company Ltd. +** Copyright (C) 2024 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. @@ -54,6 +54,7 @@ #include <QtCore/QTimer> #include <QAbstractItemView> +#include <QAction> #include <QCheckBox> #include <QComboBox> #include <QDesktopServices> @@ -64,6 +65,8 @@ #include <QLineEdit> #include <QListWidget> #include <QListWidgetItem> +#include <QMenuBar> +#include <QMenu> #include <QMessageBox> #include <QProgressBar> #include <QPushButton> @@ -71,18 +74,21 @@ #include <QSplitter> #include <QStringListModel> #include <QTextBrowser> +#include <QFontDatabase> #include <QVBoxLayout> #include <QShowEvent> #include <QFileDialog> #include <QGroupBox> -#include <QDesktopWidget> +#include <QScreen> #ifdef Q_OS_WIN # include <qt_windows.h> +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) # include <QWinTaskbarButton> # include <QWinTaskbarProgress> #endif +#endif using namespace KDUpdater; using namespace QInstaller; @@ -125,7 +131,7 @@ public: return m_widget; } - bool isComplete() const Q_DECL_OVERRIDE + bool isComplete() const override { return m_widget->property("complete").toBool(); } @@ -206,8 +212,9 @@ Q_DECLARE_METATYPE(DynamicInstallerPage*) class PackageManagerGui::Private { public: - Private() - : m_currentId(-1) + Private(PackageManagerGui *qq) + : q(qq) + , m_currentId(-1) , m_modified(false) , m_autoSwitchPage(true) , m_showSettingsButton(false) @@ -231,6 +238,20 @@ public: QLatin1String("unknown button")); } + void showSettingsButton(bool show) + { + if (m_showSettingsButton == show) + return; + q->setOption(QWizard::HaveCustomButton1, show); + q->setButtonText(QWizard::CustomButton1, tr("&Settings")); + q->button(QWizard::CustomButton1)->setToolTip( + PackageManagerGui::tr("Specify proxy settings and configure repositories for add-on components.")); + + q->updateButtonLayout(); + m_showSettingsButton = show; + } + + PackageManagerGui *q; int m_currentId; bool m_modified; bool m_autoSwitchPage; @@ -279,6 +300,11 @@ public: */ /*! + \fn void QInstaller::PackageManagerGui::aboutApplicationClicked() + \sa {gui::aboutApplicationClicked}{gui.aboutApplicationClicked} +*/ + +/*! \fn void QInstaller::PackageManagerGui::packageManagerCore() const Returns the package manager core. @@ -290,7 +316,7 @@ public: */ PackageManagerGui::PackageManagerGui(PackageManagerCore *core, QWidget *parent) : QWizard(parent) - , d(new Private) + , d(new Private(this)) , m_core(core) { if (m_core->isInstaller()) @@ -299,7 +325,17 @@ PackageManagerGui::PackageManagerGui(PackageManagerCore *core, QWidget *parent) setWindowTitle(tr("Maintain %1").arg(m_core->value(scTitle))); setWindowFlags(windowFlags() &~ Qt::WindowContextHelpButtonHint); -#ifndef Q_OS_MACOS +#ifdef Q_OS_MACOS + QMenuBar *menuBar = new QMenuBar(this); + QMenu *applicationMenu = new QMenu(menuBar); + menuBar->addMenu(applicationMenu); + + QAction *aboutAction = new QAction(applicationMenu); + aboutAction->setMenuRole(QAction::AboutRole); + applicationMenu->addAction(aboutAction); + + connect(aboutAction, &QAction::triggered, this, &PackageManagerGui::aboutApplicationClicked); +#else setWindowIcon(QIcon(m_core->settings().installerWindowIcon())); #endif if (!m_core->settings().wizardShowPageList()) { @@ -334,6 +370,11 @@ PackageManagerGui::PackageManagerGui(PackageManagerCore *core, QWidget *parent) setOption(QWizard::NoBackButtonOnStartPage); setOption(QWizard::NoBackButtonOnLastPage); +#ifdef Q_OS_MACOS + setOptions(options() & ~QWizard::NoDefaultButton); + if (QPushButton *nextButton = qobject_cast<QPushButton *>(button(QWizard::NextButton))) + nextButton->setFocusPolicy(Qt::StrongFocus); +#endif if (m_core->settings().wizardShowPageList()) { QWidget *sideWidget = new QWidget(this); @@ -349,6 +390,7 @@ PackageManagerGui::PackageManagerGui(PackageManagerCore *core, QWidget *parent) m_pageListWidget->setFocusPolicy(Qt::NoFocus); m_pageListWidget->setSelectionMode(QAbstractItemView::NoSelection); m_pageListWidget->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); + m_pageListWidget->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); QVBoxLayout *sideWidgetLayout = new QVBoxLayout(sideWidget); @@ -370,10 +412,13 @@ PackageManagerGui::PackageManagerGui(PackageManagerCore *core, QWidget *parent) connect(this, &QDialog::rejected, m_core, &PackageManagerCore::setCanceled); connect(this, &PackageManagerGui::interrupted, m_core, &PackageManagerCore::interrupt); - // both queued to show the finished page once everything is done + // all queued to show the finished page once everything is done connect(m_core, &PackageManagerCore::installationFinished, this, &PackageManagerGui::showFinishedPage, Qt::QueuedConnection); + connect(m_core, &PackageManagerCore::offlineGenerationFinished, + this, &PackageManagerGui::showFinishedPage, + Qt::QueuedConnection); connect(m_core, &PackageManagerCore::uninstallationFinished, this, &PackageManagerGui::showFinishedPage, Qt::QueuedConnection); @@ -426,7 +471,7 @@ PackageManagerGui::PackageManagerGui(PackageManagerCore *core, QWidget *parent) */ void PackageManagerGui::setMaxSize() { - QSize size = qApp->desktop()->availableGeometry(this).size(); + QSize size = this->screen()->availableGeometry().size(); int windowFrameHeight = frameGeometry().height() - geometry().height(); int availableHeight = size.height() - windowFrameHeight; @@ -469,7 +514,7 @@ void PackageManagerGui::updatePageListWidget() itemText.replace(regExp2, QLatin1String("\\1 \\2")); } QListWidgetItem *item = new QListWidgetItem(itemText, m_pageListWidget); - item->setSizeHint(QSize(item->sizeHint().width(), 30)); + item->setSizeHint(QSize(m_pageListWidget->width(), 30)); // Give visual indication about current & non-visited pages if (id == d->m_currentId) { @@ -478,7 +523,7 @@ void PackageManagerGui::updatePageListWidget() item->setFont(currentItemFont); // Current item should be always visible on the list m_pageListWidget->scrollToItem(item); - } else if (id > d->m_currentId) { + } else { item->setFlags(item->flags() & ~Qt::ItemIsEnabled); } } @@ -647,6 +692,22 @@ bool PackageManagerGui::isButtonEnabled(int wb) } /*! + Sets \a buttonText for button specified by \a buttonId to a installer page \a pageId. + + \note In some pages, installer will change the button text when entering + the page. In that case, you need to connect to \c entered() -signal of the + page to change the \a buttonText. + + \sa {gui::setWizardPageButtonText}{gui.setWizardPageButtonText} +*/ +void PackageManagerGui::setWizardPageButtonText(int pageId, int buttonId, const QString &buttonText) +{ + PackageManagerPage *const p = qobject_cast<PackageManagerPage*> (page(pageId)); + if (p) + p->setButtonText(static_cast<QWizard::WizardButton>(buttonId), buttonText); +} + +/*! Sets a validator for the custom page specified by \a name and \a callbackName requested by \a component. */ @@ -1009,16 +1070,18 @@ void PackageManagerGui::showFinishedPage() */ void PackageManagerGui::showSettingsButton(bool show) { - if (d->m_showSettingsButton == show) - return; - - d->m_showSettingsButton = show; - setOption(QWizard::HaveCustomButton1, show); - setButtonText(QWizard::CustomButton1, tr("Settings")); - button(QWizard::CustomButton1)->setToolTip( - PackageManagerGui::tr("Specify proxy settings and configure repositories for add-on components.")); + m_core->setValue(QLatin1String("ShowSettingsButton"), QString::number(show)); + d->showSettingsButton(show); +} - updateButtonLayout(); +/*! + Shows the \uicontrol Settings button if \a request is \c true. If script has + set the settings button visibility, this function has no effect. +*/ +void PackageManagerGui::requestSettingsButtonByInstaller(bool request) +{ + if (m_core->value(QLatin1String("ShowSettingsButton")).isEmpty()) + d->showSettingsButton(request); } /*! @@ -1201,10 +1264,10 @@ void PackageManagerGui::currentPageChanged(int newId) PackageManagerPage::PackageManagerPage(PackageManagerCore *core) : m_complete(true) , m_titleColor(QString()) + , m_showOnPageList(true) , m_needsSettingsButton(false) , m_core(core) , validatorComponent(nullptr) - , m_showOnPageList(true) { if (!m_core->settings().titleColor().isEmpty()) m_titleColor = m_core->settings().titleColor(); @@ -1268,7 +1331,7 @@ QString PackageManagerPage::productName() const */ void PackageManagerPage::setColoredTitle(const QString &title) { - setTitle(QString::fromLatin1("<font color=\"%1\">%2</font>").arg(m_titleColor, title)); + setTitle(QString::fromLatin1("<center><font color=\"%1\">%2</font></center>").arg(m_titleColor, title)); } /*! @@ -1276,7 +1339,7 @@ void PackageManagerPage::setColoredTitle(const QString &title) */ void PackageManagerPage::setColoredSubTitle(const QString &subTitle) { - setSubTitle(QString::fromLatin1("<font color=\"%1\">%2</font>").arg(m_titleColor, subTitle)); + setSubTitle(QString::fromLatin1("<center><font color=\"%1\">%2</font></center>").arg(m_titleColor, subTitle)); } /*! @@ -1333,12 +1396,8 @@ void PackageManagerPage::setComplete(bool complete) { m_complete = complete; if (QWizard *w = wizard()) { - if (QAbstractButton *cancel = w->button(QWizard::CancelButton)) { - if (cancel->hasFocus()) { - if (QAbstractButton *next = w->button(QWizard::NextButton)) - next->setFocus(); - } - } + if (QAbstractButton *nextButton = w->button(QWizard::NextButton)) + nextButton->setFocus(); } emit completeChanged(); } @@ -1412,20 +1471,12 @@ int PackageManagerPage::nextId() const if (next == PackageManagerCore::LicenseCheck) { // calculate the page after the license page const int nextNextId = gui()->pageIds().value(gui()->pageIds().indexOf(next) + 1, -1); - const PackageManagerCore *const core = packageManagerCore(); + PackageManagerCore *const core = packageManagerCore(); if (core->isUninstaller()) return nextNextId; // forcibly hide the license page if we run as uninstaller - - core->calculateComponentsToInstall(); - foreach (Component* component, core->orderedComponentsToInstall()) { - if (core->isMaintainer() && component->isInstalled()) - continue; // package manager or updater, hide as long as the component is installed - - // The component is about to be installed and provides a license, so the page needs to - // be shown. - if (!component->licenses().isEmpty()) - return next; - } + core->recalculateAllComponents(); + if (core->hasLicenses()) + return next; return nextNextId; // no component with a license or all components with license installed } return next; // default, show the next page @@ -1453,6 +1504,8 @@ IntroductionPage::IntroductionPage(PackageManagerCore *core) : PackageManagerPage(core) , m_updatesFetched(false) , m_allPackagesFetched(false) + , m_forceUpdate(false) + , m_offlineMaintenanceTool(false) , m_label(nullptr) , m_msgLabel(nullptr) , m_errorLabel(nullptr) @@ -1462,7 +1515,7 @@ IntroductionPage::IntroductionPage(PackageManagerCore *core) , m_removeAllComponents(nullptr) { setObjectName(QLatin1String("IntroductionPage")); - setColoredTitle(tr("Setup - %1").arg(productName())); + setColoredTitle(tr("Welcome")); QVBoxLayout *layout = new QVBoxLayout(this); setLayout(layout); @@ -1470,7 +1523,7 @@ IntroductionPage::IntroductionPage(PackageManagerCore *core) m_msgLabel = new QLabel(this); m_msgLabel->setWordWrap(true); m_msgLabel->setObjectName(QLatin1String("MessageLabel")); - m_msgLabel->setText(tr("Welcome to the %1 Setup Wizard.").arg(productName())); + m_msgLabel->setText(tr("Welcome to the %1 Setup.").arg(productName())); QWidget *widget = new QWidget(this); QVBoxLayout *boxLayout = new QVBoxLayout(widget); @@ -1510,6 +1563,8 @@ IntroductionPage::IntroductionPage(PackageManagerCore *core) m_errorLabel = new QLabel(this); m_errorLabel->setWordWrap(true); + m_errorLabel->setTextFormat(Qt::RichText); + m_errorLabel->setTextInteractionFlags(Qt::TextBrowserInteraction); boxLayout->addWidget(m_errorLabel); m_errorLabel->setObjectName(QLatin1String("ErrorLabel")); @@ -1523,9 +1578,10 @@ IntroductionPage::IntroductionPage(PackageManagerCore *core) connect(core, &PackageManagerCore::coreNetworkSettingsChanged, this, &IntroductionPage::onCoreNetworkSettingsChanged); - m_updateComponents->setEnabled(ProductKeyCheck::instance()->hasValidKey()); + m_updateComponents->setEnabled(!m_offlineMaintenanceTool && ProductKeyCheck::instance()->hasValidKey()); #ifdef Q_OS_WIN +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) if (QSysInfo::windowsVersion() >= QSysInfo::WV_WINDOWS7) { m_taskButton = new QWinTaskbarButton(this); connect(core, &PackageManagerCore::metaJobProgress, @@ -1534,6 +1590,7 @@ IntroductionPage::IntroductionPage(PackageManagerCore *core) m_taskButton = nullptr; } #endif +#endif } /*! @@ -1560,9 +1617,11 @@ bool IntroductionPage::validatePage() return true; setComplete(false); + setErrorMessage(QString()); + bool isOfflineOnlyInstaller = core->isInstaller() && core->isOfflineOnly(); // If not offline only installer, at least one valid repository needs to be available - if (!isOfflineOnlyInstaller && !validRepositoriesAvailable()) { + if (!isOfflineOnlyInstaller && !core->validRepositoriesAvailable()) { setErrorMessage(QLatin1String("<font color=\"red\">") + tr("At least one valid and enabled " "repository required for this action to succeed.") + QLatin1String("</font>")); return isComplete(); @@ -1577,6 +1636,7 @@ bool IntroductionPage::validatePage() } #ifdef Q_OS_WIN +#if QT_VERSION < QT_VERSION_CHECK(6, 0 ,0) if (m_taskButton) { if (!m_taskButton->window()) { if (QWidget *widget = QApplication::activeWindow()) @@ -1588,6 +1648,7 @@ bool IntroductionPage::validatePage() m_taskButton->progress()->setVisible(true); } #endif +#endif // fetch updater packages if (core->isUpdater()) { @@ -1607,30 +1668,30 @@ bool IntroductionPage::validatePage() // fetch common packages if (core->isInstaller() || core->isPackageManager()) { - bool localPackagesTreeFetched = false; if (!m_allPackagesFetched) { // first try to fetch the server side packages tree m_allPackagesFetched = core->fetchRemotePackagesTree(); if (!m_allPackagesFetched) { QString error = core->error(); - if (core->isPackageManager() && core->status() != PackageManagerCore::ForceUpdate) { - // if that fails and we're in maintenance mode, try to fetch local installed tree - localPackagesTreeFetched = core->fetchLocalPackagesTree(); - if (localPackagesTreeFetched) { - // if that succeeded, adjust error message - error = QLatin1String("<font color=\"red\">") + error + tr(" Only local package " - "management available.") + QLatin1String("</font>"); - } - } else if (core->status() == PackageManagerCore::ForceUpdate) { + if (core->status() == PackageManagerCore::ForceUpdate) { // replaces the error string from packagemanagercore error = tr("There is an important update available. Please select '%1' first") .arg(m_updateComponents->text().remove(QLatin1Char('&'))); + + m_forceUpdate = true; + // Don't call these directly. Need to finish the current validation first, + // because changing the selection updates the binary marker and resets the + // complete -state of the page. + QMetaObject::invokeMethod(m_updateComponents, "setChecked", + Qt::QueuedConnection, Q_ARG(bool, true)); + QMetaObject::invokeMethod(this, "setErrorMessage", + Qt::QueuedConnection, Q_ARG(QString, error)); } setErrorMessage(error); } } - if (m_allPackagesFetched || localPackagesTreeFetched) + if (m_allPackagesFetched) setComplete(true); } @@ -1643,9 +1704,11 @@ bool IntroductionPage::validatePage() gui()->setSettingsButtonEnabled(true); #ifdef Q_OS_WIN +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) if (m_taskButton) m_taskButton->progress()->setVisible(!isComplete()); #endif +#endif return isComplete(); } @@ -1687,15 +1750,41 @@ void IntroductionPage::showMaintenanceTools() /*! Sets \a enable to \c true to enable the options to install, add, and - uninstall components on the page. + uninstall components on the page. For a maintenance tool without any enabled + repositories, the package manager and updater stay disabled regardless of + the value of \a enable. */ void IntroductionPage::setMaintenanceToolsEnabled(bool enable) { - m_packageManager->setEnabled(enable); - m_updateComponents->setEnabled(enable && ProductKeyCheck::instance()->hasValidKey()); + m_packageManager->setEnabled(enable && !m_offlineMaintenanceTool); + m_updateComponents->setEnabled(enable && !m_offlineMaintenanceTool + && ProductKeyCheck::instance()->hasValidKey()); m_removeAllComponents->setEnabled(enable); } +/*! + Enables or disables the options to add or update components based on the + value of \a enable. For a maintenance tool without any enabled repositories, + the package manager and updater stay disabled regardless of the value of \a enable. +*/ +void IntroductionPage::setMaintainerToolsEnabled(bool enable) +{ + m_packageManager->setEnabled(enable && !m_offlineMaintenanceTool); + m_updateComponents->setEnabled(enable && !m_offlineMaintenanceTool + && ProductKeyCheck::instance()->hasValidKey()); +} + +/*! + Resets the internal page state, so that on clicking \uicontrol Next the metadata needs to be + fetched again. +*/ +void IntroductionPage::resetFetchedState() +{ + m_updatesFetched = false; + m_allPackagesFetched = false; + m_forceUpdate = false; +} + // -- public slots /*! @@ -1740,29 +1829,15 @@ void IntroductionPage::setErrorMessage(const QString &error) m_errorLabel->setPalette(palette); #ifdef Q_OS_WIN +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) if (m_taskButton) { m_taskButton->progress()->stop(); m_taskButton->progress()->setValue(100); } #endif +#endif } -/*! - Returns \c true if at least one valid and enabled repository is available. -*/ -bool IntroductionPage::validRepositoriesAvailable() const -{ - const PackageManagerCore *const core = packageManagerCore(); - bool valid = false; - - foreach (const Repository &repo, core->settings().repositories()) { - if (repo.isEnabled() && repo.isValid()) { - valid = true; - break; - } - } - return valid; -} // -- private slots @@ -1770,7 +1845,7 @@ void IntroductionPage::setUpdater(bool value) { if (value) { entering(); - gui()->showSettingsButton(true); + gui()->requestSettingsButtonByInstaller(true); packageManagerCore()->setUpdater(); emit packageManagerCoreTypeChanged(); @@ -1782,7 +1857,7 @@ void IntroductionPage::setUninstaller(bool value) { if (value) { entering(); - gui()->showSettingsButton(false); + gui()->requestSettingsButtonByInstaller(true); packageManagerCore()->setUninstaller(); emit packageManagerCoreTypeChanged(); @@ -1794,7 +1869,7 @@ void IntroductionPage::setPackageManager(bool value) { if (value) { entering(); - gui()->showSettingsButton(true); + gui()->requestSettingsButtonByInstaller(true); packageManagerCore()->setPackageManager(); emit packageManagerCoreTypeChanged(); @@ -1807,6 +1882,8 @@ void IntroductionPage::setPackageManager(bool value) void IntroductionPage::initializePage() { PackageManagerCore *core = packageManagerCore(); + const bool repositoriesAvailable = core->validRepositoriesAvailable(); + if (core->isPackageManager()) { m_packageManager->setChecked(true); } else if (core->isUpdater()) { @@ -1815,24 +1892,35 @@ void IntroductionPage::initializePage() // If we are running maintenance tool and the default uninstaller // marker is not overridden, set the default checked radio button // based on if we have valid repositories available. - if (!core->isUserSetBinaryMarker() && validRepositoriesAvailable()) { + if (!core->isUserSetBinaryMarker() && repositoriesAvailable) { m_packageManager->setChecked(true); } else { // No repositories available, default to complete uninstallation. m_removeAllComponents->setChecked(true); core->setCompleteUninstallation(true); } + // Disable options that are unusable without repositories + m_offlineMaintenanceTool = !repositoriesAvailable; + setMaintainerToolsEnabled(repositoriesAvailable); } } /*! Resets the internal page state, so that on clicking \uicontrol Next the metadata needs to be - fetched again. + fetched again. For maintenance tool, enables or disables options requiring enabled repositories + based on the current repository settings. */ void IntroductionPage::onCoreNetworkSettingsChanged() { - m_updatesFetched = false; - m_allPackagesFetched = false; + resetFetchedState(); + + PackageManagerCore *core = packageManagerCore(); + if (core->isUninstaller() || core->isMaintainer()) { + m_offlineMaintenanceTool = !core->validRepositoriesAvailable(); + + setMaintainerToolsEnabled(!m_offlineMaintenanceTool); + m_removeAllComponents->setChecked(m_offlineMaintenanceTool); + } } // -- private @@ -1855,7 +1943,10 @@ void IntroductionPage::entering() showMaintenanceTools(); setMaintenanceToolsEnabled(true); } - setSettingsButtonRequested((!core->isOfflineOnly()) && (!core->isUninstaller())); + if (m_forceUpdate) + m_packageManager->setEnabled(false); + + setSettingsButtonRequested((!core->isOfflineOnly())); } /*! @@ -1948,6 +2039,7 @@ LicenseAgreementPage::LicenseAgreementPage(PackageManagerCore *core) m_textBrowser->setReadOnly(true); m_textBrowser->setOpenLinks(false); m_textBrowser->setOpenExternalLinks(true); + m_textBrowser->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont)); m_textBrowser->setObjectName(QLatin1String("LicenseTextBrowser")); connect(m_textBrowser, &QTextBrowser::anchorClicked, this, &LicenseAgreementPage::openLicenseUrl); @@ -1992,7 +2084,6 @@ void LicenseAgreementPage::entering() m_textBrowser->setHtml(QString()); m_licenseListWidget->setVisible(false); - packageManagerCore()->calculateComponentsToInstall(); foreach (QInstaller::Component *component, packageManagerCore()->orderedComponentsToInstall()) packageManagerCore()->addLicenseItem(component->licenses()); @@ -2015,7 +2106,7 @@ void LicenseAgreementPage::entering() */ bool LicenseAgreementPage::isComplete() const { - return m_acceptCheckBox->isChecked(); + return m_acceptCheckBox->isChecked() && ProductKeyCheck::instance()->hasAcceptedAllLicenses(); } void LicenseAgreementPage::openLicenseUrl(const QUrl &url) @@ -2108,7 +2199,7 @@ void ComponentSelectionPage::entering() QT_TR_NOOP("Please select the components you want to update."), QT_TR_NOOP("Please select the components you want to install."), QT_TR_NOOP("Please select the components you want to uninstall."), - QT_TR_NOOP("Select the components to install. Deselect installed components to uninstall them. Any components already installed will not be updated."), + QT_TR_NOOP("Select the components to install. Deselect installed components to uninstall them.<br>Any components already installed will not be updated."), QT_TR_NOOP("Mandatory components need to be updated first before you can select other components to update.") }; @@ -2123,18 +2214,16 @@ void ComponentSelectionPage::entering() d->updateTreeView(); // check component model state so we can enable needed component selection buttons - if (core->isUpdater()) - d->onModelStateChanged(d->m_currentModel->checkedState()); + d->onModelStateChanged(d->m_currentModel->checkedState()); setModified(isComplete()); - if (core->settings().repositoryCategories().count() > 0 && !core->isOfflineOnly() - && !core->isUpdater()) { - d->showCategoryLayout(true); - core->settings().setAllowUnstableComponents(true); - } else { - d->showCategoryLayout(false); - } + d->showCategoryLayout(core->showRepositoryCategories()); d->showCompressedRepositoryButton(); + d->showCreateOfflineInstallerButton(true); + + // Reset to default supplement state. The page may set it to OfflineGenerator + // which needs to be reset after navigating back to the page. + core->resetBinaryMarkerSupplement(); } /*! @@ -2144,6 +2233,7 @@ void ComponentSelectionPage::entering() void ComponentSelectionPage::leaving() { d->hideCompressedRepositoryButton(); + d->showCreateOfflineInstallerButton(false); } /*! @@ -2162,6 +2252,29 @@ void ComponentSelectionPage::showEvent(QShowEvent *event) } /*! + Called when \c ComponentSelectionPage is validated. + Tries to load \c component scripts for components about to be installed. + Returns \c true if the script loading succeeded and the next page is shown. +*/ +bool ComponentSelectionPage::validatePage() +{ + PackageManagerCore *core = packageManagerCore(); + try { + core->loadComponentScripts(core->orderedComponentsToInstall(), true); + } catch (const Error &error) { + // As component script loading failed, there is error in the script and component is + // marked as unselected. Recalculate so that unselected component is removed from install. + // User is then able to select other components for install. + core->clearComponentsToInstallCalculated(); + core->calculateComponentsToInstall(); + MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(), QLatin1String("Error"), + tr("Error"), error.message()); + return false; + } + return true; +} + +/*! Selects all components in the component tree. */ void ComponentSelectionPage::selectAll() @@ -2204,16 +2317,6 @@ void ComponentSelectionPage::deselectComponent(const QString &id) } /*! - Adds the possibility to install a compressed repository on component selection - page. A new button which opens a file browser is added for compressed - repository selection. -*/ -void ComponentSelectionPage::allowCompressedRepositoryInstall() -{ - d->allowCompressedRepositoryInstall(); -} - -/*! Adds an additional virtual component with the \a name to be installed. Returns \c true if the virtual component is found and not installed. @@ -2226,13 +2329,18 @@ bool ComponentSelectionPage::addVirtualComponentToUninstall(const QString &name) name, allComponents); if (component && component->isInstalled() && component->isVirtual()) { component->setCheckState(Qt::Unchecked); - core->componentsToInstallNeedsRecalculation(); + core->recalculateAllComponents(); qCDebug(QInstaller::lcDeveloperBuild) << "Virtual component " << name << " was selected for uninstall by script."; return true; } return false; } +void ComponentSelectionPage::setAllowCreateOfflineInstaller(bool allow) +{ + d->setAllowCreateOfflineInstaller(allow); +} + void ComponentSelectionPage::setModified(bool modified) { setComplete(modified); @@ -2243,18 +2351,10 @@ void ComponentSelectionPage::setModified(bool modified) */ bool ComponentSelectionPage::isComplete() const { - if (packageManagerCore()->isInstaller() || packageManagerCore()->isUpdater()) - return d->m_currentModel->checked().count(); - - if (d->m_currentModel->checkedState().testFlag(ComponentModel::DefaultChecked) == false) - return true; + if (!d->componentsResolved()) + return false; - const QSet<Component *> uncheckable = d->m_currentModel->uncheckable(); - for (auto &component : uncheckable) { - if (component->forcedInstallation() && !component->isInstalled()) - return true; // allow installation for new forced components - } - return false; + return d->m_currentModel->componentsSelected(); } @@ -2391,7 +2491,7 @@ bool TargetDirectoryPage::validatePage() if (!QVariant(remove).toBool()) return true; - return this->packageManagerCore()->checkTargetDir(targetDir()); + return this->packageManagerCore()->installationAllowedToDirectory(targetDir()); } /*! @@ -2580,7 +2680,7 @@ void ReadyForInstallationPage::entering() m_taskDetailsBrowser->setVisible(false); setButtonText(QWizard::CommitButton, tr("U&ninstall")); setColoredTitle(tr("Ready to Uninstall")); - m_msgLabel->setText(tr("Setup is now ready to begin removing %1 from your computer.<br>" + m_msgLabel->setText(tr("All required information is now available to begin removing %1 from your computer.<br>" "<font color=\"red\">The program directory %2 will be deleted completely</font>, " "including all content in that directory!") .arg(productName(), @@ -2591,26 +2691,31 @@ void ReadyForInstallationPage::entering() } else if (packageManagerCore()->isMaintainer()) { setButtonText(QWizard::CommitButton, tr("U&pdate")); setColoredTitle(tr("Ready to Update Packages")); - m_msgLabel->setText(tr("Setup is now ready to begin updating your installation.")); + m_msgLabel->setText(tr("All required information is now available to begin updating your installation.")); + } else if (packageManagerCore()->isOfflineGenerator()) { + setButtonText(QWizard::CommitButton, tr("Create Offline Installer")); + setColoredTitle(tr("Ready to Create Offline Installer")); + m_msgLabel->setText(tr("All required information is now available to create an offline installer for selected components.")); } else { Q_ASSERT(packageManagerCore()->isInstaller()); setButtonText(QWizard::CommitButton, tr("&Install")); setColoredTitle(tr("Ready to Install")); - m_msgLabel->setText(tr("Setup is now ready to begin installing %1 on your computer.") + m_msgLabel->setText(tr("All required information is now available to begin installing %1 on your computer.") .arg(productName())); } - QString htmlOutput; - bool componentsOk = packageManagerCore()->calculateComponents(&htmlOutput); + bool componentsOk = packageManagerCore()->recalculateAllComponents(); + const QString htmlOutput = packageManagerCore()->componentResolveReasons(); + + qCDebug(QInstaller::lcInstallerInstallLog).noquote() << htmlToString(htmlOutput); m_taskDetailsBrowser->setHtml(htmlOutput); m_taskDetailsBrowser->setVisible(!componentsOk || LoggingHandler::instance().isVerbose()); setComplete(componentsOk); - QString spaceInfo; - if (packageManagerCore()->checkAvailableSpace(spaceInfo)) { - m_msgLabel->setText(QString::fromLatin1("%1 %2").arg(m_msgLabel->text(), spaceInfo)); + if (packageManagerCore()->checkAvailableSpace()) { + m_msgLabel->setText(QString::fromLatin1("%1 %2").arg(m_msgLabel->text(), packageManagerCore()->availableSpaceMessage())); } else { - m_msgLabel->setText(spaceInfo); + m_msgLabel->setText(packageManagerCore()->availableSpaceMessage()); setComplete(false); } } @@ -2630,7 +2735,9 @@ void ReadyForInstallationPage::leaving() void ReadyForInstallationPage::updatePageListTitle() { PackageManagerCore *core = packageManagerCore(); - if (core->isInstaller()) + if (core->isOfflineGenerator()) + setPageListTitle(tr("Ready to Create Offline Installer")); + else if (core->isInstaller()) setPageListTitle(tr("Ready to Install")); else if (core->isMaintainer()) setPageListTitle(tr("Ready to Update")); @@ -2667,7 +2774,7 @@ void ReadyForInstallationPage::updatePageListTitle() */ PerformInstallationPage::PerformInstallationPage(PackageManagerCore *core) : PackageManagerPage(core) - , m_performInstallationForm(new PerformInstallationForm(this)) + , m_performInstallationForm(new PerformInstallationForm(core, this)) { setPixmap(QWizard::WatermarkPixmap, QPixmap()); setObjectName(QLatin1String("PerformInstallationPage")); @@ -2688,6 +2795,11 @@ PerformInstallationPage::PerformInstallationPage(PackageManagerCore *core) connect(core, &PackageManagerCore::installationFinished, this, &PerformInstallationPage::installationFinished); + connect(core, &PackageManagerCore::offlineGenerationStarted, + this, &PerformInstallationPage::installationStarted); + connect(core, &PackageManagerCore::offlineGenerationFinished, + this, &PerformInstallationPage::installationFinished); + connect(core, &PackageManagerCore::uninstallationStarted, this, &PerformInstallationPage::uninstallationStarted); connect(core, &PackageManagerCore::uninstallationFinished, @@ -2757,6 +2869,11 @@ void PerformInstallationPage::entering() setColoredTitle(tr("Updating components of %1").arg(productName())); QTimer::singleShot(30, packageManagerCore(), SLOT(runPackageUpdater())); + } else if (packageManagerCore()->isOfflineGenerator()) { + setButtonText(QWizard::CommitButton, tr("&Create Offline Installer")); + setColoredTitle(tr("Creating Offline Installer for %1").arg(productName())); + + QTimer::singleShot(30, packageManagerCore(), SLOT(runOfflineGenerator())); } else { setButtonText(QWizard::CommitButton, tr("&Install")); setColoredTitle(tr("Installing %1").arg(productName())); @@ -2781,7 +2898,9 @@ void PerformInstallationPage::leaving() void PerformInstallationPage::updatePageListTitle() { PackageManagerCore *core = packageManagerCore(); - if (core->isInstaller()) + if (core->isOfflineGenerator()) + setPageListTitle(tr("Creating Offline Installer")); + else if (core->isInstaller()) setPageListTitle(tr("Installing")); else if (core->isMaintainer()) setPageListTitle(tr("Updating")); @@ -2805,17 +2924,26 @@ void PerformInstallationPage::setTitleMessage(const QString &title) */ void PerformInstallationPage::changeCurrentImage() { - const QStringList productImages = packageManagerCore()->settings().productImages(); + const QMap<QString, QVariant> productImages = packageManagerCore()->settings().productImages(); if (productImages.isEmpty()) return; - const QString nextImage = (m_currentImage.isEmpty() || m_currentImage == productImages.last()) - ? productImages.first() - : productImages.at(productImages.indexOf(m_currentImage) + 1); + QString nextImage; + QString nextUrl; + if (m_currentImage.isEmpty() || m_currentImage == productImages.lastKey()) { + nextImage = productImages.firstKey(); + nextUrl = productImages.value(nextImage).toString(); + } else { + QMap<QString, QVariant>::const_iterator i = productImages.constFind(m_currentImage); + if (++i != productImages.end()) { + nextImage = i.key(); + nextUrl = i.value().toString(); + } + } // Do not update the pixmap if there was only one image available if (nextImage != m_currentImage) { - m_performInstallationForm->setImageFromFileName(nextImage); + m_performInstallationForm->setImageFromFileName(nextImage, nextUrl); m_currentImage = nextImage; } } @@ -2876,7 +3004,7 @@ FinishedPage::FinishedPage(PackageManagerCore *core) , m_commitButton(nullptr) { setObjectName(QLatin1String("FinishedPage")); - setColoredTitle(tr("Completing the %1 Wizard").arg(productName())); + setColoredTitle(tr("Finished the %1 Setup").arg(productName())); setPageListTitle(tr("Finished")); m_msgLabel = new QLabel(this); @@ -2901,7 +3029,7 @@ FinishedPage::FinishedPage(PackageManagerCore *core) */ void FinishedPage::entering() { - m_msgLabel->setText(tr("Click %1 to exit the %2 Wizard.") + m_msgLabel->setText(tr("Click %1 to exit the %2 Setup.") .arg(gui()->defaultButtonText(QWizard::FinishButton).remove(QLatin1Char('&'))) .arg(productName())); @@ -2948,7 +3076,8 @@ void FinishedPage::entering() connect(m_commitButton, &QAbstractButton::clicked, this, &FinishedPage::handleFinishClicked); } - if (packageManagerCore()->status() == PackageManagerCore::Success) { + if (packageManagerCore()->status() == PackageManagerCore::Success + || packageManagerCore()->status() == PackageManagerCore::EssentialUpdated) { const QString finishedText = packageManagerCore()->value(QLatin1String("FinishedText")); if (!finishedText.isEmpty()) m_msgLabel->setText(finishedText); @@ -2962,7 +3091,7 @@ void FinishedPage::entering() } } else { // TODO: how to handle this using the config.xml - setColoredTitle(tr("The %1 Wizard failed.").arg(productName())); + setColoredTitle(tr("The %1 Setup failed.").arg(productName())); } m_runItCheckBox->hide(); @@ -2993,16 +3122,8 @@ void FinishedPage::leaving() */ void FinishedPage::handleFinishClicked() { - const QString program = - packageManagerCore()->replaceVariables(packageManagerCore()->value(scRunProgram)); - - const QStringList args = packageManagerCore()->replaceVariables(packageManagerCore() - ->values(scRunProgramArguments)); - if (!m_runItCheckBox->isChecked() || program.isEmpty()) - return; - - qCDebug(QInstaller::lcInstallerInstallLog) << "starting" << program << args; - QProcess::startDetached(program, args); + if (m_runItCheckBox->isChecked()) + packageManagerCore()->runProgram(); } /*! @@ -3047,7 +3168,7 @@ RestartPage::RestartPage(PackageManagerCore *core) : PackageManagerPage(core) { setObjectName(QLatin1String("RestartPage")); - setColoredTitle(tr("Completing the %1 Setup Wizard").arg(productName())); + setColoredTitle(tr("Finished the %1 Setup").arg(productName())); // Never show this page on the page list setShowOnPageList(false); |