<commit_before>/*
 *  Author:
 *     Guido Tack <guido.tack@monash.edu>
 *
 *  Copyright:
 *     NICTA 2013
 */

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "project.h"
#include "moocsubmission.h"
#include "solverdialog.h"
#include "process.h"

#include <QFileInfo>
#include <QDir>
#include <QDebug>
#include <QMessageBox>
#include <QSortFilterProxyModel>
#include <QJsonDocument>
#include <QJsonArray>

Project::Project(const QList<SolverConfiguration*>& configs, QObject* parent) : QObject(parent), solverConfigs(configs)
{
    itemModel = new QStandardItemModel(this);
    itemModel->setColumnCount(1);

    connect(itemModel, &QStandardItemModel::itemChanged, this, &Project::on_itemChanged);

    rootItem = new QStandardItem(QIcon(":/images/mznicon.png"), "Untitled Project");
    rootItem->setData(NodeType::ProjectFile, Role::Type);
    rootItem->setData("Untitled Project", Role::OriginalLabel);
    rootItem->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable);

    auto font = rootItem->font();
    font.setBold(true);

    modelsItem = new QStandardItem("Models");
    modelsItem->setData(NodeType::Group, Role::Type);
    modelsItem->setFlags(Qt::NoItemFlags);
    modelsItem->setFont(font);

    dataItem = new QStandardItem("Data (right click to run)");
    dataItem->setData(NodeType::Group, Role::Type);
    dataItem->setFlags(Qt::NoItemFlags);
    dataItem->setFont(font);

    checkersItem = new QStandardItem("Checkers (right click to run)");
    checkersItem->setData(NodeType::Group, Role::Type);
    checkersItem->setFlags(Qt::NoItemFlags);
    checkersItem->setFont(font);

    configsItem = new QStandardItem("Solver configurations");
    configsItem->setData(NodeType::Group, Role::Type);
    configsItem->setFlags(Qt::NoItemFlags);
    configsItem->setFont(font);

    otherItem = new QStandardItem("Other files");
    otherItem->setData(NodeType::Group, Role::Type);
    otherItem->setFlags(Qt::NoItemFlags);
    otherItem->setFont(font);

    rootItem->appendRow(modelsItem);
    rootItem->appendRow(dataItem);
    rootItem->appendRow(checkersItem);
    rootItem->appendRow(configsItem);
    rootItem->appendRow(otherItem);
    itemModel->appendRow(rootItem);
}

QStringList Project::loadProject(const QString& file, ConfigWindow* configWindow)
{
    clear();

    projectFile(file);

    QStringList warnings;

    QFile f(file);
    QFileInfo fi(f);
    if (!f.open(QFile::ReadOnly)) {
        throw FileError("Failed to open project file");
    }

    auto doc = QJsonDocument::fromJson(f.readAll());

    if (doc.isObject()) {
        loadJSON(doc.object(), fi, configWindow, warnings);
    } else {
        f.reset();
        QDataStream in(&f);
        loadLegacy(in, fi, configWindow, warnings);
    }

    f.close();

    for (auto& sc : solverConfigurationFiles()) {
        configWindow->addConfig(sc);
    }

    if (!selectedBuiltinConfigId.isEmpty() && !selectedBuiltinConfigVersion.isEmpty()) {
        int index = configWindow->findBuiltinConfig(selectedBuiltinConfigId, selectedBuiltinConfigVersion);
        if (index == -1) {
            warnings << "Could not find solver " + selectedBuiltinConfigId + "@" + selectedBuiltinConfigVersion;
        } else {
            configWindow->setCurrentIndex(index);
        }
    } else if (!selectedSolverConfigFile.isEmpty()) {
        int index = configWindow->findConfigFile(rootDir().absolutePath() + "/" + selectedSolverConfigFile);
        configWindow->setCurrentIndex(index);
    }

    setModified(false);

    return warnings;
}

void Project::loadJSON(const QJsonObject& obj, const QFileInfo& fi, ConfigWindow* configWindow, QStringList& warnings)
{
    int version = obj["version"].toInt();

    QString basePath = fi.absolutePath() + "/";

    auto of = obj["openFiles"].toArray();
    for (auto file : of) {
        auto path = basePath + file.toString();
        if (QFileInfo(path).exists()) {
            openTabs << path;
        } else {
            warnings << "The file " + file.toString() + " could not be found";
        }
    }

    openTabIndex = obj["openTab"].toInt();

    QList<SolverConfiguration*> configs;
    if (obj["builtinSolverConfigs"].isArray()) {
        for (auto config : obj["builtinSolverConfigs"].toArray()) {
            if (!config.isObject()) {
                warnings << "Failed to read solver builtin solver config";
                continue;
            }
            SolverConfiguration* loaded;
            if (version >= 106) {
                loaded = new (SolverConfiguration) (SolverConfiguration::loadJSON(QJsonDocument(config.toObject())));
            } else {
                loaded = new (SolverConfiguration) (SolverConfiguration::loadLegacy(QJsonDocument(config.toObject())));
            }
            loaded->isBuiltin = true;
            configs << loaded;
        }
    }

    if (obj["projectSolverConfigs"].isArray()) {
        for (auto config : obj["projectSolverConfigs"].toArray()) {
            if (!config.isObject()) {
                warnings << "Failed to read solver project solver config";
                continue;
            }
            auto loaded = new (SolverConfiguration) (SolverConfiguration::loadLegacy(QJsonDocument(config.toObject())));
            loaded->modified = true;
            configs << loaded;
        }
    }

    configWindow->mergeConfigs(configs);

    if (obj["selectedBuiltinConfigId"].isString() && obj["selectedBuiltinConfigVersion"].isString()) {
        selectedBuiltinConfigId = obj["selectedBuiltinConfigId"].toString();
        selectedBuiltinConfigVersion = obj["selectedBuiltinConfigVersion"].toString();
        selectedSolverConfigFile = "";

    } else if (obj["selectedSolverConfigFile"].isString()) {
        selectedSolverConfigFile = obj["selectedSolverConfigFile"].toString();
        selectedBuiltinConfigId = "";
        selectedBuiltinConfigVersion = "";
    } else {
        warnings << "No selected solver config in project";
    }

    for (auto file : obj["projectFiles"].toArray()) {
        auto path = basePath + file.toString();
        if (QFileInfo(path).exists()) {
            add(path);
        } else {
            warnings << "The file " + file.toString() + " could not be found";
        }
    }
}

void Project::loadLegacy(QDataStream& in, const QFileInfo& fi, ConfigWindow* configWindow, QStringList& warnings)
{
    // Load old binary format of project
    throw InternalError("This project format is no longer supported. Please use MiniZinc IDE version 2.4 to upgrade it.");
}

void Project::saveProject()
{
    QJsonObject confObject;
    confObject["version"] = 106;

    // Save the currently open tabs
    QStringList of;
    for (auto& f: openTabs) {
         of << relativeToProject(f);
    }
    confObject["openFiles"] = QJsonArray::fromStringList(of);
    confObject["openTab"] = openTabIndex;

    // Save paths of all project files
    QStringList relativeFilePaths;
    for (auto& file : files()) {
        relativeFilePaths << relativeToProject(file);
    }
    confObject["projectFiles"] = QJsonArray::fromStringList(relativeFilePaths);

    // Save which config is currently selected
    if (!selectedBuiltinConfigId.isEmpty() && !selectedBuiltinConfigVersion.isEmpty()) {
        confObject["selectedBuiltinConfigId"] = selectedBuiltinConfigId;
        confObject["selectedBuiltinConfigVersion"] = selectedBuiltinConfigVersion;
    } else if (!selectedSolverConfigFile.isEmpty()){
        confObject["selectedSolverConfigFile"] = selectedSolverConfigFile;
    }

    // Save modified built-in configs
    QJsonArray builtinSolverConfigs;
    for (auto sc : solverConfigs) {
        if (!sc->isBuiltin || *sc == SolverConfiguration(sc->solverDefinition, true)) {
            continue;
        }
        builtinSolverConfigs << sc->toJSONObject();
    }
    if (solverConfigs.count()) {
        confObject["builtinSolverConfigs"] = builtinSolverConfigs;
    }

    // Write project file
    QJsonDocument doc(confObject);
    QFile file(projectFile());
    if (!file.open(QFile::WriteOnly)) {
        throw FileError("Failed to write file");
    }
    file.write(doc.toJson());
    file.close();

    setModified(false);
}

void Project::add(const QString& fileName)
{
    auto abs = QFileInfo(fileName).absoluteFilePath();
    auto path = relativeToProject(fileName);
    if (contains(abs)) {
        return;
    }

    auto parts = path.split("/", QString::SkipEmptyParts); // Qt always uses / as the path separator
    auto file = parts.takeLast();

    QStandardItem* node = otherItem;
    QString icon;
    NodeType type = NodeType::Other;
    if (file.endsWith(".mzc.mzn") || file.endsWith(".mzc")) {
        node = checkersItem;
        icon = ":/images/mznicon.png";
        type = NodeType::Checker;
    } else if (file.endsWith(".mzn")) {
        node = modelsItem;
        icon = ":/images/mznicon.png";
        type = NodeType::Model;
    } else if (file.endsWith(".dzn") || file.endsWith(".json")) {
        node = dataItem;
        icon = ":/images/mznicon.png";
        type = NodeType::Data;
    } else if (file.endsWith(".mpc")) {
        node = configsItem;
        icon = ":/images/mznicon.png";
        type = NodeType::SolverConfig;
    } else if (file == "_mooc" || file == "_coursera") {
        if (mooc) {
            delete mooc;
        }
        mooc = new MOOCAssignment(fileName);
        emit moocChanged(mooc);
    }

    node->setFlags(Qt::ItemIsEnabled);

    // Traverse existing path items
    int i = 0;
    while (!parts.empty() && i < node->rowCount()) {
        if (getType(node->child(i)->index()) == NodeType::Dir &&
                node->child(i)->text() == parts.first()) {
            parts.pop_front();
            node = node->child(i);
            i = 0;
        } else {
            i++;
        }
    }
    // Create new path items
    for (auto& part : parts) {
        auto dir = new QStandardItem(QIcon(":/icons/images/folder.png"), part);
        dir->setData(NodeType::Dir, Role::Type);
        dir->setFlags(Qt::ItemIsEnabled);
        node->appendRow(dir);
        node->sortChildren(0);
        node = dir;
    }
    // Add file item
    auto item = new QStandardItem(QIcon(icon), file);
    item->setData(type, Role::Type);
    item->setData(abs, Role::Path);
    item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable);
    node->appendRow(item);
    node->sortChildren(0);

    entries.insert(abs, item);
    setModified(true);
}

void Project::add(const QStringList& fileNames)
{
    for (auto& fileName : fileNames) {
        add(fileName);
    }
}

void Project::remove(const QString &fileName)
{
    auto path = QFileInfo(fileName).absoluteFilePath();
    if (!contains(path)) {
        return;
    }

    // Make sure we also remove any unused dirs
    auto node = entries[path];
    if (node->text() == "_mooc" || node->text() == "_coursera") {
        delete mooc;
        mooc = nullptr;
        emit moocChanged(mooc);
    }
    while (getType(node->parent()->index()) == NodeType::Dir && node->parent()->rowCount() <= 1) {
        node = node->parent();
    }
    auto parent = node->parent();
    parent->removeRow(node->row());
    if (parent->data(Role::Type) == "group" && !parent->hasChildren()) {
        parent->setFlags(Qt::NoItemFlags);
    }
    entries.remove(path);
    setModified(true);
}

void Project::remove(const QStringList &fileNames)
{
    for (auto& fileName : fileNames) {
        remove(fileName);
    }
}

void Project::remove(const QModelIndex& index)
{
    remove(getFileName(index));
}

void Project::remove(const QModelIndexList& indexes)
{
    for (auto& index : indexes) {
        remove(index);
    }
}

void Project::clear()
{
    modelsItem->removeRows(0, modelsItem->rowCount());
    dataItem->removeRows(0, dataItem->rowCount());
    checkersItem->removeRows(0, checkersItem->rowCount());
    otherItem->removeRows(0, otherItem->rowCount());
    entries.clear();
    setModified(true);
}

QStringList Project::files(void) const {
    return entries.keys();
}

QStringList Project::modelFiles(void) const {
    return getFiles(NodeType::Model);
}

QStringList Project::solverConfigurationFiles(void) const {
    return getFiles(NodeType::SolverConfig);
}

QStringList Project::dataFiles(void) const {
    return getFiles(NodeType::Data);
}

