/**************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
**
** $QT_BEGIN_LICENSE:LGPL21$
** 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 http://www.qt.io/terms-conditions. For further
** information use the contact form at http://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** As a special exception, The Qt Company gives you certain additional
** rights. These rights are described in The Qt Company LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** $QT_END_LICENSE$
**
**************************************************************************/
#include "packagemanagergui.h"
#include "packagemanagerpage.h"
#include "packagemanagerdynamicinstallerpage.h"

#include "component.h"
#include "componentmodel.h"
#include "errors.h"
#include "fileutils.h"
#include "messageboxhandler.h"
#include "packagemanagercore.h"
#include "progresscoordinator.h"
#include "performinstallationform.h"
#include "settings.h"
#include "utils.h"
#include "scriptengine.h"
#include "productkeycheck.h"

#include "kdsysinfo.h"

#include <QApplication>

#include <QtCore/QDir>
#include <QtCore/QDirIterator>
#include <QtCore/QPair>
#include <QtCore/QProcess>
#include <QtCore/QTimer>
#include <QtCore/QMap>
#include <QtCore/QHash>
#include <QtCore/QTranslator>
#include <QDesktopWidget>

#include <QCheckBox>
#include <QDesktopServices>
#include <QFileDialog>
#include <QGridLayout>
#include <QHBoxLayout>
#include <QHeaderView>
#include <QLabel>
#include <QLineEdit>
#include <QListWidget>
#include <QListWidgetItem>
#include <QMessageBox>
#include <QProgressBar>
#include <QPushButton>
#include <QRadioButton>
#include <QTextBrowser>
#include <QTreeView>
#include <QVBoxLayout>
#include <QShowEvent>

#ifdef Q_OS_WIN
# include <qt_windows.h>
# include <QWinTaskbarButton>
# include <QWinTaskbarProgress>
#endif

using namespace KDUpdater;
using namespace QInstaller;


// -- PackageManagerGui::Private

class PackageManagerGui::Private
{
public:
    Private()
        : m_currentId(-1)
        , m_modified(false)
        , m_autoSwitchPage(true)
        , m_showSettingsButton(false)
        , m_translator(Q_NULLPTR)
    {
        m_wizardButtonTypes.insert(QWizard::BackButton, QLatin1String("QWizard::BackButton"));
        m_wizardButtonTypes.insert(QWizard::NextButton, QLatin1String("QWizard::NextButton"));
        m_wizardButtonTypes.insert(QWizard::CommitButton, QLatin1String("QWizard::CommitButton"));
        m_wizardButtonTypes.insert(QWizard::FinishButton, QLatin1String("QWizard::FinishButton"));
        m_wizardButtonTypes.insert(QWizard::CancelButton, QLatin1String("QWizard::CancelButton"));
        m_wizardButtonTypes.insert(QWizard::HelpButton, QLatin1String("QWizard::HelpButton"));
        m_wizardButtonTypes.insert(QWizard::CustomButton1, QLatin1String("QWizard::CustomButton1"));
        m_wizardButtonTypes.insert(QWizard::CustomButton2, QLatin1String("QWizard::CustomButton2"));
        m_wizardButtonTypes.insert(QWizard::CustomButton3, QLatin1String("QWizard::CustomButton3"));
        m_wizardButtonTypes.insert(QWizard::Stretch, QLatin1String("QWizard::Stretch"));
    }

    QString buttonType(int wizardButton)
    {
        return m_wizardButtonTypes.value(static_cast<QWizard::WizardButton>(wizardButton),
            QLatin1String("unknown button"));
    }

    int m_currentId;
    bool m_modified;
    bool m_autoSwitchPage;
    bool m_showSettingsButton;
    QHash<int, QWizardPage*> m_defaultPages;
    QHash<int, QString> m_defaultButtonText;

    QJSValue m_controlScriptContext;
    QHash<QWizard::WizardButton, QString> m_wizardButtonTypes;

    QString m_currentLang;
    QMap<QString, QString> m_langMap;
    QTranslator *m_translator;
};


// -- PackageManagerGui

