<commit_before>//===----------------------------------------------------------------------===//
//
//                         PelotonDB
//
// index_test.cpp
//
// Identification: test/index/index_util_test.cpp
//
// Copyright (c) 2015-16, Carnegie Mellon University Database Group
//
//===----------------------------------------------------------------------===//

#include "gtest/gtest.h"
#include "common/harness.h"

#include "common/logger.h"
#include "common/platform.h"
#include "index/index_factory.h"
#include "storage/tuple.h"
#include "index/index_util.h"

namespace peloton {
namespace test {

class IndexUtilTests : public PelotonTest {};

// These two are here since the IndexMetadata object does not claim
// ownership for the two schema objects so they will be not destroyed
// automatically
//
// Put them here to avoid Valgrind warning
static catalog::Schema *tuple_schema = nullptr;
static catalog::Schema *key_schema = nullptr;

/*
 * BuildIndex() - Builds an index with 4 columns
 *
 * The index has 4 columns as tuple key (A, B, C, D), and three of them
 * are indexed:
 *
 * tuple key: 0 1 2 3
 * index key: 3 0   1 (i.e. the 1st column of index key is the 3rd column of
 *                     tuple key)
 */
static const index::Index *BuildIndex() {
  // Build tuple and key schema
  std::vector<catalog::Column> tuple_column_list{};
  std::vector<catalog::Column> index_column_list{};

  // The following key are both in index key and tuple key and they are
  // indexed
  // The size of the key is:
  //   integer 4 * 3 = total 12

  catalog::Column column0(VALUE_TYPE_INTEGER,
                          GetTypeSize(VALUE_TYPE_INTEGER),
                          "A",
                          true);

  catalog::Column column1(VALUE_TYPE_VARCHAR,
                          1024,
                          "B",
                          false);

  // The following twoc constitutes tuple schema but does not appear in index

  catalog::Column column2(VALUE_TYPE_DOUBLE,
                          GetTypeSize(VALUE_TYPE_DOUBLE),
                          "C",
                          true);

  catalog::Column column3(VALUE_TYPE_INTEGER,
                          GetTypeSize(VALUE_TYPE_INTEGER),
                          "D",
                          true);

  // Use all four columns to build tuple schema

  tuple_column_list.push_back(column0);
  tuple_column_list.push_back(column1);
  tuple_column_list.push_back(column2);
  tuple_column_list.push_back(column3);

  tuple_schema = new catalog::Schema(tuple_column_list);

  // Use column 3, 0, 1 to build index key

  index_column_list.push_back(column3);
  index_column_list.push_back(column0);
  index_column_list.push_back(column1);

  // This will be copied into the key schema as well as into the IndexMetadata
  // object to identify indexed columns
  std::vector<oid_t> key_attrs = {3, 0, 1};

  // key schame also needs the mapping relation from index key to tuple key
  key_schema = new catalog::Schema(index_column_list);
  key_schema->SetIndexedColumns(key_attrs);

  // Build index metadata
  //
  // NOTE: Since here we use a relatively small key (size = 12)
  // so index_test is only testing with a certain kind of key
  // (most likely, GenericKey)
  //
  // For testing IntsKey and TupleKey we need more test cases
  index::IndexMetadata *index_metadata = \
    new index::IndexMetadata("index_util_test",
                             88888,                       // Index oid
                             INDEX_TYPE_BWTREE,
                             INDEX_CONSTRAINT_TYPE_DEFAULT,
                             tuple_schema,
                             key_schema,
                             key_attrs,
                             true);                      // unique_keys

  // Build index
  index::Index *index = index::IndexFactory::GetInstance(index_metadata);

  // Actually this will never be hit since if index creation fails an exception
  // would be raised (maybe out of memory would result in a nullptr? Anyway
  // leave it here)
  EXPECT_TRUE(index != NULL);

  return index;
}

/*
 * IsPointQueryTest() - Tests whether the index util correctly recognizes
 *                      point query
 *
 * The index configuration is as follows:
 *
 * tuple key: 0 1 2 3
 * index_key: 3 0 1
 */
TEST_F(IndexUtilTests, IsPointQueryTest) {
  const index::Index *index_p = BuildIndex();
  bool ret;

  std::vector<std::pair<oid_t, oid_t>> value_index_list{};

  // Test basic

  ret = FindValueIndex(index_p->GetMetadata(),
                       {3, 0, 1},
                       {EXPRESSION_TYPE_COMPARE_EQUAL,
                        EXPRESSION_TYPE_COMPARE_EQUAL,
                        EXPRESSION_TYPE_COMPARE_EQUAL},
                       value_index_list);
  EXPECT_EQ(ret, true);
  value_index_list.clear();

  ret = FindValueIndex(index_p->GetMetadata(),
                       {1, 0, 3},
                       {EXPRESSION_TYPE_COMPARE_EQUAL,
                        EXPRESSION_TYPE_COMPARE_EQUAL,
                        EXPRESSION_TYPE_COMPARE_EQUAL},
                       value_index_list);
  EXPECT_EQ(ret, true);
  value_index_list.clear();

  ret = FindValueIndex(index_p->GetMetadata(),
                       {0, 1, 3},
                       {EXPRESSION_TYPE_COMPARE_EQUAL,
                        EXPRESSION_TYPE_COMPARE_EQUAL,
                        EXPRESSION_TYPE_COMPARE_EQUAL},
                       value_index_list);
  EXPECT_EQ(ret, true);
  value_index_list.clear();

  // Test whether reconizes if only two columns are matched

  ret = FindValueIndex(index_p->GetMetadata(),
                       {0, 1},
                       {EXPRESSION_TYPE_COMPARE_EQUAL,
                        EXPRESSION_TYPE_COMPARE_EQUAL},
                       value_index_list);
  EXPECT_EQ(ret, false);
  value_index_list.clear();

  ret = FindValueIndex(index_p->GetMetadata(),
                       {3, 0},
                       {EXPRESSION_TYPE_COMPARE_EQUAL,
                        EXPRESSION_TYPE_COMPARE_EQUAL},
                       value_index_list);
  EXPECT_EQ(ret, false);
  value_index_list.clear();

  // Test empty

  ret = FindValueIndex(index_p->GetMetadata(),
                       {},
                       {},
                       value_index_list);
  EXPECT_EQ(ret, false);
  value_index_list.clear();

  // Test redundant conditions

  // This should return false, since the < already defines a lower bound
  ret = FindValueIndex(index_p->GetMetadata(),
                       {0, 3, 3, 0, 3, 1},
                       {EXPRESSION_TYPE_COMPARE_LESSTHAN,
                        EXPRESSION_TYPE_COMPARE_EQUAL,
                        EXPRESSION_TYPE_COMPARE_LESSTHAN,
                        EXPRESSION_TYPE_COMPARE_EQUAL,
                        EXPRESSION_TYPE_COMPARE_EQUAL,
                        EXPRESSION_TYPE_COMPARE_EQUAL},
                       value_index_list);
  EXPECT_EQ(ret, false);
  value_index_list.clear();

  // This should return true
  ret = FindValueIndex(index_p->GetMetadata(),
                       {0, 3, 3, 0, 3, 1},
                       {EXPRESSION_TYPE_COMPARE_EQUAL,
                        EXPRESSION_TYPE_COMPARE_EQUAL,
                        EXPRESSION_TYPE_COMPARE_LESSTHAN,
                        EXPRESSION_TYPE_COMPARE_LESSTHAN,
                        EXPRESSION_TYPE_COMPARE_EQUAL,
                        EXPRESSION_TYPE_COMPARE_EQUAL},
                       value_index_list);
  EXPECT_EQ(ret, true);
  value_index_list.clear();

  // Test duplicated conditions on a single column

  ret = FindValueIndex(index_p->GetMetadata(),
                       {3, 3, 3},
                       {EXPRESSION_TYPE_COMPARE_EQUAL,
                        EXPRESSION_TYPE_COMPARE_EQUAL,
                        EXPRESSION_TYPE_COMPARE_EQUAL},
                       value_index_list);
  EXPECT_EQ(ret, false);
  value_index_list.clear();

  //
  // The last test should logically be classified as point query
  // but our procedure does not give positive result to reduce
  // the complexity
  //

  ret = FindValueIndex(index_p->GetMetadata(),
                       {3, 0, 1, 0},
                       {EXPRESSION_TYPE_COMPARE_EQUAL,
                        EXPRESSION_TYPE_COMPARE_LESSTHANOREQUALTO,
                        EXPRESSION_TYPE_COMPARE_EQUAL,
                        EXPRESSION_TYPE_COMPARE_GREATERTHANOREQUALTO},
                       value_index_list);
  EXPECT_EQ(ret, false);
  value_index_list.clear();

  return;
}

}  // End test namespace
}  // End peloton namespace
<commit_msg>Adding few more test conditions to scan_optimizer test<commit_after>//===----------------------------------------------------------------------===//
//
//                         PelotonDB
//
// index_test.cpp
//
// Identification: test/index/index_util_test.cpp
//
// Copyright (c) 2015-16, Carnegie Mellon University Database Group
//
//===----------------------------------------------------------------------===//

#include "gtest/gtest.h"
#include "common/harness.h"

#include "common/logger.h"
#include "common/platform.h"
#include "index/index_factory.h"
#include "storage/tuple.h"
#include "index/index_util.h"
#include "index/scan_optimizer.h"