bool Project::contains(const QString &fileName)
{
    QFileInfo fi(fileName);
    return entries.contains(fi.absoluteFilePath());
}

void Project::setModified(bool m)
{
    modified = m;
    auto label = rootItem->data(Role::OriginalLabel).toString();
    if (modified) {
        rootItem->setText(label + " *");
    } else {
        rootItem->setText(label);
    }
}

void Project::projectFile(const QString& fileName)
{
    QStringList files = entries.keys();
    clear();
    if (fileName.isEmpty()) {
        rootItem->setText("Untitled Project");
        rootItem->setData("Untitled Project", Role::OriginalLabel);
        projFile = "";
    } else {
        QFileInfo fi(fileName);
        projFile = fi.absoluteFilePath();
        rootItem->setText(fi.fileName());
        rootItem->setData(fi.fileName(), Role::OriginalLabel);
    }
    add(files);
}

QDir Project::rootDir()
{
    QFileInfo fi(projectFile());
    return QDir(fi.absolutePath());
}

bool Project::hasProjectFile()
{
    return !projectFile().isEmpty();
}

Project::NodeType Project::getType(const QModelIndex& index)
{
    return static_cast<Project::NodeType>(model()->data(index, Role::Type).toInt());
}

QString Project::getFileName(const QModelIndex& index)
{
    return model()->data(index, Role::Path).toString();
}

QStringList Project::getFileNames(const QModelIndexList& indices)
{
    QStringList result;
    for (auto& index : indices) {
        result << getFileName(index);
    }
    return result;
}

QStringList Project::getFiles(NodeType type) const
{
    QStringList ret;
    for (auto it = entries.begin(); it != entries.end(); it++) {
        auto t = static_cast<NodeType>(it.value()->data(Role::Type).toInt());
        if (t == type) {
            ret << it.key();
        }
    }
    return ret;
}

QString Project::relativeToProject(const QString& fileName)
{
    QFileInfo fi(fileName);
    auto abs = fi.absoluteFilePath();
    return hasProjectFile() ? rootDir().relativeFilePath(abs) : abs;
}

void Project::openTabsChanged(const QStringList& files, int currentTab)
{
    openTabs.clear();
    for (auto& file : files) {
        auto abs = QFileInfo(file).absoluteFilePath();
        if (contains(abs)) {
            openTabs << abs;
        }
    }

    if (currentTab >= 0) {
        openTabIndex = openTabs.indexOf(QFileInfo(files[currentTab]).absoluteFilePath());
    } else {
        openTabIndex = -1;
    }

//    setModified(true);
}

void Project::activeSolverConfigChanged(const SolverConfiguration* sc)
{
    if (!sc) {
        selectedSolverConfigFile = "";
        selectedBuiltinConfigId = "";
        selectedBuiltinConfigVersion = "";
    }
    if (sc->isBuiltin) {
        selectedSolverConfigFile = "";
        selectedBuiltinConfigId = sc->solverDefinition.id;
        selectedBuiltinConfigVersion = sc->solverDefinition.version;
    } else {
        selectedBuiltinConfigId = "";
        selectedBuiltinConfigVersion = "";
        selectedSolverConfigFile = relativeToProject(sc->paramFile);
    }
//    setModified(true);
}

void Project::on_itemChanged(QStandardItem* item)
{
    auto oldPath = item->data(Qt::UserRole + 1).toString();
    auto newName = item->text();
    if (oldPath.isEmpty() || oldPath.endsWith(newName)) {
        return;
    }
    QFileInfo fi(oldPath);
    auto target = fi.path() + "/" + item->text();
    if (QFile::rename(oldPath, target)) {
        remove(oldPath);
        add(target);
        emit fileRenamed(oldPath, target);
    } else {
        item->setText(fi.fileName());
    }
}
<commit_msg>Don't save modified builtin configs in project<commit_after>/*
 *  Author:
 *     Guido Tack <guido.tack@monash.edu>
 *
 *  Copyright:
 *     NICTA 2013
 */

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "project.h"
#include "moocsubmission.h"
#include "solverdialog.h"
#include "process.h"

#include <QFileInfo>
#include <QDir>
#include <QDebug>
#include <QMessageBox>
#include <QSortFilterProxyModel>
#include <QJsonDocument>
#include <QJsonArray>

Project::Project(const QList<SolverConfiguration*>& configs, QObject* parent) : QObject(parent), solverConfigs(configs)
{
    itemModel = new QStandardItemModel(this);
    itemModel->setColumnCount(1);

    connect(itemModel, &QStandardItemModel::itemChanged, this, &Project::on_itemChanged);

    rootItem = new QStandardItem(QIcon(":/images/mznicon.png"), "Untitled Project");
    rootItem->setData(NodeType::ProjectFile, Role::Type);
    rootItem->setData("Untitled Project", Role::OriginalLabel);
    rootItem->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable);

    auto font = rootItem->font();
    font.setBold(true);

    modelsItem = new QStandardItem("Models");
    modelsItem->setData(NodeType::Group, Role::Type);
    modelsItem->setFlags(Qt::NoItemFlags);
    modelsItem->setFont(font);

    dataItem = new QStandardItem("Data (right click to run)");
    dataItem->setData(NodeType::Group, Role::Type);
    dataItem->setFlags(Qt::NoItemFlags);
    dataItem->setFont(font);

    checkersItem = new QStandardItem("Checkers (right click to run)");
    checkersItem->setData(NodeType::Group, Role::Type);
    checkersItem->setFlags(Qt::NoItemFlags);
    checkersItem->setFont(font);

    configsItem = new QStandardItem("Solver configurations");
    configsItem->setData(NodeType::Group, Role::Type);
    configsItem->setFlags(Qt::NoItemFlags);
    configsItem->setFont(font);

    otherItem = new QStandardItem("Other files");
    otherItem->setData(NodeType::Group, Role::Type);
    otherItem->setFlags(Qt::NoItemFlags);
    otherItem->setFont(font);

    rootItem->appendRow(modelsItem);
    rootItem->appendRow(dataItem);
    rootItem->appendRow(checkersItem);
    rootItem->appendRow(configsItem);
    rootItem->appendRow(otherItem);
    itemModel->appendRow(rootItem);
}

QStringList Project::loadProject(const QString& file, ConfigWindow* configWindow)
{
    clear();

    projectFile(file);

    QStringList warnings;

    QFile f(file);
    QFileInfo fi(f);
    if (!f.open(QFile::ReadOnly)) {
        throw FileError("Failed to open project file");
    }

    auto doc = QJsonDocument::fromJson(f.readAll());

    if (doc.isObject()) {
        loadJSON(doc.object(), fi, configWindow, warnings);
    } else {
        f.reset();
        QDataStream in(&f);
        loadLegacy(in, fi, configWindow, warnings);
    }

    f.close();

    for (auto& sc : solverConfigurationFiles()) {
        configWindow->addConfig(sc);
    }

    if (!selectedBuiltinConfigId.isEmpty() && !selectedBuiltinConfigVersion.isEmpty()) {
        int index = configWindow->findBuiltinConfig(selectedBuiltinConfigId, selectedBuiltinConfigVersion);
        if (index == -1) {
            warnings << "Could not find solver " + selectedBuiltinConfigId + "@" + selectedBuiltinConfigVersion;
        } else {
            configWindow->setCurrentIndex(index);
        }
    } else if (!selectedSolverConfigFile.isEmpty()) {
        int index = configWindow->findConfigFile(rootDir().absolutePath() + "/" + selectedSolverConfigFile);
        configWindow->setCurrentIndex(index);
    }

    setModified(false);

    return warnings;
}

void Project::loadJSON(const QJsonObject& obj, const QFileInfo& fi, ConfigWindow* configWindow, QStringList& warnings)
{
    int version = obj["version"].toInt();

    QString basePath = fi.absolutePath() + "/";

    auto of = obj["openFiles"].toArray();
    for (auto file : of) {
        auto path = basePath + file.toString();
        if (QFileInfo(path).exists()) {
            openTabs << path;
        } else {
            warnings << "The file " + file.toString() + " could not be found";
        }
    }

    openTabIndex = obj["openTab"].toInt();

    QList<SolverConfiguration*> configs;
    if (obj["builtinSolverConfigs"].isArray()) {
        for (auto config : obj["builtinSolverConfigs"].toArray()) {
            if (!config.isObject()) {
                warnings << "Failed to read solver builtin solver config";
                continue;
            }
            SolverConfiguration* loaded;
            if (version >= 106) {
                loaded = new (SolverConfiguration) (SolverConfiguration::loadJSON(QJsonDocument(config.toObject())));
            } else {
                loaded = new (SolverConfiguration) (SolverConfiguration::loadLegacy(QJsonDocument(config.toObject())));
            }
            loaded->isBuiltin = true;
            configs << loaded;
        }
    }

    if (obj["projectSolverConfigs"].isArray()) {
        for (auto config : obj["projectSolverConfigs"].toArray()) {
            if (!config.isObject()) {
                warnings << "Failed to read solver project solver config";
                continue;
            }
            auto loaded = new (SolverConfiguration) (SolverConfiguration::loadLegacy(QJsonDocument(config.toObject())));
            loaded->modified = true;
            configs << loaded;
        }
    }

    configWindow->mergeConfigs(configs);

    if (obj["selectedBuiltinConfigId"].isString() && obj["selectedBuiltinConfigVersion"].isString()) {
        selectedBuiltinConfigId = obj["selectedBuiltinConfigId"].toString();
        selectedBuiltinConfigVersion = obj["selectedBuiltinConfigVersion"].toString();
        selectedSolverConfigFile = "";

    } else if (obj["selectedSolverConfigFile"].isString()) {
        selectedSolverConfigFile = obj["selectedSolverConfigFile"].toString();
        selectedBuiltinConfigId = "";
        selectedBuiltinConfigVersion = "";
    } else {
        warnings << "No selected solver config in project";
    }

    for (auto file : obj["projectFiles"].toArray()) {
        auto path = basePath + file.toString();
        if (QFileInfo(path).exists()) {
            add(path);
        } else {
            warnings << "The file " + file.toString() + " could not be found";
        }
    }
}

void Project::loadLegacy(QDataStream& in, const QFileInfo& fi, ConfigWindow* configWindow, QStringList& warnings)
{
    // Load old binary format of project
    throw InternalError("This project format is no longer supported. Please use MiniZinc IDE version 2.4 to upgrade it.");
}

void Project::saveProject()
{
    QJsonObject confObject;
    confObject["version"] = 106;

    // Save the currently open tabs
    QStringList of;
    for (auto& f: openTabs) {
         of << relativeToProject(f);
    }
    confObject["openFiles"] = QJsonArray::fromStringList(of);
    confObject["openTab"] = openTabIndex;

    // Save paths of all project files
    QStringList relativeFilePaths;
    for (auto& file : files()) {
        relativeFilePaths << relativeToProject(file);
    }
    confObject["projectFiles"] = QJsonArray::fromStringList(relativeFilePaths);

    // Save which config is currently selected
    if (!selectedBuiltinConfigId.isEmpty() && !selectedBuiltinConfigVersion.isEmpty()) {
        confObject["selectedBuiltinConfigId"] = selectedBuiltinConfigId;
        confObject["selectedBuiltinConfigVersion"] = selectedBuiltinConfigVersion;
    } else if (!selectedSolverConfigFile.isEmpty()){
        confObject["selectedSolverConfigFile"] = selectedSolverConfigFile;
    }

    // Write project file
    QJsonDocument doc(confObject);
    QFile file(projectFile());
    if (!file.open(QFile::WriteOnly)) {
        throw FileError("Failed to write file");
    }
    file.write(doc.toJson());
    file.close();

    setModified(false);
}