/*!
    \class QInstaller::PackageManagerGui
    \inmodule QtInstallerFramework
    \brief The PackageManagerGui class provides the core functionality for non-interactive
        installations.
*/

/*!
    \fn void PackageManagerGui::interrupted()
    \sa {gui::interrupted}{gui.interrupted}
*/

/*!
    \fn void PackageManagerGui::languageChanged()
    \sa {gui::languageChanged}{gui.languageChanged}
*/

/*!
    \fn void PackageManagerGui::finishButtonClicked()
    \sa {gui::finishButtonClicked}{gui.finishButtonClicked}
*/

/*!
    \fn void PackageManagerGui::gotRestarted()
    \sa {gui::gotRestarted}{gui.gotRestarted}
*/

/*!
    \fn void PackageManagerGui::settingsButtonClicked()
    \sa {gui::settingsButtonClicked}{gui.settingsButtonClicked}
*/

/*!
    \fn void PackageManagerGui::setValidatorForCustomPageRequested(QInstaller::Component *component,
        const QString &name,
        const QString &callbackName)

    Sets a validator for the custom page specified by \a name and
    \a callbackName requested by \a component.
*/

/*!
    \fn void PackageManagerGui::packageManagerCore() const

    Returns the package manager core.
*/

/*!
    Constructs a package manager UI with package manager specified by \a core
    and \a parent as parent.
*/
PackageManagerGui::PackageManagerGui(PackageManagerCore *core, QWidget *parent)
    : QWizard(parent)
    , d(new Private)
    , m_core(core)
{   
#if defined(Q_OS_OSX)
       setWindowTitle(m_core->value(scTitle));
#elif defined(Q_OS_WIN)
    if (m_core->isInstaller())
        setWindowTitle(tr("Setup - %1").arg(m_core->value(QLatin1String("ProductName"))));
    else
        setWindowTitle(tr("Maintain - %1").arg(m_core->value(QLatin1String("ProductName"))));
#endif

    setWindowFlags(windowFlags() &~ Qt::WindowContextHelpButtonHint);

#ifndef Q_OS_OSX
    setWindowIcon(QIcon(m_core->settings().installerWindowIcon()));
#else
    setPixmap(QWizard::BackgroundPixmap, m_core->settings().background());
#endif
#ifdef Q_OS_LINUX
    setWizardStyle(QWizard::ModernStyle);
    setSizeGripEnabled(true);
#endif

    if (!m_core->settings().wizardStyle().isEmpty())
        setWizardStyle(getStyle(m_core->settings().wizardStyle()));

    // Fill available languages
    d->m_langMap[QString::fromUtf8("Český")] = QStringLiteral("Czech_Rep_Czech");
    d->m_langMap[QString::fromUtf8("Deutsch")] = QStringLiteral("Germany_German");
    d->m_langMap[QString::fromUtf8("English")] = QStringLiteral("US_English");
    d->m_langMap[QString::fromUtf8("Español")] = QStringLiteral("Spain_Spanish");
    d->m_langMap[QString::fromUtf8("Français")] = QStringLiteral("France_French");
    d->m_langMap[QString::fromUtf8("Italiano")] = QStringLiteral("Italy_Italian");
    d->m_langMap[QString::fromUtf8("日本語")] = QStringLiteral("Japan_Japanese");
    d->m_langMap[QString::fromUtf8("Dutch")] = QStringLiteral("Netherlands_Dutch");
    d->m_langMap[QString::fromUtf8("Polski")] = QStringLiteral("Poland_Polish");
    d->m_langMap[QString::fromUtf8("Português")] = QStringLiteral("Brazil_Portuguese");
    d->m_langMap[QString::fromUtf8("Русский")] = QStringLiteral("Russia_Russian");
    d->m_langMap[QString::fromUtf8("简体中文")] = QStringLiteral("China_Chinese");
    d->m_langMap[QString::fromUtf8("繁体中文")] = QStringLiteral("Taiwan_Chinese");

    const QString defaultLang = QStringLiteral("English");

    setOption(QWizard::NoBackButtonOnStartPage);
    setOption(QWizard::NoBackButtonOnLastPage);

    connect(this, SIGNAL(rejected()), m_core, SLOT(setCanceled()));
    connect(this, SIGNAL(interrupted()), m_core, SLOT(interrupt()));

    // both queued to show the finished page once everything is done
    connect(m_core, SIGNAL(installationFinished()), this, SLOT(showFinishedPage()),
        Qt::QueuedConnection);
    connect(m_core, SIGNAL(uninstallationFinished()), this, SLOT(showFinishedPage()),
        Qt::QueuedConnection);

    connect(this, SIGNAL(currentIdChanged(int)), this, SLOT(currentPageChanged(int)));
    connect(this, SIGNAL(currentIdChanged(int)), m_core, SIGNAL(currentPageChanged(int)));
    connect(button(QWizard::FinishButton), SIGNAL(clicked()), this, SIGNAL(finishButtonClicked()));
    connect(button(QWizard::FinishButton), SIGNAL(clicked()), m_core, SIGNAL(finishButtonClicked()));

    // make sure the QUiLoader's retranslateUi is executed first, then the script
    connect(this, SIGNAL(languageChanged()), m_core, SLOT(languageChanged()), Qt::QueuedConnection);
    connect(this, SIGNAL(languageChanged()), this, SLOT(onLanguageChanged()), Qt::QueuedConnection);

    connect(m_core,
        SIGNAL(wizardPageInsertionRequested(QWidget*,QInstaller::PackageManagerCore::WizardPage)),
        this, SLOT(wizardPageInsertionRequested(QWidget*,QInstaller::PackageManagerCore::WizardPage)));
    connect(m_core, SIGNAL(wizardPageRemovalRequested(QWidget*)), this,
        SLOT(wizardPageRemovalRequested(QWidget*)));
    connect(m_core,
        SIGNAL(wizardWidgetInsertionRequested(QWidget*,QInstaller::PackageManagerCore::WizardPage)),
        this, SLOT(wizardWidgetInsertionRequested(QWidget*,QInstaller::PackageManagerCore::WizardPage)));
    connect(m_core, SIGNAL(wizardWidgetRemovalRequested(QWidget*)), this,
        SLOT(wizardWidgetRemovalRequested(QWidget*)));
    connect(m_core, SIGNAL(wizardPageVisibilityChangeRequested(bool,int)), this,
        SLOT(wizardPageVisibilityChangeRequested(bool,int)), Qt::QueuedConnection);

    connect(m_core,
        SIGNAL(setValidatorForCustomPageRequested(QInstaller::Component*,QString,QString)), this,
        SLOT(setValidatorForCustomPageRequested(QInstaller::Component*,QString,QString)));

    connect(m_core, SIGNAL(setAutomatedPageSwitchEnabled(bool)), this,
        SLOT(setAutomatedPageSwitchEnabled(bool)));

    connect(this, SIGNAL(customButtonClicked(int)), this, SLOT(customButtonClicked(int)));

    for (int i = QWizard::BackButton; i < QWizard::CustomButton1; ++i)
        d->m_defaultButtonText.insert(i, buttonText(QWizard::WizardButton(i)));

    m_core->setGuiObject(this);

    // Get language parametr from the command line
    // that contains the file name (extension is skipped) of the translation
    // to set translation settings of the maintenance tool
    /*
     * QCommandLineParser doesn't work with QProcess command line correctly,
     * that's required some workaround about the arguments. So, it isn't helpful in this case.
     */
    QString lang;
    QStringList args = QCoreApplication::arguments();
    for (auto it = args.begin(); it != args.end(); ++it) {
        if (it->contains("language", Qt::CaseInsensitive)) {
            lang = d->m_langMap.key(*(++it), QString());
        }
    }
    // Set required language
    if (lang.isEmpty())
        lang = m_core->value(scMaintenanceToolLanguage, defaultLang);
    setCurrentLanguage(lang);

    if (m_core->settings().wizardMaximumWidth() > 0)
        setMaximumWidth(m_core->settings().wizardMaximumWidth());
    if (m_core->settings().wizardMaximumHeight() > 0)
        setMaximumHeight(m_core->settings().wizardMaximumHeight());
    if (m_core->settings().wizardDefaultWidth() > 0)
        setMinimumWidth(m_core->settings().wizardDefaultWidth());
    if (m_core->settings().wizardDefaultHeight() > 0)
        setMinimumHeight(m_core->settings().wizardDefaultHeight());

    setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
}

