<commit_before>/****************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the Qt Assistant of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** No Commercial Usage
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights.  These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
**
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "tracer.h"

#include "bookmarkmanager.h"
#include "bookmarkdialog.h"
#include "bookmarkfiltermodel.h"
#include "bookmarkitem.h"
#include "bookmarkmodel.h"
#include "centralwidget.h"
#include "helpenginewrapper.h"

#include <QtGui/QFileDialog>
#include <QtGui/QMenu>
#include <QtGui/QKeyEvent>
#include <QtGui/QMessageBox>
#include <QtGui/QSortFilterProxyModel>

#include <QFile>
#include "xbelsupport.h"

QT_BEGIN_NAMESPACE

// -- BookmarkManager::BookmarkWidget

void BookmarkManager::BookmarkWidget::focusInEvent(QFocusEvent *event)
{
    TRACE_OBJ
    if (event->reason() != Qt::MouseFocusReason) {
        ui.lineEdit->selectAll();
        ui.lineEdit->setFocus();

        // force the focus in event on bookmark manager
        emit focusInEvent();
    }
}

// -- BookmarkManager::BookmarkTreeView

BookmarkManager::BookmarkTreeView::BookmarkTreeView(QWidget *parent)
    : QTreeView(parent)
{
    TRACE_OBJ
    setAcceptDrops(true);
    setDragEnabled(true);
    setAutoExpandDelay(1000);
    setUniformRowHeights(true);
    setDropIndicatorShown(true);
    setExpandsOnDoubleClick(true);

    connect(this, SIGNAL(expanded(QModelIndex)), this,
        SLOT(setExpandedData(QModelIndex)));
    connect(this, SIGNAL(collapsed(QModelIndex)), this,
        SLOT(setExpandedData(QModelIndex)));

}

void BookmarkManager::BookmarkTreeView::subclassKeyPressEvent(QKeyEvent *event)
{
    TRACE_OBJ
    QTreeView::keyPressEvent(event);
}

void BookmarkManager::BookmarkTreeView::setExpandedData(const QModelIndex &index)
{
    TRACE_OBJ
    if (BookmarkModel *treeModel = qobject_cast<BookmarkModel*> (model()))
        treeModel->setData(index, isExpanded(index), UserRoleExpanded);
}

// -- BookmarkManager

QMutex BookmarkManager::mutex;
BookmarkManager* BookmarkManager::bookmarkManager = 0;

// -- public

BookmarkManager* BookmarkManager::instance()
{
    TRACE_OBJ
    if (!bookmarkManager) {
        QMutexLocker _(&mutex);
        if (!bookmarkManager)
            bookmarkManager = new BookmarkManager();
    }
    return bookmarkManager;
}

void BookmarkManager::destroy()
{
    TRACE_OBJ
    delete bookmarkManager;
    bookmarkManager = 0;
}

QWidget* BookmarkManager::bookmarkDockWidget() const
{
    TRACE_OBJ
    if (bookmarkWidget)
        return bookmarkWidget;
    return 0;
}

void BookmarkManager::takeBookmarksMenu(QMenu* menu)
{
    TRACE_OBJ
    bookmarkMenu = menu;
    refeshBookmarkMenu();
}

// -- public slots

void BookmarkManager::addBookmark(const QString &title, const QString &url)
{
    TRACE_OBJ
    showBookmarkDialog(title.isEmpty() ? tr("Untiled") : title,
        url.isEmpty() ? QLatin1String("about:blank") : url);
}

// -- private

BookmarkManager::BookmarkManager()
    : typeAndSearch(false)
    , bookmarkMenu(0)
    , bookmarkModel(new BookmarkModel)
    , bookmarkWidget(new BookmarkWidget)
    , bookmarkTreeView(new BookmarkTreeView)
{
    TRACE_OBJ
    bookmarkWidget->installEventFilter(this);
    connect(bookmarkWidget->ui.add, SIGNAL(clicked()), this,
        SLOT(addBookmark()));
    connect(bookmarkWidget->ui.remove, SIGNAL(clicked()), this,
        SLOT(removeBookmark()));
    connect(bookmarkWidget->ui.lineEdit, SIGNAL(textChanged(QString)), this,
        SLOT(textChanged(QString)));
    connect(bookmarkWidget, SIGNAL(focusInEvent()), this, SLOT(focusInEvent()));

    bookmarkTreeView->setModel(bookmarkModel);
    bookmarkTreeView->installEventFilter(this);
    bookmarkTreeView->viewport()->installEventFilter(this);
    bookmarkTreeView->setContextMenuPolicy(Qt::CustomContextMenu);
    bookmarkWidget->ui.stackedWidget->addWidget(bookmarkTreeView);

    connect(bookmarkTreeView, SIGNAL(activated(QModelIndex)), this,
        SLOT(setSourceFromIndex(QModelIndex)));
    connect(bookmarkTreeView, SIGNAL(customContextMenuRequested(QPoint)), this,
        SLOT(customContextMenuRequested(QPoint)));

    connect(&HelpEngineWrapper::instance(), SIGNAL(setupFinished()), this,
        SLOT(setupFinished()));
    connect(bookmarkModel, SIGNAL(rowsRemoved(QModelIndex, int, int)), this,
        SLOT(refeshBookmarkMenu()));
    connect(bookmarkModel, SIGNAL(rowsInserted(QModelIndex, int, int)), this,
        SLOT(refeshBookmarkMenu()));
    connect(bookmarkModel, SIGNAL(dataChanged(QModelIndex, QModelIndex)), this,
        SLOT(refeshBookmarkMenu()));
}

BookmarkManager::~BookmarkManager()
{
    TRACE_OBJ
    HelpEngineWrapper::instance().setBookmarks(bookmarkModel->bookmarks());
}

void BookmarkManager::removeItem(const QModelIndex &index)
{
    TRACE_OBJ
    QModelIndex current = index;
    if (typeAndSearch) { // need to map because of proxy
        current = typeAndSearchModel->mapToSource(current);
        current = bookmarkFilterModel->mapToSource(current);
    } else if (!bookmarkModel->parent(index).isValid()) {
        return;  // check if we should delete the "Bookmarks Menu", bail
    }

    if (bookmarkModel->hasChildren(current)) {
        int value = QMessageBox::question(bookmarkTreeView, tr("Remove"),
            tr("You are going to delete a Folder, this will also<br>"
            "remove it's content. Are you sure to continue?"),
            QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel);
        if (value == QMessageBox::Cancel)
            return;
    }
    bookmarkModel->removeItem(current);
}

bool BookmarkManager::eventFilter(QObject *object, QEvent *event)
{
    if (object != bookmarkTreeView && object != bookmarkTreeView->viewport()
        && object != bookmarkWidget)
            return QObject::eventFilter(object, event);

    TRACE_OBJ
    const bool isWidget = object == bookmarkWidget;
    if (event->type() == QEvent::KeyPress) {
        QKeyEvent *ke = static_cast<QKeyEvent*>(event);
        switch (ke->key()) {
            case Qt::Key_F2: {
                renameBookmark(bookmarkTreeView->currentIndex());
            }   break;

            case Qt::Key_Delete: {
                removeItem(bookmarkTreeView->currentIndex());
            }   break;

            case Qt::Key_Up: {    // needs event filter on widget
            case Qt::Key_Down:
                if (isWidget)
                    bookmarkTreeView->subclassKeyPressEvent(ke);
            }   break;

            case Qt::Key_Escape: {
                emit escapePressed();
            }   break;

            default: break;
        }
    }

    if (event->type() == QEvent::MouseButtonRelease && !isWidget) {
        QMouseEvent *me = static_cast<QMouseEvent*>(event);
        switch (me->button()) {
            case Qt::LeftButton: {
                if (me->modifiers() & Qt::ControlModifier)
                    setSourceFromIndex(bookmarkTreeView->currentIndex(), true);
            }   break;

            case Qt::MidButton: {
                setSourceFromIndex(bookmarkTreeView->currentIndex(), true);
            }   break;

            default: break;
        }
    }

    return QObject::eventFilter(object, event);
}

void BookmarkManager::buildBookmarksMenu(const QModelIndex &index, QMenu* menu)
{
    TRACE_OBJ
    if (!index.isValid())
        return;

    const QString &text = index.data().toString();
    const QIcon &icon = qVariantValue<QIcon>(index.data(Qt::DecorationRole));
    if (index.data(UserRoleFolder).toBool()) {
        if (QMenu* subMenu = menu->addMenu(icon, text)) {
            for (int i = 0; i < bookmarkModel->rowCount(index); ++i)
                buildBookmarksMenu(bookmarkModel->index(i, 0, index), subMenu);
        }
    } else {
        QAction *action = menu->addAction(icon, text);
        action->setData(index.data(UserRoleUrl).toString());
    }
}

void BookmarkManager::showBookmarkDialog(const QString &name, const QString &url)
{
    TRACE_OBJ
    BookmarkDialog dialog(bookmarkModel, name, url, bookmarkTreeView);
    dialog.exec();
}

// -- private slots

void BookmarkManager::setupFinished()
{
    TRACE_OBJ
    bookmarkModel->setBookmarks(HelpEngineWrapper::instance().bookmarks());
    bookmarkModel->expandFoldersIfNeeeded(bookmarkTreeView);

    refeshBookmarkMenu();

    bookmarkTreeView->hideColumn(1);
    bookmarkTreeView->header()->setVisible(false);
    bookmarkTreeView->header()->setStretchLastSection(true);

    bookmarkFilterModel = new BookmarkFilterModel(this);
    bookmarkFilterModel->setSourceModel(bookmarkModel);
    bookmarkFilterModel->filterBookmarkFolders();

    typeAndSearchModel = new QSortFilterProxyModel(this);
    typeAndSearchModel->setDynamicSortFilter(true);
    typeAndSearchModel->setSourceModel(bookmarkFilterModel);
}

void BookmarkManager::addBookmark()
{
    TRACE_OBJ
    if (CentralWidget *widget = CentralWidget::instance())
        addBookmark(widget->currentTitle(), widget->currentSource().toString());
}

void BookmarkManager::removeBookmark()
{
    TRACE_OBJ
    removeItem(bookmarkTreeView->currentIndex());
}

//void BookmarkManager::manageBookmarks()
//{
//    TRACE_OBJ
//}

void BookmarkManager::refeshBookmarkMenu()
{
    TRACE_OBJ
    if (!bookmarkMenu)
        return;

    bookmarkMenu->clear();

    //bookmarkMenu->addAction(tr("Manage Bookmarks..."), this,
    //    SLOT(manageBookmarks()));
    bookmarkMenu->addAction(tr("Import..."), this, SLOT(importBookmarks()));
    bookmarkMenu->addAction(tr("Export..."), this, SLOT(exportBookmarks()));
    bookmarkMenu->addAction(tr("Add Bookmark..."), this, SLOT(addBookmark()),
        QKeySequence(tr("Ctrl+D")));
    bookmarkMenu->addSeparator();

    const QModelIndex &root = bookmarkModel->index(0, 0, QModelIndex());
    for (int i = 0; i < bookmarkModel->rowCount(root); ++i)
        buildBookmarksMenu(bookmarkModel->index(i, 0, root), bookmarkMenu);

    connect(bookmarkMenu, SIGNAL(triggered(QAction*)), this,
        SLOT(setSourceFromAction(QAction*)));
}

void BookmarkManager::renameBookmark(const QModelIndex &index)
{
    // check if we should rename the "Bookmarks Menu", bail
    if (!typeAndSearch && !bookmarkModel->parent(index).isValid())
        return;

    bookmarkModel->setItemsEditable(true);
    bookmarkTreeView->edit(index);
    bookmarkModel->setItemsEditable(false);
}

void BookmarkManager::importBookmarks()
{
    TRACE_OBJ
    const QString &fileName = QFileDialog::getOpenFileName(0, tr("Open File"),
        QDir::currentPath(), tr("Files (*.xbel)"));

    if (fileName.isEmpty())
        return;

    QFile file(fileName);
    if (file.open(QIODevice::ReadOnly)) {
        XbelReader reader(bookmarkModel);
        reader.readFromFile(&file);
    }
}

void BookmarkManager::exportBookmarks()
{
    TRACE_OBJ
    QString fileName = QFileDialog::getSaveFileName(0, tr("Save File"),
        QLatin1String("untitled.xbel"), tr("Files (*.xbel)"));

    const QLatin1String suffix(".xbel");
    if (!fileName.endsWith(suffix))
        fileName.append(suffix);

    QFile file(fileName);
    if (file.open(QIODevice::WriteOnly)) {
        XbelWriter writer(bookmarkModel);
        writer.writeToFile(&file);
    } else {
        QMessageBox::information(bookmarkTreeView, tr("Qt Assistant"),
            tr("Unable to save bookmarks."), tr("OK"));
    }
}

void BookmarkManager::setSourceFromAction(QAction *action)
{
    TRACE_OBJ
    const QVariant &data = action->data();

    if (data.canConvert<QUrl>())
        emit setSource(data.toUrl());
}

void BookmarkManager::setSourceFromIndex(const QModelIndex &index, bool newTab)
{
    TRACE_OBJ
    QAbstractItemModel *base = bookmarkModel;
    if (typeAndSearch)
        base = typeAndSearchModel;

    if (base->data(index, UserRoleFolder).toBool())
        return;

    const QVariant &data = base->data(index, UserRoleUrl);
    if (data.canConvert<QUrl>()) {
        if (newTab)
            emit setSourceInNewTab(data.toUrl());
        else
            emit setSource(data.toUrl());
    }
}

void BookmarkManager::customContextMenuRequested(const QPoint &point)
{
    TRACE_OBJ
    QModelIndex index = bookmarkTreeView->indexAt(point);
    if (!index.isValid())
        return;

    // check if we should open the menu on "Bookmarks Menu", bail
    if (!typeAndSearch && !bookmarkModel->parent(index).isValid())
        return;

    QAction *remove = 0;
    QAction *rename = 0;
    QAction *showItem = 0;
    QAction *showItemInNewTab = 0;

    QMenu menu(QLatin1String(""));
    if (!typeAndSearch && bookmarkModel->data(index, UserRoleFolder).toBool()) {
        remove = menu.addAction(tr("Delete Folder"));
        rename = menu.addAction(tr("Rename Folder"));
    } else {
        showItem = menu.addAction(tr("Show Bookmark"));
        showItemInNewTab = menu.addAction(tr("Show Bookmark in New Tab"));
        menu.addSeparator();
        remove = menu.addAction(tr("Delete Bookmark"));
        rename = menu.addAction(tr("Rename Bookmark"));
    }

    QAction *pickedAction = menu.exec(bookmarkTreeView->mapToGlobal(point));
    if (pickedAction == rename)
        renameBookmark(index);
    else if (pickedAction == remove)
        removeItem(index);
    else if (pickedAction == showItem || pickedAction == showItemInNewTab)
        setSourceFromIndex(index, pickedAction == showItemInNewTab);
}

void BookmarkManager::focusInEvent()
{
    TRACE_OBJ
    const QModelIndex &index = bookmarkTreeView->indexAt(QPoint(2, 2));
    if (index.isValid())
        bookmarkTreeView->setCurrentIndex(index);
}

void BookmarkManager::textChanged(const QString &text)
{
    TRACE_OBJ
    if (!bookmarkWidget->ui.lineEdit->text().isEmpty()) {
        if (!typeAndSearch) {
            typeAndSearch = true;
            bookmarkTreeView->setItemsExpandable(false);
            bookmarkTreeView->setRootIsDecorated(false);
            bookmarkTreeView->setModel(typeAndSearchModel);
        }
        typeAndSearchModel->setFilterRegExp(QRegExp(text));
    } else {
        typeAndSearch = false;
        bookmarkTreeView->setModel(bookmarkModel);
        bookmarkTreeView->setItemsExpandable(true);
        bookmarkTreeView->setRootIsDecorated(true);
        bookmarkModel->expandFoldersIfNeeeded(bookmarkTreeView);
    }
}

QT_END_NAMESPACE
<commit_msg>Fix memmory leak.<commit_after>/****************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the Qt Assistant of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** No Commercial Usage
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights.  These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
**
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "tracer.h"

#include "bookmarkmanager.h"
#include "bookmarkdialog.h"
#include "bookmarkfiltermodel.h"
#include "bookmarkitem.h"
#include "bookmarkmodel.h"
#include "centralwidget.h"
#include "helpenginewrapper.h"

#include <QtGui/QFileDialog>
#include <QtGui/QMenu>
#include <QtGui/QKeyEvent>
#include <QtGui/QMessageBox>
#include <QtGui/QSortFilterProxyModel>

#include <QFile>
#include "xbelsupport.h"

QT_BEGIN_NAMESPACE

// -- BookmarkManager::BookmarkWidget

void BookmarkManager::BookmarkWidget::focusInEvent(QFocusEvent *event)
{
    TRACE_OBJ
    if (event->reason() != Qt::MouseFocusReason) {
        ui.lineEdit->selectAll();
        ui.lineEdit->setFocus();

        // force the focus in event on bookmark manager
        emit focusInEvent();
    }
}

// -- BookmarkManager::BookmarkTreeView

BookmarkManager::BookmarkTreeView::BookmarkTreeView(QWidget *parent)
    : QTreeView(parent)
{
    TRACE_OBJ
    setAcceptDrops(true);
    setDragEnabled(true);
    setAutoExpandDelay(1000);
    setUniformRowHeights(true);
    setDropIndicatorShown(true);
    setExpandsOnDoubleClick(true);

    connect(this, SIGNAL(expanded(QModelIndex)), this,
        SLOT(setExpandedData(QModelIndex)));
    connect(this, SIGNAL(collapsed(QModelIndex)), this,
        SLOT(setExpandedData(QModelIndex)));

}

void BookmarkManager::BookmarkTreeView::subclassKeyPressEvent(QKeyEvent *event)
{
    TRACE_OBJ
    QTreeView::keyPressEvent(event);
}

void BookmarkManager::BookmarkTreeView::setExpandedData(const QModelIndex &index)
{
    TRACE_OBJ
    if (BookmarkModel *treeModel = qobject_cast<BookmarkModel*> (model()))
        treeModel->setData(index, isExpanded(index), UserRoleExpanded);
}

// -- BookmarkManager

QMutex BookmarkManager::mutex;
BookmarkManager* BookmarkManager::bookmarkManager = 0;

// -- public

BookmarkManager* BookmarkManager::instance()
{
    TRACE_OBJ
    if (!bookmarkManager) {
        QMutexLocker _(&mutex);
        if (!bookmarkManager)
            bookmarkManager = new BookmarkManager();
    }
    return bookmarkManager;
}

void BookmarkManager::destroy()
{
    TRACE_OBJ
    delete bookmarkManager;
    bookmarkManager = 0;
}

QWidget* BookmarkManager::bookmarkDockWidget() const
{
    TRACE_OBJ
    if (bookmarkWidget)
        return bookmarkWidget;
    return 0;
}

void BookmarkManager::takeBookmarksMenu(QMenu* menu)
{
    TRACE_OBJ
    bookmarkMenu = menu;
    refeshBookmarkMenu();
}

// -- public slots

void BookmarkManager::addBookmark(const QString &title, const QString &url)
{
    TRACE_OBJ
    showBookmarkDialog(title.isEmpty() ? tr("Untiled") : title,
        url.isEmpty() ? QLatin1String("about:blank") : url);
}

// -- private

BookmarkManager::BookmarkManager()
    : typeAndSearch(false)
    , bookmarkMenu(0)
    , bookmarkModel(new BookmarkModel)
    , bookmarkWidget(new BookmarkWidget)
    , bookmarkTreeView(new BookmarkTreeView)
{
    TRACE_OBJ
    bookmarkWidget->installEventFilter(this);
    connect(bookmarkWidget->ui.add, SIGNAL(clicked()), this,
        SLOT(addBookmark()));
    connect(bookmarkWidget->ui.remove, SIGNAL(clicked()), this,
        SLOT(removeBookmark()));
    connect(bookmarkWidget->ui.lineEdit, SIGNAL(textChanged(QString)), this,
        SLOT(textChanged(QString)));
    connect(bookmarkWidget, SIGNAL(focusInEvent()), this, SLOT(focusInEvent()));

    bookmarkTreeView->setModel(bookmarkModel);
    bookmarkTreeView->installEventFilter(this);
    bookmarkTreeView->viewport()->installEventFilter(this);
    bookmarkTreeView->setContextMenuPolicy(Qt::CustomContextMenu);
    bookmarkWidget->ui.stackedWidget->addWidget(bookmarkTreeView);

    connect(bookmarkTreeView, SIGNAL(activated(QModelIndex)), this,
        SLOT(setSourceFromIndex(QModelIndex)));
    connect(bookmarkTreeView, SIGNAL(customContextMenuRequested(QPoint)), this,
        SLOT(customContextMenuRequested(QPoint)));

    connect(&HelpEngineWrapper::instance(), SIGNAL(setupFinished()), this,
        SLOT(setupFinished()));
    connect(bookmarkModel, SIGNAL(rowsRemoved(QModelIndex, int, int)), this,
        SLOT(refeshBookmarkMenu()));
    connect(bookmarkModel, SIGNAL(rowsInserted(QModelIndex, int, int)), this,
        SLOT(refeshBookmarkMenu()));
    connect(bookmarkModel, SIGNAL(dataChanged(QModelIndex, QModelIndex)), this,
        SLOT(refeshBookmarkMenu()));
}

BookmarkManager::~BookmarkManager()
{
    TRACE_OBJ
    HelpEngineWrapper::instance().setBookmarks(bookmarkModel->bookmarks());
    delete bookmarkModel;
}

void BookmarkManager::removeItem(const QModelIndex &index)
{
    TRACE_OBJ
    QModelIndex current = index;
    if (typeAndSearch) { // need to map because of proxy
        current = typeAndSearchModel->mapToSource(current);
        current = bookmarkFilterModel->mapToSource(current);
    } else if (!bookmarkModel->parent(index).isValid()) {
        return;  // check if we should delete the "Bookmarks Menu", bail
    }

    if (bookmarkModel->hasChildren(current)) {
        int value = QMessageBox::question(bookmarkTreeView, tr("Remove"),
            tr("You are going to delete a Folder, this will also<br>"
            "remove it's content. Are you sure to continue?"),
            QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel);
        if (value == QMessageBox::Cancel)
            return;
    }
    bookmarkModel->removeItem(current);
}

bool BookmarkManager::eventFilter(QObject *object, QEvent *event)
{
    if (object != bookmarkTreeView && object != bookmarkTreeView->viewport()
        && object != bookmarkWidget)
            return QObject::eventFilter(object, event);

    TRACE_OBJ
    const bool isWidget = object == bookmarkWidget;
    if (event->type() == QEvent::KeyPress) {
        QKeyEvent *ke = static_cast<QKeyEvent*>(event);
        switch (ke->key()) {
            case Qt::Key_F2: {
                renameBookmark(bookmarkTreeView->currentIndex());
            }   break;

            case Qt::Key_Delete: {
                removeItem(bookmarkTreeView->currentIndex());
            }   break;

            case Qt::Key_Up: {    // needs event filter on widget
            case Qt::Key_Down:
                if (isWidget)
                    bookmarkTreeView->subclassKeyPressEvent(ke);
            }   break;

            case Qt::Key_Escape: {
                emit escapePressed();
            }   break;

            default: break;
        }
    }

    if (event->type() == QEvent::MouseButtonRelease && !isWidget) {
        QMouseEvent *me = static_cast<QMouseEvent*>(event);
        switch (me->button()) {
            case Qt::LeftButton: {
                if (me->modifiers() & Qt::ControlModifier)
                    setSourceFromIndex(bookmarkTreeView->currentIndex(), true);
            }   break;

            case Qt::MidButton: {
                setSourceFromIndex(bookmarkTreeView->currentIndex(), true);
            }   break;

            default: break;
        }
    }

    return QObject::eventFilter(object, event);
}

void BookmarkManager::buildBookmarksMenu(const QModelIndex &index, QMenu* menu)
{
    TRACE_OBJ
    if (!index.isValid())
        return;

    const QString &text = index.data().toString();
    const QIcon &icon = qVariantValue<QIcon>(index.data(Qt::DecorationRole));
    if (index.data(UserRoleFolder).toBool()) {
        if (QMenu* subMenu = menu->addMenu(icon, text)) {
            for (int i = 0; i < bookmarkModel->rowCount(index); ++i)
                buildBookmarksMenu(bookmarkModel->index(i, 0, index), subMenu);
        }
    } else {
        QAction *action = menu->addAction(icon, text);
        action->setData(index.data(UserRoleUrl).toString());
    }
}

void BookmarkManager::showBookmarkDialog(const QString &name, const QString &url)
{
    TRACE_OBJ
    BookmarkDialog dialog(bookmarkModel, name, url, bookmarkTreeView);
    dialog.exec();
}

// -- private slots

void BookmarkManager::setupFinished()
{
    TRACE_OBJ
    bookmarkModel->setBookmarks(HelpEngineWrapper::instance().bookmarks());
    bookmarkModel->expandFoldersIfNeeeded(bookmarkTreeView);

    refeshBookmarkMenu();

    bookmarkTreeView->hideColumn(1);
    bookmarkTreeView->header()->setVisible(false);
    bookmarkTreeView->header()->setStretchLastSection(true);

    bookmarkFilterModel = new BookmarkFilterModel(this);
    bookmarkFilterModel->setSourceModel(bookmarkModel);
    bookmarkFilterModel->filterBookmarkFolders();

    typeAndSearchModel = new QSortFilterProxyModel(this);
    typeAndSearchModel->setDynamicSortFilter(true);
    typeAndSearchModel->setSourceModel(bookmarkFilterModel);
}

void BookmarkManager::addBookmark()
{
    TRACE_OBJ
    if (CentralWidget *widget = CentralWidget::instance())
        addBookmark(widget->currentTitle(), widget->currentSource().toString());
}

void BookmarkManager::removeBookmark()
{
    TRACE_OBJ
    removeItem(bookmarkTreeView->currentIndex());
}

//void BookmarkManager::manageBookmarks()
//{
//    TRACE_OBJ
//}

void BookmarkManager::refeshBookmarkMenu()
{
    TRACE_OBJ
    if (!bookmarkMenu)
        return;

    bookmarkMenu->clear();

    //bookmarkMenu->addAction(tr("Manage Bookmarks..."), this,
    //    SLOT(manageBookmarks()));
    bookmarkMenu->addAction(tr("Import..."), this, SLOT(importBookmarks()));
    bookmarkMenu->addAction(tr("Export..."), this, SLOT(exportBookmarks()));
    bookmarkMenu->addAction(tr("Add Bookmark..."), this, SLOT(addBookmark()),
        QKeySequence(tr("Ctrl+D")));
    bookmarkMenu->addSeparator();

    const QModelIndex &root = bookmarkModel->index(0, 0, QModelIndex());
    for (int i = 0; i < bookmarkModel->rowCount(root); ++i)
        buildBookmarksMenu(bookmarkModel->index(i, 0, root), bookmarkMenu);

    connect(bookmarkMenu, SIGNAL(triggered(QAction*)), this,
        SLOT(setSourceFromAction(QAction*)));
}

void BookmarkManager::renameBookmark(const QModelIndex &index)
{
    // check if we should rename the "Bookmarks Menu", bail
    if (!typeAndSearch && !bookmarkModel->parent(index).isValid())
        return;

    bookmarkModel->setItemsEditable(true);
    bookmarkTreeView->edit(index);
    bookmarkModel->setItemsEditable(false);
}

void BookmarkManager::importBookmarks()
{
    TRACE_OBJ
    const QString &fileName = QFileDialog::getOpenFileName(0, tr("Open File"),
        QDir::currentPath(), tr("Files (*.xbel)"));

    if (fileName.isEmpty())
        return;

    QFile file(fileName);
    if (file.open(QIODevice::ReadOnly)) {
        XbelReader reader(bookmarkModel);
        reader.readFromFile(&file);
    }
}

void BookmarkManager::exportBookmarks()
{
    TRACE_OBJ
    QString fileName = QFileDialog::getSaveFileName(0, tr("Save File"),
        QLatin1String("untitled.xbel"), tr("Files (*.xbel)"));

    const QLatin1String suffix(".xbel");
    if (!fileName.endsWith(suffix))
        fileName.append(suffix);

    QFile file(fileName);
    if (file.open(QIODevice::WriteOnly)) {
        XbelWriter writer(bookmarkModel);
        writer.writeToFile(&file);
    } else {
        QMessageBox::information(bookmarkTreeView, tr("Qt Assistant"),
            tr("Unable to save bookmarks."), tr("OK"));
    }
}

void BookmarkManager::setSourceFromAction(QAction *action)
{
    TRACE_OBJ
    const QVariant &data = action->data();

    if (data.canConvert<QUrl>())
        emit setSource(data.toUrl());
}

void BookmarkManager::setSourceFromIndex(const QModelIndex &index, bool newTab)
{
    TRACE_OBJ
    QAbstractItemModel *base = bookmarkModel;
    if (typeAndSearch)
        base = typeAndSearchModel;

    if (base->data(index, UserRoleFolder).toBool())
        return;

    const QVariant &data = base->data(index, UserRoleUrl);
    if (data.canConvert<QUrl>()) {
        if (newTab)
            emit setSourceInNewTab(data.toUrl());
        else
            emit setSource(data.toUrl());
    }
}

void BookmarkManager::customContextMenuRequested(const QPoint &point)
{
    TRACE_OBJ
    QModelIndex index = bookmarkTreeView->indexAt(point);
    if (!index.isValid())
        return;

    // check if we should open the menu on "Bookmarks Menu", bail
    if (!typeAndSearch && !bookmarkModel->parent(index).isValid())
        return;

    QAction *remove = 0;
    QAction *rename = 0;
    QAction *showItem = 0;
    QAction *showItemInNewTab = 0;

    QMenu menu(QLatin1String(""));
    if (!typeAndSearch && bookmarkModel->data(index, UserRoleFolder).toBool()) {
        remove = menu.addAction(tr("Delete Folder"));
        rename = menu.addAction(tr("Rename Folder"));
    } else {
        showItem = menu.addAction(tr("Show Bookmark"));
        showItemInNewTab = menu.addAction(tr("Show Bookmark in New Tab"));
        menu.addSeparator();
        remove = menu.addAction(tr("Delete Bookmark"));
        rename = menu.addAction(tr("Rename Bookmark"));
    }

    QAction *pickedAction = menu.exec(bookmarkTreeView->mapToGlobal(point));
    if (pickedAction == rename)
        renameBookmark(index);
    else if (pickedAction == remove)
        removeItem(index);
    else if (pickedAction == showItem || pickedAction == showItemInNewTab)
        setSourceFromIndex(index, pickedAction == showItemInNewTab);
}

void BookmarkManager::focusInEvent()
{
    TRACE_OBJ
    const QModelIndex &index = bookmarkTreeView->indexAt(QPoint(2, 2));
    if (index.isValid())
        bookmarkTreeView->setCurrentIndex(index);
}

void BookmarkManager::textChanged(const QString &text)
{
    TRACE_OBJ
    if (!bookmarkWidget->ui.lineEdit->text().isEmpty()) {
        if (!typeAndSearch) {
            typeAndSearch = true;
            bookmarkTreeView->setItemsExpandable(false);
            bookmarkTreeView->setRootIsDecorated(false);
            bookmarkTreeView->setModel(typeAndSearchModel);
        }
        typeAndSearchModel->setFilterRegExp(QRegExp(text));
    } else {
        typeAndSearch = false;
        bookmarkTreeView->setModel(bookmarkModel);
        bookmarkTreeView->setItemsExpandable(true);
        bookmarkTreeView->setRootIsDecorated(true);
        bookmarkModel->expandFoldersIfNeeeded(bookmarkTreeView);
    }
}

QT_END_NAMESPACE
<|endoftext|>


<commit_before>#include <boost/program_options.hpp>
#include <boost/filesystem.hpp>
#include <primesense_slam/primesense_slam.h>

using namespace std;
using namespace g2o;
using namespace clams;

int main(int argc, char** argv)
{
  namespace bpo = boost::program_options;
  namespace bfs = boost::filesystem;
  bpo::options_description opts_desc("Allowed options");
  bpo::positional_options_description p;

  string workspace;
  opts_desc.add_options()
    ("help,h", "produce help message")
    ("workspace", bpo::value(&workspace)->required(), "CLAMS workspace.")
    ("max-loopclosures", bpo::value<int>())
    ;

  p.add("workspace", 1);

  bpo::variables_map opts;
  bpo::store(bpo::command_line_parser(argc, argv).options(opts_desc).positional(p).run(), opts);
  bool badargs = false;
  try { bpo::notify(opts); }
  catch(...) { badargs = true; }
  if(opts.count("help") || badargs) {
    cout << "Usage: " << bfs::basename(argv[0]) << " [OPTS] CLAMS_WORKSPACE" << endl;
    cout << "  This program will run slam on all sequences in CLAMS_WORKSPACE/sequences/." << endl;
    cout << "  Results will be placed in CLAMS_WORKSPACE/slam_results/." << endl;
    cout << endl;
    cout << opts_desc << endl;
    return 1;
  }

  string sequences_path = workspace + "/sequences";
  string results_path = workspace + "/slam_results";
  if(!bfs::exists(results_path))
    bfs::create_directory(results_path);

  // -- Get sequence names in order.
  vector<string> sseq_names;
  bfs::directory_iterator it(sequences_path), eod;
  BOOST_FOREACH(const bfs::path& p, make_pair(it, eod)) {
    string sseq_path = sequences_path + "/" + p.leaf().string();
    if(bfs::is_directory(sseq_path))
      sseq_names.push_back(p.leaf().string());
  }
  sort(sseq_names.begin(), sseq_names.end());

  for(size_t i = 0; i < sseq_names.size(); ++i) {
    string sseq_path = sequences_path + "/" + sseq_names[i];
    string output_dir = results_path + "/" + sseq_names[i];

    cout << endl;
    cout << "Running slam on sequence in " << sseq_path << endl;
    cout << "Saving output in " << output_dir << endl;

    if(!bfs::exists(output_dir))
      boost::filesystem::create_directory(output_dir);

    StreamSequenceBase::Ptr sseq = StreamSequenceBase::initializeFromDirectory(sseq_path);

    PrimeSenseSlam pss;
    if(opts.count("max-loopclosures")) {
      pss.max_loopclosures_ = opts["max-loopclosures"].as<int>();
      cout << "Using " << pss.max_loopclosures_ << " loop closures per frame maximum." << endl;
    }
    pss.sseq_ = sseq;

    // -- Run slam.
    ThreadPtr slamthread = pss.launch();
    slamthread->join();

    pss.pgs_->save(output_dir + "/graph");

    // -- Find the largest subgraph.
    vector< pair<size_t, size_t> > index;
    for(size_t i = 0; i < pss.trajs_.size(); i++)
      index.push_back(pair<size_t, size_t>(pss.trajs_[i].numValid(), i));
    sort(index.begin(), index.end(), std::greater< pair<size_t, size_t> >());  // Descending sort.

    // -- Save outputs in order of size.
    for(size_t i = 0; i < index.size(); ++i) {
      size_t idx = index[i].second;
      ostringstream oss;
      oss << output_dir << "/traj_" << i << ".traj";
      pss.trajs_[idx].save(oss.str());
    }

    for(size_t i = 0; i < index.size(); ++i) {
      size_t idx = index[i].second;
      ostringstream oss;
      oss << output_dir << "/map_" << i << ".pcd";
      pcl::io::savePCDFileBinary(oss.str(), *pss.maps_[idx]);
    }
  }

  return 0;
}
<commit_msg>Making slam program skip sequences for which trajectories already exist.<commit_after>#include <boost/program_options.hpp>
#include <boost/filesystem.hpp>
#include <primesense_slam/primesense_slam.h>

using namespace std;
using namespace g2o;
using namespace clams;

int main(int argc, char** argv)
{
  namespace bpo = boost::program_options;
  namespace bfs = boost::filesystem;
  bpo::options_description opts_desc("Allowed options");
  bpo::positional_options_description p;

  string workspace;
  opts_desc.add_options()
    ("help,h", "produce help message")
    ("workspace", bpo::value(&workspace)->required(), "CLAMS workspace.")
    ("max-loopclosures", bpo::value<int>())
    ;

  p.add("workspace", 1);

  bpo::variables_map opts;
  bpo::store(bpo::command_line_parser(argc, argv).options(opts_desc).positional(p).run(), opts);
  bool badargs = false;
  try { bpo::notify(opts); }
  catch(...) { badargs = true; }
  if(opts.count("help") || badargs) {
    cout << "Usage: " << bfs::basename(argv[0]) << " [OPTS] CLAMS_WORKSPACE" << endl;
    cout << "  This program will run slam on all sequences in CLAMS_WORKSPACE/sequences/." << endl;
    cout << "  Results will be placed in CLAMS_WORKSPACE/slam_results/." << endl;
    cout << endl;
    cout << opts_desc << endl;
    return 1;
  }

  string sequences_path = workspace + "/sequences";
  string results_path = workspace + "/slam_results";
  if(!bfs::exists(results_path))
    bfs::create_directory(results_path);

  // -- Get sequence names in order.
  vector<string> sseq_names;
  bfs::directory_iterator it(sequences_path), eod;
  BOOST_FOREACH(const bfs::path& p, make_pair(it, eod)) {
    string sseq_path = sequences_path + "/" + p.leaf().string();
    if(bfs::is_directory(sseq_path))
      sseq_names.push_back(p.leaf().string());
  }
  sort(sseq_names.begin(), sseq_names.end());

  cout << endl;
  cout << endl;
  for(size_t i = 0; i < sseq_names.size(); ++i) {
    string sseq_path = sequences_path + "/" + sseq_names[i];
    string output_dir = results_path + "/" + sseq_names[i];

    if(bfs::exists(output_dir + "/traj_0.traj")) {
      cout << "Output trajectory " << output_dir << "/traj_0.traj already exists.  "
           << "Skipping this sequence." << endl;
      continue;
    }

    bfs::create_directory(output_dir);
    cout << endl;
    cout << "Running slam on sequence in " << sseq_path << endl;
    cout << "Saving output in " << output_dir << endl;

    StreamSequenceBase::Ptr sseq = StreamSequenceBase::initializeFromDirectory(sseq_path);
    PrimeSenseSlam pss;
    if(opts.count("max-loopclosures")) {
      pss.max_loopclosures_ = opts["max-loopclosures"].as<int>();
      cout << "Using " << pss.max_loopclosures_ << " loop closures per frame maximum." << endl;
    }
    pss.sseq_ = sseq;

    // -- Run slam.
    ThreadPtr slamthread = pss.launch();
    slamthread->join();

    pss.pgs_->save(output_dir + "/graph");

    // -- Find the largest subgraph.
    vector< pair<size_t, size_t> > index;
    for(size_t i = 0; i < pss.trajs_.size(); i++)
      index.push_back(pair<size_t, size_t>(pss.trajs_[i].numValid(), i));
    sort(index.begin(), index.end(), std::greater< pair<size_t, size_t> >());  // Descending sort.

    // -- Save outputs in order of size.
    for(size_t i = 0; i < index.size(); ++i) {
      size_t idx = index[i].second;
      ostringstream oss;
      oss << output_dir << "/traj_" << i << ".traj";
      pss.trajs_[idx].save(oss.str());
    }

    for(size_t i = 0; i < index.size(); ++i) {
      size_t idx = index[i].second;
      ostringstream oss;
      oss << output_dir << "/map_" << i << ".pcd";
      pcl::io::savePCDFileBinary(oss.str(), *pss.maps_[idx]);
    }
  }

  return 0;
}
<|endoftext|>


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

/*
 * Test extended attribute functionality related to the 'CreateAsDeleted'
 * doc flag.
 *
 * Initial use-case for this doc flag is Transactions.
 */

