<commit_before>/*********************************************************************************
 *
 * Inviwo - Interactive Visualization Workshop
 *
 * Copyright (c) 2014-2017 Inviwo Foundation
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *********************************************************************************/

#include <warn/push>
#include <warn/ignore/all>
#include <gtest/gtest.h>
#include <warn/pop>

#include <inviwo/core/util/zip.h>

#include <array>
#include <vector>
#include <list>
#include <forward_list>

#include <numeric>
#include <tuple>
#include <memory>
#include <utility>

template <size_t n, typename... T>
typename std::enable_if<(n >= sizeof...(T))>::type print_tuple(std::ostream&,
                                                               const std::tuple<T...>&) {}

template <size_t n, typename... T>
typename std::enable_if<(n > 0 && n < sizeof...(T))>::type print_tuple(
    std::ostream& os, const std::tuple<T...>& tup) {
    os << ", " << std::get<n>(tup);
    print_tuple<n + 1>(os, tup);
}

template <size_t n, typename... T>
typename std::enable_if<(n == 0)>::type print_tuple(std::ostream& os, const std::tuple<T...>& tup) {
    os << std::get<n>(tup);
    print_tuple<n + 1>(os, tup);
}

template <typename... T>
std::ostream& operator<<(std::ostream& os, const std::tuple<T...>& tup) {
    os << "[";
    print_tuple<0>(os, tup);
    return os << "]";
}

// no copy/move
struct S {
    S() = delete;
    S(const S&) = delete;
    S(S&&) = delete;
    S& operator=(const S&) = delete;
    S& operator=(S&&) = delete;

    friend constexpr bool operator==(const S& a, const S& b)  noexcept { return a.a_ == b.a_;}

    constexpr int get() const  noexcept { return a_; }

    constexpr explicit S(int a)  noexcept : a_{ a } {}
    int a_ = 0;
};
::std::ostream& operator<<(::std::ostream& os, const S& s) {
    return os << "S(" << s.a_ << ")";
}

// default type
struct D {
    constexpr D() noexcept = default;
    constexpr D(const D&) noexcept = default;
    constexpr D(D&&) noexcept = default;
    D& operator=(const D&) noexcept = default;
    D& operator=(D&&) noexcept = default;

    friend constexpr bool operator==(const D& a, const D& b) noexcept { return a.a_ == b.a_; }

    constexpr int get() const  noexcept { return a_; }

    constexpr explicit D(int a) noexcept : a_{ a } {}
    int a_ = 0;
};
::std::ostream& operator<<(::std::ostream& os, const D& d) {
    return os << "D(" << d.a_ << ")";
}

constexpr const std::array<int, 5> ints{{1, 2, 3, 4, 5}};
constexpr const std::array<D, 5> ds{{D{10}, D{20}, D{30}, D{40}, D{50}}};

namespace inviwo {

template <typename Zipped>
void forwadTest(Zipped&& iter) {
    using Iter = typename std::decay_t<decltype(iter)>::iterator;

    // Iterator default constructible
    Iter defaltConstruct{};

    // Iterator requirements
    Iter copy{defaltConstruct};
    Iter assign = defaltConstruct;
    std::swap(copy, assign);

    {
        auto i = iter.begin();
        auto j = iter.end();
        for (size_t c = 0; c < ints.size(); ++c) {
            auto ref = std::tuple<int, D>{ints[c], ds[c]};
            EXPECT_EQ(ref, *i);
            Iter& i2 = ++i;
            EXPECT_EQ(i2, i);
        }
    }
    {
        auto i = iter.begin();
        auto j = iter.end();
        // InputIterator requirements
        EXPECT_EQ(true, i != j);
        EXPECT_EQ(false, i == j);
        typename Iter::value_type vt1 = *i;
        typename Iter::value_type vt2 = *i++;
        auto ref = std::tuple<int, D>{1, D{10}};
        EXPECT_EQ(ref, vt1);
        EXPECT_EQ(ref, vt2);

        i = iter.begin();
        for (size_t c = 0; c < ints.size(); ++c) {
            i++;
        }
        EXPECT_EQ(true, i == j);
    }
    // ForwardIterator requirements
    // same as InputIterator + multi pass
    {
        auto i = iter.begin();
        auto j = iter.end();
        for (size_t c = 0; c < ints.size(); ++c) {
            auto ref = std::tuple<int, D>{ ints[c], ds[c] };
            EXPECT_EQ(ref, *i);
            Iter i1 = i;
            Iter i2 = i++;
            EXPECT_EQ(i2, i1);
        }
    }
}

template <typename Zipped>
void bidirectionalTest(Zipped&& iter) {
    {
        auto i = iter.begin();
        auto j = iter.end();
        std::advance(i, 2);

        auto ref0 = std::tuple<int, D>{3, D{30}};
        EXPECT_EQ(ref0, *i);

        using Iter = typename std::decay_t<decltype(iter)>::iterator;
        // BidirectionalIterator requirements
        Iter& i4 = --i;

        auto ref1 = std::tuple<int, D>{2, D{20}};
        EXPECT_EQ(ref1, *i4);
        EXPECT_EQ(ref1, *i);

        // const Iter& i5 = i--;
        typename Iter::value_type vt1 = *i;
        typename Iter::value_type vt2 = *i--;

        EXPECT_EQ(ref1, vt1);
        EXPECT_EQ(ref1, vt2);

        auto ref2 = std::tuple<int, D>{ 1, D{10} };
        EXPECT_EQ(ref2, *i);

    }
    {
        auto i = iter.begin();
        auto j1 = iter.end();
        auto j2 = iter.end();

        for (int c = static_cast<int>(ints.size()) - 1; c >= 0; --c) {
            auto val = *j2--;
            EXPECT_EQ(true, val == *j1);
            --j1;

            auto ref3 = std::tuple<int, D>{ints[c], ds[c]};
            EXPECT_EQ(ref3, *j1);
            EXPECT_EQ(ref3, *j2);
            EXPECT_EQ(true, j1 == j2);
        }
        EXPECT_EQ(*i, *j1);
        EXPECT_EQ(true, i == j1);
        EXPECT_EQ(true, i == j2);
    }
}

template <typename Zipped>
void randomAccessTest(Zipped&& iter) {
    using Iter = typename std::decay_t<decltype(iter)>::iterator;
    // RandomAccessIterator requirements
    using DT = typename Iter::difference_type;

    for (DT n = 0; n < static_cast<DT>(ints.size()); ++n) {

        {
            auto i = iter.begin();
            Iter& i1 = i += n;
            auto ref = std::tuple<int, D>{ints[n], ds[n]};
            EXPECT_EQ(ref, *i1);
        }

        {
            auto i = iter.begin();
            Iter i2 = i + n;
            Iter i3 = n + i;
            auto ref = std::tuple<int, D>{ints[n], ds[n]};
            EXPECT_EQ(ref, *i2);
            EXPECT_EQ(ref, *i3);
        }
        {
            auto i = iter.end();
            Iter i5 = i - (n + 1);
            auto ref = std::tuple<int, D>{ints[ints.size() - n - 1], ds[ints.size() - n - 1]};
            EXPECT_EQ(ref, *i5);
        }
        {
            auto i = iter.end();
            Iter& i4 = i -= (n + 1);
            auto ref = std::tuple<int, D>{ints[ints.size() - n - 1], ds[ints.size() - n - 1]};
            EXPECT_EQ(ref, *i4);
        }
    }
    {
        auto j = iter.end();
        auto i = iter.begin();
        for (DT n = 0; n < static_cast<DT>(ints.size()); ++n) {
            DT size = j - i;
            EXPECT_EQ(5 - n, size);
            ++i;
        }
    }

    {
        auto i = iter.begin();
        for (DT n = 0; n < static_cast<DT>(ints.size()); ++n) {
            typename Iter::reference vr1 = i[n];
            auto ref = std::tuple<int, D>{ ints[n], ds[n] };
            EXPECT_EQ(ref, vr1);
            EXPECT_EQ(ref, i[n]);
        }
    }
    {
        auto i = iter.begin();
        auto j = iter.end();
        i < j;
        i > j;
        i <= j;
        i >= j;
    }
}

TEST(ZipIterTest, ForwardIter) {
    std::forward_list<int> fl1{ ints.begin(), ints.end() };
    std::forward_list<D> fl2{ds.begin(), ds.end()};

    auto iter = util::zip(fl1, fl2);
    forwadTest(iter);
}

TEST(ZipIterTest, BidirectionalIter) {
    std::list<int> bl1{ ints.begin(), ints.end() };
    std::list<D> bl2{ds.begin(), ds.end()};

    auto iter = util::zip(bl1, bl2);
    forwadTest(iter);
    bidirectionalTest(iter);

}

TEST(ZipIterTest, RandomAccessIter) {
    std::vector<int> bl1{ ints.begin(), ints.end() };
    std::vector<D> bl2{ds.begin(), ds.end()};

    auto iter = util::zip(bl1, bl2);
    forwadTest(iter);
    bidirectionalTest(iter);
    randomAccessTest(iter);
}

TEST(ZipIterTest, Minimal) {
    std::vector<int> a(10);
    std::iota(a.begin(), a.end(), 0);
    int count = 0;
    for (auto&& i : util::zip(a)) {
        EXPECT_EQ(count, std::get<0>(i));
        ++count;
    }
}

TEST(ZipIterTest, Pair) {
    std::vector<int> a(10);
    std::vector<int> b(10);

    std::iota(a.begin(), a.end(), 0);
    std::iota(b.begin(), b.end(), 10);

    int count = 0;
    for (auto&& i : util::zip(a, b)) {
        EXPECT_EQ(count, std::get<0>(i));
        EXPECT_EQ(count+10, std::get<1>(i));
        ++count;
    }
}

TEST(ZipIterTest, NoCopy) {
    std::vector<std::unique_ptr<S>> a;
    std::vector<int> b;

    for (int i = 0; i<10; ++i) {
        a.push_back(std::make_unique<S>(i));
        b.push_back(i+10);
    }

    int count = 0;
    for (auto&& i : util::zip(a, b)) {
        EXPECT_EQ(count, std::get<0>(i)->a_);
        EXPECT_EQ(count + 10, std::get<1>(i));
        ++count;
    }
}

TEST(ZipIterTest, Sort) {
    std::vector<int> a(10);
    std::vector<int> b(10);

    std::iota(a.begin(), a.end(), 0);
    std::iota(b.begin(), b.end(), 10);

    std::reverse(a.begin(), a.end());

    auto z = util::zip(a, b);
    std::sort(z.begin(), z.end(), [](auto& a, auto& b){
        return std::get<0>(a) < std::get<0>(b);
    });

    EXPECT_EQ(19, b.front());
    EXPECT_EQ(10, b.back());
}

TEST(SequencerTest, Ranges) {
    int count = 0;
    auto r1 = util::make_sequence(0, 10, 1);
    auto r2 = util::make_sequence(10, 20, 1);
    for (auto&& i : util::zip(r1, r2)) {
        EXPECT_EQ(count, std::get<0>(i));
        EXPECT_EQ(count + 10, std::get<1>(i));
        ++count;
    }
}

TEST(SequencerTest, DifferenLenght1) {
    int count = 0;
    auto r1 = util::make_sequence(0, 10, 1);
    auto r2 = util::make_sequence(10, 30, 1);
    for (auto&& i : util::zip(r1, r2)) {
        EXPECT_EQ(count, std::get<0>(i));
        EXPECT_EQ(count + 10, std::get<1>(i));
        ++count;
    }
}
TEST(SequencerTest, DifferenLenght2) {
    int count = 0;
    auto r1 = util::make_sequence(0, 20, 1);
    auto r2 = util::make_sequence(10, 20, 1);
    for (auto&& i : util::zip(r1, r2)) {
        EXPECT_EQ(count, std::get<0>(i));
        EXPECT_EQ(count + 10, std::get<1>(i));
        ++count;
    }
}

TEST(SequencerTest, Temporaries) {
    int count = 0;
    for (auto&& i : util::zip(util::make_sequence(0, 20, 1), util::make_sequence(10, 20, 1))) {
        EXPECT_EQ(count, std::get<0>(i));
        EXPECT_EQ(count + 10, std::get<1>(i));
        ++count;
    }
}

}  // namespace
<commit_msg>Core: Zip, jenkins fix?<commit_after>/*********************************************************************************
 *
 * Inviwo - Interactive Visualization Workshop
 *
 * Copyright (c) 2014-2017 Inviwo Foundation
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *********************************************************************************/