/*!
    Destructs a package manager UI.
*/
PackageManagerGui::~PackageManagerGui()
{
    m_core->setGuiObject(0);
    delete d;
}

/*!
    Returns the style of the package manager UI depending on \a name:

    \list
        \li \c Classic - Classic UI style for Windows 7 and earlier.
        \li \c Modern - Modern UI style for Windows 8.
        \li \c Mac - UI style for OS X.
        \li \c Aero - Aero Peek for Windows 7.
    \endlist
*/
QWizard::WizardStyle PackageManagerGui::getStyle(const QString &name)
{
    if (name == QLatin1String("Classic"))
        return QWizard::ClassicStyle;

    if (name == QLatin1String("Modern"))
        return QWizard::ModernStyle;

    if (name == QLatin1String("Mac"))
        return QWizard::MacStyle;

    if (name == QLatin1String("Aero"))
        return QWizard::AeroStyle;
    return QWizard::ModernStyle;
}

/*!
    Enables automatic page switching when \a request is \c true.
*/
void PackageManagerGui::setAutomatedPageSwitchEnabled(bool request)
{
    d->m_autoSwitchPage = request;
}

/*!
    Returns the default text for the button specified by \a wizardButton.

    \sa {gui::defaultButtonText}{gui.defaultButtonText}
*/
QString PackageManagerGui::defaultButtonText(int wizardButton) const
{
    return d->m_defaultButtonText.value(wizardButton);
}