namespace peloton {
namespace test {

using namespace index;
using namespace storage;

class IndexUtilTests : public PelotonTest {};

// These two are here since the IndexMetadata object does not claim
// ownership for the two schema objects so they will be not destroyed
// automatically
//
// Put them here to avoid Valgrind warning
static catalog::Schema *tuple_schema = nullptr;
static catalog::Schema *key_schema = nullptr;

/*
 * BuildIndex() - Builds an index with 4 columns
 *
 * The index has 4 columns as tuple key (A, B, C, D), and three of them
 * are indexed:
 *
 * tuple key: 0 1 2 3
 * index key: 3 0   1 (i.e. the 1st column of index key is the 3rd column of
 *                     tuple key)
 */
static index::Index *BuildIndex() {
  // Build tuple and key schema
  std::vector<catalog::Column> tuple_column_list{};
  std::vector<catalog::Column> index_column_list{};

  // The following key are both in index key and tuple key and they are
  // indexed
  // The size of the key is:
  //   integer 4 * 3 = total 12

  catalog::Column column0(VALUE_TYPE_INTEGER,
                          GetTypeSize(VALUE_TYPE_INTEGER),
                          "A",
                          true);

  catalog::Column column1(VALUE_TYPE_VARCHAR,
                          1024,
                          "B",
                          false);

  // The following twoc constitutes tuple schema but does not appear in index

  catalog::Column column2(VALUE_TYPE_DOUBLE,
                          GetTypeSize(VALUE_TYPE_DOUBLE),
                          "C",
                          true);

  catalog::Column column3(VALUE_TYPE_INTEGER,
                          GetTypeSize(VALUE_TYPE_INTEGER),
                          "D",
                          true);

  // Use all four columns to build tuple schema

  tuple_column_list.push_back(column0);
  tuple_column_list.push_back(column1);
  tuple_column_list.push_back(column2);
  tuple_column_list.push_back(column3);

  tuple_schema = new catalog::Schema(tuple_column_list);

  // Use column 3, 0, 1 to build index key

  index_column_list.push_back(column3);
  index_column_list.push_back(column0);
  index_column_list.push_back(column1);

  // This will be copied into the key schema as well as into the IndexMetadata
  // object to identify indexed columns
  std::vector<oid_t> key_attrs = {3, 0, 1};

  // key schame also needs the mapping relation from index key to tuple key
  key_schema = new catalog::Schema(index_column_list);
  key_schema->SetIndexedColumns(key_attrs);

  // Build index metadata
  //
  // NOTE: Since here we use a relatively small key (size = 12)
  // so index_test is only testing with a certain kind of key
  // (most likely, GenericKey)
  //
  // For testing IntsKey and TupleKey we need more test cases
  index::IndexMetadata *index_metadata = \
    new index::IndexMetadata("index_util_test",
                             88888,                       // Index oid
                             INDEX_TYPE_BWTREE,
                             INDEX_CONSTRAINT_TYPE_DEFAULT,
                             tuple_schema,
                             key_schema,
                             key_attrs,
                             true);                      // unique_keys

  // Build index
  index::Index *index = index::IndexFactory::GetInstance(index_metadata);

  // Actually this will never be hit since if index creation fails an exception
  // would be raised (maybe out of memory would result in a nullptr? Anyway
  // leave it here)
  EXPECT_TRUE(index != NULL);

  return index;
}

/*
 * FindValueIndexTest() - Tests whether the index util correctly recognizes
 *                        point query
 *
 * The index configuration is as follows:
 *
 * tuple key: 0 1 2 3
 * index_key: 3 0 1
 */
TEST_F(IndexUtilTests, FindValueIndexTest) {
  const index::Index *index_p = BuildIndex();
  bool ret;

  std::vector<std::pair<oid_t, oid_t>> value_index_list{};

  // Test basic

  ret = FindValueIndex(index_p->GetMetadata(),
                       {3, 0, 1},
                       {EXPRESSION_TYPE_COMPARE_EQUAL,
                        EXPRESSION_TYPE_COMPARE_EQUAL,
                        EXPRESSION_TYPE_COMPARE_EQUAL},
                       value_index_list);
  EXPECT_EQ(ret, true);
  value_index_list.clear();

  ret = FindValueIndex(index_p->GetMetadata(),
                       {1, 0, 3},
                       {EXPRESSION_TYPE_COMPARE_EQUAL,
                        EXPRESSION_TYPE_COMPARE_EQUAL,
                        EXPRESSION_TYPE_COMPARE_EQUAL},
                       value_index_list);
  EXPECT_EQ(ret, true);
  value_index_list.clear();

  ret = FindValueIndex(index_p->GetMetadata(),
                       {0, 1, 3},
                       {EXPRESSION_TYPE_COMPARE_EQUAL,
                        EXPRESSION_TYPE_COMPARE_EQUAL,
                        EXPRESSION_TYPE_COMPARE_EQUAL},
                       value_index_list);
  EXPECT_EQ(ret, true);
  value_index_list.clear();

  // Test whether reconizes if only two columns are matched

  ret = FindValueIndex(index_p->GetMetadata(),
                       {0, 1},
                       {EXPRESSION_TYPE_COMPARE_EQUAL,
                        EXPRESSION_TYPE_COMPARE_EQUAL},
                       value_index_list);
  EXPECT_EQ(ret, false);
  value_index_list.clear();

  ret = FindValueIndex(index_p->GetMetadata(),
                       {3, 0},
                       {EXPRESSION_TYPE_COMPARE_EQUAL,
                        EXPRESSION_TYPE_COMPARE_EQUAL},
                       value_index_list);
  EXPECT_EQ(ret, false);
  value_index_list.clear();

  // Test empty

  ret = FindValueIndex(index_p->GetMetadata(),
                       {},
                       {},
                       value_index_list);
  EXPECT_EQ(ret, false);
  value_index_list.clear();

  // Test redundant conditions

  // This should return false, since the < already defines a lower bound
  ret = FindValueIndex(index_p->GetMetadata(),
                       {0, 3, 3, 0, 3, 1},
                       {EXPRESSION_TYPE_COMPARE_LESSTHAN,
                        EXPRESSION_TYPE_COMPARE_EQUAL,
                        EXPRESSION_TYPE_COMPARE_LESSTHAN,
                        EXPRESSION_TYPE_COMPARE_EQUAL,
                        EXPRESSION_TYPE_COMPARE_EQUAL,
                        EXPRESSION_TYPE_COMPARE_EQUAL},
                       value_index_list);
  EXPECT_EQ(ret, false);
  value_index_list.clear();

  // This should return true
  ret = FindValueIndex(index_p->GetMetadata(),
                       {0, 3, 3, 0, 3, 1},
                       {EXPRESSION_TYPE_COMPARE_EQUAL,
                        EXPRESSION_TYPE_COMPARE_EQUAL,
                        EXPRESSION_TYPE_COMPARE_LESSTHAN,
                        EXPRESSION_TYPE_COMPARE_LESSTHAN,
                        EXPRESSION_TYPE_COMPARE_EQUAL,
                        EXPRESSION_TYPE_COMPARE_EQUAL},
                       value_index_list);
  EXPECT_EQ(ret, true);
  value_index_list.clear();

  // Test duplicated conditions on a single column

  ret = FindValueIndex(index_p->GetMetadata(),
                       {3, 3, 3},
                       {EXPRESSION_TYPE_COMPARE_EQUAL,
                        EXPRESSION_TYPE_COMPARE_EQUAL,
                        EXPRESSION_TYPE_COMPARE_EQUAL},
                       value_index_list);
  EXPECT_EQ(ret, false);
  value_index_list.clear();

  //
  // The last test should logically be classified as point query
  // but our procedure does not give positive result to reduce
  // the complexity
  //

  ret = FindValueIndex(index_p->GetMetadata(),
                       {3, 0, 1, 0},
                       {EXPRESSION_TYPE_COMPARE_EQUAL,
                        EXPRESSION_TYPE_COMPARE_LESSTHANOREQUALTO,
                        EXPRESSION_TYPE_COMPARE_EQUAL,
                        EXPRESSION_TYPE_COMPARE_GREATERTHANOREQUALTO},
                       value_index_list);
  EXPECT_EQ(ret, false);
  value_index_list.clear();

  return;
}

/*
 * ConstructBoundaryKeyTest() - Tests ConstructBoundaryKey() function for
 *                              conjunctions
 */
TEST_F(IndexUtilTests, ConstructBoundaryKeyTest) {
  index::Index *index_p = BuildIndex();

  // This is the output variable
  std::vector<std::pair<oid_t, oid_t>> value_index_list{};

  std::vector<Value> value_list{};
  std::vector<oid_t> tuple_column_id_list{};
  std::vector<ExpressionType> expr_list{};

  value_list = {ValueFactory::GetIntegerValue(100),
                ValueFactory::GetIntegerValue(200),
                ValueFactory::GetIntegerValue(50), };

  tuple_column_id_list = {3, 3, 0};

  expr_list = {EXPRESSION_TYPE_COMPARE_GREATERTHAN,
               EXPRESSION_TYPE_COMPARE_LESSTHANOREQUALTO,
               EXPRESSION_TYPE_COMPARE_GREATERTHANOREQUALTO, };

  IndexScanPredicate isp{};

  isp.AddConjunctionScanPredicate(index_p,
                                  value_list,
                                  tuple_column_id_list,
                                  expr_list);

  const std::vector<ConjunctionScanPredicate> &cl = isp.GetConjunctionList();

  // First check the conjunction has been pushed into the scan predicate object
  EXPECT_EQ(cl.size(), 1UL);

  // Then make sure all values have been bound (i.e. no free variable)
  EXPECT_EQ(cl[0].GetBindingCount(), 0UL);

  // Check whether the entire predicate is full index scan (should not be)
  EXPECT_EQ(isp.IsFullIndexScan(), false);

  // Then check the conjunction predicate
  EXPECT_EQ(cl[0].IsFullIndexScan(), false);

  // Then check whether the conjunction predicate is a point query
  EXPECT_EQ(cl[0].IsPointQuery(), false);

  LOG_INFO("Low key = %s", cl[0].GetLowKey()->GetInfo().c_str());
  LOG_INFO("High key = %s", cl[0].GetHighKey()->GetInfo().c_str());

  return;
}

}  // End test namespace
}  // End peloton namespace
<|endoftext|>


<commit_before>/***************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (directui@nokia.com)
**
** This file is part of libmeegotouch.
**
** If you have questions regarding the use of this file, please contact
** Nokia at directui@nokia.com.
**
** This library is free software; you can redistribute it and/or
** modify it under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation
** and appearing in the file LICENSE.LGPL included in the packaging
** of this file.
**
****************************************************************************/

#include <QList>
#include <QVector>
#include <QPropertyAnimation>
#include <QStyleOptionGraphicsItem>

#include <QGestureEvent>
#include <QTapAndHoldGesture>

#include <MList>
#include <MWidgetView>
#include <MSeparator>
#include <MPannableViewport>

#include "mlistview.h"
#include "mlistview_p.h"

//// MListView /////
MListView::MListView(MWidgetController *widgetController)
    : MWidgetView(widgetController),
    d_ptr(NULL)
{
    controller = dynamic_cast<MList *>(widgetController);
}

MListView::~MListView()
{
    if (d_ptr) {
        if (!model()->headerCreator())
            delete d_ptr->headersCreator;

        delete d_ptr;
    }
}

void MListView::init()
{
    Q_ASSERT(controller);

    MCellCreator *headersCreator = NULL;

    if(d_ptr) {
        d_ptr->disconnectSignalsFromModelToListView();
        headersCreator = d_ptr->headersCreator;

        // Schedule deletion as we might have some events pending for delivery.
        d_ptr->destroy();
    }

    if (model()->columns() > 1) {
        if (model()->showGroups())
            d_ptr = new MMultiColumnListViewPrivate;
        else
            d_ptr = new MPlainMultiColumnListViewPrivate;
    } else {
        if (model()->showGroups())
            d_ptr = new MGroupHeaderListViewPrivate;
        else
            d_ptr = new MPlainListViewPrivate;
    }

    if (model()->headerCreator()) {
        d_ptr->setHeadersCreator(model()->headerCreator());
        if (model()->headerCreator() != headersCreator)
            delete headersCreator;
    } else
        d_ptr->setHeadersCreator(headersCreator);

    d_ptr->q_ptr = this;
    d_ptr->controller = dynamic_cast<MList *>(controller);
    d_ptr->createSeparators();
    d_ptr->updateSeparators();
    d_ptr->updateSeparatorSize();
    d_ptr->updateAnimations();

    connectSelectionModel();
    d_ptr->connectPannableViewport();

    d_ptr->resetModel(model());
}

void MListView::updateData(const QList<const char *>& modifications)
{
    MWidgetView::updateData(modifications);

    const char *member;
    for (int i = 0; i < modifications.count(); i++) {
        member = modifications[i];

        if (member == MListModel::ItemModel || member == MListModel::ShowGroups || member == MListModel::Columns || member == MListModel::CellCreator ||
            member == MListModel::HeaderCreator) {
            if (model()->itemModel()) {
                init();
                if (d_ptr->pannableViewport) {
                    d_ptr->updateListGeometry();
                } else {
                    // Postpone the update of the list geometry to the next
                    // event loop, otherwise too many cells get created without
                    // having a pannable viewport.
                    QTimer::singleShot(0, d_ptr, SLOT(updateListGeometry()));
                }
            }
        } else if (member == MListModel::SelectionModel) {
            connectSelectionModel();
        } else if (member == MListModel::ScrollToIndex) {
            scrollTo(model()->scrollToIndex(), static_cast<MList::ScrollHint>(model()->scrollHint()),
                     static_cast<MList::AnimationMode>(model()->animationMode()));
        } else if (member == MListModel::LongTap) {
            longTap(model()->longTap());
        } else if (member == MListModel::ListIndexDisplayMode) {
            d_ptr->updateListIndexVisibility();
        } else if (member == MListModel::LongTapEnabled) {
            d_ptr->updateItemConnections();
        }
    }
}

void MListView::connectSelectionModel()
{
    if (d_ptr->selectionModel)
        d_ptr->selectionModel->disconnect(this);

    if (model()->selectionModel()) {
        connect(model()->selectionModel(), SIGNAL(selectionChanged(QItemSelection, QItemSelection)),
                this, SLOT(selectionChanged(QItemSelection, QItemSelection)));
    }

    d_ptr->selectionModel = model()->selectionModel();
}

void MListView::setupModel()
{
    MWidgetView::setupModel();

    init();
    updateGeometry();
}