#include "testapp_xattr.h"

using namespace mcbp::subdoc;
using namespace cb::mcbp;

// Negative test: The subdoc operation returns Einval as
// doc_flag::CreateAsDeleted requires one of doc_flag::Mkdoc/Add.
void XattrNoDocTest::testRequiresMkdocOrAdd() {
    auto resp = subdoc(cb::mcbp::ClientOpcode::SubdocDictAdd,
                       name,
                       "txn.deleted",
                       "true",
                       SUBDOC_FLAG_XATTR_PATH | SUBDOC_FLAG_MKDIR_P,
                       doc_flag::CreateAsDeleted,
                       durReqs);
    EXPECT_EQ(cb::mcbp::Status::Einval, resp.getStatus());
}

TEST_P(XattrNoDocTest, RequiresMkdocOrAdd) {
    testRequiresMkdocOrAdd();
}

TEST_P(XattrNoDocDurabilityTest, RequiresMkdocOrAdd) {
    testRequiresMkdocOrAdd();
}

// Negative test: The Subdoc CreateAsDeleted doesn't allow to write in the
// body.
void XattrNoDocTest::testRequiresXattrPath() {
    // Note: subdoc-flags doesn't set SUBDOC_FLAG_XATTR_PATH
    auto resp = subdoc(cb::mcbp::ClientOpcode::SubdocDictAdd,
                       name,
                       "txn.deleted",
                       "true",
                       SUBDOC_FLAG_MKDIR_P,
                       doc_flag::Mkdoc | doc_flag::CreateAsDeleted);
    EXPECT_EQ(cb::mcbp::Status::Einval, resp.getStatus());
}