/*
    Check if we need to "transform" the finish button into a cancel button, caused by the misuse of
    cancel as the finish button on the FinishedPage. This is only a problem if we run as updater or
    package manager, as then there will be two button shown on the last page with the cancel button
    renamed to "Finish".
*/
static bool swapFinishButton(PackageManagerCore *core, int currentId, int button)
{
    if (button != QWizard::FinishButton)
        return false;

    if (currentId != PackageManagerCore::InstallationFinished)
        return false;

    if (core->isInstaller() || core->isUninstaller())
        return false;

    return true;
}

/*!
    Clicks the button specified by \a wb after the delay specified by \a delay.

    \sa {gui::clickButton}{gui.clickButton}
*/
void PackageManagerGui::clickButton(int wb, int delay)
{
    // We need to to swap here, cause scripts expect to call this function with FinishButton on the
    // finish page.
    if (swapFinishButton(m_core, currentId(), wb))
        wb = QWizard::CancelButton;

    if (QAbstractButton *b = button(static_cast<QWizard::WizardButton>(wb)))
        QTimer::singleShot(delay, b, SLOT(click()));
    else
        qWarning() << "Button with type: " << d->buttonType(wb) << "not found!";
}

/*!
    Returns \c true if the button specified by \a wb is enabled. Returns \c false
    if a button of the specified type is not found.

    \sa {gui::isButtonEnabled}{gui.isButtonEnabled}
*/
bool PackageManagerGui::isButtonEnabled(int wb)
{
    // We need to to swap here, cause scripts expect to call this function with FinishButton on the
    // finish page.
    if (swapFinishButton(m_core, currentId(), wb))
            wb = QWizard::CancelButton;

    if (QAbstractButton *b = button(static_cast<QWizard::WizardButton>(wb)))
        return b->isEnabled();

    qWarning() << "Button with type: " << d->buttonType(wb) << "not found!";
    return false;
}

/*!
    Sets a validator for the custom page specified by \a name and
    \a callbackName requested by \a component.
*/
void PackageManagerGui::setValidatorForCustomPageRequested(Component *component,
    const QString &name, const QString &callbackName)
{
    component->setValidatorCallbackName(callbackName);

    const QString componentName = QLatin1String("Dynamic") + name;
    const QList<int> ids = pageIds();
    foreach (const int i, ids) {
        PackageManagerPage *const p = qobject_cast<PackageManagerPage*> (page(i));
        if (p && p->objectName() == componentName) {
            p->setValidatePageComponent(component);
            return;
        }
    }
}