#include <warn/push>
#include <warn/ignore/all>
#include <gtest/gtest.h>
#include <warn/pop>

#include <inviwo/core/util/zip.h>

#include <array>
#include <vector>
#include <list>
#include <forward_list>

#include <numeric>
#include <tuple>
#include <memory>
#include <utility>

template <size_t n, typename... T>
typename std::enable_if<(n >= sizeof...(T))>::type print_tuple(std::ostream&,
                                                               const std::tuple<T...>&) {}

template <size_t n, typename... T>
typename std::enable_if<(n > 0 && n < sizeof...(T))>::type print_tuple(
    std::ostream& os, const std::tuple<T...>& tup) {
    os << ", " << std::get<n>(tup);
    print_tuple<n + 1>(os, tup);
}

template <size_t n, typename... T>
typename std::enable_if<(n == 0)>::type print_tuple(std::ostream& os, const std::tuple<T...>& tup) {
    os << std::get<n>(tup);
    print_tuple<n + 1>(os, tup);
}

template <typename... T>
std::ostream& operator<<(std::ostream& os, const std::tuple<T...>& tup) {
    os << "[";
    print_tuple<0>(os, tup);
    return os << "]";
}

// no copy/move
struct S {
    S() = delete;
    S(const S&) = delete;
    S(S&&) = delete;
    S& operator=(const S&) = delete;
    S& operator=(S&&) = delete;

    friend constexpr bool operator==(const S& a, const S& b)  noexcept { return a.a_ == b.a_;}

    constexpr int get() const  noexcept { return a_; }

    constexpr explicit S(int a)  noexcept : a_{ a } {}
    int a_ = 0;
};
::std::ostream& operator<<(::std::ostream& os, const S& s) {
    return os << "S(" << s.a_ << ")";
}

// default type
struct D {
    constexpr D() noexcept = default;
    constexpr D(const D&) noexcept = default;
    constexpr D(D&&) noexcept = default;
    D& operator=(const D&) noexcept = default;
    D& operator=(D&&) noexcept = default;

    friend constexpr bool operator==(const D& a, const D& b) noexcept { return a.a_ == b.a_; }

    constexpr int get() const  noexcept { return a_; }

    constexpr explicit D(int a) noexcept : a_{ a } {}
    int a_ = 0;
};
::std::ostream& operator<<(::std::ostream& os, const D& d) {
    return os << "D(" << d.a_ << ")";
}

constexpr const std::array<int, 5> ints{{1, 2, 3, 4, 5}};
constexpr const std::array<D, 5> ds{{D{10}, D{20}, D{30}, D{40}, D{50}}};

namespace inviwo {

template <typename Zipped>
void forwadTest(Zipped&& iter) {
    using Iter = typename std::decay_t<decltype(iter)>::iterator;

    // Iterator default constructible
    Iter defaltConstruct{};

    // Iterator requirements
    Iter copy{defaltConstruct};
    Iter assign = defaltConstruct;
    std::swap(copy, assign);

    {
        auto i = iter.begin();
        auto j = iter.end();
        for (size_t c = 0; c < ints.size(); ++c) {
            auto ref = std::tuple<int, D>{ints[c], ds[c]};
            EXPECT_EQ(ref, *i);
            Iter& i2 = ++i;
            EXPECT_EQ(i2, i);
        }
    }
    {
        auto i = iter.begin();
        auto j = iter.end();
        // InputIterator requirements
        EXPECT_EQ(true, i != j);
        EXPECT_EQ(false, i == j);
        typename Iter::value_type vt1 = *i;
        typename Iter::value_type vt2 = *i++;
        auto ref = std::tuple<int, D>{1, D{10}};
        EXPECT_EQ(ref, vt1);
        EXPECT_EQ(ref, vt2);

        i = iter.begin();
        for (size_t c = 0; c < ints.size(); ++c) {
            i++;
        }
        EXPECT_EQ(true, i == j);
    }
    // ForwardIterator requirements
    // same as InputIterator + multi pass
    {
        auto i = iter.begin();
        auto j = iter.end();
        for (size_t c = 0; c < ints.size(); ++c) {
            auto ref = std::tuple<int, D>{ ints[c], ds[c] };
            EXPECT_EQ(ref, *i);
            Iter i1 = i;
            Iter i2 = i++;
            EXPECT_EQ(i2, i1);
        }
    }
}

template <typename Zipped>
void bidirectionalTest(Zipped&& iter) {
    {
        auto i = iter.begin();
        auto j = iter.end();
        std::advance(i, 2);

        auto ref0 = std::tuple<int, D>{3, D{30}};
        EXPECT_EQ(ref0, *i);

        using Iter = typename std::decay_t<decltype(iter)>::iterator;
        // BidirectionalIterator requirements
        Iter& i4 = --i;

        auto ref1 = std::tuple<int, D>{2, D{20}};
        EXPECT_EQ(ref1, *i4);
        EXPECT_EQ(ref1, *i);

        // const Iter& i5 = i--;
        typename Iter::value_type vt1 = *i;
        typename Iter::value_type vt2 = *i--;

        EXPECT_EQ(ref1, vt1);
        EXPECT_EQ(ref1, vt2);

        auto ref2 = std::tuple<int, D>{ 1, D{10} };
        EXPECT_EQ(ref2, *i);

    }
    {
        auto i = iter.begin();
        auto j1 = iter.end();
        auto j2 = iter.end();

        for (int c = static_cast<int>(ints.size()) - 1; c >= 0; --c) {
            auto val = *j2--;
            EXPECT_EQ(true, val == *j1);
            --j1;

            auto ref3 = std::tuple<int, D>{ints[c], ds[c]};
            EXPECT_EQ(ref3, *j1);
            EXPECT_EQ(ref3, *j2);
            EXPECT_EQ(true, j1 == j2);
        }
        EXPECT_EQ(*i, *j1);
        EXPECT_EQ(true, i == j1);
        EXPECT_EQ(true, i == j2);
    }
}

template <typename Zipped>
void randomAccessTest(Zipped&& iter) {
    using Iter = typename std::decay_t<decltype(iter)>::iterator;
    // RandomAccessIterator requirements
    using DT = typename Iter::difference_type;

    for (DT n = 0; n < static_cast<DT>(ints.size()); ++n) {

        {
            auto i = iter.begin();
            Iter& i1 = i += n;
            auto ref = std::tuple<int, D>{ints[n], ds[n]};
            EXPECT_EQ(ref, *i1);
        }

        {
            auto i = iter.begin();
            Iter i2 = i + n;
            Iter i3 = n + i;
            auto ref = std::tuple<int, D>{ints[n], ds[n]};
            EXPECT_EQ(ref, *i2);
            EXPECT_EQ(ref, *i3);
        }
        {
            auto i = iter.end();
            Iter i5 = i - (n + 1);
            auto ref = std::tuple<int, D>{ints[ints.size() - n - 1], ds[ints.size() - n - 1]};
            EXPECT_EQ(ref, *i5);
        }
        {
            auto i = iter.end();
            Iter& i4 = i -= (n + 1);
            auto ref = std::tuple<int, D>{ints[ints.size() - n - 1], ds[ints.size() - n - 1]};
            EXPECT_EQ(ref, *i4);
        }
    }
    {
        auto j = iter.end();
        auto i = iter.begin();
        for (DT n = 0; n < static_cast<DT>(ints.size()); ++n) {
            DT size = j - i;
            EXPECT_EQ(5 - n, size);
            ++i;
        }
    }

    {
        auto i = iter.begin();
        for (DT n = 0; n < static_cast<DT>(ints.size()); ++n) {
            typename Iter::reference vr1 = i[n];
            auto ref = std::tuple<int, D>{ ints[n], ds[n] };
            EXPECT_EQ(ref, vr1);
            EXPECT_EQ(ref, i[n]);
        }
    }
    {
        auto i = iter.begin();
        auto j = iter.end();
        i < j;
        i > j;
        i <= j;
        i >= j;
    }
}

TEST(ZipIterTest, ForwardIter) {
    std::forward_list<int> fl1{ ints.begin(), ints.end() };
    std::forward_list<D> fl2{ds.begin(), ds.end()};

    auto iter = util::zip(fl1, fl2);
    forwadTest(iter);
}

TEST(ZipIterTest, BidirectionalIter) {
    std::list<int> bl1{ ints.begin(), ints.end() };
    std::list<D> bl2{ds.begin(), ds.end()};

    auto iter = util::zip(bl1, bl2);
    forwadTest(iter);
    bidirectionalTest(iter);

}

TEST(ZipIterTest, RandomAccessIter) {
    std::vector<int> bl1{ ints.begin(), ints.end() };
    std::vector<D> bl2{ds.begin(), ds.end()};

    auto iter = util::zip(bl1, bl2);
    forwadTest(iter);
    bidirectionalTest(iter);
    randomAccessTest(iter);
}

TEST(ZipIterTest, Minimal) {
    std::vector<int> a(10);
    std::iota(a.begin(), a.end(), 0);
    int count = 0;
    for (auto&& i : util::zip(a)) {
        EXPECT_EQ(count, std::get<0>(i));
        ++count;
    }
}

TEST(ZipIterTest, Pair) {
    std::vector<int> a(10);
    std::vector<int> b(10);

    std::iota(a.begin(), a.end(), 0);
    std::iota(b.begin(), b.end(), 10);

    int count = 0;
    for (auto&& i : util::zip(a, b)) {
        EXPECT_EQ(count, std::get<0>(i));
        EXPECT_EQ(count+10, std::get<1>(i));
        ++count;
    }
}

TEST(ZipIterTest, NoCopy) {
    std::vector<std::unique_ptr<S>> a;
    std::vector<int> b;

    for (int i = 0; i<10; ++i) {
        a.push_back(std::make_unique<S>(i));
        b.push_back(i+10);
    }

    int count = 0;
    for (auto&& i : util::zip(a, b)) {
        EXPECT_EQ(count, std::get<0>(i)->a_);
        EXPECT_EQ(count + 10, std::get<1>(i));
        ++count;
    }
}

TEST(ZipIterTest, Sort) {
    std::vector<int> a(10);
    std::vector<int> b(10);

    std::iota(a.begin(), a.end(), 0);
    std::iota(b.begin(), b.end(), 10);

    std::reverse(a.begin(), a.end());

    auto z = util::zip(a, b);
    std::sort(z.begin(), z.end(), [](const auto& a, const auto& b) -> bool {
        return std::get<0>(a) < std::get<0>(b);
    });

    EXPECT_EQ(19, b.front());
    EXPECT_EQ(10, b.back());
}

TEST(SequencerTest, Ranges) {
    int count = 0;
    auto r1 = util::make_sequence(0, 10, 1);
    auto r2 = util::make_sequence(10, 20, 1);
    for (auto&& i : util::zip(r1, r2)) {
        EXPECT_EQ(count, std::get<0>(i));
        EXPECT_EQ(count + 10, std::get<1>(i));
        ++count;
    }
}

TEST(SequencerTest, DifferenLenght1) {
    int count = 0;
    auto r1 = util::make_sequence(0, 10, 1);
    auto r2 = util::make_sequence(10, 30, 1);
    for (auto&& i : util::zip(r1, r2)) {
        EXPECT_EQ(count, std::get<0>(i));
        EXPECT_EQ(count + 10, std::get<1>(i));
        ++count;
    }
}
TEST(SequencerTest, DifferenLenght2) {
    int count = 0;
    auto r1 = util::make_sequence(0, 20, 1);
    auto r2 = util::make_sequence(10, 20, 1);
    for (auto&& i : util::zip(r1, r2)) {
        EXPECT_EQ(count, std::get<0>(i));
        EXPECT_EQ(count + 10, std::get<1>(i));
        ++count;
    }
}

TEST(SequencerTest, Temporaries) {
    int count = 0;
    for (auto&& i : util::zip(util::make_sequence(0, 20, 1), util::make_sequence(10, 20, 1))) {
        EXPECT_EQ(count, std::get<0>(i));
        EXPECT_EQ(count + 10, std::get<1>(i));
        ++count;
    }
}

}  // namespace
<|endoftext|>


