2022-08-19 15:59:36 +02:00
|
|
|
// Copyright (C) 2020 The Qt Company Ltd.
|
|
|
|
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
|
2020-06-03 15:56:43 +02:00
|
|
|
|
|
|
|
|
#include "plugininstallwizard.h"
|
|
|
|
|
|
2020-06-23 14:11:29 +02:00
|
|
|
#include "coreplugin.h"
|
2020-06-03 15:56:43 +02:00
|
|
|
#include "icore.h"
|
|
|
|
|
|
2020-06-23 14:11:29 +02:00
|
|
|
#include <extensionsystem/pluginspec.h>
|
|
|
|
|
|
2020-06-03 15:56:43 +02:00
|
|
|
#include <utils/archive.h>
|
|
|
|
|
#include <utils/fileutils.h>
|
|
|
|
|
#include <utils/hostosinfo.h>
|
|
|
|
|
#include <utils/infolabel.h>
|
|
|
|
|
#include <utils/pathchooser.h>
|
2020-06-23 14:11:29 +02:00
|
|
|
#include <utils/qtcassert.h>
|
2021-05-05 18:21:22 +02:00
|
|
|
#include <utils/qtcprocess.h>
|
2020-06-23 14:11:29 +02:00
|
|
|
#include <utils/runextensions.h>
|
2020-06-08 17:03:38 +02:00
|
|
|
#include <utils/temporarydirectory.h>
|
2020-06-03 15:56:43 +02:00
|
|
|
#include <utils/wizard.h>
|
|
|
|
|
#include <utils/wizardpage.h>
|
|
|
|
|
|
|
|
|
|
#include <app/app_version.h>
|
|
|
|
|
|
|
|
|
|
#include <QButtonGroup>
|
|
|
|
|
#include <QDir>
|
2020-06-23 14:11:29 +02:00
|
|
|
#include <QDirIterator>
|
2020-06-03 15:56:43 +02:00
|
|
|
#include <QFileInfo>
|
|
|
|
|
#include <QLabel>
|
|
|
|
|
#include <QMessageBox>
|
|
|
|
|
#include <QPushButton>
|
|
|
|
|
#include <QRadioButton>
|
2020-06-08 17:03:38 +02:00
|
|
|
#include <QTextEdit>
|
2020-06-03 15:56:43 +02:00
|
|
|
#include <QVBoxLayout>
|
|
|
|
|
|
2020-06-08 17:03:38 +02:00
|
|
|
#include <memory>
|
|
|
|
|
|
2020-06-23 14:11:29 +02:00
|
|
|
using namespace ExtensionSystem;
|
2020-06-03 15:56:43 +02:00
|
|
|
using namespace Utils;
|
|
|
|
|
|
2020-06-08 17:03:38 +02:00
|
|
|
struct Data
|
|
|
|
|
{
|
|
|
|
|
FilePath sourcePath;
|
|
|
|
|
FilePath extractedPath;
|
2021-02-12 15:36:07 +01:00
|
|
|
bool installIntoApplication = false;
|
2020-06-08 17:03:38 +02:00
|
|
|
};
|
2020-06-03 15:56:43 +02:00
|
|
|
|
2020-06-23 14:11:29 +02:00
|
|
|
static QStringList libraryNameFilter()
|
|
|
|
|
{
|
2020-11-18 18:42:27 +01:00
|
|
|
if (HostOsInfo::isWindowsHost())
|
2020-06-23 14:11:29 +02:00
|
|
|
return {"*.dll"};
|
2020-11-18 18:42:27 +01:00
|
|
|
if (HostOsInfo::isLinuxHost())
|
2020-06-23 14:11:29 +02:00
|
|
|
return {"*.so"};
|
|
|
|
|
return {"*.dylib"};
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-03 15:56:43 +02:00
|
|
|
static bool hasLibSuffix(const FilePath &path)
|
|
|
|
|
{
|
2020-11-18 18:42:27 +01:00
|
|
|
return (HostOsInfo::isWindowsHost() && path.endsWith(".dll"))
|
2021-06-04 09:13:49 +02:00
|
|
|
|| (HostOsInfo::isLinuxHost() && path.completeSuffix().startsWith(".so"))
|
2020-11-18 18:42:27 +01:00
|
|
|
|| (HostOsInfo::isMacHost() && path.endsWith(".dylib"));
|
2020-06-03 15:56:43 +02:00
|
|
|
}
|
|
|
|
|
|
2020-06-08 17:03:38 +02:00
|
|
|
static FilePath pluginInstallPath(bool installIntoApplication)
|
|
|
|
|
{
|
|
|
|
|
return FilePath::fromString(installIntoApplication ? Core::ICore::pluginPath()
|
|
|
|
|
: Core::ICore::userPluginPath());
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-03 15:56:43 +02:00
|
|
|
namespace Core {
|
|
|
|
|
namespace Internal {
|
|
|
|
|
|
|
|
|
|
class SourcePage : public WizardPage
|
|
|
|
|
{
|
|
|
|
|
public:
|
2020-06-08 17:03:38 +02:00
|
|
|
SourcePage(Data *data, QWidget *parent)
|
2020-06-03 15:56:43 +02:00
|
|
|
: WizardPage(parent)
|
2020-06-08 17:03:38 +02:00
|
|
|
, m_data(data)
|
2020-06-03 15:56:43 +02:00
|
|
|
{
|
|
|
|
|
setTitle(PluginInstallWizard::tr("Source"));
|
|
|
|
|
auto vlayout = new QVBoxLayout;
|
|
|
|
|
setLayout(vlayout);
|
|
|
|
|
|
|
|
|
|
auto label = new QLabel(
|
|
|
|
|
"<p>"
|
|
|
|
|
+ PluginInstallWizard::tr(
|
|
|
|
|
"Choose source location. This can be a plugin library file or a zip file.")
|
|
|
|
|
+ "</p>");
|
|
|
|
|
label->setWordWrap(true);
|
|
|
|
|
vlayout->addWidget(label);
|
|
|
|
|
|
|
|
|
|
auto path = new PathChooser;
|
|
|
|
|
path->setExpectedKind(PathChooser::Any);
|
|
|
|
|
vlayout->addWidget(path);
|
2020-06-08 17:03:38 +02:00
|
|
|
connect(path, &PathChooser::pathChanged, this, [this, path] {
|
|
|
|
|
m_data->sourcePath = path->filePath();
|
|
|
|
|
updateWarnings();
|
|
|
|
|
});
|
2020-06-03 15:56:43 +02:00
|
|
|
|
|
|
|
|
m_info = new InfoLabel;
|
|
|
|
|
m_info->setType(InfoLabel::Error);
|
|
|
|
|
m_info->setVisible(false);
|
|
|
|
|
vlayout->addWidget(m_info);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void updateWarnings()
|
|
|
|
|
{
|
|
|
|
|
m_info->setVisible(!isComplete());
|
|
|
|
|
emit completeChanged();
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-18 15:26:38 +01:00
|
|
|
bool isComplete() const final
|
2020-06-03 15:56:43 +02:00
|
|
|
{
|
2020-06-08 17:03:38 +02:00
|
|
|
const FilePath path = m_data->sourcePath;
|
2020-06-03 15:56:43 +02:00
|
|
|
if (!QFile::exists(path.toString())) {
|
|
|
|
|
m_info->setText(PluginInstallWizard::tr("File does not exist."));
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (hasLibSuffix(path))
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
QString error;
|
|
|
|
|
if (!Archive::supportsFile(path, &error)) {
|
|
|
|
|
m_info->setText(error);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-18 15:26:38 +01:00
|
|
|
int nextId() const final
|
2020-06-08 17:03:38 +02:00
|
|
|
{
|
|
|
|
|
if (hasLibSuffix(m_data->sourcePath))
|
|
|
|
|
return WizardPage::nextId() + 1; // jump over check archive
|
|
|
|
|
return WizardPage::nextId();
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-03 15:56:43 +02:00
|
|
|
InfoLabel *m_info = nullptr;
|
2020-06-08 17:03:38 +02:00
|
|
|
Data *m_data = nullptr;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class CheckArchivePage : public WizardPage
|
|
|
|
|
{
|
|
|
|
|
public:
|
2020-06-23 14:11:29 +02:00
|
|
|
struct ArchiveIssue
|
|
|
|
|
{
|
|
|
|
|
QString message;
|
|
|
|
|
InfoLabel::InfoType type;
|
|
|
|
|
};
|
|
|
|
|
|
2020-06-08 17:03:38 +02:00
|
|
|
CheckArchivePage(Data *data, QWidget *parent)
|
|
|
|
|
: WizardPage(parent)
|
|
|
|
|
, m_data(data)
|
|
|
|
|
{
|
|
|
|
|
setTitle(PluginInstallWizard::tr("Check Archive"));
|
|
|
|
|
auto vlayout = new QVBoxLayout;
|
|
|
|
|
setLayout(vlayout);
|
|
|
|
|
|
|
|
|
|
m_label = new InfoLabel;
|
2020-06-23 14:11:29 +02:00
|
|
|
m_label->setElideMode(Qt::ElideNone);
|
|
|
|
|
m_label->setWordWrap(true);
|
2020-06-08 17:03:38 +02:00
|
|
|
m_cancelButton = new QPushButton(PluginInstallWizard::tr("Cancel"));
|
|
|
|
|
m_output = new QTextEdit;
|
|
|
|
|
m_output->setReadOnly(true);
|
|
|
|
|
|
|
|
|
|
auto hlayout = new QHBoxLayout;
|
|
|
|
|
hlayout->addWidget(m_label, 1);
|
|
|
|
|
hlayout->addStretch();
|
|
|
|
|
hlayout->addWidget(m_cancelButton);
|
|
|
|
|
|
|
|
|
|
vlayout->addLayout(hlayout);
|
|
|
|
|
vlayout->addWidget(m_output);
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-18 15:26:38 +01:00
|
|
|
void initializePage() final
|
2020-06-08 17:03:38 +02:00
|
|
|
{
|
|
|
|
|
m_isComplete = false;
|
|
|
|
|
emit completeChanged();
|
|
|
|
|
m_canceled = false;
|
|
|
|
|
|
|
|
|
|
m_tempDir = std::make_unique<TemporaryDirectory>("plugininstall");
|
2021-07-01 09:58:48 +02:00
|
|
|
m_data->extractedPath = m_tempDir->path();
|
2020-06-08 17:03:38 +02:00
|
|
|
m_label->setText(PluginInstallWizard::tr("Checking archive..."));
|
2020-06-23 10:26:13 +02:00
|
|
|
m_label->setType(InfoLabel::None);
|
|
|
|
|
|
2020-06-08 17:03:38 +02:00
|
|
|
m_cancelButton->setVisible(true);
|
|
|
|
|
m_output->clear();
|
|
|
|
|
|
2022-03-24 10:29:02 +01:00
|
|
|
m_archive.reset(new Archive(m_data->sourcePath, m_tempDir->path()));
|
|
|
|
|
if (!m_archive->isValid()) {
|
2020-06-08 17:03:38 +02:00
|
|
|
m_label->setType(InfoLabel::Error);
|
|
|
|
|
m_label->setText(PluginInstallWizard::tr("The file is not an archive."));
|
|
|
|
|
return;
|
|
|
|
|
}
|
2022-03-24 10:29:02 +01:00
|
|
|
QObject::connect(m_archive.get(), &Archive::outputReceived, this,
|
|
|
|
|
[this](const QString &output) {
|
2020-06-08 17:03:38 +02:00
|
|
|
m_output->append(output);
|
|
|
|
|
});
|
2022-03-24 10:29:02 +01:00
|
|
|
QObject::connect(m_archive.get(), &Archive::finished, this, [this](bool success) {
|
|
|
|
|
m_archive.release()->deleteLater();
|
|
|
|
|
handleFinished(success);
|
|
|
|
|
});
|
|
|
|
|
QObject::connect(m_cancelButton, &QPushButton::clicked, this, [this] {
|
|
|
|
|
m_canceled = true;
|
|
|
|
|
m_archive.reset();
|
|
|
|
|
handleFinished(false);
|
|
|
|
|
});
|
|
|
|
|
m_archive->unarchive();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void handleFinished(bool success)
|
|
|
|
|
{
|
|
|
|
|
m_cancelButton->disconnect();
|
|
|
|
|
if (!success) { // unarchiving failed
|
|
|
|
|
m_cancelButton->setVisible(false);
|
|
|
|
|
if (m_canceled) {
|
|
|
|
|
m_label->setType(InfoLabel::Information);
|
|
|
|
|
m_label->setText(PluginInstallWizard::tr("Canceled."));
|
|
|
|
|
} else {
|
|
|
|
|
m_label->setType(InfoLabel::Error);
|
|
|
|
|
m_label->setText(
|
|
|
|
|
PluginInstallWizard::tr("There was an error while unarchiving."));
|
|
|
|
|
}
|
|
|
|
|
} else { // unarchiving was successful, run a check
|
|
|
|
|
m_archiveCheck = Utils::runAsync(
|
|
|
|
|
[this](QFutureInterface<ArchiveIssue> &fi) { return checkContents(fi); });
|
|
|
|
|
Utils::onFinished(m_archiveCheck, this, [this](const QFuture<ArchiveIssue> &f) {
|
2020-06-23 14:11:29 +02:00
|
|
|
m_cancelButton->setVisible(false);
|
2022-03-24 10:29:02 +01:00
|
|
|
m_cancelButton->disconnect();
|
|
|
|
|
const bool ok = f.resultCount() == 0 && !f.isCanceled();
|
|
|
|
|
if (f.isCanceled()) {
|
2020-06-08 17:03:38 +02:00
|
|
|
m_label->setType(InfoLabel::Information);
|
|
|
|
|
m_label->setText(PluginInstallWizard::tr("Canceled."));
|
2022-03-24 10:29:02 +01:00
|
|
|
} else if (ok) {
|
|
|
|
|
m_label->setType(InfoLabel::Ok);
|
|
|
|
|
m_label->setText(PluginInstallWizard::tr("Archive is OK."));
|
2020-06-08 17:03:38 +02:00
|
|
|
} else {
|
2022-03-24 10:29:02 +01:00
|
|
|
const ArchiveIssue issue = f.result();
|
|
|
|
|
m_label->setType(issue.type);
|
|
|
|
|
m_label->setText(issue.message);
|
2020-06-08 17:03:38 +02:00
|
|
|
}
|
2022-03-24 10:29:02 +01:00
|
|
|
m_isComplete = ok;
|
|
|
|
|
emit completeChanged();
|
|
|
|
|
});
|
|
|
|
|
QObject::connect(m_cancelButton, &QPushButton::clicked, this, [this] {
|
|
|
|
|
m_archiveCheck.cancel();
|
|
|
|
|
});
|
|
|
|
|
}
|
2020-06-08 17:03:38 +02:00
|
|
|
}
|
|
|
|
|
|
2020-06-23 14:11:29 +02:00
|
|
|
// Async. Result is set if any issue was found.
|
|
|
|
|
void checkContents(QFutureInterface<ArchiveIssue> &fi)
|
|
|
|
|
{
|
|
|
|
|
QTC_ASSERT(m_tempDir.get(), return );
|
|
|
|
|
|
|
|
|
|
PluginSpec *coreplugin = CorePlugin::instance()->pluginSpec();
|
|
|
|
|
|
|
|
|
|
// look for plugin
|
2021-07-01 09:58:48 +02:00
|
|
|
QDirIterator it(m_tempDir->path().path(),
|
2020-11-17 15:27:54 +01:00
|
|
|
libraryNameFilter(),
|
|
|
|
|
QDir::Files | QDir::NoSymLinks,
|
|
|
|
|
QDirIterator::Subdirectories);
|
2020-06-23 14:11:29 +02:00
|
|
|
while (it.hasNext()) {
|
|
|
|
|
if (fi.isCanceled())
|
|
|
|
|
return;
|
|
|
|
|
it.next();
|
|
|
|
|
PluginSpec *spec = PluginSpec::read(it.filePath());
|
|
|
|
|
if (spec) {
|
|
|
|
|
// Is a Qt Creator plugin. Let's see if we find a Core dependency and check the
|
|
|
|
|
// version
|
|
|
|
|
const QVector<PluginDependency> dependencies = spec->dependencies();
|
|
|
|
|
const auto found = std::find_if(dependencies.constBegin(),
|
|
|
|
|
dependencies.constEnd(),
|
|
|
|
|
[coreplugin](const PluginDependency &d) {
|
|
|
|
|
return d.name == coreplugin->name();
|
|
|
|
|
});
|
|
|
|
|
if (found != dependencies.constEnd()) {
|
|
|
|
|
if (!coreplugin->provides(found->name, found->version)) {
|
|
|
|
|
fi.reportResult({PluginInstallWizard::tr(
|
|
|
|
|
"Plugin requires an incompatible version of %1 (%2).")
|
|
|
|
|
.arg(Constants::IDE_DISPLAY_NAME)
|
|
|
|
|
.arg(found->version),
|
|
|
|
|
InfoLabel::Error});
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return; // successful / no error
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-11-17 15:27:54 +01:00
|
|
|
fi.reportResult(
|
|
|
|
|
{PluginInstallWizard::tr("Did not find %1 plugin.").arg(Constants::IDE_DISPLAY_NAME),
|
|
|
|
|
InfoLabel::Error});
|
2020-06-23 14:11:29 +02:00
|
|
|
}
|
|
|
|
|
|
2020-11-18 15:26:38 +01:00
|
|
|
void cleanupPage() final
|
2020-06-08 17:03:38 +02:00
|
|
|
{
|
|
|
|
|
// back button pressed
|
2020-06-23 14:11:29 +02:00
|
|
|
m_cancelButton->disconnect();
|
2022-03-24 10:29:02 +01:00
|
|
|
m_archive.reset();
|
2020-06-23 14:11:29 +02:00
|
|
|
if (m_archiveCheck.isRunning()) {
|
|
|
|
|
m_archiveCheck.cancel();
|
|
|
|
|
m_archiveCheck.waitForFinished();
|
|
|
|
|
}
|
2020-06-08 17:03:38 +02:00
|
|
|
m_tempDir.reset();
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-18 15:26:38 +01:00
|
|
|
bool isComplete() const final { return m_isComplete; }
|
2020-06-08 17:03:38 +02:00
|
|
|
|
|
|
|
|
std::unique_ptr<TemporaryDirectory> m_tempDir;
|
2022-03-24 10:29:02 +01:00
|
|
|
std::unique_ptr<Archive> m_archive;
|
2020-06-23 14:11:29 +02:00
|
|
|
QFuture<ArchiveIssue> m_archiveCheck;
|
2020-06-08 17:03:38 +02:00
|
|
|
InfoLabel *m_label = nullptr;
|
|
|
|
|
QPushButton *m_cancelButton = nullptr;
|
|
|
|
|
QTextEdit *m_output = nullptr;
|
|
|
|
|
Data *m_data = nullptr;
|
|
|
|
|
bool m_isComplete = false;
|
|
|
|
|
bool m_canceled = false;
|
2020-06-03 15:56:43 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class InstallLocationPage : public WizardPage
|
|
|
|
|
{
|
|
|
|
|
public:
|
2020-06-08 17:03:38 +02:00
|
|
|
InstallLocationPage(Data *data, QWidget *parent)
|
2020-06-03 15:56:43 +02:00
|
|
|
: WizardPage(parent)
|
2020-06-08 17:03:38 +02:00
|
|
|
, m_data(data)
|
2020-06-03 15:56:43 +02:00
|
|
|
{
|
|
|
|
|
setTitle(PluginInstallWizard::tr("Install Location"));
|
|
|
|
|
auto vlayout = new QVBoxLayout;
|
|
|
|
|
setLayout(vlayout);
|
|
|
|
|
|
|
|
|
|
auto label = new QLabel("<p>" + PluginInstallWizard::tr("Choose install location.")
|
|
|
|
|
+ "</p>");
|
|
|
|
|
label->setWordWrap(true);
|
|
|
|
|
vlayout->addWidget(label);
|
|
|
|
|
vlayout->addSpacing(10);
|
|
|
|
|
|
|
|
|
|
auto localInstall = new QRadioButton(PluginInstallWizard::tr("User plugins"));
|
2021-02-12 15:36:07 +01:00
|
|
|
localInstall->setChecked(!m_data->installIntoApplication);
|
2020-06-03 15:56:43 +02:00
|
|
|
auto localLabel = new QLabel(
|
|
|
|
|
PluginInstallWizard::tr("The plugin will be available to all compatible %1 "
|
|
|
|
|
"installations, but only for the current user.")
|
|
|
|
|
.arg(Constants::IDE_DISPLAY_NAME));
|
|
|
|
|
localLabel->setWordWrap(true);
|
|
|
|
|
localLabel->setAttribute(Qt::WA_MacSmallSize, true);
|
|
|
|
|
|
|
|
|
|
vlayout->addWidget(localInstall);
|
|
|
|
|
vlayout->addWidget(localLabel);
|
|
|
|
|
vlayout->addSpacing(10);
|
|
|
|
|
|
|
|
|
|
auto appInstall = new QRadioButton(
|
|
|
|
|
PluginInstallWizard::tr("%1 installation").arg(Constants::IDE_DISPLAY_NAME));
|
2021-02-12 15:36:07 +01:00
|
|
|
appInstall->setChecked(m_data->installIntoApplication);
|
2020-06-03 15:56:43 +02:00
|
|
|
auto appLabel = new QLabel(
|
|
|
|
|
PluginInstallWizard::tr("The plugin will be available only to this %1 "
|
|
|
|
|
"installation, but for all users that can access it.")
|
|
|
|
|
.arg(Constants::IDE_DISPLAY_NAME));
|
|
|
|
|
appLabel->setWordWrap(true);
|
|
|
|
|
appLabel->setAttribute(Qt::WA_MacSmallSize, true);
|
|
|
|
|
vlayout->addWidget(appInstall);
|
|
|
|
|
vlayout->addWidget(appLabel);
|
|
|
|
|
|
|
|
|
|
auto group = new QButtonGroup(this);
|
|
|
|
|
group->addButton(localInstall);
|
|
|
|
|
group->addButton(appInstall);
|
|
|
|
|
|
|
|
|
|
connect(appInstall, &QRadioButton::toggled, this, [this](bool toggled) {
|
2020-06-08 17:03:38 +02:00
|
|
|
m_data->installIntoApplication = toggled;
|
2020-06-03 15:56:43 +02:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-08 17:03:38 +02:00
|
|
|
Data *m_data = nullptr;
|
|
|
|
|
};
|
2020-06-03 15:56:43 +02:00
|
|
|
|
|
|
|
|
class SummaryPage : public WizardPage
|
|
|
|
|
{
|
|
|
|
|
public:
|
2020-06-08 17:03:38 +02:00
|
|
|
SummaryPage(Data *data, QWidget *parent)
|
2020-06-03 15:56:43 +02:00
|
|
|
: WizardPage(parent)
|
2020-06-08 17:03:38 +02:00
|
|
|
, m_data(data)
|
2020-06-03 15:56:43 +02:00
|
|
|
{
|
|
|
|
|
setTitle(PluginInstallWizard::tr("Summary"));
|
|
|
|
|
|
|
|
|
|
auto vlayout = new QVBoxLayout;
|
|
|
|
|
setLayout(vlayout);
|
|
|
|
|
|
|
|
|
|
m_summaryLabel = new QLabel(this);
|
|
|
|
|
m_summaryLabel->setWordWrap(true);
|
|
|
|
|
vlayout->addWidget(m_summaryLabel);
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-18 15:26:38 +01:00
|
|
|
void initializePage() final
|
2020-06-03 15:56:43 +02:00
|
|
|
{
|
2020-06-08 17:03:38 +02:00
|
|
|
m_summaryLabel->setText(
|
|
|
|
|
PluginInstallWizard::tr("\"%1\" will be installed into \"%2\".")
|
|
|
|
|
.arg(m_data->sourcePath.toUserOutput(),
|
|
|
|
|
pluginInstallPath(m_data->installIntoApplication).toUserOutput()));
|
2020-06-03 15:56:43 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
QLabel *m_summaryLabel;
|
2020-06-08 17:03:38 +02:00
|
|
|
Data *m_data = nullptr;
|
2020-06-03 15:56:43 +02:00
|
|
|
};
|
|
|
|
|
|
2021-08-09 08:53:40 +02:00
|
|
|
static std::function<void(FilePath)> postCopyOperation()
|
2021-02-12 16:06:24 +01:00
|
|
|
{
|
2021-08-09 08:53:40 +02:00
|
|
|
return [](const FilePath &filePath) {
|
2021-02-12 16:06:24 +01:00
|
|
|
if (!HostOsInfo::isMacHost())
|
|
|
|
|
return;
|
|
|
|
|
// On macOS, downloaded files get a quarantine flag, remove it, otherwise it is a hassle
|
|
|
|
|
// to get it loaded as a plugin in Qt Creator.
|
2021-06-22 04:33:47 +02:00
|
|
|
QtcProcess xattr;
|
2021-02-12 16:06:24 +01:00
|
|
|
xattr.setTimeoutS(1);
|
2021-08-09 08:53:40 +02:00
|
|
|
xattr.setCommand({"/usr/bin/xattr", {"-d", "com.apple.quarantine", filePath.absoluteFilePath().toString()}});
|
2021-05-17 12:02:42 +02:00
|
|
|
xattr.runBlocking();
|
2021-02-12 16:06:24 +01:00
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-03 15:56:43 +02:00
|
|
|
static bool copyPluginFile(const FilePath &src, const FilePath &dest)
|
|
|
|
|
{
|
|
|
|
|
const FilePath destFile = dest.pathAppended(src.fileName());
|
2021-08-09 08:53:40 +02:00
|
|
|
if (destFile.exists()) {
|
2020-06-03 15:56:43 +02:00
|
|
|
QMessageBox box(QMessageBox::Question,
|
|
|
|
|
PluginInstallWizard::tr("Overwrite File"),
|
|
|
|
|
PluginInstallWizard::tr("The file \"%1\" exists. Overwrite?")
|
|
|
|
|
.arg(destFile.toUserOutput()),
|
|
|
|
|
QMessageBox::Cancel,
|
|
|
|
|
ICore::dialogParent());
|
|
|
|
|
QPushButton *acceptButton = box.addButton(PluginInstallWizard::tr("Overwrite"),
|
|
|
|
|
QMessageBox::AcceptRole);
|
|
|
|
|
box.setDefaultButton(acceptButton);
|
|
|
|
|
box.exec();
|
|
|
|
|
if (box.clickedButton() != acceptButton)
|
|
|
|
|
return false;
|
2021-08-09 08:53:40 +02:00
|
|
|
destFile.removeFile();
|
2020-06-03 15:56:43 +02:00
|
|
|
}
|
2021-08-09 08:53:40 +02:00
|
|
|
dest.parentDir().ensureWritableDir();
|
|
|
|
|
if (!src.copyFile(destFile)) {
|
2020-06-03 15:56:43 +02:00
|
|
|
QMessageBox::warning(ICore::dialogParent(),
|
|
|
|
|
PluginInstallWizard::tr("Failed to Write File"),
|
|
|
|
|
PluginInstallWizard::tr("Failed to write file \"%1\".")
|
|
|
|
|
.arg(destFile.toUserOutput()));
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2021-08-09 08:53:40 +02:00
|
|
|
postCopyOperation()(destFile);
|
2020-06-03 15:56:43 +02:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool PluginInstallWizard::exec()
|
|
|
|
|
{
|
|
|
|
|
Wizard wizard(ICore::dialogParent());
|
|
|
|
|
wizard.setWindowTitle(tr("Install Plugin"));
|
|
|
|
|
|
2020-06-08 17:03:38 +02:00
|
|
|
Data data;
|
|
|
|
|
|
|
|
|
|
auto filePage = new SourcePage(&data, &wizard);
|
2020-06-03 15:56:43 +02:00
|
|
|
wizard.addPage(filePage);
|
|
|
|
|
|
2020-06-08 17:03:38 +02:00
|
|
|
auto checkArchivePage = new CheckArchivePage(&data, &wizard);
|
|
|
|
|
wizard.addPage(checkArchivePage);
|
|
|
|
|
|
|
|
|
|
auto installLocationPage = new InstallLocationPage(&data, &wizard);
|
2020-06-03 15:56:43 +02:00
|
|
|
wizard.addPage(installLocationPage);
|
|
|
|
|
|
2020-06-08 17:03:38 +02:00
|
|
|
auto summaryPage = new SummaryPage(&data, &wizard);
|
2020-06-03 15:56:43 +02:00
|
|
|
wizard.addPage(summaryPage);
|
|
|
|
|
|
|
|
|
|
if (wizard.exec()) {
|
2020-06-08 17:03:38 +02:00
|
|
|
const FilePath installPath = pluginInstallPath(data.installIntoApplication);
|
|
|
|
|
if (hasLibSuffix(data.sourcePath)) {
|
|
|
|
|
return copyPluginFile(data.sourcePath, installPath);
|
|
|
|
|
} else {
|
|
|
|
|
QString error;
|
|
|
|
|
if (!FileUtils::copyRecursively(data.extractedPath,
|
|
|
|
|
installPath,
|
|
|
|
|
&error,
|
2021-02-12 16:06:24 +01:00
|
|
|
FileUtils::CopyAskingForOverwrite(ICore::dialogParent(),
|
|
|
|
|
postCopyOperation()))) {
|
2020-06-08 17:03:38 +02:00
|
|
|
QMessageBox::warning(ICore::dialogParent(),
|
|
|
|
|
PluginInstallWizard::tr("Failed to Copy Plugin Files"),
|
|
|
|
|
error);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
2020-06-03 15:56:43 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace Internal
|
|
|
|
|
} // namespace Core
|