<commit_before>#include "OpenSpeedShopWidget.h"
#include "ui_OpenSpeedShopWidget.h"

#include <MainWindow/MainWindow.h>

#include <Experiment/ExperimentWidget.h>

namespace Plugins {
namespace OpenSpeedShop {

OpenSpeedShopWidget::OpenSpeedShopWidget(QWidget *parent) :
    QTabWidget(parent),
    ui(new Ui::OpenSpeedShopWidget)
{
    m_CreateExperiment = NULL;
    m_LoadExperiment = NULL;
    m_CloseExperiment = NULL;

    ui->setupUi(this);
    connect(this, SIGNAL(tabCloseRequested(int)), this, SLOT(closeExperiment(int)));

    /* Set up the stylesheet for periods when we have tabs */
    m_StyleSheet = styleSheet();

    setWindowTitle(QString("Open|SpeedShop%1").arg(QChar(0x2122))); //Trademark
    setWindowIcon(QIcon(":/OpenSpeedShop/app.png"));

    Core::MainWindow::MainWindow &mainWindow = Core::MainWindow::MainWindow::instance();
    foreach(QAction *action, mainWindow.menuBar()->actions()) {
        if(action->text() == tr("File")) {

            m_CreateExperiment = new QAction(tr("New O|SS Experiment"), this);
            m_CreateExperiment->setToolTip(tr("Create a new Open|SpeedShop experiment"));
            m_CreateExperiment->setIcon(QIcon(":/OpenSpeedShop/app.png"));
            m_CreateExperiment->setIconVisibleInMenu(true);
            m_CreateExperiment->setVisible(false);
            m_CreateExperiment->setProperty("oss_menuitem", QVariant(1));
            connect(m_CreateExperiment, SIGNAL(triggered()), this, SLOT(createExperiment()));

            //FIXME: When we're set up to create, enable this
            m_CreateExperiment->setEnabled(false);

            m_LoadExperiment = new QAction(tr("Load O|SS Experiment"), this);
            m_LoadExperiment->setToolTip(tr("Load an Open|SpeedShop experiment"));
            m_LoadExperiment->setIcon(QIcon(":/OpenSpeedShop/app.png"));
            m_LoadExperiment->setIconVisibleInMenu(true);
            m_LoadExperiment->setVisible(false);
            m_LoadExperiment->setProperty("oss_menuitem", QVariant(1));
            connect(m_LoadExperiment, SIGNAL(triggered()), this, SLOT(loadExperiment()));

            m_CloseExperiment = new QAction(tr("Close O|SS Experiment"), this);
            m_CloseExperiment->setToolTip(tr("Close the current Open|SpeedShop experiment"));
            m_CloseExperiment->setIcon(QIcon(":/OpenSpeedShop/app.png"));
            m_CloseExperiment->setIconVisibleInMenu(true);
            m_CloseExperiment->setEnabled(false);
            m_CloseExperiment->setVisible(false);
            m_CloseExperiment->setProperty("oss_menuitem", QVariant(1));
            connect(m_CloseExperiment, SIGNAL(triggered()), this, SLOT(closeExperiment()));

            //! \todo We really need to rely on the ActionManager to do this.
            QAction *before = NULL;
            foreach(QAction *item, action->menu()->actions()) {
                if(item->priority() == QAction::LowPriority) {
                    before = item;
                }
            }

            if(before) {
                action->menu()->insertAction(before, m_CreateExperiment);
                action->menu()->insertAction(before, m_LoadExperiment);
                action->menu()->insertAction(before, m_CloseExperiment);
            } else {
                action->menu()->addAction(m_CreateExperiment);
                action->menu()->addAction(m_LoadExperiment);
                action->menu()->addAction(m_CloseExperiment);
            }

        }
    }
}

OpenSpeedShopWidget::~OpenSpeedShopWidget()
{
    delete ui;
}

void OpenSpeedShopWidget::createExperiment()
{
    try {

        Core::MainWindow::MainWindow &mainWindow = Core::MainWindow::MainWindow::instance();
        mainWindow.setCurrentCentralWidget(this);

        ExperimentWidget *experimentWidget = new ExperimentWidget(this);
        experimentWidget->create();
        int index = addTab(experimentWidget, experimentWidget->windowTitle());
        setCurrentIndex(index);

        connect(experimentWidget, SIGNAL(windowTitleChanged()), this, SLOT(tabTitleChanged()));

    } catch(QString err) {
        using namespace Core::MainWindow;
        MainWindow::instance().notify(tr("Failed to create experiment: %1").arg(err), NotificationWidget::Critical);
    } catch(...) {
        using namespace Core::MainWindow;
        MainWindow::instance().notify(tr("Failed to create experiment."), NotificationWidget::Critical);
    }
}

void OpenSpeedShopWidget::loadExperiment()
{
    try {

        Core::MainWindow::MainWindow &mainWindow = Core::MainWindow::MainWindow::instance();
        mainWindow.setCurrentCentralWidget(this);

        ExperimentWidget *experimentWidget = new ExperimentWidget(this);
        experimentWidget->load();
        int index = addTab(experimentWidget, experimentWidget->windowTitle());
        setCurrentIndex(index);

        connect(experimentWidget, SIGNAL(windowTitleChanged()), this, SLOT(tabTitleChanged()));

    } catch(QString err) {
        using namespace Core::MainWindow;
        MainWindow::instance().notify(tr("Failed to load experiment: %1").arg(err), NotificationWidget::Critical);
    } catch(...) {
        using namespace Core::MainWindow;
        MainWindow::instance().notify(tr("Failed to load experiment."), NotificationWidget::Critical);
    }
}

void OpenSpeedShopWidget::closeExperiment(int index)
{
    try {

        if(index < 0) {
            index = currentIndex();
        }

        ExperimentWidget *experimentWidget = qobject_cast<ExperimentWidget *>(widget(index));
        if(experimentWidget->close()) {
            removeTab(index);
            experimentWidget->deleteLater();
        }

    } catch(QString err) {
        using namespace Core::MainWindow;
        MainWindow::instance().notify(tr("Failed to load experiment: %1").arg(err), NotificationWidget::Critical);
    } catch(...) {
        using namespace Core::MainWindow;
        MainWindow::instance().notify(tr("Failed to load experiment."), NotificationWidget::Critical);
    }
}

void OpenSpeedShopWidget::tabInserted(int index)
{
    Q_UNUSED(index)

    if(this->count() < 2) {
        this->tabBar()->hide();
    } else {
        this->tabBar()->show();
    }

    /* Set the stylesheet to nothing if we have tabs, otherwise we'll run into display issues */
    if(count() <= 0) {
        setStyleSheet(m_StyleSheet);
    } else {
        setStyleSheet(QString());
    }

    m_CloseExperiment->setEnabled(count());
}

void OpenSpeedShopWidget::tabRemoved(int index)
{
    Q_UNUSED(index)

    if(this->count() < 2) {
        this->tabBar()->hide();
    } else {
        this->tabBar()->show();
    }

    /* Set the stylesheet to nothing if we have tabs, otherwise we'll run into display issues */
    if(count() <= 0) {
        setStyleSheet(m_StyleSheet);
    } else {
        setStyleSheet(QString());
    }

    m_CloseExperiment->setEnabled(count());
}

void OpenSpeedShopWidget::tabTitleChanged()
{
    try {

        QWidget *widget = qobject_cast<QWidget *>(QObject::sender());
        if(widget) {
            setTabText(indexOf(widget), widget->windowTitle());
        }

    } catch(QString err) {
        using namespace Core::MainWindow;
        MainWindow::instance().notify(tr("Failed to change tab name: %1").arg(err), NotificationWidget::Critical);
    } catch(...) {
        using namespace Core::MainWindow;
        MainWindow::instance().notify(tr("Failed to change tab name."), NotificationWidget::Critical);
    }
}

void OpenSpeedShopWidget::showEvent(QShowEvent *event)
{
    Q_UNUSED(event)

    Core::MainWindow::MainWindow &mainWindow = Core::MainWindow::MainWindow::instance();
    foreach(QAction *action, mainWindow.allActions()) {
        qDebug() << action->property("oss_menuitem").isValid() << action->text();
        if(action->property("oss_menuitem").isValid()) {
            action->setVisible(true);
        }
    }
}

void OpenSpeedShopWidget::hideEvent(QHideEvent *event)
{
    Q_UNUSED(event)

    Core::MainWindow::MainWindow &mainWindow = Core::MainWindow::MainWindow::instance();
    foreach(QAction *action, mainWindow.allActions()) {
        if(action->property("oss_menuitem").isValid()) {
            action->setVisible(false);
        }
    }
}

} // namespace OpenSpeedShop
} // namespace Plugins
<commit_msg>Removed stray debug line<commit_after>#include "OpenSpeedShopWidget.h"
#include "ui_OpenSpeedShopWidget.h"

#include <MainWindow/MainWindow.h>

#include <Experiment/ExperimentWidget.h>