<commit_before>/*
 * Copyright © 2012, United States Government, as represented by the
 * Administrator of the National Aeronautics and Space Administration.
 * All rights reserved.
 *
 * The NASA Tensegrity Robotics Toolkit (NTRT) v1 platform is licensed
 * under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * http://www.apache.org/licenses/LICENSE-2.0.
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 * either express or implied. See the License for the specific language
 * governing permissions and limitations under the License.
 */

/**
 * @file tgWorldBulletPhysicsImpl.cpp
 * @brief Contains the definitions of members of class tgWorldBulletPhysicsImpl
 * @author Lee Brownston
 * $Id$
 */

// This module
#include "tgWorldBulletPhysicsImpl.h"
// This application
#include "tgWorld.h"
#include "terrain/tgBulletGround.h"
// The Bullet Physics library
#include "BulletCollision/BroadphaseCollision/btBroadphaseInterface.h"
#include "BulletCollision/BroadphaseCollision/btDbvtBroadphase.h"
#include "BulletCollision/BroadphaseCollision/btAxisSweep3.h" // New broadphase
#include "BulletCollision/CollisionDispatch/btCollisionDispatcher.h"
#include "BulletCollision/CollisionDispatch/btDefaultCollisionConfiguration.h"
#include "BulletCollision/CollisionShapes/btBoxShape.h"
#include "BulletCollision/CollisionShapes/btBvhTriangleMeshShape.h"
#include "BulletDynamics/Dynamics/btRigidBody.h"
#include "BulletDynamics/ConstraintSolver/btSequentialImpulseConstraintSolver.h"
#include "BulletSoftBody/btSoftBodyRigidBodyCollisionConfiguration.h"
#include "BulletSoftBody/btSoftRigidDynamicsWorld.h"
#include "LinearMath/btDefaultMotionState.h"
#include "LinearMath/btScalar.h"
#include "LinearMath/btTransform.h"
#include "LinearMath/btVector3.h"

/**
 * Helper class to bundle objects that have the same life cycle, so they can be
 * constructed and destructed together.
 */
class IntermediateBuildProducts
{
    public:
        IntermediateBuildProducts(double worldSize) :
            corner1 (-worldSize,-worldSize, -worldSize),
            corner2 (worldSize, worldSize, worldSize),
            dispatcher(&collisionConfiguration),
#if (1) // More acc broadphase - remeber the comma
            broadphase(corner1, corner2, 16384)
#endif
            {
            }
        const btVector3 corner1;
        const btVector3 corner2;
        btSoftBodyRigidBodyCollisionConfiguration collisionConfiguration;
        btCollisionDispatcher dispatcher;
#if (0) // Default broadphase
        btDbvtBroadphase broadphase;
#else
        // More accurate broadphase:
        btAxisSweep3 broadphase;
#endif
        btSequentialImpulseConstraintSolver solver;
};

tgWorldBulletPhysicsImpl::tgWorldBulletPhysicsImpl(const tgWorld::Config& config,
        tgBulletGround* ground) :
    tgWorldImpl(config, ground),
    m_pIntermediateBuildProducts(new IntermediateBuildProducts(config.worldSize)),
    m_pDynamicsWorld(createDynamicsWorld())
{
    // Gravitational acceleration is down on the Y axis
    const btVector3 gravityVector(0, -config.gravity, 0);
    m_pDynamicsWorld->setGravity(gravityVector);

	m_pDynamicsWorld->addRigidBody(ground->getGroundRigidBody());

    #if (1) /// @todo This is a line from the old BasicLearningApp.cpp that we're not using. Investigate further
        m_pDynamicsWorld->getSolverInfo().m_splitImpulse = true;
    #endif
    // Postcondition
    assert(invariant());
}

tgWorldBulletPhysicsImpl::~tgWorldBulletPhysicsImpl()
{
    // Delete all the collision objects. The dynamics world must exist.
    // Delete in reverse order of creation.
    const size_t nco = m_pDynamicsWorld->getNumCollisionObjects();
    btCollisionObjectArray& oa = m_pDynamicsWorld->getCollisionObjectArray();
    for (int i = nco - 1; i >= 0; --i)
    {
        btCollisionObject * const pCollisionObject = oa[i];

        // If the collision object is a rigid body, delete its motion state
        const btRigidBody* const pRigidBody =
            btRigidBody::upcast(pCollisionObject);
        if (pRigidBody)
        {
            delete pRigidBody->getMotionState();
        }

        // Remove the collision object from the dynamics world
        m_pDynamicsWorld->removeCollisionObject(pCollisionObject);
        // Delete the collision object
        delete pCollisionObject;
    }
    // All collision objects have been removed and deleted
    assert(m_pDynamicsWorld->getNumCollisionObjects() == 0);

    // Delete all the collision shapes. This can be done at any time.

    for (size_t i = 0; i < nco; ++i) { delete m_collisionShapes[i]; }

    delete m_pDynamicsWorld;

    // Delete the intermediate build products, which are now orphaned
    delete m_pIntermediateBuildProducts;
}

/**
 * Create and return a new instance of a btSoftRigidDynamicsWorld.
 * @return a pointer to a new instance of a btSoftRigidDynamicsWorld
 */
btSoftRigidDynamicsWorld* tgWorldBulletPhysicsImpl::createDynamicsWorld() const
{
    btSoftRigidDynamicsWorld * const result =
        new btSoftRigidDynamicsWorld(&m_pIntermediateBuildProducts->dispatcher,
                &m_pIntermediateBuildProducts->broadphase,
                &m_pIntermediateBuildProducts->solver,
                &m_pIntermediateBuildProducts->collisionConfiguration);

    return result;
}

void tgWorldBulletPhysicsImpl::step(double dt)
{
    // Precondition
    assert(dt > 0.0);

    const btScalar timeStep = dt;
    const int maxSubSteps = 1;
    const btScalar fixedTimeStep = dt;
    m_pDynamicsWorld->stepSimulation(timeStep, maxSubSteps, fixedTimeStep);

    // Postcondition
    assert(invariant());
}

void tgWorldBulletPhysicsImpl::addCollisionShape(btCollisionShape* pShape)
{
    if (pShape)
    {
        m_collisionShapes.push_back(pShape);
    }

      // Postcondition
      assert(invariant());
}

bool tgWorldBulletPhysicsImpl::invariant() const
{
    return (m_pDynamicsWorld != 0);
}

<commit_msg>Fixed the 'typo', actually required declaration of a different variable altogether<commit_after>/*
 * Copyright © 2012, United States Government, as represented by the
 * Administrator of the National Aeronautics and Space Administration.
 * All rights reserved.
 *
 * The NASA Tensegrity Robotics Toolkit (NTRT) v1 platform is licensed
 * under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * http://www.apache.org/licenses/LICENSE-2.0.
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 * either express or implied. See the License for the specific language
 * governing permissions and limitations under the License.
 */

/**
 * @file tgWorldBulletPhysicsImpl.cpp
 * @brief Contains the definitions of members of class tgWorldBulletPhysicsImpl
 * @author Lee Brownston
 * $Id$
 */

// This module
#include "tgWorldBulletPhysicsImpl.h"
// This application
#include "tgWorld.h"
#include "terrain/tgBulletGround.h"
// The Bullet Physics library
#include "BulletCollision/BroadphaseCollision/btBroadphaseInterface.h"
#include "BulletCollision/BroadphaseCollision/btDbvtBroadphase.h"
#include "BulletCollision/BroadphaseCollision/btAxisSweep3.h" // New broadphase
#include "BulletCollision/CollisionDispatch/btCollisionDispatcher.h"
#include "BulletCollision/CollisionDispatch/btDefaultCollisionConfiguration.h"
#include "BulletCollision/CollisionShapes/btBoxShape.h"
#include "BulletCollision/CollisionShapes/btBvhTriangleMeshShape.h"
#include "BulletDynamics/Dynamics/btRigidBody.h"
#include "BulletDynamics/ConstraintSolver/btSequentialImpulseConstraintSolver.h"
#include "BulletSoftBody/btSoftBodyRigidBodyCollisionConfiguration.h"
#include "BulletSoftBody/btSoftRigidDynamicsWorld.h"
#include "LinearMath/btDefaultMotionState.h"
#include "LinearMath/btScalar.h"
#include "LinearMath/btTransform.h"
#include "LinearMath/btVector3.h"

/**
 * Helper class to bundle objects that have the same life cycle, so they can be
 * constructed and destructed together.
 */
class IntermediateBuildProducts
{
    public:
        IntermediateBuildProducts(double worldSize) :
            corner1 (-worldSize,-worldSize, -worldSize),
            corner2 (worldSize, worldSize, worldSize),
            dispatcher(&collisionConfiguration),
#if (1) // More acc broadphase - remeber the comma
            broadphase(corner1, corner2, 16384)
#endif
            {
            }
        const btVector3 corner1;
        const btVector3 corner2;
        btSoftBodyRigidBodyCollisionConfiguration collisionConfiguration;
        btCollisionDispatcher dispatcher;
#if (0) // Default broadphase
        btDbvtBroadphase broadphase;
#else
        // More accurate broadphase:
        btAxisSweep3 broadphase;
#endif
        btSequentialImpulseConstraintSolver solver;
};

tgWorldBulletPhysicsImpl::tgWorldBulletPhysicsImpl(const tgWorld::Config& config,
        tgBulletGround* ground) :
    tgWorldImpl(config, ground),
    m_pIntermediateBuildProducts(new IntermediateBuildProducts(config.worldSize)),
    m_pDynamicsWorld(createDynamicsWorld())
{
    // Gravitational acceleration is down on the Y axis
    const btVector3 gravityVector(0, -config.gravity, 0);
    m_pDynamicsWorld->setGravity(gravityVector);

	m_pDynamicsWorld->addRigidBody(ground->getGroundRigidBody());

    #if (1) /// @todo This is a line from the old BasicLearningApp.cpp that we're not using. Investigate further
        m_pDynamicsWorld->getSolverInfo().m_splitImpulse = true;
    #endif
    // Postcondition
    assert(invariant());
}

tgWorldBulletPhysicsImpl::~tgWorldBulletPhysicsImpl()
{
    // Delete all the collision objects. The dynamics world must exist.
    // Delete in reverse order of creation.
    const size_t nco = m_pDynamicsWorld->getNumCollisionObjects();
    btCollisionObjectArray& oa = m_pDynamicsWorld->getCollisionObjectArray();
    for (int i = nco - 1; i >= 0; --i)
    {
        btCollisionObject * const pCollisionObject = oa[i];

        // If the collision object is a rigid body, delete its motion state
        const btRigidBody* const pRigidBody =
            btRigidBody::upcast(pCollisionObject);
        if (pRigidBody)
        {
            delete pRigidBody->getMotionState();
        }

        // Remove the collision object from the dynamics world
        m_pDynamicsWorld->removeCollisionObject(pCollisionObject);
        // Delete the collision object
        delete pCollisionObject;
    }
    // All collision objects have been removed and deleted
    assert(m_pDynamicsWorld->getNumCollisionObjects() == 0);

    // Delete all the collision shapes. This can be done at any time.
    const size_t ncs = m_collisionShapes.size();

    for (size_t i = 0; i < ncs; ++i) { delete m_collisionShapes[i]; }

    delete m_pDynamicsWorld;

    // Delete the intermediate build products, which are now orphaned
    delete m_pIntermediateBuildProducts;
}

/**
 * Create and return a new instance of a btSoftRigidDynamicsWorld.
 * @return a pointer to a new instance of a btSoftRigidDynamicsWorld
 */
btSoftRigidDynamicsWorld* tgWorldBulletPhysicsImpl::createDynamicsWorld() const
{
    btSoftRigidDynamicsWorld * const result =
        new btSoftRigidDynamicsWorld(&m_pIntermediateBuildProducts->dispatcher,
                &m_pIntermediateBuildProducts->broadphase,
                &m_pIntermediateBuildProducts->solver,
                &m_pIntermediateBuildProducts->collisionConfiguration);

    return result;
}