void MListView::applyStyle()
{
    MWidgetView::applyStyle();

    if (d_ptr) {
        d_ptr->clearVisibleItemsArray();
        d_ptr->updateAnimations();
        d_ptr->updateItemHeight();
        d_ptr->updateSeparators();
        d_ptr->updateSeparatorSize();
        d_ptr->updateHeaders();
        d_ptr->updateHeaderHeight();
        d_ptr->updateListIndexStyle();

        relayoutItemsInViewportRect();
    }
}

void MListView::notifyItemChange(QGraphicsItem::GraphicsItemChange change, const QVariant &value)
{
    MWidgetView::notifyItemChange(change, value);

    if(change == QGraphicsItem::ItemSceneHasChanged)
        emit controller->parentChanged();
}

QSizeF MListView::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const
{
    if (!d_ptr)
        return MWidgetView::sizeHint(which, constraint);

    return QSizeF(-1, d_ptr->totalHeight());
}

void MListView::setGeometry(const QRectF &rect)
{
    if (d_ptr) {
        d_ptr->viewWidth = rect.width();
        if (!d_ptr->pannableViewport)
            d_ptr->viewportVisibleHeight = d_ptr->totalHeight();
        d_ptr->updatePannableViewportPosition();
        d_ptr->updateItemSize();
        d_ptr->updateSeparatorSize();
        relayoutItemsInViewportRect();

        if (model()->scrollToIndex().isValid())
            d_ptr->scrollToLastIndex();
    }

    MWidgetView::setGeometry(rect);
}

void MListView::relayoutItemsInViewportRect()
{
    if (d_ptr->clearVisibleOnRelayout) {
        d_ptr->clearVisibleItemsArray();
        d_ptr->clearFirstAndLastVisibleRows();
        d_ptr->clearVisibleOnRelayout = false;
    }

    if (d_ptr->model && model()->cellCreator()) {
        int rowCount = d_ptr->rowCount;

        if (rowCount) {
            QModelIndex firstVisibleIndex = d_ptr->locateVisibleIndexAt(d_ptr->viewportTopLeft.y());
            d_ptr->updateFirstVisibleRow(firstVisibleIndex);
            QModelIndex lastVisibleIndex = d_ptr->locateLastVisibleIndexInRowAt(d_ptr->viewportTopLeft.y() + d_ptr->viewportVisibleHeight);
            d_ptr->updateLastVisibleRow(lastVisibleIndex);

            QPoint firstVisibleItemPos(0, d_ptr->locatePosOfItem(firstVisibleIndex));
            QPoint lastVisibleItemPos(0, d_ptr->locatePosOfItem(lastVisibleIndex));
            d_ptr->removeInvisibleItems(firstVisibleItemPos, lastVisibleItemPos);

            d_ptr->createVisibleItems(firstVisibleIndex, lastVisibleIndex);
        } else {
            d_ptr->clearVisibleItemsArray();
            d_ptr->clearFirstAndLastVisibleRows();
        }
    }
}

void MListView::drawForeground(QPainter *painter, const QStyleOptionGraphicsItem *option) const
{
    Q_UNUSED(painter);
    Q_UNUSED(option);
}

void MListView::drawBackground(QPainter *painter, const QStyleOptionGraphicsItem *option) const
{
    if (!d_ptr->clearVisibleOnRelayout)
        d_ptr->drawSeparators(painter, option);
}

void MListView::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
{
    if (!model()->firstVisibleItem().isValid() && !model()->lastVisibleItem().isValid())
        return;

    if (d_ptr->controller->isVisible()) {
        const MCellCreator *cellCreator = model()->cellCreator();

        int firstVisibleRow = d_ptr->indexToFlatRow(model()->firstVisibleItem());
        int lastVisibleRow = d_ptr->indexToFlatRow(model()->lastVisibleItem());
        int topLeftRow = d_ptr->indexToFlatRow(topLeft);
        int bottomRightRow = d_ptr->indexToFlatRow(bottomRight);

        int top = qMax(topLeftRow, firstVisibleRow);
        int lastCellInLastVisibleRow = lastVisibleRow + model()->columns() - lastVisibleRow % model()->columns() - 1;
        int bottom = qMin(bottomRightRow, lastCellInLastVisibleRow);

        for (int i = top; i <= bottom; i++) {
            QModelIndex cellIndex = d_ptr->flatRowToIndex(i);
            if (!d_ptr->isGroupHeader(cellIndex)) {
                MWidget *cell = d_ptr->findCellAtRow(i);
                if( (controller->optimizationFlags() & MList::DontCallCreateCellDuringUpdate) == MList::DontCallCreateCellDuringUpdate) {
                    cellCreator->updateCell(cellIndex, cell);
                } else {
                    MWidget* newCell = d_ptr->createCell(i);
                    d_ptr->replaceItem(cell, newCell);
                }
            }
        }
    } else {
        d_ptr->clearVisibleOnRelayout = true;
    }
}

/*!
 * This slot is called when items are inserted under the given \a parent.
 * The changed items are those from \a start to \a end inclusive.
 *
 * \sa QAbstractItemView::rowsInserted(), exposedRectUpdated()
 */
void MListView::rowsInserted(const QModelIndex &parent, int start, int end, bool animated)
{
    Q_UNUSED(parent);
    Q_UNUSED(start);
    Q_UNUSED(end);
    Q_UNUSED(animated);

    layoutChanged();
}

/*!
 * This slot is called when items under the given \a parent are removed.
 * The removed items are those from \a start to \a end inclusive.
 *
 * \sa rowsInserted(), exposedRectUpdated()
 */
void MListView::rowsRemoved(const QModelIndex &parent, int start, int end, bool animated)
{
    d_ptr->removeRows(parent, start, end, animated);

    if (!animated)
        layoutChanged();
}

void MListView::layoutChanged()
{
    if (!d_ptr->isAnimating()) {
        d_ptr->layoutChanged();

        updateGeometry();
        d_ptr->clearVisibleOnRelayout = true;
    }
}

void MListView::modelReset()
{
    layoutChanged();
}

void MListView::rowsMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd,
                                const QModelIndex &destinationParent, int destinationRow)
{
    Q_UNUSED(sourceParent);
    Q_UNUSED(sourceStart);
    Q_UNUSED(sourceEnd);
    Q_UNUSED(destinationParent);
    Q_UNUSED(destinationRow);

    layoutChanged();
}

void MListView::selectionChanged(const QItemSelection &selected, const QItemSelection &deselected)
{
    d_ptr->selectionChange(selected, deselected);
}

void MListView::itemClick()
{
    QObject *s = sender();
    MWidget *senderWidget = qobject_cast<MWidget *>(s);
    if (senderWidget)
        d_ptr->cellClicked(senderWidget);
}

void MListView::scrollTo(const QModelIndex &index, MList::ScrollHint hint, MList::AnimationMode mode)
{
    if (index.isValid()) {
        if (d_ptr->pannableViewport) {
            QPointF targetPosition = d_ptr->locateScrollToPosition(index, hint);

            if (targetPosition != d_ptr->pannableViewport->position())
                d_ptr->scrollToPos(targetPosition, mode);
        }
    }
}

void MListView::longTap(const QPointF &pos)
{
    QPointF relativePos = d_ptr->controller->mapFromScene(pos);
    QModelIndex index = d_ptr->flatRowToIndex(d_ptr->locateVisibleRowAt(relativePos.y(), relativePos.x()));
    d_ptr->cellLongTapped(index, pos);
}

#include "moc_mlistview.cpp"

M_REGISTER_VIEW_NEW(MListView, MList)
<commit_msg>Fixes: NB#239249 - MList incorrectly caches 'scroll to' index and references it later<commit_after>/***************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (directui@nokia.com)
**
** This file is part of libmeegotouch.
**
** If you have questions regarding the use of this file, please contact
** Nokia at directui@nokia.com.
**
** This library is free software; you can redistribute it and/or
** modify it under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation
** and appearing in the file LICENSE.LGPL included in the packaging
** of this file.
**
****************************************************************************/

#include <QList>
#include <QVector>
#include <QPropertyAnimation>
#include <QStyleOptionGraphicsItem>

#include <QGestureEvent>
#include <QTapAndHoldGesture>

#include <MList>
#include <MWidgetView>
#include <MSeparator>
#include <MPannableViewport>

#include "mlistview.h"
#include "mlistview_p.h"

//// MListView /////
MListView::MListView(MWidgetController *widgetController)
    : MWidgetView(widgetController),
    d_ptr(NULL)
{
    controller = dynamic_cast<MList *>(widgetController);
}

MListView::~MListView()
{
    if (d_ptr) {
        if (!model()->headerCreator())
            delete d_ptr->headersCreator;

        delete d_ptr;
    }
}

void MListView::init()
{
    Q_ASSERT(controller);

    MCellCreator *headersCreator = NULL;

    if(d_ptr) {
        d_ptr->disconnectSignalsFromModelToListView();
        headersCreator = d_ptr->headersCreator;

        // Schedule deletion as we might have some events pending for delivery.
        d_ptr->destroy();
    }

    if (model()->columns() > 1) {
        if (model()->showGroups())
            d_ptr = new MMultiColumnListViewPrivate;
        else
            d_ptr = new MPlainMultiColumnListViewPrivate;
    } else {
        if (model()->showGroups())
            d_ptr = new MGroupHeaderListViewPrivate;
        else
            d_ptr = new MPlainListViewPrivate;
    }

    if (model()->headerCreator()) {
        d_ptr->setHeadersCreator(model()->headerCreator());
        if (model()->headerCreator() != headersCreator)
            delete headersCreator;
    } else
        d_ptr->setHeadersCreator(headersCreator);

    d_ptr->q_ptr = this;
    d_ptr->controller = dynamic_cast<MList *>(controller);
    d_ptr->createSeparators();
    d_ptr->updateSeparators();
    d_ptr->updateSeparatorSize();
    d_ptr->updateAnimations();

    connectSelectionModel();
    d_ptr->connectPannableViewport();

    d_ptr->resetModel(model());
}

void MListView::updateData(const QList<const char *>& modifications)
{
    MWidgetView::updateData(modifications);

    const char *member;
    for (int i = 0; i < modifications.count(); i++) {
        member = modifications[i];

        if (member == MListModel::ItemModel || member == MListModel::ShowGroups || member == MListModel::Columns || member == MListModel::CellCreator ||
            member == MListModel::HeaderCreator) {
            if (model()->itemModel()) {
                init();
                if (d_ptr->pannableViewport) {
                    d_ptr->updateListGeometry();
                } else {
                    // Postpone the update of the list geometry to the next
                    // event loop, otherwise too many cells get created without
                    // having a pannable viewport.
                    QTimer::singleShot(0, d_ptr, SLOT(updateListGeometry()));
                }
            }
        } else if (member == MListModel::SelectionModel) {
            connectSelectionModel();
        } else if (member == MListModel::ScrollToIndex) {
            scrollTo(model()->scrollToIndex(), static_cast<MList::ScrollHint>(model()->scrollHint()),
                     static_cast<MList::AnimationMode>(model()->animationMode()));
        } else if (member == MListModel::LongTap) {
            longTap(model()->longTap());
        } else if (member == MListModel::ListIndexDisplayMode) {
            d_ptr->updateListIndexVisibility();
        } else if (member == MListModel::LongTapEnabled) {
            d_ptr->updateItemConnections();
        }
    }
}

void MListView::connectSelectionModel()
{
    if (d_ptr->selectionModel)
        d_ptr->selectionModel->disconnect(this);

    if (model()->selectionModel()) {
        connect(model()->selectionModel(), SIGNAL(selectionChanged(QItemSelection, QItemSelection)),
                this, SLOT(selectionChanged(QItemSelection, QItemSelection)));
    }

    d_ptr->selectionModel = model()->selectionModel();
}

void MListView::setupModel()
{
    MWidgetView::setupModel();

    init();
    updateGeometry();
}

