Extract plugin install wizard

It will become more complicated, so pull it into its own files.

Change-Id: If362f0775cc9d97a3c715c046d32d6a20c30a1f9
Reviewed-by: Alessandro Portale <alessandro.portale@qt.io>
This commit is contained in:
Eike Ziller
2020-06-03 15:56:43 +02:00
parent 65ced9733b
commit 4613199548
6 changed files with 322 additions and 219 deletions

View File

@@ -130,6 +130,7 @@ add_qtc_plugin(Core
outputwindow.cpp outputwindow.h outputwindow.cpp outputwindow.h
patchtool.cpp patchtool.h patchtool.cpp patchtool.h
plugindialog.cpp plugindialog.h plugindialog.cpp plugindialog.h
plugininstallwizard.cpp plugininstallwizard.h
progressmanager/futureprogress.cpp progressmanager/futureprogress.h progressmanager/futureprogress.cpp progressmanager/futureprogress.h
progressmanager/progressbar.cpp progressmanager/progressbar.h progressmanager/progressbar.cpp progressmanager/progressbar.h
progressmanager/progressmanager.cpp progressmanager/progressmanager.h progressmanager/progressmanager_p.h progressmanager/progressmanager.cpp progressmanager/progressmanager.h progressmanager/progressmanager_p.h

View File

@@ -113,7 +113,8 @@ SOURCES += corejsextensions.cpp \
diffservice.cpp \ diffservice.cpp \
menubarfilter.cpp \ menubarfilter.cpp \
welcomepagehelper.cpp \ welcomepagehelper.cpp \
dialogs/codecselector.cpp dialogs/codecselector.cpp \
plugininstallwizard.cpp
HEADERS += corejsextensions.h \ HEADERS += corejsextensions.h \
mainwindow.h \ mainwindow.h \
@@ -230,7 +231,8 @@ HEADERS += corejsextensions.h \
menubarfilter.h \ menubarfilter.h \
editormanager/ieditorfactory_p.h \ editormanager/ieditorfactory_p.h \
welcomepagehelper.h \ welcomepagehelper.h \
dialogs/codecselector.h dialogs/codecselector.h \
plugininstallwizard.h
FORMS += dialogs/newdialog.ui \ FORMS += dialogs/newdialog.ui \
dialogs/saveitemsdialog.ui \ dialogs/saveitemsdialog.ui \

View File

@@ -153,6 +153,8 @@ Project {
"patchtool.h", "patchtool.h",
"plugindialog.cpp", "plugindialog.cpp",
"plugindialog.h", "plugindialog.h",
"plugininstallwizard.cpp",
"plugininstallwizard.h",
"reaper.cpp", "reaper.cpp",
"reaper.h", "reaper.h",
"reaper_p.h", "reaper_p.h",

View File

@@ -28,6 +28,7 @@
#include "icore.h" #include "icore.h"
#include "dialogs/restartdialog.h" #include "dialogs/restartdialog.h"
#include "plugininstallwizard.h"
#include <app/app_version.h> #include <app/app_version.h>
@@ -37,24 +38,15 @@
#include <extensionsystem/pluginspec.h> #include <extensionsystem/pluginspec.h>
#include <extensionsystem/pluginview.h> #include <extensionsystem/pluginview.h>
#include <utils/archive.h>
#include <utils/fancylineedit.h> #include <utils/fancylineedit.h>
#include <utils/infolabel.h>
#include <utils/pathchooser.h>
#include <utils/wizard.h>
#include <utils/wizardpage.h>
#include <QButtonGroup>
#include <QCheckBox> #include <QCheckBox>
#include <QDebug> #include <QDebug>
#include <QDialog> #include <QDialog>
#include <QDialogButtonBox> #include <QDialogButtonBox>
#include <QDir>
#include <QFileInfo>
#include <QHBoxLayout> #include <QHBoxLayout>
#include <QLabel> #include <QLabel>
#include <QPushButton> #include <QPushButton>
#include <QRadioButton>
#include <QVBoxLayout> #include <QVBoxLayout>
using namespace Utils; using namespace Utils;
@@ -64,163 +56,6 @@ namespace Internal {
static bool s_isRestartRequired = false; static bool s_isRestartRequired = false;
const char kPath[] = "Path";
const char kApplicationInstall[] = "ApplicationInstall";
static bool hasLibSuffix(const FilePath &path)
{
return (HostOsInfo().isWindowsHost() && path.endsWith(".dll"))
|| (HostOsInfo().isLinuxHost() && path.toFileInfo().completeSuffix().startsWith(".so"))
|| (HostOsInfo().isMacHost() && path.endsWith(".dylib"));
}
class SourcePage : public WizardPage
{
public:
SourcePage(QWidget *parent)
: WizardPage(parent)
{
setTitle(PluginDialog::tr("Source"));
auto vlayout = new QVBoxLayout;
setLayout(vlayout);
auto label = new QLabel(
"<p>"
+ PluginDialog::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);
registerFieldWithName(kPath, path, "path", SIGNAL(pathChanged(QString)));
connect(path, &PathChooser::pathChanged, this, &SourcePage::updateWarnings);
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();
}
bool isComplete() const
{
const auto path = FilePath::fromVariant(field(kPath));
if (!QFile::exists(path.toString())) {
m_info->setText(PluginDialog::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;
}
InfoLabel *m_info = nullptr;
};
class InstallLocationPage : public WizardPage
{
public:
InstallLocationPage(QWidget *parent)
: WizardPage(parent)
{
setTitle(PluginDialog::tr("Install Location"));
auto vlayout = new QVBoxLayout;
setLayout(vlayout);
auto label = new QLabel("<p>" + PluginDialog::tr("Choose install location.") + "</p>");
label->setWordWrap(true);
vlayout->addWidget(label);
vlayout->addSpacing(10);
auto localInstall = new QRadioButton(PluginDialog::tr("User plugins"));
localInstall->setChecked(true);
auto localLabel = new QLabel(
PluginDialog::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(
PluginDialog::tr("%1 installation").arg(Constants::IDE_DISPLAY_NAME));
auto appLabel = new QLabel(
PluginDialog::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);
registerFieldWithName(kApplicationInstall, this);
setField(kApplicationInstall, false);
connect(appInstall, &QRadioButton::toggled, this, [this](bool toggled) {
setField(kApplicationInstall, toggled);
});
}
};
static FilePath pluginInstallPath(QWizard *wizard)
{
return FilePath::fromString(wizard->field(kApplicationInstall).toBool()
? ICore::pluginPath()
: ICore::userPluginPath());
}
static FilePath pluginFilePath(QWizard *wizard)
{
return FilePath::fromVariant(wizard->field(kPath));
}
class SummaryPage : public WizardPage
{
public:
SummaryPage(QWidget *parent)
: WizardPage(parent)
{
setTitle(PluginDialog::tr("Summary"));
auto vlayout = new QVBoxLayout;
setLayout(vlayout);
m_summaryLabel = new QLabel(this);
m_summaryLabel->setWordWrap(true);
vlayout->addWidget(m_summaryLabel);
}
void initializePage()
{
m_summaryLabel->setText(PluginDialog::tr("\"%1\" will be installed into \"%2\".")
.arg(pluginFilePath(wizard()).toUserOutput(),
pluginInstallPath(wizard()).toUserOutput()));
}
private:
QLabel *m_summaryLabel;
};
PluginDialog::PluginDialog(QWidget *parent) PluginDialog::PluginDialog(QWidget *parent)
: QDialog(parent), : QDialog(parent),
m_view(new ExtensionSystem::PluginView(this)) m_view(new ExtensionSystem::PluginView(this))
@@ -298,59 +133,10 @@ void PluginDialog::closeDialog()
accept(); accept();
} }
static bool copyPluginFile(const FilePath &src, const FilePath &dest)
{
const FilePath destFile = dest.pathAppended(src.fileName());
if (QFile::exists(destFile.toString())) {
QMessageBox box(QMessageBox::Question,
PluginDialog::tr("Overwrite File"),
PluginDialog::tr("The file \"%1\" exists. Overwrite?")
.arg(destFile.toUserOutput()),
QMessageBox::Cancel,
ICore::dialogParent());
QPushButton *acceptButton = box.addButton(PluginDialog::tr("Overwrite"), QMessageBox::AcceptRole);
box.setDefaultButton(acceptButton);
box.exec();
if (box.clickedButton() != acceptButton)
return false;
QFile::remove(destFile.toString());
}
QDir(dest.toString()).mkpath(".");
if (!QFile::copy(src.toString(), destFile.toString())) {
QMessageBox::warning(ICore::dialogParent(),
PluginDialog::tr("Failed to Write File"),
PluginDialog::tr("Failed to write file \"%1\".")
.arg(destFile.toUserOutput()));
return false;
}
return true;
}
void PluginDialog::showInstallWizard() void PluginDialog::showInstallWizard()
{ {
Wizard wizard(ICore::dialogParent()); if (PluginInstallWizard::exec())
wizard.setWindowTitle(tr("Install Plugin"));
auto filePage = new SourcePage(&wizard);
wizard.addPage(filePage);
auto installLocationPage = new InstallLocationPage(&wizard);
wizard.addPage(installLocationPage);
auto summaryPage = new SummaryPage(&wizard);
wizard.addPage(summaryPage);
if (wizard.exec()) {
const FilePath path = pluginFilePath(&wizard);
const FilePath installPath = pluginInstallPath(&wizard);
if (hasLibSuffix(path)) {
if (copyPluginFile(path, installPath))
updateRestartRequired(); updateRestartRequired();
} else if (Archive::supportsFile(path)) {
if (Archive::unarchive(path, installPath, ICore::dialogParent()))
updateRestartRequired();
}
}
} }
void PluginDialog::updateRestartRequired() void PluginDialog::updateRestartRequired()

View File

@@ -0,0 +1,270 @@
/****************************************************************************
**
** Copyright (C) 2020 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "plugininstallwizard.h"
#include "icore.h"
#include <utils/archive.h>
#include <utils/fileutils.h>
#include <utils/hostosinfo.h>
#include <utils/infolabel.h>
#include <utils/pathchooser.h>
#include <utils/wizard.h>
#include <utils/wizardpage.h>
#include <app/app_version.h>
#include <QButtonGroup>
#include <QDir>
#include <QFileInfo>
#include <QLabel>
#include <QMessageBox>
#include <QPushButton>
#include <QRadioButton>
#include <QVBoxLayout>
using namespace Utils;
const char kPath[] = "Path";
const char kApplicationInstall[] = "ApplicationInstall";
static bool hasLibSuffix(const FilePath &path)
{
return (HostOsInfo().isWindowsHost() && path.endsWith(".dll"))
|| (HostOsInfo().isLinuxHost() && path.toFileInfo().completeSuffix().startsWith(".so"))
|| (HostOsInfo().isMacHost() && path.endsWith(".dylib"));
}
namespace Core {
namespace Internal {
class SourcePage : public WizardPage
{
public:
SourcePage(QWidget *parent)
: WizardPage(parent)
{
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);
registerFieldWithName(kPath, path, "path", SIGNAL(pathChanged(QString)));
connect(path, &PathChooser::pathChanged, this, &SourcePage::updateWarnings);
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();
}
bool isComplete() const
{
const auto path = FilePath::fromVariant(field(kPath));
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;
}
InfoLabel *m_info = nullptr;
};
class InstallLocationPage : public WizardPage
{
public:
InstallLocationPage(QWidget *parent)
: WizardPage(parent)
{
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"));
localInstall->setChecked(true);
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));
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);
registerFieldWithName(kApplicationInstall, this);
setField(kApplicationInstall, false);
connect(appInstall, &QRadioButton::toggled, this, [this](bool toggled) {
setField(kApplicationInstall, toggled);
});
}
};
static FilePath pluginInstallPath(QWizard *wizard)
{
return FilePath::fromString(wizard->field(kApplicationInstall).toBool()
? ICore::pluginPath()
: ICore::userPluginPath());
}
static FilePath pluginFilePath(QWizard *wizard)
{
return FilePath::fromVariant(wizard->field(kPath));
}
class SummaryPage : public WizardPage
{
public:
SummaryPage(QWidget *parent)
: WizardPage(parent)
{
setTitle(PluginInstallWizard::tr("Summary"));
auto vlayout = new QVBoxLayout;
setLayout(vlayout);
m_summaryLabel = new QLabel(this);
m_summaryLabel->setWordWrap(true);
vlayout->addWidget(m_summaryLabel);
}
void initializePage()
{
m_summaryLabel->setText(PluginInstallWizard::tr("\"%1\" will be installed into \"%2\".")
.arg(pluginFilePath(wizard()).toUserOutput(),
pluginInstallPath(wizard()).toUserOutput()));
}
private:
QLabel *m_summaryLabel;
};
static bool copyPluginFile(const FilePath &src, const FilePath &dest)
{
const FilePath destFile = dest.pathAppended(src.fileName());
if (QFile::exists(destFile.toString())) {
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;
QFile::remove(destFile.toString());
}
QDir(dest.toString()).mkpath(".");
if (!QFile::copy(src.toString(), destFile.toString())) {
QMessageBox::warning(ICore::dialogParent(),
PluginInstallWizard::tr("Failed to Write File"),
PluginInstallWizard::tr("Failed to write file \"%1\".")
.arg(destFile.toUserOutput()));
return false;
}
return true;
}
bool PluginInstallWizard::exec()
{
Wizard wizard(ICore::dialogParent());
wizard.setWindowTitle(tr("Install Plugin"));
auto filePage = new SourcePage(&wizard);
wizard.addPage(filePage);
auto installLocationPage = new InstallLocationPage(&wizard);
wizard.addPage(installLocationPage);
auto summaryPage = new SummaryPage(&wizard);
wizard.addPage(summaryPage);
if (wizard.exec()) {
const FilePath path = pluginFilePath(&wizard);
const FilePath installPath = pluginInstallPath(&wizard);
if (hasLibSuffix(path)) {
if (copyPluginFile(path, installPath))
return true;
} else if (Archive::supportsFile(path)) {
if (Archive::unarchive(path, installPath, ICore::dialogParent()))
return true;
}
}
return false;
}
} // namespace Internal
} // namespace Core

View File

@@ -0,0 +1,42 @@
/****************************************************************************
**
** Copyright (C) 2020 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include <QCoreApplication>
namespace Core {
namespace Internal {
class PluginInstallWizard
{
Q_DECLARE_TR_FUNCTIONS(Core::Internal::PluginInstallWizard)
public:
static bool exec();
};
} // namespace Internal
} // namespace Core