void tgWorldBulletPhysicsImpl::step(double dt)
{
    // Precondition
    assert(dt > 0.0);

    const btScalar timeStep = dt;
    const int maxSubSteps = 1;
    const btScalar fixedTimeStep = dt;
    m_pDynamicsWorld->stepSimulation(timeStep, maxSubSteps, fixedTimeStep);

    // Postcondition
    assert(invariant());
}

void tgWorldBulletPhysicsImpl::addCollisionShape(btCollisionShape* pShape)
{
    if (pShape)
    {
        m_collisionShapes.push_back(pShape);
    }

      // Postcondition
      assert(invariant());
}

bool tgWorldBulletPhysicsImpl::invariant() const
{
    return (m_pDynamicsWorld != 0);
}

<|endoftext|>


<commit_before>/*
 * Copyright (c) 2006, Ken McDonell.  All Rights Reserved.
 * Copyright (c) 2007, Aconex.  All Rights Reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2 of the License, or (at your
 * option) any later version.
 *
 * 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 General Public License
 * for more details.
 */

//
// Source Class
//
// manipulate one or more PMAPI contexts

#include <QtGui/QMessageBox>
#include "namespace.h"
#include "main.h"

#define DESPERATE 1

QList<QmcContext *>contextList;
QmcContext *currentContext;

Source::Source(QmcGroup *group)
{
    my.fetchGroup = group;
    my.context = NULL;
}

QString Source::makeSourceBaseName(const QmcContext *cp)
{
    if (cp->source().type() == PM_CONTEXT_ARCHIVE)
	return cp->source().source();
    return cp->source().host();
}

QString Source::makeSourceAnnotatedName(const QmcContext *cp)
{
    QString t;

    if (cp->source().type() == PM_CONTEXT_HOST) {
	t = cp->source().host();
	// TODO - pmproxy host support needed here.
	t.append(" (no proxy)");
    }
    else {
	t = cp->source().source();
	t.append(" (");
	t.append(cp->source().host());
	t.append(")");
    }
    return t;
}

QString Source::makeComboText(const QmcContext *ctx)
{
    return makeSourceAnnotatedName(ctx);
}

int useSourceContext(QWidget *parent, QString &source)
{
    int sts;
    QmcGroup *group = activeTab->group();
    uint_t ctxcount = group->numContexts();

    // TODO: proxy support needed (we toss proxy hostname atm)
    source = source.section(QChar(' '), 0, 0);

    console->post("useSourceContext trying new source: host=%s proxy=%s",
			(const char *)source.toAscii(), "none");

    if ((sts = group->use(activeSources->type(), source)) < 0) {
	QString msg;
	msg.sprintf("Failed to %s \"%s\".\n%s.\n\n",
		    (activeSources->type() == PM_CONTEXT_HOST) ?
		    "connect to pmcd on" : "open archive",
		    (const char *)source.toAscii(), pmErrStr(sts));
	QMessageBox::warning(parent, pmProgname, msg,
		QMessageBox::Ok | QMessageBox::Default | QMessageBox::Escape,
		QMessageBox::NoButton, QMessageBox::NoButton);
    }
    else if (group->numContexts() > ctxcount)
	activeSources->add(group->which());
    return sts;
}

int Source::useComboContext(QWidget *parent, QComboBox *combo)
{
    QString source = combo->currentText();
    int sts = useSourceContext(parent, source);
    if (sts < 0)
	combo->removeItem(0);
    return sts;
}

int Source::type()
{
    return currentContext ? currentContext->source().type() : -1;
}

QString Source::host()
{
    // TODO: nathans - these aint QString's, theyre char* ... hmm?
    return my.context ? my.context->source().host() : NULL;
}

const char *Source::sourceAscii()
{
#if DESPERATE
    console->post("Source::sourceAscii(): currentContext=%p", currentContext);
#endif

    if (!currentContext)
    	return NULL;

#if DESPERATE
    console->post("  currentContext handle=%d source=%s",
		  currentContext->handle(),
		  currentContext->source().sourceAscii());
#endif

    return currentContext->source().sourceAscii();
}

void Source::add(QmcContext *context)
{
    bool send_bounds = (context->source().type() == PM_CONTEXT_ARCHIVE);

    contextList.append(context);
    currentContext = context;
    my.context = context;

#if DESPERATE
    dump();
#endif

    // For archives, send a message to kmtime to grow its time window.
    // This is already done if we're the first, so don't do it again;
    // we also don't have a kmtime connection yet if processing args.

    if (kmtime && send_bounds) {
	const QmcSource *source = &context->source();
	QString tz = source->timezone();
	QString host = source->host();
	struct timeval logStartTime = source->start();
	struct timeval logEndTime = source->end();
	kmtime->addArchive(&logStartTime, &logEndTime, tz, host);
    }
}

void Source::dump()
{
    QTextStream cerr(stderr);
    cerr << "Source::dump: current: " << currentContext;
    for (int i = 0; i < contextList.size(); i++) {
	contextList.at(i)->dump(cerr);
	contextList.at(i)->dumpMetrics(cerr);
    }
}

void Source::setupCombo(QComboBox *combo)
{
    combo->clear();
    for (int i = 0; i < contextList.size(); i++) {
	QmcContext *cp = contextList.at(i);
	QString source = makeComboText(cp);
	combo->insertItem(i, source);
	if (cp == currentContext)
	    combo->setCurrentIndex(i);
    }
}

void Source::setCurrentInCombo(QComboBox *combo)
{
    if (!currentContext)
	return;

    QString source = makeComboText(currentContext);
    for (int i = 0; i < combo->count(); i++) {
	if (combo->itemText(i) == source) {
	    combo->setCurrentIndex(i);
	    return;
	}
    }
}

// Called from a QComboBox widget, where name is setup by Source::setupCombo
//
void Source::setCurrentFromCombo(const QString name)
{
    for (int i = 0; i < contextList.size(); i++) {
	QmcContext *cp = contextList.at(i);
	if (name == makeComboText(cp)) {
	    currentContext = cp;
	    return;
	}
    }
}

void Source::setupTree(QTreeWidget *tree)
{
    NameSpace *current = NULL;
    QList<QTreeWidgetItem*> items;

    tree->clear();
    for (int i = 0; i < contextList.size(); i++) {
	QmcContext *cp = contextList.at(i);
	NameSpace *name = new NameSpace(tree, cp, activeTab->isArchiveSource());
	name->setExpanded(true);
	name->setSelectable(false);
	tree->addTopLevelItem(name);
	if (cp == currentContext)
	    current = name;
	items.append(name);
    }
    tree->insertTopLevelItems(0, items);
    if (current)
    	tree->setCurrentItem(current);
}
<commit_msg>Disable debugging info on stderr by default.<commit_after>/*
 * Copyright (c) 2006, Ken McDonell.  All Rights Reserved.
 * Copyright (c) 2007, Aconex.  All Rights Reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2 of the License, or (at your
 * option) any later version.
 *
 * 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 General Public License
 * for more details.
 */

//
// Source Class
//
// manipulate one or more PMAPI contexts

#include <QtGui/QMessageBox>
#include "namespace.h"
#include "main.h"

#define DESPERATE 0

QList<QmcContext *>contextList;
QmcContext *currentContext;

Source::Source(QmcGroup *group)
{
    my.fetchGroup = group;
    my.context = NULL;
}

QString Source::makeSourceBaseName(const QmcContext *cp)
{
    if (cp->source().type() == PM_CONTEXT_ARCHIVE)
	return cp->source().source();
    return cp->source().host();
}

QString Source::makeSourceAnnotatedName(const QmcContext *cp)
{
    QString t;

    if (cp->source().type() == PM_CONTEXT_HOST) {
	t = cp->source().host();
	// TODO - pmproxy host support needed here.
	t.append(" (no proxy)");
    }
    else {
	t = cp->source().source();
	t.append(" (");
	t.append(cp->source().host());
	t.append(")");
    }
    return t;
}

QString Source::makeComboText(const QmcContext *ctx)
{
    return makeSourceAnnotatedName(ctx);
}

int useSourceContext(QWidget *parent, QString &source)
{
    int sts;
    QmcGroup *group = activeTab->group();
    uint_t ctxcount = group->numContexts();

    // TODO: proxy support needed (we toss proxy hostname atm)
    source = source.section(QChar(' '), 0, 0);

    console->post("useSourceContext trying new source: host=%s proxy=%s",
			(const char *)source.toAscii(), "none");

    if ((sts = group->use(activeSources->type(), source)) < 0) {
	QString msg;
	msg.sprintf("Failed to %s \"%s\".\n%s.\n\n",
		    (activeSources->type() == PM_CONTEXT_HOST) ?
		    "connect to pmcd on" : "open archive",
		    (const char *)source.toAscii(), pmErrStr(sts));
	QMessageBox::warning(parent, pmProgname, msg,
		QMessageBox::Ok | QMessageBox::Default | QMessageBox::Escape,
		QMessageBox::NoButton, QMessageBox::NoButton);
    }
    else if (group->numContexts() > ctxcount)
	activeSources->add(group->which());
    return sts;
}

int Source::useComboContext(QWidget *parent, QComboBox *combo)
{
    QString source = combo->currentText();
    int sts = useSourceContext(parent, source);
    if (sts < 0)
	combo->removeItem(0);
    return sts;
}

int Source::type()
{
    return currentContext ? currentContext->source().type() : -1;
}

QString Source::host()
{
    // TODO: nathans - these aint QString's, theyre char* ... hmm?
    return my.context ? my.context->source().host() : NULL;
}

const char *Source::sourceAscii()
{
#if DESPERATE
    console->post("Source::sourceAscii(): currentContext=%p", currentContext);
#endif

    if (!currentContext)
    	return NULL;

#if DESPERATE
    console->post("  currentContext handle=%d source=%s",
		  currentContext->handle(),
		  currentContext->source().sourceAscii());
#endif

    return currentContext->source().sourceAscii();
}

void Source::add(QmcContext *context)
{
    bool send_bounds = (context->source().type() == PM_CONTEXT_ARCHIVE);

    contextList.append(context);
    currentContext = context;
    my.context = context;

#if DESPERATE
    dump();
#endif

    // For archives, send a message to kmtime to grow its time window.
    // This is already done if we're the first, so don't do it again;
    // we also don't have a kmtime connection yet if processing args.

    if (kmtime && send_bounds) {
	const QmcSource *source = &context->source();
	QString tz = source->timezone();
	QString host = source->host();
	struct timeval logStartTime = source->start();
	struct timeval logEndTime = source->end();
	kmtime->addArchive(&logStartTime, &logEndTime, tz, host);
    }
}

void Source::dump()
{
    QTextStream cerr(stderr);
    cerr << "Source::dump: current: " << currentContext << endl;
    for (int i = 0; i < contextList.size(); i++) {
	contextList.at(i)->dump(cerr);
	contextList.at(i)->dumpMetrics(cerr);
    }
}

void Source::setupCombo(QComboBox *combo)
{
    combo->clear();
    for (int i = 0; i < contextList.size(); i++) {
	QmcContext *cp = contextList.at(i);
	QString source = makeComboText(cp);
	combo->insertItem(i, source);
	if (cp == currentContext)
	    combo->setCurrentIndex(i);
    }
}

void Source::setCurrentInCombo(QComboBox *combo)
{
    if (!currentContext)
	return;

    QString source = makeComboText(currentContext);
    for (int i = 0; i < combo->count(); i++) {
	if (combo->itemText(i) == source) {
	    combo->setCurrentIndex(i);
	    return;
	}
    }
}

// Called from a QComboBox widget, where name is setup by Source::setupCombo
//
void Source::setCurrentFromCombo(const QString name)
{
    for (int i = 0; i < contextList.size(); i++) {
	QmcContext *cp = contextList.at(i);
	if (name == makeComboText(cp)) {
	    currentContext = cp;
	    return;
	}
    }
}