TEST_P(XattrNoDocTest, RequiresXattrPath) {
    testRequiresXattrPath();
}

TEST_P(XattrNoDocDurabilityTest, RequiresXattrPath) {
    testRequiresXattrPath();
}

// Positive test: Can User XAttrs be added to a document which doesn't exist
// (and doesn't have a tombstone) using the new CreateAsDeleted flag.
void XattrNoDocTest::testSinglePathDictAdd() {
    if (durReqs && !supportSyncRepl()) {
        return;
    }

    // let's add a user XATTR to a non-existing document
    auto resp = subdoc(cb::mcbp::ClientOpcode::SubdocDictAdd,
                       name,
                       "txn.deleted",
                       "true",
                       SUBDOC_FLAG_XATTR_PATH | SUBDOC_FLAG_MKDIR_P,
                       doc_flag::Mkdoc | doc_flag::CreateAsDeleted,
                       durReqs);
    EXPECT_EQ(cb::mcbp::Status::SubdocSuccessDeleted, resp.getStatus());

    resp = subdoc_get(
            "txn.deleted", SUBDOC_FLAG_XATTR_PATH, doc_flag::AccessDeleted);
    ASSERT_EQ(cb::mcbp::Status::SubdocSuccessDeleted, resp.getStatus());
    EXPECT_EQ("true", resp.getValue());
}