void MListView::applyStyle()
{
    MWidgetView::applyStyle();

    if (d_ptr) {
        d_ptr->clearVisibleItemsArray();
        d_ptr->updateAnimations();
        d_ptr->updateItemHeight();
        d_ptr->updateSeparators();
        d_ptr->updateSeparatorSize();
        d_ptr->updateHeaders();
        d_ptr->updateHeaderHeight();
        d_ptr->updateListIndexStyle();

        relayoutItemsInViewportRect();
    }
}

void MListView::notifyItemChange(QGraphicsItem::GraphicsItemChange change, const QVariant &value)
{
    MWidgetView::notifyItemChange(change, value);

    if(change == QGraphicsItem::ItemSceneHasChanged)
        emit controller->parentChanged();
}

QSizeF MListView::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const
{
    if (!d_ptr)
        return MWidgetView::sizeHint(which, constraint);

    return QSizeF(-1, d_ptr->totalHeight());
}

void MListView::setGeometry(const QRectF &rect)
{
    if (d_ptr) {
        d_ptr->viewWidth = rect.width();
        if (!d_ptr->pannableViewport)
            d_ptr->viewportVisibleHeight = d_ptr->totalHeight();
        d_ptr->updatePannableViewportPosition();
        d_ptr->updateItemSize();
        d_ptr->updateSeparatorSize();
        relayoutItemsInViewportRect();

        QModelIndex index = model()->scrollToIndex();
        if (index.isValid()) {
            if (model()->itemModel() &&
                    model()->itemModel()->hasIndex(index.row(), index.column(), index.parent()))
                d_ptr->scrollToLastIndex();
            else
                model()->setScrollToIndex(QModelIndex());
        }
    }

    MWidgetView::setGeometry(rect);
}

void MListView::relayoutItemsInViewportRect()
{
    if (d_ptr->clearVisibleOnRelayout) {
        d_ptr->clearVisibleItemsArray();
        d_ptr->clearFirstAndLastVisibleRows();
        d_ptr->clearVisibleOnRelayout = false;
    }

    if (d_ptr->model && model()->cellCreator()) {
        int rowCount = d_ptr->rowCount;

        if (rowCount) {
            QModelIndex firstVisibleIndex = d_ptr->locateVisibleIndexAt(d_ptr->viewportTopLeft.y());
            d_ptr->updateFirstVisibleRow(firstVisibleIndex);
            QModelIndex lastVisibleIndex = d_ptr->locateLastVisibleIndexInRowAt(d_ptr->viewportTopLeft.y() + d_ptr->viewportVisibleHeight);
            d_ptr->updateLastVisibleRow(lastVisibleIndex);

            QPoint firstVisibleItemPos(0, d_ptr->locatePosOfItem(firstVisibleIndex));
            QPoint lastVisibleItemPos(0, d_ptr->locatePosOfItem(lastVisibleIndex));
            d_ptr->removeInvisibleItems(firstVisibleItemPos, lastVisibleItemPos);

            d_ptr->createVisibleItems(firstVisibleIndex, lastVisibleIndex);
        } else {
            d_ptr->clearVisibleItemsArray();
            d_ptr->clearFirstAndLastVisibleRows();
        }
    }
}

void MListView::drawForeground(QPainter *painter, const QStyleOptionGraphicsItem *option) const
{
    Q_UNUSED(painter);
    Q_UNUSED(option);
}

void MListView::drawBackground(QPainter *painter, const QStyleOptionGraphicsItem *option) const
{
    if (!d_ptr->clearVisibleOnRelayout)
        d_ptr->drawSeparators(painter, option);
}

void MListView::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
{
    if (!model()->firstVisibleItem().isValid() && !model()->lastVisibleItem().isValid())
        return;

    if (d_ptr->controller->isVisible()) {
        const MCellCreator *cellCreator = model()->cellCreator();

        int firstVisibleRow = d_ptr->indexToFlatRow(model()->firstVisibleItem());
        int lastVisibleRow = d_ptr->indexToFlatRow(model()->lastVisibleItem());
        int topLeftRow = d_ptr->indexToFlatRow(topLeft);
        int bottomRightRow = d_ptr->indexToFlatRow(bottomRight);

        int top = qMax(topLeftRow, firstVisibleRow);
        int lastCellInLastVisibleRow = lastVisibleRow + model()->columns() - lastVisibleRow % model()->columns() - 1;
        int bottom = qMin(bottomRightRow, lastCellInLastVisibleRow);

        for (int i = top; i <= bottom; i++) {
            QModelIndex cellIndex = d_ptr->flatRowToIndex(i);
            if (!d_ptr->isGroupHeader(cellIndex)) {
                MWidget *cell = d_ptr->findCellAtRow(i);
                if( (controller->optimizationFlags() & MList::DontCallCreateCellDuringUpdate) == MList::DontCallCreateCellDuringUpdate) {
                    cellCreator->updateCell(cellIndex, cell);
                } else {
                    MWidget* newCell = d_ptr->createCell(i);
                    d_ptr->replaceItem(cell, newCell);
                }
            }
        }
    } else {
        d_ptr->clearVisibleOnRelayout = true;
    }
}

/*!
 * This slot is called when items are inserted under the given \a parent.
 * The changed items are those from \a start to \a end inclusive.
 *
 * \sa QAbstractItemView::rowsInserted(), exposedRectUpdated()
 */
void MListView::rowsInserted(const QModelIndex &parent, int start, int end, bool animated)
{
    Q_UNUSED(parent);
    Q_UNUSED(start);
    Q_UNUSED(end);
    Q_UNUSED(animated);

    layoutChanged();
}

/*!
 * This slot is called when items under the given \a parent are removed.
 * The removed items are those from \a start to \a end inclusive.
 *
 * \sa rowsInserted(), exposedRectUpdated()
 */
void MListView::rowsRemoved(const QModelIndex &parent, int start, int end, bool animated)
{
    d_ptr->removeRows(parent, start, end, animated);

    if (!animated)
        layoutChanged();
}

void MListView::layoutChanged()
{
    if (!d_ptr->isAnimating()) {
        d_ptr->layoutChanged();

        updateGeometry();
        d_ptr->clearVisibleOnRelayout = true;
    }
}

void MListView::modelReset()
{
    layoutChanged();
}

void MListView::rowsMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd,
                                const QModelIndex &destinationParent, int destinationRow)
{
    Q_UNUSED(sourceParent);
    Q_UNUSED(sourceStart);
    Q_UNUSED(sourceEnd);
    Q_UNUSED(destinationParent);
    Q_UNUSED(destinationRow);

    layoutChanged();
}

void MListView::selectionChanged(const QItemSelection &selected, const QItemSelection &deselected)
{
    d_ptr->selectionChange(selected, deselected);
}

void MListView::itemClick()
{
    QObject *s = sender();
    MWidget *senderWidget = qobject_cast<MWidget *>(s);
    if (senderWidget)
        d_ptr->cellClicked(senderWidget);
}

void MListView::scrollTo(const QModelIndex &index, MList::ScrollHint hint, MList::AnimationMode mode)
{
    if (index.isValid()) {
        if (d_ptr->pannableViewport) {
            QPointF targetPosition = d_ptr->locateScrollToPosition(index, hint);

            if (targetPosition != d_ptr->pannableViewport->position())
                d_ptr->scrollToPos(targetPosition, mode);
        }
    }
}

void MListView::longTap(const QPointF &pos)
{
    QPointF relativePos = d_ptr->controller->mapFromScene(pos);
    QModelIndex index = d_ptr->flatRowToIndex(d_ptr->locateVisibleRowAt(relativePos.y(), relativePos.x()));
    d_ptr->cellLongTapped(index, pos);
}

#include "moc_mlistview.cpp"

M_REGISTER_VIEW_NEW(MListView, MList)
<|endoftext|>


<commit_before>// vrkit is (C) Copyright 2005-2007
//    by Allen Bierbaum, Aron Bierbuam, Patrick Hartling, and Daniel Shipton
//
// This file is part of vrkit.
//
// vrkit is free software; you can redistribute it and/or modify it under the
// terms of the GNU Lesser General Public License as published by the Free
// Software Foundation; either version 2 of the License, or (at your option)
// any later version.
//
// vrkit is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
// more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

#if defined(WIN32) || defined(WIN64)
#include <windows.h>
#include <iostream>
#include <sstream>
#include <cstdlib>
#include <string>
#include <boost/filesystem/path.hpp>
#include <boost/filesystem/exception.hpp>

namespace fs = boost::filesystem;

/**
 * Windows DLL entry point function. This ensures that the environment
 * variable \c VRKIT_BASE_DIR is set as soon as this DLL is attached to the
 * process. If it is not set, then it sets it based on an assumption about the
 * structure of a vrkit installation. More specifically, an assumption is made
 * that this DLL lives in the \c lib subdirectory of the vrkit installation.
 * Therefore, the root of the vrkit installation is the parent of the
 * directory containing this DLL.
 */
BOOL __stdcall DllMain(HINSTANCE module, DWORD reason, LPVOID reserved)
{
   switch (reason)
   {
      case DLL_PROCESS_ATTACH:
         {
#if defined(_MSC_VER) && _MSC_VER >= 1400
            char* env_dir(NULL);
            size_t len;
            _dupenv_s(&env_dir, &len, "VRKIT_BASE_DIR");
#else
            const char* env_dir = std::getenv("VRKIT_BASE_DIR");
#endif

            if ( NULL == env_dir )
            {
               char tmppath[1024];
               std::memset(tmppath, 0, sizeof(tmppath));
               GetModuleFileName(module, tmppath, sizeof(tmppath));

               try
               {
                  fs::path dll_path(tmppath, fs::native);
                  fs::path base_dir = dll_path.branch_path().branch_path();
#if defined(VRKIT_DEBUG) && ! defined(_DEBUG)
                  // The debug DLL linked against the release runtime is in
                  // <base_dir>\lib\debug.
                  base_dir = base_dir.branch_path();
#endif
                  const std::string base_dir_str =
                     base_dir.native_directory_string();
#if defined(_MSC_VER) && _MSC_VER >= 1400
                  _putenv_s("VRKIT_BASE_DIR", base_dir_str.c_str());
#else
                  std::ostringstream env_stream;
                  env_stream << "VRKIT_BASE_DIR=" << base_dir_str;
                  putenv(env_stream.str().c_str());
#endif
               }
               catch (fs::filesystem_error& ex)
               {
                  std::cerr << "Automatic assignment of VRKIT_BASE_DIR "
                            << "failed:\n" << ex.what() << std::endl;
               }
            }
#if defined(_MSC_VER) && _MSC_VER >= 1400
            else
            {
               std::free(env_dir);
            }
#endif
         }
         break;
      default:
         break;
   }

   return TRUE;
}

#else /* Non-windows or..Unix case. */
#include <dlfcn.h>
#include <string>

#include <boost/filesystem/path.hpp>
#include <boost/filesystem/operations.hpp>

#include <vpr/System.h>

#include <vrkit/Status.h>
#include <vrkit/Version.h>

namespace fs = boost::filesystem;