namespace Plugins {
namespace OpenSpeedShop {

OpenSpeedShopWidget::OpenSpeedShopWidget(QWidget *parent) :
    QTabWidget(parent),
    ui(new Ui::OpenSpeedShopWidget)
{
    m_CreateExperiment = NULL;
    m_LoadExperiment = NULL;
    m_CloseExperiment = NULL;

    ui->setupUi(this);
    connect(this, SIGNAL(tabCloseRequested(int)), this, SLOT(closeExperiment(int)));

    /* Set up the stylesheet for periods when we have tabs */
    m_StyleSheet = styleSheet();

    setWindowTitle(QString("Open|SpeedShop%1").arg(QChar(0x2122))); //Trademark
    setWindowIcon(QIcon(":/OpenSpeedShop/app.png"));

    Core::MainWindow::MainWindow &mainWindow = Core::MainWindow::MainWindow::instance();
    foreach(QAction *action, mainWindow.menuBar()->actions()) {
        if(action->text() == tr("File")) {

            m_CreateExperiment = new QAction(tr("New O|SS Experiment"), this);
            m_CreateExperiment->setToolTip(tr("Create a new Open|SpeedShop experiment"));
            m_CreateExperiment->setIcon(QIcon(":/OpenSpeedShop/app.png"));
            m_CreateExperiment->setIconVisibleInMenu(true);
            m_CreateExperiment->setVisible(false);
            m_CreateExperiment->setProperty("oss_menuitem", QVariant(1));
            connect(m_CreateExperiment, SIGNAL(triggered()), this, SLOT(createExperiment()));

            //FIXME: When we're set up to create, enable this
            m_CreateExperiment->setEnabled(false);

            m_LoadExperiment = new QAction(tr("Load O|SS Experiment"), this);
            m_LoadExperiment->setToolTip(tr("Load an Open|SpeedShop experiment"));
            m_LoadExperiment->setIcon(QIcon(":/OpenSpeedShop/app.png"));
            m_LoadExperiment->setIconVisibleInMenu(true);
            m_LoadExperiment->setVisible(false);
            m_LoadExperiment->setProperty("oss_menuitem", QVariant(1));
            connect(m_LoadExperiment, SIGNAL(triggered()), this, SLOT(loadExperiment()));

            m_CloseExperiment = new QAction(tr("Close O|SS Experiment"), this);
            m_CloseExperiment->setToolTip(tr("Close the current Open|SpeedShop experiment"));
            m_CloseExperiment->setIcon(QIcon(":/OpenSpeedShop/app.png"));
            m_CloseExperiment->setIconVisibleInMenu(true);
            m_CloseExperiment->setEnabled(false);
            m_CloseExperiment->setVisible(false);
            m_CloseExperiment->setProperty("oss_menuitem", QVariant(1));
            connect(m_CloseExperiment, SIGNAL(triggered()), this, SLOT(closeExperiment()));

            //! \todo We really need to rely on the ActionManager to do this.
            QAction *before = NULL;
            foreach(QAction *item, action->menu()->actions()) {
                if(item->priority() == QAction::LowPriority) {
                    before = item;
                }
            }

            if(before) {
                action->menu()->insertAction(before, m_CreateExperiment);
                action->menu()->insertAction(before, m_LoadExperiment);
                action->menu()->insertAction(before, m_CloseExperiment);
            } else {
                action->menu()->addAction(m_CreateExperiment);
                action->menu()->addAction(m_LoadExperiment);
                action->menu()->addAction(m_CloseExperiment);
            }

        }
    }
}

OpenSpeedShopWidget::~OpenSpeedShopWidget()
{
    delete ui;
}

void OpenSpeedShopWidget::createExperiment()
{
    try {

        Core::MainWindow::MainWindow &mainWindow = Core::MainWindow::MainWindow::instance();
        mainWindow.setCurrentCentralWidget(this);

        ExperimentWidget *experimentWidget = new ExperimentWidget(this);
        experimentWidget->create();
        int index = addTab(experimentWidget, experimentWidget->windowTitle());
        setCurrentIndex(index);

        connect(experimentWidget, SIGNAL(windowTitleChanged()), this, SLOT(tabTitleChanged()));

    } catch(QString err) {
        using namespace Core::MainWindow;
        MainWindow::instance().notify(tr("Failed to create experiment: %1").arg(err), NotificationWidget::Critical);
    } catch(...) {
        using namespace Core::MainWindow;
        MainWindow::instance().notify(tr("Failed to create experiment."), NotificationWidget::Critical);
    }
}

void OpenSpeedShopWidget::loadExperiment()
{
    try {

        Core::MainWindow::MainWindow &mainWindow = Core::MainWindow::MainWindow::instance();
        mainWindow.setCurrentCentralWidget(this);

        ExperimentWidget *experimentWidget = new ExperimentWidget(this);
        experimentWidget->load();
        int index = addTab(experimentWidget, experimentWidget->windowTitle());
        setCurrentIndex(index);

        connect(experimentWidget, SIGNAL(windowTitleChanged()), this, SLOT(tabTitleChanged()));

    } catch(QString err) {
        using namespace Core::MainWindow;
        MainWindow::instance().notify(tr("Failed to load experiment: %1").arg(err), NotificationWidget::Critical);
    } catch(...) {
        using namespace Core::MainWindow;
        MainWindow::instance().notify(tr("Failed to load experiment."), NotificationWidget::Critical);
    }
}

void OpenSpeedShopWidget::closeExperiment(int index)
{
    try {

        if(index < 0) {
            index = currentIndex();
        }

        ExperimentWidget *experimentWidget = qobject_cast<ExperimentWidget *>(widget(index));
        if(experimentWidget->close()) {
            removeTab(index);
            experimentWidget->deleteLater();
        }

    } catch(QString err) {
        using namespace Core::MainWindow;
        MainWindow::instance().notify(tr("Failed to load experiment: %1").arg(err), NotificationWidget::Critical);
    } catch(...) {
        using namespace Core::MainWindow;
        MainWindow::instance().notify(tr("Failed to load experiment."), NotificationWidget::Critical);
    }
}

void OpenSpeedShopWidget::tabInserted(int index)
{
    Q_UNUSED(index)

    if(this->count() < 2) {
        this->tabBar()->hide();
    } else {
        this->tabBar()->show();
    }

    /* Set the stylesheet to nothing if we have tabs, otherwise we'll run into display issues */
    if(count() <= 0) {
        setStyleSheet(m_StyleSheet);
    } else {
        setStyleSheet(QString());
    }

    m_CloseExperiment->setEnabled(count());
}

void OpenSpeedShopWidget::tabRemoved(int index)
{
    Q_UNUSED(index)

    if(this->count() < 2) {
        this->tabBar()->hide();
    } else {
        this->tabBar()->show();
    }

    /* Set the stylesheet to nothing if we have tabs, otherwise we'll run into display issues */
    if(count() <= 0) {
        setStyleSheet(m_StyleSheet);
    } else {
        setStyleSheet(QString());
    }

    m_CloseExperiment->setEnabled(count());
}

void OpenSpeedShopWidget::tabTitleChanged()
{
    try {

        QWidget *widget = qobject_cast<QWidget *>(QObject::sender());
        if(widget) {
            setTabText(indexOf(widget), widget->windowTitle());
        }

    } catch(QString err) {
        using namespace Core::MainWindow;
        MainWindow::instance().notify(tr("Failed to change tab name: %1").arg(err), NotificationWidget::Critical);
    } catch(...) {
        using namespace Core::MainWindow;
        MainWindow::instance().notify(tr("Failed to change tab name."), NotificationWidget::Critical);
    }
}

void OpenSpeedShopWidget::showEvent(QShowEvent *event)
{
    Q_UNUSED(event)

    Core::MainWindow::MainWindow &mainWindow = Core::MainWindow::MainWindow::instance();
    foreach(QAction *action, mainWindow.allActions()) {
        if(action->property("oss_menuitem").isValid()) {
            action->setVisible(true);
        }
    }
}

void OpenSpeedShopWidget::hideEvent(QHideEvent *event)
{
    Q_UNUSED(event)

    Core::MainWindow::MainWindow &mainWindow = Core::MainWindow::MainWindow::instance();
    foreach(QAction *action, mainWindow.allActions()) {
        if(action->property("oss_menuitem").isValid()) {
            action->setVisible(false);
        }
    }
}

} // namespace OpenSpeedShop
} // namespace Plugins
<|endoftext|>


<commit_before>/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 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 "qmlprojectnodes.h"
#include "qmlprojectmanager.h"
#include "qmlproject.h"

#include <coreplugin/ifile.h>
#include <projectexplorer/projectexplorer.h>

#include <QFileInfo>
#include <QDir>
#include <QTextStream>

namespace QmlProjectManager {
namespace Internal {

QmlProjectNode::QmlProjectNode(QmlProject *project, Core::IFile *projectFile)
    : ProjectExplorer::ProjectNode(QFileInfo(projectFile->fileName()).absoluteFilePath()),
      m_project(project),
      m_projectFile(projectFile)
{
    setDisplayName(QFileInfo(projectFile->fileName()).completeBaseName());
}

QmlProjectNode::~QmlProjectNode()
{ }

Core::IFile *QmlProjectNode::projectFile() const
{ return m_projectFile; }

QString QmlProjectNode::projectFilePath() const
{ return m_projectFile->fileName(); }

void QmlProjectNode::refresh()
{
    using namespace ProjectExplorer;

    // remove the existing nodes.
    removeFileNodes(fileNodes(), this);
    removeFolderNodes(subFolderNodes(), this);

    //ProjectExplorerPlugin::instance()->setCurrentNode(0); // ### remove me

    FileNode *projectFilesNode = new FileNode(m_project->filesFileName(),
                                              ProjectFileType,
                                              /* generated = */ false);

    QStringList files = m_project->files();
    files.removeAll(m_project->filesFileName());

    addFileNodes(QList<FileNode *>()
                 << projectFilesNode,
                 this);

    QHash<QString, QStringList> filesInDirectory;

    foreach (const QString &fileName, files) {
        QFileInfo fileInfo(fileName);

        QString absoluteFilePath;
        QString relativeDirectory;

        if (fileInfo.isAbsolute()) {
            // plain old file format
            absoluteFilePath = fileInfo.filePath();
            relativeDirectory = m_project->projectDir().relativeFilePath(fileInfo.path());
        } else {
            absoluteFilePath = m_project->projectDir().absoluteFilePath(fileInfo.filePath());
            relativeDirectory = fileInfo.path();
            if (relativeDirectory == ".")
                relativeDirectory.clear();
        }

        filesInDirectory[relativeDirectory].append(absoluteFilePath);
    }

    foreach (const QString &directory, filesInDirectory.keys()) {
        FolderNode *folder = findOrCreateFolderByName(directory);

        QList<FileNode *> fileNodes;
        foreach (const QString &file, filesInDirectory.value(directory)) {
            FileType fileType = SourceType; // ### FIXME
            FileNode *fileNode = new FileNode(file, fileType, /*generated = */ false);
            fileNodes.append(fileNode);
        }

        addFileNodes(fileNodes, folder);
    }

    m_folderByName.clear();
}

ProjectExplorer::FolderNode *QmlProjectNode::findOrCreateFolderByName(const QStringList &components, int end)
{
    if (! end)
        return 0;

    QString baseDir = QFileInfo(path()).path();

    QString folderName;
    for (int i = 0; i < end; ++i) {
        folderName.append(components.at(i));
        folderName += QLatin1Char('/'); // ### FIXME
    }

    const QString component = components.at(end - 1);

    if (component.isEmpty())
        return this;

    else if (FolderNode *folder = m_folderByName.value(folderName))
        return folder;

    FolderNode *folder = new FolderNode(baseDir + "/" + folderName);
    folder->setFolderName(component);

    m_folderByName.insert(folderName, folder);

    FolderNode *parent = findOrCreateFolderByName(components, end - 1);
    if (! parent)
        parent = this;

    addFolderNodes(QList<FolderNode*>() << folder, parent);

    return folder;
}

ProjectExplorer::FolderNode *QmlProjectNode::findOrCreateFolderByName(const QString &filePath)
{
    QStringList components = filePath.split(QLatin1Char('/'));
    return findOrCreateFolderByName(components, components.length());
}

bool QmlProjectNode::hasBuildTargets() const
{
    return true;
}

QList<ProjectExplorer::ProjectNode::ProjectAction> QmlProjectNode::supportedActions() const
{
    QList<ProjectAction> actions;
    actions.append(AddFile);
    return actions;
}

bool QmlProjectNode::addSubProjects(const QStringList &proFilePaths)
{
    Q_UNUSED(proFilePaths)
    return false;
}

bool QmlProjectNode::removeSubProjects(const QStringList &proFilePaths)
{
    Q_UNUSED(proFilePaths)
    return false;
}

bool QmlProjectNode::addFiles(const ProjectExplorer::FileType /*fileType*/,
                              const QStringList &filePaths, QStringList * /*notAdded*/)
{
    return m_project->addFiles(filePaths);
}

bool QmlProjectNode::removeFiles(const ProjectExplorer::FileType /*fileType*/,
                                 const QStringList & /*filePaths*/, QStringList * /*notRemoved*/)
{
    return false;
}

bool QmlProjectNode::renameFile(const ProjectExplorer::FileType /*fileType*/,
                                    const QString & /*filePath*/, const QString & /*newFilePath*/)
{
    return false;
}

} // namespace Internal
} // namespace QmlProjectManager
<commit_msg>Compile<commit_after>/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 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 "qmlprojectnodes.h"
#include "qmlprojectmanager.h"
#include "qmlproject.h"

#include <coreplugin/ifile.h>
#include <projectexplorer/projectexplorer.h>

#include <QFileInfo>
#include <QDir>
#include <QTextStream>

namespace QmlProjectManager {
namespace Internal {

QmlProjectNode::QmlProjectNode(QmlProject *project, Core::IFile *projectFile)
    : ProjectExplorer::ProjectNode(QFileInfo(projectFile->fileName()).absoluteFilePath()),
      m_project(project),
      m_projectFile(projectFile)
{
    setDisplayName(QFileInfo(projectFile->fileName()).completeBaseName());
}

QmlProjectNode::~QmlProjectNode()
{ }

Core::IFile *QmlProjectNode::projectFile() const
{ return m_projectFile; }

QString QmlProjectNode::projectFilePath() const
{ return m_projectFile->fileName(); }

void QmlProjectNode::refresh()
{
    using namespace ProjectExplorer;

    // remove the existing nodes.
    removeFileNodes(fileNodes(), this);
    removeFolderNodes(subFolderNodes(), this);

    //ProjectExplorerPlugin::instance()->setCurrentNode(0); // ### remove me

    FileNode *projectFilesNode = new FileNode(m_project->filesFileName(),
                                              ProjectFileType,
                                              /* generated = */ false);

    QStringList files = m_project->files();
    files.removeAll(m_project->filesFileName());

    addFileNodes(QList<FileNode *>()
                 << projectFilesNode,
                 this);

    QHash<QString, QStringList> filesInDirectory;

    foreach (const QString &fileName, files) {
        QFileInfo fileInfo(fileName);

        QString absoluteFilePath;
        QString relativeDirectory;

        if (fileInfo.isAbsolute()) {
            // plain old file format
            absoluteFilePath = fileInfo.filePath();
            relativeDirectory = m_project->projectDir().relativeFilePath(fileInfo.path());
        } else {
            absoluteFilePath = m_project->projectDir().absoluteFilePath(fileInfo.filePath());
            relativeDirectory = fileInfo.path();
            if (relativeDirectory == ".")
                relativeDirectory.clear();
        }

        filesInDirectory[relativeDirectory].append(absoluteFilePath);
    }

    foreach (const QString &directory, filesInDirectory.keys()) {
        FolderNode *folder = findOrCreateFolderByName(directory);

        QList<FileNode *> fileNodes;
        foreach (const QString &file, filesInDirectory.value(directory)) {
            FileType fileType = SourceType; // ### FIXME
            FileNode *fileNode = new FileNode(file, fileType, /*generated = */ false);
            fileNodes.append(fileNode);
        }

        addFileNodes(fileNodes, folder);
    }

    m_folderByName.clear();
}

ProjectExplorer::FolderNode *QmlProjectNode::findOrCreateFolderByName(const QStringList &components, int end)
{
    if (! end)
        return 0;

    QString baseDir = QFileInfo(path()).path();

    QString folderName;
    for (int i = 0; i < end; ++i) {
        folderName.append(components.at(i));
        folderName += QLatin1Char('/'); // ### FIXME
    }

    const QString component = components.at(end - 1);

    if (component.isEmpty())
        return this;

    else if (FolderNode *folder = m_folderByName.value(folderName))
        return folder;

    FolderNode *folder = new FolderNode(baseDir + "/" + folderName);
    folder->setDisplayName(component);

    m_folderByName.insert(folderName, folder);

    FolderNode *parent = findOrCreateFolderByName(components, end - 1);
    if (! parent)
        parent = this;

    addFolderNodes(QList<FolderNode*>() << folder, parent);

    return folder;
}

ProjectExplorer::FolderNode *QmlProjectNode::findOrCreateFolderByName(const QString &filePath)
{
    QStringList components = filePath.split(QLatin1Char('/'));
    return findOrCreateFolderByName(components, components.length());
}

bool QmlProjectNode::hasBuildTargets() const
{
    return true;
}

QList<ProjectExplorer::ProjectNode::ProjectAction> QmlProjectNode::supportedActions() const
{
    QList<ProjectAction> actions;
    actions.append(AddFile);
    return actions;
}

bool QmlProjectNode::addSubProjects(const QStringList &proFilePaths)
{
    Q_UNUSED(proFilePaths)
    return false;
}

bool QmlProjectNode::removeSubProjects(const QStringList &proFilePaths)
{
    Q_UNUSED(proFilePaths)
    return false;
}

bool QmlProjectNode::addFiles(const ProjectExplorer::FileType /*fileType*/,
                              const QStringList &filePaths, QStringList * /*notAdded*/)
{
    return m_project->addFiles(filePaths);
}

bool QmlProjectNode::removeFiles(const ProjectExplorer::FileType /*fileType*/,
                                 const QStringList & /*filePaths*/, QStringList * /*notRemoved*/)
{
    return false;
}

bool QmlProjectNode::renameFile(const ProjectExplorer::FileType /*fileType*/,
                                    const QString & /*filePath*/, const QString & /*newFilePath*/)
{
    return false;
}

} // namespace Internal
} // namespace QmlProjectManager
<|endoftext|>


