<commit_before>/*
 *
 *    Copyright (c) 2020 Project CHIP Authors
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

/**
 *    @file
 *          Provides the implementation of the Device Layer ConfigurationManager object
 *          for Qorvo QPG platforms.
 */
/* this file behaves like a config.h, comes first */
#include <platform/internal/CHIPDeviceLayerInternal.h>

#include <platform/ConfigurationManager.h>
#include <platform/internal/GenericConfigurationManagerImpl.cpp>

#include <lib/core/CHIPVendorIdentifiers.hpp>
#include <platform/qpg/qpgConfig.h>

#if CHIP_DEVICE_CONFIG_ENABLE_FACTORY_PROVISIONING
#include <platform/internal/FactoryProvisioning.cpp>
#endif // CHIP_DEVICE_CONFIG_ENABLE_FACTORY_PROVISIONING

#include <lib/support/CodeUtils.h>
#include <lib/support/logging/CHIPLogging.h>

namespace chip {
namespace DeviceLayer {

using namespace ::chip::DeviceLayer::Internal;

/** Singleton instance of the ConfigurationManager implementation object.
 */
ConfigurationManagerImpl ConfigurationManagerImpl::sInstance;

CHIP_ERROR ConfigurationManagerImpl::Init()
{
    CHIP_ERROR err;
    bool failSafeArmed;

    // Initialize the generic implementation base class.
    err = Internal::GenericConfigurationManagerImpl<ConfigurationManagerImpl>::Init();
    SuccessOrExit(err);

    // If the fail-safe was armed when the device last shutdown, initiate a factory reset.
    if (GetFailSafeArmed(failSafeArmed) == CHIP_NO_ERROR && failSafeArmed)
    {
        ChipLogProgress(DeviceLayer, "Detected fail-safe armed on reboot; initiating factory reset");
        InitiateFactoryReset();
    }
    err = CHIP_NO_ERROR;

exit:
    return err;
}

bool ConfigurationManagerImpl::CanFactoryReset()
{
    // TODO: query the application to determine if factory reset is allowed.
    return true;
}

void ConfigurationManagerImpl::InitiateFactoryReset()
{
    PlatformMgr().ScheduleWork(DoFactoryReset);
}

CHIP_ERROR ConfigurationManagerImpl::ReadPersistedStorageValue(::chip::Platform::PersistedStorage::Key persistedStorageKey,
                                                               uint32_t & value)
{
    CHIP_ERROR err;
    uintmax_t recordKey = persistedStorageKey + kConfigKey_GroupKeyBase;

    VerifyOrExit(recordKey <= kConfigKey_GroupKeyMax, err = CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND);

    err = ReadConfigValue(persistedStorageKey, value);
    if (err == CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND)
    {
        err = CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND;
    }
    SuccessOrExit(err);

exit:
    return err;
}

CHIP_ERROR ConfigurationManagerImpl::WritePersistedStorageValue(::chip::Platform::PersistedStorage::Key persistedStorageKey,
                                                                uint32_t value)
{
    CHIP_ERROR err;

    uintmax_t recordKey = persistedStorageKey + kConfigKey_GroupKeyBase;

    VerifyOrExit(recordKey <= kConfigKey_GroupKeyMax, err = CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND);

    err = WriteConfigValue(persistedStorageKey, value);
    SuccessOrExit(err);

exit:
    return err;
}

void ConfigurationManagerImpl::DoFactoryReset(intptr_t arg)
{
    CHIP_ERROR err;

    ChipLogProgress(DeviceLayer, "Performing factory reset");

    err = FactoryResetConfig();
    if (err != CHIP_NO_ERROR)
    {
        ChipLogError(DeviceLayer, "FactoryResetConfig() failed: %s", ErrorStr(err));
    }

#if CHIP_DEVICE_CONFIG_ENABLE_THREAD

    ChipLogProgress(DeviceLayer, "Clearing Thread provision");
    ThreadStackMgr().ErasePersistentInfo();

#endif // CHIP_DEVICE_CONFIG_ENABLE_THREAD

    // Restart the system.
    ChipLogProgress(DeviceLayer, "System restarting");
    qvCHIP_ResetSystem();
}

} // namespace DeviceLayer
} // namespace chip
<commit_msg>[qpg] Clear KVS on factory reset (#11180)<commit_after>/*
 *
 *    Copyright (c) 2020 Project CHIP Authors
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

/**
 *    @file
 *          Provides the implementation of the Device Layer ConfigurationManager object
 *          for Qorvo QPG platforms.
 */
/* this file behaves like a config.h, comes first */
#include <platform/internal/CHIPDeviceLayerInternal.h>

#include <platform/ConfigurationManager.h>
#include <platform/internal/GenericConfigurationManagerImpl.cpp>

#include <lib/core/CHIPVendorIdentifiers.hpp>
#include <platform/qpg/qpgConfig.h>

#if CHIP_DEVICE_CONFIG_ENABLE_FACTORY_PROVISIONING
#include <platform/internal/FactoryProvisioning.cpp>
#endif // CHIP_DEVICE_CONFIG_ENABLE_FACTORY_PROVISIONING

#include <lib/support/CodeUtils.h>
#include <lib/support/logging/CHIPLogging.h>

namespace chip {
namespace DeviceLayer {

using namespace ::chip::DeviceLayer::Internal;

/** Singleton instance of the ConfigurationManager implementation object.
 */
ConfigurationManagerImpl ConfigurationManagerImpl::sInstance;

CHIP_ERROR ConfigurationManagerImpl::Init()
{
    CHIP_ERROR err;
    bool failSafeArmed;

    // Initialize the generic implementation base class.
    err = Internal::GenericConfigurationManagerImpl<ConfigurationManagerImpl>::Init();
    SuccessOrExit(err);

    // If the fail-safe was armed when the device last shutdown, initiate a factory reset.
    if (GetFailSafeArmed(failSafeArmed) == CHIP_NO_ERROR && failSafeArmed)
    {
        ChipLogProgress(DeviceLayer, "Detected fail-safe armed on reboot; initiating factory reset");
        InitiateFactoryReset();
    }
    err = CHIP_NO_ERROR;

exit:
    return err;
}

bool ConfigurationManagerImpl::CanFactoryReset()
{
    // TODO: query the application to determine if factory reset is allowed.
    return true;
}

void ConfigurationManagerImpl::InitiateFactoryReset()
{
    PlatformMgr().ScheduleWork(DoFactoryReset);
}

CHIP_ERROR ConfigurationManagerImpl::ReadPersistedStorageValue(::chip::Platform::PersistedStorage::Key persistedStorageKey,
                                                               uint32_t & value)
{
    CHIP_ERROR err;
    uintmax_t recordKey = persistedStorageKey + kConfigKey_GroupKeyBase;

    VerifyOrExit(recordKey <= kConfigKey_GroupKeyMax, err = CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND);

    err = ReadConfigValue(persistedStorageKey, value);
    if (err == CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND)
    {
        err = CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND;
    }
    SuccessOrExit(err);

exit:
    return err;
}

CHIP_ERROR ConfigurationManagerImpl::WritePersistedStorageValue(::chip::Platform::PersistedStorage::Key persistedStorageKey,
                                                                uint32_t value)
{
    CHIP_ERROR err;

    uintmax_t recordKey = persistedStorageKey + kConfigKey_GroupKeyBase;

    VerifyOrExit(recordKey <= kConfigKey_GroupKeyMax, err = CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND);

    err = WriteConfigValue(persistedStorageKey, value);
    SuccessOrExit(err);

exit:
    return err;
}

void ConfigurationManagerImpl::DoFactoryReset(intptr_t arg)
{
    CHIP_ERROR err;
    qvStatus_t qvErr;

    ChipLogProgress(DeviceLayer, "Performing factory reset");

    err = FactoryResetConfig();
    if (err != CHIP_NO_ERROR)
    {
        ChipLogError(DeviceLayer, "FactoryResetConfig() failed: %s", ErrorStr(err));
    }

    qvErr = qvCHIP_KvsErasePartition();
    if (qvErr != QV_STATUS_NO_ERROR)
    {
        ChipLogError(DeviceLayer, "qvCHIP_KvsErasePartition() failed: %d", qvErr);
    }

#if CHIP_DEVICE_CONFIG_ENABLE_THREAD

    ChipLogProgress(DeviceLayer, "Clearing Thread provision");
    ThreadStackMgr().ErasePersistentInfo();

#endif // CHIP_DEVICE_CONFIG_ENABLE_THREAD

    // Restart the system.
    ChipLogProgress(DeviceLayer, "System restarting");
    qvCHIP_ResetSystem();
}

} // namespace DeviceLayer
} // namespace chip
<|endoftext|>


<commit_before>/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** 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 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.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://qt.nokia.com/contact.
**
**************************************************************************/

#include "ioutputparser.h"
#include "utils/qtcassert.h"

namespace ProjectExplorer {

IOutputParser::IOutputParser() : m_parser(0)
{
}

IOutputParser::~IOutputParser()
{
    delete m_parser;
}

void IOutputParser::appendOutputParser(IOutputParser *parser)
{
    QTC_ASSERT(parser, return);
    if (m_parser) {
        m_parser->appendOutputParser(parser);
        return;
    }

    m_parser = parser;
    connect(parser, SIGNAL(addOutput(QString,ProjectExplorer::BuildStep::OutputFormat)),
            this, SLOT(outputAdded(QString,ProjectExplorer::BuildStep::OutputFormat)), Qt::DirectConnection);
    connect(parser, SIGNAL(addTask(ProjectExplorer::Task)),
            this, SLOT(taskAdded(ProjectExplorer::Task)), Qt::DirectConnection);
}

IOutputParser *IOutputParser::takeOutputParserChain()
{
    IOutputParser *parser = m_parser;
    disconnect(parser, SIGNAL(addOutput(QString, QTextCharFormat)),
            this, SLOT(outputAdded(QString, QTextCharFormat)));
    disconnect(parser, SIGNAL(addTask(ProjectExplorer::Task)),
            this, SLOT(taskAdded(ProjectExplorer::Task)));
    m_parser = 0;
    return parser;
}

IOutputParser *IOutputParser::childParser() const
{
    return m_parser;
}

void IOutputParser::stdOutput(const QString &line)
{
    if (m_parser)
        m_parser->stdOutput(line);
}

void IOutputParser::stdError(const QString &line)
{
    if (m_parser)
        m_parser->stdError(line);
}

void IOutputParser::outputAdded(const QString &string, ProjectExplorer::BuildStep::OutputFormat format)
{
    emit addOutput(string, format);
}

void IOutputParser::taskAdded(const ProjectExplorer::Task &task)
{
    emit addTask(task);
}

}
<commit_msg>Update signal parameter types in disconnect<commit_after>/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** 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 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.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://qt.nokia.com/contact.
**
**************************************************************************/

#include "ioutputparser.h"
#include "utils/qtcassert.h"

namespace ProjectExplorer {

IOutputParser::IOutputParser() : m_parser(0)
{
}

IOutputParser::~IOutputParser()
{
    delete m_parser;
}

void IOutputParser::appendOutputParser(IOutputParser *parser)
{
    QTC_ASSERT(parser, return);
    if (m_parser) {
        m_parser->appendOutputParser(parser);
        return;
    }

    m_parser = parser;
    connect(parser, SIGNAL(addOutput(QString,ProjectExplorer::BuildStep::OutputFormat)),
            this, SLOT(outputAdded(QString,ProjectExplorer::BuildStep::OutputFormat)), Qt::DirectConnection);
    connect(parser, SIGNAL(addTask(ProjectExplorer::Task)),
            this, SLOT(taskAdded(ProjectExplorer::Task)), Qt::DirectConnection);
}

IOutputParser *IOutputParser::takeOutputParserChain()
{
    IOutputParser *parser = m_parser;
    disconnect(parser, SIGNAL(addOutput(QString,ProjectExplorer::BuildStep::OutputFormat)),
            this, SLOT(outputAdded(QString,ProjectExplorer::BuildStep::OutputFormat)));
    disconnect(parser, SIGNAL(addTask(ProjectExplorer::Task)),
            this, SLOT(taskAdded(ProjectExplorer::Task)));
    m_parser = 0;
    return parser;
}

IOutputParser *IOutputParser::childParser() const
{
    return m_parser;
}

void IOutputParser::stdOutput(const QString &line)
{
    if (m_parser)
        m_parser->stdOutput(line);
}

void IOutputParser::stdError(const QString &line)
{
    if (m_parser)
        m_parser->stdError(line);
}

void IOutputParser::outputAdded(const QString &string, ProjectExplorer::BuildStep::OutputFormat format)
{
    emit addOutput(string, format);
}

void IOutputParser::taskAdded(const ProjectExplorer::Task &task)
{
    emit addTask(task);
}

}
<|endoftext|>


<commit_before>/**************************************************************************
**
** This file is part of Qt Creator
**
** 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
**
** Alternatively, 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 have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
**************************************************************************/

#include "qmldumptool.h"
#include "qt4project.h"
#include "qt4projectmanagerconstants.h"
#include "qtversionmanager.h"
#include "debugginghelperbuildtask.h"
#include <coreplugin/icore.h>
#include <coreplugin/progressmanager/progressmanager.h>

#include <projectexplorer/project.h>
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/runconfiguration.h>
#include <qtconcurrent/runextensions.h>
#include <qmljs/qmljsmodelmanagerinterface.h>
#include <utils/qtcassert.h>
#include <QtGui/QDesktopServices>
#include <QtCore/QCoreApplication>
#include <QtCore/QDir>
#include <QtCore/QDebug>
#include <QtCore/QHash>

namespace {

using namespace Qt4ProjectManager;
using Qt4ProjectManager::Internal::DebuggingHelperBuildTask;

class QmlDumpBuildTask;

typedef QHash<int, QmlDumpBuildTask *> QmlDumpByVersion;
Q_GLOBAL_STATIC(QmlDumpByVersion, qmlDumpBuilds)

// A task suitable to be run by QtConcurrent to build qmldump.
class QmlDumpBuildTask : public QObject {
    Q_DISABLE_COPY(QmlDumpBuildTask)
    Q_OBJECT
public:
    explicit QmlDumpBuildTask(QtVersion *version)
        : m_buildTask(new DebuggingHelperBuildTask(version, DebuggingHelperBuildTask::QmlDump))
        , m_failed(false)
    {
        qmlDumpBuilds()->insert(m_version.uniqueId(), this);

        connect(m_buildTask, SIGNAL(finished(int,DebuggingHelperBuildTask::Tools,QString)),
                this, SLOT(finish(int,DebuggingHelperBuildTask::Tools,QString)),
                Qt::QueuedConnection);
    }

    void run(QFutureInterface<void> &future)
    {
        m_buildTask->run(future);
    }

    void updateProjectWhenDone(QPointer<ProjectExplorer::Project> project, bool preferDebug)
    {
        foreach (const ProjectToUpdate &update, m_projectsToUpdate) {
            if (update.project == project)
                return;
        }

        ProjectToUpdate update;
        update.project = project;
        update.preferDebug = preferDebug;
        m_projectsToUpdate += update;
    }

    bool hasFailed() const
    {
        return m_failed;
    }

private slots:
    void finish(int qtId, DebuggingHelperBuildTask::Tools tools, const QString &output)
    {
        QtVersion *version = QtVersionManager::instance()->version(qtId);

        QTC_ASSERT(tools == DebuggingHelperBuildTask::QmlDump, return);
        QString errorMessage;
        if (!version) {
            m_failed = true;
            errorMessage = QString::fromLatin1("Qt version became invalid");
        } else {
            version->invalidateCache();

            if (!version->hasQmlDump()) {
                m_failed = true;
                errorMessage = QString::fromLatin1("Could not build QML plugin dumping helper for %1\n"
                                                   "Output:\n%2").
                        arg(version->displayName(), output);
            }
        }

        if (m_failed) {
            qWarning("%s", qPrintable(errorMessage));
        }

        // update qmldump path for all the project
        QmlJS::ModelManagerInterface *modelManager = QmlJS::ModelManagerInterface::instance();
        if (!modelManager)
            return;

        foreach (const ProjectToUpdate &update, m_projectsToUpdate) {
            if (!update.project)
                continue;
            QmlJS::ModelManagerInterface::ProjectInfo projectInfo = modelManager->projectInfo(update.project);
            projectInfo.qmlDumpPath = version->qmlDumpTool(update.preferDebug);
            if (projectInfo.qmlDumpPath.isEmpty())
                projectInfo.qmlDumpPath = version->qmlDumpTool(!update.preferDebug);
            projectInfo.qmlDumpEnvironment = version->qmlToolsEnvironment();
            modelManager->updateProjectInfo(projectInfo);
        }

        // clean up
        qmlDumpBuilds()->remove(qtId);
        deleteLater();
    }

private:
    class ProjectToUpdate {
    public:
        QPointer<ProjectExplorer::Project> project;
        bool preferDebug;
    };

    QList<ProjectToUpdate> m_projectsToUpdate;
    Internal::DebuggingHelperBuildTask *m_buildTask; // deletes itself after run()
    QtVersion m_version;
    bool m_failed;
};
} // end of anonymous namespace

namespace Qt4ProjectManager {

static inline QStringList validBinaryFilenames(bool debugBuild)
{
    QStringList list = QStringList()
            << QLatin1String("qmldump.exe")
            << QLatin1String("qmldump")
            << QLatin1String("qmldump.app/Contents/MacOS/qmldump");
    if (debugBuild)
        list.prepend(QLatin1String("debug/qmldump.exe"));
    else
        list.prepend(QLatin1String("release/qmldump.exe"));
    return list;
}

bool QmlDumpTool::canBuild(const QtVersion *qtVersion)
{
    const QString installHeaders = qtVersion->versionInfo().value("QT_INSTALL_HEADERS");
    const QString header = installHeaders + QLatin1String("/QtDeclarative/private/qdeclarativemetatype_p.h");
    return (qtVersion->supportsTargetId(Constants::DESKTOP_TARGET_ID)
            || (qtVersion->supportsTargetId(Constants::QT_SIMULATOR_TARGET_ID)
                && (qtVersion->qtVersion() > QtVersionNumber(4, 7, 1))))
            && QFile::exists(header);
}

static QtVersion *qtVersionForProject(ProjectExplorer::Project *project)
{
    if (project && project->id() == Qt4ProjectManager::Constants::QT4PROJECT_ID) {
        Qt4Project *qt4Project = static_cast<Qt4Project*>(project);
        if (qt4Project && qt4Project->activeTarget()
                && qt4Project->activeTarget()->activeBuildConfiguration()) {
            QtVersion *version = qt4Project->activeTarget()->activeBuildConfiguration()->qtVersion();
            if (version->isValid())
                return version;
        }
        return 0;
    }

    if (project && project->id() == QLatin1String("QmlProjectManager.QmlProject")) {
        // We cannot access the QmlProject interfaces here, therefore use the metatype system
        if (!project->activeTarget() || !project->activeTarget()->activeRunConfiguration())
            return 0;
        QVariant variant = project->activeTarget()->activeRunConfiguration()->property("qtVersionId");
        QTC_ASSERT(variant.isValid() && variant.canConvert(QVariant::Int), return 0);
        QtVersion *version = QtVersionManager::instance()->version(variant.toInt());
        if (version && version->isValid())
            return version;
        return 0;
    }

    // else, find any desktop or simulator Qt version that has qmldump, or
    // - if there isn't any - one that could build it
    QtVersion *canBuildQmlDump = 0;
    QtVersionManager *qtVersions = QtVersionManager::instance();
    foreach (QtVersion *version, qtVersions->validVersions()) {
        if (version->supportsTargetId(Constants::DESKTOP_TARGET_ID)
                || version->supportsTargetId(Constants::QT_SIMULATOR_TARGET_ID)) {
            if (version->hasQmlDump())
                return version;

            if (!canBuildQmlDump && QmlDumpTool::canBuild(version)) {
                canBuildQmlDump = version;
            }
        }
    }

    return canBuildQmlDump;
}

QString QmlDumpTool::toolForProject(ProjectExplorer::Project *project, bool debugDump)
{
    QtVersion *version = qtVersionForProject(project);
    if (version) {
        QString qtInstallData = version->versionInfo().value("QT_INSTALL_DATA");
        QString toolPath = toolByInstallData(qtInstallData, debugDump);
        return toolPath;
    }

    return QString();
}

static QString sourcePath()
{
    return Core::ICore::instance()->resourcePath() + QLatin1String("/qml/qmldump/");
}

static QStringList sourceFileNames()
{
    QStringList files;
    files << QLatin1String("main.cpp") << QLatin1String("qmldump.pro")
          << QLatin1String("qmlstreamwriter.cpp") << QLatin1String("qmlstreamwriter.h")
          << QLatin1String("LICENSE.LGPL") << QLatin1String("LGPL_EXCEPTION.TXT")
          << QLatin1String("Info.plist");
    return files;
}

QString QmlDumpTool::toolByInstallData(const QString &qtInstallData, bool debugDump)
{
    if (!Core::ICore::instance())
        return QString();

    const QString mainFilename = Core::ICore::instance()->resourcePath()
            + QLatin1String("/qml/qmldump/main.cpp");
    const QStringList directories = installDirectories(qtInstallData);
    const QStringList binFilenames = validBinaryFilenames(debugDump);

    return byInstallDataHelper(sourcePath(), sourceFileNames(), directories, binFilenames);
}

QStringList QmlDumpTool::locationsByInstallData(const QString &qtInstallData, bool debugDump)
{
    QStringList result;
    QFileInfo fileInfo;
    const QStringList binFilenames = validBinaryFilenames(debugDump);
    foreach(const QString &directory, installDirectories(qtInstallData)) {
        if (getHelperFileInfoFor(binFilenames, directory, &fileInfo))
            result << fileInfo.filePath();
    }
    return result;
}

bool QmlDumpTool::build(const QString &directory, const QString &makeCommand,
                        const QString &qmakeCommand, const QString &mkspec,
                        const Utils::Environment &env, const QString &targetMode,
                        const QStringList &qmakeArguments, QString *output, QString *errorMessage)
{
    return buildHelper(QCoreApplication::translate("Qt4ProjectManager::QmlDumpTool", "qmldump"), QLatin1String("qmldump.pro"),
                       directory, makeCommand, qmakeCommand, mkspec, env, targetMode,
                       qmakeArguments, output, errorMessage);
}

QString QmlDumpTool::copy(const QString &qtInstallData, QString *errorMessage)
{
    const QStringList directories = QmlDumpTool::installDirectories(qtInstallData);

    // Try to find a writeable directory.
    foreach(const QString &directory, directories) {
        if (copyFiles(sourcePath(), sourceFileNames(), directory, errorMessage)) {
            return directory;
        }
    }
    *errorMessage = QCoreApplication::translate("ProjectExplorer::QmlDumpTool",
                                                "qmldump could not be built in any of the directories:\n- %1\n\nReason: %2")
                    .arg(directories.join(QLatin1String("\n- ")), *errorMessage);
    return QString();
}

QStringList QmlDumpTool::installDirectories(const QString &qtInstallData)
{
    const QChar slash = QLatin1Char('/');
    const uint hash = qHash(qtInstallData);
    QStringList directories;
    directories
            << (qtInstallData + QLatin1String("/qtc-qmldump/"))
            << QDir::cleanPath((QCoreApplication::applicationDirPath() + QLatin1String("/../qtc-qmldump/") + QString::number(hash))) + slash
            << (QDesktopServices::storageLocation(QDesktopServices::DataLocation) + QLatin1String("/qtc-qmldump/") + QString::number(hash)) + slash;
    return directories;
}

void QmlDumpTool::pathAndEnvironment(ProjectExplorer::Project *project, bool preferDebug,
                                     QString *dumperPath, Utils::Environment *env)
{
    QString path;

    QtVersion *version = qtVersionForProject(project);
    if (version && !version->hasQmlDump() && QmlDumpTool::canBuild(version)) {
        QmlDumpBuildTask *qmlDumpBuildTask = qmlDumpBuilds()->value(version->uniqueId());
        if (qmlDumpBuildTask) {
            if (!qmlDumpBuildTask->hasFailed())
                qmlDumpBuildTask->updateProjectWhenDone(project, preferDebug);
        } else {
            QmlDumpBuildTask *buildTask = new QmlDumpBuildTask(version);
            buildTask->updateProjectWhenDone(project, preferDebug);
            QFuture<void> task = QtConcurrent::run(&QmlDumpBuildTask::run, buildTask);
            const QString taskName = QmlDumpBuildTask::tr("Building helper");
            Core::ICore::instance()->progressManager()->addTask(task, taskName,
                                                                QLatin1String("Qt4ProjectManager::BuildHelpers"));
        }
        return;
    }

    path = Qt4ProjectManager::QmlDumpTool::toolForProject(project, preferDebug);
    if (path.isEmpty())
        path = Qt4ProjectManager::QmlDumpTool::toolForProject(project, !preferDebug);

    if (!path.isEmpty()) {
        QFileInfo qmldumpFileInfo(path);
        if (!qmldumpFileInfo.exists()) {
            qWarning() << "QmlDumpTool::qmlDumpPath: qmldump executable does not exist at" << path;
            path.clear();
        } else if (!qmldumpFileInfo.isFile()) {
            qWarning() << "QmlDumpTool::qmlDumpPath: " << path << " is not a file";
            path.clear();
        }
    }

    if (!path.isEmpty() && version && dumperPath && env) {
        *dumperPath = path;
        *env = version->qmlToolsEnvironment();
    }
}

} // namespace Qt4ProjectManager

#include "qmldumptool.moc"
<commit_msg>QmlDumpTool: Remove unneeded QtVersion<commit_after>/**************************************************************************
**
** This file is part of Qt Creator
**
** 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
**
** Alternatively, 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 have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
**************************************************************************/

#include "qmldumptool.h"
#include "qt4project.h"
#include "qt4projectmanagerconstants.h"
#include "qtversionmanager.h"
#include "debugginghelperbuildtask.h"
#include <coreplugin/icore.h>
#include <coreplugin/progressmanager/progressmanager.h>

#include <projectexplorer/project.h>
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/runconfiguration.h>
#include <qtconcurrent/runextensions.h>
#include <qmljs/qmljsmodelmanagerinterface.h>
#include <utils/qtcassert.h>
#include <QtGui/QDesktopServices>
#include <QtCore/QCoreApplication>
#include <QtCore/QDir>
#include <QtCore/QDebug>
#include <QtCore/QHash>

namespace {

using namespace Qt4ProjectManager;
using Qt4ProjectManager::Internal::DebuggingHelperBuildTask;

class QmlDumpBuildTask;

typedef QHash<int, QmlDumpBuildTask *> QmlDumpByVersion;
Q_GLOBAL_STATIC(QmlDumpByVersion, qmlDumpBuilds)

// A task suitable to be run by QtConcurrent to build qmldump.
class QmlDumpBuildTask : public QObject {
    Q_DISABLE_COPY(QmlDumpBuildTask)
    Q_OBJECT
public:
    explicit QmlDumpBuildTask(QtVersion *version)
        : m_buildTask(new DebuggingHelperBuildTask(version, DebuggingHelperBuildTask::QmlDump))
        , m_failed(false)
    {
        qmlDumpBuilds()->insert(version->uniqueId(), this);

        connect(m_buildTask, SIGNAL(finished(int,DebuggingHelperBuildTask::Tools,QString)),
                this, SLOT(finish(int,DebuggingHelperBuildTask::Tools,QString)),
                Qt::QueuedConnection);
    }

    void run(QFutureInterface<void> &future)
    {
        m_buildTask->run(future);
    }

    void updateProjectWhenDone(QPointer<ProjectExplorer::Project> project, bool preferDebug)
    {
        foreach (const ProjectToUpdate &update, m_projectsToUpdate) {
            if (update.project == project)
                return;
        }

        ProjectToUpdate update;
        update.project = project;
        update.preferDebug = preferDebug;
        m_projectsToUpdate += update;
    }

    bool hasFailed() const
    {
        return m_failed;
    }

private slots:
    void finish(int qtId, DebuggingHelperBuildTask::Tools tools, const QString &output)
    {
        QtVersion *version = QtVersionManager::instance()->version(qtId);

        QTC_ASSERT(tools == DebuggingHelperBuildTask::QmlDump, return);
        QString errorMessage;
        if (!version) {
            m_failed = true;
            errorMessage = QString::fromLatin1("Qt version became invalid");
        } else {
            version->invalidateCache();

            if (!version->hasQmlDump()) {
                m_failed = true;
                errorMessage = QString::fromLatin1("Could not build QML plugin dumping helper for %1\n"
                                                   "Output:\n%2").
                        arg(version->displayName(), output);
            }
        }

        if (m_failed) {
            qWarning("%s", qPrintable(errorMessage));
        }

        // update qmldump path for all the project
        QmlJS::ModelManagerInterface *modelManager = QmlJS::ModelManagerInterface::instance();
        if (!modelManager)
            return;

        foreach (const ProjectToUpdate &update, m_projectsToUpdate) {
            if (!update.project)
                continue;
            QmlJS::ModelManagerInterface::ProjectInfo projectInfo = modelManager->projectInfo(update.project);
            projectInfo.qmlDumpPath = version->qmlDumpTool(update.preferDebug);
            if (projectInfo.qmlDumpPath.isEmpty())
                projectInfo.qmlDumpPath = version->qmlDumpTool(!update.preferDebug);
            projectInfo.qmlDumpEnvironment = version->qmlToolsEnvironment();
            modelManager->updateProjectInfo(projectInfo);
        }

        // clean up
        qmlDumpBuilds()->remove(qtId);
        deleteLater();
    }

private:
    class ProjectToUpdate {
    public:
        QPointer<ProjectExplorer::Project> project;
        bool preferDebug;
    };

    QList<ProjectToUpdate> m_projectsToUpdate;
    Internal::DebuggingHelperBuildTask *m_buildTask; // deletes itself after run()
    bool m_failed;
};
} // end of anonymous namespace

namespace Qt4ProjectManager {

static inline QStringList validBinaryFilenames(bool debugBuild)
{
    QStringList list = QStringList()
            << QLatin1String("qmldump.exe")
            << QLatin1String("qmldump")
            << QLatin1String("qmldump.app/Contents/MacOS/qmldump");
    if (debugBuild)
        list.prepend(QLatin1String("debug/qmldump.exe"));
    else
        list.prepend(QLatin1String("release/qmldump.exe"));
    return list;
}

bool QmlDumpTool::canBuild(const QtVersion *qtVersion)
{
    const QString installHeaders = qtVersion->versionInfo().value("QT_INSTALL_HEADERS");
    const QString header = installHeaders + QLatin1String("/QtDeclarative/private/qdeclarativemetatype_p.h");
    return (qtVersion->supportsTargetId(Constants::DESKTOP_TARGET_ID)
            || (qtVersion->supportsTargetId(Constants::QT_SIMULATOR_TARGET_ID)
                && (qtVersion->qtVersion() > QtVersionNumber(4, 7, 1))))
            && QFile::exists(header);
}

static QtVersion *qtVersionForProject(ProjectExplorer::Project *project)
{
    if (project && project->id() == Qt4ProjectManager::Constants::QT4PROJECT_ID) {
        Qt4Project *qt4Project = static_cast<Qt4Project*>(project);
        if (qt4Project && qt4Project->activeTarget()
                && qt4Project->activeTarget()->activeBuildConfiguration()) {
            QtVersion *version = qt4Project->activeTarget()->activeBuildConfiguration()->qtVersion();
            if (version->isValid())
                return version;
        }
        return 0;
    }

    if (project && project->id() == QLatin1String("QmlProjectManager.QmlProject")) {
        // We cannot access the QmlProject interfaces here, therefore use the metatype system
        if (!project->activeTarget() || !project->activeTarget()->activeRunConfiguration())
            return 0;
        QVariant variant = project->activeTarget()->activeRunConfiguration()->property("qtVersionId");
        QTC_ASSERT(variant.isValid() && variant.canConvert(QVariant::Int), return 0);
        QtVersion *version = QtVersionManager::instance()->version(variant.toInt());
        if (version && version->isValid())
            return version;
        return 0;
    }

    // else, find any desktop or simulator Qt version that has qmldump, or
    // - if there isn't any - one that could build it
    QtVersion *canBuildQmlDump = 0;
    QtVersionManager *qtVersions = QtVersionManager::instance();
    foreach (QtVersion *version, qtVersions->validVersions()) {
        if (version->supportsTargetId(Constants::DESKTOP_TARGET_ID)
                || version->supportsTargetId(Constants::QT_SIMULATOR_TARGET_ID)) {
            if (version->hasQmlDump())
                return version;

            if (!canBuildQmlDump && QmlDumpTool::canBuild(version)) {
                canBuildQmlDump = version;
            }
        }
    }

    return canBuildQmlDump;
}

QString QmlDumpTool::toolForProject(ProjectExplorer::Project *project, bool debugDump)
{
    QtVersion *version = qtVersionForProject(project);
    if (version) {
        QString qtInstallData = version->versionInfo().value("QT_INSTALL_DATA");
        QString toolPath = toolByInstallData(qtInstallData, debugDump);
        return toolPath;
    }

    return QString();
}

static QString sourcePath()
{
    return Core::ICore::instance()->resourcePath() + QLatin1String("/qml/qmldump/");
}

static QStringList sourceFileNames()
{
    QStringList files;
    files << QLatin1String("main.cpp") << QLatin1String("qmldump.pro")
          << QLatin1String("qmlstreamwriter.cpp") << QLatin1String("qmlstreamwriter.h")
          << QLatin1String("LICENSE.LGPL") << QLatin1String("LGPL_EXCEPTION.TXT")
          << QLatin1String("Info.plist");
    return files;
}

QString QmlDumpTool::toolByInstallData(const QString &qtInstallData, bool debugDump)
{
    if (!Core::ICore::instance())
        return QString();

    const QString mainFilename = Core::ICore::instance()->resourcePath()
            + QLatin1String("/qml/qmldump/main.cpp");
    const QStringList directories = installDirectories(qtInstallData);
    const QStringList binFilenames = validBinaryFilenames(debugDump);

    return byInstallDataHelper(sourcePath(), sourceFileNames(), directories, binFilenames);
}

QStringList QmlDumpTool::locationsByInstallData(const QString &qtInstallData, bool debugDump)
{
    QStringList result;
    QFileInfo fileInfo;
    const QStringList binFilenames = validBinaryFilenames(debugDump);
    foreach(const QString &directory, installDirectories(qtInstallData)) {
        if (getHelperFileInfoFor(binFilenames, directory, &fileInfo))
            result << fileInfo.filePath();
    }
    return result;
}

bool QmlDumpTool::build(const QString &directory, const QString &makeCommand,
                        const QString &qmakeCommand, const QString &mkspec,
                        const Utils::Environment &env, const QString &targetMode,
                        const QStringList &qmakeArguments, QString *output, QString *errorMessage)
{
    return buildHelper(QCoreApplication::translate("Qt4ProjectManager::QmlDumpTool", "qmldump"), QLatin1String("qmldump.pro"),
                       directory, makeCommand, qmakeCommand, mkspec, env, targetMode,
                       qmakeArguments, output, errorMessage);
}

QString QmlDumpTool::copy(const QString &qtInstallData, QString *errorMessage)
{
    const QStringList directories = QmlDumpTool::installDirectories(qtInstallData);

    // Try to find a writeable directory.
    foreach(const QString &directory, directories) {
        if (copyFiles(sourcePath(), sourceFileNames(), directory, errorMessage)) {
            return directory;
        }
    }
    *errorMessage = QCoreApplication::translate("ProjectExplorer::QmlDumpTool",
                                                "qmldump could not be built in any of the directories:\n- %1\n\nReason: %2")
                    .arg(directories.join(QLatin1String("\n- ")), *errorMessage);
    return QString();
}

QStringList QmlDumpTool::installDirectories(const QString &qtInstallData)
{
    const QChar slash = QLatin1Char('/');
    const uint hash = qHash(qtInstallData);
    QStringList directories;
    directories
            << (qtInstallData + QLatin1String("/qtc-qmldump/"))
            << QDir::cleanPath((QCoreApplication::applicationDirPath() + QLatin1String("/../qtc-qmldump/") + QString::number(hash))) + slash
            << (QDesktopServices::storageLocation(QDesktopServices::DataLocation) + QLatin1String("/qtc-qmldump/") + QString::number(hash)) + slash;
    return directories;
}

void QmlDumpTool::pathAndEnvironment(ProjectExplorer::Project *project, bool preferDebug,
                                     QString *dumperPath, Utils::Environment *env)
{
    QString path;

    QtVersion *version = qtVersionForProject(project);
    if (version && !version->hasQmlDump() && QmlDumpTool::canBuild(version)) {
        QmlDumpBuildTask *qmlDumpBuildTask = qmlDumpBuilds()->value(version->uniqueId());
        if (qmlDumpBuildTask) {
            if (!qmlDumpBuildTask->hasFailed())
                qmlDumpBuildTask->updateProjectWhenDone(project, preferDebug);
        } else {
            QmlDumpBuildTask *buildTask = new QmlDumpBuildTask(version);
            buildTask->updateProjectWhenDone(project, preferDebug);
            QFuture<void> task = QtConcurrent::run(&QmlDumpBuildTask::run, buildTask);
            const QString taskName = QmlDumpBuildTask::tr("Building helper");
            Core::ICore::instance()->progressManager()->addTask(task, taskName,
                                                                QLatin1String("Qt4ProjectManager::BuildHelpers"));
        }
        return;
    }

    path = Qt4ProjectManager::QmlDumpTool::toolForProject(project, preferDebug);
    if (path.isEmpty())
        path = Qt4ProjectManager::QmlDumpTool::toolForProject(project, !preferDebug);

    if (!path.isEmpty()) {
        QFileInfo qmldumpFileInfo(path);
        if (!qmldumpFileInfo.exists()) {
            qWarning() << "QmlDumpTool::qmlDumpPath: qmldump executable does not exist at" << path;
            path.clear();
        } else if (!qmldumpFileInfo.isFile()) {
            qWarning() << "QmlDumpTool::qmlDumpPath: " << path << " is not a file";
            path.clear();
        }
    }

    if (!path.isEmpty() && version && dumperPath && env) {
        *dumperPath = path;
        *env = version->qmlToolsEnvironment();
    }
}

} // namespace Qt4ProjectManager

#include "qmldumptool.moc"
<|endoftext|>


<commit_before>/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (info@qt.nokia.com)
**
**
** 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.
**
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
** If you have questions regarding the use of this file, please contact
** Nokia at info@qt.nokia.com.
**
**************************************************************************/

#include "refactoringchanges.h"
#include "basetexteditor.h"

#include <coreplugin/icore.h>
#include <coreplugin/editormanager/editormanager.h>
#include <extensionsystem/pluginmanager.h>

#include <utils/qtcassert.h>

#include <QtCore/QFile>
#include <QtCore/QSet>
#include <QtGui/QMainWindow>
#include <QtGui/QTextBlock>
#include <QtGui/QTextCursor>
#include <QtGui/QTextDocument>
#include <QtCore/QDebug>

using namespace TextEditor;

RefactoringChanges::RefactoringChanges()
    : m_data(new RefactoringChangesData)
{}

RefactoringChanges::RefactoringChanges(RefactoringChangesData *data)
    : m_data(data)
{}

RefactoringChanges::~RefactoringChanges()
{}

BaseTextEditorWidget *RefactoringChanges::editorForFile(const QString &fileName)
{
    Core::EditorManager *editorManager = Core::EditorManager::instance();

    const QList<Core::IEditor *> editors = editorManager->editorsForFileName(fileName);
    foreach (Core::IEditor *editor, editors) {
        BaseTextEditorWidget *textEditor = qobject_cast<BaseTextEditorWidget *>(editor->widget());
        if (textEditor != 0)
            return textEditor;
    }
    return 0;
}

QList<QTextCursor> RefactoringChanges::rangesToSelections(QTextDocument *document, const QList<Range> &ranges)
{
    QList<QTextCursor> selections;

    foreach (const Range &range, ranges) {
        QTextCursor selection(document);
        // ### workaround for moving the textcursor when inserting text at the beginning of the range.
        selection.setPosition(qMax(0, range.start - 1));
        selection.setPosition(qMin(range.end, document->characterCount() - 1), QTextCursor::KeepAnchor);

        selections.append(selection);
    }

    return selections;
}

bool RefactoringChanges::createFile(const QString &fileName, const QString &contents, bool reindent, bool openEditor) const
{
    if (QFile::exists(fileName))
        return false;

    BaseTextEditorWidget *editor = editorForFile(fileName);
    if (!editor && openEditor) {
        editor = this->openEditor(fileName, false, -1, -1);
    }

    QTextDocument *document;
    if (editor)
        document = editor->document();
    else
        document = new QTextDocument;

    {
        QTextCursor cursor(document);
        cursor.beginEditBlock();

        cursor.insertText(contents);

        if (reindent) {
            cursor.select(QTextCursor::Document);
            m_data->indentSelection(cursor, fileName, editor);
        }

        cursor.endEditBlock();
    }

    if (!editor) {
        Utils::TextFileFormat format;
        format.codec = Core::EditorManager::instance()->defaultTextCodec();
        QString error;
        bool saveOk = format.writeFile(fileName, document->toPlainText(), &error);
        delete document;
        if (!saveOk)
            return false;
    }

    m_data->fileChanged(fileName);

    return true;
}

bool RefactoringChanges::removeFile(const QString &fileName) const
{
    if (!QFile::exists(fileName))
        return false;

    // ### implement!
    qWarning() << "RefactoringChanges::removeFile is not implemented";
    return true;
}

BaseTextEditorWidget *RefactoringChanges::openEditor(const QString &fileName, bool activate, int line, int column)
{
    Core::EditorManager::OpenEditorFlags flags = Core::EditorManager::IgnoreNavigationHistory;
    if (!activate)
        flags |= Core::EditorManager::NoActivate;
    if (line != -1) {
        // openEditorAt uses a 1-based line and a 0-based column!
        column -= 1;
    }
    Core::IEditor *editor = BaseTextEditorWidget::openEditorAt(
                fileName, line, column, QString(), flags);
    return qobject_cast<BaseTextEditorWidget *>(editor->widget());
}

RefactoringFilePtr RefactoringChanges::file(BaseTextEditorWidget *editor)
{
    return RefactoringFilePtr(new RefactoringFile(editor));
}

RefactoringFilePtr RefactoringChanges::file(const QString &fileName) const
{
    return RefactoringFilePtr(new RefactoringFile(fileName, m_data));
}

RefactoringFile::RefactoringFile(QTextDocument *document, const QString &fileName)
    : m_fileName(fileName)
    , m_document(document)
    , m_editor(0)
    , m_openEditor(false)
    , m_activateEditor(false)
    , m_editorCursorPosition(-1)
{ }

RefactoringFile::RefactoringFile(BaseTextEditorWidget *editor)
    : m_fileName(editor->file()->fileName())
    , m_document(0)
    , m_editor(editor)
    , m_openEditor(false)
    , m_activateEditor(false)
    , m_editorCursorPosition(-1)
{ }

RefactoringFile::RefactoringFile(const QString &fileName, const QSharedPointer<RefactoringChangesData> &data)
    : m_fileName(fileName)
    , m_data(data)
    , m_document(0)
    , m_editor(0)
    , m_openEditor(false)
    , m_activateEditor(false)
    , m_editorCursorPosition(-1)
{
    m_editor = RefactoringChanges::editorForFile(fileName);
}

RefactoringFile::~RefactoringFile()
{
    delete m_document;
}

bool RefactoringFile::isValid() const
{
    if (m_fileName.isEmpty())
        return false;
    return document();
}

const QTextDocument *RefactoringFile::document() const
{
    return mutableDocument();
}

QTextDocument *RefactoringFile::mutableDocument() const
{
    if (m_editor)
        return m_editor->document();
    else if (!m_document) {
        QString fileContents;
        if (!m_fileName.isEmpty()) {
            QString error;
            QTextCodec *defaultCodec = Core::EditorManager::instance()->defaultTextCodec();
            Utils::TextFileFormat::ReadResult result = Utils::TextFileFormat::readFile(
                        m_fileName, defaultCodec,
                        &fileContents, &m_textFileFormat,
                        &error);
            if (result != Utils::TextFileFormat::ReadSuccess) {
                qWarning() << "Could not read " << m_fileName << ". Error: " << error;
                m_textFileFormat.codec = 0;
            }
        }
        // always make a QTextDocument to avoid excessive null checks
        m_document = new QTextDocument(fileContents);
    }
    return m_document;
}

const QTextCursor RefactoringFile::cursor() const
{
    if (m_editor)
        return m_editor->textCursor();
    else if (!m_fileName.isEmpty()) {
        if (QTextDocument *doc = mutableDocument())
            return QTextCursor(doc);
    }

    return QTextCursor();
}

QString RefactoringFile::fileName() const
{
    return m_fileName;
}

int RefactoringFile::position(unsigned line, unsigned column) const
{
    Q_ASSERT(line != 0);
    Q_ASSERT(column != 0);
    if (const QTextDocument *doc = document())
        return doc->findBlockByNumber(line - 1).position() + column - 1;
    return -1;
}

void RefactoringFile::lineAndColumn(int offset, unsigned *line, unsigned *column) const
{
    Q_ASSERT(line);
    Q_ASSERT(column);
    Q_ASSERT(offset >= 0);
    QTextCursor c(cursor());
    c.setPosition(offset);
    *line = c.blockNumber() + 1;
    *column = c.positionInBlock() + 1;
}

QChar RefactoringFile::charAt(int pos) const
{
    if (const QTextDocument *doc = document())
        return doc->characterAt(pos);
    return QChar();
}

QString RefactoringFile::textOf(int start, int end) const
{
    QTextCursor c = cursor();
    c.setPosition(start);
    c.setPosition(end, QTextCursor::KeepAnchor);
    return c.selectedText();
}

QString RefactoringFile::textOf(const Range &range) const
{
    return textOf(range.start, range.end);
}

void RefactoringFile::setChangeSet(const Utils::ChangeSet &changeSet)
{
    if (m_fileName.isEmpty())
        return;

    m_changes = changeSet;
}

void RefactoringFile::appendIndentRange(const Range &range)
{
    if (m_fileName.isEmpty())
        return;

    m_indentRanges.append(range);
}

void RefactoringFile::setOpenEditor(bool activate, int pos)
{
    m_openEditor = true;
    m_activateEditor = activate;
    m_editorCursorPosition = pos;
}

void RefactoringFile::apply()
{
    // open / activate / goto position
    if (m_openEditor && !m_fileName.isEmpty()) {
        unsigned line = -1, column = -1;
        if (m_editorCursorPosition != -1)
            lineAndColumn(m_editorCursorPosition, &line, &column);
        m_editor = RefactoringChanges::openEditor(m_fileName, m_activateEditor, line, column);
        m_openEditor = false;
        m_activateEditor = false;
        m_editorCursorPosition = -1;
    }

    // apply changes, if any
    if (m_data && !(m_indentRanges.isEmpty() && m_changes.isEmpty())) {
        QTextDocument *doc = mutableDocument();
        if (!doc)
            return;

        {
            QTextCursor c(doc);
            c.beginEditBlock();

            // build indent selections now, applying the changeset will change locations
            const QList<QTextCursor> &indentSelections =
                    RefactoringChanges::rangesToSelections(
                            doc, m_indentRanges);
            m_indentRanges.clear();

            // apply changes and reindent
            m_changes.apply(&c);
            m_changes.clear();
            foreach (const QTextCursor &selection, indentSelections) {
                m_data->indentSelection(selection, m_fileName, m_editor);
            }

            c.endEditBlock();
        }

        // if this document doesn't have an editor, write the result to a file
        if (!m_editor && m_textFileFormat.codec) {
            QTC_ASSERT(!m_fileName.isEmpty(), return);
            QString error;
            if (!m_textFileFormat.writeFile(m_fileName, doc->toPlainText(), &error))
                qWarning() << "Could not apply changes to" << m_fileName << ". Error: " << error;
        }

        fileChanged();
    }
}

void RefactoringFile::fileChanged()
{
    if (!m_fileName.isEmpty())
        m_data->fileChanged(m_fileName);
}

RefactoringChangesData::~RefactoringChangesData()
{}

void RefactoringChangesData::indentSelection(const QTextCursor &, const QString &, const BaseTextEditorWidget *) const
{
    qWarning() << Q_FUNC_INFO << "not implemented";
}

void RefactoringChangesData::fileChanged(const QString &)
{
}
<commit_msg>RefactoringFile: Use editor's cursor, if possible.<commit_after>/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (info@qt.nokia.com)
**
**
** 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.
**
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
** If you have questions regarding the use of this file, please contact
** Nokia at info@qt.nokia.com.
**
**************************************************************************/

#include "refactoringchanges.h"
#include "basetexteditor.h"

#include <coreplugin/icore.h>
#include <coreplugin/editormanager/editormanager.h>
#include <extensionsystem/pluginmanager.h>

#include <utils/qtcassert.h>

#include <QtCore/QFile>
#include <QtCore/QSet>
#include <QtGui/QMainWindow>
#include <QtGui/QTextBlock>
#include <QtGui/QTextCursor>
#include <QtGui/QTextDocument>
#include <QtCore/QDebug>

using namespace TextEditor;

RefactoringChanges::RefactoringChanges()
    : m_data(new RefactoringChangesData)
{}

RefactoringChanges::RefactoringChanges(RefactoringChangesData *data)
    : m_data(data)
{}

RefactoringChanges::~RefactoringChanges()
{}

BaseTextEditorWidget *RefactoringChanges::editorForFile(const QString &fileName)
{
    Core::EditorManager *editorManager = Core::EditorManager::instance();

    const QList<Core::IEditor *> editors = editorManager->editorsForFileName(fileName);
    foreach (Core::IEditor *editor, editors) {
        BaseTextEditorWidget *textEditor = qobject_cast<BaseTextEditorWidget *>(editor->widget());
        if (textEditor != 0)
            return textEditor;
    }
    return 0;
}

QList<QTextCursor> RefactoringChanges::rangesToSelections(QTextDocument *document, const QList<Range> &ranges)
{
    QList<QTextCursor> selections;

    foreach (const Range &range, ranges) {
        QTextCursor selection(document);
        // ### workaround for moving the textcursor when inserting text at the beginning of the range.
        selection.setPosition(qMax(0, range.start - 1));
        selection.setPosition(qMin(range.end, document->characterCount() - 1), QTextCursor::KeepAnchor);

        selections.append(selection);
    }

    return selections;
}

bool RefactoringChanges::createFile(const QString &fileName, const QString &contents, bool reindent, bool openEditor) const
{
    if (QFile::exists(fileName))
        return false;

    BaseTextEditorWidget *editor = editorForFile(fileName);
    if (!editor && openEditor) {
        editor = this->openEditor(fileName, false, -1, -1);
    }

    QTextDocument *document;
    if (editor)
        document = editor->document();
    else
        document = new QTextDocument;

    {
        QTextCursor cursor(document);
        cursor.beginEditBlock();

        cursor.insertText(contents);

        if (reindent) {
            cursor.select(QTextCursor::Document);
            m_data->indentSelection(cursor, fileName, editor);
        }

        cursor.endEditBlock();
    }

    if (!editor) {
        Utils::TextFileFormat format;
        format.codec = Core::EditorManager::instance()->defaultTextCodec();
        QString error;
        bool saveOk = format.writeFile(fileName, document->toPlainText(), &error);
        delete document;
        if (!saveOk)
            return false;
    }

    m_data->fileChanged(fileName);

    return true;
}

bool RefactoringChanges::removeFile(const QString &fileName) const
{
    if (!QFile::exists(fileName))
        return false;

    // ### implement!
    qWarning() << "RefactoringChanges::removeFile is not implemented";
    return true;
}

BaseTextEditorWidget *RefactoringChanges::openEditor(const QString &fileName, bool activate, int line, int column)
{
    Core::EditorManager::OpenEditorFlags flags = Core::EditorManager::IgnoreNavigationHistory;
    if (!activate)
        flags |= Core::EditorManager::NoActivate;
    if (line != -1) {
        // openEditorAt uses a 1-based line and a 0-based column!
        column -= 1;
    }
    Core::IEditor *editor = BaseTextEditorWidget::openEditorAt(
                fileName, line, column, QString(), flags);
    return qobject_cast<BaseTextEditorWidget *>(editor->widget());
}

RefactoringFilePtr RefactoringChanges::file(BaseTextEditorWidget *editor)
{
    return RefactoringFilePtr(new RefactoringFile(editor));
}

RefactoringFilePtr RefactoringChanges::file(const QString &fileName) const
{
    return RefactoringFilePtr(new RefactoringFile(fileName, m_data));
}

RefactoringFile::RefactoringFile(QTextDocument *document, const QString &fileName)
    : m_fileName(fileName)
    , m_document(document)
    , m_editor(0)
    , m_openEditor(false)
    , m_activateEditor(false)
    , m_editorCursorPosition(-1)
{ }

RefactoringFile::RefactoringFile(BaseTextEditorWidget *editor)
    : m_fileName(editor->file()->fileName())
    , m_document(0)
    , m_editor(editor)
    , m_openEditor(false)
    , m_activateEditor(false)
    , m_editorCursorPosition(-1)
{ }

RefactoringFile::RefactoringFile(const QString &fileName, const QSharedPointer<RefactoringChangesData> &data)
    : m_fileName(fileName)
    , m_data(data)
    , m_document(0)
    , m_editor(0)
    , m_openEditor(false)
    , m_activateEditor(false)
    , m_editorCursorPosition(-1)
{
    m_editor = RefactoringChanges::editorForFile(fileName);
}

RefactoringFile::~RefactoringFile()
{
    delete m_document;
}

bool RefactoringFile::isValid() const
{
    if (m_fileName.isEmpty())
        return false;
    return document();
}

const QTextDocument *RefactoringFile::document() const
{
    return mutableDocument();
}

QTextDocument *RefactoringFile::mutableDocument() const
{
    if (m_editor)
        return m_editor->document();
    else if (!m_document) {
        QString fileContents;
        if (!m_fileName.isEmpty()) {
            QString error;
            QTextCodec *defaultCodec = Core::EditorManager::instance()->defaultTextCodec();
            Utils::TextFileFormat::ReadResult result = Utils::TextFileFormat::readFile(
                        m_fileName, defaultCodec,
                        &fileContents, &m_textFileFormat,
                        &error);
            if (result != Utils::TextFileFormat::ReadSuccess) {
                qWarning() << "Could not read " << m_fileName << ". Error: " << error;
                m_textFileFormat.codec = 0;
            }
        }
        // always make a QTextDocument to avoid excessive null checks
        m_document = new QTextDocument(fileContents);
    }
    return m_document;
}

const QTextCursor RefactoringFile::cursor() const
{
    if (m_editor)
        return m_editor->textCursor();
    else if (!m_fileName.isEmpty()) {
        if (QTextDocument *doc = mutableDocument())
            return QTextCursor(doc);
    }

    return QTextCursor();
}

QString RefactoringFile::fileName() const
{
    return m_fileName;
}

int RefactoringFile::position(unsigned line, unsigned column) const
{
    Q_ASSERT(line != 0);
    Q_ASSERT(column != 0);
    if (const QTextDocument *doc = document())
        return doc->findBlockByNumber(line - 1).position() + column - 1;
    return -1;
}

void RefactoringFile::lineAndColumn(int offset, unsigned *line, unsigned *column) const
{
    Q_ASSERT(line);
    Q_ASSERT(column);
    Q_ASSERT(offset >= 0);
    QTextCursor c(cursor());
    c.setPosition(offset);
    *line = c.blockNumber() + 1;
    *column = c.positionInBlock() + 1;
}

QChar RefactoringFile::charAt(int pos) const
{
    if (const QTextDocument *doc = document())
        return doc->characterAt(pos);
    return QChar();
}

QString RefactoringFile::textOf(int start, int end) const
{
    QTextCursor c = cursor();
    c.setPosition(start);
    c.setPosition(end, QTextCursor::KeepAnchor);
    return c.selectedText();
}

QString RefactoringFile::textOf(const Range &range) const
{
    return textOf(range.start, range.end);
}

void RefactoringFile::setChangeSet(const Utils::ChangeSet &changeSet)
{
    if (m_fileName.isEmpty())
        return;

    m_changes = changeSet;
}

void RefactoringFile::appendIndentRange(const Range &range)
{
    if (m_fileName.isEmpty())
        return;

    m_indentRanges.append(range);
}

void RefactoringFile::setOpenEditor(bool activate, int pos)
{
    m_openEditor = true;
    m_activateEditor = activate;
    m_editorCursorPosition = pos;
}

void RefactoringFile::apply()
{
    // open / activate / goto position
    if (m_openEditor && !m_fileName.isEmpty()) {
        unsigned line = -1, column = -1;
        if (m_editorCursorPosition != -1)
            lineAndColumn(m_editorCursorPosition, &line, &column);
        m_editor = RefactoringChanges::openEditor(m_fileName, m_activateEditor, line, column);
        m_openEditor = false;
        m_activateEditor = false;
        m_editorCursorPosition = -1;
    }

    // apply changes, if any
    if (m_data && !(m_indentRanges.isEmpty() && m_changes.isEmpty())) {
        QTextDocument *doc = mutableDocument();
        if (!doc)
            return;

        {
            QTextCursor c = cursor();
            c.beginEditBlock();

            // build indent selections now, applying the changeset will change locations
            const QList<QTextCursor> &indentSelections =
                    RefactoringChanges::rangesToSelections(
                            doc, m_indentRanges);
            m_indentRanges.clear();

            // apply changes and reindent
            m_changes.apply(&c);
            m_changes.clear();
            foreach (const QTextCursor &selection, indentSelections) {
                m_data->indentSelection(selection, m_fileName, m_editor);
            }

            c.endEditBlock();
        }

        // if this document doesn't have an editor, write the result to a file
        if (!m_editor && m_textFileFormat.codec) {
            QTC_ASSERT(!m_fileName.isEmpty(), return);
            QString error;
            if (!m_textFileFormat.writeFile(m_fileName, doc->toPlainText(), &error))
                qWarning() << "Could not apply changes to" << m_fileName << ". Error: " << error;
        }

        fileChanged();
    }
}

void RefactoringFile::fileChanged()
{
    if (!m_fileName.isEmpty())
        m_data->fileChanged(m_fileName);
}

RefactoringChangesData::~RefactoringChangesData()
{}

void RefactoringChangesData::indentSelection(const QTextCursor &, const QString &, const BaseTextEditorWidget *) const
{
    qWarning() << Q_FUNC_INFO << "not implemented";
}

void RefactoringChangesData::fileChanged(const QString &)
{
}
<|endoftext|>


<commit_before>#include "widgets/helper/NotebookTab.hpp"

#include "Application.hpp"
#include "common/Common.hpp"
#include "debug/Log.hpp"
#include "singletons/Fonts.hpp"
#include "singletons/Settings.hpp"
#include "singletons/Theme.hpp"
#include "util/Clamp.hpp"
#include "util/Helpers.hpp"
#include "widgets/Notebook.hpp"
#include "widgets/dialogs/SettingsDialog.hpp"
#include "widgets/dialogs/TextInputDialog.hpp"
#include "widgets/splits/SplitContainer.hpp"

#include <QApplication>
#include <QDebug>
#include <QLinearGradient>
#include <QMimeData>
#include <QPainter>
#include <boost/bind.hpp>

namespace chatterino {

NotebookTab::NotebookTab(Notebook *notebook)
    : Button(notebook)
    , positionChangedAnimation_(this, "pos")
    , notebook_(notebook)
    , menu_(this)
{
    auto app = getApp();

    this->setAcceptDrops(true);

    this->positionChangedAnimation_.setEasingCurve(
        QEasingCurve(QEasingCurve::InCubic));

    getSettings()->showTabCloseButton.connect(
        boost::bind(&NotebookTab::hideTabXChanged, this, _1),
        this->managedConnections_);

    this->setMouseTracking(true);

    this->menu_.addAction("Rename", [this]() {
        TextInputDialog d(this);

        d.setWindowTitle(
            "Change tab title (Leave empty for default behaviour)");
        d.setText(this->getCustomTitle());
        d.highlightText();

        if (d.exec() == QDialog::Accepted) {
            QString newTitle = d.getText();
            this->setCustomTitle(newTitle);
        }
    });

    //    QAction *enableHighlightsOnNewMessageAction =
    //        new QAction("Enable highlights on new message", &this->menu);
    //    enableHighlightsOnNewMessageAction->setCheckable(true);

    this->menu_.addAction("Close",
                          [=]() { this->notebook_->removePage(this->page); });

    auto highlightNewMessagesAction = new QAction("Enable highlights on new messages", &this->menu_);
    highlightNewMessagesAction->setCheckable(true);
    highlightNewMessagesAction->setChecked(true);
    QObject::connect(highlightNewMessagesAction, &QAction::triggered, [this] (bool checked) {
        this->highlightEnabled = checked;
    });
    this->menu_.addAction(highlightNewMessagesAction);
}

void NotebookTab::themeChangedEvent()
{
    this->update();

    //    this->setMouseEffectColor(QColor("#999"));
    this->setMouseEffectColor(this->theme->tabs.regular.text);
}

void NotebookTab::updateSize()
{
    float scale = getScale();

    int width;
    QFontMetrics metrics = getApp()->fonts->getFontMetrics(
        FontStyle::UiTabs,
        float(qreal(this->getScale()) * this->devicePixelRatioF()));

    if (this->hasXButton()) {
        width = (metrics.width(this->getTitle()) + int(32 * scale));
    } else {
        width = (metrics.width(this->getTitle()) + int(16 * scale));
    }

    width = clamp(width, this->height(), int(150 * scale));

    if (this->width() != width) {
        this->resize(width, int(NOTEBOOK_TAB_HEIGHT * scale));
        this->notebook_->performLayout();
    }
}

const QString &NotebookTab::getCustomTitle() const
{
    return this->customTitle_;
}

void NotebookTab::setCustomTitle(const QString &newTitle)
{
    if (this->customTitle_ != newTitle) {
        this->customTitle_ = newTitle;
        this->titleUpdated();
    }
}

void NotebookTab::resetCustomTitle()
{
    this->setCustomTitle(QString());
}

bool NotebookTab::hasCustomTitle() const
{
    return !this->customTitle_.isEmpty();
}

void NotebookTab::setDefaultTitle(const QString &title)
{
    if (this->defaultTitle_ != title) {
        this->defaultTitle_ = title;

        if (this->customTitle_.isEmpty()) {
            this->titleUpdated();
        }
    }
}

const QString &NotebookTab::getDefaultTitle() const
{
    return this->defaultTitle_;
}

const QString &NotebookTab::getTitle() const
{
    return this->customTitle_.isEmpty() ? this->defaultTitle_
                                        : this->customTitle_;
}

void NotebookTab::titleUpdated()
{
    this->updateSize();
    this->update();
}

bool NotebookTab::isSelected() const
{
    return this->selected_;
}

void NotebookTab::setSelected(bool value)
{
    this->selected_ = value;

    this->highlightState_ = HighlightState::None;

    this->update();
}

void NotebookTab::setHighlightState(HighlightState newHighlightStyle)
{
    if (this->isSelected() || !this->highlightEnabled) {
        return;
    }
    if (this->highlightState_ != HighlightState::Highlighted &&
        this->highlightState_ != HighlightState::Notification) {
        this->highlightState_ = newHighlightStyle;

        this->update();
    }
}

QRect NotebookTab::getDesiredRect() const
{
    return QRect(this->positionAnimationDesiredPoint_, size());
}

void NotebookTab::hideTabXChanged(bool)
{
    this->updateSize();
    this->update();
}

void NotebookTab::moveAnimated(QPoint pos, bool animated)
{
    this->positionAnimationDesiredPoint_ = pos;

    QWidget *w = this->window();

    if ((w != nullptr && !w->isVisible()) || !animated ||
        !this->positionChangedAnimationRunning_) {
        this->move(pos);

        this->positionChangedAnimationRunning_ = true;
        return;
    }

    if (this->positionChangedAnimation_.endValue() == pos) {
        return;
    }

    this->positionChangedAnimation_.stop();
    this->positionChangedAnimation_.setDuration(75);
    this->positionChangedAnimation_.setStartValue(this->pos());
    this->positionChangedAnimation_.setEndValue(pos);
    this->positionChangedAnimation_.start();
}

void NotebookTab::paintEvent(QPaintEvent *)
{
    auto app = getApp();
    QPainter painter(this);
    float scale = this->getScale();

    painter.setFont(getApp()->fonts->getFont(
        FontStyle::UiTabs,
        scale * 96.f / this->logicalDpiX() * this->devicePixelRatioF()));
    QFontMetrics metrics = app->fonts->getFontMetrics(
        FontStyle::UiTabs,
        scale * 96.f / this->logicalDpiX() * this->devicePixelRatioF());

    int height = int(scale * NOTEBOOK_TAB_HEIGHT);
    //    int fullHeight = (int)(scale * 48);

    // select the right tab colors
    Theme::TabColors colors;
    Theme::TabColors regular = this->theme->tabs.regular;

    if (this->selected_) {
        colors = this->theme->tabs.selected;
    } else if (this->highlightState_ == HighlightState::Highlighted) {
        colors = this->theme->tabs.highlighted;
    } else if (this->highlightState_ == HighlightState::Notification) {
        colors = this->theme->tabs.notified;
    } else if (this->highlightState_ == HighlightState::NewMessage) {
        colors = this->theme->tabs.newMessage;
    } else {
        colors = this->theme->tabs.regular;
    }

    bool windowFocused = this->window() == QApplication::activeWindow();
    // || SettingsDialog::getHandle() == QApplication::activeWindow();

    QBrush tabBackground = /*this->mouseOver_ ? colors.backgrounds.hover
                                            :*/
        (windowFocused ? colors.backgrounds.regular
                       : colors.backgrounds.unfocused);

    //    painter.fillRect(rect(), this->mouseOver_ ? regular.backgrounds.hover
    //                                              : (windowFocused ?
    //                                              regular.backgrounds.regular
    //                                                               :
    //                                                               regular.backgrounds.unfocused));

    // fill the tab background
    auto bgRect = rect();
    bgRect.setTop(bgRect.top() + 2);

    painter.fillRect(bgRect, tabBackground);

    // draw border
    //    painter.setPen(QPen("#fff"));
    //    QPainterPath path(QPointF(0, height));
    //    path.lineTo(0, 0);
    //    path.lineTo(this->width() - 1, 0);
    //    path.lineTo(this->width() - 1, this->height() - 1);
    //    path.lineTo(0, this->height() - 1);
    //    painter.drawPath(path);

    // top line
    painter.fillRect(
        QRectF(0, (this->selected_ ? 0.f : 1.f) * scale, this->width(),
               (this->selected_ ? 2.f : 1.f) * scale),
        this->mouseOver_
            ? colors.line.hover
            : (windowFocused ? colors.line.regular : colors.line.unfocused));

    // set the pen color
    painter.setPen(colors.text);

    // set area for text
    int rectW = (!getSettings()->showTabCloseButton ? 0 : int(16 * scale));
    QRect rect(0, 0, this->width() - rectW, height);

    // draw text
    int offset = int(scale * 8);
    QRect textRect(offset, this->selected_ ? 1 : 2,
                   this->width() - offset - offset, height);

    if (this->shouldDrawXButton()) {
        textRect.setRight(textRect.right() - this->height() / 2);
    }

    int width = metrics.width(this->getTitle());
    Qt::Alignment alignment = width > textRect.width()
                                  ? Qt::AlignLeft | Qt::AlignVCenter
                                  : Qt::AlignHCenter | Qt::AlignVCenter;

    QTextOption option(alignment);
    option.setWrapMode(QTextOption::NoWrap);
    painter.drawText(textRect, this->getTitle(), option);

    // draw close x
    if (this->shouldDrawXButton()) {
        QRect xRect = this->getXRect();
        if (!xRect.isNull()) {
            if (this->selected_) xRect.moveTop(xRect.top() - 1);

            painter.setBrush(QColor("#fff"));

            if (this->mouseOverX_) {
                painter.fillRect(xRect, QColor(0, 0, 0, 64));

                if (this->mouseDownX_) {
                    painter.fillRect(xRect, QColor(0, 0, 0, 64));
                }
            }

            int a = static_cast<int>(scale * 4);

            painter.drawLine(xRect.topLeft() + QPoint(a, a),
                             xRect.bottomRight() + QPoint(-a, -a));
            painter.drawLine(xRect.topRight() + QPoint(-a, a),
                             xRect.bottomLeft() + QPoint(a, -a));
        }
    }

    // draw line at bottom
    if (!this->selected_) {
        painter.fillRect(0, this->height() - 1, this->width(), 1,
                         app->themes->window.background);

        this->fancyPaint(painter);
    }
}

bool NotebookTab::hasXButton()
{
    return getSettings()->showTabCloseButton &&
           this->notebook_->getAllowUserTabManagement();
}

bool NotebookTab::shouldDrawXButton()
{
    return this->hasXButton() && (this->mouseOver_ || this->selected_);
}

void NotebookTab::mousePressEvent(QMouseEvent *event)
{
    this->mouseDown_ = true;
    this->mouseDownX_ = this->getXRect().contains(event->pos());

    this->update();

    this->notebook_->select(page);

    if (this->notebook_->getAllowUserTabManagement()) {
        switch (event->button()) {
            case Qt::RightButton: {
                this->menu_.popup(event->globalPos());
            } break;
            default:;
        }
    }
}

void NotebookTab::mouseReleaseEvent(QMouseEvent *event)
{
    this->mouseDown_ = false;

    auto removeThisPage = [this] {
        auto reply = QMessageBox::question(
            this, "Remove this tab",
            "Are you sure that you want to remove this tab?",
            QMessageBox::Yes | QMessageBox::Cancel);

        if (reply == QMessageBox::Yes) {
            this->notebook_->removePage(this->page);
        }
    };

    if (event->button() == Qt::MiddleButton) {
        if (this->rect().contains(event->pos())) {
            removeThisPage();
        }
    } else {
        if (this->hasXButton() && this->mouseDownX_ &&
            this->getXRect().contains(event->pos())) {
            this->mouseDownX_ = false;

            removeThisPage();
        } else {
            this->update();
        }
    }
}

void NotebookTab::enterEvent(QEvent *event)
{
    this->mouseOver_ = true;

    this->update();

    Button::enterEvent(event);
}

void NotebookTab::leaveEvent(QEvent *event)
{
    this->mouseOverX_ = false;
    this->mouseOver_ = false;

    this->update();

    Button::leaveEvent(event);
}

void NotebookTab::dragEnterEvent(QDragEnterEvent *event)
{
    if (!event->mimeData()->hasFormat("chatterino/split")) return;

    if (!SplitContainer::isDraggingSplit) return;

    if (this->notebook_->getAllowUserTabManagement()) {
        this->notebook_->select(this->page);
    }
}

void NotebookTab::mouseMoveEvent(QMouseEvent *event)
{
    auto app = getApp();

    if (getSettings()->showTabCloseButton &&
        this->notebook_->getAllowUserTabManagement())  //
    {
        bool overX = this->getXRect().contains(event->pos());

        if (overX != this->mouseOverX_) {
            // Over X state has been changed (we either left or entered it;
            this->mouseOverX_ = overX;

            this->update();
        }
    }

    QPoint relPoint = this->mapToParent(event->pos());

    if (this->mouseDown_ && !this->getDesiredRect().contains(relPoint) &&
        this->notebook_->getAllowUserTabManagement())  //
    {
        int index;
        QWidget *clickedPage =
            this->notebook_->tabAt(relPoint, index, this->width());

        if (clickedPage != nullptr && clickedPage != this->page) {
            this->notebook_->rearrangePage(this->page, index);
        }
    }

    Button::mouseMoveEvent(event);
}

QRect NotebookTab::getXRect()
{
    //    if (!this->notebook->getAllowUserTabManagement()) {
    //        return QRect();
    //    }

    float s = this->getScale();
    return QRect(this->width() - static_cast<int>(20 * s),
                 static_cast<int>(9 * s), static_cast<int>(16 * s),
                 static_cast<int>(16 * s));
}

}  // namespace chatterino
<commit_msg>Don't switch tab if only right mouse is pressed on other tab.<commit_after>#include "widgets/helper/NotebookTab.hpp"

#include "Application.hpp"
#include "common/Common.hpp"
#include "debug/Log.hpp"
#include "singletons/Fonts.hpp"
#include "singletons/Settings.hpp"
#include "singletons/Theme.hpp"
#include "util/Clamp.hpp"
#include "util/Helpers.hpp"
#include "widgets/Notebook.hpp"
#include "widgets/dialogs/SettingsDialog.hpp"
#include "widgets/dialogs/TextInputDialog.hpp"
#include "widgets/splits/SplitContainer.hpp"

#include <QApplication>
#include <QDebug>
#include <QLinearGradient>
#include <QMimeData>
#include <QPainter>
#include <boost/bind.hpp>

namespace chatterino {

NotebookTab::NotebookTab(Notebook *notebook)
    : Button(notebook)
    , positionChangedAnimation_(this, "pos")
    , notebook_(notebook)
    , menu_(this)
{
    auto app = getApp();

    this->setAcceptDrops(true);

    this->positionChangedAnimation_.setEasingCurve(
        QEasingCurve(QEasingCurve::InCubic));

    getSettings()->showTabCloseButton.connect(
        boost::bind(&NotebookTab::hideTabXChanged, this, _1),
        this->managedConnections_);

    this->setMouseTracking(true);

    this->menu_.addAction("Rename", [this]() {
        TextInputDialog d(this);

        d.setWindowTitle(
            "Change tab title (Leave empty for default behaviour)");
        d.setText(this->getCustomTitle());
        d.highlightText();

        if (d.exec() == QDialog::Accepted) {
            QString newTitle = d.getText();
            this->setCustomTitle(newTitle);
        }
    });

    //    QAction *enableHighlightsOnNewMessageAction =
    //        new QAction("Enable highlights on new message", &this->menu);
    //    enableHighlightsOnNewMessageAction->setCheckable(true);

    this->menu_.addAction("Close",
                          [=]() { this->notebook_->removePage(this->page); });

    auto highlightNewMessagesAction = new QAction("Enable highlights on new messages", &this->menu_);
    highlightNewMessagesAction->setCheckable(true);
    highlightNewMessagesAction->setChecked(true);
    QObject::connect(highlightNewMessagesAction, &QAction::triggered, [this] (bool checked) {
        this->highlightEnabled = checked;
    });
    this->menu_.addAction(highlightNewMessagesAction);
}

void NotebookTab::themeChangedEvent()
{
    this->update();

    //    this->setMouseEffectColor(QColor("#999"));
    this->setMouseEffectColor(this->theme->tabs.regular.text);
}

void NotebookTab::updateSize()
{
    float scale = getScale();

    int width;
    QFontMetrics metrics = getApp()->fonts->getFontMetrics(
        FontStyle::UiTabs,
        float(qreal(this->getScale()) * this->devicePixelRatioF()));

    if (this->hasXButton()) {
        width = (metrics.width(this->getTitle()) + int(32 * scale));
    } else {
        width = (metrics.width(this->getTitle()) + int(16 * scale));
    }

    width = clamp(width, this->height(), int(150 * scale));

    if (this->width() != width) {
        this->resize(width, int(NOTEBOOK_TAB_HEIGHT * scale));
        this->notebook_->performLayout();
    }
}

const QString &NotebookTab::getCustomTitle() const
{
    return this->customTitle_;
}

void NotebookTab::setCustomTitle(const QString &newTitle)
{
    if (this->customTitle_ != newTitle) {
        this->customTitle_ = newTitle;
        this->titleUpdated();
    }
}

void NotebookTab::resetCustomTitle()
{
    this->setCustomTitle(QString());
}

bool NotebookTab::hasCustomTitle() const
{
    return !this->customTitle_.isEmpty();
}

void NotebookTab::setDefaultTitle(const QString &title)
{
    if (this->defaultTitle_ != title) {
        this->defaultTitle_ = title;

        if (this->customTitle_.isEmpty()) {
            this->titleUpdated();
        }
    }
}

const QString &NotebookTab::getDefaultTitle() const
{
    return this->defaultTitle_;
}

const QString &NotebookTab::getTitle() const
{
    return this->customTitle_.isEmpty() ? this->defaultTitle_
                                        : this->customTitle_;
}

void NotebookTab::titleUpdated()
{
    this->updateSize();
    this->update();
}

bool NotebookTab::isSelected() const
{
    return this->selected_;
}

void NotebookTab::setSelected(bool value)
{
    this->selected_ = value;

    this->highlightState_ = HighlightState::None;

    this->update();
}

void NotebookTab::setHighlightState(HighlightState newHighlightStyle)
{
    if (this->isSelected() || !this->highlightEnabled) {
        return;
    }
    if (this->highlightState_ != HighlightState::Highlighted &&
        this->highlightState_ != HighlightState::Notification) {
        this->highlightState_ = newHighlightStyle;

        this->update();
    }
}

QRect NotebookTab::getDesiredRect() const
{
    return QRect(this->positionAnimationDesiredPoint_, size());
}

void NotebookTab::hideTabXChanged(bool)
{
    this->updateSize();
    this->update();
}

void NotebookTab::moveAnimated(QPoint pos, bool animated)
{
    this->positionAnimationDesiredPoint_ = pos;

    QWidget *w = this->window();

    if ((w != nullptr && !w->isVisible()) || !animated ||
        !this->positionChangedAnimationRunning_) {
        this->move(pos);

        this->positionChangedAnimationRunning_ = true;
        return;
    }

    if (this->positionChangedAnimation_.endValue() == pos) {
        return;
    }

    this->positionChangedAnimation_.stop();
    this->positionChangedAnimation_.setDuration(75);
    this->positionChangedAnimation_.setStartValue(this->pos());
    this->positionChangedAnimation_.setEndValue(pos);
    this->positionChangedAnimation_.start();
}

void NotebookTab::paintEvent(QPaintEvent *)
{
    auto app = getApp();
    QPainter painter(this);
    float scale = this->getScale();

    painter.setFont(getApp()->fonts->getFont(
        FontStyle::UiTabs,
        scale * 96.f / this->logicalDpiX() * this->devicePixelRatioF()));
    QFontMetrics metrics = app->fonts->getFontMetrics(
        FontStyle::UiTabs,
        scale * 96.f / this->logicalDpiX() * this->devicePixelRatioF());

    int height = int(scale * NOTEBOOK_TAB_HEIGHT);
    //    int fullHeight = (int)(scale * 48);

    // select the right tab colors
    Theme::TabColors colors;
    Theme::TabColors regular = this->theme->tabs.regular;

    if (this->selected_) {
        colors = this->theme->tabs.selected;
    } else if (this->highlightState_ == HighlightState::Highlighted) {
        colors = this->theme->tabs.highlighted;
    } else if (this->highlightState_ == HighlightState::Notification) {
        colors = this->theme->tabs.notified;
    } else if (this->highlightState_ == HighlightState::NewMessage) {
        colors = this->theme->tabs.newMessage;
    } else {
        colors = this->theme->tabs.regular;
    }

    bool windowFocused = this->window() == QApplication::activeWindow();
    // || SettingsDialog::getHandle() == QApplication::activeWindow();

    QBrush tabBackground = /*this->mouseOver_ ? colors.backgrounds.hover
                                            :*/
        (windowFocused ? colors.backgrounds.regular
                       : colors.backgrounds.unfocused);

    //    painter.fillRect(rect(), this->mouseOver_ ? regular.backgrounds.hover
    //                                              : (windowFocused ?
    //                                              regular.backgrounds.regular
    //                                                               :
    //                                                               regular.backgrounds.unfocused));

    // fill the tab background
    auto bgRect = rect();
    bgRect.setTop(bgRect.top() + 2);

    painter.fillRect(bgRect, tabBackground);

    // draw border
    //    painter.setPen(QPen("#fff"));
    //    QPainterPath path(QPointF(0, height));
    //    path.lineTo(0, 0);
    //    path.lineTo(this->width() - 1, 0);
    //    path.lineTo(this->width() - 1, this->height() - 1);
    //    path.lineTo(0, this->height() - 1);
    //    painter.drawPath(path);

    // top line
    painter.fillRect(
        QRectF(0, (this->selected_ ? 0.f : 1.f) * scale, this->width(),
               (this->selected_ ? 2.f : 1.f) * scale),
        this->mouseOver_
            ? colors.line.hover
            : (windowFocused ? colors.line.regular : colors.line.unfocused));

    // set the pen color
    painter.setPen(colors.text);

    // set area for text
    int rectW = (!getSettings()->showTabCloseButton ? 0 : int(16 * scale));
    QRect rect(0, 0, this->width() - rectW, height);

    // draw text
    int offset = int(scale * 8);
    QRect textRect(offset, this->selected_ ? 1 : 2,
                   this->width() - offset - offset, height);

    if (this->shouldDrawXButton()) {
        textRect.setRight(textRect.right() - this->height() / 2);
    }

    int width = metrics.width(this->getTitle());
    Qt::Alignment alignment = width > textRect.width()
                                  ? Qt::AlignLeft | Qt::AlignVCenter
                                  : Qt::AlignHCenter | Qt::AlignVCenter;

    QTextOption option(alignment);
    option.setWrapMode(QTextOption::NoWrap);
    painter.drawText(textRect, this->getTitle(), option);

    // draw close x
    if (this->shouldDrawXButton()) {
        QRect xRect = this->getXRect();
        if (!xRect.isNull()) {
            if (this->selected_) xRect.moveTop(xRect.top() - 1);

            painter.setBrush(QColor("#fff"));

            if (this->mouseOverX_) {
                painter.fillRect(xRect, QColor(0, 0, 0, 64));

                if (this->mouseDownX_) {
                    painter.fillRect(xRect, QColor(0, 0, 0, 64));
                }
            }

            int a = static_cast<int>(scale * 4);

            painter.drawLine(xRect.topLeft() + QPoint(a, a),
                             xRect.bottomRight() + QPoint(-a, -a));
            painter.drawLine(xRect.topRight() + QPoint(-a, a),
                             xRect.bottomLeft() + QPoint(a, -a));
        }
    }

    // draw line at bottom
    if (!this->selected_) {
        painter.fillRect(0, this->height() - 1, this->width(), 1,
                         app->themes->window.background);

        this->fancyPaint(painter);
    }
}

bool NotebookTab::hasXButton()
{
    return getSettings()->showTabCloseButton &&
           this->notebook_->getAllowUserTabManagement();
}

bool NotebookTab::shouldDrawXButton()
{
    return this->hasXButton() && (this->mouseOver_ || this->selected_);
}

void NotebookTab::mousePressEvent(QMouseEvent *event)
{
    if (event->button() == Qt::LeftButton) {
        this->mouseDown_ = true;
        this->mouseDownX_ = this->getXRect().contains(event->pos());

        this->update();

        this->notebook_->select(page);
    }

    if (this->notebook_->getAllowUserTabManagement()) {
        switch (event->button()) {
            case Qt::RightButton: {
                this->menu_.popup(event->globalPos());
            } break;
            default:;
        }
    }
}

void NotebookTab::mouseReleaseEvent(QMouseEvent *event)
{
    this->mouseDown_ = false;

    auto removeThisPage = [this] {
        auto reply = QMessageBox::question(
            this, "Remove this tab",
            "Are you sure that you want to remove this tab?",
            QMessageBox::Yes | QMessageBox::Cancel);

        if (reply == QMessageBox::Yes) {
            this->notebook_->removePage(this->page);
        }
    };

    if (event->button() == Qt::MiddleButton) {
        if (this->rect().contains(event->pos())) {
            removeThisPage();
        }
    } else {
        if (this->hasXButton() && this->mouseDownX_ &&
            this->getXRect().contains(event->pos())) {
            this->mouseDownX_ = false;

            removeThisPage();
        } else {
            this->update();
        }
    }
}

void NotebookTab::enterEvent(QEvent *event)
{
    this->mouseOver_ = true;

    this->update();

    Button::enterEvent(event);
}

void NotebookTab::leaveEvent(QEvent *event)
{
    this->mouseOverX_ = false;
    this->mouseOver_ = false;

    this->update();

    Button::leaveEvent(event);
}

void NotebookTab::dragEnterEvent(QDragEnterEvent *event)
{
    if (!event->mimeData()->hasFormat("chatterino/split")) return;

    if (!SplitContainer::isDraggingSplit) return;

    if (this->notebook_->getAllowUserTabManagement()) {
        this->notebook_->select(this->page);
    }
}

void NotebookTab::mouseMoveEvent(QMouseEvent *event)
{
    auto app = getApp();

    if (getSettings()->showTabCloseButton &&
        this->notebook_->getAllowUserTabManagement())  //
    {
        bool overX = this->getXRect().contains(event->pos());

        if (overX != this->mouseOverX_) {
            // Over X state has been changed (we either left or entered it;
            this->mouseOverX_ = overX;

            this->update();
        }
    }

    QPoint relPoint = this->mapToParent(event->pos());

    if (this->mouseDown_ && !this->getDesiredRect().contains(relPoint) &&
        this->notebook_->getAllowUserTabManagement())  //
    {
        int index;
        QWidget *clickedPage =
            this->notebook_->tabAt(relPoint, index, this->width());

        if (clickedPage != nullptr && clickedPage != this->page) {
            this->notebook_->rearrangePage(this->page, index);
        }
    }

    Button::mouseMoveEvent(event);
}

QRect NotebookTab::getXRect()
{
    //    if (!this->notebook->getAllowUserTabManagement()) {
    //        return QRect();
    //    }

    float s = this->getScale();
    return QRect(this->width() - static_cast<int>(20 * s),
                 static_cast<int>(9 * s), static_cast<int>(16 * s),
                 static_cast<int>(16 * s));
}

}  // namespace chatterino
<|endoftext|>