void Source::setupTree(QTreeWidget *tree)
{
    NameSpace *current = NULL;
    QList<QTreeWidgetItem*> items;

    tree->clear();
    for (int i = 0; i < contextList.size(); i++) {
	QmcContext *cp = contextList.at(i);
	NameSpace *name = new NameSpace(tree, cp, activeTab->isArchiveSource());
	name->setExpanded(true);
	name->setSelectable(false);
	tree->addTopLevelItem(name);
	if (cp == currentContext)
	    current = name;
	items.append(name);
    }
    tree->insertTopLevelItems(0, items);
    if (current)
    	tree->setCurrentItem(current);
}
<|endoftext|>


<commit_before>// Copyright (c) 2011 - 2015, GS Group, https://github.com/GSGroup
// Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted,
// provided that the above copyright notice and this permission notice appear in all copies.
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
// IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
// WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

#include <stingraykit/string/string_stream.h>
#include <stingraykit/exception.h>
#include <stdio.h>

namespace stingray
{
	template<>
	template<>
	void basic_string_ostream<char>::Insert(bool value)
	{
		if (value)
			write("true", 4);
		else
			write("false", 5);
	}
	extern template void string_ostream::Insert(bool);

#define DECLARE_INSERT(VALUE_TYPE, VALUE_FORMAT, VALUE_FORMAT_TYPE) \
	template<> \
	template<> \
	void basic_string_ostream<char>::Insert(VALUE_TYPE value) \
	{ \
		char buf[32]; \
		int r = snprintf(buf, sizeof(buf), VALUE_FORMAT, static_cast<VALUE_FORMAT_TYPE>(value)); \
		if (r == -1) \
			STINGRAYKIT_THROW("snprintf failed"); \
		write(buf, r); \
	} \
	extern template void string_ostream::Insert(VALUE_TYPE)

	DECLARE_INSERT(unsigned,			"%u",	unsigned);
	DECLARE_INSERT(u8,					"%hu",	unsigned short);
	DECLARE_INSERT(int,					"%d",	int);
	DECLARE_INSERT(unsigned short,		"%hu",	unsigned short);
	DECLARE_INSERT(short,				"%hd",	short);
	DECLARE_INSERT(unsigned long,		"%lu",	unsigned long);
	DECLARE_INSERT(long,				"%ld",	long);
	DECLARE_INSERT(unsigned long long,	"%llu",	unsigned long long);
	DECLARE_INSERT(long long,			"%lld",	long long);
	DECLARE_INSERT(long double,			"%16.16Lg",long double);
	DECLARE_INSERT(double,				"%16.16g",	double);
	DECLARE_INSERT(float,				"%7.7g",	double);
	DECLARE_INSERT(const void *,		"%p",	const void *);

}
<commit_msg>removed printf (slow) from stringstream integer routines<commit_after>// Copyright (c) 2011 - 2015, GS Group, https://github.com/GSGroup
// Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted,
// provided that the above copyright notice and this permission notice appear in all copies.
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
// IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
// WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

#include <stingraykit/string/string_stream.h>
#include <stingraykit/exception.h>
#include <stdio.h>

namespace stingray
{
	template<>
	template<>
	void basic_string_ostream<char>::Insert(bool value)
	{
		if (value)
			write("true", 4);
		else
			write("false", 5);
	}
	extern template void string_ostream::Insert(bool);

#define DECLARE_INSERT_UNSIGNED(VALUE_TYPE) \
	template<> \
	template<> \
	void basic_string_ostream<char>::Insert(VALUE_TYPE value) \
	{ \
		if (value == 0) \
		{ \
			push_back('0'); \
			return; \
		} \
		char buf[32]; \
		size_t pos = sizeof(buf); \
		while(value >= 10) \
		{ \
			u8 digit = value % 10; \
			value /= 10; \
			buf[--pos] = digit + '0'; \
		} \
		buf[--pos] = value + '0'; \
		write(buf + pos, sizeof(buf) - pos); \
	}

#define DECLARE_INSERT_SIGNED(VALUE_TYPE, UNSIGNED_TYPE) \
	template<> \
	template<> \
	void basic_string_ostream<char>::Insert(VALUE_TYPE value) \
	{ \
		if (value < 0) \
		{ \
			push_back('-'); \
			value = -value; \
		} \
		Insert<UNSIGNED_TYPE>(static_cast<UNSIGNED_TYPE>(value)); \
	}

#define DECLARE_INSERT_PRINTF(VALUE_TYPE, VALUE_FORMAT, VALUE_FORMAT_TYPE) \
	template<> \
	template<> \
	void basic_string_ostream<char>::Insert(VALUE_TYPE value) \
	{ \
		char buf[32]; \
		int r = snprintf(buf, sizeof(buf), VALUE_FORMAT, static_cast<VALUE_FORMAT_TYPE>(value)); \
		if (r == -1) \
			STINGRAYKIT_THROW("snprintf failed"); \
		write(buf, r); \
	} \
	extern template void string_ostream::Insert(VALUE_TYPE)

	DECLARE_INSERT_UNSIGNED(u8);

	DECLARE_INSERT_UNSIGNED(unsigned short);
	DECLARE_INSERT_SIGNED(short, unsigned short);

	DECLARE_INSERT_UNSIGNED(unsigned int);
	DECLARE_INSERT_SIGNED(int, unsigned int);

	DECLARE_INSERT_UNSIGNED(unsigned long);
	DECLARE_INSERT_SIGNED(long, unsigned long);

	DECLARE_INSERT_UNSIGNED(unsigned long long);
	DECLARE_INSERT_SIGNED(long long, unsigned long long);

	DECLARE_INSERT_PRINTF(long double,			"%16.16Lg",long double);
	DECLARE_INSERT_PRINTF(double,				"%16.16g",	double);
	DECLARE_INSERT_PRINTF(float,				"%7.7g",	double);
	DECLARE_INSERT_PRINTF(const void *,			"%p",	const void *);

}
<|endoftext|>


<commit_before>#include <cutehmi/dataacquisition/Schema.hpp>
#include <cutehmi/dataacquisition/Exception.hpp>

#include "../../../cutehmi.dirs.hpp"

#include <QFile>
#include <QSqlRecord>

namespace cutehmi {
namespace dataacquisition {

Schema::Schema(QObject * parent):
	shareddatabase::DataObject(parent),
	m(new Members)
{
}

QString Schema::name() const
{
	return m->name;
}

void Schema::setName(const QString & name)
{
	if (m->name != name) {
		m->name = name;
		emit nameChanged();
	}
}

QString Schema::user() const
{
	return m->user;
}

void Schema::setUser(const QString & user)
{
	if (m->user != user) {
		m->user = user;
		emit userChanged();
	}
}

void Schema::create()
{
	worker([this](QSqlDatabase & db) {

		bool warning = false;
		bool error = false;

		if (db.driverName() == "QPSQL") {
			QSqlQuery query(db);
			try {
				CUTEHMI_DEBUG("Creating schema...");

				QString queryString = readScript(POSTGRESQL_SCRIPTS_SUBDIR, "create.sql").arg(name());
				CUTEHMI_DEBUG("SQL query:\n```\n" << queryString + "\n```");

				if (!query.exec(queryString))
					error = true;
				pushError(query.lastError(), query.lastQuery());
				query.finish();
			} catch (const Exception & e) {
				CUTEHMI_CRITICAL(e.what());
				error = true;
			}

			if (!user().isEmpty()) {
				CUTEHMI_DEBUG("Setting schema owner...");

				const char * alterSchemaQuery = R"SQL(
					ALTER SCHEMA %1 OWNER TO %2;
				)SQL";

				if (!query.exec(QString(alterSchemaQuery).arg(name(), user())))
					warning = true;

				pushError(query.lastError(), query.lastQuery());
				query.finish();
			}
		} else if (db.driverName() == "QSQLITE") {
			QSqlQuery query(db);
			try {
				CUTEHMI_DEBUG("Creating schema...");

				QString queryString = readScript(SQLITE_SCRIPTS_SUBDIR, "create.sql").arg(name());
				QStringList queryList = queryString.split(';');
				queryList.removeLast();	// Remove empty query.
				for (auto queryIt = queryList.begin(); queryIt != queryList.end(); ++queryIt) {
					CUTEHMI_DEBUG("SQL query:\n```\n" << *queryIt + "\n```");

					if (!query.exec(*queryIt))
						error = true;
					pushError(query.lastError(), query.lastQuery());
					query.finish();
				}
			} catch (const Exception & e) {
				CUTEHMI_CRITICAL(e.what());
				error = true;
			}
		} else {
			emit errored(CUTEHMI_ERROR(tr("Driver '%1' is not supported.").arg(db.driverName())));
			error = true;
		}

		if (error)
			Notification::Critical(tr("Failed to create '%1' schema.").arg(name()));
		else if (warning)
			Notification::Warning(tr("Created '%1' schema although the operation wasn't clean.").arg(name()));
		else
			Notification::Info(tr("Successfully created '%1' schema.").arg(name()));

	})->work();
}

void Schema::drop()
{
	worker([this](QSqlDatabase & db) {

		bool warning = false;
		bool error = false;

		if (db.driverName() == "QPSQL") {
			QSqlQuery query(db);
			try {
				CUTEHMI_DEBUG("Dropping schema...");

				QString queryString = readScript(POSTGRESQL_SCRIPTS_SUBDIR, "drop.sql").arg(name());
				CUTEHMI_DEBUG("SQL query:\n```\n" << queryString + "\n```");

				if (!query.exec(queryString))
					error = true;
				pushError(query.lastError(), query.lastQuery());
				query.finish();
			} catch (const Exception & e) {
				CUTEHMI_CRITICAL(e.what());
				error = true;
			}
		} else if (db.driverName() == "QSQLITE") {
			QSqlQuery query(db);
			try {
				CUTEHMI_DEBUG("Dropping schema...");

				QString queryString = readScript(SQLITE_SCRIPTS_SUBDIR, "drop.sql").arg(name());
				QStringList queryList = queryString.split(';');
				queryList.removeLast();	// Remove empty query.
				for (auto queryIt = queryList.begin(); queryIt != queryList.end(); ++queryIt) {
					CUTEHMI_DEBUG("SQL query:\n```\n" << *queryIt + "\n```");

					if (!query.exec(*queryIt))
						warning = true;
					pushError(query.lastError(), query.lastQuery());
					query.finish();
				}
			} catch (const Exception & e) {
				CUTEHMI_CRITICAL(e.what());
				error = true;
			}
		} else {
			emit errored(CUTEHMI_ERROR(tr("Driver '%1' is not supported.").arg(db.driverName())));
			error = true;
		}

		if (error)
			Notification::Critical(tr("Failed to drop '%1' schema.").arg(name()));
		else if (warning)
			Notification::Warning(tr("Dropped '%1' schema although the operation wasn't clean.").arg(name()));
		else
			Notification::Info(tr("Dropped '%1' schema.").arg(name()));

	})->work();
}

void Schema::validate()
{
	worker([this](QSqlDatabase & db) {
		if (db.driverName() == "QPSQL") {
			QSqlQuery query(db);

			bool result = true;

			result &= validatePostgresTable("history_int", query);
			result &= validatePostgresTable("history_bool", query);
			result &= validatePostgresTable("history_real", query);

			result &= validatePostgresTable("event_int", query);
			result &= validatePostgresTable("event_bool", query);
			result &= validatePostgresTable("event_real", query);

			result &= validatePostgresTable("recency_int", query);
			result &= validatePostgresTable("recency_bool", query);
			result &= validatePostgresTable("recency_real", query);

			emit validated(result);
		} else if (db.driverName() == "QSQLITE") {
			QSqlQuery query(db);

			bool result = true;

			result &= validateSqliteTable("history_int", query);
			result &= validateSqliteTable("history_bool", query);
			result &= validateSqliteTable("history_real", query);

			result &= validateSqliteTable("event_int", query);
			result &= validateSqliteTable("event_bool", query);
			result &= validateSqliteTable("event_real", query);

			result &= validateSqliteTable("recency_int", query);
			result &= validateSqliteTable("recency_bool", query);
			result &= validateSqliteTable("recency_real", query);

			emit validated(result);
		} else {
			emit errored(CUTEHMI_ERROR(tr("Driver '%1' is not supported.").arg(db.driverName())));
			emit validated(false);
		}
	})->work();
}

bool Schema::validatePostgresTable(const QString & tableName, QSqlQuery & query)
{
	bool result = true;

	CUTEHMI_DEBUG("Checking if '" << tableName << "' table exists...");
	const char * tableExistsQuery = R"SQL(
		SELECT EXISTS (
			SELECT FROM information_schema.tables
			WHERE table_schema = '%1' AND table_name = '%2'
		);
	)SQL";
	query.exec(QString(tableExistsQuery).arg(name(), tableName));
	pushError(query.lastError(), query.lastQuery());
	int existsIndex = query.record().indexOf("exists");
	if (query.first())
		if (!query.value(existsIndex).toBool()) {
			emit errored(CUTEHMI_ERROR(QObject::tr("Table '%1' does not exist in schema '%2'.").arg(tableName, name())));
			result = false;
		}
	query.finish();