/*!
    Loads the script specified by \a scriptPath to perform the installation non-interactively.
    Throws QInstaller::Error if the script is not readable or it cannot be
    parsed.
*/
void PackageManagerGui::loadControlScript(const QString &scriptPath)
{
    d->m_controlScriptContext = m_core->controlScriptEngine()->loadInContext(
        QLatin1String("Controller"), scriptPath);
    qDebug() << "Loaded control script" << scriptPath;
}

/*!
    Calls the control script method specified by \a methodName.
*/
void PackageManagerGui::callControlScriptMethod(const QString &methodName)
{
    if (d->m_controlScriptContext.isUndefined())
        return;
    try {
        const QJSValue returnValue = m_core->controlScriptEngine()->callScriptMethod(
            d->m_controlScriptContext, methodName);
        if (returnValue.isUndefined()) {
            qDebug() << "Control script callback" << methodName << "does not exist.";
            return;
        }
    } catch (const QInstaller::Error &e) {
        qCritical() << qPrintable(e.message());
    }
}

/*!
    Executes the control script on the page specified by \a pageId.
*/
void PackageManagerGui::executeControlScript(int pageId)
{
    if (PackageManagerPage *const p = qobject_cast<PackageManagerPage*> (page(pageId)))
        callControlScriptMethod(p->objectName() + QLatin1String("Callback"));
}

/*!
    Replaces the default button text with translated text when the application
    language changes.
*/
void PackageManagerGui::onLanguageChanged()
{
    setButtonText(QWizard::BackButton, tr("Back"));
    setButtonText(QWizard::NextButton, tr("Next"));
    setButtonText(QWizard::CommitButton, tr("Commit"));
    setButtonText(QWizard::FinishButton, tr("Finish"));
    setButtonText(QWizard::CancelButton, tr("Cancel"));
    setButtonText(QWizard::HelpButton, tr("Help"));

#if defined(Q_OS_WIN)
    if (m_core->isInstaller())
        setWindowTitle(tr("Setup - %1").arg(m_core->value(QLatin1String("ProductName"))));
    else
        setWindowTitle(tr("Maintain - %1").arg(m_core->value(QLatin1String("ProductName"))));
#endif

    d->m_defaultButtonText.clear();
    for (int i = QWizard::BackButton; i < QWizard::CustomButton1; ++i)
        d->m_defaultButtonText.insert(i, buttonText(QWizard::WizardButton(i)));

    foreach (int id, pageIds())
        qobject_cast<PackageManagerPage*>(page(id))->retranslateUi();
}

/*!
    \reimp
*/
bool PackageManagerGui::event(QEvent *event)
{
    switch(event->type()) {
    case QEvent::LanguageChange:
        emit languageChanged();
        break;
    default:
        break;
    }
    return QWizard::event(event);
}

/*!
    \reimp
*/
void PackageManagerGui::showEvent(QShowEvent *event)
{
    if (!event->spontaneous()) {
        foreach (int id, pageIds()) {
            const QString subTitle = page(id)->subTitle();
            if (subTitle.isEmpty()) {
                const QWizard::WizardStyle style = wizardStyle();
                if ((style == QWizard::ClassicStyle) || (style == QWizard::ModernStyle)) {
                    // otherwise the colors might screw up
                    page(id)->setSubTitle(QLatin1String(" "));
                }
            }
        }
        /*setMinimumSize(size());
        if (minimumWidth() < m_core->settings().wizardDefaultWidth())
            resize(m_core->settings().wizardDefaultWidth(), height());
        if (minimumHeight() < m_core->settings().wizardDefaultHeight())
            resize(width(), m_core->settings().wizardDefaultHeight());  */
    }
    QWizard::showEvent(event);
    QMetaObject::invokeMethod(this, "dependsOnLocalInstallerBinary", Qt::QueuedConnection);
}