extern "C"
{

void __attribute ((constructor))
vrkit_library_init()
{
   Dl_info info;
   info.dli_fname = 0;
   const int result =
      dladdr(reinterpret_cast<const void*>(&vrkit_library_init), &info);

   // NOTE: dladdr(3) really does return a non-zero value on success.
   if ( 0 != result )
   {
      fs::path vrkit_lib_file(info.dli_fname, fs::native);
      vrkit_lib_file = fs::system_complete(vrkit_lib_file);

      fs::path vrkit_lib_path = vrkit_lib_file.branch_path();

      //construct VRKIT_BASE_DIR, VRKIT_DATA_DIR, VRKIT_PLUGINS_DIR
      std::string vrkit_versioned_dir_name = "vrkit";
#ifdef VRKIT_USE_VERSIONING
      vrkit_versioned_dir_name.append("-");
      vrkit_versioned_dir_name.append(vrkit::getVersion());
#endif
      // Go from base/lib/(debug/release) down to base
      fs::path vrkit_base_dir = vrkit_lib_path.branch_path().branch_path();

      // Go from /base to base/lib/versioned_vrkit_dir/plugins
      fs::path vrkit_plugins_dir = vrkit_base_dir / "lib" /
                                      vrkit_versioned_dir_name / "plugins";
      // Go from /base to /base/share/versioned_vrkit_dir
      fs::path vrkit_data_dir = vrkit_base_dir / "share" /
                                   vrkit_versioned_dir_name;

      std::string vrkit_base_dir_env_var;
      vpr::System::getenv("VRKIT_BASE_DIR", vrkit_base_dir_env_var);

      if ( vrkit_base_dir_env_var.empty() )
      {
         vpr::System::setenv("VRKIT_BASE_DIR", vrkit_base_dir.string());
         VRKIT_STATUS << "VRKIT_BASE_DIR set to: " << vrkit_base_dir.string() << std::endl;
      }
      else
      {
         // VRKIT_BASE_DIR set...check if path will match up
      }

      std::string vrkit_data_dir_env_var;
      vpr::System::getenv("VRKIT_DATA_DIR", vrkit_data_dir_env_var);

      if ( vrkit_data_dir_env_var.empty() )
      {
         vpr::System::setenv("VRKIT_DATA_DIR", vrkit_data_dir.string());
         VRKIT_STATUS << "VRKIT_DATA_DIR set to: " << vrkit_data_dir.string() << std::endl;
      }
      else
      {
         // VRKIT_DATA_DIR set...check if path will match up
      }

      std::string vrkit_plugin_dir_env_var;
      vpr::System::getenv("VRKIT_PLUGINS_DIR", vrkit_plugin_dir_env_var);

      if ( vrkit_plugin_dir_env_var.empty() )
      {
         vpr::System::setenv("VRKIT_PLUGINS_DIR", vrkit_plugins_dir.string());
         VRKIT_STATUS << "VRKIT_PLUGINS_DIR set to: " << vrkit_plugins_dir.string() << std::endl;
      }
      else
      {
         // VRKIT_PLUGINS_DIR set...check if path will match up
      }
   }
}

}
#endif
<commit_msg>More simplifications.<commit_after>// vrkit is (C) Copyright 2005-2007
//    by Allen Bierbaum, Aron Bierbuam, Patrick Hartling, and Daniel Shipton
//
// This file is part of vrkit.
//
// vrkit is free software; you can redistribute it and/or modify it under the
// terms of the GNU Lesser General Public License as published by the Free
// Software Foundation; either version 2 of the License, or (at your option)
// any later version.
//
// vrkit is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
// more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

#if defined(WIN32) || defined(WIN64)
#include <windows.h>
#include <iostream>
#include <sstream>
#include <cstdlib>
#include <string>
#include <boost/filesystem/path.hpp>
#include <boost/filesystem/exception.hpp>

namespace fs = boost::filesystem;

/**
 * Windows DLL entry point function. This ensures that the environment
 * variable \c VRKIT_BASE_DIR is set as soon as this DLL is attached to the
 * process. If it is not set, then it sets it based on an assumption about the
 * structure of a vrkit installation. More specifically, an assumption is made
 * that this DLL lives in the \c lib subdirectory of the vrkit installation.
 * Therefore, the root of the vrkit installation is the parent of the
 * directory containing this DLL.
 */
BOOL __stdcall DllMain(HINSTANCE module, DWORD reason, LPVOID reserved)
{
   switch (reason)
   {
      case DLL_PROCESS_ATTACH:
         {
#if defined(_MSC_VER) && _MSC_VER >= 1400
            char* env_dir(NULL);
            size_t len;
            _dupenv_s(&env_dir, &len, "VRKIT_BASE_DIR");
#else
            const char* env_dir = std::getenv("VRKIT_BASE_DIR");
#endif

            if ( NULL == env_dir )
            {
               char tmppath[1024];
               std::memset(tmppath, 0, sizeof(tmppath));
               GetModuleFileName(module, tmppath, sizeof(tmppath));

               try
               {
                  fs::path dll_path(tmppath, fs::native);
                  fs::path base_dir = dll_path.branch_path().branch_path();
#if defined(VRKIT_DEBUG) && ! defined(_DEBUG)
                  // The debug DLL linked against the release runtime is in
                  // <base_dir>\lib\debug.
                  base_dir = base_dir.branch_path();
#endif
                  const std::string base_dir_str =
                     base_dir.native_directory_string();
#if defined(_MSC_VER) && _MSC_VER >= 1400
                  _putenv_s("VRKIT_BASE_DIR", base_dir_str.c_str());
#else
                  std::ostringstream env_stream;
                  env_stream << "VRKIT_BASE_DIR=" << base_dir_str;
                  putenv(env_stream.str().c_str());
#endif
               }
               catch (fs::filesystem_error& ex)
               {
                  std::cerr << "Automatic assignment of VRKIT_BASE_DIR "
                            << "failed:\n" << ex.what() << std::endl;
               }
            }
#if defined(_MSC_VER) && _MSC_VER >= 1400
            else
            {
               std::free(env_dir);
            }
#endif
         }
         break;
      default:
         break;
   }

   return TRUE;
}

#else /* Non-windows or..Unix case. */
#include <dlfcn.h>
#include <string>

#include <boost/filesystem/path.hpp>
#include <boost/filesystem/operations.hpp>

#include <vpr/System.h>

#include <vrkit/Status.h>
#include <vrkit/Version.h>

namespace fs = boost::filesystem;

extern "C" void __attribute ((constructor)) vrkit_library_init()
{
   Dl_info info;
   info.dli_fname = 0;
   const int result =
      dladdr(reinterpret_cast<const void*>(&vrkit_library_init), &info);

   // NOTE: dladdr(3) really does return a non-zero value on success.
   if ( 0 != result )
   {
      fs::path vrkit_lib_file(info.dli_fname, fs::native);
      vrkit_lib_file = fs::system_complete(vrkit_lib_file);

      fs::path vrkit_lib_path = vrkit_lib_file.branch_path();

      //construct VRKIT_BASE_DIR, VRKIT_DATA_DIR, VRKIT_PLUGINS_DIR
      std::string vrkit_versioned_dir_name = "vrkit";
#ifdef VRKIT_USE_VERSIONING
      vrkit_versioned_dir_name.append("-");
      vrkit_versioned_dir_name.append(vrkit::getVersion());
#endif
      // Go from base/lib/(debug/release) down to base
      fs::path vrkit_base_dir = vrkit_lib_path.branch_path().branch_path();

      // Go from /base to base/lib/versioned_vrkit_dir/plugins
      fs::path vrkit_plugins_dir = vrkit_base_dir / "lib" /
                                      vrkit_versioned_dir_name / "plugins";
      // Go from /base to /base/share/versioned_vrkit_dir
      fs::path vrkit_data_dir = vrkit_base_dir / "share" /
                                   vrkit_versioned_dir_name;

      std::string vrkit_base_dir_env_var;
      vpr::System::getenv("VRKIT_BASE_DIR", vrkit_base_dir_env_var);

      if ( vrkit_base_dir_env_var.empty() )
      {
         vpr::System::setenv("VRKIT_BASE_DIR", vrkit_base_dir.string());
         VRKIT_STATUS << "VRKIT_BASE_DIR set to: " << vrkit_base_dir.string() << std::endl;
      }
      else
      {
         // VRKIT_BASE_DIR set...check if path will match up
      }

      std::string vrkit_data_dir_env_var;
      vpr::System::getenv("VRKIT_DATA_DIR", vrkit_data_dir_env_var);

      if ( vrkit_data_dir_env_var.empty() )
      {
         vpr::System::setenv("VRKIT_DATA_DIR", vrkit_data_dir.string());
         VRKIT_STATUS << "VRKIT_DATA_DIR set to: " << vrkit_data_dir.string() << std::endl;
      }
      else
      {
         // VRKIT_DATA_DIR set...check if path will match up
      }

      std::string vrkit_plugin_dir_env_var;
      vpr::System::getenv("VRKIT_PLUGINS_DIR", vrkit_plugin_dir_env_var);

      if ( vrkit_plugin_dir_env_var.empty() )
      {
         vpr::System::setenv("VRKIT_PLUGINS_DIR", vrkit_plugins_dir.string());
         VRKIT_STATUS << "VRKIT_PLUGINS_DIR set to: " << vrkit_plugins_dir.string() << std::endl;
      }
      else
      {
         // VRKIT_PLUGINS_DIR set...check if path will match up
      }
   }
}
#endif
<|endoftext|>


<commit_before>/*****************************************************************************************
 *                                                                                *
 * GHOUL                                                                                *
 * General Helpful Open Utility Library                                                  *
 *                                                                                *
 * Copyright (c) 2012-2014                                                               *
 *                                                                                *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this  *
 * software and associated documentation files (the "Software"), to deal in the Software *
 * without restriction, including without limitation the rights to use, copy, modify,    *
 * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to    *
 * permit persons to whom the Software is furnished to do so, subject to the following   *
 * conditions:                                                                           *
 *                                                                                *
 * The above copyright notice and this permission notice shall be included in all copies *
 * or substantial portions of the Software.                                              *
 *                                                                                *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,   *
 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A         *
 * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT    *
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF  *
 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE  *
 * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                                         *
 ****************************************************************************************/

#include <ghoul/opengl/shaderobject.h>

#include <ghoul/logging/log.h>
#include <ghoul/filesystem/filesystem>
#include <ghoul/filesystem/cachemanager.h>
#include <ghoul/filesystem/file.h>

#include <algorithm>
#include <cassert>
#include <cctype>
#include <cstring>
#include <fstream>
#include <functional>

#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable:4996)
#endif

namespace {
const std::string _loggerCat = "ShaderObject";

std::string glslVersionString() {
	int versionMajor;
	int versionMinor;
	int profileMask;
	glGetIntegerv(GL_MAJOR_VERSION, &versionMajor);
	glGetIntegerv(GL_MINOR_VERSION, &versionMinor);
	glGetIntegerv(GL_CONTEXT_PROFILE_MASK, &profileMask);
	std::stringstream ss;
	ss << "#version " << versionMajor << versionMinor << "0" << // version is set
		(profileMask & GL_CONTEXT_CORE_PROFILE_BIT ? " core" :
		(profileMask & GL_CONTEXT_COMPATIBILITY_PROFILE_BIT ? " compatibility" : ""));
	return ss.str();
}
}