TEST_P(XattrNoDocTest, SinglePathDictAdd) {
    testSinglePathDictAdd();
}

TEST_P(XattrNoDocDurabilityTest, SinglePathDictAdd) {
    testSinglePathDictAdd();
}

// Positive tests: Can User XAttrs be added to a document which doesn't exist
// (and doesn't have a tombstone) using the new CreateAsDeleted flag, using
// multi-mutation with each mutation opcode.
void XattrNoDocTest::testMultipathDictAdd() {
    if (durReqs && !supportSyncRepl()) {
        return;
    }

    BinprotSubdocMultiMutationCommand cmd(
            name,
            {{ClientOpcode::SubdocDictAdd,
              SUBDOC_FLAG_XATTR_PATH,
              "txn",
              "\"foo\""}},
            doc_flag::Mkdoc | doc_flag::CreateAsDeleted,
            durReqs);

    auto resp = subdocMultiMutation(cmd);
    EXPECT_EQ(cb::mcbp::Status::SubdocSuccessDeleted, resp.getStatus());

    // Check the last path was created correctly.
    resp = subdoc_get("txn", SUBDOC_FLAG_XATTR_PATH, doc_flag::AccessDeleted);
    ASSERT_EQ(cb::mcbp::Status::SubdocSuccessDeleted, resp.getStatus());
    EXPECT_EQ("\"foo\"", resp.getValue());
}

TEST_P(XattrNoDocTest, MultipathDictAdd) {
    testMultipathDictAdd();
}

TEST_P(XattrNoDocDurabilityTest, MultipathDictAdd) {
    testMultipathDictAdd();
}

void XattrNoDocTest::testMultipathDictUpsert() {
    if (durReqs && !supportSyncRepl()) {
        return;
    }

    BinprotSubdocMultiMutationCommand cmd(
            name,
            {{ClientOpcode::SubdocDictUpsert,
              SUBDOC_FLAG_XATTR_PATH,
              "txn",
              "\"bar\""}},
            doc_flag::Mkdoc | doc_flag::CreateAsDeleted,
            durReqs);

    auto resp = subdocMultiMutation(cmd);
    EXPECT_EQ(cb::mcbp::Status::SubdocSuccessDeleted, resp.getStatus());

    resp = subdoc_get("txn", SUBDOC_FLAG_XATTR_PATH, doc_flag::AccessDeleted);
    ASSERT_EQ(cb::mcbp::Status::SubdocSuccessDeleted, resp.getStatus());
    EXPECT_EQ("\"bar\"", resp.getValue());
}

TEST_P(XattrNoDocTest, MultipathDictUpsert) {
    testMultipathDictUpsert();
}

TEST_P(XattrNoDocDurabilityTest, MultipathDictUpsert) {
    testMultipathDictUpsert();
}

void XattrNoDocTest::testMultipathArrayPushLast() {
    if (durReqs && !supportSyncRepl()) {
        return;
    }

    BinprotSubdocMultiMutationCommand cmd(
            name,
            {{ClientOpcode::SubdocArrayPushLast,
              SUBDOC_FLAG_XATTR_PATH,
              "array",
              "1"}},
            doc_flag::Mkdoc | doc_flag::CreateAsDeleted,
            durReqs);

    auto resp = subdocMultiMutation(cmd);
    EXPECT_EQ(cb::mcbp::Status::SubdocSuccessDeleted, resp.getStatus());

    resp = subdoc_get("array", SUBDOC_FLAG_XATTR_PATH, doc_flag::AccessDeleted);
    ASSERT_EQ(cb::mcbp::Status::SubdocSuccessDeleted, resp.getStatus());
    EXPECT_EQ("[1]", resp.getValue());
}

TEST_P(XattrNoDocTest, MultipathArrayPushLast) {
    testMultipathArrayPushLast();
}

TEST_P(XattrNoDocDurabilityTest, MultipathArrayPushLast) {
    testMultipathArrayPushLast();
}

void XattrNoDocTest::testMultipathArrayPushFirst() {
    if (durReqs && !supportSyncRepl()) {
        return;
    }

    BinprotSubdocMultiMutationCommand cmd(
            name,
            {{ClientOpcode::SubdocArrayPushFirst,
              SUBDOC_FLAG_XATTR_PATH,
              "array",
              "2"}},
            doc_flag::Mkdoc | doc_flag::CreateAsDeleted,
            durReqs);

    auto resp = subdocMultiMutation(cmd);
    EXPECT_EQ(cb::mcbp::Status::SubdocSuccessDeleted, resp.getStatus());

    resp = subdoc_get("array", SUBDOC_FLAG_XATTR_PATH, doc_flag::AccessDeleted);
    ASSERT_EQ(cb::mcbp::Status::SubdocSuccessDeleted, resp.getStatus());
    EXPECT_EQ("[2]", resp.getValue());
}

TEST_P(XattrNoDocTest, MultipathArrayPushFirst) {
    testMultipathArrayPushFirst();
}

TEST_P(XattrNoDocDurabilityTest, MultipathArrayPushFirst) {
    testMultipathArrayPushFirst();
}

// @todo MB-39545: This test fails because doc_flag::Mkdoc is invalid with
//  ArrayInsert (the test fails at validation for ArrayInsert with "Invalid
//  arguments").
//  By changing to doc_flag::Add (valid for ArrayInsert) the test gets to the
//  execution of ArrayInsert but is fails with SubdocMultiPathFailure.
//  Specific and simple (single-path) tests on ArrayInsert suggests that we may
//  have some existing issue in that area that we should address in a dedicated
//  patch.
TEST_P(XattrNoDocTest, DISABLED_MultipathArrayInsert) {
    BinprotSubdocMultiMutationCommand cmd(
            name,
            {{ClientOpcode::SubdocArrayPushFirst,
              SUBDOC_FLAG_XATTR_PATH,
              "array",
              "0"},
             {ClientOpcode::SubdocArrayInsert,
              SUBDOC_FLAG_XATTR_PATH,
              "array.[0]",
              "1"}},
            doc_flag::Mkdoc | doc_flag::CreateAsDeleted,
            durReqs);

    auto resp = subdocMultiMutation(cmd);
    EXPECT_EQ(cb::mcbp::Status::SubdocSuccessDeleted, resp.getStatus());

    resp = subdoc_get("array", SUBDOC_FLAG_XATTR_PATH, doc_flag::AccessDeleted);
    ASSERT_EQ(cb::mcbp::Status::SubdocSuccessDeleted, resp.getStatus());
    EXPECT_EQ("[0,1]", resp.getValue());
}

void XattrNoDocTest::testMultipathArrayAddUnique() {
    if (durReqs && !supportSyncRepl()) {
        return;
    }

    BinprotSubdocMultiMutationCommand cmd(
            name,
            {{ClientOpcode::SubdocArrayAddUnique,
              SUBDOC_FLAG_XATTR_PATH,
              "array",
              "4"}},
            doc_flag::Mkdoc | doc_flag::CreateAsDeleted,
            durReqs);

    auto resp = subdocMultiMutation(cmd);
    EXPECT_EQ(cb::mcbp::Status::SubdocSuccessDeleted, resp.getStatus());

    resp = subdoc_get("array", SUBDOC_FLAG_XATTR_PATH, doc_flag::AccessDeleted);
    ASSERT_EQ(cb::mcbp::Status::SubdocSuccessDeleted, resp.getStatus());
    EXPECT_EQ("[4]", resp.getValue());
}

TEST_P(XattrNoDocTest, MultipathArrayAddUnique) {
    testMultipathArrayAddUnique();
}

TEST_P(XattrNoDocDurabilityTest, MultipathArrayAddUnique) {
    testMultipathArrayAddUnique();
}

void XattrNoDocTest::testMultipathCounter() {
    if (durReqs && !supportSyncRepl()) {
        return;
    }

    BinprotSubdocMultiMutationCommand cmd(
            name,
            {{ClientOpcode::SubdocCounter,
              SUBDOC_FLAG_XATTR_PATH,
              "counter",
              "5"}},
            doc_flag::Mkdoc | doc_flag::CreateAsDeleted,
            durReqs);

    auto resp = subdocMultiMutation(cmd);
    EXPECT_EQ(cb::mcbp::Status::SubdocSuccessDeleted, resp.getStatus());

    // Check the last path was created correctly.
    resp = subdoc_get(
            "counter", SUBDOC_FLAG_XATTR_PATH, doc_flag::AccessDeleted);
    ASSERT_EQ(cb::mcbp::Status::SubdocSuccessDeleted, resp.getStatus());
    EXPECT_EQ("5", resp.getValue());
}

TEST_P(XattrNoDocTest, MultipathCounter) {
    testMultipathCounter();
}

