mirror of
https://github.com/QuasarApp/installer-framework.git
synced 2025-05-13 13:29:35 +00:00
Files and syminks are not allowed as target dir either. Fixes a broken check if the selected target contains an installation already. Do not complain on empty targets.
1688 lines
56 KiB
C++
1688 lines
56 KiB
C++
/**************************************************************************
|
|
**
|
|
** This file is part of Qt SDK**
|
|
**
|
|
** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).*
|
|
**
|
|
** Contact: Nokia Corporation qt-info@nokia.com**
|
|
**
|
|
** No Commercial Usage
|
|
**
|
|
** This file contains pre-release code and may not be distributed.
|
|
** You may use this file in accordance with the terms and conditions
|
|
** contained in the Technology Preview License Agreement accompanying
|
|
** this package.
|
|
**
|
|
** GNU Lesser General Public License Usage
|
|
**
|
|
** This file may be used under the terms of the GNU Lesser General Public
|
|
** License version 2.1 as published by the Free Software Foundation and
|
|
** appearing in the file LICENSE.LGPL included in the packaging of this file.
|
|
** Please review the following information to ensure the GNU Lesser General
|
|
** Public License version 2.1 requirements will be met:
|
|
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
|
**
|
|
** In addition, as a special exception, Nokia gives you certain additional
|
|
** rights. These rights are described in the Nokia Qt LGPL Exception version
|
|
** 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
|
**
|
|
** If you are unsure which license is appropriate for your use, please contact
|
|
** (qt-info@nokia.com).
|
|
**
|
|
**************************************************************************/
|
|
#include "qinstallergui.h"
|
|
|
|
#include "componentmodel.h"
|
|
#include "qinstaller.h"
|
|
#include "qinstallerglobal.h"
|
|
#include "qinstallercomponent.h"
|
|
#include "progresscoordinator.h"
|
|
#include "performinstallationform.h"
|
|
|
|
#include "common/errors.h"
|
|
#include "common/utils.h"
|
|
#include "common/installersettings.h"
|
|
#include "common/fileutils.h"
|
|
|
|
#include <KDToolsCore/KDByteSize>
|
|
#include <KDToolsCore/KDSysInfo>
|
|
|
|
#include <QtCore/QDir>
|
|
#include <QtCore/QDynamicPropertyChangeEvent>
|
|
#include <QtCore/QPair>
|
|
#include <QtCore/QProcess>
|
|
#include <QtCore/QRegExp>
|
|
#include <QtCore/QSettings>
|
|
#include <QtCore/QTimer>
|
|
|
|
#include <QtGui/QApplication>
|
|
#include <QtGui/QCheckBox>
|
|
#include <QtGui/QDesktopServices>
|
|
#include <QtGui/QFileDialog>
|
|
#include <QtGui/QGridLayout>
|
|
#include <QtGui/QFormLayout>
|
|
#include <QtGui/QHBoxLayout>
|
|
#include <QtGui/QHeaderView>
|
|
#include <QtGui/QGroupBox>
|
|
#include <QtGui/QLabel>
|
|
#include <QtGui/QLineEdit>
|
|
#include <QtGui/QListWidget>
|
|
#include <QtGui/QListWidgetItem>
|
|
#include <QtGui/QMessageBox>
|
|
#include <QtGui/QProgressBar>
|
|
#include <QtGui/QPushButton>
|
|
#include <QtGui/QRadioButton>
|
|
#include <QtGui/QTextBrowser>
|
|
#include <QtGui/QTreeWidget>
|
|
#include <QtGui/QTreeView>
|
|
#include <QtGui/QVBoxLayout>
|
|
#include <QtGui/QScrollBar>
|
|
|
|
#include <QtScript/QScriptEngine>
|
|
|
|
using namespace QInstaller;
|
|
|
|
/*
|
|
TRANSLATOR QInstaller::Installer;
|
|
*/
|
|
/*
|
|
TRANSLATOR QInstaller::Gui
|
|
*/
|
|
/*
|
|
TRANSLATOR QInstaller::Page
|
|
*/
|
|
/*
|
|
TRANSLATOR QInstaller::IntroductionPage
|
|
*/
|
|
/*
|
|
TRANSLATOR QInstaller::LicenseAgreementPage
|
|
*/
|
|
/*
|
|
TRANSLATOR QInstaller::ComponentSelectionPage
|
|
*/
|
|
/*
|
|
TRANSLATOR QInstaller::TargetDirectoryPage
|
|
*/
|
|
/*
|
|
TRANSLATOR QInstaller::StartMenuDirectoryPage
|
|
*/
|
|
/*
|
|
TRANSLATOR QInstaller::ReadyForInstallationPage
|
|
*/
|
|
/*
|
|
TRANSLATOR QInstaller::PerformInstallationPage
|
|
*/
|
|
/*
|
|
TRANSLATOR QInstaller::FinishedPage
|
|
*/
|
|
|
|
|
|
class DynamicInstallerPage : public Page
|
|
{
|
|
public:
|
|
explicit DynamicInstallerPage(QWidget* widget, Installer* parent = 0)
|
|
: Page(parent)
|
|
, m_widget(widget)
|
|
{
|
|
setObjectName(QLatin1String("Dynamic") + widget->objectName());
|
|
setPixmap(QWizard::LogoPixmap, logoPixmap());
|
|
setPixmap(QWizard::WatermarkPixmap, QPixmap());
|
|
|
|
setLayout(new QVBoxLayout);
|
|
setTitle(widget->windowTitle());
|
|
m_widget->setProperty("complete", true);
|
|
m_widget->setProperty("final", false);
|
|
widget->installEventFilter(this);
|
|
layout()->addWidget(widget);
|
|
}
|
|
|
|
QWidget* widget() const
|
|
{
|
|
return m_widget;
|
|
}
|
|
|
|
bool isComplete() const
|
|
{
|
|
return m_widget->property("complete").toBool();
|
|
}
|
|
|
|
protected:
|
|
bool eventFilter(QObject* obj, QEvent* event)
|
|
{
|
|
if (obj == m_widget) {
|
|
switch(event->type()) {
|
|
case QEvent::WindowTitleChange:
|
|
setTitle(m_widget->windowTitle());
|
|
break;
|
|
|
|
case QEvent::DynamicPropertyChange:
|
|
emit completeChanged();
|
|
if (m_widget->property("final").toBool() != isFinalPage())
|
|
setFinalPage(m_widget->property("final").toBool());
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
return Page::eventFilter(obj, event);
|
|
}
|
|
|
|
private:
|
|
QWidget* const m_widget;
|
|
};
|
|
|
|
|
|
// -- Gui::Private
|
|
|
|
class Gui::Private
|
|
{
|
|
public:
|
|
Private()
|
|
: autoSwitchPage(true)
|
|
, modified(false) { }
|
|
|
|
QScriptEngine controlScriptEngine;
|
|
QScriptValue controlScript;
|
|
bool autoSwitchPage;
|
|
bool modified;
|
|
};
|
|
|
|
|
|
// -- Gui
|
|
|
|
QScriptEngine* Gui::controlScriptEngine() const
|
|
{
|
|
return &d->controlScriptEngine;
|
|
}
|
|
|
|
/*!
|
|
\class QInstaller::Gui
|
|
Is the "gui" object in a none interactive installation
|
|
*/
|
|
Gui::Gui(Installer *installer, QWidget *parent)
|
|
: QWizard(parent)
|
|
, m_installer(installer)
|
|
, d(new Private)
|
|
{
|
|
if (installer->isInstaller())
|
|
setWindowTitle(tr("%1 Setup").arg(m_installer->value(QLatin1String("Title"))));
|
|
else
|
|
setWindowTitle(tr("%1").arg(m_installer->value(QLatin1String("maintenanceTitle"))));
|
|
|
|
#ifndef Q_WS_MAC
|
|
setWindowIcon(QIcon(m_installer->settings().icon()));
|
|
setWizardStyle(QWizard::ModernStyle);
|
|
#else
|
|
setPixmap(QWizard::BackgroundPixmap, m_installer->settings().background());
|
|
#endif
|
|
setOption(QWizard::NoBackButtonOnStartPage);
|
|
setOption(QWizard::NoBackButtonOnLastPage);
|
|
setLayout(new QVBoxLayout(this));
|
|
|
|
connect(this, SIGNAL(interrupted()), installer, SLOT(interrupt()));
|
|
connect(this, SIGNAL(rejected()), installer, SLOT(setCanceled()));
|
|
// both queued to show the finished page once everything is done
|
|
connect(installer, SIGNAL(installationFinished()), this,
|
|
SLOT(showFinishedPage()), Qt::QueuedConnection);
|
|
connect(installer, SIGNAL(uninstallationFinished()), this,
|
|
SLOT(showFinishedPage()), Qt::QueuedConnection);
|
|
|
|
connect(this, SIGNAL(currentIdChanged(int)), this, SLOT(slotCurrentPageChanged(int)));
|
|
connect(this, SIGNAL(currentIdChanged(int)), m_installer, SIGNAL(currentPageChanged(int)));
|
|
connect(button(QWizard::FinishButton), SIGNAL(clicked()), this, SIGNAL(finishButtonClicked()));
|
|
connect(this, SIGNAL(finishButtonClicked()), installer, SIGNAL(finishButtonClicked()));
|
|
|
|
// make sure the QUiLoader's retranslateUi is executed first, then the script
|
|
connect(this, SIGNAL(languageChanged()),
|
|
installer, SLOT(languageChanged()), Qt::QueuedConnection);
|
|
|
|
connect(installer, SIGNAL(wizardPageInsertionRequested(QWidget*, Installer::WizardPage)),
|
|
this, SLOT(wizardPageInsertionRequested(QWidget*, Installer::WizardPage)));
|
|
connect(installer, SIGNAL(wizardPageRemovalRequested(QWidget*)),
|
|
this, SLOT(wizardPageRemovalRequested(QWidget*)));
|
|
connect(installer, SIGNAL(wizardWidgetInsertionRequested(QWidget*, Installer::WizardPage)),
|
|
this, SLOT(wizardWidgetInsertionRequested(QWidget*, Installer::WizardPage)));
|
|
connect(installer, SIGNAL(wizardWidgetRemovalRequested(QWidget*)),
|
|
this, SLOT(wizardWidgetRemovalRequested(QWidget*)));
|
|
connect(installer, SIGNAL(wizardPageVisibilityChangeRequested(bool, int)),
|
|
this, SLOT(wizardPageVisibilityChangeRequested(bool, int)), Qt::QueuedConnection);
|
|
|
|
connect(installer, SIGNAL(setAutomatedPageSwitchEnabled(bool)),
|
|
this, SLOT(setAutomatedPageSwitchEnabled(bool)));
|
|
|
|
#ifdef Q_WS_MAC
|
|
setButtonText(QWizard::BackButton, tr("&Go Back"));
|
|
setButtonText(QWizard::NextButton, tr("&Continue"));
|
|
setButtonText(QWizard::FinishButton, tr("&Done"));
|
|
#else
|
|
setButtonText(QWizard::BackButton, tr("&Back"));
|
|
setButtonText(QWizard::NextButton, tr("&Next"));
|
|
setButtonText(QWizard::FinishButton, tr("&Finish"));
|
|
#endif
|
|
setButtonText(QWizard::CancelButton, tr("&Cancel"));
|
|
|
|
dynamic_cast<QPushButton*>(button(QWizard::NextButton))->setDefault(true);
|
|
|
|
#ifdef Q_WS_MAC
|
|
resize(sizeHint() * 1.25);
|
|
#else
|
|
resize(sizeHint());
|
|
#endif
|
|
}
|
|
|
|
Gui::~Gui()
|
|
{
|
|
delete d;
|
|
}
|
|
|
|
void Gui::setAutomatedPageSwitchEnabled(bool request)
|
|
{
|
|
d->autoSwitchPage = request;
|
|
}
|
|
|
|
void Gui::clickButton(int wb, int delay)
|
|
{
|
|
if (QAbstractButton* b = button(static_cast<QWizard::WizardButton>(wb) )) {
|
|
QTimer::singleShot(delay, b, SLOT(click()));
|
|
} else {
|
|
//TODO we should probably abort immediately here (faulty test script)
|
|
verbose() << "Button " << wb << " not found!" << std::endl;
|
|
}
|
|
}
|
|
|
|
/*!
|
|
Loads a script to perform the installation non-interactively.
|
|
@throws QInstaller::Error if the script is not readable/cannot be parsed
|
|
*/
|
|
void Gui::loadControlScript(const QString& scriptPath)
|
|
{
|
|
QFile file(scriptPath);
|
|
if (!file.open(QIODevice::ReadOnly)) {
|
|
throw Error(QObject::tr("Could not open the requested script file at %1: %2")
|
|
.arg(scriptPath, file.errorString()));
|
|
}
|
|
|
|
d->controlScriptEngine.globalObject().setProperty(QLatin1String("installer"),
|
|
d->controlScriptEngine.newQObject(m_installer));
|
|
d->controlScriptEngine.globalObject().setProperty(QLatin1String("gui"),
|
|
d->controlScriptEngine.newQObject(this));
|
|
registerMessageBox(&d->controlScriptEngine);
|
|
|
|
#undef REGISTER_BUTTON
|
|
#define REGISTER_BUTTON(x) buttons.setProperty(QLatin1String(#x), \
|
|
d->controlScriptEngine.newVariant(static_cast<int>(QWizard::x)));
|
|
|
|
QScriptValue buttons = d->controlScriptEngine.newArray();
|
|
REGISTER_BUTTON(BackButton)
|
|
REGISTER_BUTTON(NextButton)
|
|
REGISTER_BUTTON(CommitButton)
|
|
REGISTER_BUTTON(FinishButton)
|
|
REGISTER_BUTTON(CancelButton)
|
|
REGISTER_BUTTON(HelpButton)
|
|
REGISTER_BUTTON(CustomButton1)
|
|
REGISTER_BUTTON(CustomButton2)
|
|
REGISTER_BUTTON(CustomButton3)
|
|
|
|
#undef REGISTER_BUTTON
|
|
|
|
d->controlScriptEngine.globalObject().setProperty(QLatin1String("buttons"), buttons);
|
|
|
|
d->controlScriptEngine.evaluate(QLatin1String(file.readAll()), scriptPath);
|
|
if (d->controlScriptEngine.hasUncaughtException()) {
|
|
throw Error(QObject::tr("Exception while loading the control script %1")
|
|
.arg(uncaughtExceptionString(&(d->controlScriptEngine)/*, scriptPath*/)));
|
|
}
|
|
|
|
QScriptValue comp = d->controlScriptEngine.evaluate(QLatin1String("Controller"));
|
|
if (d->controlScriptEngine.hasUncaughtException()) {
|
|
throw Error(QObject::tr("Exception while loading the control script %1")
|
|
.arg(uncaughtExceptionString(&(d->controlScriptEngine)/*, scriptPath*/)));
|
|
}
|
|
|
|
d->controlScript = comp;
|
|
d->controlScript.construct();
|
|
|
|
verbose() << "Loaded control script " << qPrintable(scriptPath) << std::endl;
|
|
}
|
|
|
|
void Gui::triggerControlScriptForCurrentPage()
|
|
{
|
|
slotCurrentPageChanged(currentId());
|
|
}
|
|
|
|
void Gui::slotCurrentPageChanged(int id)
|
|
{
|
|
QMetaObject::invokeMethod(this, "delayedControlScriptExecution", Qt::QueuedConnection,
|
|
Q_ARG(int, id));
|
|
}
|
|
|
|
void Gui::callControlScriptMethod(const QString& methodName)
|
|
{
|
|
QScriptValue method = d->controlScript.property(QLatin1String("prototype")).property(methodName);
|
|
|
|
if (!method.isValid()) {
|
|
verbose() << "Control script callback " << qPrintable(methodName) << " does not exist."
|
|
<< std::endl;
|
|
return;
|
|
}
|
|
|
|
verbose() << "Calling control script callback " << qPrintable(methodName) << std::endl;
|
|
|
|
method.call(d->controlScript);
|
|
|
|
if (d->controlScriptEngine.hasUncaughtException()) {
|
|
qCritical() << uncaughtExceptionString(&(d->controlScriptEngine)
|
|
/*, QLatin1String("control script")*/);
|
|
//TODO handle error
|
|
}
|
|
}
|
|
|
|
void Gui::delayedControlScriptExecution(int id)
|
|
{
|
|
if (Page* const p = qobject_cast<Page*>(page(id))) {
|
|
const QString methodName = p->objectName() + QLatin1String("Callback");
|
|
callControlScriptMethod(methodName);
|
|
}
|
|
}
|
|
|
|
bool Gui::event(QEvent* event)
|
|
{
|
|
switch(event->type()) {
|
|
case QEvent::LanguageChange:
|
|
emit languageChanged();
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return QWizard::event(event);
|
|
}
|
|
|
|
void Gui::wizardPageInsertionRequested(QWidget* widget, Installer::WizardPage page)
|
|
{
|
|
// just in case it was already in there...
|
|
wizardPageRemovalRequested(widget);
|
|
|
|
// now find a suitable ID lower than page
|
|
int p = static_cast<int>(page) - 1;
|
|
while (QWizard::page(p) != 0)
|
|
--p;
|
|
// add it
|
|
setPage(p, new DynamicInstallerPage(widget, m_installer));
|
|
}
|
|
|
|
void Gui::wizardPageRemovalRequested(QWidget* widget)
|
|
{
|
|
const QList<int> pages = pageIds();
|
|
for (QList<int>::const_iterator it = pages.begin(); it != pages.end(); ++it) {
|
|
QWizardPage* const p = page(*it);
|
|
DynamicInstallerPage* const dynamicPage = dynamic_cast<DynamicInstallerPage*>(p);
|
|
if (dynamicPage == 0)
|
|
continue;
|
|
if (dynamicPage->widget() != widget)
|
|
continue;
|
|
removePage(*it);
|
|
}
|
|
}
|
|
|
|
void Gui::wizardWidgetInsertionRequested(QWidget* widget, Installer::WizardPage page)
|
|
{
|
|
Q_ASSERT(widget);
|
|
if (QWizardPage* const p = QWizard::page(page))
|
|
p->layout()->addWidget(widget);
|
|
}
|
|
|
|
void Gui::wizardWidgetRemovalRequested(QWidget* widget)
|
|
{
|
|
Q_ASSERT(widget);
|
|
widget->setParent(0);
|
|
}
|
|
|
|
void Gui::wizardPageVisibilityChangeRequested(bool visible, int p)
|
|
{
|
|
if (visible && page(p) == 0) {
|
|
setPage(p, defaultPages[p]);
|
|
} else if (!visible && page(p) != 0) {
|
|
defaultPages[p] = page(p);
|
|
removePage(p);
|
|
}
|
|
}
|
|
|
|
Page* Gui::page(int pageId) const
|
|
{
|
|
return qobject_cast<Page*>(QWizard::page(pageId));
|
|
}
|
|
|
|
QWidget* Gui::pageWidgetByObjectName(const QString& name) const
|
|
{
|
|
const QList<int> ids = pageIds();
|
|
foreach (const int i, ids) {
|
|
Page* const p = qobject_cast<Page*>(page(i));
|
|
if (p && p->objectName() == name) {
|
|
// For dynamic pages, return the contained widget (as read from the UI file), not the
|
|
// wrapper page
|
|
if (DynamicInstallerPage* dp = dynamic_cast<DynamicInstallerPage*>(p))
|
|
return dp->widget();
|
|
return p;
|
|
}
|
|
}
|
|
verbose() << "No page found for object name " << name << std::endl;
|
|
return 0;
|
|
}
|
|
|
|
QWidget* Gui::currentPageWidget() const
|
|
{
|
|
return currentPage();
|
|
}
|
|
|
|
void Gui::cancelButtonClicked()
|
|
{
|
|
const bool doIgnore = m_installer->isInstaller()
|
|
? m_installer->status() == Installer::Canceled
|
|
|| m_installer->status() == Installer::Success
|
|
: m_installer->status() == Installer::Canceled;
|
|
|
|
//if the user canceled (all modes) or the installation is finished (installer mode only), ignore
|
|
//clicks on cancel.
|
|
if (doIgnore)
|
|
return;
|
|
|
|
//close the manager without asking if nothing was modified, always ask for the installer
|
|
if (!m_installer->isInstaller() && !d->modified) {
|
|
QDialog::reject();
|
|
return;
|
|
}
|
|
|
|
Page* const page = qobject_cast<Page*>(currentPage());
|
|
verbose() << "CANCEL CLICKED" << currentPage() << page << std::endl;
|
|
if (page && page->isInterruptible()) {
|
|
const QMessageBox::StandardButton bt =
|
|
MessageBoxHandler::question(MessageBoxHandler::currentBestSuitParent(),
|
|
QLatin1String("cancelInstallation"), tr("Question"),
|
|
tr("Do you want to abort the %1 process?").arg(m_installer->isUninstaller()
|
|
? tr("uninstallation") : tr("installation")), QMessageBox::Yes | QMessageBox::No);
|
|
if (bt == QMessageBox::Yes)
|
|
emit interrupted();
|
|
} else {
|
|
const QMessageBox::StandardButton bt =
|
|
MessageBoxHandler::question(MessageBoxHandler::currentBestSuitParent(),
|
|
QLatin1String("cancelInstallation"), tr("Question"),
|
|
tr("Do you want to abort the %1 application?").arg(m_installer->isUninstaller()
|
|
? tr("uninstaller") : tr("installer")), QMessageBox::Yes | QMessageBox::No);
|
|
if (bt == QMessageBox::Yes)
|
|
QDialog::reject();
|
|
}
|
|
}
|
|
|
|
void Gui::rejectWithoutPrompt()
|
|
{
|
|
m_installer->setCanceled();
|
|
QDialog::reject();
|
|
}
|
|
|
|
void Gui::reject()
|
|
{
|
|
cancelButtonClicked();
|
|
}
|
|
|
|
void Gui::setModified(bool value)
|
|
{
|
|
d->modified = value;
|
|
}
|
|
|
|
void Gui::showFinishedPage()
|
|
{
|
|
verbose() << "SHOW FINISHED PAGE" << std::endl;
|
|
if (d->autoSwitchPage)
|
|
next();
|
|
else
|
|
dynamic_cast< QPushButton*>(button(QWizard::CancelButton))->setEnabled(false);
|
|
}
|
|
|
|
|
|
// -- Page
|
|
|
|
Page::Page(Installer *installer)
|
|
: m_installer(installer)
|
|
, m_fresh(true)
|
|
, m_complete(true)
|
|
{
|
|
// otherwise the colors will screw up
|
|
setSubTitle(QLatin1String(" "));
|
|
}
|
|
|
|
Installer *Page::installer() const
|
|
{
|
|
return m_installer;
|
|
}
|
|
|
|
QPixmap Page::watermarkPixmap() const
|
|
{
|
|
return QPixmap(m_installer->value(QLatin1String("WatermarkPixmap")));
|
|
}
|
|
|
|
QPixmap Page::logoPixmap() const
|
|
{
|
|
return QPixmap(m_installer->value(QLatin1String("LogoPixmap")));
|
|
}
|
|
|
|
QString Page::productName() const
|
|
{
|
|
return m_installer->value(QLatin1String("ProductName"));
|
|
}
|
|
|
|
bool Page::isComplete() const
|
|
{
|
|
return m_complete;
|
|
}
|
|
|
|
void Page::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();
|
|
}
|
|
}
|
|
}
|
|
emit completeChanged();
|
|
}
|
|
|
|
void Page::insertWidget(QWidget *widget, const QString &siblingName, int offset)
|
|
{
|
|
QWidget *sibling = findChild<QWidget *>(siblingName);
|
|
QWidget *parent = sibling ? sibling->parentWidget() : 0;
|
|
QLayout *layout = parent ? parent->layout() : 0;
|
|
QBoxLayout *blayout = qobject_cast<QBoxLayout *>(layout);
|
|
|
|
if (blayout) {
|
|
const int index = blayout->indexOf(sibling) + offset;
|
|
blayout->insertWidget(index, widget);
|
|
}
|
|
}
|
|
|
|
QWidget *Page::findWidget(const QString &objectName) const
|
|
{
|
|
return findChild<QWidget *>(objectName);
|
|
}
|
|
|
|
/*!
|
|
\reimp
|
|
\Overwritten to support some kind of initializePage() in the case the wizard has been set
|
|
to QWizard::IndependentPages. If that option has been set, initializePage() would be only called
|
|
once. So we provide entering() and leaving() based on this overwritten function.
|
|
*/
|
|
void Page::setVisible(bool visible)
|
|
{
|
|
QWizardPage::setVisible(visible);
|
|
qApp->processEvents();
|
|
|
|
if (m_fresh && !visible) {
|
|
// this is only hit once when the page gets added to the wizard
|
|
m_fresh = false;
|
|
return;
|
|
}
|
|
|
|
if (visible)
|
|
entering();
|
|
else
|
|
leaving();
|
|
}
|
|
|
|
int Page::nextId() const
|
|
{
|
|
return QWizardPage::nextId();
|
|
}
|
|
|
|
|
|
// -- QInstallerIntroductionPage
|
|
|
|
IntroductionPage::IntroductionPage(Installer *inst)
|
|
: Page(inst)
|
|
, m_widget(0)
|
|
{
|
|
setObjectName(QLatin1String("IntroductionPage"));
|
|
setTitle(tr("Setup - %1").arg(productName()));
|
|
setPixmap(QWizard::WatermarkPixmap, watermarkPixmap());
|
|
setSubTitle(QString());
|
|
|
|
m_msgLabel = new QLabel(this);
|
|
m_msgLabel->setObjectName(QLatin1String("MessageLabel"));
|
|
m_msgLabel->setWordWrap(true);
|
|
m_msgLabel->setText(tr("Welcome to the %1 Setup Wizard.")
|
|
.arg(productName()));
|
|
|
|
QVBoxLayout *layout = new QVBoxLayout(this);
|
|
setLayout(layout);
|
|
layout->addWidget(m_msgLabel);
|
|
layout->addItem(new QSpacerItem(20, 20, QSizePolicy::Minimum, QSizePolicy::Expanding));
|
|
}
|
|
|
|
void IntroductionPage::setText(const QString &text)
|
|
{
|
|
m_msgLabel->setText(text);
|
|
}
|
|
|
|
void IntroductionPage::setWidget(QWidget *w)
|
|
{
|
|
if (m_widget) {
|
|
layout()->removeWidget(m_widget);
|
|
delete m_widget;
|
|
}
|
|
m_widget = w;
|
|
if (m_widget)
|
|
static_cast<QVBoxLayout*>(layout())->addWidget(m_widget, 1);
|
|
}
|
|
|
|
|
|
// -- LicenseAgreementPage
|
|
|
|
LicenseAgreementPage::LicenseAgreementPage(Installer *inst)
|
|
: Page(inst)
|
|
{
|
|
setTitle(tr("License Agreement"));
|
|
setSubTitle(tr("Please read the following license agreement(s). You must accept the terms contained "
|
|
"in these agreement(s) before continuing with the installation."));
|
|
|
|
setPixmap(QWizard::LogoPixmap, logoPixmap());
|
|
setPixmap(QWizard::WatermarkPixmap, QPixmap());
|
|
setObjectName(QLatin1String("LicenseAgreementPage"));
|
|
|
|
#if !defined(Q_WS_MAC)
|
|
QGroupBox *licenseBox = new QGroupBox(this);
|
|
licenseBox->setObjectName(QString::fromUtf8("licenseBox"));
|
|
#endif
|
|
|
|
m_licenseListWidget = new QListWidget(this);
|
|
connect(m_licenseListWidget, SIGNAL(currentItemChanged(QListWidgetItem *, QListWidgetItem *)),
|
|
this, SLOT(currentItemChanged(QListWidgetItem *)));
|
|
|
|
QSizePolicy sizePolicy(QSizePolicy::Maximum, QSizePolicy::Expanding);
|
|
sizePolicy.setHeightForWidth(m_licenseListWidget->sizePolicy().hasHeightForWidth());
|
|
m_licenseListWidget->setSizePolicy(sizePolicy);
|
|
|
|
m_textBrowser = new QTextBrowser(this);
|
|
m_textBrowser->setReadOnly(true);
|
|
m_textBrowser->setOpenLinks(false);
|
|
m_textBrowser->setOpenExternalLinks(true);
|
|
connect(m_textBrowser, SIGNAL(anchorClicked(QUrl)), this, SLOT(openLicenseUrl(QUrl)));
|
|
|
|
QSizePolicy sizePolicy2(QSizePolicy::Minimum, QSizePolicy::Expanding);
|
|
sizePolicy2.setHeightForWidth(m_textBrowser->sizePolicy().hasHeightForWidth());
|
|
m_textBrowser->setSizePolicy(sizePolicy2);
|
|
|
|
QHBoxLayout *licenseBoxLayout = new QHBoxLayout();
|
|
licenseBoxLayout->addWidget(m_licenseListWidget);
|
|
licenseBoxLayout->addWidget(m_textBrowser);
|
|
|
|
QVBoxLayout *layout = new QVBoxLayout(this);
|
|
#if !defined(Q_WS_MAC)
|
|
layout->addWidget(licenseBox);
|
|
licenseBox->setLayout(licenseBoxLayout);
|
|
#else
|
|
layout->addLayout(licenseBoxLayout);
|
|
#endif
|
|
|
|
m_acceptRadioButton = new QRadioButton(this);
|
|
m_acceptRadioButton->setObjectName(QString::fromUtf8("acceptLicenseRB"));
|
|
|
|
m_acceptRadioButton->setShortcut(QKeySequence(tr("Alt+A", "agree license")));
|
|
QLabel *acceptLabel = new QLabel(tr("I h<u>a</u>ve read and agree to the following terms contained in "
|
|
"the license agreements accompanying the Qt SDK and additional items. I agree that my use of "
|
|
"the Qt SDK is governed by the terms and conditions contained in these license agreements."));
|
|
acceptLabel->setWordWrap(true);
|
|
ClickForwarder* acceptClickForwarder = new ClickForwarder(m_acceptRadioButton);
|
|
acceptLabel->installEventFilter(acceptClickForwarder);
|
|
|
|
m_rejectRadioButton = new QRadioButton(this);
|
|
m_acceptRadioButton->setObjectName(QString::fromUtf8("rejectLicenseRB"));
|
|
|
|
m_rejectRadioButton->setShortcut(QKeySequence(tr("Alt+N", "do not agree license")));
|
|
QLabel *rejectLabel = new QLabel(tr("I do <u>n</u>ot accept the terms and conditions of the above "
|
|
"listed license agreements. Please note by checking the box, you must cancel the "
|
|
"installation or downloading the Qt SDK and must destroy all copies, or portions thereof, "
|
|
"of the Qt SDK in your possessions."));
|
|
rejectLabel->setWordWrap(true);
|
|
ClickForwarder* rejectClickForwarder = new ClickForwarder(m_rejectRadioButton);
|
|
rejectLabel->installEventFilter(rejectClickForwarder);
|
|
|
|
QSizePolicy sizePolicy3(QSizePolicy::Preferred, QSizePolicy::Minimum);
|
|
sizePolicy3.setHeightForWidth(rejectLabel->sizePolicy().hasHeightForWidth());
|
|
sizePolicy3.setVerticalStretch(0);
|
|
sizePolicy3.setHorizontalStretch(0);
|
|
|
|
acceptLabel->setSizePolicy(sizePolicy3);
|
|
rejectLabel->setSizePolicy(sizePolicy3);
|
|
|
|
#if defined(Q_WS_X11) || defined(Q_WS_MAC)
|
|
QFont labelFont(font());
|
|
labelFont.setPixelSize(9);
|
|
|
|
acceptLabel->setFont(labelFont);
|
|
rejectLabel->setFont(labelFont);
|
|
#endif
|
|
|
|
QGridLayout *gridLayout = new QGridLayout;
|
|
gridLayout->setColumnStretch(1, 1);
|
|
gridLayout->addWidget(m_acceptRadioButton, 0, 0);
|
|
gridLayout->addWidget(acceptLabel, 0, 1);
|
|
gridLayout->addWidget(m_rejectRadioButton, 1, 0);
|
|
gridLayout->addWidget(rejectLabel, 1, 1);
|
|
layout->addLayout(gridLayout);
|
|
|
|
connect(m_acceptRadioButton, SIGNAL(toggled(bool)), this, SIGNAL(completeChanged()));
|
|
connect(m_rejectRadioButton, SIGNAL(toggled(bool)), this, SIGNAL(completeChanged()));
|
|
|
|
m_rejectRadioButton->setChecked(true);
|
|
}
|
|
|
|
void LicenseAgreementPage::entering()
|
|
{
|
|
m_licenseListWidget->clear();
|
|
m_textBrowser->setText(QLatin1String(""));
|
|
|
|
RunMode runMode = installer()->runMode();
|
|
QList<QInstaller::Component*> components = installer()->components(false, runMode);
|
|
|
|
QInstaller::Component *rootComponent = 0;
|
|
// TODO: this needs to be fixed once we support several root components
|
|
foreach (QInstaller::Component* root, components) {
|
|
if (root->isInstalled())
|
|
continue;
|
|
|
|
const QHash<QString, QPair<QString, QString> > &hash = root->licenses();
|
|
if (!hash.isEmpty()) {
|
|
addLicenseItem(hash);
|
|
rootComponent = root;
|
|
}
|
|
}
|
|
|
|
components = installer()->componentsToInstall(runMode);
|
|
foreach (QInstaller::Component* component, components) {
|
|
if (rootComponent != component && !component->isInstalled())
|
|
addLicenseItem(component->licenses());
|
|
}
|
|
|
|
if (m_licenseListWidget->count() > 0)
|
|
m_licenseListWidget->setCurrentItem(m_licenseListWidget->item(0));
|
|
}
|
|
|
|
bool LicenseAgreementPage::isComplete() const
|
|
{
|
|
return m_acceptRadioButton->isChecked();
|
|
}
|
|
|
|
void LicenseAgreementPage::openLicenseUrl(const QUrl &url)
|
|
{
|
|
QDesktopServices::openUrl(url);
|
|
}
|
|
|
|
void LicenseAgreementPage::currentItemChanged(QListWidgetItem *current)
|
|
{
|
|
if (current)
|
|
m_textBrowser->setText(current->data(Qt::UserRole).toString());
|
|
}
|
|
|
|
void LicenseAgreementPage::addLicenseItem(const QHash<QString, QPair<QString, QString> > &hash)
|
|
{
|
|
for (QHash<QString, QPair<QString, QString> >::const_iterator it = hash.begin();
|
|
it != hash.end(); ++it) {
|
|
QListWidgetItem *item = new QListWidgetItem(it.key(), m_licenseListWidget);
|
|
item->setData(Qt::UserRole, it.value().second);
|
|
}
|
|
}
|
|
|
|
|
|
// -- ComponentSelectionPage::Private
|
|
|
|
class ComponentSelectionPage::Private : public QObject
|
|
{
|
|
Q_OBJECT
|
|
|
|
public:
|
|
Private(ComponentSelectionPage *qq, Installer *installer)
|
|
: q(qq)
|
|
, m_installer(installer)
|
|
, m_treeView(new QTreeView(q))
|
|
, m_allModel(new ComponentModel(5, m_installer))
|
|
, m_updaterModel(new ComponentModel(5, m_installer))
|
|
{
|
|
m_treeView->setObjectName(QLatin1String("ComponentTreeView"));
|
|
|
|
int i = 0;
|
|
m_currentModel = m_allModel;
|
|
ComponentModel *list[] = { m_allModel, m_updaterModel, 0 };
|
|
while (ComponentModel *model = list[i++]) {
|
|
connect(model, SIGNAL(defaultCheckStateChanged(bool)), q, SLOT(setModified(bool)));
|
|
model->setHeaderData(ComponentModelHelper::NameColumn, Qt::Horizontal, tr("Name"));
|
|
model->setHeaderData(ComponentModelHelper::InstalledVersionColumn, Qt::Horizontal,
|
|
tr("Installed Version"));
|
|
model->setHeaderData(ComponentModelHelper::NewVersionColumn, Qt::Horizontal, tr("New Version"));
|
|
model->setHeaderData(ComponentModelHelper::UncompressedSizeColumn, Qt::Horizontal, tr("Size"));
|
|
}
|
|
|
|
QHBoxLayout *hlayout = new QHBoxLayout;
|
|
hlayout->addWidget(m_treeView, 3);
|
|
|
|
m_descriptionLabel = new QLabel(q);
|
|
m_descriptionLabel->setWordWrap(true);
|
|
m_descriptionLabel->setObjectName(QLatin1String("ComponentDescriptionLabel"));
|
|
|
|
QVBoxLayout *vlayout = new QVBoxLayout;
|
|
vlayout->addWidget(m_descriptionLabel);
|
|
|
|
m_sizeLabel = new QLabel(q);
|
|
m_sizeLabel->setWordWrap(true);
|
|
vlayout->addWidget(m_sizeLabel);
|
|
m_sizeLabel->setObjectName(QLatin1String("ComponentSizeLabel"));
|
|
|
|
vlayout->addSpacerItem(new QSpacerItem(1, 1, QSizePolicy::MinimumExpanding,
|
|
QSizePolicy::MinimumExpanding));
|
|
hlayout->addLayout(vlayout, 2);
|
|
|
|
QVBoxLayout *layout = new QVBoxLayout(q);
|
|
layout->addLayout(hlayout, 1);
|
|
|
|
QPushButton *button;
|
|
hlayout = new QHBoxLayout;
|
|
if (m_installer->isInstaller()) {
|
|
hlayout->addWidget(button = new QPushButton(tr("Default")));
|
|
connect(button, SIGNAL(clicked()), this, SLOT(selectDefault()));
|
|
}
|
|
hlayout->addWidget(button = new QPushButton(tr("Select All")));
|
|
connect(button, SIGNAL(clicked()), this, SLOT(selectAll()));
|
|
hlayout->addWidget(button = new QPushButton(tr("Deselect All")));
|
|
connect(button, SIGNAL(clicked()), this, SLOT(deselectAll()));
|
|
hlayout->addSpacerItem(new QSpacerItem(1, 1, QSizePolicy::MinimumExpanding,
|
|
QSizePolicy::MinimumExpanding));
|
|
layout->addLayout(hlayout);
|
|
|
|
connect(m_installer, SIGNAL(finishAllComponentsReset()), this, SLOT(allComponentsChanged()),
|
|
Qt::QueuedConnection);
|
|
connect(m_installer, SIGNAL(finishUpdaterComponentsReset()), this, SLOT(updaterComponentsChanged()),
|
|
Qt::QueuedConnection);
|
|
}
|
|
|
|
void updateTreeView()
|
|
{
|
|
m_currentModel = m_installer->isUpdater() ? m_updaterModel : m_allModel;
|
|
m_treeView->setModel(m_currentModel);
|
|
|
|
if (m_installer->isInstaller()) {
|
|
m_treeView->setHeaderHidden(true);
|
|
for (int i = 1; i < m_currentModel->columnCount(); ++i)
|
|
m_treeView->hideColumn(i);
|
|
} else {
|
|
m_treeView->header()->setStretchLastSection(true);
|
|
for (int i = 0; i < m_currentModel->columnCount(); ++i)
|
|
m_treeView->resizeColumnToContents(i);
|
|
}
|
|
|
|
bool hasChildren = false;
|
|
const int rowCount = m_currentModel->rowCount();
|
|
for (int row = 0; row < rowCount && !hasChildren; ++row)
|
|
hasChildren = m_currentModel->hasChildren(m_currentModel->index(row, 0));
|
|
m_treeView->setRootIsDecorated(hasChildren);
|
|
|
|
disconnect(m_treeView->selectionModel(), SIGNAL(currentChanged(QModelIndex, QModelIndex)),
|
|
this, SLOT(currentChanged(QModelIndex)));
|
|
connect(m_treeView->selectionModel(), SIGNAL(currentChanged(QModelIndex, QModelIndex)),
|
|
this, SLOT(currentChanged(QModelIndex)));
|
|
|
|
m_treeView->setCurrentIndex(m_currentModel->index(0, 0));
|
|
m_treeView->setExpanded(m_currentModel->index(0, 0), true);
|
|
}
|
|
|
|
public slots:
|
|
void currentChanged(const QModelIndex ¤t)
|
|
{
|
|
m_sizeLabel->clear();
|
|
m_descriptionLabel->clear();
|
|
|
|
if (current.isValid()) {
|
|
m_descriptionLabel->setText(m_currentModel->data(m_currentModel->index(current.row(),
|
|
ComponentModelHelper::NameColumn, current.parent()), Qt::ToolTipRole).toString());
|
|
if (!m_installer->isUninstaller()) {
|
|
m_sizeLabel->setText(tr("This component will occupy approximately %1 on your "
|
|
"harddisk.").arg(m_currentModel->data(m_currentModel->index(current.row(),
|
|
ComponentModelHelper::UncompressedSizeColumn, current.parent())).toString()));
|
|
}
|
|
}
|
|
}
|
|
|
|
void selectAll()
|
|
{
|
|
m_currentModel->selectAll();
|
|
}
|
|
|
|
void deselectAll()
|
|
{
|
|
m_currentModel->deselectAll();
|
|
}
|
|
|
|
void selectDefault()
|
|
{
|
|
m_currentModel->selectDefault();
|
|
}
|
|
|
|
private slots:
|
|
void allComponentsChanged()
|
|
{
|
|
m_allModel->setRootComponents(m_installer->components(false, AllMode));
|
|
}
|
|
|
|
void updaterComponentsChanged()
|
|
{
|
|
m_updaterModel->setRootComponents(m_installer->components(false, UpdaterMode));
|
|
}
|
|
|
|
public:
|
|
ComponentSelectionPage *q;
|
|
Installer *m_installer;
|
|
QTreeView *m_treeView;
|
|
ComponentModel *m_allModel;
|
|
ComponentModel *m_updaterModel;
|
|
ComponentModel *m_currentModel;
|
|
QLabel *m_sizeLabel;
|
|
QLabel *m_descriptionLabel;
|
|
};
|
|
|
|
|
|
// -- ComponentSelectionPage
|
|
|
|
/*!
|
|
\class QInstaller::ComponentSelectionPage
|
|
On this page the user can select and deselect what he wants to be installed.
|
|
*/
|
|
ComponentSelectionPage::ComponentSelectionPage(Installer* installer)
|
|
: Page(installer)
|
|
, d(new Private(this, installer))
|
|
{
|
|
setTitle(tr("Select Components"));
|
|
setPixmap(QWizard::LogoPixmap, logoPixmap());
|
|
setPixmap(QWizard::WatermarkPixmap, QPixmap());
|
|
setObjectName(QLatin1String("ComponentSelectionPage"));
|
|
|
|
if (installer->isInstaller())
|
|
setSubTitle(tr("Please select the components you want to install."));
|
|
if (installer->isUninstaller())
|
|
setSubTitle(tr("Please select the components you want to uninstall."));
|
|
|
|
if (installer->isUpdater())
|
|
setSubTitle(tr("Please select the components you want to update."));
|
|
if (installer->isPackageManager())
|
|
setSubTitle(tr("Please (de)select the components you want to (un)install."));
|
|
}
|
|
|
|
ComponentSelectionPage::~ComponentSelectionPage()
|
|
{
|
|
delete d;
|
|
}
|
|
|
|
void ComponentSelectionPage::entering()
|
|
{
|
|
d->updateTreeView();
|
|
|
|
setModified(isComplete());
|
|
wizard()->button(QWizard::CancelButton)->setEnabled(true);
|
|
}
|
|
|
|
void ComponentSelectionPage::selectAll()
|
|
{
|
|
d->selectAll();
|
|
}
|
|
|
|
void ComponentSelectionPage::deselectAll()
|
|
{
|
|
d->deselectAll();
|
|
}
|
|
|
|
void ComponentSelectionPage::selectDefault()
|
|
{
|
|
if (installer()->isInstaller())
|
|
d->selectDefault();
|
|
}
|
|
|
|
/*!
|
|
Selects the component with /a id in the component tree.
|
|
*/
|
|
void ComponentSelectionPage::selectComponent(const QString& id)
|
|
{
|
|
const QModelIndex &idx = d->m_currentModel->indexFromComponentName(id);
|
|
if (idx.isValid())
|
|
d->m_allModel->setData(idx, Qt::Checked, Qt::CheckStateRole);
|
|
}
|
|
|
|
/*!
|
|
Deselects the component with /a id in the component tree.
|
|
*/
|
|
void ComponentSelectionPage::deselectComponent(const QString& id)
|
|
{
|
|
const QModelIndex &idx = d->m_currentModel->indexFromComponentName(id);
|
|
if (idx.isValid())
|
|
d->m_allModel->setData(idx, Qt::Unchecked, Qt::CheckStateRole);
|
|
}
|
|
|
|
void ComponentSelectionPage::setModified(bool modified)
|
|
{
|
|
setComplete(modified);
|
|
setButtonText(QWizard::CancelButton, isComplete() ? tr("&Cancel") : tr("&Close"));
|
|
}
|
|
|
|
bool ComponentSelectionPage::isComplete() const
|
|
{
|
|
if (installer()->isInstaller() || installer()->isUpdater())
|
|
return d->m_currentModel->hasCheckedComponents();
|
|
return !d->m_currentModel->defaultCheckState();
|
|
}
|
|
|
|
|
|
// -- TargetDirectoryPage
|
|
|
|
TargetDirectoryPage::TargetDirectoryPage(Installer *installer)
|
|
: Page(installer)
|
|
{
|
|
setObjectName(QLatin1String("TargetDirectoryPage"));
|
|
setTitle(tr("Installation Folder"));
|
|
setPixmap(QWizard::LogoPixmap, logoPixmap());
|
|
setPixmap(QWizard::WatermarkPixmap, QPixmap());
|
|
|
|
QLabel *msgLabel = new QLabel(this);
|
|
msgLabel->setText(tr("Please specify the folder where %1 "
|
|
"will be installed.").arg(productName()));
|
|
msgLabel->setWordWrap(true);
|
|
msgLabel->setObjectName(QLatin1String("MessageLabel"));
|
|
|
|
m_lineEdit = new QLineEdit(this);
|
|
m_lineEdit->setObjectName(QLatin1String("targetDirectoryLE"));
|
|
|
|
QPushButton *browseButton = new QPushButton(this);
|
|
browseButton->setObjectName(QLatin1String("BrowseButton"));
|
|
browseButton->setText(tr("Browse..."));
|
|
|
|
QVBoxLayout *layout = new QVBoxLayout(this);
|
|
layout->addWidget(msgLabel);
|
|
QHBoxLayout *hlayout = new QHBoxLayout;
|
|
hlayout->addWidget(m_lineEdit);
|
|
hlayout->addWidget(browseButton);
|
|
layout->addLayout(hlayout);
|
|
setLayout(layout);
|
|
|
|
connect(browseButton, SIGNAL(clicked()), this, SLOT(dirRequested()));
|
|
connect(m_lineEdit, SIGNAL(textChanged(QString)), this, SIGNAL(completeChanged()));
|
|
}
|
|
|
|
QString TargetDirectoryPage::targetDir() const
|
|
{
|
|
return m_lineEdit->text();
|
|
}
|
|
|
|
void TargetDirectoryPage::setTargetDir(const QString &dirName)
|
|
{
|
|
m_lineEdit->setText(dirName);
|
|
}
|
|
|
|
void TargetDirectoryPage::initializePage()
|
|
{
|
|
QString targetDir = installer()->value(QLatin1String("TargetDir"));
|
|
if (targetDir.isEmpty()) {
|
|
targetDir = QDir::homePath() + QDir::separator();
|
|
// prevent spaces in the default target directory
|
|
if (targetDir.contains(QLatin1Char(' ')))
|
|
targetDir = QDir::rootPath();
|
|
targetDir += productName().remove(QLatin1Char(' '));
|
|
}
|
|
m_lineEdit->setText(QDir::toNativeSeparators(QDir(targetDir).absolutePath()));
|
|
|
|
Page::initializePage();
|
|
}
|
|
|
|
bool TargetDirectoryPage::validatePage()
|
|
{
|
|
if (targetDir().isEmpty()) {
|
|
MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(),
|
|
QLatin1String("forbiddenTargetDirectory"), tr("Error"),
|
|
tr( "The install directory cannot be empty, please specify a valid folder"), QMessageBox::Ok);
|
|
return false;
|
|
}
|
|
|
|
const QDir dir(targetDir());
|
|
// it exists, but is empty (might be created by the Browse button (getExistingDirectory)
|
|
if (dir.exists() && dir.entryList(QDir::NoDotAndDotDot).isEmpty()) {
|
|
return true;
|
|
} else if (dir.exists() && dir.isReadable()) {
|
|
// it exists, but is not empty
|
|
if (dir == QDir::root()) {
|
|
MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(),
|
|
QLatin1String("forbiddenTargetDirectory"), tr("Error"),
|
|
tr("As the install directory is completely deleted installing in %1 is forbidden")
|
|
.arg(QDir::rootPath()), QMessageBox::Ok);
|
|
return false;
|
|
}
|
|
|
|
QString remove = installer()->value(QLatin1String("RemoveTargetDir"));
|
|
if (!QVariant(remove).toBool())
|
|
return true;
|
|
|
|
return MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(),
|
|
QLatin1String("overwriteTargetDirectory"), tr("Warning"),
|
|
tr("You have selected an existing, non-empty folder for installation.\n"
|
|
"Note that it will be completely wiped on uninstallation of this application.\n"
|
|
"It is not advisable to install into this folder as installation might fail.\n"
|
|
"Do you want to continue?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void TargetDirectoryPage::entering()
|
|
{
|
|
}
|
|
|
|
void TargetDirectoryPage::leaving()
|
|
{
|
|
installer()->setValue(QLatin1String("TargetDir"), targetDir());
|
|
}
|
|
|
|
void TargetDirectoryPage::targetDirSelected()
|
|
{
|
|
}
|
|
|
|
void TargetDirectoryPage::dirRequested()
|
|
{
|
|
QString newDirName = QFileDialog::getExistingDirectory(this, tr("Select Installation Folder"),
|
|
targetDir());
|
|
if (newDirName.isEmpty() || newDirName == targetDir())
|
|
return;
|
|
m_lineEdit->setText(QDir::toNativeSeparators(newDirName));
|
|
}
|
|
|
|
|
|
// -- StartMenuDirectoryPage
|
|
|
|
StartMenuDirectoryPage::StartMenuDirectoryPage(Installer *installer)
|
|
: Page(installer)
|
|
{
|
|
setObjectName(QLatin1String("StartMenuDirectoryPage"));
|
|
setTitle(tr("Start Menu shortcuts"));
|
|
|
|
setPixmap(QWizard::LogoPixmap, logoPixmap());
|
|
setPixmap(QWizard::WatermarkPixmap, QPixmap());
|
|
|
|
QString msg = tr("Select the Start Menu in which you would like to create the program's "
|
|
"shortcuts. You can also enter a name to create a new folder.");
|
|
|
|
QLabel *msgLabel = new QLabel(this);
|
|
msgLabel->setText(msg);
|
|
msgLabel->setWordWrap(true);
|
|
msgLabel->setObjectName(QLatin1String("MessageLabel"));
|
|
|
|
m_lineEdit = new QLineEdit(this);
|
|
m_lineEdit->setObjectName(QLatin1String("LineEdit"));
|
|
|
|
QString startMenuDir = installer->value(QLatin1String("StartMenuDir"));
|
|
if (startMenuDir.isEmpty())
|
|
startMenuDir = productName();
|
|
m_lineEdit->setText(startMenuDir);
|
|
|
|
// grab existing start menu folders
|
|
QSettings user(QLatin1String("HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\"
|
|
"Explorer\\User Shell Folders"), QSettings::NativeFormat);
|
|
//User Shell Folders uses %USERPROFILE%
|
|
startMenuPath = replaceWindowsEnvironmentVariables(user.value(QLatin1String("Programs"),
|
|
QString()).toString());
|
|
installer->setValue(QLatin1String("DesktopDir"),
|
|
replaceWindowsEnvironmentVariables(user.value(QLatin1String("Desktop")).toString()));
|
|
|
|
QDir dir(startMenuPath); // user only dirs
|
|
QStringList dirs = dir.entryList(QDir::AllDirs | QDir::NoDotAndDotDot);
|
|
|
|
if (installer->value(QLatin1String("AllUsers")) == QLatin1String("true")) {
|
|
verbose() << "AllUsers set. Using HKEY_LOCAL_MACHINE" << std::endl;
|
|
QSettings system(QLatin1String("HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows\\"
|
|
"CurrentVersion\\Explorer\\Shell Folders"), QSettings::NativeFormat);
|
|
startMenuPath = system.value(QLatin1String("Common Programs"), QString()).toString();
|
|
installer->setValue(QLatin1String("DesktopDir"),
|
|
system.value(QLatin1String("Desktop")).toString());
|
|
|
|
dir.setPath(startMenuPath); // system only dirs
|
|
dirs += dir.entryList(QDir::AllDirs | QDir::NoDotAndDotDot);
|
|
}
|
|
|
|
verbose() << "StartMenuPath: " << startMenuPath;
|
|
verbose() << "DesktopDir:" << installer->value(QLatin1String("DesktopDir")) << std::endl;
|
|
|
|
m_listWidget = new QListWidget(this);
|
|
if (!dirs.isEmpty()) {
|
|
dirs.removeDuplicates();
|
|
foreach (const QString &dir, dirs)
|
|
new QListWidgetItem(dir, m_listWidget);
|
|
}
|
|
|
|
QVBoxLayout *layout = new QVBoxLayout(this);
|
|
layout->addWidget(msgLabel);
|
|
layout->addWidget(m_lineEdit);
|
|
layout->addWidget(m_listWidget);
|
|
|
|
setLayout(layout);
|
|
|
|
connect(m_listWidget, SIGNAL(currentItemChanged(QListWidgetItem*, QListWidgetItem*)), this,
|
|
SLOT(currentItemChanged(QListWidgetItem*)));
|
|
}
|
|
|
|
QString StartMenuDirectoryPage::startMenuDir() const
|
|
{
|
|
return m_lineEdit->text();
|
|
}
|
|
|
|
void StartMenuDirectoryPage::setStartMenuDir(const QString &startMenuDir)
|
|
{
|
|
m_lineEdit->setText(startMenuDir);
|
|
}
|
|
|
|
void StartMenuDirectoryPage::leaving()
|
|
{
|
|
installer()->setValue(QLatin1String("StartMenuDir"), startMenuPath + QDir::separator()
|
|
+ startMenuDir());
|
|
}
|
|
|
|
void StartMenuDirectoryPage::currentItemChanged(QListWidgetItem* current)
|
|
{
|
|
if (current) {
|
|
QString dir = current->data(Qt::DisplayRole).toString();
|
|
if (!dir.isEmpty())
|
|
dir += QDir::separator();
|
|
setStartMenuDir(dir + installer()->value(QLatin1String("StartMenuDir")));
|
|
}
|
|
}
|
|
|
|
|
|
// -- ReadyForInstallationPage
|
|
|
|
ReadyForInstallationPage::ReadyForInstallationPage(Installer* installer)
|
|
: Page(installer)
|
|
, msgLabel(new QLabel)
|
|
{
|
|
setPixmap(QWizard::LogoPixmap, logoPixmap());
|
|
setPixmap(QWizard::WatermarkPixmap, QPixmap());
|
|
|
|
setObjectName(QLatin1String("ReadyForInstallationPage"));
|
|
|
|
msgLabel->setWordWrap(true);
|
|
msgLabel->setObjectName(QLatin1String("MessageLabel"));
|
|
|
|
QLayout *layout = new QVBoxLayout(this);
|
|
layout->addWidget(msgLabel);
|
|
setLayout(layout);
|
|
}
|
|
|
|
/*!
|
|
\reimp
|
|
*/
|
|
void ReadyForInstallationPage::initializePage()
|
|
{
|
|
setCommitPage(true);
|
|
|
|
if (installer()->isUninstaller()) {
|
|
setTitle(tr("Ready to Uninstall"));
|
|
setButtonText(QWizard::CommitButton, tr("U&ninstall"));
|
|
msgLabel->setText(tr("Setup is now ready to begin removing %1 from your computer.\nThe "
|
|
"program dir %2 will be deleted completely, including all content in that directory!")
|
|
.arg(productName(),
|
|
QDir::toNativeSeparators(QDir(installer()->value(QLatin1String("TargetDir")))
|
|
.absolutePath())));
|
|
} else if (installer()->isPackageManager() || installer()->isUpdater()) {
|
|
setTitle(tr("Ready to Update Packages"));
|
|
setButtonText(QWizard::CommitButton, tr("U&pdate"));
|
|
msgLabel->setText(tr("Setup is now ready to begin updating your installation."));
|
|
} else {
|
|
Q_ASSERT(installer()->isInstaller());
|
|
setTitle(tr("Ready to Install"));
|
|
setButtonText(QWizard::CommitButton, tr("&Install"));
|
|
msgLabel->setText(tr("Setup is now ready to begin installing %1 on your computer.")
|
|
.arg(productName()));
|
|
}
|
|
}
|
|
|
|
inline QString unitSizeText(const qint64 size)
|
|
{
|
|
if (size < 10000)
|
|
return QString::number(size) + QLatin1Char(' ') + ReadyForInstallationPage::tr("Bytes");
|
|
|
|
if (size < 1024 * 10000)
|
|
return QString::number(size / 1024) + QLatin1Char(' ') + ReadyForInstallationPage::tr("kBytes");
|
|
|
|
return QString::number(size / 1024 / 1024) + QLatin1Char(' ') + ReadyForInstallationPage::tr("MBytes");
|
|
}
|
|
|
|
/*!
|
|
\reimp
|
|
*/
|
|
void ReadyForInstallationPage::entering()
|
|
{
|
|
initializePage();
|
|
|
|
if (installer()->isUninstaller())
|
|
return;
|
|
|
|
const QString target = installer()->value(QLatin1String("TargetDir"));
|
|
|
|
const KDSysInfo::Volume vol = KDSysInfo::Volume::fromPath(target);
|
|
const KDSysInfo::Volume tempVolume =
|
|
KDSysInfo::Volume::fromPath(QInstaller::generateTemporaryFileName());
|
|
const bool tempOnSameVolume = vol == tempVolume;
|
|
|
|
//there is no better way atm to check this
|
|
if (vol.size().size() == 0 && vol.availableSpace().size() == 0) {
|
|
verbose() << "Could not determine available space on device " << target
|
|
<< ". Continue silently." << std::endl;
|
|
return;
|
|
}
|
|
|
|
const KDByteSize required(installer()->requiredDiskSpace());
|
|
const KDByteSize tempRequired(installer()->requiredTemporaryDiskSpace());
|
|
|
|
const KDByteSize available = vol.availableSpace();
|
|
const KDByteSize tempAvailable = tempVolume.availableSpace();
|
|
const KDByteSize realRequiredTempSpace = KDByteSize(0.1 * tempRequired + tempRequired);
|
|
const KDByteSize realRequiredSpace = KDByteSize(0.5 * required + required);
|
|
|
|
const bool tempInstFailure = tempOnSameVolume && available < realRequiredSpace
|
|
+ realRequiredTempSpace;
|
|
|
|
verbose() << "Disk space check on " << target << ": required=" << required.size()
|
|
<< " available=" << available.size() << " size=" << vol.size().size() << std::endl;
|
|
|
|
QString tempString;
|
|
if (tempAvailable < realRequiredTempSpace || tempInstFailure) {
|
|
if (tempOnSameVolume) {
|
|
tempString = tr("Not enough diskspace to store temporary files and the installation, "
|
|
"at least %1 are required").arg(unitSizeText(realRequiredTempSpace + realRequiredSpace));
|
|
} else {
|
|
tempString = tr("Not enough diskspace to store temporary files, at least %1 are required")
|
|
.arg(unitSizeText(realRequiredTempSpace.size()));
|
|
}
|
|
}
|
|
|
|
// error on not enough space
|
|
if (available < required || tempInstFailure) {
|
|
if (tempOnSameVolume) {
|
|
msgLabel->setText(tempString);
|
|
} else {
|
|
msgLabel->setText(tr("The volume you selected for installation has insufficient space "
|
|
"for the selected components.\n\nThe installation requires approximately %1.")
|
|
.arg(required.toString()) + tempString);
|
|
}
|
|
setCommitPage(false);
|
|
} else if (available - required < 0.01 * vol.size()) {
|
|
// warn for less than 1% of the volume's space being free
|
|
msgLabel->setText(tr("The volume you selected for installation seems to have sufficient "
|
|
"space for installation,\nbut there will not more than 1% of the volume's space "
|
|
"available afterwards.\n\n%1").arg(msgLabel->text()));
|
|
} else if (available - required < 100*1024*1024LL) {
|
|
// warn for less than 100MB being free
|
|
msgLabel->setText(tr("The volume you selected for installation seems to have sufficient "
|
|
"space for installation,\nbut there will not more than 100 MB available afterwards.\n\n%1")
|
|
.arg(msgLabel->text()));
|
|
}
|
|
}
|
|
|
|
/*!
|
|
\reimp
|
|
*/
|
|
bool ReadyForInstallationPage::isComplete() const
|
|
{
|
|
return isCommitPage();
|
|
}
|
|
|
|
|
|
// -- PerformInstallationPage
|
|
|
|
/*!
|
|
\class QInstaller::PerformInstallationPage
|
|
On this page the user can see on a progressbar how far the current installation is.
|
|
*/
|
|
PerformInstallationPage::PerformInstallationPage(Installer *gui)
|
|
: Page(gui)
|
|
, m_performInstallationForm(new PerformInstallationForm(this))
|
|
{
|
|
setPixmap(QWizard::LogoPixmap, logoPixmap());
|
|
setPixmap(QWizard::WatermarkPixmap, QPixmap());
|
|
|
|
setObjectName(QLatin1String("PerformInstallationPage"));
|
|
setCommitPage(true);
|
|
|
|
m_performInstallationForm->setupUi(this);
|
|
|
|
m_performInstallationForm->setDetailsWidgetVisible(installer()->isInstaller()
|
|
|| installer()->isPackageManager() || installer()->isUpdater());
|
|
connect(ProgressCoordninator::instance(), SIGNAL(detailTextChanged(QString)),
|
|
m_performInstallationForm, SLOT(appendProgressDetails(QString)));
|
|
connect(ProgressCoordninator::instance(), SIGNAL(detailTextResetNeeded()),
|
|
m_performInstallationForm, SLOT(clearDetailsBrowser()));
|
|
|
|
connect(m_performInstallationForm, SIGNAL(showDetailsChanged()), this,
|
|
SLOT(toggleDetailsWereChanged()));
|
|
|
|
connect(installer(), SIGNAL(installationStarted()), this, SLOT(installationStarted()));
|
|
connect(installer(), SIGNAL(uninstallationStarted()), this, SLOT(installationStarted()));
|
|
connect(installer(), SIGNAL(installationFinished()), this, SLOT(installationFinished()));
|
|
connect(installer(), SIGNAL(uninstallationFinished()), this, SLOT(installationFinished()));
|
|
connect(installer(), SIGNAL(titleMessageChanged(QString)), this, SLOT(setTitleMessage(QString)));
|
|
connect(this, SIGNAL(setAutomatedPageSwitchEnabled(bool)), installer(),
|
|
SIGNAL(setAutomatedPageSwitchEnabled(bool)));
|
|
}
|
|
|
|
PerformInstallationPage::~PerformInstallationPage()
|
|
{
|
|
delete m_performInstallationForm;
|
|
}
|
|
|
|
void PerformInstallationPage::entering()
|
|
{
|
|
setComplete(false);
|
|
|
|
if (installer()->isUninstaller()) {
|
|
setTitle(tr("Uninstalling %1").arg(installer()->value(QLatin1String("ProductName"))));
|
|
setButtonText(QWizard::CommitButton, tr("U&ninstall"));
|
|
} else if (installer()->isPackageManager() || installer()->isUpdater()) {
|
|
setTitle(tr("Updating component configuration of %1")
|
|
.arg(installer()->value(QLatin1String("ProductName"))));
|
|
setButtonText(QWizard::CommitButton, tr("U&pdate"));
|
|
} else {
|
|
Q_ASSERT(installer()->isInstaller());
|
|
setTitle(tr("Installing %1").arg(installer()->value(QLatin1String("ProductName"))));
|
|
setButtonText(QWizard::CommitButton, tr("&Install"));
|
|
}
|
|
m_performInstallationForm->enableDetails();
|
|
emit setAutomatedPageSwitchEnabled(true);
|
|
}
|
|
|
|
void PerformInstallationPage::initializePage()
|
|
{
|
|
QWizardPage::initializePage();
|
|
if (installer()->isUninstaller()) {
|
|
wizard()->removePage(QInstaller::Installer::InstallationFinished + 1);
|
|
wizard()->removePage(QInstaller::Installer::InstallationFinished + 2);
|
|
setTitle(tr("Uninstalling %1").arg(installer()->value(QLatin1String("ProductName"))));
|
|
m_commitBtnText = tr("U&ninstall");
|
|
setButtonText(QWizard::CommitButton, m_commitBtnText);
|
|
QTimer::singleShot(30, installer(), SLOT(runUninstaller()));
|
|
} else if (installer()->isPackageManager() || installer()->isUpdater()) {
|
|
setTitle(tr("Updating components of %1").arg(installer()->value(QLatin1String("ProductName"))));
|
|
m_commitBtnText = tr ("U&pdate");
|
|
setButtonText(QWizard::CommitButton, m_commitBtnText);
|
|
QTimer::singleShot(30, installer(), SLOT(runPackageUpdater()));
|
|
} else {
|
|
Q_ASSERT(installer()->isInstaller());
|
|
setTitle(tr("Installing %1").arg(installer()->value(QLatin1String("ProductName"))));
|
|
m_commitBtnText = tr("I&nstall");
|
|
setButtonText(QWizard::CommitButton, m_commitBtnText);
|
|
QTimer::singleShot(30, installer(), SLOT(runInstaller()));
|
|
}
|
|
}
|
|
|
|
void PerformInstallationPage::installationStarted()
|
|
{
|
|
m_performInstallationForm->startUpdateProgress();
|
|
}
|
|
|
|
void PerformInstallationPage::installationFinished()
|
|
{
|
|
m_performInstallationForm->stopUpdateProgress();
|
|
if (!isAutoSwitching()) {
|
|
m_performInstallationForm->scrollDetailsToTheEnd();
|
|
m_performInstallationForm->setDetailsButtonEnabled(false);
|
|
setButtonText(QWizard::NextButton, tr("&Next"));
|
|
setButtonText(QWizard::CommitButton, tr("&Next"));
|
|
setComplete(true);
|
|
}
|
|
}
|
|
|
|
bool PerformInstallationPage::isAutoSwitching() const
|
|
{
|
|
return !m_performInstallationForm->isShowingDetails();
|
|
}
|
|
|
|
void PerformInstallationPage::toggleDetailsWereChanged()
|
|
{
|
|
const bool needAutoSwitching = isAutoSwitching();
|
|
setButtonText(QWizard::CommitButton, needAutoSwitching ? m_commitBtnText : tr("&Next"));
|
|
emit setAutomatedPageSwitchEnabled(needAutoSwitching);
|
|
}
|
|
|
|
void PerformInstallationPage::setTitleMessage(const QString& title)
|
|
{
|
|
setTitle(title);
|
|
}
|
|
|
|
|
|
// -- FinishedPage
|
|
|
|
FinishedPage::FinishedPage(Installer *installer)
|
|
: Page(installer)
|
|
{
|
|
setObjectName(QLatin1String("FinishedPage"));
|
|
setTitle(tr("Completing the %1 Setup Wizard").arg(productName()));
|
|
setPixmap(QWizard::WatermarkPixmap, watermarkPixmap());
|
|
setSubTitle(QString());
|
|
|
|
m_msgLabel = new QLabel(this);
|
|
m_msgLabel->setObjectName(QLatin1String("MessageLabel"));
|
|
m_msgLabel->setWordWrap(true);
|
|
#ifdef Q_WS_MAC
|
|
m_msgLabel->setText(tr("Click Done to exit the Setup Wizard"));
|
|
#else
|
|
m_msgLabel->setText(tr("Click Finish to exit the Setup Wizard"));
|
|
#endif
|
|
|
|
m_runItCheckBox = new QCheckBox(this);
|
|
m_runItCheckBox->setObjectName(QLatin1String("RunItCheckBox"));
|
|
m_runItCheckBox->setChecked(true);
|
|
|
|
QVBoxLayout *layout = new QVBoxLayout(this);
|
|
layout->addWidget(m_msgLabel);
|
|
layout->addWidget(m_runItCheckBox);
|
|
setLayout(layout);
|
|
}
|
|
|
|
void FinishedPage::entering()
|
|
{
|
|
if (wizard()->button(QWizard::BackButton))
|
|
wizard()->button(QWizard::BackButton)->setVisible(false);
|
|
|
|
if (installer()->isPackageManager() || installer()->isUpdater()) {
|
|
setButtonText(QWizard::CommitButton, QLatin1String("&Finish"));
|
|
wizard()->button(QWizard::CommitButton)->disconnect(this);
|
|
connect(wizard()->button(QWizard::CommitButton), SIGNAL(clicked()), this,
|
|
SLOT(handleFinishClicked()));
|
|
if (Gui* par = dynamic_cast<Gui*> (wizard()))
|
|
connect(this, SIGNAL(finishClicked()), par, SIGNAL(finishButtonClicked()));
|
|
}
|
|
|
|
verbose() << "FINISHED ENTERING: " << std::endl;
|
|
|
|
connect(wizard()->button(QWizard::FinishButton), SIGNAL(clicked()), this,
|
|
SLOT(handleFinishClicked()));
|
|
wizard()->setOption(QWizard::NoCancelButton, true);
|
|
|
|
if (installer()->status() == Installer::Success) {
|
|
const QString finishedtext = installer()->value(QLatin1String("FinishedText"));
|
|
if (!finishedtext.isEmpty())
|
|
m_msgLabel->setText(finishedtext);
|
|
|
|
if (!installer()->value(QLatin1String("RunProgram")).isEmpty()) {
|
|
m_runItCheckBox->show();
|
|
m_runItCheckBox->setText(installer()->value(QLatin1String("RunProgramDescription"),
|
|
tr("Run %1 now.").arg(productName())));
|
|
return; // job done
|
|
}
|
|
} else {
|
|
setTitle(tr("The %1 Setup Wizard failed").arg(productName()));
|
|
}
|
|
m_runItCheckBox->hide();
|
|
m_runItCheckBox->setChecked(false);
|
|
}
|
|
|
|
void FinishedPage::initializePage()
|
|
{
|
|
setCommitPage(true);
|
|
if (wizard()->button(QWizard::BackButton))
|
|
wizard()->button(QWizard::BackButton)->setVisible(false);
|
|
|
|
if (installer()->isPackageManager() || installer()->isUpdater()) {
|
|
setButtonText(QWizard::CommitButton, QLatin1String("&Finish"));
|
|
setButtonText(QWizard::NextButton, QLatin1String("&Finish"));
|
|
}
|
|
}
|
|
|
|
void FinishedPage::leaving()
|
|
{
|
|
disconnect(wizard(), SIGNAL(customButtonClicked(int)), this, SLOT(handleFinishClicked()));
|
|
if (installer()->isPackageManager() || installer()->isUpdater()) {
|
|
disconnect(wizard()->button(QWizard::CommitButton), SIGNAL(clicked()), this,
|
|
SLOT(handleFinishClicked()));
|
|
if (Gui* par = dynamic_cast<Gui*> (wizard()))
|
|
disconnect(this, SIGNAL(finishClicked()), par, SIGNAL(finishButtonClicked()));
|
|
}
|
|
}
|
|
|
|
void FinishedPage::handleFinishClicked()
|
|
{
|
|
QString program = installer()->value(QLatin1String("RunProgram"));
|
|
if (!m_runItCheckBox->isChecked() || program.isEmpty())
|
|
return;
|
|
program = installer()->replaceVariables(program);
|
|
verbose() << "STARTING " << program << std::endl;
|
|
QProcess::startDetached(program);
|
|
}
|
|
|
|
|
|
// -- RestartPage
|
|
|
|
RestartPage::RestartPage(Installer *installer)
|
|
: Page(installer)
|
|
{
|
|
setObjectName(QLatin1String("RestartPage"));
|
|
setTitle(tr("Completing the %1 Setup Wizard").arg(productName()));
|
|
setPixmap(QWizard::WatermarkPixmap, watermarkPixmap());
|
|
setSubTitle(QString());
|
|
setFinalPage(false);
|
|
}
|
|
|
|
void RestartPage::entering()
|
|
{
|
|
emit restart();
|
|
wizard()->restart();
|
|
if (wizard()->button(QWizard::FinishButton))
|
|
wizard()->button(QWizard::FinishButton)->setVisible(false);
|
|
wizard()->setOption(QWizard::NoCancelButton, false);
|
|
}
|
|
|
|
void RestartPage::leaving()
|
|
{
|
|
}
|
|
|
|
#include "qinstallergui.moc"
|