<commit_before>#include "videoframe.h"
#include <algorithm>
#include <cstring>

namespace gg
{

VideoFrame::VideoFrame(bool manage_data)
  : _manage_data(manage_data),
    _data(nullptr),
    _data_length(0),
    _rows(0),
    _cols(0)
{

}

#ifdef USE_OPENCV
std::unique_ptr<MaskFrame> VideoFrame::compute_image_mask(int x, int y,
                                                               int width, int height)
{
    cv::Rect rect(x, y, width, height);
    cv::Mat temp(_rows, _cols, CV_8UC4, _data);
    cv::Mat image, result;
    temp.copyTo(image);
    cv::cvtColor(image, image, CV_BGRA2BGR);
    cv::Mat bg, fg;

    for (int i = 0; i < 3; ++i) {
        if (i == 0) cv::grabCut(image, result, rect, bg, fg, 1, cv::GC_INIT_WITH_RECT);
        else cv::grabCut(image, result, rect, bg, fg, 1, cv::GC_INIT_WITH_MASK);
    }

    cv::Mat result_a, result_b;
    cv::compare(result,cv::GC_FGD,result_a,cv::CMP_EQ);
    // Get the pixels marked as likely background
    cv::compare(result,cv::GC_PR_FGD,result_b,cv::CMP_EQ);
    // Final results
    result=result_a + result_b;

    cv::vector<cv::vector<cv::Point> > contours;
    cv::findContours(result, contours, cv::RETR_LIST, cv::CHAIN_APPROX_SIMPLE);

    if (!(contours.size() >= 1)) {
        throw std::runtime_error("Could not generate the image mask");
    }

    int index = 0;
    for (int i = 1; i < contours.size(); ++i) {
        if (contours[i].size() > contours[index].size()) index = i;
    }

    if (contours[index].size() < 5) {
        throw std::runtime_error("Could not generate the image mask");
    }

    cv::RotatedRect e = cv::fitEllipse(contours[index]);
    // Small fudge factor
    e.size.width -= 30;
    e.size.height -= 30;

    cv::Rect bound_rect = e.boundingRect();
    int rx_1, ry_1, rx_2, ry_2;
    rx_1 = bound_rect.x; ry_1 = bound_rect.y;
    rx_2 = rx_1 + bound_rect.width; ry_2 = ry_1 + bound_rect.height;

    rx_1 = std::max(0, rx_1); rx_1 = std::min((int)_cols -1, rx_1);
    ry_1 = std::max(0, ry_1); ry_1 = std::min((int)_rows -1, ry_1);
    rx_2 = std::max(0, rx_2); rx_2 = std::min((int)_cols -1, rx_2);
    ry_2 = std::max(0, ry_2); ry_2 = std::min((int)_rows -1, ry_2);

    cv::Mat filled_mask = cv::Mat::zeros(_rows, _cols, CV_8UC1);
    cv::ellipse(filled_mask, e, cv::Scalar(255), CV_FILLED);

    int mw = rx_2 - rx_1;
    int mh = ry_2 - ry_1;

    mw -= (int)mw % 16;
    mh -= (int)mh % 16;

    cv::Rect sub_rect(rx_1, ry_1, mw, mh);
    cv::Mat cropped_mask;
    filled_mask(sub_rect).copyTo(cropped_mask);
    // Now create the mask with the correct dimensions.
    auto mask = std::unique_ptr<MaskFrame>(new MaskFrame(mw, mh));
    unsigned char * mask_data = const_cast<unsigned char *>(mask->data());
    memcpy(mask_data, cropped_mask.data, mw * mh);
    mask->set_ellipse(e.center.x, e.center.y, e.size.width, e.size.height, e.angle);
    mask->set_bound_rect(rx_1, ry_1, mw, mh);

    unsigned char * mask_weights = const_cast<unsigned char *>(mask->weights());
    cv::Mat wts = cv::Mat(cropped_mask.rows, cropped_mask.cols, CV_8UC1, mask_weights);

    float w;
    double cx = mw/2.0;
    double cy = mh/2.0;

    for (double r = 0; r < cropped_mask.rows; ++r) {
        uchar * p = cropped_mask.ptr((int)r);
        uchar * q = wts.ptr((int)r);
        for (double c = 0; c < cropped_mask.cols; ++c) {
            if (p[(int)c] > 0) {
                w = std::sqrt(std::pow((cx - c)/cx, 2.f) + std::pow((cy - r)/cy, 2.f));
                if (w > 1) w = 1.f;
                q[(int)c] = (unsigned char)((1.f - w)*255.9999);
            }
        }
    }
    return mask;
}
#endif // USE_OPENCV

VideoFrame::~VideoFrame()
{
    clear();
}

void VideoFrame::clear()
{
    if (_manage_data) {
        delete [] _data;
        _data = 0;
        _data_length = 0;
        _rows = 0;
        _cols = 0;
    }
}

}

VideoFrame_BGRA::VideoFrame_BGRA(bool manage_data)
    : VideoFrame(manage_data)
{

}

VideoFrame_BGRA::VideoFrame_BGRA(const size_t rows, const size_t cols)
    : gg::VideoFrame(true)
{
#ifdef USE_OPENCV
    init_from_opencv_mat(cv::Mat::zeros(rows, cols, CV_8UC4));
#else
    unsigned char data[rows * cols * 4] = { 0 };
    init_from_pointer(data, rows, cols);
#endif // USE_OPENCV
}

#ifdef USE_OPENCV
VideoFrame_BGRA::VideoFrame_BGRA(const cv::Mat & mat, bool manage_data)
{
    _manage_data = manage_data;
    init_from_opencv_mat(mat);
}
#endif // USE_OPENCV

VideoFrame_BGRA::VideoFrame_BGRA(unsigned char * data, size_t rows, size_t cols, bool manage_data)
  : VideoFrame(manage_data)
{
    init_from_pointer(data, rows, cols);
}

VideoFrame_BGRA::VideoFrame_BGRA(const VideoFrame_BGRA & rhs)
{
    clone(rhs);
}

void VideoFrame_BGRA::operator =(const VideoFrame_BGRA & rhs)
{
    clone(rhs);
}

#ifdef USE_OPENCV
void VideoFrame_BGRA::init_from_opencv_mat(const cv::Mat &mat)
{
    assert(mat.channels() == 4);
    _rows = mat.rows;
    _cols = mat.cols;
    _data_length = _rows * _cols * 4;

    // OpenCV stores images top to bottom while OpenGL is bottom to top.
    // The caller is supposed to setup the client visualization to have
    // origin at upper left corner
    //cv::flip(mat, mat, 0);

    if (_manage_data) {
        _data = new unsigned char[_data_length];
        memcpy(_data, mat.data, _data_length * sizeof(unsigned char));
    }
    // Just copy the pointer address
    else {
        _data = mat.data;
    }
}
#endif // USE_OPENCV

void VideoFrame_BGRA::init_from_pointer(unsigned char * data, size_t rows, size_t cols)
{
    _rows = rows;
    _cols = cols;

    _data_length = _rows * _cols * 4;
    if( _manage_data ){
        _data = new unsigned char[_data_length];
        memcpy(_data, data, _data_length * sizeof(unsigned char));
    }
    else{
        _data = data;
    }
}

void VideoFrame_BGRA::clone(const VideoFrame_BGRA & rhs)
{
    clear();
    _manage_data = true;
    _rows = rhs.rows();
    _cols = rhs.cols();
    _data_length = _rows * _cols * 4;
    _data = new unsigned char[_data_length];
    memcpy(_data, rhs._data, _data_length * sizeof(unsigned char));
}
<commit_msg>using memset instead of {0} type initialiser as clang does not seem to accept it<commit_after>#include "videoframe.h"
#include <algorithm>
#include <cstring>

namespace gg
{

VideoFrame::VideoFrame(bool manage_data)
  : _manage_data(manage_data),
    _data(nullptr),
    _data_length(0),
    _rows(0),
    _cols(0)
{

}

#ifdef USE_OPENCV
std::unique_ptr<MaskFrame> VideoFrame::compute_image_mask(int x, int y,
                                                               int width, int height)
{
    cv::Rect rect(x, y, width, height);
    cv::Mat temp(_rows, _cols, CV_8UC4, _data);
    cv::Mat image, result;
    temp.copyTo(image);
    cv::cvtColor(image, image, CV_BGRA2BGR);
    cv::Mat bg, fg;

    for (int i = 0; i < 3; ++i) {
        if (i == 0) cv::grabCut(image, result, rect, bg, fg, 1, cv::GC_INIT_WITH_RECT);
        else cv::grabCut(image, result, rect, bg, fg, 1, cv::GC_INIT_WITH_MASK);
    }

    cv::Mat result_a, result_b;
    cv::compare(result,cv::GC_FGD,result_a,cv::CMP_EQ);
    // Get the pixels marked as likely background
    cv::compare(result,cv::GC_PR_FGD,result_b,cv::CMP_EQ);
    // Final results
    result=result_a + result_b;

    cv::vector<cv::vector<cv::Point> > contours;
    cv::findContours(result, contours, cv::RETR_LIST, cv::CHAIN_APPROX_SIMPLE);

    if (!(contours.size() >= 1)) {
        throw std::runtime_error("Could not generate the image mask");
    }

    int index = 0;
    for (int i = 1; i < contours.size(); ++i) {
        if (contours[i].size() > contours[index].size()) index = i;
    }

    if (contours[index].size() < 5) {
        throw std::runtime_error("Could not generate the image mask");
    }

    cv::RotatedRect e = cv::fitEllipse(contours[index]);
    // Small fudge factor
    e.size.width -= 30;
    e.size.height -= 30;

    cv::Rect bound_rect = e.boundingRect();
    int rx_1, ry_1, rx_2, ry_2;
    rx_1 = bound_rect.x; ry_1 = bound_rect.y;
    rx_2 = rx_1 + bound_rect.width; ry_2 = ry_1 + bound_rect.height;

    rx_1 = std::max(0, rx_1); rx_1 = std::min((int)_cols -1, rx_1);
    ry_1 = std::max(0, ry_1); ry_1 = std::min((int)_rows -1, ry_1);
    rx_2 = std::max(0, rx_2); rx_2 = std::min((int)_cols -1, rx_2);
    ry_2 = std::max(0, ry_2); ry_2 = std::min((int)_rows -1, ry_2);

    cv::Mat filled_mask = cv::Mat::zeros(_rows, _cols, CV_8UC1);
    cv::ellipse(filled_mask, e, cv::Scalar(255), CV_FILLED);

    int mw = rx_2 - rx_1;
    int mh = ry_2 - ry_1;

    mw -= (int)mw % 16;
    mh -= (int)mh % 16;

    cv::Rect sub_rect(rx_1, ry_1, mw, mh);
    cv::Mat cropped_mask;
    filled_mask(sub_rect).copyTo(cropped_mask);
    // Now create the mask with the correct dimensions.
    auto mask = std::unique_ptr<MaskFrame>(new MaskFrame(mw, mh));
    unsigned char * mask_data = const_cast<unsigned char *>(mask->data());
    memcpy(mask_data, cropped_mask.data, mw * mh);
    mask->set_ellipse(e.center.x, e.center.y, e.size.width, e.size.height, e.angle);
    mask->set_bound_rect(rx_1, ry_1, mw, mh);

    unsigned char * mask_weights = const_cast<unsigned char *>(mask->weights());
    cv::Mat wts = cv::Mat(cropped_mask.rows, cropped_mask.cols, CV_8UC1, mask_weights);

    float w;
    double cx = mw/2.0;
    double cy = mh/2.0;

    for (double r = 0; r < cropped_mask.rows; ++r) {
        uchar * p = cropped_mask.ptr((int)r);
        uchar * q = wts.ptr((int)r);
        for (double c = 0; c < cropped_mask.cols; ++c) {
            if (p[(int)c] > 0) {
                w = std::sqrt(std::pow((cx - c)/cx, 2.f) + std::pow((cy - r)/cy, 2.f));
                if (w > 1) w = 1.f;
                q[(int)c] = (unsigned char)((1.f - w)*255.9999);
            }
        }
    }
    return mask;
}
#endif // USE_OPENCV

VideoFrame::~VideoFrame()
{
    clear();
}

void VideoFrame::clear()
{
    if (_manage_data) {
        delete [] _data;
        _data = 0;
        _data_length = 0;
        _rows = 0;
        _cols = 0;
    }
}

}