/*!
    Requests the insertion of the page specified by \a widget at the position specified by \a page.
    If that position is already occupied by another page, the value is decremented until an empty
    slot is found.
*/
void PackageManagerGui::wizardPageInsertionRequested(QWidget *widget,
    QInstaller::PackageManagerCore::WizardPage page)
{
    // just in case it was already in there...
    wizardPageRemovalRequested(widget);

    int pageId = static_cast<int>(page) - 1;
    while (QWizard::page(pageId) != 0)
        --pageId;

    // add it
    setPage(pageId, new DynamicInstallerPage(widget, m_core));
}

/*!
    Requests the removal of the page specified by \a widget.
*/
void PackageManagerGui::wizardPageRemovalRequested(QWidget *widget)
{
    foreach (int pageId, pageIds()) {
        DynamicInstallerPage *const dynamicPage = qobject_cast<DynamicInstallerPage*>(page(pageId));
        if (dynamicPage == 0)
            continue;
        if (dynamicPage->widget() != widget)
            continue;
        removePage(pageId);
        d->m_defaultPages.remove(pageId);
        packageManagerCore()->controlScriptEngine()->removeFromGlobalObject(dynamicPage);
        packageManagerCore()->componentScriptEngine()->removeFromGlobalObject(dynamicPage);
    }
}

/*!
    Requests the insertion of \a widget on \a page.
*/
void PackageManagerGui::wizardWidgetInsertionRequested(QWidget *widget,
    QInstaller::PackageManagerCore::WizardPage page)
{
    Q_ASSERT(widget);
    if (QWizardPage *const p = QWizard::page(page)) {
        p->layout()->addWidget(widget);
        packageManagerCore()->controlScriptEngine()->addToGlobalObject(p);
        packageManagerCore()->componentScriptEngine()->addToGlobalObject(p);
    }
}

/*!
    Requests the removal of \a widget from installer pages.
*/
void PackageManagerGui::wizardWidgetRemovalRequested(QWidget *widget)
{
    Q_ASSERT(widget);
    widget->setParent(0);
    packageManagerCore()->controlScriptEngine()->removeFromGlobalObject(widget);
    packageManagerCore()->componentScriptEngine()->removeFromGlobalObject(widget);
}

/*!
    Requests changing the visibility of the page specified by \a p to
    \a visible.
*/
void PackageManagerGui::wizardPageVisibilityChangeRequested(bool visible, int p)
{
    if (visible && page(p) == 0) {
        setPage(p, d->m_defaultPages[p]);
    } else if (!visible && page(p) != 0) {
        d->m_defaultPages[p] = page(p);
        removePage(p);
    }
}

/*!
    Returns the page specified by \a id.

    \sa {gui::pageById}{gui.pageById}
*/
QWidget *PackageManagerGui::pageById(int id) const
{
    return page(id);
}

/*!
    Returns the page specified by the object name \a name from a UI file.

    \sa {gui::pageByObjectName}{gui.pageByObjectName}
*/
QWidget *PackageManagerGui::pageByObjectName(const QString &name) const
{
    const QList<int> ids = pageIds();
    foreach (const int i, ids) {
        PackageManagerPage *const p = qobject_cast<PackageManagerPage*> (page(i));
        if (p && p->objectName() == name)
            return p;
    }
    qWarning() << "No page found for object name" << name;
    return 0;
}

/*!
    \sa {gui::currentPageWidget}{gui.currentPageWidget}
*/
QWidget *PackageManagerGui::currentPageWidget() const
{
    return currentPage();
}

/*!
    For dynamic pages, returns the widget specified by \a name read from the UI
    file.

    \sa {gui::pageWidgetByObjectName}{gui.pageWidgetByObjectName}
*/
QWidget *PackageManagerGui::pageWidgetByObjectName(const QString &name) const
{
    QWidget *const widget = pageByObjectName(name);
    if (PackageManagerPage *const p = qobject_cast<PackageManagerPage*> (widget)) {
        // For dynamic pages, return the contained widget (as read from the UI file), not the
        // wrapper page
        if (DynamicInstallerPage *dp = qobject_cast<DynamicInstallerPage *>(p))
            return dp->widget();
        return p;
    }
    qWarning() << "No page found for object name" << name;
    return 0;
}