	return result;
}

bool Schema::validateSqliteTable(const QString & tableName, QSqlQuery & query)
{
	bool result = true;

	CUTEHMI_DEBUG("Checking if '" << tableName << "' table exists...");
	const char * tableExistsQuery = R"SQL(
		SELECT name FROM sqlite_master WHERE type='table' AND name='%1.%2';
	)SQL";
	query.exec(QString(tableExistsQuery).arg(name(), tableName));
	pushError(query.lastError(), query.lastQuery());
	if (!query.first()) {
		emit errored(CUTEHMI_ERROR(QObject::tr("Table '%1' does not exist in schema '%2'.").arg(tableName, name())));
		result = false;
	}
	query.finish();

	return result;
}

QString Schema::readScript(const QString & dbms, const QString & scriptName) const
{
	QFile file(QString(CUTEHMI_DIRS_TOOL_RELATIVE_PATH) + "/" + SQL_SCRIPTS_SUBDIR + "/" + dbms + "/" + scriptName);
	if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
		throw Exception(QString("Could not open file '%1'").arg(file.fileName()));
	return QTextStream(& file).readAll();
}

}
}

//(c)C: Copyright © 2020-2022, Michał Policht <michal@policht.pl>, Yuri Chornoivan <yurchor@ukr.net>. All rights reserved.
//(c)C: SPDX-License-Identifier: LGPL-3.0-or-later OR MIT
//(c)C: This file is a part of CuteHMI.
//(c)C: CuteHMI 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 3 of the License, or (at your option) any later version.
//(c)C: CuteHMI 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.
//(c)C: You should have received a copy of the GNU Lesser General Public License along with CuteHMI.  If not, see <https://www.gnu.org/licenses/>.
//(c)C: Additionally, this file is licensed under terms of MIT license as expressed below.
//(c)C: 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:
//(c)C: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
//(c)C: 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.
<commit_msg>Add check for "tag" table<commit_after>#include <cutehmi/dataacquisition/Schema.hpp>
#include <cutehmi/dataacquisition/Exception.hpp>

#include "../../../cutehmi.dirs.hpp"

#include <QFile>
#include <QSqlRecord>

namespace cutehmi {
namespace dataacquisition {

Schema::Schema(QObject * parent):
	shareddatabase::DataObject(parent),
	m(new Members)
{
}

QString Schema::name() const
{
	return m->name;
}

void Schema::setName(const QString & name)
{
	if (m->name != name) {
		m->name = name;
		emit nameChanged();
	}
}

QString Schema::user() const
{
	return m->user;
}

void Schema::setUser(const QString & user)
{
	if (m->user != user) {
		m->user = user;
		emit userChanged();
	}
}

void Schema::create()
{
	worker([this](QSqlDatabase & db) {

		bool warning = false;
		bool error = false;

		if (db.driverName() == "QPSQL") {
			QSqlQuery query(db);
			try {
				CUTEHMI_DEBUG("Creating schema...");

				QString queryString = readScript(POSTGRESQL_SCRIPTS_SUBDIR, "create.sql").arg(name());
				CUTEHMI_DEBUG("SQL query:\n```\n" << queryString + "\n```");

				if (!query.exec(queryString))
					error = true;
				pushError(query.lastError(), query.lastQuery());
				query.finish();
			} catch (const Exception & e) {
				CUTEHMI_CRITICAL(e.what());
				error = true;
			}

			if (!user().isEmpty()) {
				CUTEHMI_DEBUG("Setting schema owner...");

				const char * alterSchemaQuery = R"SQL(
					ALTER SCHEMA %1 OWNER TO %2;
				)SQL";

				if (!query.exec(QString(alterSchemaQuery).arg(name(), user())))
					warning = true;

				pushError(query.lastError(), query.lastQuery());
				query.finish();
			}
		} else if (db.driverName() == "QSQLITE") {
			QSqlQuery query(db);
			try {
				CUTEHMI_DEBUG("Creating schema...");

				QString queryString = readScript(SQLITE_SCRIPTS_SUBDIR, "create.sql").arg(name());
				QStringList queryList = queryString.split(';');
				queryList.removeLast();	// Remove empty query.
				for (auto queryIt = queryList.begin(); queryIt != queryList.end(); ++queryIt) {
					CUTEHMI_DEBUG("SQL query:\n```\n" << *queryIt + "\n```");

					if (!query.exec(*queryIt))
						error = true;
					pushError(query.lastError(), query.lastQuery());
					query.finish();
				}
			} catch (const Exception & e) {
				CUTEHMI_CRITICAL(e.what());
				error = true;
			}
		} else {
			emit errored(CUTEHMI_ERROR(tr("Driver '%1' is not supported.").arg(db.driverName())));
			error = true;
		}

		if (error)
			Notification::Critical(tr("Failed to create '%1' schema.").arg(name()));
		else if (warning)
			Notification::Warning(tr("Created '%1' schema although the operation wasn't clean.").arg(name()));
		else
			Notification::Info(tr("Successfully created '%1' schema.").arg(name()));

	})->work();
}

void Schema::drop()
{
	worker([this](QSqlDatabase & db) {

		bool warning = false;
		bool error = false;

		if (db.driverName() == "QPSQL") {
			QSqlQuery query(db);
			try {
				CUTEHMI_DEBUG("Dropping schema...");

				QString queryString = readScript(POSTGRESQL_SCRIPTS_SUBDIR, "drop.sql").arg(name());
				CUTEHMI_DEBUG("SQL query:\n```\n" << queryString + "\n```");

				if (!query.exec(queryString))
					error = true;
				pushError(query.lastError(), query.lastQuery());
				query.finish();
			} catch (const Exception & e) {
				CUTEHMI_CRITICAL(e.what());
				error = true;
			}
		} else if (db.driverName() == "QSQLITE") {
			QSqlQuery query(db);
			try {
				CUTEHMI_DEBUG("Dropping schema...");

				QString queryString = readScript(SQLITE_SCRIPTS_SUBDIR, "drop.sql").arg(name());
				QStringList queryList = queryString.split(';');
				queryList.removeLast();	// Remove empty query.
				for (auto queryIt = queryList.begin(); queryIt != queryList.end(); ++queryIt) {
					CUTEHMI_DEBUG("SQL query:\n```\n" << *queryIt + "\n```");

					if (!query.exec(*queryIt))
						warning = true;
					pushError(query.lastError(), query.lastQuery());
					query.finish();
				}
			} catch (const Exception & e) {
				CUTEHMI_CRITICAL(e.what());
				error = true;
			}
		} else {
			emit errored(CUTEHMI_ERROR(tr("Driver '%1' is not supported.").arg(db.driverName())));
			error = true;
		}

		if (error)
			Notification::Critical(tr("Failed to drop '%1' schema.").arg(name()));
		else if (warning)
			Notification::Warning(tr("Dropped '%1' schema although the operation wasn't clean.").arg(name()));
		else
			Notification::Info(tr("Dropped '%1' schema.").arg(name()));

	})->work();
}

void Schema::validate()
{
	worker([this](QSqlDatabase & db) {
		if (db.driverName() == "QPSQL") {
			QSqlQuery query(db);

			bool result = true;

			result &= validatePostgresTable("tag", query);

			result &= validatePostgresTable("history_int", query);
			result &= validatePostgresTable("history_bool", query);
			result &= validatePostgresTable("history_real", query);

			result &= validatePostgresTable("event_int", query);
			result &= validatePostgresTable("event_bool", query);
			result &= validatePostgresTable("event_real", query);

			result &= validatePostgresTable("recency_int", query);
			result &= validatePostgresTable("recency_bool", query);
			result &= validatePostgresTable("recency_real", query);

			emit validated(result);
		} else if (db.driverName() == "QSQLITE") {
			QSqlQuery query(db);

			bool result = true;

			result &= validateSqliteTable("tag", query);

			result &= validateSqliteTable("history_int", query);
			result &= validateSqliteTable("history_bool", query);
			result &= validateSqliteTable("history_real", query);

			result &= validateSqliteTable("event_int", query);
			result &= validateSqliteTable("event_bool", query);
			result &= validateSqliteTable("event_real", query);

			result &= validateSqliteTable("recency_int", query);
			result &= validateSqliteTable("recency_bool", query);
			result &= validateSqliteTable("recency_real", query);

			emit validated(result);
		} else {
			emit errored(CUTEHMI_ERROR(tr("Driver '%1' is not supported.").arg(db.driverName())));
			emit validated(false);
		}
	})->work();
}

bool Schema::validatePostgresTable(const QString & tableName, QSqlQuery & query)
{
	bool result = true;

	CUTEHMI_DEBUG("Checking if '" << tableName << "' table exists...");
	const char * tableExistsQuery = R"SQL(
		SELECT EXISTS (
			SELECT FROM information_schema.tables
			WHERE table_schema = '%1' AND table_name = '%2'
		);
	)SQL";
	query.exec(QString(tableExistsQuery).arg(name(), tableName));
	pushError(query.lastError(), query.lastQuery());
	int existsIndex = query.record().indexOf("exists");
	if (query.first())
		if (!query.value(existsIndex).toBool()) {
			emit errored(CUTEHMI_ERROR(QObject::tr("Table '%1' does not exist in schema '%2'.").arg(tableName, name())));
			result = false;
		}
	query.finish();

	return result;
}

bool Schema::validateSqliteTable(const QString & tableName, QSqlQuery & query)
{
	bool result = true;

	CUTEHMI_DEBUG("Checking if '" << tableName << "' table exists...");
	const char * tableExistsQuery = R"SQL(
		SELECT name FROM sqlite_master WHERE type='table' AND name='%1.%2';
	)SQL";
	query.exec(QString(tableExistsQuery).arg(name(), tableName));
	pushError(query.lastError(), query.lastQuery());
	if (!query.first()) {
		emit errored(CUTEHMI_ERROR(QObject::tr("Table '%1' does not exist in schema '%2'.").arg(tableName, name())));
		result = false;
	}
	query.finish();

	return result;
}

QString Schema::readScript(const QString & dbms, const QString & scriptName) const
{
	QFile file(QString(CUTEHMI_DIRS_TOOL_RELATIVE_PATH) + "/" + SQL_SCRIPTS_SUBDIR + "/" + dbms + "/" + scriptName);
	if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
		throw Exception(QString("Could not open file '%1'").arg(file.fileName()));
	return QTextStream(& file).readAll();
}

}
}

//(c)C: Copyright © 2020-2022, Michał Policht <michal@policht.pl>, Yuri Chornoivan <yurchor@ukr.net>. All rights reserved.
//(c)C: SPDX-License-Identifier: LGPL-3.0-or-later OR MIT
//(c)C: This file is a part of CuteHMI.
//(c)C: CuteHMI 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 3 of the License, or (at your option) any later version.
//(c)C: CuteHMI 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.
//(c)C: You should have received a copy of the GNU Lesser General Public License along with CuteHMI.  If not, see <https://www.gnu.org/licenses/>.
//(c)C: Additionally, this file is licensed under terms of MIT license as expressed below.
//(c)C: 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:
//(c)C: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
//(c)C: 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.
<|endoftext|>


<commit_before>#include <dali-toolkit/dali-toolkit.h>
#include <dali/public-api/object/property-map.h>

using namespace Dali;
using namespace Dali::Toolkit;