VideoFrame_BGRA::VideoFrame_BGRA(bool manage_data)
    : VideoFrame(manage_data)
{

}

VideoFrame_BGRA::VideoFrame_BGRA(const size_t rows, const size_t cols)
    : gg::VideoFrame(true)
{
#ifdef USE_OPENCV
    init_from_opencv_mat(cv::Mat::zeros(rows, cols, CV_8UC4));
#else
    size_t data_length = rows * cols * 4;
    unsigned char data[data_length];
    memset(data, 0, data_length * sizeof(unsigned char));
    init_from_pointer(data, rows, cols);
#endif // USE_OPENCV
}

#ifdef USE_OPENCV
VideoFrame_BGRA::VideoFrame_BGRA(const cv::Mat & mat, bool manage_data)
{
    _manage_data = manage_data;
    init_from_opencv_mat(mat);
}
#endif // USE_OPENCV

VideoFrame_BGRA::VideoFrame_BGRA(unsigned char * data, size_t rows, size_t cols, bool manage_data)
  : VideoFrame(manage_data)
{
    init_from_pointer(data, rows, cols);
}

VideoFrame_BGRA::VideoFrame_BGRA(const VideoFrame_BGRA & rhs)
{
    clone(rhs);
}

void VideoFrame_BGRA::operator =(const VideoFrame_BGRA & rhs)
{
    clone(rhs);
}

#ifdef USE_OPENCV
void VideoFrame_BGRA::init_from_opencv_mat(const cv::Mat &mat)
{
    assert(mat.channels() == 4);
    _rows = mat.rows;
    _cols = mat.cols;
    _data_length = _rows * _cols * 4;

    // OpenCV stores images top to bottom while OpenGL is bottom to top.
    // The caller is supposed to setup the client visualization to have
    // origin at upper left corner
    //cv::flip(mat, mat, 0);

    if (_manage_data) {
        _data = new unsigned char[_data_length];
        memcpy(_data, mat.data, _data_length * sizeof(unsigned char));
    }
    // Just copy the pointer address
    else {
        _data = mat.data;
    }
}
#endif // USE_OPENCV

void VideoFrame_BGRA::init_from_pointer(unsigned char * data, size_t rows, size_t cols)
{
    _rows = rows;
    _cols = cols;

    _data_length = _rows * _cols * 4;
    if( _manage_data ){
        _data = new unsigned char[_data_length];
        memcpy(_data, data, _data_length * sizeof(unsigned char));
    }
    else{
        _data = data;
    }
}

void VideoFrame_BGRA::clone(const VideoFrame_BGRA & rhs)
{
    clear();
    _manage_data = true;
    _rows = rhs.rows();
    _cols = rhs.cols();
    _data_length = _rows * _cols * 4;
    _data = new unsigned char[_data_length];
    memcpy(_data, rhs._data, _data_length * sizeof(unsigned char));
}
<|endoftext|>


<commit_before>///
/// @file   cmdoptions.cpp
/// @brief  Parse command-line options for the primecount console
///         (terminal) application.
///
/// Copyright (C) 2019 Kim Walisch, <kim.walisch@gmail.com>
///
/// This file is distributed under the BSD License. See the COPYING
/// file in the top level directory.
///

#include "cmdoptions.hpp"

#include <primecount.hpp>
#include <primecount-internal.hpp>
#include <print.hpp>
#include <int128_t.hpp>

#include <stdint.h>
#include <cstddef>
#include <map>
#include <string>
#include <type_traits>
#include <vector>
#include <utility>

using namespace std;

namespace primecount {

void help(int exitCode);
void version();
void test();

/// Some command-line options require an additional parameter.
/// Examples: --threads THREADS, -a ALPHA, ...
enum IsParam
{
  NO_PARAM,
  REQUIRED_PARAM,
  OPTIONAL_PARAM
};

/// Command-line options
map<string, std::pair<OptionID, IsParam>> optionMap =
{
  { "-a", make_pair(OPTION_ALPHA, REQUIRED_PARAM) },
  { "--alpha", make_pair(OPTION_ALPHA, REQUIRED_PARAM) },
  { "--alpha-y", make_pair(OPTION_ALPHA_Y, REQUIRED_PARAM) },
  { "--alpha-z", make_pair(OPTION_ALPHA_Z, REQUIRED_PARAM) },
  { "-d", make_pair(OPTION_DELEGLISE_RIVAT, NO_PARAM) },
  { "--deleglise-rivat", make_pair(OPTION_DELEGLISE_RIVAT, NO_PARAM) },
  { "--deleglise-rivat-64", make_pair(OPTION_DELEGLISE_RIVAT_64, NO_PARAM) },
  { "--deleglise-rivat-128", make_pair(OPTION_DELEGLISE_RIVAT_128, NO_PARAM) },
  { "-g", make_pair(OPTION_GOURDON, NO_PARAM) },
  { "--gourdon", make_pair(OPTION_GOURDON, NO_PARAM) },
  { "--gourdon-64", make_pair(OPTION_GOURDON_64, NO_PARAM) },
  { "--gourdon-128", make_pair(OPTION_GOURDON_128, NO_PARAM) },
  { "-h", make_pair(OPTION_HELP, NO_PARAM) },
  { "--help", make_pair(OPTION_HELP, NO_PARAM) },
  { "-l", make_pair(OPTION_LEGENDRE, NO_PARAM) },
  { "--legendre", make_pair(OPTION_LEGENDRE, NO_PARAM) },
  { "--lehmer", make_pair(OPTION_LEHMER, NO_PARAM) },
  { "--lmo", make_pair(OPTION_LMO, NO_PARAM) },
  { "--lmo1", make_pair(OPTION_LMO1, NO_PARAM) },
  { "--lmo2", make_pair(OPTION_LMO2, NO_PARAM) },
  { "--lmo3", make_pair(OPTION_LMO3, NO_PARAM) },
  { "--lmo4", make_pair(OPTION_LMO4, NO_PARAM) },
  { "--lmo5", make_pair(OPTION_LMO5, NO_PARAM) },
  { "-m", make_pair(OPTION_MEISSEL, NO_PARAM) },
  { "--meissel", make_pair(OPTION_MEISSEL, NO_PARAM) },
  { "-n", make_pair(OPTION_NTHPRIME, NO_PARAM) },
  { "--nth-prime", make_pair(OPTION_NTHPRIME, NO_PARAM) },
  { "--number", make_pair(OPTION_NUMBER, REQUIRED_PARAM) },
  { "-p", make_pair(OPTION_PRIMESIEVE, NO_PARAM) },
  { "--primesieve", make_pair(OPTION_PRIMESIEVE, NO_PARAM) },
  { "--Li", make_pair(OPTION_LI, NO_PARAM) },
  { "--Li-inverse", make_pair(OPTION_LIINV, NO_PARAM) },
  { "--Ri", make_pair(OPTION_RI, NO_PARAM) },
  { "--Ri-inverse", make_pair(OPTION_RIINV, NO_PARAM) },
  { "--phi", make_pair(OPTION_PHI, NO_PARAM) },
  { "--P2", make_pair(OPTION_P2, NO_PARAM) },
  { "--S1", make_pair(OPTION_S1, NO_PARAM) },
  { "--S2-easy", make_pair(OPTION_S2_EASY, NO_PARAM) },
  { "--S2-hard", make_pair(OPTION_S2_HARD, NO_PARAM) },
  { "--S2-trivial", make_pair(OPTION_S2_TRIVIAL, NO_PARAM) },
  { "--AC", make_pair(OPTION_AC, NO_PARAM) },
  { "--B", make_pair(OPTION_B, NO_PARAM) },
  { "--D", make_pair(OPTION_D, NO_PARAM) },
  { "--Phi0", make_pair(OPTION_PHI0, NO_PARAM) },
  { "--Sigma", make_pair(OPTION_SIGMA, NO_PARAM) },
  { "-s", make_pair(OPTION_STATUS, OPTIONAL_PARAM) },
  { "--status", make_pair(OPTION_STATUS, OPTIONAL_PARAM) },
  { "--test", make_pair(OPTION_TEST, NO_PARAM) },
  { "--time", make_pair(OPTION_TIME, NO_PARAM) },
  { "-t", make_pair(OPTION_THREADS, REQUIRED_PARAM) },
  { "--threads", make_pair(OPTION_THREADS, REQUIRED_PARAM) },
  { "-v", make_pair(OPTION_VERSION, NO_PARAM) },
  { "--version", make_pair(OPTION_VERSION, NO_PARAM) }
};

/// Command-line option
struct Option
{
  // Example:
  // str = "--threads=32"
  // opt = "--threads"
  // val = "32"
  string str;
  string opt;
  string val;