/*!
    \sa {gui::cancelButtonClicked}{gui.cancelButtonClicked}
*/
void PackageManagerGui::cancelButtonClicked()
{
    const int id = currentId();
    if (id == PackageManagerCore::Introduction ||
            id == PackageManagerCore::InstallationFinished ||
            id == PackageManagerCore::SelectLanguage) {
        m_core->setNeedsHardRestart(false);
        QDialog::reject(); return;
    }

    QString question;
    bool interrupt = false;
    PackageManagerPage *const page = qobject_cast<PackageManagerPage*> (currentPage());
    if (page && page->isInterruptible()
        && m_core->status() != PackageManagerCore::Canceled
        && m_core->status() != PackageManagerCore::Failure) {
            interrupt = true;
            question = tr("Cancel the installation?");
            if (m_core->isUninstaller())
                question = tr("Cancel the uninstall?");
    } else {
        question = tr("Quit the installer?");
        if (m_core->isUninstaller())
            question = tr("Quit the uninstaller?");
        if (m_core->isUpdater() || m_core->isPackageManager())
            question = tr("Quit the maintenance application?");
    }

    const QMessageBox::StandardButton button =
        MessageBoxHandler::question(MessageBoxHandler::currentBestSuitParent(),
        QLatin1String("cancelInstallation"), tr("Question"), question,
        QMessageBox::Yes | QMessageBox::No);

    if (button == QMessageBox::Yes) {
        if (interrupt)
            emit interrupted();
        else
            QDialog::reject();
    }
}

/*!
   \sa {gui::rejectWithoutPrompt}{gui.rejectWithoutPrompt}
*/
void PackageManagerGui::rejectWithoutPrompt()
{
    QDialog::reject();
}

/*!
    \reimp
*/
void PackageManagerGui::reject()
{
    cancelButtonClicked();
}

/*!
    \internal
*/
void PackageManagerGui::setModified(bool value)
{
    d->m_modified = value;
}

bool PackageManagerGui::setCurrentLanguage(const QString &lang) {
    d->m_currentLang = lang;

    if (d->m_translator) {
        qApp->removeTranslator(d->m_translator);
        d->m_translator = Q_NULLPTR;
    }

    if (!d->m_langMap.contains(lang))
        return false;

    const QString directory = QLatin1String(":/translations");

    QDirIterator it(directory, QDir::Files);
    while (it.hasNext()) {
        const QString filename = it.next();
        const QString basename = QFileInfo(filename).baseName();

        if (basename == d->m_langMap[lang]) {
            //QMessageBox::information(0, "Found", filename + " " + basename);

            d->m_translator = new QTranslator(this);

            if (d->m_translator->load(filename)) {
                //QMessageBox::information(0, "Loaded", filename + " " + basename);
                // Do not throw if translator returns false as it may just be an intentionally
                // empty file. See also QTBUG-31031
                qApp->installTranslator(d->m_translator);

                // To save the language in %unistaller%.ini
                m_core->setValue(scMaintenanceToolLanguage, lang);

                Q_EMIT languageChanged();
            }

            return true;
        }
    }

     return false;
}

QString PackageManagerGui::currentLanguage() const {
    return d->m_currentLang;
}

QString PackageManagerGui::currentLanguageFileName() const {
    if (!d->m_langMap.contains(d->m_currentLang))
        return QString();

    return d->m_langMap[d->m_currentLang];
}

QStringList PackageManagerGui::availableLanguages() const {
    QStringList lst;

    QMapIterator<QString, QString> languageIterator(d->m_langMap);
    while (languageIterator.hasNext()) {
        languageIterator.next();
         lst << languageIterator.key();
    }

    return lst;
}

void PackageManagerGui::moveToCenterOfTheScreen() {
    move(QApplication::desktop()->screen()->rect().center() - rect().center());
}