TEST_P(XattrNoDocDurabilityTest, MultipathCounter) {
    testMultipathCounter();
}

// Positive test: Can User XAttrs be added to a document which doesn't exist
// (and doesn't have a tombstone) using the new CreateAsDeleted flag, using
// a combination of subdoc-multi mutation types.
void XattrNoDocTest::testMultipathCombo() {
    if (durReqs && !supportSyncRepl()) {
        return;
    }

    BinprotSubdocMultiMutationCommand cmd;
    cmd.setKey(name);
    cmd.addMutation(
            ClientOpcode::SubdocDictAdd, SUBDOC_FLAG_XATTR_PATH, "txn", "{}");
    cmd.addMutation(ClientOpcode::SubdocDictUpsert,
                    SUBDOC_FLAG_XATTR_PATH,
                    "txn.id",
                    "2");
    cmd.addMutation(ClientOpcode::SubdocArrayPushLast,
                    SUBDOC_FLAG_XATTR_PATH,
                    "txn.array",
                    "2");
    cmd.addMutation(ClientOpcode::SubdocArrayPushFirst,
                    SUBDOC_FLAG_XATTR_PATH,
                    "txn.array",
                    "0");
    cmd.addMutation(ClientOpcode::SubdocArrayAddUnique,
                    SUBDOC_FLAG_XATTR_PATH,
                    "txn.array",
                    "3");
    cmd.addMutation(ClientOpcode::SubdocCounter,
                    SUBDOC_FLAG_XATTR_PATH,
                    "txn.counter",
                    "1");

    cmd.addDocFlag(doc_flag::Mkdoc);
    cmd.addDocFlag(doc_flag::CreateAsDeleted);

    if (durReqs) {
        cmd.setDurabilityReqs(*durReqs);
    }

    auto& conn = getConnection();
    conn.sendCommand(cmd);
    BinprotSubdocResponse resp;
    conn.recvResponse(resp);
    EXPECT_EQ(cb::mcbp::Status::SubdocSuccessDeleted, resp.getStatus());

    // Check the last path was created correctly.
    resp = subdoc_get(
            "txn.counter", SUBDOC_FLAG_XATTR_PATH, doc_flag::AccessDeleted);
    ASSERT_EQ(cb::mcbp::Status::SubdocSuccessDeleted, resp.getStatus());
    EXPECT_EQ("1", resp.getValue());
}

TEST_P(XattrNoDocTest, MultipathCombo) {
    testMultipathCombo();
}

TEST_P(XattrNoDocDurabilityTest, MultipathCombo) {
    testMultipathCombo();
}<commit_msg>MB-37374: XattrNoDocTest: Make explicit when tests are not supported<commit_after>/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/*
 *     Copyright 2020 Couchbase, Inc
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 */

/*
 * Test extended attribute functionality related to the 'CreateAsDeleted'
 * doc flag.
 *
 * Initial use-case for this doc flag is Transactions.
 */

#include "testapp_xattr.h"

using namespace mcbp::subdoc;
using namespace cb::mcbp;

// Naive backport of GTEST_SKIP macro from v1.10. Remove once this is merged
// to master branch (where we have gtest 1.10).
#define GTEST_SKIP(message)                              \
    do {                                                 \
        fprintf(stderr, "Skipping test: " message "\n"); \
        return;                                          \
    } while (false)

// Negative test: The subdoc operation returns Einval as
// doc_flag::CreateAsDeleted requires one of doc_flag::Mkdoc/Add.
void XattrNoDocTest::testRequiresMkdocOrAdd() {
    auto resp = subdoc(cb::mcbp::ClientOpcode::SubdocDictAdd,
                       name,
                       "txn.deleted",
                       "true",
                       SUBDOC_FLAG_XATTR_PATH | SUBDOC_FLAG_MKDIR_P,
                       doc_flag::CreateAsDeleted,
                       durReqs);
    EXPECT_EQ(cb::mcbp::Status::Einval, resp.getStatus());
}

TEST_P(XattrNoDocTest, RequiresMkdocOrAdd) {
    testRequiresMkdocOrAdd();
}

TEST_P(XattrNoDocDurabilityTest, RequiresMkdocOrAdd) {
    testRequiresMkdocOrAdd();
}

// Negative test: The Subdoc CreateAsDeleted doesn't allow to write in the
// body.
void XattrNoDocTest::testRequiresXattrPath() {
    // Note: subdoc-flags doesn't set SUBDOC_FLAG_XATTR_PATH
    auto resp = subdoc(cb::mcbp::ClientOpcode::SubdocDictAdd,
                       name,
                       "txn.deleted",
                       "true",
                       SUBDOC_FLAG_MKDIR_P,
                       doc_flag::Mkdoc | doc_flag::CreateAsDeleted);
    EXPECT_EQ(cb::mcbp::Status::Einval, resp.getStatus());
}

TEST_P(XattrNoDocTest, RequiresXattrPath) {
    testRequiresXattrPath();
}

TEST_P(XattrNoDocDurabilityTest, RequiresXattrPath) {
    testRequiresXattrPath();
}

// Positive test: Can User XAttrs be added to a document which doesn't exist
// (and doesn't have a tombstone) using the new CreateAsDeleted flag.
void XattrNoDocTest::testSinglePathDictAdd() {
    if (durReqs && !supportSyncRepl()) {
        GTEST_SKIP("Requires SyncReplication support");
    }

    // let's add a user XATTR to a non-existing document
    auto resp = subdoc(cb::mcbp::ClientOpcode::SubdocDictAdd,
                       name,
                       "txn.deleted",
                       "true",
                       SUBDOC_FLAG_XATTR_PATH | SUBDOC_FLAG_MKDIR_P,
                       doc_flag::Mkdoc | doc_flag::CreateAsDeleted,
                       durReqs);
    EXPECT_EQ(cb::mcbp::Status::SubdocSuccessDeleted, resp.getStatus());

    resp = subdoc_get(
            "txn.deleted", SUBDOC_FLAG_XATTR_PATH, doc_flag::AccessDeleted);
    ASSERT_EQ(cb::mcbp::Status::SubdocSuccessDeleted, resp.getStatus());
    EXPECT_EQ("true", resp.getValue());
}

TEST_P(XattrNoDocTest, SinglePathDictAdd) {
    testSinglePathDictAdd();
}

TEST_P(XattrNoDocDurabilityTest, SinglePathDictAdd) {
    testSinglePathDictAdd();
}

// Positive tests: Can User XAttrs be added to a document which doesn't exist
// (and doesn't have a tombstone) using the new CreateAsDeleted flag, using
// multi-mutation with each mutation opcode.
void XattrNoDocTest::testMultipathDictAdd() {
    if (durReqs && !supportSyncRepl()) {
        GTEST_SKIP("Requires SyncReplication support");
    }

    BinprotSubdocMultiMutationCommand cmd(
            name,
            {{ClientOpcode::SubdocDictAdd,
              SUBDOC_FLAG_XATTR_PATH,
              "txn",
              "\"foo\""}},
            doc_flag::Mkdoc | doc_flag::CreateAsDeleted,
            durReqs);

    auto resp = subdocMultiMutation(cmd);
    EXPECT_EQ(cb::mcbp::Status::SubdocSuccessDeleted, resp.getStatus());

    // Check the last path was created correctly.
    resp = subdoc_get("txn", SUBDOC_FLAG_XATTR_PATH, doc_flag::AccessDeleted);
    ASSERT_EQ(cb::mcbp::Status::SubdocSuccessDeleted, resp.getStatus());
    EXPECT_EQ("\"foo\"", resp.getValue());
}

TEST_P(XattrNoDocTest, MultipathDictAdd) {
    testMultipathDictAdd();
}

TEST_P(XattrNoDocDurabilityTest, MultipathDictAdd) {
    testMultipathDictAdd();
}

void XattrNoDocTest::testMultipathDictUpsert() {
    if (durReqs && !supportSyncRepl()) {
        GTEST_SKIP("Requires SyncReplication support");
    }

    BinprotSubdocMultiMutationCommand cmd(
            name,
            {{ClientOpcode::SubdocDictUpsert,
              SUBDOC_FLAG_XATTR_PATH,
              "txn",
              "\"bar\""}},
            doc_flag::Mkdoc | doc_flag::CreateAsDeleted,
            durReqs);

    auto resp = subdocMultiMutation(cmd);
    EXPECT_EQ(cb::mcbp::Status::SubdocSuccessDeleted, resp.getStatus());

    resp = subdoc_get("txn", SUBDOC_FLAG_XATTR_PATH, doc_flag::AccessDeleted);
    ASSERT_EQ(cb::mcbp::Status::SubdocSuccessDeleted, resp.getStatus());
    EXPECT_EQ("\"bar\"", resp.getValue());
}

TEST_P(XattrNoDocTest, MultipathDictUpsert) {
    testMultipathDictUpsert();
}

TEST_P(XattrNoDocDurabilityTest, MultipathDictUpsert) {
    testMultipathDictUpsert();
}

void XattrNoDocTest::testMultipathArrayPushLast() {
    if (durReqs && !supportSyncRepl()) {
        GTEST_SKIP("Requires SyncReplication support");
    }

    BinprotSubdocMultiMutationCommand cmd(
            name,
            {{ClientOpcode::SubdocArrayPushLast,
              SUBDOC_FLAG_XATTR_PATH,
              "array",
              "1"}},
            doc_flag::Mkdoc | doc_flag::CreateAsDeleted,
            durReqs);

    auto resp = subdocMultiMutation(cmd);
    EXPECT_EQ(cb::mcbp::Status::SubdocSuccessDeleted, resp.getStatus());

    resp = subdoc_get("array", SUBDOC_FLAG_XATTR_PATH, doc_flag::AccessDeleted);
    ASSERT_EQ(cb::mcbp::Status::SubdocSuccessDeleted, resp.getStatus());
    EXPECT_EQ("[1]", resp.getValue());
}

TEST_P(XattrNoDocTest, MultipathArrayPushLast) {
    testMultipathArrayPushLast();
}

TEST_P(XattrNoDocDurabilityTest, MultipathArrayPushLast) {
    testMultipathArrayPushLast();
}

void XattrNoDocTest::testMultipathArrayPushFirst() {
    if (durReqs && !supportSyncRepl()) {
        GTEST_SKIP("Requires SyncReplication support");
    }

    BinprotSubdocMultiMutationCommand cmd(
            name,
            {{ClientOpcode::SubdocArrayPushFirst,
              SUBDOC_FLAG_XATTR_PATH,
              "array",
              "2"}},
            doc_flag::Mkdoc | doc_flag::CreateAsDeleted,
            durReqs);

    auto resp = subdocMultiMutation(cmd);
    EXPECT_EQ(cb::mcbp::Status::SubdocSuccessDeleted, resp.getStatus());

    resp = subdoc_get("array", SUBDOC_FLAG_XATTR_PATH, doc_flag::AccessDeleted);
    ASSERT_EQ(cb::mcbp::Status::SubdocSuccessDeleted, resp.getStatus());
    EXPECT_EQ("[2]", resp.getValue());
}

TEST_P(XattrNoDocTest, MultipathArrayPushFirst) {
    testMultipathArrayPushFirst();
}

TEST_P(XattrNoDocDurabilityTest, MultipathArrayPushFirst) {
    testMultipathArrayPushFirst();
}

// @todo MB-39545: This test fails because doc_flag::Mkdoc is invalid with
//  ArrayInsert (the test fails at validation for ArrayInsert with "Invalid
//  arguments").
//  By changing to doc_flag::Add (valid for ArrayInsert) the test gets to the
//  execution of ArrayInsert but is fails with SubdocMultiPathFailure.
//  Specific and simple (single-path) tests on ArrayInsert suggests that we may
//  have some existing issue in that area that we should address in a dedicated
//  patch.
TEST_P(XattrNoDocTest, DISABLED_MultipathArrayInsert) {
    BinprotSubdocMultiMutationCommand cmd(
            name,
            {{ClientOpcode::SubdocArrayPushFirst,
              SUBDOC_FLAG_XATTR_PATH,
              "array",
              "0"},
             {ClientOpcode::SubdocArrayInsert,
              SUBDOC_FLAG_XATTR_PATH,
              "array.[0]",
              "1"}},
            doc_flag::Mkdoc | doc_flag::CreateAsDeleted,
            durReqs);

    auto resp = subdocMultiMutation(cmd);
    EXPECT_EQ(cb::mcbp::Status::SubdocSuccessDeleted, resp.getStatus());

    resp = subdoc_get("array", SUBDOC_FLAG_XATTR_PATH, doc_flag::AccessDeleted);
    ASSERT_EQ(cb::mcbp::Status::SubdocSuccessDeleted, resp.getStatus());
    EXPECT_EQ("[0,1]", resp.getValue());
}