void Project::add(const QString& fileName)
{
    auto abs = QFileInfo(fileName).absoluteFilePath();
    auto path = relativeToProject(fileName);
    if (contains(abs)) {
        return;
    }

    auto parts = path.split("/", QString::SkipEmptyParts); // Qt always uses / as the path separator
    auto file = parts.takeLast();

    QStandardItem* node = otherItem;
    QString icon;
    NodeType type = NodeType::Other;
    if (file.endsWith(".mzc.mzn") || file.endsWith(".mzc")) {
        node = checkersItem;
        icon = ":/images/mznicon.png";
        type = NodeType::Checker;
    } else if (file.endsWith(".mzn")) {
        node = modelsItem;
        icon = ":/images/mznicon.png";
        type = NodeType::Model;
    } else if (file.endsWith(".dzn") || file.endsWith(".json")) {
        node = dataItem;
        icon = ":/images/mznicon.png";
        type = NodeType::Data;
    } else if (file.endsWith(".mpc")) {
        node = configsItem;
        icon = ":/images/mznicon.png";
        type = NodeType::SolverConfig;
    } else if (file == "_mooc" || file == "_coursera") {
        if (mooc) {
            delete mooc;
        }
        mooc = new MOOCAssignment(fileName);
        emit moocChanged(mooc);
    }

    node->setFlags(Qt::ItemIsEnabled);

    // Traverse existing path items
    int i = 0;
    while (!parts.empty() && i < node->rowCount()) {
        if (getType(node->child(i)->index()) == NodeType::Dir &&
                node->child(i)->text() == parts.first()) {
            parts.pop_front();
            node = node->child(i);
            i = 0;
        } else {
            i++;
        }
    }
    // Create new path items
    for (auto& part : parts) {
        auto dir = new QStandardItem(QIcon(":/icons/images/folder.png"), part);
        dir->setData(NodeType::Dir, Role::Type);
        dir->setFlags(Qt::ItemIsEnabled);
        node->appendRow(dir);
        node->sortChildren(0);
        node = dir;
    }
    // Add file item
    auto item = new QStandardItem(QIcon(icon), file);
    item->setData(type, Role::Type);
    item->setData(abs, Role::Path);
    item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable);
    node->appendRow(item);
    node->sortChildren(0);

    entries.insert(abs, item);
    setModified(true);
}

void Project::add(const QStringList& fileNames)
{
    for (auto& fileName : fileNames) {
        add(fileName);
    }
}

void Project::remove(const QString &fileName)
{
    auto path = QFileInfo(fileName).absoluteFilePath();
    if (!contains(path)) {
        return;
    }

    // Make sure we also remove any unused dirs
    auto node = entries[path];
    if (node->text() == "_mooc" || node->text() == "_coursera") {
        delete mooc;
        mooc = nullptr;
        emit moocChanged(mooc);
    }
    while (getType(node->parent()->index()) == NodeType::Dir && node->parent()->rowCount() <= 1) {
        node = node->parent();
    }
    auto parent = node->parent();
    parent->removeRow(node->row());
    if (parent->data(Role::Type) == "group" && !parent->hasChildren()) {
        parent->setFlags(Qt::NoItemFlags);
    }
    entries.remove(path);
    setModified(true);
}

void Project::remove(const QStringList &fileNames)
{
    for (auto& fileName : fileNames) {
        remove(fileName);
    }
}

void Project::remove(const QModelIndex& index)
{
    remove(getFileName(index));
}

void Project::remove(const QModelIndexList& indexes)
{
    for (auto& index : indexes) {
        remove(index);
    }
}

void Project::clear()
{
    modelsItem->removeRows(0, modelsItem->rowCount());
    dataItem->removeRows(0, dataItem->rowCount());
    checkersItem->removeRows(0, checkersItem->rowCount());
    otherItem->removeRows(0, otherItem->rowCount());
    entries.clear();
    setModified(true);
}

QStringList Project::files(void) const {
    return entries.keys();
}

QStringList Project::modelFiles(void) const {
    return getFiles(NodeType::Model);
}

QStringList Project::solverConfigurationFiles(void) const {
    return getFiles(NodeType::SolverConfig);
}

QStringList Project::dataFiles(void) const {
    return getFiles(NodeType::Data);
}

bool Project::contains(const QString &fileName)
{
    QFileInfo fi(fileName);
    return entries.contains(fi.absoluteFilePath());
}

void Project::setModified(bool m)
{
    modified = m;
    auto label = rootItem->data(Role::OriginalLabel).toString();
    if (modified) {
        rootItem->setText(label + " *");
    } else {
        rootItem->setText(label);
    }
}

void Project::projectFile(const QString& fileName)
{
    QStringList files = entries.keys();
    clear();
    if (fileName.isEmpty()) {
        rootItem->setText("Untitled Project");
        rootItem->setData("Untitled Project", Role::OriginalLabel);
        projFile = "";
    } else {
        QFileInfo fi(fileName);
        projFile = fi.absoluteFilePath();
        rootItem->setText(fi.fileName());
        rootItem->setData(fi.fileName(), Role::OriginalLabel);
    }
    add(files);
}

QDir Project::rootDir()
{
    QFileInfo fi(projectFile());
    return QDir(fi.absolutePath());
}

bool Project::hasProjectFile()
{
    return !projectFile().isEmpty();
}

Project::NodeType Project::getType(const QModelIndex& index)
{
    return static_cast<Project::NodeType>(model()->data(index, Role::Type).toInt());
}

QString Project::getFileName(const QModelIndex& index)
{
    return model()->data(index, Role::Path).toString();
}

QStringList Project::getFileNames(const QModelIndexList& indices)
{
    QStringList result;
    for (auto& index : indices) {
        result << getFileName(index);
    }
    return result;
}

QStringList Project::getFiles(NodeType type) const
{
    QStringList ret;
    for (auto it = entries.begin(); it != entries.end(); it++) {
        auto t = static_cast<NodeType>(it.value()->data(Role::Type).toInt());
        if (t == type) {
            ret << it.key();
        }
    }
    return ret;
}

QString Project::relativeToProject(const QString& fileName)
{
    QFileInfo fi(fileName);
    auto abs = fi.absoluteFilePath();
    return hasProjectFile() ? rootDir().relativeFilePath(abs) : abs;
}

void Project::openTabsChanged(const QStringList& files, int currentTab)
{
    openTabs.clear();
    for (auto& file : files) {
        auto abs = QFileInfo(file).absoluteFilePath();
        if (contains(abs)) {
            openTabs << abs;
        }
    }

    if (currentTab >= 0) {
        openTabIndex = openTabs.indexOf(QFileInfo(files[currentTab]).absoluteFilePath());
    } else {
        openTabIndex = -1;
    }

//    setModified(true);
}

void Project::activeSolverConfigChanged(const SolverConfiguration* sc)
{
    if (!sc) {
        selectedSolverConfigFile = "";
        selectedBuiltinConfigId = "";
        selectedBuiltinConfigVersion = "";
    }
    if (sc->isBuiltin) {
        selectedSolverConfigFile = "";
        selectedBuiltinConfigId = sc->solverDefinition.id;
        selectedBuiltinConfigVersion = sc->solverDefinition.version;
    } else {
        selectedBuiltinConfigId = "";
        selectedBuiltinConfigVersion = "";
        selectedSolverConfigFile = relativeToProject(sc->paramFile);
    }
//    setModified(true);
}

void Project::on_itemChanged(QStandardItem* item)
{
    auto oldPath = item->data(Qt::UserRole + 1).toString();
    auto newName = item->text();
    if (oldPath.isEmpty() || oldPath.endsWith(newName)) {
        return;
    }
    QFileInfo fi(oldPath);
    auto target = fi.path() + "/" + item->text();
    if (QFile::rename(oldPath, target)) {
        remove(oldPath);
        add(target);
        emit fileRenamed(oldPath, target);
    } else {
        item->setText(fi.fileName());
    }
}
<|endoftext|>


<commit_before>//
// This file is part of the Marble Desktop Globe.
//
// This program is free software licensed under the GNU LGPL. You can
// find a copy of this license in LICENSE.txt in the top directory of
// the source code.
//
// Copyright 2011 Niko Sams <niko.sams@gmail.com>
//

#include "ElevationModel.h"
#include "GeoSceneHead.h"
#include "GeoSceneMap.h"
#include "GeoSceneDocument.h"
#include "GeoSceneTexture.h"
#include "TextureTile.h"
#include "TileLoader.h"
#include "TileLoaderHelper.h"
#include "MarbleModel.h"
#include "MarbleDebug.h"
#include "MapThemeManager.h"

#include <QtGui/QLabel>
#include <QtCore/qmath.h>

namespace Marble
{

class ElevationModelPrivate
{
public:
    ElevationModelPrivate( ElevationModel *_q, MarbleModel *const model )
        : q( _q ),
          m_tileLoader( model->downloadManager() ),
          m_textureLayer( 0 )
    {
        m_cache.setMaxCost( 10 ); //keep 10 tiles in memory (~17MB)

        const GeoSceneDocument *srtmTheme = model->mapThemeManager()->loadMapTheme( "earth/srtm2/srtm2.dgml" );
        Q_ASSERT( srtmTheme );

        const GeoSceneHead *head = srtmTheme->head();
        Q_ASSERT( head );

        const GeoSceneMap *map = srtmTheme->map();
        Q_ASSERT( map );

        const GeoSceneLayer *sceneLayer = map->layer( head->theme() );
        Q_ASSERT( sceneLayer );

        m_textureLayer = dynamic_cast<GeoSceneTexture*>( sceneLayer->datasets().first() );
        Q_ASSERT( m_textureLayer );

        QVector<const GeoSceneTexture*> textureLayers;
        textureLayers << m_textureLayer;

        m_tileLoader.setTextureLayers( textureLayers );
    }

    void tileCompleted( const TileId & tileId, const QImage &image )
    {
        m_cache.insert( tileId, new QImage( image ) );
        emit q->updateAvailable();
    }

public:
    ElevationModel *q;

    TileLoader m_tileLoader;
    const GeoSceneTexture *m_textureLayer;
    QCache<TileId, const QImage> m_cache;
};

ElevationModel::ElevationModel( MarbleModel *const model )
    : QObject( 0 ),
      d( new ElevationModelPrivate( this, model ) )
{
    connect( &d->m_tileLoader, SIGNAL( tileCompleted( TileId, QImage ) ),
             this, SLOT( tileCompleted( TileId, QImage ) ) );
}

qreal ElevationModel::height( qreal lon, qreal lat ) const
{
    const int tileZoomLevel = d->m_tileLoader.maximumTileLevel( *( d->m_textureLayer ) );
    Q_ASSERT( tileZoomLevel == 9 );

    const int width = d->m_textureLayer->tileSize().width();
    const int height = d->m_textureLayer->tileSize().height();

    const int numTilesX = TileLoaderHelper::levelToColumn( d->m_textureLayer->levelZeroColumns(), tileZoomLevel );
    const int numTilesY = TileLoaderHelper::levelToRow( d->m_textureLayer->levelZeroRows(), tileZoomLevel );
    Q_ASSERT( numTilesX > 0 );
    Q_ASSERT( numTilesY > 0 );

    qreal textureX = 180 + lon;
    textureX *= numTilesX * width / 360;

    qreal textureY = 90 - lat;
    textureY *= numTilesY * height / 180;

    qreal ret = 0;
    bool hasHeight = false;
    qreal noData = 0;

    for ( int i = 0; i < 4; ++i ) {
        const int x = static_cast<int>( textureX + ( i % 2 ) );
        const int y = static_cast<int>( textureY + ( i / 2 ) );

        //mDebug() << "x" << x << ( x / width );
        //mDebug() << "y" << y << ( y / height );

        const TileId id( "earth/srtm2", tileZoomLevel, ( x % ( numTilesX * width ) ) / width, ( y % ( numTilesY * height ) ) / height );
        //mDebug() << "LAT" << lat << "LON" << lon << "tile" << ( x % ( numTilesX * width ) ) / width << ( y % ( numTilesY * height ) ) / height;

        const QImage *image = d->m_cache[id];
        if ( image == 0 ) {
            image = new QImage( d->m_tileLoader.loadTile( id, DownloadBrowse ) );
            d->m_cache.insert( id, image );
        }
        Q_ASSERT( image );
        Q_ASSERT( !image->isNull() );
        Q_ASSERT( width == image->width() );
        Q_ASSERT( height == image->height() );

        const qreal dx = ( textureX > ( qreal )x ) ? textureX - ( qreal )x : ( qreal )x - textureX;
        const qreal dy = ( textureY > ( qreal )y ) ? textureY - ( qreal )y : ( qreal )y - textureY;

        Q_ASSERT( 0 <= dx && dx <= 1 );
        Q_ASSERT( 0 <= dy && dy <= 1 );
        unsigned int pixel;
        pixel = image->pixel( x % width, y % height );
        pixel -= 0xFF000000; //fully opaque
        //mDebug() << "(1-dx)" << (1-dx) << "(1-dy)" << (1-dy);
        if ( pixel != 32768 ) { //no data?
            //mDebug() << "got at x" << x % width << "y" << y % height << "a height of" << pixel << "** RGB" << qRed(pixel) << qGreen(pixel) << qBlue(pixel);
            ret += ( qreal )pixel * ( 1 - dx ) * ( 1 - dy );
            hasHeight = true;
        } else {
            //mDebug() << "no data at" <<  x % width << "y" << y % height;
            noData += ( 1 - dx ) * ( 1 - dy );
        }
    }

    if ( !hasHeight ) {
        ret = 32768; //no data
    } else {
        if ( noData ) {
            //mDebug() << "NO DATA" << noData;
            ret += ( ret / ( 1 - noData ) ) * noData;
        }
    }

    //mDebug() << ">>>" << lat << lon << "returning an elevation of" << ret;
    return ret;
}

QList<GeoDataCoordinates> ElevationModel::heightProfile( qreal fromLon, qreal fromLat, qreal toLon, qreal toLat ) const
{
    const int tileZoomLevel = d->m_tileLoader.maximumTileLevel( *( d->m_textureLayer ) );
    const int width = d->m_textureLayer->tileSize().width();
    const int numTilesX = TileLoaderHelper::levelToColumn( d->m_textureLayer->levelZeroColumns(), tileZoomLevel );

    qreal distPerPixel = ( qreal )360 / ( width * numTilesX );
    //mDebug() << "heightProfile" << fromLat << fromLon << toLat << toLon << "distPerPixel" << distPerPixel;

    qreal lat = fromLat;
    qreal lon = fromLon;
    char dirLat = fromLat < toLat ? 1 : -1;
    char dirLon = fromLon < toLon ? 1 : -1;
    qreal k = qAbs( ( fromLat - toLat ) / ( fromLon - toLon ) );
    //mDebug() << "fromLon" << fromLon << "fromLat" << fromLat;
    //mDebug() << "diff lon" << ( fromLon - toLon ) << "diff lat" << ( fromLat - toLat );
    //mDebug() << "dirLon" << QString::number(dirLon) << "dirLat" << QString::number(dirLat) << "k" << k;
    QList<GeoDataCoordinates> ret;
    while ( lat*dirLat <= toLat*dirLat && lon*dirLon <= toLon * dirLon ) {
        //mDebug() << lat << lon;
        qreal h = height( lon, lat );
        if ( h < 32000 ) {
            ret << GeoDataCoordinates( lon, lat, h, GeoDataCoordinates::Degree );
        }
        if ( k < 0.5 ) {
            //mDebug() << "lon(x) += distPerPixel";
            lat += distPerPixel * k * dirLat;
            lon += distPerPixel * dirLon;
        } else {
            //mDebug() << "lat(y) += distPerPixel";
            lat += distPerPixel * dirLat;
            lon += distPerPixel / k * dirLon;
        }
    }
    //mDebug() << ret;
    return ret;
}

}