namespace ghoul {
namespace opengl {

ShaderObject::ShaderObject(ShaderType shaderType)
    : _id(0)
    , _type(shaderType)
    , _fileName("")
    , _shaderName("")
	, _loggerCat("ShaderObject")
	, _onChangeCallback(nullptr)
{
    _id = glCreateShader(_type);
    if (_id == 0)
        LERROR("glCreateShader returned 0");
}

ShaderObject::ShaderObject(ShaderType shaderType, std::string filename)
    : _id(0)
    , _type(shaderType)
    , _fileName(std::move(filename))
    , _shaderName("")
	, _loggerCat("ShaderObject")
	, _onChangeCallback(nullptr)
{
#ifdef DEBUG
    if (filename == "")
        LWARNING("Filename is empty");
#endif

    _id = glCreateShader(_type);
    if (_id == 0)
        LERROR("glCreateShader returned 0");
    setShaderFilename(_fileName);
}

ShaderObject::ShaderObject(ShaderType shaderType, std::string filename,
						   std::string name)
    : _id(0)
    , _type(shaderType)
    , _fileName(std::move(filename))
    , _shaderName(std::move(name))
	, _loggerCat("ShaderObject('" + _shaderName + "')")
	, _onChangeCallback(nullptr)
{
    _id = glCreateShader(_type);
    if (_id == 0)
        LERROR("glCreateShader returned 0");
#ifdef GL_VERSION_4_3
    if (glObjectLabel)
        glObjectLabel(GL_SHADER, _id, GLsizei(_shaderName.length() + 1),
                      _shaderName.c_str());
#endif
    setShaderFilename(_fileName);
}

ShaderObject::ShaderObject(const ShaderObject& cpy)
    : _id(0)
    , _type(cpy._type)
    , _fileName(cpy._fileName)
	, _shaderName(cpy._shaderName)
	, _loggerCat(cpy._loggerCat)
	, _onChangeCallback(cpy._onChangeCallback)
{
    _id = glCreateShader(_type);
    if (_id == 0)
        LERROR("glCreateShader returned 0");
#ifdef GL_VERSION_4_3
    if (glObjectLabel)
        glObjectLabel(GL_SHADER, _id, GLsizei(_shaderName.length() + 1),
                      _shaderName.c_str());
#endif
    setShaderFilename(_fileName);
}

ShaderObject::ShaderObject(ShaderObject&& rhs) {
	if (this != &rhs) {
		glDeleteShader(_id);
		_id = rhs._id;
		rhs._id = 0;

		_type = rhs._type;
		_fileName = std::move(rhs._fileName);
		_shaderName = std::move(rhs._shaderName);
		_loggerCat = std::move(rhs._loggerCat);
		_onChangeCallback = rhs._onChangeCallback;
		_trackedFiles = std::move(rhs._trackedFiles);
	}
}

ShaderObject::~ShaderObject() {
    glDeleteShader(_id);
	_id = 0;
	for (auto f : _trackedFiles)
		delete f.second;
}

ShaderObject::operator GLuint() const {
    return _id;
}

ShaderObject& ShaderObject::operator=(const ShaderObject& rhs) {
    if (this != &rhs) {
        _type = rhs._type;
        _fileName = rhs._fileName;
        _shaderName = rhs._shaderName;
		_loggerCat = rhs._loggerCat;
		_onChangeCallback = rhs._onChangeCallback;

        glDeleteShader(_id);
        _id = glCreateShader(_type);
        if (_id == 0)
            LERROR("glCreateShader returned 0");
#ifdef GL_VERSION_4_3
        if (glObjectLabel)
            glObjectLabel(GL_SHADER, _id, GLsizei(_shaderName.length() + 1)
                          , _shaderName.c_str());
#endif
        setShaderFilename(_fileName);
    }
    return *this;
}

ShaderObject& ShaderObject::operator=(ShaderObject&& rhs) {
	if (this != &rhs) {
		glDeleteShader(_id);
		_id = rhs._id;
		rhs._id = 0;

		_type = rhs._type;
		_fileName = std::move(rhs._fileName);
		_shaderName = std::move(rhs._shaderName);
		_loggerCat = std::move(rhs._loggerCat);
		_onChangeCallback = rhs._onChangeCallback;
		_trackedFiles = std::move(rhs._trackedFiles);
	}
	return *this;
}

void ShaderObject::setName(std::string name) {
	_shaderName = std::move(name);
	_loggerCat = "ShaderObject['" + _shaderName + "']";
#ifdef GL_VERSION_4_3
	if (glObjectLabel)
		glObjectLabel(GL_SHADER, _id, GLsizei(_shaderName.length() + 1),
		_shaderName.c_str());
#endif
}

const std::string& ShaderObject::name() const {
    return _shaderName;
}

void ShaderObject::setShaderObjectCallback(ShaderObjectCallback changeCallback) {
	_onChangeCallback = changeCallback;

	if (_onChangeCallback) {
		ghoul::filesystem::File* fileObject = new ghoul::filesystem::File(_fileName, false);
		fileObject->setCallback(_onChangeCallback);
		_trackedFiles[_fileName] = fileObject;
	}
}

bool ShaderObject::hasName() const {
    return !_shaderName.empty();
}

std::string ShaderObject::filename() {
	return _fileName;
}

bool ShaderObject::setShaderFilename(std::string filename) {
	_fileName = std::move(filename);
    if (_fileName == "") {
        deleteShader();
        return true;
    }

	// Clear tracked files, this might have changed since last time
	for (auto f : _trackedFiles)
		delete f.second;
	_trackedFiles.erase(_trackedFiles.begin(), _trackedFiles.end());

	// No need to alocate a new contents string for every shader
	// This makes ShaderObjects not thread safe
	static std::string contents;
	contents = "";

	bool success = readFile(_fileName, contents);

	// If in debug mode, output the source to file
#ifndef NDEBUG
	std::string generatedFilename;
	ghoul::filesystem::File ghlFile(_fileName);
	if (!FileSys.cacheManager() ||
		!FileSys.cacheManager()->getCachedFile(
			_fileName,
			generatedFilename,
			true)
		)
	{
		// Either the cachemanager wasn't initialized or the
		// filename could not be fetched
		generatedFilename += ".GhoulGenerated.glsl";
	}

	std::ofstream os(generatedFilename);
	os << contents;
	os.close();
#endif

	if (!success)
		return false;

    const char* contentPtr =  contents.c_str();
    glShaderSource(_id, 1, &contentPtr, NULL);

    LINFO("Loaded " + typeAsString() + ": '" + _fileName + "'");
    return true;
}

void ShaderObject::deleteShader() {
    glDeleteShader(_id);
    _id = 0;
}

bool ShaderObject::rebuildFromFile() {
    return setShaderFilename(_fileName);
}

bool ShaderObject::compile() {
    glCompileShader(_id);

    GLint compilationStatus;
    glGetShaderiv(_id, GL_COMPILE_STATUS, &compilationStatus);
    if (compilationStatus == GL_FALSE) {
        GLint logLength;
        glGetShaderiv(_id, GL_INFO_LOG_LENGTH, &logLength);

        if (logLength == 0) {
            LERROR(typeAsString() + " compile error: Unknown error");
            return false;
        }

        GLchar* log = new GLchar[logLength];
        glGetShaderInfoLog(_id, logLength, NULL, log);
        LERROR(typeAsString() + " compile error:\n" + log);
        delete[] log;

        return false;
    }
    return compilationStatus == GL_TRUE;
}

std::string ShaderObject::typeAsString() const {
    return ShaderObject::stringForShaderType(_type);
}

std::string ShaderObject::stringForShaderType(ShaderType type) {
    switch (type) {
    case ShaderTypeVertex:
        return "Vertex shader";
    case ShaderTypeTesselationControl:
        return "Tesselation Control shader";
    case ShaderTypeTesselationEvaluation:
        return "Tesselation Evaluation shader";
    case ShaderTypeGeometry:
        return "Geometry shader";
    case ShaderTypeFragment:
        return "Fragment shader";
#ifdef GL_VERSION_4_3
    case ShaderTypeCompute:
        return "Compute shader";
#endif
    default:
        assert(false);
        return "";
    }
}

bool ShaderObject::readFile(const std::string& filename, std::string& content, bool track) {
	// check that the file exists
	if (!FileSys.fileExists(filename)) {
		LWARNING("Could not find '" << filename << "'");
		return false;
	}

	// Check that the file is currently not in _trackedFiles
	auto s = _trackedFiles.find(filename);
	if (s != _trackedFiles.end()) {
		LWARNING("Already including '" << filename << "'");
		return false;
	}

	// Check that file can be opened
	std::ifstream f(filename, std::ifstream::in);
	if (!f.is_open()) {
		LWARNING("Could not open '" << filename << "': " << strerror(errno));
		return false;
	}

	// Construct a file object
	using namespace ghoul::filesystem;

	// CODE FOR DEBUG PURPOSE
	//filesystem::File::FileChangedCallback c = nullptr;
	//c = [ this](const filesystem::File&){
	//	LDEBUG("Update fired from " << name());
	//	if (_onChangeCallback)
	//		_onChangeCallback;
	//};

	File* fileObject = new File(filename, false);
	if (track) {
		fileObject->setCallback(_onChangeCallback);
		_trackedFiles[filename] = fileObject;
	}

	// Ready to start parsing
	// ugly slightly more efficient version, 3-4x faster
	static std::string line;
	static const std::string ws = " \n\r\t";
	static const std::string includeString = "#include";
	static const std::string notrackString = ":notrack";
	static const std::string versionString = "#version __CONTEXT__";
	while (std::getline(f, line)) {
		size_t start_pos = line.find_first_not_of(ws);
		if (start_pos == std::string::npos)
			start_pos = 0;
		size_t end_pos = line.find_last_not_of(ws);
		if (end_pos == std::string::npos)
			end_pos = line.length();
		else
			end_pos += 1;

		const size_t length = end_pos - start_pos;

		// If begins with #include
		if (length > 11 && line.substr(start_pos, includeString.length()) == includeString) {
			bool success = false;

			size_t p1 = line.find_first_not_of(ws, start_pos + includeString.length());
			size_t p2 = std::string::npos;
			if (p1 != std::string::npos) {
				if (line.at(p1) == '\"') {
					p2 = line.find_first_of("\"", p1 + 1);
					if (p2 != std::string::npos) {
						std::string includeFilename = fileObject->directoryName() + FileSystem::PathSeparator + line.substr(p1 + 1, p2 - p1 - 1);

						size_t remaining = end_pos - p2;
						bool keepTrack = true;
						if (remaining > notrackString.length()) {
							if (line.find_first_of(notrackString, p2) != std::string::npos)
								keepTrack = false;
						}
						content += "// Begin including '" + includeFilename + "'\n";
						if (!readFile(includeFilename, content, track && keepTrack))
							content += "// Warning, unsuccessful loading of '" + includeFilename + "'\n";
						content += "// End including '" + includeFilename + "'\n";
						success = true;
					}
				}
				else if (line.at(p1) == '<') {
					p2 = line.find_first_of(">", p1 + 1);
					if (p2 != std::string::npos) {
						std::string includeFilename = absPath(line.substr(p1 + 1, p2 - p1 - 1));

						size_t remaining = end_pos - p2;
						bool keepTrack = true;
						if (remaining > notrackString.length()) {
							if (line.find_first_of(notrackString, p2) != std::string::npos)
								keepTrack = false;
						}
						content += "// Begin including '" + includeFilename + "'\n";
						if (!readFile(includeFilename, content, track && keepTrack))
							content += "// Warning, unsuccessful loading of '" + includeFilename + "'\n";
						content += "// End including '" + includeFilename + "'\n";
						success = true;
					}
				}
			}
			if (!success) {
				content += "// Error in #include pattern!\n";
			}
		}
		else if (line == versionString) {
			static std::string versionString = glslVersionString();
			content += versionString + "\n";
		}
		else{
			content += line + "\n";
		}
	}

	if (!track) {
		delete fileObject;
	}

	f.close();
	return true;
}

} // namespace opengl
} // namespace ghoul

#ifdef _MSC_VER
#pragma warning(pop)
#endif
<commit_msg>ShaderObject filecallback fix<commit_after>/*****************************************************************************************
 *                                                                                *
 * GHOUL                                                                                *
 * General Helpful Open Utility Library                                                  *
 *                                                                                *
 * Copyright (c) 2012-2014                                                               *
 *                                                                                *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this  *
 * software and associated documentation files (the "Software"), to deal in the Software *
 * without restriction, including without limitation the rights to use, copy, modify,    *
 * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to    *
 * permit persons to whom the Software is furnished to do so, subject to the following   *
 * conditions:                                                                           *
 *                                                                                *
 * The above copyright notice and this permission notice shall be included in all copies *
 * or substantial portions of the Software.                                              *
 *                                                                                *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,   *
 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A         *
 * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT    *
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF  *
 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE  *
 * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                                         *
 ****************************************************************************************/

#include <ghoul/opengl/shaderobject.h>

#include <ghoul/logging/log.h>
#include <ghoul/filesystem/filesystem>
#include <ghoul/filesystem/cachemanager.h>
#include <ghoul/filesystem/file.h>

#include <algorithm>
#include <cassert>
#include <cctype>
#include <cstring>
#include <fstream>
#include <functional>

#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable:4996)
#endif