  template <typename T>
  T to() const
  {
    try {
      if (std::is_floating_point<T>::value)
        return (T) stod(val);
      else
        return (T) to_maxint(val);
    }
    catch (std::exception&) {
      throw primecount_error("invalid option '" + opt + "=" + val + "'");
    }
  }
};

/// Options start with "-" or "--", then
/// follows a Latin ASCII character.
///
bool isOption(const string& str)
{
  // Option of type: -o...
  if (str.size() >= 2 &&
      str[0] == '-' &&
      ((str[1] >= 'a' && str[1] <= 'z') ||
       (str[1] >= 'A' && str[1] <= 'Z')))
    return true;

  // Option of type: --o...
  if (str.size() >= 3 &&
      str[0] == '-' &&
      str[1] == '-' &&
      ((str[2] >= 'a' && str[2] <= 'z') ||
       (str[2] >= 'A' && str[2] <= 'Z')))
    return true;

  return false;
}

void optionStatus(Option& opt,
                  CmdOptions& opts)
{
  set_print(true);
  opts.time = true;

  if (!opt.val.empty())
    set_status_precision(opt.to<int>());
}

/// Parse the next command-line option.
/// e.g. "--threads=32"
/// -> opt.str = "--threads=32"
/// -> opt.opt = "--threads"
/// -> opt.val = "8"
///
Option parseOption(int argc, char* argv[], int& i)
{
  Option opt;
  opt.str = argv[i];

  if (opt.str.empty())
    throw primecount_error("unrecognized option ''");

  // Check if the option has the format:
  // --opt or -o (but not --opt=N)
  if (optionMap.count(opt.str))
  {
    opt.opt = opt.str;
    IsParam isParam = optionMap[opt.str].second;

    if (isParam == REQUIRED_PARAM)
    {
      i += 1;

      if (i < argc)
        opt.val = argv[i];

      // Prevent --threads --other-option
      if (opt.val.empty() || isOption(opt.val))
        throw primecount_error("missing value for option '" + opt.opt + "'");
    }

    // If the option takes an optional argument we
    // assume the next value is an optional argument
    // if the value is not a vaild option.
    if (isParam == OPTIONAL_PARAM &&
        i + 1 < argc &&
        !string(argv[i + 1]).empty() &&
        !isOption(argv[i + 1]))
    {
      i += 1;
      opt.val = argv[i];
    }
  }
  else
  {
    // Here the option is either:
    // 1) An option of type: --opt[=N]
    // 2) An option of type: --opt[N]
    // 3) A number (e.g. the start number)

    if (isOption(opt.str))
    {
      size_t pos = opt.str.find("=");

      // Option of type: --opt=N
      if (pos != string::npos)
      {
        opt.opt = opt.str.substr(0, pos);
        opt.val = opt.str.substr(pos + 1);

        // Print partial option: --opt (without =N)
        if (!optionMap.count(opt.opt))
          throw primecount_error("unrecognized option '" + opt.opt + "'");
      }
      else
      {
        // Option of type: --opt[N]
        pos = opt.str.find_first_of("0123456789");

        if (pos == string::npos)
          opt.opt = opt.str;
        else
        {
          opt.opt = opt.str.substr(0, pos);
          opt.val = opt.str.substr(pos);
        }

        // Print full option e.g.: --opt123
        if (!optionMap.count(opt.opt))
          throw primecount_error("unrecognized option '" + opt.str + "'");
      }

      // Prevent '--option='
      if (opt.val.empty() &&
          optionMap[opt.opt].second == REQUIRED_PARAM)
        throw primecount_error("missing value for option '" + opt.opt + "'");
    }
    else
    {
      // Here the option is actually a number or
      // an integer arithmetic expression.
      opt.opt = "--number";
      opt.val = opt.str;

      // This is not a valid number
      if (opt.str.find_first_of("0123456789") == string::npos)
        throw primecount_error("unrecognized option '" + opt.str + "'");

      // Prevent negative numbers as there are
      // no negative prime numbers.
      if (opt.str.at(0) == '-')
        throw primecount_error("unrecognized option '" + opt.str + "'");
    }
  }

  return opt;
}

CmdOptions parseOptions(int argc, char* argv[])
{
  CmdOptions opts;
  vector<maxint_t> numbers;

  // No command-line options provided
  if (argc <= 1)
    help(/* exitCode */ 1);

  for (int i = 1; i < argc; i++)
  {
    Option opt = parseOption(argc, argv, i);
    OptionID optionID = optionMap[opt.opt].first;

    switch (optionID)
    {
      case OPTION_ALPHA:   set_alpha(opt.to<double>()); break;
      case OPTION_ALPHA_Y: set_alpha_y(opt.to<double>()); break;
      case OPTION_ALPHA_Z: set_alpha_z(opt.to<double>()); break;
      case OPTION_NUMBER:  numbers.push_back(opt.to<maxint_t>()); break;
      case OPTION_THREADS: set_num_threads(opt.to<int>()); break;
      case OPTION_HELP:    help(/* exitCode */ 0); break;
      case OPTION_STATUS:  optionStatus(opt, opts); break;
      case OPTION_TIME:    opts.time = true; break;
      case OPTION_TEST:    test(); break;
      case OPTION_VERSION: version(); break;
      default:             opts.option = optionID;
    }
  }

  if (opts.option == OPTION_PHI)
  {
    if (numbers.size() < 2)
      throw primecount_error("option --phi requires 2 numbers");
    opts.a = numbers[1];
  }

  if (numbers.empty())
    throw primecount_error("missing x number");

  opts.x = numbers[0];

  return opts;
}

} // namespace
<commit_msg>Remove spaces at end of line<commit_after>///
/// @file   cmdoptions.cpp
/// @brief  Parse command-line options for the primecount console
///         (terminal) application.
///
/// Copyright (C) 2019 Kim Walisch, <kim.walisch@gmail.com>
///
/// This file is distributed under the BSD License. See the COPYING
/// file in the top level directory.
///

#include "cmdoptions.hpp"

#include <primecount.hpp>
#include <primecount-internal.hpp>
#include <print.hpp>
#include <int128_t.hpp>

#include <stdint.h>
#include <cstddef>
#include <map>
#include <string>
#include <type_traits>
#include <vector>
#include <utility>

using namespace std;

namespace primecount {

void help(int exitCode);
void version();
void test();

/// Some command-line options require an additional parameter.
/// Examples: --threads THREADS, -a ALPHA, ...
enum IsParam
{
  NO_PARAM,
  REQUIRED_PARAM,
  OPTIONAL_PARAM
};

/// Command-line options
map<string, std::pair<OptionID, IsParam>> optionMap =
{
  { "-a", make_pair(OPTION_ALPHA, REQUIRED_PARAM) },
  { "--alpha", make_pair(OPTION_ALPHA, REQUIRED_PARAM) },
  { "--alpha-y", make_pair(OPTION_ALPHA_Y, REQUIRED_PARAM) },
  { "--alpha-z", make_pair(OPTION_ALPHA_Z, REQUIRED_PARAM) },
  { "-d", make_pair(OPTION_DELEGLISE_RIVAT, NO_PARAM) },
  { "--deleglise-rivat", make_pair(OPTION_DELEGLISE_RIVAT, NO_PARAM) },
  { "--deleglise-rivat-64", make_pair(OPTION_DELEGLISE_RIVAT_64, NO_PARAM) },
  { "--deleglise-rivat-128", make_pair(OPTION_DELEGLISE_RIVAT_128, NO_PARAM) },
  { "-g", make_pair(OPTION_GOURDON, NO_PARAM) },
  { "--gourdon", make_pair(OPTION_GOURDON, NO_PARAM) },
  { "--gourdon-64", make_pair(OPTION_GOURDON_64, NO_PARAM) },
  { "--gourdon-128", make_pair(OPTION_GOURDON_128, NO_PARAM) },
  { "-h", make_pair(OPTION_HELP, NO_PARAM) },
  { "--help", make_pair(OPTION_HELP, NO_PARAM) },
  { "-l", make_pair(OPTION_LEGENDRE, NO_PARAM) },
  { "--legendre", make_pair(OPTION_LEGENDRE, NO_PARAM) },
  { "--lehmer", make_pair(OPTION_LEHMER, NO_PARAM) },
  { "--lmo", make_pair(OPTION_LMO, NO_PARAM) },
  { "--lmo1", make_pair(OPTION_LMO1, NO_PARAM) },
  { "--lmo2", make_pair(OPTION_LMO2, NO_PARAM) },
  { "--lmo3", make_pair(OPTION_LMO3, NO_PARAM) },
  { "--lmo4", make_pair(OPTION_LMO4, NO_PARAM) },
  { "--lmo5", make_pair(OPTION_LMO5, NO_PARAM) },
  { "-m", make_pair(OPTION_MEISSEL, NO_PARAM) },
  { "--meissel", make_pair(OPTION_MEISSEL, NO_PARAM) },
  { "-n", make_pair(OPTION_NTHPRIME, NO_PARAM) },
  { "--nth-prime", make_pair(OPTION_NTHPRIME, NO_PARAM) },
  { "--number", make_pair(OPTION_NUMBER, REQUIRED_PARAM) },
  { "-p", make_pair(OPTION_PRIMESIEVE, NO_PARAM) },
  { "--primesieve", make_pair(OPTION_PRIMESIEVE, NO_PARAM) },
  { "--Li", make_pair(OPTION_LI, NO_PARAM) },
  { "--Li-inverse", make_pair(OPTION_LIINV, NO_PARAM) },
  { "--Ri", make_pair(OPTION_RI, NO_PARAM) },
  { "--Ri-inverse", make_pair(OPTION_RIINV, NO_PARAM) },
  { "--phi", make_pair(OPTION_PHI, NO_PARAM) },
  { "--P2", make_pair(OPTION_P2, NO_PARAM) },
  { "--S1", make_pair(OPTION_S1, NO_PARAM) },
  { "--S2-easy", make_pair(OPTION_S2_EASY, NO_PARAM) },
  { "--S2-hard", make_pair(OPTION_S2_HARD, NO_PARAM) },
  { "--S2-trivial", make_pair(OPTION_S2_TRIVIAL, NO_PARAM) },
  { "--AC", make_pair(OPTION_AC, NO_PARAM) },
  { "--B", make_pair(OPTION_B, NO_PARAM) },
  { "--D", make_pair(OPTION_D, NO_PARAM) },
  { "--Phi0", make_pair(OPTION_PHI0, NO_PARAM) },
  { "--Sigma", make_pair(OPTION_SIGMA, NO_PARAM) },
  { "-s", make_pair(OPTION_STATUS, OPTIONAL_PARAM) },
  { "--status", make_pair(OPTION_STATUS, OPTIONAL_PARAM) },
  { "--test", make_pair(OPTION_TEST, NO_PARAM) },
  { "--time", make_pair(OPTION_TIME, NO_PARAM) },
  { "-t", make_pair(OPTION_THREADS, REQUIRED_PARAM) },
  { "--threads", make_pair(OPTION_THREADS, REQUIRED_PARAM) },
  { "-v", make_pair(OPTION_VERSION, NO_PARAM) },
  { "--version", make_pair(OPTION_VERSION, NO_PARAM) }
};

/// Command-line option
struct Option
{
  // Example:
  // str = "--threads=32"
  // opt = "--threads"
  // val = "32"
  string str;
  string opt;
  string val;

  template <typename T>
  T to() const
  {
    try {
      if (std::is_floating_point<T>::value)
        return (T) stod(val);
      else
        return (T) to_maxint(val);
    }
    catch (std::exception&) {
      throw primecount_error("invalid option '" + opt + "=" + val + "'");
    }
  }
};

/// Options start with "-" or "--", then
/// follows a Latin ASCII character.
///
bool isOption(const string& str)
{
  // Option of type: -o...
  if (str.size() >= 2 &&
      str[0] == '-' &&
      ((str[1] >= 'a' && str[1] <= 'z') ||
       (str[1] >= 'A' && str[1] <= 'Z')))
    return true;

  // Option of type: --o...
  if (str.size() >= 3 &&
      str[0] == '-' &&
      str[1] == '-' &&
      ((str[2] >= 'a' && str[2] <= 'z') ||
       (str[2] >= 'A' && str[2] <= 'Z')))
    return true;

  return false;
}

void optionStatus(Option& opt,
                  CmdOptions& opts)
{
  set_print(true);
  opts.time = true;

  if (!opt.val.empty())
    set_status_precision(opt.to<int>());
}

/// Parse the next command-line option.
/// e.g. "--threads=32"
/// -> opt.str = "--threads=32"
/// -> opt.opt = "--threads"
/// -> opt.val = "8"
///
Option parseOption(int argc, char* argv[], int& i)
{
  Option opt;
  opt.str = argv[i];

  if (opt.str.empty())
    throw primecount_error("unrecognized option ''");

  // Check if the option has the format:
  // --opt or -o (but not --opt=N)
  if (optionMap.count(opt.str))
  {
    opt.opt = opt.str;
    IsParam isParam = optionMap[opt.str].second;

    if (isParam == REQUIRED_PARAM)
    {
      i += 1;

      if (i < argc)
        opt.val = argv[i];

      // Prevent --threads --other-option
      if (opt.val.empty() || isOption(opt.val))
        throw primecount_error("missing value for option '" + opt.opt + "'");
    }

    // If the option takes an optional argument we
    // assume the next value is an optional argument
    // if the value is not a vaild option.
    if (isParam == OPTIONAL_PARAM &&
        i + 1 < argc &&
        !string(argv[i + 1]).empty() &&
        !isOption(argv[i + 1]))
    {
      i += 1;
      opt.val = argv[i];
    }
  }
  else
  {
    // Here the option is either:
    // 1) An option of type: --opt[=N]
    // 2) An option of type: --opt[N]
    // 3) A number (e.g. the start number)

    if (isOption(opt.str))
    {
      size_t pos = opt.str.find("=");

      // Option of type: --opt=N
      if (pos != string::npos)
      {
        opt.opt = opt.str.substr(0, pos);
        opt.val = opt.str.substr(pos + 1);

        // Print partial option: --opt (without =N)
        if (!optionMap.count(opt.opt))
          throw primecount_error("unrecognized option '" + opt.opt + "'");
      }
      else
      {
        // Option of type: --opt[N]
        pos = opt.str.find_first_of("0123456789");

        if (pos == string::npos)
          opt.opt = opt.str;
        else
        {
          opt.opt = opt.str.substr(0, pos);
          opt.val = opt.str.substr(pos);
        }

        // Print full option e.g.: --opt123
        if (!optionMap.count(opt.opt))
          throw primecount_error("unrecognized option '" + opt.str + "'");
      }

      // Prevent '--option='
      if (opt.val.empty() &&
          optionMap[opt.opt].second == REQUIRED_PARAM)
        throw primecount_error("missing value for option '" + opt.opt + "'");
    }
    else
    {
      // Here the option is actually a number or
      // an integer arithmetic expression.
      opt.opt = "--number";
      opt.val = opt.str;

      // This is not a valid number
      if (opt.str.find_first_of("0123456789") == string::npos)
        throw primecount_error("unrecognized option '" + opt.str + "'");

      // Prevent negative numbers as there are
      // no negative prime numbers.
      if (opt.str.at(0) == '-')
        throw primecount_error("unrecognized option '" + opt.str + "'");
    }
  }

  return opt;
}

CmdOptions parseOptions(int argc, char* argv[])
{
  CmdOptions opts;
  vector<maxint_t> numbers;

  // No command-line options provided
  if (argc <= 1)
    help(/* exitCode */ 1);

  for (int i = 1; i < argc; i++)
  {
    Option opt = parseOption(argc, argv, i);
    OptionID optionID = optionMap[opt.opt].first;

    switch (optionID)
    {
      case OPTION_ALPHA:   set_alpha(opt.to<double>()); break;
      case OPTION_ALPHA_Y: set_alpha_y(opt.to<double>()); break;
      case OPTION_ALPHA_Z: set_alpha_z(opt.to<double>()); break;
      case OPTION_NUMBER:  numbers.push_back(opt.to<maxint_t>()); break;
      case OPTION_THREADS: set_num_threads(opt.to<int>()); break;
      case OPTION_HELP:    help(/* exitCode */ 0); break;
      case OPTION_STATUS:  optionStatus(opt, opts); break;
      case OPTION_TIME:    opts.time = true; break;
      case OPTION_TEST:    test(); break;
      case OPTION_VERSION: version(); break;
      default:             opts.option = optionID;
    }
  }

  if (opts.option == OPTION_PHI)
  {
    if (numbers.size() < 2)
      throw primecount_error("option --phi requires 2 numbers");
    opts.a = numbers[1];
  }

  if (numbers.empty())
    throw primecount_error("missing x number");

  opts.x = numbers[0];

  return opts;
}

} // namespace
<|endoftext|>