#include "ElevationModel.moc"
<commit_msg>Don't crash when the srtm2 map theme is missing.<commit_after>//
// This file is part of the Marble Desktop Globe.
//
// This program is free software licensed under the GNU LGPL. You can
// find a copy of this license in LICENSE.txt in the top directory of
// the source code.
//
// Copyright 2011 Niko Sams <niko.sams@gmail.com>
//

#include "ElevationModel.h"
#include "GeoSceneHead.h"
#include "GeoSceneMap.h"
#include "GeoSceneDocument.h"
#include "GeoSceneTexture.h"
#include "TextureTile.h"
#include "TileLoader.h"
#include "TileLoaderHelper.h"
#include "MarbleModel.h"
#include "MarbleDebug.h"
#include "MapThemeManager.h"

#include <QtGui/QLabel>
#include <QtCore/qmath.h>

namespace Marble
{

namespace {
    unsigned int const invalidElevationData = 32768;
}

class ElevationModelPrivate
{
public:
    ElevationModelPrivate( ElevationModel *_q, MarbleModel *const model )
        : q( _q ),
          m_tileLoader( model->downloadManager() ),
          m_textureLayer( 0 )
    {
        m_cache.setMaxCost( 10 ); //keep 10 tiles in memory (~17MB)

        const GeoSceneDocument *srtmTheme = model->mapThemeManager()->loadMapTheme( "earth/srtm2/srtm2.dgml" );
        if ( !srtmTheme ) {
            mDebug() << "Failed to load map theme earth/srtm2/srtm2.dgml. Check your installation. No elevation will be returned.";
            return;
        }

        const GeoSceneHead *head = srtmTheme->head();
        Q_ASSERT( head );

        const GeoSceneMap *map = srtmTheme->map();
        Q_ASSERT( map );

        const GeoSceneLayer *sceneLayer = map->layer( head->theme() );
        Q_ASSERT( sceneLayer );

        m_textureLayer = dynamic_cast<GeoSceneTexture*>( sceneLayer->datasets().first() );
        Q_ASSERT( m_textureLayer );

        QVector<const GeoSceneTexture*> textureLayers;
        textureLayers << m_textureLayer;

        m_tileLoader.setTextureLayers( textureLayers );
    }

    void tileCompleted( const TileId & tileId, const QImage &image )
    {
        m_cache.insert( tileId, new QImage( image ) );
        emit q->updateAvailable();
    }

public:
    ElevationModel *q;

    TileLoader m_tileLoader;
    const GeoSceneTexture *m_textureLayer;
    QCache<TileId, const QImage> m_cache;
};

ElevationModel::ElevationModel( MarbleModel *const model )
    : QObject( 0 ),
      d( new ElevationModelPrivate( this, model ) )
{
    connect( &d->m_tileLoader, SIGNAL( tileCompleted( TileId, QImage ) ),
             this, SLOT( tileCompleted( TileId, QImage ) ) );
}

qreal ElevationModel::height( qreal lon, qreal lat ) const
{
    if ( !d->m_textureLayer ) {
        return invalidElevationData;
    }

    const int tileZoomLevel = d->m_tileLoader.maximumTileLevel( *( d->m_textureLayer ) );
    Q_ASSERT( tileZoomLevel == 9 );

    const int width = d->m_textureLayer->tileSize().width();
    const int height = d->m_textureLayer->tileSize().height();

    const int numTilesX = TileLoaderHelper::levelToColumn( d->m_textureLayer->levelZeroColumns(), tileZoomLevel );
    const int numTilesY = TileLoaderHelper::levelToRow( d->m_textureLayer->levelZeroRows(), tileZoomLevel );
    Q_ASSERT( numTilesX > 0 );
    Q_ASSERT( numTilesY > 0 );

    qreal textureX = 180 + lon;
    textureX *= numTilesX * width / 360;

    qreal textureY = 90 - lat;
    textureY *= numTilesY * height / 180;

    qreal ret = 0;
    bool hasHeight = false;
    qreal noData = 0;

    for ( int i = 0; i < 4; ++i ) {
        const int x = static_cast<int>( textureX + ( i % 2 ) );
        const int y = static_cast<int>( textureY + ( i / 2 ) );

        //mDebug() << "x" << x << ( x / width );
        //mDebug() << "y" << y << ( y / height );

        const TileId id( "earth/srtm2", tileZoomLevel, ( x % ( numTilesX * width ) ) / width, ( y % ( numTilesY * height ) ) / height );
        //mDebug() << "LAT" << lat << "LON" << lon << "tile" << ( x % ( numTilesX * width ) ) / width << ( y % ( numTilesY * height ) ) / height;

        const QImage *image = d->m_cache[id];
        if ( image == 0 ) {
            image = new QImage( d->m_tileLoader.loadTile( id, DownloadBrowse ) );
            d->m_cache.insert( id, image );
        }
        Q_ASSERT( image );
        Q_ASSERT( !image->isNull() );
        Q_ASSERT( width == image->width() );
        Q_ASSERT( height == image->height() );

        const qreal dx = ( textureX > ( qreal )x ) ? textureX - ( qreal )x : ( qreal )x - textureX;
        const qreal dy = ( textureY > ( qreal )y ) ? textureY - ( qreal )y : ( qreal )y - textureY;

        Q_ASSERT( 0 <= dx && dx <= 1 );
        Q_ASSERT( 0 <= dy && dy <= 1 );
        unsigned int pixel;
        pixel = image->pixel( x % width, y % height );
        pixel -= 0xFF000000; //fully opaque
        //mDebug() << "(1-dx)" << (1-dx) << "(1-dy)" << (1-dy);
        if ( pixel != invalidElevationData ) { //no data?
            //mDebug() << "got at x" << x % width << "y" << y % height << "a height of" << pixel << "** RGB" << qRed(pixel) << qGreen(pixel) << qBlue(pixel);
            ret += ( qreal )pixel * ( 1 - dx ) * ( 1 - dy );
            hasHeight = true;
        } else {
            //mDebug() << "no data at" <<  x % width << "y" << y % height;
            noData += ( 1 - dx ) * ( 1 - dy );
        }
    }

    if ( !hasHeight ) {
        ret = invalidElevationData; //no data
    } else {
        if ( noData ) {
            //mDebug() << "NO DATA" << noData;
            ret += ( ret / ( 1 - noData ) ) * noData;
        }
    }

    //mDebug() << ">>>" << lat << lon << "returning an elevation of" << ret;
    return ret;
}

QList<GeoDataCoordinates> ElevationModel::heightProfile( qreal fromLon, qreal fromLat, qreal toLon, qreal toLat ) const
{
    if ( !d->m_textureLayer ) {
        return QList<GeoDataCoordinates>();
    }

    const int tileZoomLevel = d->m_tileLoader.maximumTileLevel( *( d->m_textureLayer ) );
    const int width = d->m_textureLayer->tileSize().width();
    const int numTilesX = TileLoaderHelper::levelToColumn( d->m_textureLayer->levelZeroColumns(), tileZoomLevel );

    qreal distPerPixel = ( qreal )360 / ( width * numTilesX );
    //mDebug() << "heightProfile" << fromLat << fromLon << toLat << toLon << "distPerPixel" << distPerPixel;

    qreal lat = fromLat;
    qreal lon = fromLon;
    char dirLat = fromLat < toLat ? 1 : -1;
    char dirLon = fromLon < toLon ? 1 : -1;
    qreal k = qAbs( ( fromLat - toLat ) / ( fromLon - toLon ) );
    //mDebug() << "fromLon" << fromLon << "fromLat" << fromLat;
    //mDebug() << "diff lon" << ( fromLon - toLon ) << "diff lat" << ( fromLat - toLat );
    //mDebug() << "dirLon" << QString::number(dirLon) << "dirLat" << QString::number(dirLat) << "k" << k;
    QList<GeoDataCoordinates> ret;
    while ( lat*dirLat <= toLat*dirLat && lon*dirLon <= toLon * dirLon ) {
        //mDebug() << lat << lon;
        qreal h = height( lon, lat );
        if ( h < 32000 ) {
            ret << GeoDataCoordinates( lon, lat, h, GeoDataCoordinates::Degree );
        }
        if ( k < 0.5 ) {
            //mDebug() << "lon(x) += distPerPixel";
            lat += distPerPixel * k * dirLat;
            lon += distPerPixel * dirLon;
        } else {
            //mDebug() << "lat(y) += distPerPixel";
            lat += distPerPixel * dirLat;
            lon += distPerPixel / k * dirLon;
        }
    }
    //mDebug() << ret;
    return ret;
}

}

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


<commit_before>// Copyright 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/stl_util.h"

#include <set>

#include "testing/gtest/include/gtest/gtest.h"

namespace {

// Used as test case to ensure the various base::STLXxx functions don't require
// more than operators "<" and "==" on values stored in containers.
class ComparableValue {
 public:
  explicit ComparableValue(int value) : value_(value) {}

  bool operator==(const ComparableValue& rhs) const {
    return value_ == rhs.value_;
  }

  bool operator<(const ComparableValue& rhs) const {
    return value_ < rhs.value_;
  }

 private:
  int value_;
};

}