namespace
{
  //Keeps information about each model for access.
  struct Model
  {
    Control control; // Control housing the mesh renderer of the model.
    Vector2 rotation; // Keeps track of rotation about x and y axis for manual rotation.
    Animation rotationAnimation; // Automatically rotates when left alone.
  };

  //Files for meshes
  const char * const MODEL_FILE[] =
  {
      DEMO_MODEL_DIR "Dino.obj",
      DEMO_MODEL_DIR "ToyRobot-Metal.obj",
      DEMO_MODEL_DIR "Toyrobot-Plastic.obj"
  };

  const char * const MATERIAL_FILE[] =
  {
      DEMO_MODEL_DIR "Dino.mtl",
      DEMO_MODEL_DIR "ToyRobot-Metal.mtl",
      DEMO_MODEL_DIR "Toyrobot-Plastic.mtl"
  };

  const char * const TEXTURES_PATH( DEMO_IMAGE_DIR "" );

  //Possible shader options.
  const char * const SHADER_TYPE[] =
  {
    "allTextures",
    "diffuseTexture",
    "textureless"
  };

  //Files for background and toolbar
  const char * const BACKGROUND_IMAGE( DEMO_IMAGE_DIR "background-1.jpg");

  const float X_ROTATION_DISPLACEMENT_FACTOR = 60.0f;
  const float Y_ROTATION_DISPLACEMENT_FACTOR = 60.0f;
  const float MODEL_SCALE = 0.45f;

} //End namespace

class SharedMeshRendererController : public ConnectionTracker
{
public:

  SharedMeshRendererController( Application& application )
  : mApplication( application ),   //Store handle to the application.
    mModelIndex( 1 ),              //Start with metal robot.
    mShaderIndex( 0 ),             //Start with all textures.
    mSelectedModelIndex( 0 )       //Non-valid default, which will get set to a correct value when used.
  {
    // Connect to the Application's Init signal
    mApplication.InitSignal().Connect( this, &SharedMeshRendererController::Create );
  }

  ~SharedMeshRendererController()
  {
  }

  // The Init signal is received once (only) during the Application lifetime
  void Create( Application& application )
  {
    // Get a handle to the stage
    Stage stage = Stage::GetCurrent();

    //Add background
    ImageView backView = ImageView::New( BACKGROUND_IMAGE );
    backView.SetAnchorPoint( AnchorPoint::TOP_LEFT );
    stage.Add( backView );

    //Setup and load the 3D models and buttons
    LoadScene();
  }

  //Sets up the on-screen elements.
  void LoadScene()
  {
    Stage stage = Stage::GetCurrent();

    //Set up 3D layer to place objects on.
    Layer layer = Layer::New();
    layer.SetParentOrigin( ParentOrigin::CENTER );
    layer.SetAnchorPoint( AnchorPoint::CENTER );
    layer.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
    layer.SetBehavior( Layer::LAYER_3D );
    stage.Add( layer );

    //Containers to house each renderer-holding-actor, to provide a constant hitbox for pan detection.
    Actor container1 = Actor::New();
    container1.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
    container1.SetSizeModeFactor( Vector3( MODEL_SCALE, MODEL_SCALE, 0.0f ) );
    container1.SetParentOrigin( ParentOrigin::CENTER );
    container1.SetAnchorPoint( AnchorPoint::CENTER );
    container1.SetPosition( stage.GetSize().width * 0.25, 0.0 ); //Place on right half of screen.
    container1.RegisterProperty( "Tag", Property::Value( 0 ) ); // Used to identify this actor and index into the model.
    layer.Add( container1 );

    Actor container2 = Actor::New();
    container2.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
    container2.SetSizeModeFactor( Vector3( MODEL_SCALE / 2, MODEL_SCALE / 2, 0.0f ) );
    container2.SetParentOrigin( ParentOrigin::CENTER );
    container2.SetAnchorPoint( AnchorPoint::CENTER );
    container2.SetPosition( stage.GetSize().width * -0.25, 0.0 ); //Place on left half of screen.
    container2.RegisterProperty( "Tag", Property::Value( 1 ) ); // Used to identify this actor and index into the model.
    layer.Add( container2 );

    //Attach gesture detector to pan models when rotated.
    mPanGestureDetector = PanGestureDetector::New();
    mPanGestureDetector.Attach( container1 );
    mPanGestureDetector.Attach( container2 );
    mPanGestureDetector.DetectedSignal().Connect( this, &SharedMeshRendererController::OnPan );

    //Create actors to display meshes.
    Control control1 = Control::New();
    control1.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
    control1.SetParentOrigin( ParentOrigin::CENTER );
    control1.SetAnchorPoint( AnchorPoint::CENTER );
    container1.Add( control1 );

    Control control2 = Control::New();
    control2.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
    control2.SetParentOrigin( ParentOrigin::CENTER );
    control2.SetAnchorPoint( AnchorPoint::CENTER );
    container2.Add( control2 );

    //Make actors spin to demonstrate 3D.
    Animation rotationAnimation1 = Animation::New( 15.0f );
    rotationAnimation1.AnimateBy( Property( control1, Actor::Property::ORIENTATION ),
                                  Quaternion( Degree( 0.0f ), Degree( 360.0f ), Degree( 0.0f ) ) );
    rotationAnimation1.SetLooping( true );
    rotationAnimation1.Play();

    Animation rotationAnimation2 = Animation::New( 15.0f );
    rotationAnimation2.AnimateBy( Property( control2, Actor::Property::ORIENTATION ),
                                  Quaternion( Degree( 0.0f ), Degree( -360.0f ), Degree( 0.0f ) ) );
    rotationAnimation2.SetLooping( true );
    rotationAnimation2.Play();

    //Store model information in corresponding structs.
    mModels[0].control = control1;
    mModels[0].rotation.x = 0.0f;
    mModels[0].rotation.y = 0.0f;
    mModels[0].rotationAnimation = rotationAnimation1;

    mModels[1].control = control2;
    mModels[1].rotation.x = 0.0f;
    mModels[1].rotation.y = 0.0f;
    mModels[1].rotationAnimation = rotationAnimation2;

    //Calling this sets the model in the two actors.
    ReloadModel();

    //Create button for model changing
    Toolkit::PushButton modelButton = Toolkit::PushButton::New();
    modelButton.SetResizePolicy( ResizePolicy::USE_NATURAL_SIZE, Dimension::ALL_DIMENSIONS );
    modelButton.ClickedSignal().Connect( this, &SharedMeshRendererController::OnChangeModelClicked );
    modelButton.SetParentOrigin( Vector3( 0.1, 0.9, 0.5 ) ); //Offset from bottom left
    modelButton.SetAnchorPoint( AnchorPoint::BOTTOM_LEFT );
    modelButton.SetLabelText( "Change Model" );
    layer.Add( modelButton );

    //Create button for shader changing
    Toolkit::PushButton shaderButton = Toolkit::PushButton::New();
    shaderButton.SetResizePolicy( ResizePolicy::USE_NATURAL_SIZE, Dimension::ALL_DIMENSIONS );
    shaderButton.ClickedSignal().Connect( this, &SharedMeshRendererController::OnChangeShaderClicked );
    shaderButton.SetParentOrigin( Vector3( 0.9, 0.9, 0.5 ) ); //Offset from bottom right
    shaderButton.SetAnchorPoint( AnchorPoint::BOTTOM_RIGHT );
    shaderButton.SetLabelText( "Change Shader" );
    layer.Add( shaderButton );
  }

  //Updates the displayed models to account for parameter changes.
  void ReloadModel()
  {
    //Create mesh property map
    Property::Map map;
    map.Insert( "rendererType", "mesh" );
    map.Insert( "objectUrl", MODEL_FILE[mModelIndex] );
    map.Insert( "materialUrl", MATERIAL_FILE[mModelIndex] );
    map.Insert( "texturesPath", TEXTURES_PATH );
    map.Insert( "shaderType", SHADER_TYPE[mShaderIndex] );

    //Set the two controls to use the mesh
    mModels[0].control.SetProperty( Control::Property::BACKGROUND, Property::Value( map ) );
    mModels[1].control.SetProperty( Control::Property::BACKGROUND, Property::Value( map ) );
  }

  //Rotates the panned model based on the gesture.
  void OnPan( Actor actor, const PanGesture& gesture )
  {
    switch( gesture.state )
    {
      case Gesture::Started:
      {
        //Find out which model has been selected
        actor.GetProperty( actor.GetPropertyIndex( "Tag" ) ).Get( mSelectedModelIndex );

        //Pause current animation, as the gesture will be used to manually rotate the model
        mModels[mSelectedModelIndex].rotationAnimation.Pause();

        break;
      }
      case Gesture::Continuing:
      {
        //Rotate based off the gesture.
        mModels[mSelectedModelIndex].rotation.x -= gesture.displacement.y / X_ROTATION_DISPLACEMENT_FACTOR; // Y displacement rotates around X axis
        mModels[mSelectedModelIndex].rotation.y += gesture.displacement.x / Y_ROTATION_DISPLACEMENT_FACTOR; // X displacement rotates around Y axis
        Quaternion rotation = Quaternion( Radian( mModels[mSelectedModelIndex].rotation.x ), Vector3::XAXIS) *
                              Quaternion( Radian( mModels[mSelectedModelIndex].rotation.y ), Vector3::YAXIS);

        mModels[mSelectedModelIndex].control.SetOrientation( rotation );

        break;
      }
      case Gesture::Finished:
      {
        //Return to automatic animation
        mModels[mSelectedModelIndex].rotationAnimation.Play();

        break;
      }
      case Gesture::Cancelled:
      {
        //Return to automatic animation
        mModels[mSelectedModelIndex].rotationAnimation.Play();

        break;
      }
      default:
      {
        //We can ignore other gestures and gesture states.
        break;
      }
    }
  }

  //Cycle through the list of models.
  bool OnChangeModelClicked( Toolkit::Button button )
  {
    ++mModelIndex %= 3;

    ReloadModel();

    return true;
  }

  //Cycle through the list of shaders.
  bool OnChangeShaderClicked( Toolkit::Button button )
  {
    ++mShaderIndex %= 3;

    ReloadModel();

    return true;
  }

private:
  Application&  mApplication;

  //The models displayed on screen, including information about rotation.
  Model mModels[2];

  //Used to detect panning to rotate the selected model.
  PanGestureDetector mPanGestureDetector;

  int mModelIndex; //Index of model to load.
  int mShaderIndex; //Index of shader type to use.
  int mSelectedModelIndex; //Index of model selected on screen.
};

void RunTest( Application& application )
{
  SharedMeshRendererController test( application );

  application.MainLoop();
}

// Entry point for Linux & Tizen applications
//
int main( int argc, char **argv )
{
  Application application = Application::New( &argc, &argv );

  RunTest( application );

  return 0;
}
<commit_msg>Mesh demo improvements.<commit_after>#include <dali-toolkit/dali-toolkit.h>
#include <dali/public-api/object/property-map.h>

using namespace Dali;
using namespace Dali::Toolkit;

namespace
{
  //Keeps information about each model for access.
  struct Model
  {
    Control control; // Control housing the mesh renderer of the model.
    Vector2 rotation; // Keeps track of rotation about x and y axis for manual rotation.
    Animation rotationAnimation; // Automatically rotates when left alone.
  };

  //Files for meshes
  const char * const MODEL_FILE[] =
  {
      DEMO_MODEL_DIR "Dino.obj",
      DEMO_MODEL_DIR "ToyRobot-Metal.obj",
      DEMO_MODEL_DIR "Toyrobot-Plastic.obj"
  };

  const char * const MATERIAL_FILE[] =
  {
      DEMO_MODEL_DIR "Dino.mtl",
      DEMO_MODEL_DIR "ToyRobot-Metal.mtl",
      DEMO_MODEL_DIR "Toyrobot-Plastic.mtl"
  };

  const char * const TEXTURES_PATH( DEMO_IMAGE_DIR "" );

  //Possible shader options.
  const char * const SHADER_TYPE[] =
  {
    "allTextures",
    "diffuseTexture",
    "textureless"
  };

  //Files for background and toolbar
  const char * const BACKGROUND_IMAGE( DEMO_IMAGE_DIR "background-1.jpg");