<commit_before>/**
 * Copyright 2013 Truphone
 */
#include <bb/cascades/Application>
#include <bb/cascades/TabbedPane>
#include <bb/cascades/Tab>
#include <bb/cascades/Page>
#include <bb/cascades/ActionItem>
#include <QTcpSocket>

#include "CascadesHarness.h"
#include "Connection.h"
#include "CommandFactory.h"
#include "Utils.h"
#include "Server.h"

namespace truphone
{
namespace test
{
namespace cascades
{
    CascadesHarness::CascadesHarness(
            QObject* parent) :
        QObject(parent),
        serverSocket(new Server(this)),
        delim(", ")
    {
        if (this->serverSocket)
        {
            connect(this->serverSocket,
                    SIGNAL(newConnection(Connection*)),
                    SLOT(handleNewConnection(Connection*)));
        }
    }

    CascadesHarness::~CascadesHarness()
    {
        if (this->serverSocket)
        {
            try
            {
                this->serverSocket->close();
            }
            catch (...)  // NOLINT(whitespace/parens)
            {
                qWarning("Caught an unexpected exception on socket close");
            }
            delete this->serverSocket;
        }
    }

    // cppcheck-suppress unusedFunction This is the entry point for clients
    bool CascadesHarness::startHarness(const quint16 port)
    {
        return this->serverSocket->startServer(port);
    }

    void CascadesHarness::handleNewConnection(Connection * connection)
    {
        connect(connection,
                SIGNAL(packetReceived(Connection*, const QString&)),
                SLOT(processPacket(Connection*, const QString&)));
    }

    void CascadesHarness::processPacket(Connection * connection, const QString& packet)
    {
        QStringList tokens = Utils::tokenise(this->delim, packet.trimmed());
        if (not tokens.empty())
        {
            const QString command = tokens.first();
            if (not command.startsWith("#", Qt::CaseInsensitive))
            {
                tokens.removeFirst();
                Command * const cmd = CommandFactory::getCommand(
                            connection,
                            command,
                            this);
                if (cmd)
                {
                    const bool cmdOk = cmd->executeCommand(&tokens);
                    if (cmdOk)
                    {
                        connection->write("OK\r\n");
                    }
                    // may not actually clean/delete anything right
                    // now if the command is async
                    cmd->cleanUp();
                }
                else
                {
                    connection->write("ERROR: I don't understand that command\r\n");
                }
            }
        }
    }
}  // namespace cascades
}  // namespace test
}  // namespace truphone
<commit_msg>Print out the command being executed.<commit_after>/**
 * Copyright 2013 Truphone
 */
#include <bb/cascades/Application>
#include <bb/cascades/TabbedPane>
#include <bb/cascades/Tab>
#include <bb/cascades/Page>
#include <bb/cascades/ActionItem>
#include <QTcpSocket>

#include "CascadesHarness.h"
#include "Connection.h"
#include "CommandFactory.h"
#include "Utils.h"
#include "Server.h"

namespace truphone
{
namespace test
{
namespace cascades
{
    CascadesHarness::CascadesHarness(
            QObject* parent) :
        QObject(parent),
        serverSocket(new Server(this)),
        delim(", ")
    {
        if (this->serverSocket)
        {
            connect(this->serverSocket,
                    SIGNAL(newConnection(Connection*)),
                    SLOT(handleNewConnection(Connection*)));
        }
    }

    CascadesHarness::~CascadesHarness()
    {
        if (this->serverSocket)
        {
            try
            {
                this->serverSocket->close();
            }
            catch (...)  // NOLINT(whitespace/parens)
            {
                qWarning("Caught an unexpected exception on socket close");
            }
            delete this->serverSocket;
        }
    }

    // cppcheck-suppress unusedFunction This is the entry point for clients
    bool CascadesHarness::startHarness(const quint16 port)
    {
        return this->serverSocket->startServer(port);
    }

    void CascadesHarness::handleNewConnection(Connection * connection)
    {
        connect(connection,
                SIGNAL(packetReceived(Connection*, const QString&)),
                SLOT(processPacket(Connection*, const QString&)));
    }

    void CascadesHarness::processPacket(Connection * connection, const QString& packet)
    {
        QStringList tokens = Utils::tokenise(this->delim, packet.trimmed());
        qDebug() << "test-cascades-lib: " << packet.trimmed();
        if (not tokens.empty())
        {
            const QString command = tokens.first();
            if (not command.startsWith("#", Qt::CaseInsensitive))
            {
                tokens.removeFirst();
                Command * const cmd = CommandFactory::getCommand(
                            connection,
                            command,
                            this);
                if (cmd)
                {
                    const bool cmdOk = cmd->executeCommand(&tokens);
                    if (cmdOk)
                    {
                        connection->write("OK\r\n");
                    }
                    // may not actually clean/delete anything right
                    // now if the command is async
                    cmd->cleanUp();
                }
                else
                {
                    connection->write("ERROR: I don't understand that command\r\n");
                }
            }
        }
    }
}  // namespace cascades
}  // namespace test
}  // namespace truphone
<|endoftext|>


<commit_before>// ======================================================================== //
// Copyright 2009-2016 Intel Corporation                                    //
//                                                                          //
// 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.                                           //
// ======================================================================== //

#include "taskschedulerinternal.h"
#include "../math/math.h"
#include "../sys/sysinfo.h"
#include <algorithm>

namespace embree
{
  size_t TaskScheduler::g_numThreads = 0;
  __thread TaskScheduler* TaskScheduler::g_instance = nullptr;
  __thread TaskScheduler::Thread* TaskScheduler::thread_local_thread = nullptr;
  TaskScheduler::ThreadPool* TaskScheduler::threadPool = nullptr;

  template<typename Predicate, typename Body>
  __forceinline void TaskScheduler::steal_loop(Thread& thread, const Predicate& pred, const Body& body)
  {
    while (true)
    {
      /*! some rounds that yield */
      for (size_t i=0; i<32; i++)
      {
        /*! some spinning rounds */
        const size_t threadCount = thread.threadCount();
        for (size_t j=0; j<1024; j+=threadCount)
        {
          if (!pred()) return;
          if (thread.scheduler->steal_from_other_threads(thread)) {
            i=j=0;
            body();
          }
        }
        yield();
      }
    }
  }

  /*! run this task */
  __dllexport void TaskScheduler::Task::run (Thread& thread) // FIXME: avoid as many __dllexports as possible
  {
    /* try to run if not already stolen */
    if (try_switch_state(INITIALIZED,DONE))
    {
      Task* prevTask = thread.task;
      thread.task = this;
      try {
        if (thread.scheduler->cancellingException == nullptr)
          closure->execute();
      } catch (...) {
        if (thread.scheduler->cancellingException == nullptr)
          thread.scheduler->cancellingException = std::current_exception();
      }
      thread.task = prevTask;
      add_dependencies(-1);
    }

    /* steal until all dependencies have completed */
    steal_loop(thread,
               [&] () { return dependencies>0; },
               [&] () { while (thread.tasks.execute_local(thread,this)); });

    /* now signal our parent task that we are finished */
    if (parent)
      parent->add_dependencies(-1);
  }

  __dllexport bool TaskScheduler::TaskQueue::execute_local(Thread& thread, Task* parent)
  {
    /* stop if we run out of local tasks or reach the waiting task */
    if (right == 0 || &tasks[right-1] == parent)
      return false;

    /* execute task */
    size_t oldRight = right;
    tasks[right-1].run(thread);
    if (right != oldRight) {
      THROW_RUNTIME_ERROR("you have to wait for spawned subtasks");
    }

    /* pop task and closure from stack */
    right--;
    if (tasks[right].stackPtr != size_t(-1))
      stackPtr = tasks[right].stackPtr;

    /* also move left pointer */
    if (left >= right) left.store(right.load());

    return right != 0;
  }

  bool TaskScheduler::TaskQueue::steal(Thread& thread)
  {
    size_t l = left;
    if (l < right)
      l = left++;
    else
      return false;

    if (!tasks[l].try_steal(thread.tasks.tasks[thread.tasks.right]))
      return false;

    thread.tasks.right++;
    return true;
  }

  /* we steal from the left */
  size_t TaskScheduler::TaskQueue::getTaskSizeAtLeft()
  {
    if (left >= right) return 0;
    return tasks[left].N;
  }

  static MutexSys g_mutex;
  static BarrierSys g_barrier(2);

  void threadPoolFunction(std::pair<TaskScheduler::ThreadPool*,size_t>* pair)
  {
    TaskScheduler::ThreadPool* pool = pair->first;
    size_t threadIndex = pair->second;
    g_barrier.wait();
    pool->thread_loop(threadIndex);
  }

  TaskScheduler::ThreadPool::ThreadPool(bool set_affinity)
    : numThreads(0), numThreadsRunning(0), set_affinity(set_affinity), running(false) {}

  __dllexport void TaskScheduler::ThreadPool::startThreads()
  {
    if (running) return;
    setNumThreads(numThreads,true);
  }

  void TaskScheduler::ThreadPool::setNumThreads(size_t newNumThreads, bool startThreads)
  {
    Lock<MutexSys> lock(g_mutex);

    if (newNumThreads == 0)
      newNumThreads = getNumberOfLogicalThreads();

    numThreads = newNumThreads;
    if (!startThreads && !running) return;
    running = true;
    size_t numThreadsActive = numThreadsRunning;

    mutex.lock();
    numThreadsRunning = newNumThreads;
    mutex.unlock();
    condition.notify_all();

    /* start new threads */
    for (size_t t=numThreadsActive; t<numThreads; t++)
    {
      if (t == 0) continue;
      auto pair = std::make_pair(this,t);
      threads.push_back(createThread((thread_func)threadPoolFunction,&pair,4*1024*1024,set_affinity ? t : -1));
      g_barrier.wait();
    }

    /* stop some threads if we reduce the number of threads */
    for (ssize_t t=numThreadsActive-1; t>=ssize_t(numThreadsRunning); t--) {
      if (t == 0) continue;
      embree::join(threads.back());
      threads.pop_back();
    }
  }