namespace {
const std::string _loggerCat = "ShaderObject";

std::string glslVersionString() {
	int versionMajor;
	int versionMinor;
	int profileMask;
	glGetIntegerv(GL_MAJOR_VERSION, &versionMajor);
	glGetIntegerv(GL_MINOR_VERSION, &versionMinor);
	glGetIntegerv(GL_CONTEXT_PROFILE_MASK, &profileMask);
	std::stringstream ss;
	ss << "#version " << versionMajor << versionMinor << "0" << // version is set
		(profileMask & GL_CONTEXT_CORE_PROFILE_BIT ? " core" :
		(profileMask & GL_CONTEXT_COMPATIBILITY_PROFILE_BIT ? " compatibility" : ""));
	return ss.str();
}
}

namespace ghoul {
namespace opengl {

ShaderObject::ShaderObject(ShaderType shaderType)
    : _id(0)
    , _type(shaderType)
    , _fileName("")
    , _shaderName("")
	, _loggerCat("ShaderObject")
	, _onChangeCallback(nullptr)
{
    _id = glCreateShader(_type);
    if (_id == 0)
        LERROR("glCreateShader returned 0");
}

ShaderObject::ShaderObject(ShaderType shaderType, std::string filename)
    : _id(0)
    , _type(shaderType)
    , _fileName(std::move(filename))
    , _shaderName("")
	, _loggerCat("ShaderObject")
	, _onChangeCallback(nullptr)
{
#ifdef DEBUG
    if (filename == "")
        LWARNING("Filename is empty");
#endif

    _id = glCreateShader(_type);
    if (_id == 0)
        LERROR("glCreateShader returned 0");
    setShaderFilename(_fileName);
}

ShaderObject::ShaderObject(ShaderType shaderType, std::string filename,
						   std::string name)
    : _id(0)
    , _type(shaderType)
    , _fileName(std::move(filename))
    , _shaderName(std::move(name))
	, _loggerCat("ShaderObject('" + _shaderName + "')")
	, _onChangeCallback(nullptr)
{
    _id = glCreateShader(_type);
    if (_id == 0)
        LERROR("glCreateShader returned 0");
#ifdef GL_VERSION_4_3
    if (glObjectLabel)
        glObjectLabel(GL_SHADER, _id, GLsizei(_shaderName.length() + 1),
                      _shaderName.c_str());
#endif
    setShaderFilename(_fileName);
}

ShaderObject::ShaderObject(const ShaderObject& cpy)
    : _id(0)
    , _type(cpy._type)
    , _fileName(cpy._fileName)
	, _shaderName(cpy._shaderName)
	, _loggerCat(cpy._loggerCat)
	, _onChangeCallback(cpy._onChangeCallback)
{
    _id = glCreateShader(_type);
    if (_id == 0)
        LERROR("glCreateShader returned 0");
#ifdef GL_VERSION_4_3
    if (glObjectLabel)
        glObjectLabel(GL_SHADER, _id, GLsizei(_shaderName.length() + 1),
                      _shaderName.c_str());
#endif
    setShaderFilename(_fileName);
}

ShaderObject::ShaderObject(ShaderObject&& rhs) {
	if (this != &rhs) {
		glDeleteShader(_id);
		_id = rhs._id;
		rhs._id = 0;

		_type = rhs._type;
		_fileName = std::move(rhs._fileName);
		_shaderName = std::move(rhs._shaderName);
		_loggerCat = std::move(rhs._loggerCat);
		_onChangeCallback = rhs._onChangeCallback;
		_trackedFiles = std::move(rhs._trackedFiles);
	}
}

ShaderObject::~ShaderObject() {
    glDeleteShader(_id);
	_id = 0;
	for (auto f : _trackedFiles)
		delete f.second;
}

ShaderObject::operator GLuint() const {
    return _id;
}

ShaderObject& ShaderObject::operator=(const ShaderObject& rhs) {
    if (this != &rhs) {
        _type = rhs._type;
        _fileName = rhs._fileName;
        _shaderName = rhs._shaderName;
		_loggerCat = rhs._loggerCat;
		_onChangeCallback = rhs._onChangeCallback;

        glDeleteShader(_id);
        _id = glCreateShader(_type);
        if (_id == 0)
            LERROR("glCreateShader returned 0");
#ifdef GL_VERSION_4_3
        if (glObjectLabel)
            glObjectLabel(GL_SHADER, _id, GLsizei(_shaderName.length() + 1)
                          , _shaderName.c_str());
#endif
        setShaderFilename(_fileName);
    }
    return *this;
}

ShaderObject& ShaderObject::operator=(ShaderObject&& rhs) {
	if (this != &rhs) {
		glDeleteShader(_id);
		_id = rhs._id;
		rhs._id = 0;

		_type = rhs._type;
		_fileName = std::move(rhs._fileName);
		_shaderName = std::move(rhs._shaderName);
		_loggerCat = std::move(rhs._loggerCat);
		_onChangeCallback = rhs._onChangeCallback;
		_trackedFiles = std::move(rhs._trackedFiles);
	}
	return *this;
}

void ShaderObject::setName(std::string name) {
	_shaderName = std::move(name);
	_loggerCat = "ShaderObject['" + _shaderName + "']";
#ifdef GL_VERSION_4_3
	if (glObjectLabel)
		glObjectLabel(GL_SHADER, _id, GLsizei(_shaderName.length() + 1),
		_shaderName.c_str());
#endif
}

const std::string& ShaderObject::name() const {
    return _shaderName;
}

void ShaderObject::setShaderObjectCallback(ShaderObjectCallback changeCallback) {
	_onChangeCallback = changeCallback;

	for (auto fileObject : _trackedFiles) {
		fileObject.second->setCallback(_onChangeCallback);
	}
}

bool ShaderObject::hasName() const {
    return !_shaderName.empty();
}

std::string ShaderObject::filename() {
	return _fileName;
}

bool ShaderObject::setShaderFilename(std::string filename) {
	_fileName = std::move(filename);
    if (_fileName == "") {
        deleteShader();
        return true;
    }

	// Clear tracked files, this might have changed since last time
	for (auto f : _trackedFiles)
		delete f.second;
	_trackedFiles.erase(_trackedFiles.begin(), _trackedFiles.end());

	// No need to alocate a new contents string for every shader
	// This makes ShaderObjects not thread safe
	static std::string contents;
	contents = "";

	bool success = readFile(_fileName, contents);

	// If in debug mode, output the source to file
#ifndef NDEBUG
	std::string generatedFilename;
	ghoul::filesystem::File ghlFile(_fileName);
	if (!FileSys.cacheManager() ||
		!FileSys.cacheManager()->getCachedFile(
			_fileName,
			generatedFilename,
			true)
		)
	{
		// Either the cachemanager wasn't initialized or the
		// filename could not be fetched
		generatedFilename += ".GhoulGenerated.glsl";
	}

	std::ofstream os(generatedFilename);
	os << contents;
	os.close();
#endif

	if (!success)
		return false;

    const char* contentPtr =  contents.c_str();
    glShaderSource(_id, 1, &contentPtr, NULL);

    LINFO("Loaded " + typeAsString() + ": '" + _fileName + "'");
    return true;
}

void ShaderObject::deleteShader() {
    glDeleteShader(_id);
    _id = 0;
}

bool ShaderObject::rebuildFromFile() {
    return setShaderFilename(_fileName);
}

bool ShaderObject::compile() {
    glCompileShader(_id);

    GLint compilationStatus;
    glGetShaderiv(_id, GL_COMPILE_STATUS, &compilationStatus);
    if (compilationStatus == GL_FALSE) {
        GLint logLength;
        glGetShaderiv(_id, GL_INFO_LOG_LENGTH, &logLength);

        if (logLength == 0) {
            LERROR(typeAsString() + " compile error: Unknown error");
            return false;
        }

        GLchar* log = new GLchar[logLength];
        glGetShaderInfoLog(_id, logLength, NULL, log);
        LERROR(typeAsString() + " compile error:\n" + log);
        delete[] log;

        return false;
    }
    return compilationStatus == GL_TRUE;
}

std::string ShaderObject::typeAsString() const {
    return ShaderObject::stringForShaderType(_type);
}

std::string ShaderObject::stringForShaderType(ShaderType type) {
    switch (type) {
    case ShaderTypeVertex:
        return "Vertex shader";
    case ShaderTypeTesselationControl:
        return "Tesselation Control shader";
    case ShaderTypeTesselationEvaluation:
        return "Tesselation Evaluation shader";
    case ShaderTypeGeometry:
        return "Geometry shader";
    case ShaderTypeFragment:
        return "Fragment shader";
#ifdef GL_VERSION_4_3
    case ShaderTypeCompute:
        return "Compute shader";
#endif
    default:
        assert(false);
        return "";
    }
}

bool ShaderObject::readFile(const std::string& filename, std::string& content, bool track) {
	// check that the file exists
	if (!FileSys.fileExists(filename)) {
		LWARNING("Could not find '" << filename << "'");
		return false;
	}

	// Check that the file is currently not in _trackedFiles
	auto s = _trackedFiles.find(filename);
	if (s != _trackedFiles.end()) {
		LWARNING("Already including '" << filename << "'");
		return false;
	}

	// Check that file can be opened
	std::ifstream f(filename, std::ifstream::in);
	if (!f.is_open()) {
		LWARNING("Could not open '" << filename << "': " << strerror(errno));
		return false;
	}

	// Construct a file object
	using namespace ghoul::filesystem;

	// CODE FOR DEBUG PURPOSE
	//filesystem::File::FileChangedCallback c = nullptr;
	//c = [ this](const filesystem::File&){
	//	LDEBUG("Update fired from " << name());
	//	if (_onChangeCallback)
	//		_onChangeCallback;
	//};

	File* fileObject = new File(filename, false);
	if (track) {
		fileObject->setCallback(_onChangeCallback);
		_trackedFiles[filename] = fileObject;
	}

	// Ready to start parsing
	// ugly slightly more efficient version, 3-4x faster
	static std::string line;
	static const std::string ws = " \n\r\t";
	static const std::string includeString = "#include";
	static const std::string notrackString = ":notrack";
	static const std::string versionString = "#version __CONTEXT__";
	while (std::getline(f, line)) {
		size_t start_pos = line.find_first_not_of(ws);
		if (start_pos == std::string::npos)
			start_pos = 0;
		size_t end_pos = line.find_last_not_of(ws);
		if (end_pos == std::string::npos)
			end_pos = line.length();
		else
			end_pos += 1;

		const size_t length = end_pos - start_pos;

		// If begins with #include
		if (length > 11 && line.substr(start_pos, includeString.length()) == includeString) {
			bool success = false;

			size_t p1 = line.find_first_not_of(ws, start_pos + includeString.length());
			size_t p2 = std::string::npos;
			if (p1 != std::string::npos) {
				if (line.at(p1) == '\"') {
					p2 = line.find_first_of("\"", p1 + 1);
					if (p2 != std::string::npos) {
						std::string includeFilename = fileObject->directoryName() + FileSystem::PathSeparator + line.substr(p1 + 1, p2 - p1 - 1);

						size_t remaining = end_pos - p2;
						bool keepTrack = true;
						if (remaining > notrackString.length()) {
							if (line.find_first_of(notrackString, p2) != std::string::npos)
								keepTrack = false;
						}
						content += "// Begin including '" + includeFilename + "'\n";
						if (!readFile(includeFilename, content, track && keepTrack))
							content += "// Warning, unsuccessful loading of '" + includeFilename + "'\n";
						content += "// End including '" + includeFilename + "'\n";
						success = true;
					}
				}
				else if (line.at(p1) == '<') {
					p2 = line.find_first_of(">", p1 + 1);
					if (p2 != std::string::npos) {
						std::string includeFilename = absPath(line.substr(p1 + 1, p2 - p1 - 1));

						size_t remaining = end_pos - p2;
						bool keepTrack = true;
						if (remaining > notrackString.length()) {
							if (line.find_first_of(notrackString, p2) != std::string::npos)
								keepTrack = false;
						}
						content += "// Begin including '" + includeFilename + "'\n";
						if (!readFile(includeFilename, content, track && keepTrack))
							content += "// Warning, unsuccessful loading of '" + includeFilename + "'\n";
						content += "// End including '" + includeFilename + "'\n";
						success = true;
					}
				}
			}
			if (!success) {
				content += "// Error in #include pattern!\n";
			}
		}
		else if (line == versionString) {
			static std::string versionString = glslVersionString();
			content += versionString + "\n";
		}
		else{
			content += line + "\n";
		}
	}

	if (!track) {
		delete fileObject;
	}

	f.close();
	return true;
}

} // namespace opengl
} // namespace ghoul