void XattrNoDocTest::testMultipathArrayAddUnique() {
    if (durReqs && !supportSyncRepl()) {
        GTEST_SKIP("Requires SyncReplication support");
    }

    BinprotSubdocMultiMutationCommand cmd(
            name,
            {{ClientOpcode::SubdocArrayAddUnique,
              SUBDOC_FLAG_XATTR_PATH,
              "array",
              "4"}},
            doc_flag::Mkdoc | doc_flag::CreateAsDeleted,
            durReqs);

    auto resp = subdocMultiMutation(cmd);
    EXPECT_EQ(cb::mcbp::Status::SubdocSuccessDeleted, resp.getStatus());

    resp = subdoc_get("array", SUBDOC_FLAG_XATTR_PATH, doc_flag::AccessDeleted);
    ASSERT_EQ(cb::mcbp::Status::SubdocSuccessDeleted, resp.getStatus());
    EXPECT_EQ("[4]", resp.getValue());
}

TEST_P(XattrNoDocTest, MultipathArrayAddUnique) {
    testMultipathArrayAddUnique();
}

TEST_P(XattrNoDocDurabilityTest, MultipathArrayAddUnique) {
    testMultipathArrayAddUnique();
}

void XattrNoDocTest::testMultipathCounter() {
    if (durReqs && !supportSyncRepl()) {
        GTEST_SKIP("Requires SyncReplication support");
    }

    BinprotSubdocMultiMutationCommand cmd(
            name,
            {{ClientOpcode::SubdocCounter,
              SUBDOC_FLAG_XATTR_PATH,
              "counter",
              "5"}},
            doc_flag::Mkdoc | doc_flag::CreateAsDeleted,
            durReqs);

    auto resp = subdocMultiMutation(cmd);
    EXPECT_EQ(cb::mcbp::Status::SubdocSuccessDeleted, resp.getStatus());

    // Check the last path was created correctly.
    resp = subdoc_get(
            "counter", SUBDOC_FLAG_XATTR_PATH, doc_flag::AccessDeleted);
    ASSERT_EQ(cb::mcbp::Status::SubdocSuccessDeleted, resp.getStatus());
    EXPECT_EQ("5", resp.getValue());
}

TEST_P(XattrNoDocTest, MultipathCounter) {
    testMultipathCounter();
}

TEST_P(XattrNoDocDurabilityTest, MultipathCounter) {
    testMultipathCounter();
}

// Positive test: Can User XAttrs be added to a document which doesn't exist
// using the new CreateAsDeleted flag, using a combination of subdoc-multi
// mutation types.
void XattrNoDocTest::testMultipathCombo() {
    if (durReqs && !supportSyncRepl()) {
        GTEST_SKIP("Requires SyncReplication support");
    }

    BinprotSubdocMultiMutationCommand cmd;
    cmd.setKey(name);
    cmd.addMutation(
            ClientOpcode::SubdocDictAdd, SUBDOC_FLAG_XATTR_PATH, "txn", "{}");
    cmd.addMutation(ClientOpcode::SubdocDictUpsert,
                    SUBDOC_FLAG_XATTR_PATH,
                    "txn.id",
                    "2");
    cmd.addMutation(ClientOpcode::SubdocArrayPushLast,
                    SUBDOC_FLAG_XATTR_PATH,
                    "txn.array",
                    "2");
    cmd.addMutation(ClientOpcode::SubdocArrayPushFirst,
                    SUBDOC_FLAG_XATTR_PATH,
                    "txn.array",
                    "0");
    cmd.addMutation(ClientOpcode::SubdocArrayAddUnique,
                    SUBDOC_FLAG_XATTR_PATH,
                    "txn.array",
                    "3");
    cmd.addMutation(ClientOpcode::SubdocCounter,
                    SUBDOC_FLAG_XATTR_PATH,
                    "txn.counter",
                    "1");

    cmd.addDocFlag(doc_flag::Mkdoc);
    cmd.addDocFlag(doc_flag::CreateAsDeleted);

    if (durReqs) {
        cmd.setDurabilityReqs(*durReqs);
    }

    auto& conn = getConnection();
    conn.sendCommand(cmd);
    BinprotSubdocResponse resp;
    conn.recvResponse(resp);
    EXPECT_EQ(cb::mcbp::Status::SubdocSuccessDeleted, resp.getStatus());

    // Check the last path was created correctly.
    resp = subdoc_get(
            "txn.counter", SUBDOC_FLAG_XATTR_PATH, doc_flag::AccessDeleted);
    ASSERT_EQ(cb::mcbp::Status::SubdocSuccessDeleted, resp.getStatus());
    EXPECT_EQ("1", resp.getValue());
}

TEST_P(XattrNoDocTest, MultipathCombo) {
    testMultipathCombo();
}

TEST_P(XattrNoDocDurabilityTest, MultipathCombo) {
    testMultipathCombo();
}<|endoftext|>


<commit_before>// Licensed GNU LGPL v2.1 or later: http://www.gnu.org/licenses/lgpl.html
#ifndef __BSE_MEMORY_HH__
#define __BSE_MEMORY_HH__

#include <bse/bseenums.hh>

namespace Bse {

namespace FastMemory {
/// Minimum alignment >= cache line size, see getconf LEVEL1_DCACHE_LINESIZE.
inline constexpr size_t cache_line_size = 64;
} // FastMemory

// Allocate cache-line aligned memory block from fast memory pool, MT-Safe.
void*   fast_mem_alloc  (size_t size);
// Free a memory block allocated with aligned_malloc(), MT-Safe.
void    fast_mem_free   (void *mem);

/// Array with cache-line-alignment containing a fixed numer of PODs.
template<typename T, size_t ALIGNMENT = FastMemory::cache_line_size>
class FastMemArray {
  static_assert (std::is_trivially_copyable<T>::value);
  static_assert (ALIGNMENT <= FastMemory::cache_line_size);
  static_assert ((ALIGNMENT & (ALIGNMENT - 1)) == 0);
  static_assert (alignof (T) <= ALIGNMENT);
  const size_t n_elements_ = 0;
  T     *const data_ = nullptr;
  BSE_CLASS_NON_COPYABLE (FastMemArray);
protected:
  void     range_check (size_t n) const
  {
    if (n >= n_elements_)
      throw std::out_of_range (string_format ("FastMemArray::range_check: n >= size(): %u >= %u", n, size()));
  }
public:
  FastMemArray (size_t n_elements) :
    n_elements_ (n_elements),
    data_ ((T*) fast_mem_alloc (sizeof (T) * n_elements_))
  {
    std::fill (begin(), end(), T());
  }
  FastMemArray (const vector<T>& elements) :
    n_elements_ (elements.size()),
    data_ ((T*) fast_mem_alloc (sizeof (T) * n_elements_))
  {
    std::copy (elements.begin(), elements.end(), begin());
  }
  ~FastMemArray()
  {
    static_assert (std::is_trivially_destructible<T>::value);
    fast_mem_free (data_);
  }
  T*       begin      ()                { return &data_[0]; }
  T*       end        ()                { return &data_[n_elements_]; }
  size_t   size       () const          { return n_elements_; }
  T&       operator[] (size_t n)        { return data_[n]; }
  const T& operator[] (size_t n) const  { return data_[n]; }
  T&       at         (size_t n)        { range_check (n); return data_[n]; }
  const T& at         (size_t n) const  { range_check (n); return data_[n]; }
};

namespace FastMemory {

// == NewDeleteBase ==
class NewDeleteBase {
  static constexpr const std::align_val_t __cxxalignment = std::align_val_t (__STDCPP_DEFAULT_NEW_ALIGNMENT__);
  static void    delete_  (void *ptr, std::size_t sz, std::align_val_t al);
  static void*   new_     (std::size_t sz, std::align_val_t al);
public:
  // 'static' is implicit for operator new/delete
  void* operator new      (std::size_t sz)                            { return new_ (sz, __cxxalignment); }
  void* operator new[]    (std::size_t sz)                            { return new_ (sz, __cxxalignment); }
  void* operator new      (std::size_t sz, std::align_val_t al)       { return new_ (sz, al); }
  void* operator new[]    (std::size_t sz, std::align_val_t al)       { return new_ (sz, al); }
  // variants without size_t MUST NOT be defined for the following to be used
  void  operator delete   (void *ptr, std::size_t sz)                 { delete_ (ptr, sz, __cxxalignment); }
  void  operator delete[] (void *ptr, std::size_t sz)                 { delete_ (ptr, sz, __cxxalignment); }
  void  operator delete   (void *ptr, std::size_t sz, std::align_val_t al) { delete_ (ptr, sz, al); }
  void  operator delete[] (void *ptr, std::size_t sz, std::align_val_t al) { delete_ (ptr, sz, al); }
};

/// Internal allocator handle.
struct Allocator;
using AllocatorP = std::shared_ptr<Allocator>;

/// Reference for an allocated memory block.
struct Block {
  void  *const block_start = nullptr;
  const uint32 block_length = 0;
  Block& operator= (const Block &src) { this->~Block(); new (this) Block (src); return *this; }
  /*copy*/ Block   (const Block &src) = default;
  /*dflt*/ Block   () = default;
};

/// Memory area (over-)aligned to cache size and utilizing huge pages.
struct Arena {
  /// Create isolated memory area.
  explicit Arena     (uint32 mem_size, uint32 alignment = cache_line_size);
  /// Alignment for block addresses and length.
  size_t   alignment () const;
  /// Address of memory area.
  uint64   location  () const;
  /// Reserved memory area in bytes.
  uint64   reserved  () const;
  /// Create a memory block from cache-line aligned memory area, MT-Unsafe.
  Block    allocate  (uint32 length) const;
  Block    allocate  (uint32 length, std::nothrow_t) const;
  /// Realease a previously allocated block, MT-Unsafe.
  void     release   (Block allocatedblock) const;
protected:
  AllocatorP fma; ///< Identifier for the associated memory allocator.
  explicit Arena     (AllocatorP);
};

} // FastMemory

} // Bse

#endif /* __BSE_MEMORY_HH__ */
<commit_msg>BSE: memory.hh: document FastMemory namespace<commit_after>// Licensed GNU LGPL v2.1 or later: http://www.gnu.org/licenses/lgpl.html
#ifndef __BSE_MEMORY_HH__
#define __BSE_MEMORY_HH__

#include <bse/bseenums.hh>

namespace Bse {

/// Utilities for allocating cache line aligned memory from huge pages.
namespace FastMemory {

/// Minimum alignment >= cache line size, see getconf LEVEL1_DCACHE_LINESIZE.
inline constexpr size_t cache_line_size = 64;

} // FastMemory

// Allocate cache-line aligned memory block from fast memory pool, MT-Safe.
void*   fast_mem_alloc  (size_t size);
// Free a memory block allocated with aligned_malloc(), MT-Safe.
void    fast_mem_free   (void *mem);

/// Array with cache-line-alignment containing a fixed numer of PODs.
template<typename T, size_t ALIGNMENT = FastMemory::cache_line_size>
class FastMemArray {
  static_assert (std::is_trivially_copyable<T>::value);
  static_assert (ALIGNMENT <= FastMemory::cache_line_size);
  static_assert ((ALIGNMENT & (ALIGNMENT - 1)) == 0);
  static_assert (alignof (T) <= ALIGNMENT);
  const size_t n_elements_ = 0;
  T     *const data_ = nullptr;
  BSE_CLASS_NON_COPYABLE (FastMemArray);
protected:
  void     range_check (size_t n) const
  {
    if (n >= n_elements_)
      throw std::out_of_range (string_format ("FastMemArray::range_check: n >= size(): %u >= %u", n, size()));
  }
public:
  FastMemArray (size_t n_elements) :
    n_elements_ (n_elements),
    data_ ((T*) fast_mem_alloc (sizeof (T) * n_elements_))
  {
    std::fill (begin(), end(), T());
  }
  FastMemArray (const vector<T>& elements) :
    n_elements_ (elements.size()),
    data_ ((T*) fast_mem_alloc (sizeof (T) * n_elements_))
  {
    std::copy (elements.begin(), elements.end(), begin());
  }
  ~FastMemArray()
  {
    static_assert (std::is_trivially_destructible<T>::value);
    fast_mem_free (data_);
  }
  T*       begin      ()                { return &data_[0]; }
  T*       end        ()                { return &data_[n_elements_]; }
  size_t   size       () const          { return n_elements_; }
  T&       operator[] (size_t n)        { return data_[n]; }
  const T& operator[] (size_t n) const  { return data_[n]; }
  T&       at         (size_t n)        { range_check (n); return data_[n]; }
  const T& at         (size_t n) const  { range_check (n); return data_[n]; }
};

namespace FastMemory {

// == NewDeleteBase ==
class NewDeleteBase {
  static constexpr const std::align_val_t __cxxalignment = std::align_val_t (__STDCPP_DEFAULT_NEW_ALIGNMENT__);
  static void    delete_  (void *ptr, std::size_t sz, std::align_val_t al);
  static void*   new_     (std::size_t sz, std::align_val_t al);
public:
  // 'static' is implicit for operator new/delete
  void* operator new      (std::size_t sz)                            { return new_ (sz, __cxxalignment); }
  void* operator new[]    (std::size_t sz)                            { return new_ (sz, __cxxalignment); }
  void* operator new      (std::size_t sz, std::align_val_t al)       { return new_ (sz, al); }
  void* operator new[]    (std::size_t sz, std::align_val_t al)       { return new_ (sz, al); }
  // variants without size_t MUST NOT be defined for the following to be used
  void  operator delete   (void *ptr, std::size_t sz)                 { delete_ (ptr, sz, __cxxalignment); }
  void  operator delete[] (void *ptr, std::size_t sz)                 { delete_ (ptr, sz, __cxxalignment); }
  void  operator delete   (void *ptr, std::size_t sz, std::align_val_t al) { delete_ (ptr, sz, al); }
  void  operator delete[] (void *ptr, std::size_t sz, std::align_val_t al) { delete_ (ptr, sz, al); }
};

/// Internal allocator handle.
struct Allocator;
using AllocatorP = std::shared_ptr<Allocator>;

/// Reference for an allocated memory block.
struct Block {
  void  *const block_start = nullptr;
  const uint32 block_length = 0;
  Block& operator= (const Block &src) { this->~Block(); new (this) Block (src); return *this; }
  /*copy*/ Block   (const Block &src) = default;
  /*dflt*/ Block   () = default;
};

/// Memory area (over-)aligned to cache size and utilizing huge pages.
struct Arena {
  /// Create isolated memory area.
  explicit Arena     (uint32 mem_size, uint32 alignment = cache_line_size);
  /// Alignment for block addresses and length.
  size_t   alignment () const;
  /// Address of memory area.
  uint64   location  () const;
  /// Reserved memory area in bytes.
  uint64   reserved  () const;
  /// Create a memory block from cache-line aligned memory area, MT-Unsafe.
  Block    allocate  (uint32 length) const;
  Block    allocate  (uint32 length, std::nothrow_t) const;
  /// Realease a previously allocated block, MT-Unsafe.
  void     release   (Block allocatedblock) const;
protected:
  AllocatorP fma; ///< Identifier for the associated memory allocator.
  explicit Arena     (AllocatorP);
};

} // FastMemory

} // Bse