namespace base {
namespace {

TEST(STLUtilTest, STLIsSorted) {
  {
    std::set<int> set;
    set.insert(24);
    set.insert(1);
    set.insert(12);
    EXPECT_TRUE(STLIsSorted(set));
  }

  {
    std::set<ComparableValue> set;
    set.insert(ComparableValue(24));
    set.insert(ComparableValue(1));
    set.insert(ComparableValue(12));
    EXPECT_TRUE(STLIsSorted(set));
  }

  {
    std::vector<int> vector;
    vector.push_back(1);
    vector.push_back(1);
    vector.push_back(4);
    vector.push_back(64);
    vector.push_back(12432);
    EXPECT_TRUE(STLIsSorted(vector));
    vector.back() = 1;
    EXPECT_FALSE(STLIsSorted(vector));
  }
}

TEST(STLUtilTest, STLSetDifference) {
  std::set<int> a1;
  a1.insert(1);
  a1.insert(2);
  a1.insert(3);
  a1.insert(4);

  std::set<int> a2;
  a2.insert(3);
  a2.insert(4);
  a2.insert(5);
  a2.insert(6);
  a2.insert(7);

  {
    std::set<int> difference;
    difference.insert(1);
    difference.insert(2);
    EXPECT_EQ(difference, STLSetDifference<std::set<int> >(a1, a2));
  }

  {
    std::set<int> difference;
    difference.insert(5);
    difference.insert(6);
    difference.insert(7);
    EXPECT_EQ(difference, STLSetDifference<std::set<int> >(a2, a1));
  }

  {
    std::vector<int> difference;
    difference.push_back(1);
    difference.push_back(2);
    EXPECT_EQ(difference, STLSetDifference<std::vector<int> >(a1, a2));
  }

  {
    std::vector<int> difference;
    difference.push_back(5);
    difference.push_back(6);
    difference.push_back(7);
    EXPECT_EQ(difference, STLSetDifference<std::vector<int> >(a2, a1));
  }
}

TEST(STLUtilTest, STLSetUnion) {
  std::set<int> a1;
  a1.insert(1);
  a1.insert(2);
  a1.insert(3);
  a1.insert(4);

  std::set<int> a2;
  a2.insert(3);
  a2.insert(4);
  a2.insert(5);
  a2.insert(6);
  a2.insert(7);

  {
    std::set<int> result;
    result.insert(1);
    result.insert(2);
    result.insert(3);
    result.insert(4);
    result.insert(5);
    result.insert(6);
    result.insert(7);
    EXPECT_EQ(result, STLSetUnion<std::set<int> >(a1, a2));
  }

  {
    std::set<int> result;
    result.insert(1);
    result.insert(2);
    result.insert(3);
    result.insert(4);
    result.insert(5);
    result.insert(6);
    result.insert(7);
    EXPECT_EQ(result, STLSetUnion<std::set<int> >(a2, a1));
  }

  {
    std::vector<int> result;
    result.push_back(1);
    result.push_back(2);
    result.push_back(3);
    result.push_back(4);
    result.push_back(5);
    result.push_back(6);
    result.push_back(7);
    EXPECT_EQ(result, STLSetUnion<std::vector<int> >(a1, a2));
  }

  {
    std::vector<int> result;
    result.push_back(1);
    result.push_back(2);
    result.push_back(3);
    result.push_back(4);
    result.push_back(5);
    result.push_back(6);
    result.push_back(7);
    EXPECT_EQ(result, STLSetUnion<std::vector<int> >(a2, a1));
  }
}

TEST(STLUtilTest, STLSetIntersection) {
  std::set<int> a1;
  a1.insert(1);
  a1.insert(2);
  a1.insert(3);
  a1.insert(4);

  std::set<int> a2;
  a2.insert(3);
  a2.insert(4);
  a2.insert(5);
  a2.insert(6);
  a2.insert(7);

  {
    std::set<int> result;
    result.insert(3);
    result.insert(4);
    EXPECT_EQ(result, STLSetIntersection<std::set<int> >(a1, a2));
  }

  {
    std::set<int> result;
    result.insert(3);
    result.insert(4);
    EXPECT_EQ(result, STLSetIntersection<std::set<int> >(a2, a1));
  }

  {
    std::vector<int> result;
    result.push_back(3);
    result.push_back(4);
    EXPECT_EQ(result, STLSetIntersection<std::vector<int> >(a1, a2));
  }

  {
    std::vector<int> result;
    result.push_back(3);
    result.push_back(4);
    EXPECT_EQ(result, STLSetIntersection<std::vector<int> >(a2, a1));
  }
}

TEST(STLUtilTest, STLIncludes) {
  std::set<int> a1;
  a1.insert(1);
  a1.insert(2);
  a1.insert(3);
  a1.insert(4);

  std::set<int> a2;
  a2.insert(3);
  a2.insert(4);

  std::set<int> a3;
  a3.insert(3);
  a3.insert(4);
  a3.insert(5);

  EXPECT_TRUE(STLIncludes<std::set<int> >(a1, a2));
  EXPECT_FALSE(STLIncludes<std::set<int> >(a1, a3));
  EXPECT_FALSE(STLIncludes<std::set<int> >(a2, a1));
  EXPECT_FALSE(STLIncludes<std::set<int> >(a2, a3));
  EXPECT_FALSE(STLIncludes<std::set<int> >(a3, a1));
  EXPECT_TRUE(STLIncludes<std::set<int> >(a3, a2));
}

}  // namespace
}  // namespace base
<commit_msg>Add unit tests for string_as_array().<commit_after>// Copyright 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/stl_util.h"

#include <set>

#include "testing/gtest/include/gtest/gtest.h"

namespace {

// Used as test case to ensure the various base::STLXxx functions don't require
// more than operators "<" and "==" on values stored in containers.
class ComparableValue {
 public:
  explicit ComparableValue(int value) : value_(value) {}

  bool operator==(const ComparableValue& rhs) const {
    return value_ == rhs.value_;
  }

  bool operator<(const ComparableValue& rhs) const {
    return value_ < rhs.value_;
  }

 private:
  int value_;
};

}

namespace base {
namespace {

TEST(STLUtilTest, STLIsSorted) {
  {
    std::set<int> set;
    set.insert(24);
    set.insert(1);
    set.insert(12);
    EXPECT_TRUE(STLIsSorted(set));
  }

  {
    std::set<ComparableValue> set;
    set.insert(ComparableValue(24));
    set.insert(ComparableValue(1));
    set.insert(ComparableValue(12));
    EXPECT_TRUE(STLIsSorted(set));
  }

  {
    std::vector<int> vector;
    vector.push_back(1);
    vector.push_back(1);
    vector.push_back(4);
    vector.push_back(64);
    vector.push_back(12432);
    EXPECT_TRUE(STLIsSorted(vector));
    vector.back() = 1;
    EXPECT_FALSE(STLIsSorted(vector));
  }
}

TEST(STLUtilTest, STLSetDifference) {
  std::set<int> a1;
  a1.insert(1);
  a1.insert(2);
  a1.insert(3);
  a1.insert(4);

  std::set<int> a2;
  a2.insert(3);
  a2.insert(4);
  a2.insert(5);
  a2.insert(6);
  a2.insert(7);

  {
    std::set<int> difference;
    difference.insert(1);
    difference.insert(2);
    EXPECT_EQ(difference, STLSetDifference<std::set<int> >(a1, a2));
  }

  {
    std::set<int> difference;
    difference.insert(5);
    difference.insert(6);
    difference.insert(7);
    EXPECT_EQ(difference, STLSetDifference<std::set<int> >(a2, a1));
  }

  {
    std::vector<int> difference;
    difference.push_back(1);
    difference.push_back(2);
    EXPECT_EQ(difference, STLSetDifference<std::vector<int> >(a1, a2));
  }

  {
    std::vector<int> difference;
    difference.push_back(5);
    difference.push_back(6);
    difference.push_back(7);
    EXPECT_EQ(difference, STLSetDifference<std::vector<int> >(a2, a1));
  }
}

TEST(STLUtilTest, STLSetUnion) {
  std::set<int> a1;
  a1.insert(1);
  a1.insert(2);
  a1.insert(3);
  a1.insert(4);

  std::set<int> a2;
  a2.insert(3);
  a2.insert(4);
  a2.insert(5);
  a2.insert(6);
  a2.insert(7);

  {
    std::set<int> result;
    result.insert(1);
    result.insert(2);
    result.insert(3);
    result.insert(4);
    result.insert(5);
    result.insert(6);
    result.insert(7);
    EXPECT_EQ(result, STLSetUnion<std::set<int> >(a1, a2));
  }

  {
    std::set<int> result;
    result.insert(1);
    result.insert(2);
    result.insert(3);
    result.insert(4);
    result.insert(5);
    result.insert(6);
    result.insert(7);
    EXPECT_EQ(result, STLSetUnion<std::set<int> >(a2, a1));
  }

  {
    std::vector<int> result;
    result.push_back(1);
    result.push_back(2);
    result.push_back(3);
    result.push_back(4);
    result.push_back(5);
    result.push_back(6);
    result.push_back(7);
    EXPECT_EQ(result, STLSetUnion<std::vector<int> >(a1, a2));
  }

  {
    std::vector<int> result;
    result.push_back(1);
    result.push_back(2);
    result.push_back(3);
    result.push_back(4);
    result.push_back(5);
    result.push_back(6);
    result.push_back(7);
    EXPECT_EQ(result, STLSetUnion<std::vector<int> >(a2, a1));
  }
}

TEST(STLUtilTest, STLSetIntersection) {
  std::set<int> a1;
  a1.insert(1);
  a1.insert(2);
  a1.insert(3);
  a1.insert(4);

  std::set<int> a2;
  a2.insert(3);
  a2.insert(4);
  a2.insert(5);
  a2.insert(6);
  a2.insert(7);

  {
    std::set<int> result;
    result.insert(3);
    result.insert(4);
    EXPECT_EQ(result, STLSetIntersection<std::set<int> >(a1, a2));
  }

  {
    std::set<int> result;
    result.insert(3);
    result.insert(4);
    EXPECT_EQ(result, STLSetIntersection<std::set<int> >(a2, a1));
  }

  {
    std::vector<int> result;
    result.push_back(3);
    result.push_back(4);
    EXPECT_EQ(result, STLSetIntersection<std::vector<int> >(a1, a2));
  }

  {
    std::vector<int> result;
    result.push_back(3);
    result.push_back(4);
    EXPECT_EQ(result, STLSetIntersection<std::vector<int> >(a2, a1));
  }
}

TEST(STLUtilTest, STLIncludes) {
  std::set<int> a1;
  a1.insert(1);
  a1.insert(2);
  a1.insert(3);
  a1.insert(4);

  std::set<int> a2;
  a2.insert(3);
  a2.insert(4);

  std::set<int> a3;
  a3.insert(3);
  a3.insert(4);
  a3.insert(5);

  EXPECT_TRUE(STLIncludes<std::set<int> >(a1, a2));
  EXPECT_FALSE(STLIncludes<std::set<int> >(a1, a3));
  EXPECT_FALSE(STLIncludes<std::set<int> >(a2, a1));
  EXPECT_FALSE(STLIncludes<std::set<int> >(a2, a3));
  EXPECT_FALSE(STLIncludes<std::set<int> >(a3, a1));
  EXPECT_TRUE(STLIncludes<std::set<int> >(a3, a2));
}

TEST(StringAsArrayTest, Empty) {
  std::string empty;
  EXPECT_EQ(nullptr, string_as_array(&empty));
}

TEST(StringAsArrayTest, NullTerminated) {
  // If any std::string implementation is not null-terminated, this should
  // fail. All compilers we use return a null-terminated buffer, but please do
  // not rely on this fact in your code.
  std::string str("abcde");
  str.resize(3);
  EXPECT_STREQ("abc", string_as_array(&str));
}

TEST(StringAsArrayTest, WriteCopy) {
  // With a COW implementation, this test will fail if
  // string_as_array(&str) is implemented as
  // const_cast<char*>(str->data()).
  std::string s1("abc");
  const std::string s2(s1);
  string_as_array(&s1)[1] = 'x';
  EXPECT_EQ("axc", s1);
  EXPECT_EQ("abc", s2);
}

}  // namespace
}  // namespace base
<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  $RCSfile: morphdlg.cxx,v $
 *
 *  $Revision: 1.6 $
 *
 *  last change: $Author: vg $ $Date: 2004-01-06 18:44:12 $
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 by Sun Microsystems, Inc.
 *  901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License version 2.1, as published by the Free Software Foundation.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *  MA  02111-1307  USA
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (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.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 *
 *  Copyright: 2000 by Sun Microsystems, Inc.
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): _______________________________________
 *
 *
 ************************************************************************/

#include "strings.hrc"
#include "sdresid.hxx"
#include "sdmod.hxx"
#include "sdiocmpt.hxx"
#include "morphdlg.hxx"
#include "morphdlg.hrc"

#ifndef _CONFIG_HXX
#include <tools/config.hxx>
#endif
#ifndef SVX_XFILLIT0_HXX //autogen
#include <svx/xfillit0.hxx>
#endif
#ifndef _SVX_XLINEIT0_HXX //autogen
#include <svx/xlineit0.hxx>
#endif
#ifndef _XENUM_HXX //autogen
#include <svx/xenum.hxx>
#endif
#ifndef _SVDOBJ_HXX //autogen
#include <svx/svdobj.hxx>
#endif
#ifndef _SFXITEMSET_HXX //autogen
#include <svtools/itemset.hxx>
#endif
#ifndef _SFXITEMPOOL_HXX //autogen
#include <svtools/itempool.hxx>
#endif