  TaskScheduler::ThreadPool::~ThreadPool()
  {
    /* leave all taskschedulers */
    mutex.lock();
    numThreadsRunning = 0;
    mutex.unlock();
    condition.notify_all();

    /* wait for threads to terminate */
    for (size_t i=0; i<threads.size(); i++)
      embree::join(threads[i]);
  }

  __dllexport void TaskScheduler::ThreadPool::add(const Ref<TaskScheduler>& scheduler)
  {
    mutex.lock();
    schedulers.push_back(scheduler);
    mutex.unlock();
    condition.notify_all();
  }

  __dllexport void TaskScheduler::ThreadPool::remove(const Ref<TaskScheduler>& scheduler)
  {
    Lock<MutexSys> lock(mutex);
    for (std::list<Ref<TaskScheduler> >::iterator it = schedulers.begin(); it != schedulers.end(); it++) {
      if (scheduler == *it) {
        schedulers.erase(it);
        return;
      }
    }
  }

  void TaskScheduler::ThreadPool::thread_loop(size_t globalThreadIndex)
  {
    while (globalThreadIndex < numThreadsRunning)
    {
      Ref<TaskScheduler> scheduler = NULL;
      ssize_t threadIndex = -1;
      {
        Lock<MutexSys> lock(mutex);
        condition.wait(mutex, [&] () { return globalThreadIndex >= numThreadsRunning || !schedulers.empty(); });
        if (globalThreadIndex >= numThreadsRunning) break;
        scheduler = schedulers.front();
        threadIndex = scheduler->allocThreadIndex();
      }
      scheduler->thread_loop(threadIndex);
    }
  }

  TaskScheduler::TaskScheduler()
    : threadCounter(0), anyTasksRunning(0), hasRootTask(false)
  {
    threadLocal.resize(2*getNumberOfLogicalThreads()); // FIXME: this has to be 2x as in the join mode the worker threads also join
    for (size_t i=0; i<threadLocal.size(); i++)
      threadLocal[i].store(nullptr);
  }

  TaskScheduler::~TaskScheduler()
  {
    assert(threadCounter == 0);
  }

  __dllexport size_t TaskScheduler::threadIndex()
  {
    Thread* thread = TaskScheduler::thread();
    if (thread) return thread->threadIndex;
    else        return 0;
  }

  __dllexport size_t TaskScheduler::threadCount() {
    return threadPool->size();
  }

  __dllexport TaskScheduler* TaskScheduler::instance()
  {
    if (g_instance == NULL) {
      g_instance = new TaskScheduler;
      g_instance->refInc();
    }
    return g_instance;
  }

  void TaskScheduler::create(size_t numThreads, bool set_affinity)
  {
    if (!threadPool) threadPool = new TaskScheduler::ThreadPool(set_affinity);
    threadPool->setNumThreads(numThreads,false);
  }

  void TaskScheduler::destroy() {
    delete threadPool; threadPool = nullptr;
  }

  __dllexport ssize_t TaskScheduler::allocThreadIndex()
  {
    size_t threadIndex = threadCounter++;
    assert(threadIndex < threadLocal.size());
    return threadIndex;
  }

  void TaskScheduler::join()
  {
    mutex.lock();
    size_t threadIndex = allocThreadIndex();
    condition.wait(mutex, [&] () { return hasRootTask.load(); });
    mutex.unlock();
    std::exception_ptr except = thread_loop(threadIndex);
    if (except != nullptr) std::rethrow_exception(except);
  }

  void TaskScheduler::reset() {
    hasRootTask = false;
  }

  void TaskScheduler::wait_for_threads(size_t threadCount)
  {
    while (threadCounter < threadCount-1)
      __pause_cpu();
  }

  __dllexport TaskScheduler::Thread* TaskScheduler::thread() {
    return thread_local_thread;
  }

  __dllexport TaskScheduler::Thread* TaskScheduler::swapThread(Thread* thread)
  {
    Thread* old = thread_local_thread;
    thread_local_thread = thread;
    return old;
  }

  __dllexport bool TaskScheduler::wait()
  {
    Thread* thread = TaskScheduler::thread();
    if (thread == nullptr) return true;
    while (thread->tasks.execute_local(*thread,thread->task)) {};
    return thread->scheduler->cancellingException == nullptr;
  }

  std::exception_ptr TaskScheduler::thread_loop(size_t threadIndex)
  {
    /* allocate thread structure */
    std::unique_ptr<Thread> mthread(new Thread(threadIndex,this)); // too large for stack allocation
    Thread& thread = *mthread;
    threadLocal[threadIndex].store(&thread);
    Thread* oldThread = swapThread(&thread);

    /* main thread loop */
    while (anyTasksRunning)
    {
      steal_loop(thread,
                 [&] () { return anyTasksRunning > 0; },
                 [&] () {
                   anyTasksRunning++;
                   while (thread.tasks.execute_local(thread,nullptr));
                   anyTasksRunning--;
                 });
    }
    threadLocal[threadIndex].store(nullptr);
    swapThread(oldThread);

    /* remember exception to throw */
    std::exception_ptr except = nullptr;
    if (cancellingException != nullptr) except = cancellingException;

    /* wait for all threads to terminate */
    threadCounter--;
    while (threadCounter > 0) yield();
    return except;
  }

  bool TaskScheduler::steal_from_other_threads(Thread& thread)
  {
    const size_t threadIndex = thread.threadIndex;
    const size_t threadCount = this->threadCounter;

    for (size_t i=1; i<threadCount; i++)
    {
      __pause_cpu(32);
      size_t otherThreadIndex = threadIndex+i;
      if (otherThreadIndex >= threadCount) otherThreadIndex -= threadCount;

      Thread* othread = threadLocal[otherThreadIndex].load();
      if (!othread)
        continue;

      if (othread->tasks.steal(thread))
        return true;
    }

    return false;
  }

  __dllexport void TaskScheduler::startThreads() {
    threadPool->startThreads();
  }

  __dllexport void TaskScheduler::addScheduler(const Ref<TaskScheduler>& scheduler) {
    threadPool->add(scheduler);
  }

  __dllexport void TaskScheduler::removeScheduler(const Ref<TaskScheduler>& scheduler) {
    threadPool->remove(scheduler);
  }
}
<commit_msg>added spinning loop when terminating threads in internal tasking system<commit_after>// ======================================================================== //
// Copyright 2009-2016 Intel Corporation                                    //
//                                                                          //
// 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.                                           //
// ======================================================================== //

#include "taskschedulerinternal.h"
#include "../math/math.h"
#include "../sys/sysinfo.h"
#include <algorithm>

namespace embree
{
  size_t TaskScheduler::g_numThreads = 0;
  __thread TaskScheduler* TaskScheduler::g_instance = nullptr;
  __thread TaskScheduler::Thread* TaskScheduler::thread_local_thread = nullptr;
  TaskScheduler::ThreadPool* TaskScheduler::threadPool = nullptr;

  template<typename Predicate, typename Body>
  __forceinline void TaskScheduler::steal_loop(Thread& thread, const Predicate& pred, const Body& body)
  {
    while (true)
    {
      /*! some rounds that yield */
      for (size_t i=0; i<32; i++)
      {
        /*! some spinning rounds */
        const size_t threadCount = thread.threadCount();
        for (size_t j=0; j<1024; j+=threadCount)
        {
          if (!pred()) return;
          if (thread.scheduler->steal_from_other_threads(thread)) {
            i=j=0;
            body();
          }
        }
        yield();
      }
    }
  }

  /*! run this task */
  __dllexport void TaskScheduler::Task::run (Thread& thread) // FIXME: avoid as many __dllexports as possible
  {
    /* try to run if not already stolen */
    if (try_switch_state(INITIALIZED,DONE))
    {
      Task* prevTask = thread.task;
      thread.task = this;
      try {
        if (thread.scheduler->cancellingException == nullptr)
          closure->execute();
      } catch (...) {
        if (thread.scheduler->cancellingException == nullptr)
          thread.scheduler->cancellingException = std::current_exception();
      }
      thread.task = prevTask;
      add_dependencies(-1);
    }

    /* steal until all dependencies have completed */
    steal_loop(thread,
               [&] () { return dependencies>0; },
               [&] () { while (thread.tasks.execute_local(thread,this)); });

    /* now signal our parent task that we are finished */
    if (parent)
      parent->add_dependencies(-1);
  }

  __dllexport bool TaskScheduler::TaskQueue::execute_local(Thread& thread, Task* parent)
  {
    /* stop if we run out of local tasks or reach the waiting task */
    if (right == 0 || &tasks[right-1] == parent)
      return false;

    /* execute task */
    size_t oldRight = right;
    tasks[right-1].run(thread);
    if (right != oldRight) {
      THROW_RUNTIME_ERROR("you have to wait for spawned subtasks");
    }

    /* pop task and closure from stack */
    right--;
    if (tasks[right].stackPtr != size_t(-1))
      stackPtr = tasks[right].stackPtr;

    /* also move left pointer */
    if (left >= right) left.store(right.load());

    return right != 0;
  }

  bool TaskScheduler::TaskQueue::steal(Thread& thread)
  {
    size_t l = left;
    if (l < right)
      l = left++;
    else
      return false;

    if (!tasks[l].try_steal(thread.tasks.tasks[thread.tasks.right]))
      return false;

    thread.tasks.right++;
    return true;
  }

  /* we steal from the left */
  size_t TaskScheduler::TaskQueue::getTaskSizeAtLeft()
  {
    if (left >= right) return 0;
    return tasks[left].N;
  }

  static MutexSys g_mutex;
  static BarrierSys g_barrier(2);

  void threadPoolFunction(std::pair<TaskScheduler::ThreadPool*,size_t>* pair)
  {
    TaskScheduler::ThreadPool* pool = pair->first;
    size_t threadIndex = pair->second;
    g_barrier.wait();
    pool->thread_loop(threadIndex);
  }

  TaskScheduler::ThreadPool::ThreadPool(bool set_affinity)
    : numThreads(0), numThreadsRunning(0), set_affinity(set_affinity), running(false) {}

  __dllexport void TaskScheduler::ThreadPool::startThreads()
  {
    if (running) return;
    setNumThreads(numThreads,true);
  }

  void TaskScheduler::ThreadPool::setNumThreads(size_t newNumThreads, bool startThreads)
  {
    Lock<MutexSys> lock(g_mutex);

    if (newNumThreads == 0)
      newNumThreads = getNumberOfLogicalThreads();

    numThreads = newNumThreads;
    if (!startThreads && !running) return;
    running = true;
    size_t numThreadsActive = numThreadsRunning;

    mutex.lock();
    numThreadsRunning = newNumThreads;
    mutex.unlock();
    condition.notify_all();

    /* start new threads */
    for (size_t t=numThreadsActive; t<numThreads; t++)
    {
      if (t == 0) continue;
      auto pair = std::make_pair(this,t);
      threads.push_back(createThread((thread_func)threadPoolFunction,&pair,4*1024*1024,set_affinity ? t : -1));
      g_barrier.wait();
    }

    /* stop some threads if we reduce the number of threads */
    for (ssize_t t=numThreadsActive-1; t>=ssize_t(numThreadsRunning); t--) {
      if (t == 0) continue;
      embree::join(threads.back());
      threads.pop_back();
    }
  }

  TaskScheduler::ThreadPool::~ThreadPool()
  {
    /* leave all taskschedulers */
    mutex.lock();
    numThreadsRunning = 0;
    mutex.unlock();
    condition.notify_all();

    /* wait for threads to terminate */
    for (size_t i=0; i<threads.size(); i++)
      embree::join(threads[i]);
  }

  __dllexport void TaskScheduler::ThreadPool::add(const Ref<TaskScheduler>& scheduler)
  {
    mutex.lock();
    schedulers.push_back(scheduler);
    mutex.unlock();
    condition.notify_all();
  }

  __dllexport void TaskScheduler::ThreadPool::remove(const Ref<TaskScheduler>& scheduler)
  {
    Lock<MutexSys> lock(mutex);
    for (std::list<Ref<TaskScheduler> >::iterator it = schedulers.begin(); it != schedulers.end(); it++) {
      if (scheduler == *it) {
        schedulers.erase(it);
        return;
      }
    }
  }

  void TaskScheduler::ThreadPool::thread_loop(size_t globalThreadIndex)
  {
    while (globalThreadIndex < numThreadsRunning)
    {
      Ref<TaskScheduler> scheduler = NULL;
      ssize_t threadIndex = -1;
      {
        Lock<MutexSys> lock(mutex);
        condition.wait(mutex, [&] () { return globalThreadIndex >= numThreadsRunning || !schedulers.empty(); });
        if (globalThreadIndex >= numThreadsRunning) break;
        scheduler = schedulers.front();
        threadIndex = scheduler->allocThreadIndex();
      }
      scheduler->thread_loop(threadIndex);
    }
  }