#endif /* __BSE_MEMORY_HH__ */
<|endoftext|>


<commit_before>#include <babylon/meshes/instanced_mesh.h>

#include <babylon/core/logging.h>
#include <babylon/culling/bounding_info.h>
#include <babylon/engines/scene.h>
#include <babylon/maths/tmp_vectors.h>
#include <babylon/meshes/geometry.h>
#include <babylon/meshes/mesh.h>
#include <babylon/meshes/sub_mesh.h>
#include <babylon/rendering/edges_renderer.h>
#include <babylon/rendering/rendering_group.h>

namespace BABYLON {

InstancedMesh::InstancedMesh(const std::string& iName, const MeshPtr& source)
    : AbstractMesh(iName, source->getScene())
    , _indexInSourceMeshInstanceArray{-1}
    , sourceMesh{this, &InstancedMesh::get_sourceMesh}
    , _currentLOD{nullptr}
{
  source->addInstance(this);

  _sourceMesh = source;

  _unIndexed = source->_unIndexed;

  position().copyFrom(source->position());
  rotation().copyFrom(source->rotation());
  scaling().copyFrom(source->scaling());

  if (source->rotationQuaternion()) {
    rotationQuaternion = source->rotationQuaternion();
  }

  infiniteDistance = source->infiniteDistance();

  setPivotMatrix(source->getPivotMatrix());

  refreshBoundingInfo();
}

InstancedMesh::~InstancedMesh() = default;

void InstancedMesh::syncSubMeshes(const InstancedMeshPtr& instancedMesh)
{
  instancedMesh->_syncSubMeshes();
}

std::string InstancedMesh::getClassName() const
{
  return "InstancedMesh";
}

Type InstancedMesh::type() const
{
  return Type::INSTANCEDMESH;
}

void InstancedMesh::_resyncLightSources()
{
  // Do nothing as all the work will be done by source mesh
}

void InstancedMesh::_resyncLighSource(const LightPtr& /*light*/)
{
  // Do nothing as all the work will be done by source mesh
}

void InstancedMesh::_removeLightSource(const LightPtr& /*light*/, bool /*dispose*/)
{
  // Do nothing as all the work will be done by source mesh
}

bool InstancedMesh::get_receiveShadows() const
{
  return _sourceMesh->receiveShadows();
}

MaterialPtr& InstancedMesh::get_material()
{
  return _sourceMesh->material();
}

float InstancedMesh::get_visibility() const
{
  return _sourceMesh->visibility();
}

SkeletonPtr& InstancedMesh::get_skeleton()
{
  return _sourceMesh->skeleton();
}

int InstancedMesh::get_renderingGroupId() const
{
  return _sourceMesh->renderingGroupId;
}

void InstancedMesh::set_renderingGroupId(int value)
{
  if (!_sourceMesh || value == _sourceMesh->renderingGroupId) {
    return;
  }

  // no-op with warning
  BABYLON_LOG_WARN("InstancedMesh",
                   "Note - setting renderingGroupId of an instanced mesh has "
                   "no effect on the scene")
}

size_t InstancedMesh::getTotalVertices() const
{
  return _sourceMesh ? _sourceMesh->getTotalVertices() : 0;
}

size_t InstancedMesh::getTotalIndices() const
{
  return _sourceMesh->getTotalIndices();
}

MeshPtr& InstancedMesh::get_sourceMesh()
{
  return _sourceMesh;
}

bool InstancedMesh::isReady(bool completeCheck, bool /*forceInstanceSupport*/)
{
  return _sourceMesh->isReady(completeCheck, true);
}

Float32Array InstancedMesh::getVerticesData(const std::string& kind, bool copyWhenShared,
                                            bool forceCopy)
{
  return _sourceMesh->getVerticesData(kind, copyWhenShared, forceCopy);
}

AbstractMesh* InstancedMesh::setVerticesData(const std::string& kind, const Float32Array& data,
                                             bool updatable, const std::optional<size_t>& stride)
{
  if (sourceMesh()) {
    sourceMesh()->setVerticesData(kind, data, updatable, stride);
  }
  return sourceMesh().get();
}

AbstractMesh* InstancedMesh::updateVerticesData(const std::string& kind, const Float32Array& data,
                                                bool updateExtends, bool makeItUnique)
{
  if (sourceMesh()) {
    sourceMesh()->updateVerticesData(kind, data, updateExtends, makeItUnique);
  }
  return sourceMesh().get();
}

AbstractMesh* InstancedMesh::setIndices(const IndicesArray& indices, size_t totalVertices,
                                        bool /*updatable*/)
{
  if (sourceMesh()) {
    sourceMesh()->setIndices(indices, totalVertices);
  }
  return sourceMesh().get();
}

bool InstancedMesh::isVerticesDataPresent(const std::string& kind) const
{
  return _sourceMesh->isVerticesDataPresent(kind);
}

IndicesArray InstancedMesh::getIndices(bool /*copyWhenShared*/, bool /*forceCopy*/)
{
  return _sourceMesh->getIndices();
}

std::vector<Vector3>& InstancedMesh::_positions()
{
  return _sourceMesh->_positions();
}

InstancedMesh& InstancedMesh::refreshBoundingInfo(bool applySkeleton)
{
  if (_boundingInfo && _boundingInfo->isLocked()) {
    return *this;
  }

  const auto bias
    = _sourceMesh->geometry() ? _sourceMesh->geometry()->boundingBias() : std::nullopt;
  _refreshBoundingInfo(_sourceMesh->_getPositionData(applySkeleton), bias);
  return *this;
}

void InstancedMesh::_preActivate()
{
  if (_currentLOD) {
    _currentLOD->_preActivate();
  }
}

bool InstancedMesh::_activate(int renderId, bool intermediateRendering)
{
  if (_sourceMesh->subMeshes.empty()) {
    BABYLON_LOG_WARN("InstancedMesh", "Instances should only be created for meshes with geometry.")
  }

  if (_currentLOD) {
    auto differentSign
      = (_currentLOD->_getWorldMatrixDeterminant() > 0.f) != (_getWorldMatrixDeterminant() > 0.f);
    if (differentSign) {
      _internalAbstractMeshDataInfo._actAsRegularMesh = true;
      return true;
    }
    _internalAbstractMeshDataInfo._actAsRegularMesh = false;

    _currentLOD->_registerInstanceForRenderId(this, renderId);

    if (intermediateRendering) {
      if (!_currentLOD->_internalAbstractMeshDataInfo._isActiveIntermediate) {
        _currentLOD->_internalAbstractMeshDataInfo._onlyForInstancesIntermediate = true;
        return true;
      }
    }
    else {
      if (!_currentLOD->_internalAbstractMeshDataInfo._isActive) {
        _currentLOD->_internalAbstractMeshDataInfo._onlyForInstances = true;
        return true;
      }
    }
  }
  return false;
}

void InstancedMesh::_postActivate()
{
  if (_edgesRenderer && _edgesRenderer->isEnabled && _sourceMesh->_renderingGroup) {
    _sourceMesh->_renderingGroup->_edgesRenderers.emplace_back(_edgesRenderer);
  }
}

Matrix& InstancedMesh::getWorldMatrix()
{
  if (_currentLOD && _currentLOD->billboardMode() != TransformNode::BILLBOARDMODE_NONE
      && _currentLOD->_masterMesh != this) {
    const auto& tempMaster   = _currentLOD->_masterMesh;
    _currentLOD->_masterMesh = this;
    TmpVectors::MatrixArray[0].copyFrom(_currentLOD->computeWorldMatrix(true));
    _currentLOD->_masterMesh = tempMaster;
    return TmpVectors::MatrixArray[0];
  }

  return AbstractMesh::getWorldMatrix();
}

bool InstancedMesh::isAnInstance() const
{
  return true;
}

AbstractMesh* InstancedMesh::getLOD(const CameraPtr& camera, BoundingSphere* /*boundingSphere*/)
{
  if (!camera) {
    return this;
  }

  const auto& boundingInfo = getBoundingInfo();

  auto currentLOD = sourceMesh()->getLOD(getScene()->activeCamera, &boundingInfo->boundingSphere);
  _currentLOD     = dynamic_cast<Mesh*>(currentLOD);

  if (_currentLOD == sourceMesh().get()) {
    return sourceMesh().get();
  }

  return _currentLOD;
}

InstancedMesh& InstancedMesh::_syncSubMeshes()
{
  releaseSubMeshes();
  if (!_sourceMesh->subMeshes.empty()) {
    for (const auto& subMesh : _sourceMesh->subMeshes) {
      subMesh->clone(shared_from_base<InstancedMesh>(), _sourceMesh);
    }
  }

  return *this;
}

bool InstancedMesh::_generatePointsArray()
{
  return _sourceMesh->_generatePointsArray();
}

InstancedMeshPtr InstancedMesh::clone(const std::string& /*iNname*/, Node* newParent,
                                      bool doNotCloneChildren)
{
  auto result = _sourceMesh->createInstance(name);

  // Deep copy
  // Tools.DeepCopy(this, result, ["name"], []);

  // Bounding info
  refreshBoundingInfo();

  // Parent
  if (newParent) {
    std::static_pointer_cast<Node>(result)->parent = newParent;
  }

  if (!doNotCloneChildren) {
    // Children
    for (auto& mesh : getScene()->meshes) {
      if (mesh->parent() == this) {
        // mesh->clone(mesh->name, std::static_pointer_cast<Node>(result));
      }
    }
  }

  result->computeWorldMatrix(true);

  return result;
}

void InstancedMesh::dispose(bool doNotRecurse, bool disposeMaterialAndTextures)
{
  // Remove from mesh
  _sourceMesh->removeInstance(this);
  AbstractMesh::dispose(doNotRecurse, disposeMaterialAndTextures);
}

} // end of namespace BABYLON
<commit_msg>Updated InstancedMesh class to v4.1.0-beta.23<commit_after>#include <babylon/meshes/instanced_mesh.h>

#include <babylon/core/logging.h>
#include <babylon/culling/bounding_info.h>
#include <babylon/engines/scene.h>
#include <babylon/maths/tmp_vectors.h>
#include <babylon/meshes/geometry.h>
#include <babylon/meshes/mesh.h>
#include <babylon/meshes/sub_mesh.h>
#include <babylon/rendering/edges_renderer.h>
#include <babylon/rendering/rendering_group.h>