/******************************************************************************/

#define FADE_STEP   "FadeSteps"
#define FADE_ATTRIB "FadeAttributes"
#define FADE_ORIENT "FadeOrientation"
#define FADE_TRUE   "true"
#define FADE_FALSE  "false"

/******************************************************************************/

/******************************************************************************
|*
|*
|*
\******************************************************************************/

SdMorphDlg::SdMorphDlg( Window* pParent, const SdrObject* pObj1, const SdrObject* pObj2 ) :
            ModalDialog     ( pParent, SdResId( DLG_MORPH ) ),
            aBtnOK          ( this, SdResId( BTN_OK ) ),
            aBtnCancel      ( this, SdResId( BTN_CANCEL ) ),
            aBtnHelp        ( this, SdResId( BTN_HELP ) ),
            aGrpPreset      ( this, SdResId( GRP_PRESET ) ),
            aFtSteps        ( this, SdResId( FT_STEPS ) ),
            aMtfSteps       ( this, SdResId( MTF_STEPS ) ),
            aCbxAttributes  ( this, SdResId( CBX_ATTRIBUTES ) ),
            aCbxOrientation ( this, SdResId( CBX_ORIENTATION ) )
{
    FreeResource();
    LoadSettings();

    SfxItemPool*    pPool = (SfxItemPool*) pObj1->GetItemPool();
    SfxItemSet      aSet1( *pPool );
    SfxItemSet      aSet2( *pPool );

    aSet1.Put(pObj1->GetMergedItemSet());
    aSet2.Put(pObj2->GetMergedItemSet());

    const XLineStyle    eLineStyle1 = ( (const XLineStyleItem&) aSet1.Get( XATTR_LINESTYLE ) ).GetValue();
    const XLineStyle    eLineStyle2 = ( (const XLineStyleItem&) aSet2.Get( XATTR_LINESTYLE ) ).GetValue();
    const XFillStyle    eFillStyle1 = ( (const XFillStyleItem&) aSet1.Get( XATTR_FILLSTYLE ) ).GetValue();
    const XFillStyle    eFillStyle2 = ( (const XFillStyleItem&) aSet2.Get( XATTR_FILLSTYLE ) ).GetValue();

    if ( ( ( eLineStyle1 == XLINE_NONE ) || ( eLineStyle2 == XLINE_NONE ) ) &&
         ( ( eFillStyle1 != XFILL_SOLID ) || ( eFillStyle2 != XFILL_SOLID ) ) )
    {
        aCbxAttributes.Disable();
    }
}

/******************************************************************************
|*
|*
|*
\******************************************************************************/

SdMorphDlg::~SdMorphDlg()
{
}

/******************************************************************************
|*
|*
|*
\******************************************************************************/

void SdMorphDlg::LoadSettings()
{
    SvStorageStreamRef  xIStm( SD_MOD()->GetOptionStream( UniString::CreateFromAscii(
                               RTL_CONSTASCII_STRINGPARAM( SD_OPTION_MORPHING ) ),
                               SD_OPTION_LOAD ) );
    UINT16              nSteps;
    BOOL                bOrient, bAttrib;

    if( xIStm.Is() )
    {
        SdIOCompat aCompat( *xIStm, STREAM_READ );

        *xIStm >> nSteps >> bOrient >> bAttrib;
    }
    else
    {
        nSteps = 16;
        bOrient = bAttrib = TRUE;
    }

    aMtfSteps.SetValue( nSteps );
    aCbxOrientation.Check( bOrient );
    aCbxAttributes.Check( bAttrib );
}

// -----------------------------------------------------------------------------

void SdMorphDlg::SaveSettings() const
{
    SvStorageStreamRef xOStm( SD_MOD()->GetOptionStream( UniString::CreateFromAscii(
                               RTL_CONSTASCII_STRINGPARAM( SD_OPTION_MORPHING ) ),
                               SD_OPTION_STORE ) );

    if( xOStm.Is() )
    {
        SdIOCompat aCompat( *xOStm, STREAM_WRITE, 1 );

        *xOStm << (UINT16) aMtfSteps.GetValue()
               << aCbxOrientation.IsChecked()
               << aCbxAttributes.IsChecked();
    }
}

<commit_msg>INTEGRATION: CWS impress1 (1.4.218); FILE MERGED 2003/11/27 15:08:36 af 1.4.218.3: RESYNC: (1.4-1.5); FILE MERGED 2003/10/01 11:52:34 af 1.4.218.2: #111996# Renamed some include files. 2003/09/17 09:24:35 af 1.4.218.1: #111996# Transition to stacked sub-shells. Introduction of namespace sd.<commit_after>/*************************************************************************
 *
 *  $RCSfile: morphdlg.cxx,v $
 *
 *  $Revision: 1.7 $
 *
 *  last change: $Author: obo $ $Date: 2004-01-20 10:46:33 $
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 by Sun Microsystems, Inc.
 *  901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License version 2.1, as published by the Free Software Foundation.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *  MA  02111-1307  USA
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (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.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 *
 *  Copyright: 2000 by Sun Microsystems, Inc.
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): _______________________________________
 *
 *
 ************************************************************************/

#include "morphdlg.hxx"

#include "strings.hrc"
#include "sdresid.hxx"
#include "sdmod.hxx"
#include "sdiocmpt.hxx"
#include "morphdlg.hrc"

#ifndef _CONFIG_HXX
#include <tools/config.hxx>
#endif
#ifndef SVX_XFILLIT0_HXX //autogen
#include <svx/xfillit0.hxx>
#endif
#ifndef _SVX_XLINEIT0_HXX //autogen
#include <svx/xlineit0.hxx>
#endif
#ifndef _XENUM_HXX //autogen
#include <svx/xenum.hxx>
#endif
#ifndef _SVDOBJ_HXX //autogen
#include <svx/svdobj.hxx>
#endif
#ifndef _SFXITEMSET_HXX //autogen
#include <svtools/itemset.hxx>
#endif
#ifndef _SFXITEMPOOL_HXX //autogen
#include <svtools/itempool.hxx>
#endif

namespace sd {

/******************************************************************************/

#define FADE_STEP   "FadeSteps"
#define FADE_ATTRIB "FadeAttributes"
#define FADE_ORIENT "FadeOrientation"
#define FADE_TRUE   "true"
#define FADE_FALSE  "false"

/******************************************************************************/

/******************************************************************************
|*
|*
|*
\******************************************************************************/

MorphDlg::MorphDlg( ::Window* pParent, const SdrObject* pObj1, const SdrObject* pObj2 ) :
            ModalDialog     ( pParent, SdResId( DLG_MORPH ) ),
            aBtnOK          ( this, SdResId( BTN_OK ) ),
            aBtnCancel      ( this, SdResId( BTN_CANCEL ) ),
            aBtnHelp        ( this, SdResId( BTN_HELP ) ),
            aGrpPreset      ( this, SdResId( GRP_PRESET ) ),
            aFtSteps        ( this, SdResId( FT_STEPS ) ),
            aMtfSteps       ( this, SdResId( MTF_STEPS ) ),
            aCbxAttributes  ( this, SdResId( CBX_ATTRIBUTES ) ),
            aCbxOrientation ( this, SdResId( CBX_ORIENTATION ) )
{
    FreeResource();
    LoadSettings();

    SfxItemPool*    pPool = (SfxItemPool*) pObj1->GetItemPool();
    SfxItemSet      aSet1( *pPool );
    SfxItemSet      aSet2( *pPool );

    aSet1.Put(pObj1->GetMergedItemSet());
    aSet2.Put(pObj2->GetMergedItemSet());

    const XLineStyle    eLineStyle1 = ( (const XLineStyleItem&) aSet1.Get( XATTR_LINESTYLE ) ).GetValue();
    const XLineStyle    eLineStyle2 = ( (const XLineStyleItem&) aSet2.Get( XATTR_LINESTYLE ) ).GetValue();
    const XFillStyle    eFillStyle1 = ( (const XFillStyleItem&) aSet1.Get( XATTR_FILLSTYLE ) ).GetValue();
    const XFillStyle    eFillStyle2 = ( (const XFillStyleItem&) aSet2.Get( XATTR_FILLSTYLE ) ).GetValue();

    if ( ( ( eLineStyle1 == XLINE_NONE ) || ( eLineStyle2 == XLINE_NONE ) ) &&
         ( ( eFillStyle1 != XFILL_SOLID ) || ( eFillStyle2 != XFILL_SOLID ) ) )
    {
        aCbxAttributes.Disable();
    }
}

/******************************************************************************
|*
|*
|*
\******************************************************************************/

MorphDlg::~MorphDlg()
{
}

/******************************************************************************
|*
|*
|*
\******************************************************************************/

void MorphDlg::LoadSettings()
{
    SvStorageStreamRef  xIStm( SD_MOD()->GetOptionStream( UniString::CreateFromAscii(
                               RTL_CONSTASCII_STRINGPARAM( SD_OPTION_MORPHING ) ),
                               SD_OPTION_LOAD ) );
    UINT16              nSteps;
    BOOL                bOrient, bAttrib;

    if( xIStm.Is() )
    {
        SdIOCompat aCompat( *xIStm, STREAM_READ );

        *xIStm >> nSteps >> bOrient >> bAttrib;
    }
    else
    {
        nSteps = 16;
        bOrient = bAttrib = TRUE;
    }

    aMtfSteps.SetValue( nSteps );
    aCbxOrientation.Check( bOrient );
    aCbxAttributes.Check( bAttrib );
}

// -----------------------------------------------------------------------------

void MorphDlg::SaveSettings() const
{
    SvStorageStreamRef xOStm( SD_MOD()->GetOptionStream( UniString::CreateFromAscii(
                               RTL_CONSTASCII_STRINGPARAM( SD_OPTION_MORPHING ) ),
                               SD_OPTION_STORE ) );

    if( xOStm.Is() )
    {
        SdIOCompat aCompat( *xOStm, STREAM_WRITE, 1 );

        *xOStm << (UINT16) aMtfSteps.GetValue()
               << aCbxOrientation.IsChecked()
               << aCbxAttributes.IsChecked();
    }
}

} // end of namespace sd
<|endoftext|>


<commit_before>#ifndef _SDD_VALUES_UNIQUE_FLAT_SET_HH_
#define _SDD_VALUES_UNIQUE_FLAT_SET_HH_

#include <algorithm>  // copy, set_difference, set_intersection, set_union
#include <functional> // hash
#include <initializer_list>
#include <iosfwd>
#include <iterator>   // inserter
#include <utility>    // pair

#include <boost/container/flat_set.hpp>
#include <boost/intrusive/unordered_set.hpp>

#include "sdd/internal/util/hash.hh"

namespace sdd { namespace values {

/*------------------------------------------------------------------------------------------------*/

/// @brief A unified set of values, implemented with a sorted vector.
template <typename Value>
class unique_flat_set
{
public:

  /// @brief The type of the real container.
  typedef boost::container::flat_set<Value> flat_set_type;

  /// @brief The type of the contained value.
  typedef Value value_type;

private:

  /// @brief Faster, unsafe mode for Boost.Intrusive.
  typedef boost::intrusive::link_mode<boost::intrusive::normal_link> link_mode;

  /// @brief An entry in the unique table of set of values.
  struct entry
    : public boost::intrusive::unordered_set_base_hook<link_mode>
  {
    // Can't copy an entry.
    entry(const entry&) = delete;
    entry& operator=(const entry&) = delete;

    /// @brief The unified flat set.
    const flat_set_type data;

    /// @brief Constructor.
    template <typename... Args>
    entry(Args&&... args)
      : data(std::forward<Args>(args)...)
    {
    }

    /// @brief Comparison.
    bool
    operator==(const entry& other)
    const noexcept
    {
      return data == other.data;
    }
  };

  /// @brief Hash an entry.
  struct hash
  {
    std::size_t
    operator()(const entry& e)
    const noexcept
    {
      std::size_t seed = 0;
      for (const auto& v : e.data)
      {
        internal::util::hash_combine(seed, v);
      }
      return seed;
    }
  };

  /// @brief The type of the set of flat sets.
  typedef typename boost::intrusive::make_unordered_set<entry, boost::intrusive::hash<hash>>::type
          set_type;
  typedef typename set_type::bucket_type bucket_type;
  typedef typename set_type::bucket_traits bucket_traits;