  TaskScheduler::TaskScheduler()
    : threadCounter(0), anyTasksRunning(0), hasRootTask(false)
  {
    threadLocal.resize(2*getNumberOfLogicalThreads()); // FIXME: this has to be 2x as in the join mode the worker threads also join
    for (size_t i=0; i<threadLocal.size(); i++)
      threadLocal[i].store(nullptr);
  }

  TaskScheduler::~TaskScheduler()
  {
    assert(threadCounter == 0);
  }

  __dllexport size_t TaskScheduler::threadIndex()
  {
    Thread* thread = TaskScheduler::thread();
    if (thread) return thread->threadIndex;
    else        return 0;
  }

  __dllexport size_t TaskScheduler::threadCount() {
    return threadPool->size();
  }

  __dllexport TaskScheduler* TaskScheduler::instance()
  {
    if (g_instance == NULL) {
      g_instance = new TaskScheduler;
      g_instance->refInc();
    }
    return g_instance;
  }

  void TaskScheduler::create(size_t numThreads, bool set_affinity)
  {
    if (!threadPool) threadPool = new TaskScheduler::ThreadPool(set_affinity);
    threadPool->setNumThreads(numThreads,false);
  }

  void TaskScheduler::destroy() {
    delete threadPool; threadPool = nullptr;
  }

  __dllexport ssize_t TaskScheduler::allocThreadIndex()
  {
    size_t threadIndex = threadCounter++;
    assert(threadIndex < threadLocal.size());
    return threadIndex;
  }

  void TaskScheduler::join()
  {
    mutex.lock();
    size_t threadIndex = allocThreadIndex();
    condition.wait(mutex, [&] () { return hasRootTask.load(); });
    mutex.unlock();
    std::exception_ptr except = thread_loop(threadIndex);
    if (except != nullptr) std::rethrow_exception(except);
  }

  void TaskScheduler::reset() {
    hasRootTask = false;
  }

  void TaskScheduler::wait_for_threads(size_t threadCount)
  {
    while (threadCounter < threadCount-1)
      __pause_cpu();
  }

  __dllexport TaskScheduler::Thread* TaskScheduler::thread() {
    return thread_local_thread;
  }

  __dllexport TaskScheduler::Thread* TaskScheduler::swapThread(Thread* thread)
  {
    Thread* old = thread_local_thread;
    thread_local_thread = thread;
    return old;
  }

  __dllexport bool TaskScheduler::wait()
  {
    Thread* thread = TaskScheduler::thread();
    if (thread == nullptr) return true;
    while (thread->tasks.execute_local(*thread,thread->task)) {};
    return thread->scheduler->cancellingException == nullptr;
  }

  std::exception_ptr TaskScheduler::thread_loop(size_t threadIndex)
  {
    /* allocate thread structure */
    std::unique_ptr<Thread> mthread(new Thread(threadIndex,this)); // too large for stack allocation
    Thread& thread = *mthread;
    threadLocal[threadIndex].store(&thread);
    Thread* oldThread = swapThread(&thread);

    /* main thread loop */
    while (anyTasksRunning)
    {
      steal_loop(thread,
                 [&] () { return anyTasksRunning > 0; },
                 [&] () {
                   anyTasksRunning++;
                   while (thread.tasks.execute_local(thread,nullptr));
                   anyTasksRunning--;
                 });
    }
    threadLocal[threadIndex].store(nullptr);
    swapThread(oldThread);

    /* remember exception to throw */
    std::exception_ptr except = nullptr;
    if (cancellingException != nullptr) except = cancellingException;

    /* wait for all threads to terminate */
    threadCounter--;
	size_t loopIndex = 1;
#define LOOP_YIELD_THRESHOLD 4096
	while (threadCounter > 0) {
		if ((loopIndex % LOOP_YIELD_THRESHOLD) == 0)
			yield();
		else
			_mm_pause();

	  loopIndex++;
	}
    return except;
  }

  bool TaskScheduler::steal_from_other_threads(Thread& thread)
  {
    const size_t threadIndex = thread.threadIndex;
    const size_t threadCount = this->threadCounter;

    for (size_t i=1; i<threadCount; i++)
    {
      __pause_cpu(32);
      size_t otherThreadIndex = threadIndex+i;
      if (otherThreadIndex >= threadCount) otherThreadIndex -= threadCount;

      Thread* othread = threadLocal[otherThreadIndex].load();
      if (!othread)
        continue;

      if (othread->tasks.steal(thread))
        return true;
    }

    return false;
  }

  __dllexport void TaskScheduler::startThreads() {
    threadPool->startThreads();
  }

  __dllexport void TaskScheduler::addScheduler(const Ref<TaskScheduler>& scheduler) {
    threadPool->add(scheduler);
  }

  __dllexport void TaskScheduler::removeScheduler(const Ref<TaskScheduler>& scheduler) {
    threadPool->remove(scheduler);
  }
}
<|endoftext|>


<commit_before>#pragma once

#include <QStandardItemModel>
#include <QTimer>
#include <boost/noncopyable.hpp>
#include <pajlada/signals/signal.hpp>
#include <vector>

#include "debug/AssertInGuiThread.hpp"

namespace chatterino {

template <typename TVectorItem>
struct SignalVectorItemArgs {
    const TVectorItem &item;
    int index;
    void *caller;
};

template <typename TVectorItem>
class ReadOnlySignalVector : boost::noncopyable
{
public:
    ReadOnlySignalVector()
    {
        QObject::connect(&this->itemsChangedTimer, &QTimer::timeout,
                         [this] { this->delayedItemsChanged.invoke(); });
        this->itemsChangedTimer.setInterval(100);
        this->itemsChangedTimer.setSingleShot(true);
    }
    virtual ~ReadOnlySignalVector() = default;

    pajlada::Signals::Signal<SignalVectorItemArgs<TVectorItem>> itemInserted;
    pajlada::Signals::Signal<SignalVectorItemArgs<TVectorItem>> itemRemoved;
    pajlada::Signals::NoArgSignal delayedItemsChanged;

    const std::vector<TVectorItem> &getVector() const
    {
        assertInGuiThread();

        return this->vector;
    }

    void invokeDelayedItemsChanged()
    {
        assertInGuiThread();

        if (!this->itemsChangedTimer.isActive()) {
            itemsChangedTimer.start();
        }
    }

    virtual bool isSorted() const = 0;

protected:
    std::vector<TVectorItem> vector;
    QTimer itemsChangedTimer;
};

template <typename TVectorItem>
class BaseSignalVector : public ReadOnlySignalVector<TVectorItem>
{
public:
    // returns the actual index of the inserted item
    virtual int insertItem(const TVectorItem &item, int proposedIndex = -1, void *caller = 0) = 0;

    void removeItem(int index, void *caller = 0)
    {
        assertInGuiThread();
        assert(index >= 0 && index < this->vector.size());

        TVectorItem item = this->vector[index];
        this->vector.erase(this->vector.begin() + index);
        SignalVectorItemArgs<TVectorItem> args{item, index, caller};
        this->itemRemoved.invoke(args);

        this->invokeDelayedItemsChanged();
    }

    int appendItem(const TVectorItem &item, void *caller = 0)
    {
        return this->insertItem(item, -1, caller);
    }
};

template <typename TVectorItem>
class UnsortedSignalVector : public BaseSignalVector<TVectorItem>
{
public:
    virtual int insertItem(const TVectorItem &item, int index = -1, void *caller = 0) override
    {
        assertInGuiThread();
        if (index == -1) {
            index = this->vector.size();
        } else {
            assert(index >= 0 && index <= this->vector.size());
        }

        this->vector.insert(this->vector.begin() + index, item);

        SignalVectorItemArgs<TVectorItem> args{item, index, caller};
        this->itemInserted.invoke(args);
        this->invokeDelayedItemsChanged();
        return index;
    }

    virtual bool isSorted() const override
    {
        return false;
    }
};

template <typename TVectorItem, typename Compare>
class SortedSignalVector : public BaseSignalVector<TVectorItem>
{
public:
    virtual int insertItem(const TVectorItem &item, int = -1, void *caller = nullptr) override
    {
        assertInGuiThread();

        auto it = std::lower_bound(this->vector.begin(), this->vector.end(), item, Compare{});
        int index = it - this->vector.begin();
        this->vector.insert(it, item);

        SignalVectorItemArgs<TVectorItem> args{item, index, caller};
        this->itemInserted.invoke(args);
        this->invokeDelayedItemsChanged();
        return index;
    }

    virtual bool isSorted() const override
    {
        return true;
    }
};

}  // namespace chatterino
<commit_msg>replaced 0 with nullptr in signalvector<commit_after>#pragma once

#include <QStandardItemModel>
#include <QTimer>
#include <boost/noncopyable.hpp>
#include <pajlada/signals/signal.hpp>
#include <vector>

#include "debug/AssertInGuiThread.hpp"

namespace chatterino {

template <typename TVectorItem>
struct SignalVectorItemArgs {
    const TVectorItem &item;
    int index;
    void *caller;
};

template <typename TVectorItem>
class ReadOnlySignalVector : boost::noncopyable
{
public:
    ReadOnlySignalVector()
    {
        QObject::connect(&this->itemsChangedTimer, &QTimer::timeout,
                         [this] { this->delayedItemsChanged.invoke(); });
        this->itemsChangedTimer.setInterval(100);
        this->itemsChangedTimer.setSingleShot(true);
    }
    virtual ~ReadOnlySignalVector() = default;

    pajlada::Signals::Signal<SignalVectorItemArgs<TVectorItem>> itemInserted;
    pajlada::Signals::Signal<SignalVectorItemArgs<TVectorItem>> itemRemoved;
    pajlada::Signals::NoArgSignal delayedItemsChanged;

    const std::vector<TVectorItem> &getVector() const
    {
        assertInGuiThread();

        return this->vector;
    }

    void invokeDelayedItemsChanged()
    {
        assertInGuiThread();

        if (!this->itemsChangedTimer.isActive()) {
            itemsChangedTimer.start();
        }
    }

    virtual bool isSorted() const = 0;

protected:
    std::vector<TVectorItem> vector;
    QTimer itemsChangedTimer;
};

template <typename TVectorItem>
class BaseSignalVector : public ReadOnlySignalVector<TVectorItem>
{
public:
    // returns the actual index of the inserted item
    virtual int insertItem(const TVectorItem &item, int proposedIndex = -1,
                           void *caller = nullptr) = 0;

    void removeItem(int index, void *caller = nullptr)
    {
        assertInGuiThread();
        assert(index >= 0 && index < this->vector.size());

        TVectorItem item = this->vector[index];
        this->vector.erase(this->vector.begin() + index);
        SignalVectorItemArgs<TVectorItem> args{item, index, caller};
        this->itemRemoved.invoke(args);

        this->invokeDelayedItemsChanged();
    }

    int appendItem(const TVectorItem &item, void *caller = nullptr)
    {
        return this->insertItem(item, -1, caller);
    }
};

template <typename TVectorItem>
class UnsortedSignalVector : public BaseSignalVector<TVectorItem>
{
public:
    virtual int insertItem(const TVectorItem &item, int index = -1, void *caller = nullptr) override
    {
        assertInGuiThread();
        if (index == -1) {
            index = this->vector.size();
        } else {
            assert(index >= 0 && index <= this->vector.size());
        }

        this->vector.insert(this->vector.begin() + index, item);

        SignalVectorItemArgs<TVectorItem> args{item, index, caller};
        this->itemInserted.invoke(args);
        this->invokeDelayedItemsChanged();
        return index;
    }

    virtual bool isSorted() const override
    {
        return false;
    }
};

template <typename TVectorItem, typename Compare>
class SortedSignalVector : public BaseSignalVector<TVectorItem>
{
public:
    virtual int insertItem(const TVectorItem &item, int = -1, void *caller = nullptr) override
    {
        assertInGuiThread();

        auto it = std::lower_bound(this->vector.begin(), this->vector.end(), item, Compare{});
        int index = it - this->vector.begin();
        this->vector.insert(it, item);

        SignalVectorItemArgs<TVectorItem> args{item, index, caller};
        this->itemInserted.invoke(args);
        this->invokeDelayedItemsChanged();
        return index;
    }

    virtual bool isSorted() const override
    {
        return true;
    }
};

}  // namespace chatterino
<|endoftext|>