  const float X_ROTATION_DISPLACEMENT_FACTOR = 60.0f;
  const float Y_ROTATION_DISPLACEMENT_FACTOR = 60.0f;
  const float MODEL_SCALE = 0.75f;
  const int NUM_MESHES = 3;

} //End namespace

class MeshRendererController : public ConnectionTracker
{
public:

  MeshRendererController( Application& application )
  : mApplication( application ),   //Store handle to the application.
    mModelIndex( 1 ),              //Start with metal robot.
    mShaderIndex( 0 ),             //Start with all textures.
    mSelectedModelIndex( 0 )       //Non-valid default, which will get set to a correct value when used.
  {
    // Connect to the Application's Init signal
    mApplication.InitSignal().Connect( this, &MeshRendererController::Create );
  }

  ~MeshRendererController()
  {
  }

  // The Init signal is received once (only) during the Application lifetime
  void Create( Application& application )
  {
    // Get a handle to the stage
    Stage stage = Stage::GetCurrent();

    //Add background
    ImageView backView = ImageView::New( BACKGROUND_IMAGE );
    backView.SetAnchorPoint( AnchorPoint::TOP_LEFT );
    stage.Add( backView );

    //Setup and load the 3D models and buttons
    LoadScene();

    //Allow for exiting of the application via key presses.
    stage.KeyEventSignal().Connect( this, &MeshRendererController::OnKeyEvent );
  }

  //Sets up the on-screen elements.
  void LoadScene()
  {
    Stage stage = Stage::GetCurrent();

    //Set up 3D layer to place objects on.
    Layer layer = Layer::New();
    layer.SetParentOrigin( ParentOrigin::CENTER );
    layer.SetAnchorPoint( AnchorPoint::CENTER );
    layer.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
    layer.SetBehavior( Layer::LAYER_2D ); //We use a 2D layer as this is closer to UI work than full 3D scene creation.
    layer.SetDepthTestDisabled( false ); //Enable depth testing, as otherwise the 2D layer would not do so.
    stage.Add( layer );

    //Create gesture detector for panning of models.
    mPanGestureDetector = PanGestureDetector::New();
    mPanGestureDetector.DetectedSignal().Connect( this, &MeshRendererController::OnPan );

    //Add containers to house each renderer-holding-actor.
    for( int i = 0; i < NUM_MESHES; i++ )
    {
      mContainers[i] = Actor::New();
      mContainers[i].SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
      mContainers[i].RegisterProperty( "Tag", Property::Value( i ) ); //Used to identify the actor and index into the model.

      //Position each container on screen
      if( i == 0 )
      {
        //Main, central model
        mContainers[i].SetSizeModeFactor( Vector3( MODEL_SCALE, MODEL_SCALE, 0.0f ) );
        mContainers[i].SetParentOrigin( ParentOrigin::CENTER );
        mContainers[i].SetAnchorPoint( AnchorPoint::CENTER );
      }
      else if( i == 1 )
      {
        //Top left model
        mContainers[i].SetSizeModeFactor( Vector3( MODEL_SCALE / 3.0f, MODEL_SCALE / 3.0f, 0.0f ) );
        mContainers[i].SetParentOrigin( Vector3( 0.05, 0.03, 0.5 ) ); //Offset from top left
        mContainers[i].SetAnchorPoint( AnchorPoint::TOP_LEFT );
      }
      else if( i == 2 )
      {
        //Top right model
        mContainers[i].SetSizeModeFactor( Vector3( MODEL_SCALE / 3.0f, MODEL_SCALE / 3.0f, 0.0f ) );
        mContainers[i].SetParentOrigin( Vector3( 0.95, 0.03, 0.5 ) ); //Offset from top right
        mContainers[i].SetAnchorPoint( AnchorPoint::TOP_RIGHT );
      }

      mPanGestureDetector.Attach( mContainers[i] );
      layer.Add( mContainers[i] );
    }

    //Set up models
    for( int i = 0; i < NUM_MESHES; i++ )
    {
      //Create control to display model
      Control control = Control::New();
      control.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
      control.SetParentOrigin( ParentOrigin::CENTER );
      control.SetAnchorPoint( AnchorPoint::CENTER );
      mContainers[i].Add( control );

      //Make model spin to demonstrate 3D
      Animation rotationAnimation = Animation::New( 15.0f );
      float spin = i % 2 == 0 ? 1.0f : -1.0f; //Make actors spin in different directions to better show independence.
      rotationAnimation.AnimateBy( Property( control, Actor::Property::ORIENTATION ),
                                   Quaternion( Degree( 0.0f ), Degree( spin * 360.0f ), Degree( 0.0f ) ) );
      rotationAnimation.SetLooping( true );
      rotationAnimation.Play();

      //Store model information in corresponding structs.
      mModels[i].control = control;
      mModels[i].rotation.x = 0.0f;
      mModels[i].rotation.y = 0.0f;
      mModels[i].rotationAnimation = rotationAnimation;
    }

    //Calling this sets the model in the controls.
    ReloadModel();

    //Create button for model changing
    Toolkit::PushButton modelButton = Toolkit::PushButton::New();
    modelButton.SetResizePolicy( ResizePolicy::USE_NATURAL_SIZE, Dimension::ALL_DIMENSIONS );
    modelButton.ClickedSignal().Connect( this, &MeshRendererController::OnChangeModelClicked );
    modelButton.SetParentOrigin( Vector3( 0.1, 0.95, 0.5 ) ); //Offset from bottom left
    modelButton.SetAnchorPoint( AnchorPoint::BOTTOM_LEFT );
    modelButton.SetLabelText( "Change Model" );
    layer.Add( modelButton );

    //Create button for shader changing
    Toolkit::PushButton shaderButton = Toolkit::PushButton::New();
    shaderButton.SetResizePolicy( ResizePolicy::USE_NATURAL_SIZE, Dimension::ALL_DIMENSIONS );
    shaderButton.ClickedSignal().Connect( this, &MeshRendererController::OnChangeShaderClicked );
    shaderButton.SetParentOrigin( Vector3( 0.9, 0.95, 0.5 ) ); //Offset from bottom right
    shaderButton.SetAnchorPoint( AnchorPoint::BOTTOM_RIGHT );
    shaderButton.SetLabelText( "Change Shader" );
    layer.Add( shaderButton );
  }

  //Updates the displayed models to account for parameter changes.
  void ReloadModel()
  {
    //Create mesh property map
    Property::Map map;
    map.Insert( "rendererType", "mesh" );
    map.Insert( "objectUrl", MODEL_FILE[mModelIndex] );
    map.Insert( "materialUrl", MATERIAL_FILE[mModelIndex] );
    map.Insert( "texturesPath", TEXTURES_PATH );
    map.Insert( "shaderType", SHADER_TYPE[mShaderIndex] );

    //Set the two controls to use the mesh
    for( int i = 0; i < NUM_MESHES; i++ )
    {
      mModels[i].control.SetProperty( Control::Property::BACKGROUND, Property::Value( map ) );
    }
  }

  //Rotates the panned model based on the gesture.
  void OnPan( Actor actor, const PanGesture& gesture )
  {
    switch( gesture.state )
    {
      case Gesture::Started:
      {
        //Find out which model has been selected
        actor.GetProperty( actor.GetPropertyIndex( "Tag" ) ).Get( mSelectedModelIndex );

        //Pause current animation, as the gesture will be used to manually rotate the model
        mModels[mSelectedModelIndex].rotationAnimation.Pause();

        break;
      }
      case Gesture::Continuing:
      {
        //Rotate based off the gesture.
        mModels[mSelectedModelIndex].rotation.x -= gesture.displacement.y / X_ROTATION_DISPLACEMENT_FACTOR; // Y displacement rotates around X axis
        mModels[mSelectedModelIndex].rotation.y += gesture.displacement.x / Y_ROTATION_DISPLACEMENT_FACTOR; // X displacement rotates around Y axis
        Quaternion rotation = Quaternion( Radian( mModels[mSelectedModelIndex].rotation.x ), Vector3::XAXIS) *
                              Quaternion( Radian( mModels[mSelectedModelIndex].rotation.y ), Vector3::YAXIS);

        mModels[mSelectedModelIndex].control.SetOrientation( rotation );

        break;
      }
      case Gesture::Finished:
      {
        //Return to automatic animation
        mModels[mSelectedModelIndex].rotationAnimation.Play();

        break;
      }
      case Gesture::Cancelled:
      {
        //Return to automatic animation
        mModels[mSelectedModelIndex].rotationAnimation.Play();

        break;
      }
      default:
      {
        //We can ignore other gestures and gesture states.
        break;
      }
    }
  }

  //Cycle through the list of models.
  bool OnChangeModelClicked( Toolkit::Button button )
  {
    ++mModelIndex %= 3;

    ReloadModel();

    return true;
  }

  //Cycle through the list of shaders.
  bool OnChangeShaderClicked( Toolkit::Button button )
  {
    ++mShaderIndex %= 3;

    ReloadModel();

    return true;
  }

  //If escape or the back button is pressed, quit the application (and return to the launcher)
  void OnKeyEvent( const KeyEvent& event )
  {
    if( event.state == KeyEvent::Down )
    {
      if( IsKey( event, DALI_KEY_ESCAPE) || IsKey( event, DALI_KEY_BACK ) )
      {
        mApplication.Quit();
      }
    }
  }

private:
  Application&  mApplication;

  //The models displayed on screen, including information about rotation.
  Model mModels[NUM_MESHES];
  Actor mContainers[NUM_MESHES];

  //Used to detect panning to rotate the selected model.
  PanGestureDetector mPanGestureDetector;

  int mModelIndex; //Index of model to load.
  int mShaderIndex; //Index of shader type to use.
  int mSelectedModelIndex; //Index of model selected on screen.
};

void RunTest( Application& application )
{
  MeshRendererController test( application );

  application.MainLoop();
}

// Entry point for Linux & Tizen applications
//
int main( int argc, char **argv )
{
  Application application = Application::New( &argc, &argv );

  RunTest( application );

  return 0;
}
<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: proplinelistener.hxx,v $
 *
 *  $Revision: 1.6 $
 *
 *  last change: $Author: kz $ $Date: 2006-12-13 12:03:09 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

#ifndef _EXTENSIONS_PROPCTRLR_PROPLINELISTENER_HXX_
#define _EXTENSIONS_PROPCTRLR_PROPLINELISTENER_HXX_

#ifndef _STRING_HXX
#include <tools/string.hxx>
#endif

//........................................................................
namespace pcr
{
//........................................................................

    //====================================================================
    class IPropertyLineListener
    {
    public:
        virtual void    Clicked(    const ::rtl::OUString& _rName, sal_Bool _bPrimary ) = 0;
        virtual void    Commit(     const ::rtl::OUString& _rName, const ::com::sun::star::uno::Any& _rVal ) = 0;
    };

//............................................................................
} // namespace pcr
//............................................................................

#endif // _EXTENSIONS_PROPCTRLR_PROPLINELISTENER_HXX_

<commit_msg>INTEGRATION: CWS changefileheader (1.6.222); FILE MERGED 2008/04/01 15:15:20 thb 1.6.222.2: #i85898# Stripping all external header guards 2008/03/31 12:31:54 rt 1.6.222.1: #i87441# Change license header to LPGL v3.<commit_after>/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * Copyright 2008 by Sun Microsystems, Inc.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * $RCSfile: proplinelistener.hxx,v $
 * $Revision: 1.7 $
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org 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 version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/

#ifndef _EXTENSIONS_PROPCTRLR_PROPLINELISTENER_HXX_
#define _EXTENSIONS_PROPCTRLR_PROPLINELISTENER_HXX_

#include <tools/string.hxx>

//........................................................................
namespace pcr
{
//........................................................................

    //====================================================================
    class IPropertyLineListener
    {
    public:
        virtual void    Clicked(    const ::rtl::OUString& _rName, sal_Bool _bPrimary ) = 0;
        virtual void    Commit(     const ::rtl::OUString& _rName, const ::com::sun::star::uno::Any& _rVal ) = 0;
    };

//............................................................................
} // namespace pcr
//............................................................................

#endif // _EXTENSIONS_PROPCTRLR_PROPLINELISTENER_HXX_

<|endoftext|>