namespace BABYLON {

InstancedMesh::InstancedMesh(const std::string& iName, const MeshPtr& source)
    : AbstractMesh(iName, source->getScene())
    , _indexInSourceMeshInstanceArray{-1}
    , sourceMesh{this, &InstancedMesh::get_sourceMesh}
    , _currentLOD{nullptr}
{
  source->addInstance(this);

  _sourceMesh = source;

  _unIndexed = source->_unIndexed;

  position().copyFrom(source->position());
  rotation().copyFrom(source->rotation());
  scaling().copyFrom(source->scaling());

  if (source->rotationQuaternion()) {
    rotationQuaternion = source->rotationQuaternion();
  }

  infiniteDistance = source->infiniteDistance();

  setPivotMatrix(source->getPivotMatrix());

  refreshBoundingInfo();
}

InstancedMesh::~InstancedMesh() = default;

void InstancedMesh::syncSubMeshes(const InstancedMeshPtr& instancedMesh)
{
  instancedMesh->_syncSubMeshes();
}

std::string InstancedMesh::getClassName() const
{
  return "InstancedMesh";
}

Type InstancedMesh::type() const
{
  return Type::INSTANCEDMESH;
}

void InstancedMesh::_resyncLightSources()
{
  // Do nothing as all the work will be done by source mesh
}

void InstancedMesh::_resyncLighSource(const LightPtr& /*light*/)
{
  // Do nothing as all the work will be done by source mesh
}

void InstancedMesh::_removeLightSource(const LightPtr& /*light*/, bool /*dispose*/)
{
  // Do nothing as all the work will be done by source mesh
}

bool InstancedMesh::get_receiveShadows() const
{
  return _sourceMesh->receiveShadows();
}

MaterialPtr& InstancedMesh::get_material()
{
  return _sourceMesh->material();
}

float InstancedMesh::get_visibility() const
{
  return _sourceMesh->visibility();
}

SkeletonPtr& InstancedMesh::get_skeleton()
{
  return _sourceMesh->skeleton();
}

int InstancedMesh::get_renderingGroupId() const
{
  return _sourceMesh->renderingGroupId;
}

void InstancedMesh::set_renderingGroupId(int value)
{
  if (!_sourceMesh || value == _sourceMesh->renderingGroupId) {
    return;
  }

  // no-op with warning
  BABYLON_LOG_WARN("InstancedMesh",
                   "Note - setting renderingGroupId of an instanced mesh has "
                   "no effect on the scene")
}

size_t InstancedMesh::getTotalVertices() const
{
  return _sourceMesh ? _sourceMesh->getTotalVertices() : 0;
}

size_t InstancedMesh::getTotalIndices() const
{
  return _sourceMesh->getTotalIndices();
}

MeshPtr& InstancedMesh::get_sourceMesh()
{
  return _sourceMesh;
}

bool InstancedMesh::isReady(bool completeCheck, bool /*forceInstanceSupport*/)
{
  return _sourceMesh->isReady(completeCheck, true);
}

Float32Array InstancedMesh::getVerticesData(const std::string& kind, bool copyWhenShared,
                                            bool forceCopy)
{
  return _sourceMesh->getVerticesData(kind, copyWhenShared, forceCopy);
}

AbstractMesh* InstancedMesh::setVerticesData(const std::string& kind, const Float32Array& data,
                                             bool updatable, const std::optional<size_t>& stride)
{
  if (sourceMesh()) {
    sourceMesh()->setVerticesData(kind, data, updatable, stride);
  }
  return sourceMesh().get();
}

AbstractMesh* InstancedMesh::updateVerticesData(const std::string& kind, const Float32Array& data,
                                                bool updateExtends, bool makeItUnique)
{
  if (sourceMesh()) {
    sourceMesh()->updateVerticesData(kind, data, updateExtends, makeItUnique);
  }
  return sourceMesh().get();
}

AbstractMesh* InstancedMesh::setIndices(const IndicesArray& indices, size_t totalVertices,
                                        bool /*updatable*/)
{
  if (sourceMesh()) {
    sourceMesh()->setIndices(indices, totalVertices);
  }
  return sourceMesh().get();
}

bool InstancedMesh::isVerticesDataPresent(const std::string& kind) const
{
  return _sourceMesh->isVerticesDataPresent(kind);
}

IndicesArray InstancedMesh::getIndices(bool /*copyWhenShared*/, bool /*forceCopy*/)
{
  return _sourceMesh->getIndices();
}

std::vector<Vector3>& InstancedMesh::_positions()
{
  return _sourceMesh->_positions();
}

InstancedMesh& InstancedMesh::refreshBoundingInfo(bool applySkeleton)
{
  if (_boundingInfo && _boundingInfo->isLocked()) {
    return *this;
  }

  const auto bias
    = _sourceMesh->geometry() ? _sourceMesh->geometry()->boundingBias() : std::nullopt;
  _refreshBoundingInfo(_sourceMesh->_getPositionData(applySkeleton), bias);
  return *this;
}

void InstancedMesh::_preActivate()
{
  if (_currentLOD) {
    _currentLOD->_preActivate();
  }
}

bool InstancedMesh::_activate(int renderId, bool intermediateRendering)
{
  if (_sourceMesh->subMeshes.empty()) {
    BABYLON_LOG_WARN("InstancedMesh", "Instances should only be created for meshes with geometry.")
  }

  if (_currentLOD) {
    auto differentSign
      = (_currentLOD->_getWorldMatrixDeterminant() > 0.f) != (_getWorldMatrixDeterminant() > 0.f);
    if (differentSign) {
      _internalAbstractMeshDataInfo._actAsRegularMesh = true;
      return true;
    }
    _internalAbstractMeshDataInfo._actAsRegularMesh = false;

    _currentLOD->_registerInstanceForRenderId(this, renderId);

    if (intermediateRendering) {
      if (!_currentLOD->_internalAbstractMeshDataInfo._isActiveIntermediate) {
        _currentLOD->_internalAbstractMeshDataInfo._onlyForInstancesIntermediate = true;
        return true;
      }
    }
    else {
      if (!_currentLOD->_internalAbstractMeshDataInfo._isActive) {
        _currentLOD->_internalAbstractMeshDataInfo._onlyForInstances = true;
        return true;
      }
    }
  }
  return false;
}

void InstancedMesh::_postActivate()
{
  if (_edgesRenderer && _edgesRenderer->isEnabled && _sourceMesh->_renderingGroup) {
    _sourceMesh->_renderingGroup->_edgesRenderers.emplace_back(_edgesRenderer);
  }
}

Matrix& InstancedMesh::getWorldMatrix()
{
  if (_currentLOD && _currentLOD->billboardMode() != TransformNode::BILLBOARDMODE_NONE
      && _currentLOD->_masterMesh != this) {
    const auto& tempMaster   = _currentLOD->_masterMesh;
    _currentLOD->_masterMesh = this;
    TmpVectors::Vector3Array[7].copyFrom(_currentLOD->position());
    _currentLOD->position().set(0.f, 0.f, 0.f);
    TmpVectors::MatrixArray[0].copyFrom(_currentLOD->computeWorldMatrix(true));
    _currentLOD->position().copyFrom(TmpVectors::Vector3Array[7]);
    _currentLOD->_masterMesh = tempMaster;
    return TmpVectors::MatrixArray[0];
  }

  return AbstractMesh::getWorldMatrix();
}

bool InstancedMesh::isAnInstance() const
{
  return true;
}

AbstractMesh* InstancedMesh::getLOD(const CameraPtr& camera, BoundingSphere* /*boundingSphere*/)
{
  if (!camera) {
    return this;
  }

  const auto& boundingInfo = getBoundingInfo();

  auto currentLOD = sourceMesh()->getLOD(getScene()->activeCamera, &boundingInfo->boundingSphere);
  _currentLOD     = dynamic_cast<Mesh*>(currentLOD);

  if (_currentLOD == sourceMesh().get()) {
    return sourceMesh().get();
  }

  return _currentLOD;
}

InstancedMesh& InstancedMesh::_syncSubMeshes()
{
  releaseSubMeshes();
  if (!_sourceMesh->subMeshes.empty()) {
    for (const auto& subMesh : _sourceMesh->subMeshes) {
      subMesh->clone(shared_from_base<InstancedMesh>(), _sourceMesh);
    }
  }

  return *this;
}

bool InstancedMesh::_generatePointsArray()
{
  return _sourceMesh->_generatePointsArray();
}

InstancedMeshPtr InstancedMesh::clone(const std::string& /*iNname*/, Node* newParent,
                                      bool doNotCloneChildren)
{
  auto result = _sourceMesh->createInstance(name);

  // Deep copy
  // Tools.DeepCopy(this, result, ["name"], []);

  // Bounding info
  refreshBoundingInfo();

  // Parent
  if (newParent) {
    std::static_pointer_cast<Node>(result)->parent = newParent;
  }

  if (!doNotCloneChildren) {
    // Children
    for (auto& mesh : getScene()->meshes) {
      if (mesh->parent() == this) {
        // mesh->clone(mesh->name, std::static_pointer_cast<Node>(result));
      }
    }
  }

  result->computeWorldMatrix(true);

  return result;
}

void InstancedMesh::dispose(bool doNotRecurse, bool disposeMaterialAndTextures)
{
  // Remove from mesh
  _sourceMesh->removeInstance(this);
  AbstractMesh::dispose(doNotRecurse, disposeMaterialAndTextures);
}

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


<commit_before>/*
 *  FreezableVariable.cpp
 *  MWorksCore
 *
 *  Created by David Cox on 4/13/10.
 *  Copyright 2010 Harvard University. All rights reserved.
 *
 */

#include "FreezableVariableContainer.h"

BEGIN_NAMESPACE_MW

FreezableVariableContainer::FreezableVariableContainer(shared_ptr<Variable> _var){
    frozen = false;
    variable = _var;
    if(variable == NULL){
        // TODO: make a better exception here; this is only effective for debugging as stands
        throw FatalParserException("Registering an empty variable within a FreezableVariableContainer "
                                   "(is there an empty variable passed into a Stimulus object?)");
    }
}

FreezableVariableContainer::FreezableVariableContainer(const FreezableVariableContainer& tocopy){
    frozen = false;
    variable = tocopy.variable;
    frozen_value = tocopy.frozen_value;
}

Variable * FreezableVariableContainer::clone(){
    return (Variable *)new FreezableVariableContainer(*this);
}

void FreezableVariableContainer::freeze(bool should_freeze){

    boost::mutex::scoped_lock(lock);

    frozen = should_freeze;

    if(frozen){
        frozen_value = variable->getValue();
    }
}

void FreezableVariableContainer::thaw(){
    freeze(false);
}

Datum FreezableVariableContainer::getValue(){
    boost::mutex::scoped_lock(lock);

    if(frozen){
        return frozen_value;
    } else {
        // the contained variable may have it own
        // mutex/locking, but this is not a problem
        return variable->getValue();
    }
}

// Pass-through methods
void FreezableVariableContainer::setValue(Datum value){
    // this can rely on the contained object's locking
    // for thread safety
    variable->setValue(value);
}

void FreezableVariableContainer::setValue(Datum value, MWTime time){
    // this can rely on the contained object's locking
    // for thread safety
    variable->setValue(value, time);
}

void FreezableVariableContainer::setSilentValue(Datum value){
    // this can rely on the contained object's locking
    // for thread safety
    variable->setSilentValue(value);
}

END_NAMESPACE_MW
<commit_msg>Fixed some broken mutex locking in FreezableVariableContainer<commit_after>/*
 *  FreezableVariable.cpp
 *  MWorksCore
 *
 *  Created by David Cox on 4/13/10.
 *  Copyright 2010 Harvard University. All rights reserved.
 *
 */

#include "FreezableVariableContainer.h"

BEGIN_NAMESPACE_MW

FreezableVariableContainer::FreezableVariableContainer(shared_ptr<Variable> _var){
    frozen = false;
    variable = _var;
    if(variable == NULL){
        // TODO: make a better exception here; this is only effective for debugging as stands
        throw FatalParserException("Registering an empty variable within a FreezableVariableContainer "
                                   "(is there an empty variable passed into a Stimulus object?)");
    }
}

FreezableVariableContainer::FreezableVariableContainer(const FreezableVariableContainer& tocopy){
    frozen = false;
    variable = tocopy.variable;
    frozen_value = tocopy.frozen_value;
}

Variable * FreezableVariableContainer::clone(){
    return (Variable *)new FreezableVariableContainer(*this);
}

void FreezableVariableContainer::freeze(bool should_freeze){

    boost::mutex::scoped_lock locker(lock);

    frozen = should_freeze;

    if(frozen){
        frozen_value = variable->getValue();
    }
}

void FreezableVariableContainer::thaw(){
    freeze(false);
}

Datum FreezableVariableContainer::getValue(){
    boost::mutex::scoped_lock locker(lock);

    if(frozen){
        return frozen_value;
    } else {
        // the contained variable may have it own
        // mutex/locking, but this is not a problem
        return variable->getValue();
    }
}

// Pass-through methods
void FreezableVariableContainer::setValue(Datum value){
    // this can rely on the contained object's locking
    // for thread safety
    variable->setValue(value);
}

void FreezableVariableContainer::setValue(Datum value, MWTime time){
    // this can rely on the contained object's locking
    // for thread safety
    variable->setValue(value, time);
}

void FreezableVariableContainer::setSilentValue(Datum value){
    // this can rely on the contained object's locking
    // for thread safety
    variable->setSilentValue(value);
}

END_NAMESPACE_MW
<|endoftext|>