  /// @brief A pointer to the unified set of values.
  const flat_set_type* data_;

public:

  /// @brief The type of an iterator on a flat set of values.
  typedef typename flat_set_type::const_iterator const_iterator;

  /// @brief Default constructor.
  unique_flat_set()
    : data_(empty_set())
  {
  }

  /// @brief Constructor with a range.
  template <typename InputIterator>
  unique_flat_set(InputIterator begin, InputIterator end)
    : data_(unify(begin, end))
  {
  }

  /// @brief Constructor with a initializer_list.
  unique_flat_set(std::initializer_list<Value> values)
    : unique_flat_set(values.begin(), values.end())
  {
  }

  /// @brief Constructor from a temporary flat_set_type.
  unique_flat_set(flat_set_type&& fs)
    : data_(unify(std::move(fs)))
  {
  }

  unique_flat_set(const unique_flat_set&) = default;
  unique_flat_set& operator=(const unique_flat_set&) = default;
  unique_flat_set(unique_flat_set&&) = default;
  unique_flat_set& operator=(unique_flat_set&&) = default;

  /// @brief Insert a value.
  void
  insert(const Value& x)
  {
    flat_set_type s(*data_);
    const auto insertion = s.insert(x);
    if (insertion.second)
    {
      data_ = unify(std::move(s));
    }
  }

  /// @brief Get the beginning of this set of values.
  const_iterator
  cbegin()
  const noexcept
  {
    return data_->cbegin();
  }

  /// @brief Get the end of this set of values.
  const_iterator
  cend()
  const noexcept
  {
    return data_->cend();
  }

  /// @brief Tell if this set of values is empty.
  bool
  empty()
  const noexcept
  {
    return data_->empty();
  }

  /// @brief Get the number of contained values.
  std::size_t
  size()
  const noexcept
  {
    return data_->size();
  }

  /// @brief Find a value.
  const_iterator
  find(const Value& x)
  const
  {
    return data_->find(x);
  }

  /// @brief Erase a value.
  std::size_t
  erase(const Value& x)
  {
    flat_set_type fs(*data_);
    const std::size_t nb_erased = fs.erase(x);
    unify(std::move(fs));
    return nb_erased;
  }

/// @cond INTERNAL_DOC

  const flat_set_type* const
  data()
  const noexcept
  {
    return data_;
  }

/// @endcond

private:

  /// @brief Get the static set of flat sets.
  static
  set_type&
  set()
  {
    static bucket_type buckets[32000];
    static set_type set(bucket_traits(buckets, 32000));
    return set;
  }

  /// @brief Get the static empty flat set.
  static
  const flat_set_type*
  empty_set()
  {
    static auto e = unify(flat_set_type());
    return e;
  }

  /// @brief Unify a flat_set_type using a unique table.
  template <typename... Args>
  static
  const flat_set_type*
  unify(Args&&... args)
  {
    entry* ptr = new entry(std::forward<Args>(args)...);
    const auto insertion = set().insert(*ptr);
    if (not insertion.second)
    {
      delete ptr;
    }
    return &(insertion.first->data);
  }
};

/*------------------------------------------------------------------------------------------------*/

/// @brief Equality of unique_flat_set
/// @related unique_flat_set
template <typename Value>
inline
bool
operator==(const unique_flat_set<Value>& lhs, const unique_flat_set<Value>& rhs)
noexcept
{
  // Pointer comparison.
  return lhs.data() == rhs.data();
}

/*------------------------------------------------------------------------------------------------*/

/// @brief Comparison of unique_flat_set
/// @related unique_flat_set
template <typename Value>
inline
bool
operator<(const unique_flat_set<Value>& lhs, const unique_flat_set<Value>& rhs)
noexcept
{
  // Pointer comparison.
  return lhs.data() < rhs.data();
}

/*------------------------------------------------------------------------------------------------*/

/// @brief Textual output of a unique_flat_set
/// @related unique_flat_set
template <typename Value>
std::ostream&
operator<<(std::ostream& os, const unique_flat_set<Value>& fs)
{
  os << "{";
  if (not fs.empty())
  {
    std::copy(fs.cbegin(), std::prev(fs.cend()), std::ostream_iterator<Value>(os, ","));
    os << *std::prev(fs.cend());
  }
  return os << "}";
}

/*------------------------------------------------------------------------------------------------*/

/// @related unique_flat_set
template <typename Value>
inline
unique_flat_set<Value>
difference(const unique_flat_set<Value>& lhs, const unique_flat_set<Value>& rhs)
noexcept
{
  typename unique_flat_set<Value>::flat_set_type res;
  std::set_difference( lhs.cbegin(), lhs.cend(), rhs.cbegin(), rhs.cend()
                     , std::inserter(res, res.begin()));
  return unique_flat_set<Value>(std::move(res));
}

/*------------------------------------------------------------------------------------------------*/

/// @related unique_flat_set
template <typename Value>
inline
unique_flat_set<Value>
intersection(const unique_flat_set<Value>& lhs, const unique_flat_set<Value>& rhs)
noexcept
{
  typename unique_flat_set<Value>::flat_set_type res;
  std::set_intersection( lhs.cbegin(), lhs.cend(), rhs.cbegin(), rhs.cend()
                       , std::inserter(res, res.begin()));
  return unique_flat_set<Value>(std::move(res));
}

/*------------------------------------------------------------------------------------------------*/

/// @related unique_flat_set
template <typename Value>
inline
unique_flat_set<Value>
sum(const unique_flat_set<Value>& lhs, const unique_flat_set<Value>& rhs)
noexcept
{
  typename unique_flat_set<Value>::flat_set_type res;
  std::set_union( lhs.cbegin(), lhs.cend(), rhs.cbegin(), rhs.cend()
                , std::inserter(res, res.begin()));
  return unique_flat_set<Value>(std::move(res));
}

/*------------------------------------------------------------------------------------------------*/

}} // namespace sdd::values

namespace std {

/// @brief Hash specialization for sdd::values::flat_set
template <typename Value>
struct hash<sdd::values::unique_flat_set<Value>>
{
  std::size_t
  operator()(const sdd::values::unique_flat_set<Value>& fs)
  const noexcept
  {
    return std::hash<decltype(fs.data())>()(fs.data());
  }
};

} // namespace std

#endif // _SDD_VALUES_UNIQUE_FLAT_SET_HH_
<commit_msg>Avoid memory leak report of static member.<commit_after>#ifndef _SDD_VALUES_UNIQUE_FLAT_SET_HH_
#define _SDD_VALUES_UNIQUE_FLAT_SET_HH_

#include <algorithm>  // copy, set_difference, set_intersection, set_union
#include <functional> // hash
#include <initializer_list>
#include <iosfwd>
#include <iterator>   // inserter
#include <utility>    // pair

#include <boost/container/flat_set.hpp>
#include <boost/intrusive/unordered_set.hpp>

#include "sdd/internal/util/hash.hh"

namespace sdd { namespace values {

/*------------------------------------------------------------------------------------------------*/

/// @brief A unified set of values, implemented with a sorted vector.
template <typename Value>
class unique_flat_set
{
public:

  /// @brief The type of the real container.
  typedef boost::container::flat_set<Value> flat_set_type;

  /// @brief The type of the contained value.
  typedef Value value_type;

private:

  /// @brief Faster, unsafe mode for Boost.Intrusive.
  typedef boost::intrusive::link_mode<boost::intrusive::normal_link> link_mode;

  /// @brief An entry in the unique table of set of values.
  struct entry
    : public boost::intrusive::unordered_set_base_hook<link_mode>
  {
    // Can't copy an entry.
    entry(const entry&) = delete;
    entry& operator=(const entry&) = delete;

    /// @brief The unified flat set.
    const flat_set_type data;

    /// @brief Constructor.
    template <typename... Args>
    entry(Args&&... args)
      : data(std::forward<Args>(args)...)
    {
    }

    /// @brief Comparison.
    bool
    operator==(const entry& other)
    const noexcept
    {
      return data == other.data;
    }
  };

  /// @brief Hash an entry.
  struct hash
  {
    std::size_t
    operator()(const entry& e)
    const noexcept
    {
      std::size_t seed = 0;
      for (const auto& v : e.data)
      {
        internal::util::hash_combine(seed, v);
      }
      return seed;
    }
  };

  /// @brief The type of the set of flat sets.
  typedef typename boost::intrusive::make_unordered_set<entry, boost::intrusive::hash<hash>>::type
          set_type;
  typedef typename set_type::bucket_type bucket_type;
  typedef typename set_type::bucket_traits bucket_traits;

  /// @brief A pointer to the unified set of values.
  const flat_set_type* data_;

public:

  /// @brief The type of an iterator on a flat set of values.
  typedef typename flat_set_type::const_iterator const_iterator;

  /// @brief Default constructor.
  unique_flat_set()
    : data_(empty_set())
  {
  }

  /// @brief Constructor with a range.
  template <typename InputIterator>
  unique_flat_set(InputIterator begin, InputIterator end)
    : data_(unify(begin, end))
  {
  }

  /// @brief Constructor with a initializer_list.
  unique_flat_set(std::initializer_list<Value> values)
    : unique_flat_set(values.begin(), values.end())
  {
  }

  /// @brief Constructor from a temporary flat_set_type.
  unique_flat_set(flat_set_type&& fs)
    : data_(unify(std::move(fs)))
  {
  }

  unique_flat_set(const unique_flat_set&) = default;
  unique_flat_set& operator=(const unique_flat_set&) = default;
  unique_flat_set(unique_flat_set&&) = default;
  unique_flat_set& operator=(unique_flat_set&&) = default;

  /// @brief Insert a value.
  void
  insert(const Value& x)
  {
    flat_set_type s(*data_);
    const auto insertion = s.insert(x);
    if (insertion.second)
    {
      data_ = unify(std::move(s));
    }
  }

  /// @brief Get the beginning of this set of values.
  const_iterator
  cbegin()
  const noexcept
  {
    return data_->cbegin();
  }

  /// @brief Get the end of this set of values.
  const_iterator
  cend()
  const noexcept
  {
    return data_->cend();
  }

  /// @brief Tell if this set of values is empty.
  bool
  empty()
  const noexcept
  {
    return data_->empty();
  }

  /// @brief Get the number of contained values.
  std::size_t
  size()
  const noexcept
  {
    return data_->size();
  }

  /// @brief Find a value.
  const_iterator
  find(const Value& x)
  const
  {
    return data_->find(x);
  }

  /// @brief Erase a value.
  std::size_t
  erase(const Value& x)
  {
    flat_set_type fs(*data_);
    const std::size_t nb_erased = fs.erase(x);
    unify(std::move(fs));
    return nb_erased;
  }

/// @cond INTERNAL_DOC

  const flat_set_type* const
  data()
  const noexcept
  {
    return data_;
  }

/// @endcond

private:

  /// @brief Help cleaning the static set.
  struct set_disposer
  {
    bucket_type* buckets;
    set_type* set;

    set_disposer(std::size_t size)
      : buckets(new bucket_type[size])
      , set(new set_type(bucket_traits(buckets, size)))
    {
    }

    ~set_disposer()
    {
      set->clear_and_dispose([](entry* e){delete e;});
      delete set;
      delete[] buckets;
    }
  };

  /// @brief Get the static set of flat sets.
  static
  set_type&
  set()
  {
    static set_disposer disposer(32000);
    return *disposer.set;
  }

  /// @brief Get the static empty flat set.
  static
  const flat_set_type*
  empty_set()
  {
    static auto e = unify(flat_set_type());
    return e;
  }