/*!
    \sa {gui::showFinishedPage}{gui.showFinishedPage}
*/
void PackageManagerGui::showFinishedPage()
{
    if (d->m_autoSwitchPage)
        next();
    else
        qobject_cast<QPushButton*>(button(QWizard::CancelButton))->setEnabled(false);
}

/*!
    Shows the \uicontrol Settings button if \a show is \c true.

    \sa {gui::showSettingsButton}{gui.showSettingsButton}
*/
void PackageManagerGui::showSettingsButton(bool show)
{
    if (d->m_showSettingsButton == show)
        return;

    d->m_showSettingsButton = show;
    setOption(QWizard::HaveCustomButton1, show);
    setButtonText(QWizard::CustomButton1, tr("Settings"));

    updateButtonLayout();
}

/*!
    Forces an update of our own button layout. Needs to be called whenever a
    button option has been set.
*/
void PackageManagerGui::updateButtonLayout()
{
    QVector<QWizard::WizardButton> buttons(12, QWizard::NoButton);
    if (options() & QWizard::HaveHelpButton)
        buttons[(options() & QWizard::HelpButtonOnRight) ? 11 : 0] = QWizard::HelpButton;

    buttons[1] = QWizard::Stretch;
    if (options() & QWizard::HaveCustomButton1) {
        buttons[1] = QWizard::CustomButton1;
        buttons[2] = QWizard::Stretch;
    }

    if (options() & QWizard::HaveCustomButton2)
        buttons[3] = QWizard::CustomButton2;

    if (options() & QWizard::HaveCustomButton3)
        buttons[4] = QWizard::CustomButton3;

    if (!(options() & QWizard::NoCancelButton))
        buttons[(options() & QWizard::CancelButtonOnLeft) ? 5 : 10] = QWizard::CancelButton;

    buttons[6] = QWizard::BackButton;
    buttons[7] = QWizard::NextButton;
    buttons[8] = QWizard::CommitButton;
    buttons[9] = QWizard::FinishButton;

    setOption(QWizard::NoBackButtonOnLastPage, true);
    setOption(QWizard::NoBackButtonOnStartPage, true);

    setButtonLayout(buttons.toList());
}

/*!
    Enables the \uicontrol Settings button by setting \a enabled to \c true.

    \sa {gui::setSettingsButtonEnabled}{gui.setSettingsButtonEnabled}
*/
void PackageManagerGui::setSettingsButtonEnabled(bool enabled)
{
    if (QAbstractButton *btn = button(QWizard::CustomButton1))
        btn->setEnabled(enabled);
}

/*!
    Emits the settingsButtonClicked() signal when the custom button specified by \a which is
    clicked if \a which is the \uicontrol Settings button.
*/
void PackageManagerGui::customButtonClicked(int which)
{
    if (QWizard::WizardButton(which) == QWizard::CustomButton1 && d->m_showSettingsButton)
        emit settingsButtonClicked();
}

/*!
    Prevents installation from a network location by determining that a local
    installer binary must be used.
*/
void PackageManagerGui::dependsOnLocalInstallerBinary()
{
    if (m_core->settings().dependsOnLocalInstallerBinary() && !m_core->localInstallerBinaryUsed()) {
        MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(),
            QLatin1String("Installer_Needs_To_Be_Local_Error"), tr("Error"),
            tr("Unable to install %1 from network location.\nPlease copy the installer to your computer's hard drive and try again."), QMessageBox::Ok);
        rejectWithoutPrompt();
    }
}

/*!
    Called when the current page changes to \a newId. Calls the leaving() method for the old page
    and the entering() method for the new one. Also, executes the control script associated with the
    new page by calling executeControlScript().

    Emits the left() and entered() signals.
*/
void PackageManagerGui::currentPageChanged(int newId)
{
    PackageManagerPage *oldPage = qobject_cast<PackageManagerPage *>(page(d->m_currentId));
    if (oldPage) {
        oldPage->leaving();
        emit oldPage->left();
    }

    d->m_currentId = newId;

    PackageManagerPage *newPage = qobject_cast<PackageManagerPage *>(page(d->m_currentId));
    if (newPage) {
        newPage->entering();
        emit newPage->entered();
    }

    executeControlScript(newId);
}