#ifdef _MSC_VER
#pragma warning(pop)
#endif
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2011-2015 libbitcoin developers (see AUTHORS)
 *
 * This file is part of libbitcoin.
 *
 * libbitcoin is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License with
 * additional permissions to the one published by the Free Software
 * Foundation, either version 3 of the License, or (at your option)
 * any later version. For more information see LICENSE.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */
#include <bitcoin/bitcoin/wallet/mnemonic.hpp>

#include <algorithm>
#include <cstdint>
#include <boost/locale.hpp>
#include <bitcoin/bitcoin/define.hpp>
#include <bitcoin/bitcoin/math/external/pkcs5_pbkdf2.h>
#include <bitcoin/bitcoin/utility/assert.hpp>
#include <bitcoin/bitcoin/utility/binary.hpp>
#include <bitcoin/bitcoin/utility/general.hpp>
#include <bitcoin/bitcoin/wallet/dictionary.hpp>

namespace libbitcoin {
namespace bip39 {

// BIP-39 private constants.
constexpr size_t bits_per_word = 11;
constexpr size_t hmac_iterations = 2048;
constexpr size_t dictionary_length = 2048;
constexpr size_t entropy_bit_divisor = 32;

// It would be nice if we could do this statically.
static void validate_dictionary()
{
    BITCOIN_ASSERT_MSG(dictionary.size() == sizeof(bip39::language),
        "The dictionary does not have the required number of languages.");

#ifndef NDEBUG
    for (const auto& dictionary: bip39::dictionary)
    {
        BITCOIN_ASSERT_MSG(dictionary.second.size() == dictionary_length,
            "A dictionary does not have the required number of elements.");
    }
#endif
}

static const string_list& get_dictionary(bip39::language language)
{
    validate_dictionary();

    const auto it = bip39::dictionary.find(language);

    // We should *always* find the language.
    BITCOIN_ASSERT(it != bip39::dictionary.end());
    return it->second;
}

static bip39::language get_language(const string_list& words)
{
    validate_dictionary();

    if (words.empty())
        return bip39::language::en;

    const auto& first_word = words.front();

    for (const auto& dictionary : bip39::dictionary)
        if (find_position(dictionary.second, first_word) != -1)
            return dictionary.first;

    return bip39::language::en;
}

static std::string normalize_nfkd(const std::string& value)
{
    using namespace boost::locale;
    const generator locale;
    return normalize(value, norm_type::norm_nfkd, locale(""));
}

static uint8_t bip39_shift(size_t bit)
{
    return (1 << (byte_bits - (bit % byte_bits) - 1));
}

// extracts entropy/checksum from the mnemonic and rebuilds the word list
// for comparison; returns true if it's a match (valid), false otherwise
static bool validate_mnemonic(const string_list& words,
    const string_list& dictionary, bip39::language language)
{
    const auto word_count = words.size();
    if ((word_count < min_word_count) || (word_count > max_word_count))
        return false;

    size_t bit = 0;
    data_chunk seed(max_word_count, 0);

    for (const auto& word: words)
    {
        const auto position = find_position(dictionary, word);
        if (position == -1)
            return false;

        for (size_t loop = 0; loop < bits_per_word; loop++, bit++)
        {
            if (position & (1 << (bits_per_word - loop - 1)))
            {
                const auto byte = bit / byte_bits;
                seed[byte] |= bip39_shift(bit);
            }
        }
    }

    const auto size = bit / byte_bits;
    seed.resize(size);

    const auto mnemonic = create_mnemonic(seed, language);
    return std::equal(mnemonic.begin(), mnemonic.end(), words.begin());
}

data_chunk decode_mnemonic(const string_list& mnemonic,
    const std::string& passphrase)
{
    const auto language = get_language(mnemonic);
    const auto& dictionary = get_dictionary(language);

    if (!validate_mnemonic(mnemonic, dictionary, language))
        return data_chunk();

    const auto sentence = join(mnemonic);
    const auto salt = normalize_nfkd("mnemonic" + passphrase);
    const auto salt_chunk = to_data_chunk(salt);

    long_hash hash;
    if (pkcs5_pbkdf2_hmac_sha512(sentence, salt_chunk, hmac_iterations, hash))
        return to_data_chunk(hash);

    return data_chunk();
}

string_list create_mnemonic(data_slice entropy, bip39::language language)
{
    if ((entropy.size() % seed_multiple) != 0)
        return string_list();

    const auto& dictionary = get_dictionary(language);
    const auto hash = sha256_hash(entropy);
    auto chunk = to_data_chunk(entropy);
    extend_data(chunk, hash);

    const size_t entropy_bits = (entropy.size() * byte_bits);
    const size_t check_bits = (entropy_bits / entropy_bit_divisor);
    const size_t total_bits = (entropy_bits + check_bits);
    const size_t word_count = (total_bits / bits_per_word);

    BITCOIN_ASSERT((total_bits % bits_per_word) == 0);
    BITCOIN_ASSERT((word_count % word_multiple) == 0);

    size_t bit = 0;
    string_list words;

    for (size_t word = 0; word < word_count; word++)
    {
        size_t position = 0;
        for (size_t loop = 0; loop < bits_per_word; loop++)
        {
            bit = (word * bits_per_word + loop);
            position <<= 1;

            const auto byte = bit / byte_bits;

            if ((chunk[byte] & bip39_shift(bit)) > 0)
                position++;
        }

        BITCOIN_ASSERT(position < dictionary_length);
        words.push_back(dictionary[position]);
    }

    BITCOIN_ASSERT(words.size() == (bit / bits_per_word));
    return words;
}

} // namespace bip39
} // namespace libbitcoin

<commit_msg>Fix unused variable warning (NDEBUG).<commit_after>/*
 * Copyright (c) 2011-2015 libbitcoin developers (see AUTHORS)
 *
 * This file is part of libbitcoin.
 *
 * libbitcoin is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License with
 * additional permissions to the one published by the Free Software
 * Foundation, either version 3 of the License, or (at your option)
 * any later version. For more information see LICENSE.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */
#include <bitcoin/bitcoin/wallet/mnemonic.hpp>

#include <algorithm>
#include <cstdint>
#include <boost/locale.hpp>
#include <bitcoin/bitcoin/define.hpp>
#include <bitcoin/bitcoin/math/external/pkcs5_pbkdf2.h>
#include <bitcoin/bitcoin/utility/assert.hpp>
#include <bitcoin/bitcoin/utility/binary.hpp>
#include <bitcoin/bitcoin/utility/general.hpp>
#include <bitcoin/bitcoin/wallet/dictionary.hpp>

namespace libbitcoin {
namespace bip39 {

// BIP-39 private constants.
constexpr size_t bits_per_word = 11;
constexpr size_t entropy_bit_divisor = 32;
constexpr size_t hmac_iterations = 2048;
DEBUG_ONLY(constexpr size_t dictionary_length = 2048;)

// It would be nice if we could do this statically.
static void validate_dictionary()
{
    BITCOIN_ASSERT_MSG(dictionary.size() == sizeof(bip39::language),
        "The dictionary does not have the required number of languages.");

#ifndef NDEBUG
    for (const auto& dictionary: bip39::dictionary)
    {
        BITCOIN_ASSERT_MSG(dictionary.second.size() == dictionary_length,
            "A dictionary does not have the required number of elements.");
    }
#endif
}

static const string_list& get_dictionary(bip39::language language)
{
    validate_dictionary();

    const auto it = bip39::dictionary.find(language);

    // Guards against lack of uniqueness in dictionary languages.
    BITCOIN_ASSERT(it != bip39::dictionary.end());
    return it->second;
}

static bip39::language get_language(const string_list& words)
{
    validate_dictionary();

    if (words.empty())
        return bip39::language::en;

    const auto& first_word = words.front();

    for (const auto& dictionary : bip39::dictionary)
        if (find_position(dictionary.second, first_word) != -1)
            return dictionary.first;

    return bip39::language::en;
}

static std::string normalize_nfkd(const std::string& value)
{
    using namespace boost::locale;
    const generator locale;
    return normalize(value, norm_type::norm_nfkd, locale(""));
}

static uint8_t bip39_shift(size_t bit)
{
    return (1 << (byte_bits - (bit % byte_bits) - 1));
}

// extracts entropy/checksum from the mnemonic and rebuilds the word list
// for comparison; returns true if it's a match (valid), false otherwise
static bool validate_mnemonic(const string_list& words,
    const string_list& dictionary, bip39::language language)
{
    const auto word_count = words.size();
    if ((word_count < min_word_count) || (word_count > max_word_count))
        return false;

    size_t bit = 0;
    data_chunk seed(max_word_count, 0);

    for (const auto& word: words)
    {
        const auto position = find_position(dictionary, word);
        if (position == -1)
            return false;

        for (size_t loop = 0; loop < bits_per_word; loop++, bit++)
        {
            if (position & (1 << (bits_per_word - loop - 1)))
            {
                const auto byte = bit / byte_bits;
                seed[byte] |= bip39_shift(bit);
            }
        }
    }

    const auto size = bit / byte_bits;
    seed.resize(size);

    const auto mnemonic = create_mnemonic(seed, language);
    return std::equal(mnemonic.begin(), mnemonic.end(), words.begin());
}

data_chunk decode_mnemonic(const string_list& mnemonic,
    const std::string& passphrase)
{
    const auto language = get_language(mnemonic);
    const auto& dictionary = get_dictionary(language);

    if (!validate_mnemonic(mnemonic, dictionary, language))
        return data_chunk();

    const auto sentence = join(mnemonic);
    const auto salt = normalize_nfkd("mnemonic" + passphrase);
    const auto salt_chunk = to_data_chunk(salt);

    long_hash hash;
    if (pkcs5_pbkdf2_hmac_sha512(sentence, salt_chunk, hmac_iterations, hash))
        return to_data_chunk(hash);

    return data_chunk();
}

string_list create_mnemonic(data_slice entropy, bip39::language language)
{
    if ((entropy.size() % seed_multiple) != 0)
        return string_list();

    const auto& dictionary = get_dictionary(language);
    const auto hash = sha256_hash(entropy);
    auto chunk = to_data_chunk(entropy);
    extend_data(chunk, hash);

    const size_t entropy_bits = (entropy.size() * byte_bits);
    const size_t check_bits = (entropy_bits / entropy_bit_divisor);
    const size_t total_bits = (entropy_bits + check_bits);
    const size_t word_count = (total_bits / bits_per_word);

    BITCOIN_ASSERT((total_bits % bits_per_word) == 0);
    BITCOIN_ASSERT((word_count % word_multiple) == 0);

    size_t bit = 0;
    string_list words;

    for (size_t word = 0; word < word_count; word++)
    {
        size_t position = 0;
        for (size_t loop = 0; loop < bits_per_word; loop++)
        {
            bit = (word * bits_per_word + loop);
            position <<= 1;

            const auto byte = bit / byte_bits;

            if ((chunk[byte] & bip39_shift(bit)) > 0)
                position++;
        }

        BITCOIN_ASSERT(position < dictionary_length);
        words.push_back(dictionary[position]);
    }

    BITCOIN_ASSERT(words.size() == (bit / bits_per_word));
    return words;
}

} // namespace bip39
} // namespace libbitcoin

<|endoftext|>