  /// @brief Unify a flat_set_type using a unique table.
  template <typename... Args>
  static
  const flat_set_type*
  unify(Args&&... args)
  {
    entry* ptr = new entry(std::forward<Args>(args)...);
    const auto insertion = set().insert(*ptr);
    if (not insertion.second)
    {
      delete ptr;
    }
    return &(insertion.first->data);
  }
};

/*------------------------------------------------------------------------------------------------*/

/// @brief Equality of unique_flat_set
/// @related unique_flat_set
template <typename Value>
inline
bool
operator==(const unique_flat_set<Value>& lhs, const unique_flat_set<Value>& rhs)
noexcept
{
  // Pointer comparison.
  return lhs.data() == rhs.data();
}

/*------------------------------------------------------------------------------------------------*/

/// @brief Comparison of unique_flat_set
/// @related unique_flat_set
template <typename Value>
inline
bool
operator<(const unique_flat_set<Value>& lhs, const unique_flat_set<Value>& rhs)
noexcept
{
  // Pointer comparison.
  return lhs.data() < rhs.data();
}

/*------------------------------------------------------------------------------------------------*/

/// @brief Textual output of a unique_flat_set
/// @related unique_flat_set
template <typename Value>
std::ostream&
operator<<(std::ostream& os, const unique_flat_set<Value>& fs)
{
  os << "{";
  if (not fs.empty())
  {
    std::copy(fs.cbegin(), std::prev(fs.cend()), std::ostream_iterator<Value>(os, ","));
    os << *std::prev(fs.cend());
  }
  return os << "}";
}

/*------------------------------------------------------------------------------------------------*/

/// @related unique_flat_set
template <typename Value>
inline
unique_flat_set<Value>
difference(const unique_flat_set<Value>& lhs, const unique_flat_set<Value>& rhs)
noexcept
{
  typename unique_flat_set<Value>::flat_set_type res;
  std::set_difference( lhs.cbegin(), lhs.cend(), rhs.cbegin(), rhs.cend()
                     , std::inserter(res, res.begin()));
  return unique_flat_set<Value>(std::move(res));
}

/*------------------------------------------------------------------------------------------------*/

/// @related unique_flat_set
template <typename Value>
inline
unique_flat_set<Value>
intersection(const unique_flat_set<Value>& lhs, const unique_flat_set<Value>& rhs)
noexcept
{
  typename unique_flat_set<Value>::flat_set_type res;
  std::set_intersection( lhs.cbegin(), lhs.cend(), rhs.cbegin(), rhs.cend()
                       , std::inserter(res, res.begin()));
  return unique_flat_set<Value>(std::move(res));
}

/*------------------------------------------------------------------------------------------------*/

/// @related unique_flat_set
template <typename Value>
inline
unique_flat_set<Value>
sum(const unique_flat_set<Value>& lhs, const unique_flat_set<Value>& rhs)
noexcept
{
  typename unique_flat_set<Value>::flat_set_type res;
  std::set_union( lhs.cbegin(), lhs.cend(), rhs.cbegin(), rhs.cend()
                , std::inserter(res, res.begin()));
  return unique_flat_set<Value>(std::move(res));
}

/*------------------------------------------------------------------------------------------------*/

}} // namespace sdd::values

namespace std {

/// @brief Hash specialization for sdd::values::flat_set
template <typename Value>
struct hash<sdd::values::unique_flat_set<Value>>
{
  std::size_t
  operator()(const sdd::values::unique_flat_set<Value>& fs)
  const noexcept
  {
    return std::hash<decltype(fs.data())>()(fs.data());
  }
};

} // namespace std

#endif // _SDD_VALUES_UNIQUE_FLAT_SET_HH_
<|endoftext|>


<commit_before>#ifndef GENERIC_FORWARDER_HPP
# define GENERIC_FORWARDER_HPP
# pragma once

#include <cassert>

// ::std::size_t
#include <cstddef>

#include <functional>

#include <type_traits>

#include <utility>

namespace generic
{

namespace detail
{

namespace forwarder
{

template <typename ...A>
struct argument_types
{
};

template <typename A>
struct argument_types
{
  using argument_type = A;
};

template <typename A, typename B>
struct argument_types
{
  using first_argument_type = A;
  using second_argument_type = B;
};

}

}

constexpr auto const default_forwarder_noexcept =
#if __cpp_exceptions
false;
#else
true;
#endif // __cpp_exceptions

constexpr auto const default_forwarder_size = 4 * sizeof(void*);

template <typename F,
  bool NE = default_forwarder_noexcept,
  ::std::size_t N = default_forwarder_size>
class forwarder;

template <typename R, typename ...A, bool NE, ::std::size_t N>
class forwarder<R (A...), NE, N> : public detail::forwarder::argument_types
{
  R (*stub_)(void const*, A&&...) noexcept(NE) {};

  typename ::std::aligned_storage_t<N> store_;

  template<typename T, typename ...U, ::std::size_t M>
  friend bool operator==(forwarder<T (U...), M> const&,
    ::std::nullptr_t) noexcept;
  template<typename T, typename ...U, ::std::size_t M>
  friend bool operator==(::std::nullptr_t,
    forwarder<T (U...), M> const&) noexcept;

  template<typename T, typename ...U, ::std::size_t M>
  friend bool operator!=(forwarder<T (U...), M> const&,
    ::std::nullptr_t) noexcept;
  template<typename T, typename ...U, ::std::size_t M>
  friend bool operator!=(::std::nullptr_t,
    forwarder<T (U...), M> const&) noexcept;

public:
  using result_type = R;

public:
  forwarder() = default;

  forwarder(forwarder const&) = default;

  forwarder(forwarder&&) = default;

  template<typename T>
  forwarder(T&& t) noexcept
  {
    assign(::std::forward<T>(t));
  }

  forwarder& operator=(forwarder const&) = default;

  forwarder& operator=(forwarder&&) = default;

  template <typename T>
  auto& operator=(T&& f) noexcept
  {
    assign(::std::forward<T>(f));

    return *this;
  }

  explicit operator bool() const noexcept { return stub_; }

  R operator()(A... args) const
    noexcept(noexcept(stub_(&store_, ::std::forward(args)...)))
  {
    //assert(stub_);
    return stub_(&store_, ::std::forward(args)...);
  }

  void assign(::std::nullptr_t) noexcept
  {
    reset();
  }

  template <typename T>
  void assign(T&& f) noexcept
  {
    using functor_type = ::std::add_const_t<::std::remove_reference_t<T>>;

    static_assert(sizeof(functor_type) <= sizeof(store_),
      "functor too large");
    static_assert(::std::is_trivially_copyable<T>{},
      "functor not trivially copyable");

    ::new (static_cast<void*>(&store_)) functor_type(::std::forward<T>(f));

    stub_ = [](void const* const ptr, A&&... args) noexcept(
        noexcept(
#if __cplusplus <= 201402L
        (
          *static_cast<functor_type*>(ptr))(::std::forward(args)...)
#else
          ::std::invoke(*static_cast<functor_type*>(ptr),
            ::std::forward(args)...
          )
#endif // __cplusplus
        )
      ) -> R
      {
#if __cplusplus <= 201402L
        return (*static_cast<functor_type*>(ptr))(::std::forward(args)...);
#else
        return ::std::invoke(*static_cast<functor_type*>(ptr),
          ::std::forward(args)...
        );
#endif // __cplusplus
      };
  }

  void reset() noexcept { stub_ = nullptr; }

  void swap(forwarder& other) noexcept { ::std::swap(*this, other); }

  template <typename T>
  auto target() noexcept
  {
    return reinterpret_cast<T*>(&store_);
  }

  template <typename T>
  auto target() const noexcept
  {
    return reinterpret_cast<T const*>(&store_);
  }
};

template<typename R, typename ...A, ::std::size_t N>
bool operator==(forwarder<R (A...), N> const& f,
  ::std::nullptr_t const) noexcept
{
  return f.stub_ == nullptr;
}

template<typename R, typename ...A, ::std::size_t N>
bool operator==(::std::nullptr_t const,
  forwarder<R (A...), N> const& f) noexcept
{
  return f.stub_ == nullptr;
}

template<typename R, typename ...A, ::std::size_t N>
bool operator!=(forwarder<R (A...), N> const& f,
  ::std::nullptr_t const) noexcept
{
  return !operator==(f, nullptr);
}

template<typename R, typename ...A, ::std::size_t N>
bool operator!=(::std::nullptr_t const,
  forwarder<R (A...), N> const& f) noexcept
{
  return !operator==(f, nullptr);
}

}

#endif // GENERIC_FORWARDER_HPP
<commit_msg>some fixes<commit_after>#ifndef GENERIC_FORWARDER_HPP
# define GENERIC_FORWARDER_HPP
# pragma once

#include <cassert>

// ::std::size_t
#include <cstddef>

#include <functional>

#include <type_traits>

#include <utility>

namespace generic
{

namespace detail
{

namespace forwarder
{

template <typename ...A>
struct argument_types
{
};

template <typename A>
struct argument_types
{
  using argument_type = A;
};

template <typename A, typename B>
struct argument_types
{
  using first_argument_type = A;
  using second_argument_type = B;
};

}

}

constexpr auto const default_forwarder_noexcept =
#if __cpp_exceptions
false;
#else
true;
#endif // __cpp_exceptions

constexpr auto const default_forwarder_size = 4 * sizeof(void*);

template <typename F,
  bool NE = default_forwarder_noexcept,
  ::std::size_t N = default_forwarder_size>
class forwarder;

template <typename R, typename ...A, bool NE, ::std::size_t N>
class forwarder<R (A...), NE, N> : public detail::forwarder::argument_types
{
  R (*stub_)(void const*, A&&...) noexcept(NE) {};

  typename ::std::aligned_storage_t<N> store_;

  template<typename T, typename ...U, ::std::size_t M>
  friend bool operator==(forwarder<T (U...), M> const&,
    ::std::nullptr_t) noexcept;
  template<typename T, typename ...U, ::std::size_t M>
  friend bool operator==(::std::nullptr_t,
    forwarder<T (U...), M> const&) noexcept;

  template<typename T, typename ...U, ::std::size_t M>
  friend bool operator!=(forwarder<T (U...), M> const&,
    ::std::nullptr_t) noexcept;
  template<typename T, typename ...U, ::std::size_t M>
  friend bool operator!=(::std::nullptr_t,
    forwarder<T (U...), M> const&) noexcept;

public:
  using result_type = R;

public:
  forwarder() = default;

  forwarder(forwarder const&) = default;

  forwarder(forwarder&&) = default;

  template<typename T>
  forwarder(T&& t) noexcept
  {
    assign(::std::forward<T>(t));
  }

  forwarder& operator=(forwarder const&) = default;

  forwarder& operator=(forwarder&&) = default;

  template <typename T>
  auto& operator=(T&& f) noexcept
  {
    assign(::std::forward<T>(f));

    return *this;
  }

  explicit operator bool() const noexcept { return stub_; }

  R operator()(A... args) const
    noexcept(noexcept(stub_(&store_, ::std::forward(args)...)))
  {
    //assert(stub_);
    return stub_(&store_, ::std::forward(args)...);
  }

  void assign(::std::nullptr_t) noexcept
  {
    reset();
  }

  template <typename T>
  void assign(T&& f) noexcept
  {
    using functor_type = ::std::decay_t<T>;

    static_assert(sizeof(functor_type) <= sizeof(store_),
      "functor too large");
    static_assert(::std::is_trivially_copyable<functor_type>{},
      "functor not trivially copyable");

    ::new (static_cast<void*>(&store_)) functor_type(::std::forward<T>(f));

    stub_ = [](void const* const ptr, A&&... args) noexcept(
        noexcept(
        (
#if __cplusplus <= 201402L
          *static_cast<functor_type const*>(ptr))(
            ::std::forward(args)...)
#else
          ::std::invoke(*static_cast<functor_type const*>(ptr),
            ::std::forward(args)...)
#endif // __cplusplus
        )
      ) -> R
      {
#if __cplusplus <= 201402L
        return (*static_cast<functor_type const*>(ptr))(
          ::std::forward(args)...);
#else
        return ::std::invoke(*static_cast<functor_type const*>(ptr),
          ::std::forward(args)...);
#endif // __cplusplus
      };
  }

  void reset() noexcept { stub_ = nullptr; }

  void swap(forwarder& other) noexcept { ::std::swap(*this, other); }

  template <typename T>
  auto target() noexcept
  {
    return reinterpret_cast<T*>(&store_);
  }

  template <typename T>
  auto target() const noexcept
  {
    return reinterpret_cast<T const*>(&store_);
  }
};

template<typename R, typename ...A, ::std::size_t N>
bool operator==(forwarder<R (A...), N> const& f,
  ::std::nullptr_t const) noexcept
{
  return f.stub_ == nullptr;
}

template<typename R, typename ...A, ::std::size_t N>
bool operator==(::std::nullptr_t const,
  forwarder<R (A...), N> const& f) noexcept
{
  return f.stub_ == nullptr;
}

template<typename R, typename ...A, ::std::size_t N>
bool operator!=(forwarder<R (A...), N> const& f,
  ::std::nullptr_t const) noexcept
{
  return !operator==(f, nullptr);
}

template<typename R, typename ...A, ::std::size_t N>
bool operator!=(::std::nullptr_t const,
  forwarder<R (A...), N> const& f) noexcept
{
  return !operator==(f, nullptr);
}

}

#endif // GENERIC_FORWARDER_HPP
<|endoftext|>