<commit_before>/* vim: set sw=4 sts=4 et foldmethod=syntax : */

#include <max_clique/dbmcsa_max_clique.hh>
#include <max_clique/colourise.hh>
#include <max_clique/print_incumbent.hh>
#include <graph/degree_sort.hh>
#include <graph/min_width_sort.hh>

#include <algorithm>

using namespace parasols;

namespace
{
    template <unsigned size_>
    struct Defer
    {
        std::array<unsigned, size_ * bits_per_word> p_order;
        std::array<unsigned, size_ * bits_per_word> colours;
        FixedBitSet<size_> c;
        FixedBitSet<size_> p;
        std::vector<int> positions;
    };

    template <unsigned size_>
    auto expand(
            const FixedBitGraph<size_> & graph,
            const std::vector<int> & o,                      // vertex ordering
            const std::array<unsigned, size_ * bits_per_word> & p_order,
            const std::array<unsigned, size_ * bits_per_word> & colours,
            FixedBitSet<size_> & c,                          // current candidate clique
            FixedBitSet<size_> & p,                          // potential additions
            MaxCliqueResult & result,
            const MaxCliqueParams & params,
            std::vector<int> & positions,
            bool already_split
            ) -> void
    {
        auto c_popcount = c.popcount();
        int n = p.popcount() - 1;

        // bound, timeout or early exit?
        if (c_popcount + colours[n] <= result.size || result.size >= params.stop_after_finding || params.abort.load())
            return;

        auto v = p_order[n];
        ++positions.back();

        // consider taking v
        c.set(v);
        ++c_popcount;

        // filter p to contain vertices adjacent to v
        FixedBitSet<size_> new_p = p;
        graph.intersect_with_row(v, new_p);

        if (new_p.empty()) {
            // potential new best
            if (c_popcount > result.size) {
                result.size = c_popcount;
                result.members.clear();
                for (int i = 0 ; i < graph.size() ; ++i)
                    if (c.test(i))
                        result.members.insert(o[i]);

                print_incumbent(params, c_popcount, positions);
            }
        }
        else {
            // get our coloured vertices
            ++result.nodes;
            std::array<unsigned, size_ * bits_per_word> new_p_order, new_colours;
            colourise<size_>(graph, new_p, new_p_order, new_colours);
            positions.push_back(0);
            expand<size_>(graph, o, new_p_order, new_colours, c, new_p, result, params, positions, false);
            positions.pop_back();
        }

        if (! already_split) {
            // now consider not taking v
            c.unset(v);
            p.unset(v);
            --c_popcount;

            if (n > 0) {
                expand<size_>(graph, o, p_order, colours, c, p, result, params, positions, false);
            }
        }
    }

    template <MaxCliqueOrder order_, unsigned size_>
    auto dbmcsa(const Graph & graph, const MaxCliqueParams & params) -> MaxCliqueResult
    {
        MaxCliqueResult result;
        result.size = params.initial_bound;

        std::vector<int> o(graph.size()); // vertex ordering

        FixedBitSet<size_> c; // current candidate clique
        c.resize(graph.size());

        FixedBitSet<size_> p; // potential additions
        p.resize(graph.size());
        p.set_all();

        // populate our order with every vertex initially
        std::iota(o.begin(), o.end(), 0);

        switch (order_) {
            case MaxCliqueOrder::Degree:
                degree_sort(graph, o, false);
                break;
            case MaxCliqueOrder::MinWidth:
                min_width_sort(graph, o, false);
                break;
            case MaxCliqueOrder::ExDegree:
                exdegree_sort(graph, o, false);
                break;
            case MaxCliqueOrder::DynExDegree:
                dynexdegree_sort(graph, o, false);
                break;
        }

        // re-encode graph as a bit graph
        FixedBitGraph<size_> bit_graph;
        bit_graph.resize(graph.size());

        for (int i = 0 ; i < graph.size() ; ++i)
            for (int j = 0 ; j < graph.size() ; ++j)
                if (graph.adjacent(o[i], o[j]))
                    bit_graph.add_edge(i, j);

        std::vector<int> positions;
        positions.reserve(graph.size());
        positions.push_back(0);

        // initial ordering
        ++result.nodes;
        std::array<unsigned, size_ * bits_per_word> new_p_order, new_colours;
        colourise<size_>(bit_graph, p, new_p_order, new_colours);

        // first job
        Defer<size_> next_job{ new_p_order, new_colours, c, p, positions };

        // go!
        while (! next_job.p.empty()) {
            // split
            Defer<size_> job = next_job;

            ++next_job.positions.back();
            auto v = next_job.p_order[next_job.p.popcount() - 1];
            next_job.c.unset(v);
            next_job.p.unset(v);

            expand<size_>(bit_graph, o, job.p_order, job.colours, job.c, job.p, result, params, job.positions, true);
        }

        return result;
    }
}

template <MaxCliqueOrder order_>
auto parasols::dbmcsa_max_clique(const Graph & graph, const MaxCliqueParams & params) -> MaxCliqueResult
{
    /* This is pretty horrible: in order to avoid dynamic allocation, select
     * the appropriate specialisation for our graph's size. */
    static_assert(max_graph_words == 1024, "Need to update here if max_graph_size is changed.");
    if (graph.size() < bits_per_word)
        return dbmcsa<order_, 1>(graph, params);
    else if (graph.size() < 2 * bits_per_word)
        return dbmcsa<order_, 2>(graph, params);
    else if (graph.size() < 4 * bits_per_word)
        return dbmcsa<order_, 4>(graph, params);
    else if (graph.size() < 8 * bits_per_word)
        return dbmcsa<order_, 8>(graph, params);
    else if (graph.size() < 16 * bits_per_word)
        return dbmcsa<order_, 16>(graph, params);
    else if (graph.size() < 32 * bits_per_word)
        return dbmcsa<order_, 32>(graph, params);
    else if (graph.size() < 64 * bits_per_word)
        return dbmcsa<order_, 64>(graph, params);
    else if (graph.size() < 128 * bits_per_word)
        return dbmcsa<order_, 128>(graph, params);
    else if (graph.size() < 256 * bits_per_word)
        return dbmcsa<order_, 256>(graph, params);
    else if (graph.size() < 512 * bits_per_word)
        return dbmcsa<order_, 512>(graph, params);
    else if (graph.size() < 1024 * bits_per_word)
        return dbmcsa<order_, 1024>(graph, params);
    else
        throw GraphTooBig();
}

template auto parasols::dbmcsa_max_clique<MaxCliqueOrder::Degree>(const Graph &, const MaxCliqueParams &) -> MaxCliqueResult;
template auto parasols::dbmcsa_max_clique<MaxCliqueOrder::MinWidth>(const Graph &, const MaxCliqueParams &) -> MaxCliqueResult;
template auto parasols::dbmcsa_max_clique<MaxCliqueOrder::ExDegree>(const Graph &, const MaxCliqueParams &) -> MaxCliqueResult;
template auto parasols::dbmcsa_max_clique<MaxCliqueOrder::DynExDegree>(const Graph &, const MaxCliqueParams &) -> MaxCliqueResult;

<commit_msg>Threaded goodness<commit_after>/* vim: set sw=4 sts=4 et foldmethod=syntax : */

#include <max_clique/dbmcsa_max_clique.hh>
#include <max_clique/colourise.hh>
#include <max_clique/print_incumbent.hh>
#include <graph/degree_sort.hh>
#include <graph/min_width_sort.hh>
#include <threads/atomic_incumbent.hh>

#include <algorithm>
#include <thread>
#include <mutex>

using namespace parasols;

namespace
{
    template <unsigned size_>
    struct Defer
    {
        std::array<unsigned, size_ * bits_per_word> p_order;
        std::array<unsigned, size_ * bits_per_word> colours;
        FixedBitSet<size_> c;
        FixedBitSet<size_> p;
        std::vector<int> positions;
    };

    template <unsigned size_>
    auto found_possible_new_best(
            const FixedBitGraph<size_> & graph,
            const std::vector<int> & o,
            const FixedBitSet<size_> & c,
            int c_popcount,
            const MaxCliqueParams & params,
            MaxCliqueResult & result,
            AtomicIncumbent & best_anywhere,
            const std::vector<int> & position) -> void
    {
        if (best_anywhere.update(c_popcount)) {
            result.size = c_popcount;
            result.members.clear();
            for (int i = 0 ; i < graph.size() ; ++i)
                if (c.test(i))
                    result.members.insert(o[i]);
            print_incumbent(params, result.size, position);
        }
    }

    auto bound(unsigned c_popcount, unsigned cn, const MaxCliqueParams & params, AtomicIncumbent & best_anywhere) -> bool
    {
        unsigned best_anywhere_value = best_anywhere.get();
        return (c_popcount + cn <= best_anywhere_value || best_anywhere_value >= params.stop_after_finding);
    }

    template <unsigned size_>
    auto expand(
            const FixedBitGraph<size_> & graph,
            const std::vector<int> & o,                      // vertex ordering
            const std::array<unsigned, size_ * bits_per_word> & p_order,
            const std::array<unsigned, size_ * bits_per_word> & colours,
            FixedBitSet<size_> & c,                          // current candidate clique
            FixedBitSet<size_> & p,                          // potential additions
            MaxCliqueResult & result,
            AtomicIncumbent & best_anywhere,
            const MaxCliqueParams & params,
            std::vector<int> & positions,
            bool already_split
            ) -> void
    {
        auto c_popcount = c.popcount();
        int n = p.popcount() - 1;

        // bound, timeout or early exit?
        if (bound(c_popcount, colours[n], params, best_anywhere) || params.abort.load())
            return;

        auto v = p_order[n];
        ++positions.back();

        // consider taking v
        c.set(v);
        ++c_popcount;

        // filter p to contain vertices adjacent to v
        FixedBitSet<size_> new_p = p;
        graph.intersect_with_row(v, new_p);

        if (new_p.empty()) {
            found_possible_new_best(graph, o, c, c_popcount, params, result, best_anywhere, positions);
        }
        else {
            // get our coloured vertices
            ++result.nodes;
            std::array<unsigned, size_ * bits_per_word> new_p_order, new_colours;
            colourise<size_>(graph, new_p, new_p_order, new_colours);
            positions.push_back(0);
            expand<size_>(graph, o, new_p_order, new_colours, c, new_p, result, best_anywhere, params, positions, false);
            positions.pop_back();
        }

        if (! already_split) {
            // now consider not taking v
            c.unset(v);
            p.unset(v);
            --c_popcount;

            if (n > 0) {
                expand<size_>(graph, o, p_order, colours, c, p, result, best_anywhere, params, positions, false);
            }
        }
    }

    template <MaxCliqueOrder order_, unsigned size_>
    auto dbmcsa(const Graph & graph, const MaxCliqueParams & params) -> MaxCliqueResult
    {
        MaxCliqueResult result;
        std::mutex result_mutex;
        result.size = params.initial_bound;

        AtomicIncumbent best_anywhere; // global incumbent
        best_anywhere.update(params.initial_bound);

        std::vector<int> o(graph.size()); // vertex ordering

        FixedBitSet<size_> c; // current candidate clique
        c.resize(graph.size());

        FixedBitSet<size_> p; // potential additions
        p.resize(graph.size());
        p.set_all();

        // populate our order with every vertex initially
        std::iota(o.begin(), o.end(), 0);

        switch (order_) {
            case MaxCliqueOrder::Degree:
                degree_sort(graph, o, false);
                break;
            case MaxCliqueOrder::MinWidth:
                min_width_sort(graph, o, false);
                break;
            case MaxCliqueOrder::ExDegree:
                exdegree_sort(graph, o, false);
                break;
            case MaxCliqueOrder::DynExDegree:
                dynexdegree_sort(graph, o, false);
                break;
        }

        // re-encode graph as a bit graph
        FixedBitGraph<size_> bit_graph;
        bit_graph.resize(graph.size());

        for (int i = 0 ; i < graph.size() ; ++i)
            for (int j = 0 ; j < graph.size() ; ++j)
                if (graph.adjacent(o[i], o[j]))
                    bit_graph.add_edge(i, j);

        std::vector<int> positions;
        positions.reserve(graph.size());
        positions.push_back(0);

        // initial ordering
        ++result.nodes;
        std::array<unsigned, size_ * bits_per_word> new_p_order, new_colours;
        colourise<size_>(bit_graph, p, new_p_order, new_colours);

        // first job
        std::mutex next_job_mutex;
        Defer<size_> next_job{ new_p_order, new_colours, c, p, positions };

        std::list<std::thread> threads; // workers
        for (unsigned i = 0 ; i < params.n_threads ; ++i) {
            threads.push_back(std::thread([&, i] {

                auto start_time = std::chrono::steady_clock::now(); // local start time
                MaxCliqueResult tr; // local result

                while (true) {
                    Defer<size_> job;
                    {
                        std::unique_lock<std::mutex> guard(next_job_mutex);
                        if (next_job.p.empty())
                            break;

                        // split
                        job = next_job;

                        ++next_job.positions.back();
                        auto v = next_job.p_order[next_job.p.popcount() - 1];
                        next_job.c.unset(v);
                        next_job.p.unset(v);
                    }

                    expand<size_>(bit_graph, o, job.p_order, job.colours, job.c, job.p, tr, best_anywhere, params, job.positions, true);
                }

                auto overall_time = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - start_time);

                // merge results
                {
                    std::unique_lock<std::mutex> guard(result_mutex);
                    result.merge(tr);
                    result.times.push_back(overall_time);
                }
            }));
        }

        for (auto & t : threads)
            t.join();

        return result;
    }
}

template <MaxCliqueOrder order_>
auto parasols::dbmcsa_max_clique(const Graph & graph, const MaxCliqueParams & params) -> MaxCliqueResult
{
    /* This is pretty horrible: in order to avoid dynamic allocation, select
     * the appropriate specialisation for our graph's size. */
    static_assert(max_graph_words == 1024, "Need to update here if max_graph_size is changed.");
    if (graph.size() < bits_per_word)
        return dbmcsa<order_, 1>(graph, params);
    else if (graph.size() < 2 * bits_per_word)
        return dbmcsa<order_, 2>(graph, params);
    else if (graph.size() < 4 * bits_per_word)
        return dbmcsa<order_, 4>(graph, params);
    else if (graph.size() < 8 * bits_per_word)
        return dbmcsa<order_, 8>(graph, params);
    else if (graph.size() < 16 * bits_per_word)
        return dbmcsa<order_, 16>(graph, params);
    else if (graph.size() < 32 * bits_per_word)
        return dbmcsa<order_, 32>(graph, params);
    else if (graph.size() < 64 * bits_per_word)
        return dbmcsa<order_, 64>(graph, params);
    else if (graph.size() < 128 * bits_per_word)
        return dbmcsa<order_, 128>(graph, params);
    else if (graph.size() < 256 * bits_per_word)
        return dbmcsa<order_, 256>(graph, params);
    else if (graph.size() < 512 * bits_per_word)
        return dbmcsa<order_, 512>(graph, params);
    else if (graph.size() < 1024 * bits_per_word)
        return dbmcsa<order_, 1024>(graph, params);
    else
        throw GraphTooBig();
}

template auto parasols::dbmcsa_max_clique<MaxCliqueOrder::Degree>(const Graph &, const MaxCliqueParams &) -> MaxCliqueResult;
template auto parasols::dbmcsa_max_clique<MaxCliqueOrder::MinWidth>(const Graph &, const MaxCliqueParams &) -> MaxCliqueResult;
template auto parasols::dbmcsa_max_clique<MaxCliqueOrder::ExDegree>(const Graph &, const MaxCliqueParams &) -> MaxCliqueResult;
template auto parasols::dbmcsa_max_clique<MaxCliqueOrder::DynExDegree>(const Graph &, const MaxCliqueParams &) -> MaxCliqueResult;

<|endoftext|>


<commit_before>// Copyright 2010-2013 RethinkDB, all rights reserved.
#include "rdb_protocol/datum.hpp"
#include "rdb_protocol/error.hpp"
#include "rdb_protocol/validate.hpp"
#include "utils.hpp"

#define check_has(pb, has_field, field) do {                            \
        auto const &check_has_tmp = (pb);                               \
        rcheck_toplevel(                                                \
            check_has_tmp.has_field(),                                  \
            ql::base_exc_t::GENERIC,                                    \
            strprintf("MALFORMED PROTOBUF (missing field `%s`):\n%s",   \
                      (field), check_has_tmp.DebugString().c_str()));   \
    } while (0)

#define check_not_has(pb, has_field, field) do {                        \
        auto const &check_not_has_tmp = (pb);                           \
        rcheck_toplevel(                                                \
            !check_not_has_tmp.has_field(),                             \
            ql::base_exc_t::GENERIC,                                    \
            strprintf("MALFORMED PROTOBUF (spurious field `%s`):\n%s",  \
                      (field),                                          \
                      check_not_has_tmp.DebugString().c_str()));        \
    } while (0)

#define check_size(expected_size, pb, field_size, field) do {           \
        auto const &check_size_tmp = (pb);                              \
        const int check_size_expected = (expected_size);                \
        rcheck_toplevel(                                                \
            check_size_tmp.field_size() == check_size_expected,         \
            ql::base_exc_t::GENERIC,                                    \
            strprintf("MALFORMED PROTOBUF (expected field `%s` "        \
                      "to have size %d):\n%s",                          \
                      (field), check_size_expected,                     \
                      check_size_tmp.DebugString().c_str()));           \
    } while(0)

#define check_empty(pb, field_size, field) check_size(0, pb, field_size, field)

#define check_type(pbname, pb) do {                             \
        check_has(pb, has_type, "type");                        \
        rcheck_toplevel(                                                \
            pbname##_##pbname##Type_IsValid(pb.type()),                 \
            ql::base_exc_t::GENERIC,                                    \
            strprintf("MALFORMED PROTOBUF (Illegal " #pbname " type %d).", \
                      pb.type()));                                      \
    } while (0)

void validate_pb(const Query &q) {
    check_type(Query, q);
    if (q.type() == Query::START) {
        check_has(q, has_query, "query");
        validate_pb(q.query());
    } else {
        check_not_has(q, has_query, "query");
    }
    check_has(q, has_token, "token");
    for (int i = 0; i < q.global_optargs_size(); ++i) {
        validate_pb(q.global_optargs(i).val());
    }
}

void validate_pb(const Query::AssocPair &ap) {
    check_has(ap, has_key, "key");
    check_has(ap, has_val, "val");
    validate_pb(ap.val());
}

void validate_pb(const Frame &f) {
    check_type(Frame, f);
    if (f.type() == Frame::POS) {
        check_has(f, has_pos, "pos");
    } else {
        check_has(f, has_opt, "opt");
    }
}

void validate_pb(const Backtrace &bt) {
    for (int i = 0; i < bt.frames_size(); ++i) {
        validate_pb(bt.frames(i));
    }
}

void validate_pb(const Response &r) {
    check_type(Response, r);
    if (r.type() == Response::SUCCESS_ATOM
        || r.type() == Response::SUCCESS_SEQUENCE
        || r.type() == Response::SUCCESS_PARTIAL) {
        check_not_has(r, has_backtrace, "backtrace");
    } else {
        check_has(r, has_backtrace, "backtrace");
    }
    check_has(r, has_token, "token");
    for (int i = 0; i < r.response_size(); ++i) {
        validate_pb(r.response(i));
    }
}

void validate_pb(const Datum &d) {
    check_type(Datum, d);
    if (d.type() == Datum::R_BOOL) {
        check_has(d, has_r_bool, "r_bool");
    } else {
        check_not_has(d, has_r_bool, "r_bool");
    }
    if (d.type() == Datum::R_NUM) {
        check_has(d, has_r_num, "r_num");
    } else {
        check_not_has(d, has_r_num, "r_num");
    }
    if (d.type() == Datum::R_STR || d.type() == Datum::R_JSON) {
        check_has(d, has_r_str, "r_str");
    } else {
        check_not_has(d, has_r_str, "r_str");
    }
    if (d.type() == Datum::R_ARRAY) {
        for (int i = 0; i < d.r_array_size(); ++i) {
            validate_pb(d.r_array(i));
        }
    } else {
        check_empty(d, r_array_size, "r_array");
    }
    if (d.type() == Datum::R_OBJECT) {
        for (int i = 0; i < d.r_object_size(); ++i) {
            validate_pb(d.r_object(i));
        }
    } else {
        check_empty(d, r_object_size, "r_object");
    }
}

void validate_pb(const Datum::AssocPair &ap) {
    check_has(ap, has_key, "key");
    check_has(ap, has_val, "val");
    validate_pb(ap.val());
}

void validate_var_term(const Term &t) {
    check_empty(t, optargs_size, "optargs");
    check_size(1, t, args_size, "args");
    const Term &arg0 = t.args(0);
    rcheck_toplevel(arg0.type() == Term::DATUM && arg0.has_datum() &&
                    arg0.datum().type() == Datum::R_NUM &&
                    arg0.datum().has_r_num(),
                    ql::base_exc_t::GENERIC,
                    strprintf("MALFORMED PROTOBUF (expected VAR term "
                              "to have DATUM of type R_NUM):\n%s",
                              t.DebugString().c_str()));
    double number = arg0.datum().r_num();
    int64_t i;
    if (!ql::number_as_integer(number, &i) || i < 0) {
        rfail_toplevel(ql::base_exc_t::GENERIC,
                       "MALFORMED PROTOBUF (VAR term should have "
                       "a positive integer value):\n%s",
                       t.DebugString().c_str());
    }
}

void validate_pb(const Term &t) {
    check_type(Term, t);
    if (t.type() == Term::DATUM) {
        check_has(t, has_datum, "datum");
    } else {
        check_not_has(t, has_datum, "datum");
        if (t.type() == Term::VAR) {
            validate_var_term(t);
            return;
        }
    }
    for (int i = 0; i < t.args_size(); ++i) {
        validate_pb(t.args(i));
    }
    for (int i = 0; i < t.optargs_size(); ++i) {
        validate_pb(t.optargs(i));
    }
}

void validate_pb(const Term::AssocPair &ap) {
    check_has(ap, has_key, "key");
    check_has(ap, has_val, "val");
    validate_pb(ap.val());
}

static std::set<std::string> acceptable_keys = {
    "use_outdated",
    "noreply",
    "time_format",
    "profile",
    "durability",
    "group_format",
    "binary_format",
    "array_limit",
    "identifier_format",
    "min_batch_rows",
    "max_batch_rows",
    "max_batch_bytes",
    "max_batch_seconds",
    "first_batch_scaledown_factor",
};

void validate_optargs(const Query &q) {
    for (int i = 0; i < q.global_optargs_size(); ++i) {
        rcheck_toplevel(
            acceptable_keys.find(q.global_optargs(i).key()) != acceptable_keys.end(),
            ql::base_exc_t::GENERIC,
            strprintf("MALFORMED QUERY (global optarg `%s` is not recognized)",
                      q.global_optargs(i).key().c_str()));
    }
}
<commit_msg>Add `right_bound` to the list.<commit_after>// Copyright 2010-2013 RethinkDB, all rights reserved.
#include "rdb_protocol/datum.hpp"
#include "rdb_protocol/error.hpp"
#include "rdb_protocol/validate.hpp"
#include "utils.hpp"

#define check_has(pb, has_field, field) do {                            \
        auto const &check_has_tmp = (pb);                               \
        rcheck_toplevel(                                                \
            check_has_tmp.has_field(),                                  \
            ql::base_exc_t::GENERIC,                                    \
            strprintf("MALFORMED PROTOBUF (missing field `%s`):\n%s",   \
                      (field), check_has_tmp.DebugString().c_str()));   \
    } while (0)

#define check_not_has(pb, has_field, field) do {                        \
        auto const &check_not_has_tmp = (pb);                           \
        rcheck_toplevel(                                                \
            !check_not_has_tmp.has_field(),                             \
            ql::base_exc_t::GENERIC,                                    \
            strprintf("MALFORMED PROTOBUF (spurious field `%s`):\n%s",  \
                      (field),                                          \
                      check_not_has_tmp.DebugString().c_str()));        \
    } while (0)

#define check_size(expected_size, pb, field_size, field) do {           \
        auto const &check_size_tmp = (pb);                              \
        const int check_size_expected = (expected_size);                \
        rcheck_toplevel(                                                \
            check_size_tmp.field_size() == check_size_expected,         \
            ql::base_exc_t::GENERIC,                                    \
            strprintf("MALFORMED PROTOBUF (expected field `%s` "        \
                      "to have size %d):\n%s",                          \
                      (field), check_size_expected,                     \
                      check_size_tmp.DebugString().c_str()));           \
    } while(0)

#define check_empty(pb, field_size, field) check_size(0, pb, field_size, field)

#define check_type(pbname, pb) do {                             \
        check_has(pb, has_type, "type");                        \
        rcheck_toplevel(                                                \
            pbname##_##pbname##Type_IsValid(pb.type()),                 \
            ql::base_exc_t::GENERIC,                                    \
            strprintf("MALFORMED PROTOBUF (Illegal " #pbname " type %d).", \
                      pb.type()));                                      \
    } while (0)

void validate_pb(const Query &q) {
    check_type(Query, q);
    if (q.type() == Query::START) {
        check_has(q, has_query, "query");
        validate_pb(q.query());
    } else {
        check_not_has(q, has_query, "query");
    }
    check_has(q, has_token, "token");
    for (int i = 0; i < q.global_optargs_size(); ++i) {
        validate_pb(q.global_optargs(i).val());
    }
}

void validate_pb(const Query::AssocPair &ap) {
    check_has(ap, has_key, "key");
    check_has(ap, has_val, "val");
    validate_pb(ap.val());
}

void validate_pb(const Frame &f) {
    check_type(Frame, f);
    if (f.type() == Frame::POS) {
        check_has(f, has_pos, "pos");
    } else {
        check_has(f, has_opt, "opt");
    }
}

void validate_pb(const Backtrace &bt) {
    for (int i = 0; i < bt.frames_size(); ++i) {
        validate_pb(bt.frames(i));
    }
}

void validate_pb(const Response &r) {
    check_type(Response, r);
    if (r.type() == Response::SUCCESS_ATOM
        || r.type() == Response::SUCCESS_SEQUENCE
        || r.type() == Response::SUCCESS_PARTIAL) {
        check_not_has(r, has_backtrace, "backtrace");
    } else {
        check_has(r, has_backtrace, "backtrace");
    }
    check_has(r, has_token, "token");
    for (int i = 0; i < r.response_size(); ++i) {
        validate_pb(r.response(i));
    }
}

void validate_pb(const Datum &d) {
    check_type(Datum, d);
    if (d.type() == Datum::R_BOOL) {
        check_has(d, has_r_bool, "r_bool");
    } else {
        check_not_has(d, has_r_bool, "r_bool");
    }
    if (d.type() == Datum::R_NUM) {
        check_has(d, has_r_num, "r_num");
    } else {
        check_not_has(d, has_r_num, "r_num");
    }
    if (d.type() == Datum::R_STR || d.type() == Datum::R_JSON) {
        check_has(d, has_r_str, "r_str");
    } else {
        check_not_has(d, has_r_str, "r_str");
    }
    if (d.type() == Datum::R_ARRAY) {
        for (int i = 0; i < d.r_array_size(); ++i) {
            validate_pb(d.r_array(i));
        }
    } else {
        check_empty(d, r_array_size, "r_array");
    }
    if (d.type() == Datum::R_OBJECT) {
        for (int i = 0; i < d.r_object_size(); ++i) {
            validate_pb(d.r_object(i));
        }
    } else {
        check_empty(d, r_object_size, "r_object");
    }
}

void validate_pb(const Datum::AssocPair &ap) {
    check_has(ap, has_key, "key");
    check_has(ap, has_val, "val");
    validate_pb(ap.val());
}

void validate_var_term(const Term &t) {
    check_empty(t, optargs_size, "optargs");
    check_size(1, t, args_size, "args");
    const Term &arg0 = t.args(0);
    rcheck_toplevel(arg0.type() == Term::DATUM && arg0.has_datum() &&
                    arg0.datum().type() == Datum::R_NUM &&
                    arg0.datum().has_r_num(),
                    ql::base_exc_t::GENERIC,
                    strprintf("MALFORMED PROTOBUF (expected VAR term "
                              "to have DATUM of type R_NUM):\n%s",
                              t.DebugString().c_str()));
    double number = arg0.datum().r_num();
    int64_t i;
    if (!ql::number_as_integer(number, &i) || i < 0) {
        rfail_toplevel(ql::base_exc_t::GENERIC,
                       "MALFORMED PROTOBUF (VAR term should have "
                       "a positive integer value):\n%s",
                       t.DebugString().c_str());
    }
}

void validate_pb(const Term &t) {
    check_type(Term, t);
    if (t.type() == Term::DATUM) {
        check_has(t, has_datum, "datum");
    } else {
        check_not_has(t, has_datum, "datum");
        if (t.type() == Term::VAR) {
            validate_var_term(t);
            return;
        }
    }
    for (int i = 0; i < t.args_size(); ++i) {
        validate_pb(t.args(i));
    }
    for (int i = 0; i < t.optargs_size(); ++i) {
        validate_pb(t.optargs(i));
    }
}

void validate_pb(const Term::AssocPair &ap) {
    check_has(ap, has_key, "key");
    check_has(ap, has_val, "val");
    validate_pb(ap.val());
}

static std::set<std::string> acceptable_keys = {
    "use_outdated",
    "noreply",
    "time_format",
    "profile",
    "durability",
    "group_format",
    "binary_format",
    "array_limit",
    "identifier_format",
    "right_bound",

    "min_batch_rows",
    "max_batch_rows",
    "max_batch_bytes",
    "max_batch_seconds",
    "first_batch_scaledown_factor",
};

void validate_optargs(const Query &q) {
    for (int i = 0; i < q.global_optargs_size(); ++i) {
        rcheck_toplevel(
            acceptable_keys.find(q.global_optargs(i).key()) != acceptable_keys.end(),
            ql::base_exc_t::GENERIC,
            strprintf("MALFORMED QUERY (global optarg `%s` is not recognized)",
                      q.global_optargs(i).key().c_str()));
    }
}
<|endoftext|>


<commit_before>#include "gtest/gtest.h"
#include "dsb/util.hpp"
#include <vector>

using namespace dsb::util;

TEST(dsb_util, EncodeUint16) {
    char b[2] = { '\xFF', '\xFF' };

    EncodeUint16(0, b);
    EXPECT_EQ('\x00', b[0]);
    EXPECT_EQ('\x00', b[1]);

    EncodeUint16(65535, b);
    EXPECT_EQ('\xFF', b[0]);
    EXPECT_EQ('\xFF', b[1]);

    EncodeUint16(4608, b);
    EXPECT_EQ('\x00', b[0]);
    EXPECT_EQ('\x12', b[1]);

    EncodeUint16(63, b);
    EXPECT_EQ('\x3F', b[0]);
    EXPECT_EQ('\x00', b[1]);

    EncodeUint16(15238, b);
    EXPECT_EQ('\x86', b[0]);
    EXPECT_EQ('\x3B', b[1]);
}

TEST(dsb_util, DecodeUint16) {
    EXPECT_EQ(    0, DecodeUint16("\x00\x00"));
    EXPECT_EQ(65535, DecodeUint16("\xFF\xFF"));
    EXPECT_EQ( 4608, DecodeUint16("\x00\x12"));
    EXPECT_EQ(   63, DecodeUint16("\x3F\x00"));
    EXPECT_EQ(15238, DecodeUint16("\x86\x3B"));
}

TEST(dsb_util, RandomUUID)
{
    const auto u = RandomUUID();
    EXPECT_EQ(36U, u.size());
    EXPECT_NE(u, RandomUUID());
}

TEST(dsb_util, SwapOut_value)
{
    int a = 123;
    int b = SwapOut(a, 456);
    EXPECT_EQ(456, a);
    EXPECT_EQ(123, b);
    int c = SwapOut(b);
    EXPECT_EQ(0, b);
    EXPECT_EQ(123, c);
}

TEST(dsb_util, SwapOut_class)
{
    std::vector<int> a;
    a.push_back(123);
    const auto dataPtr = a.data();
    std::vector<int> r;
    r.push_back(456);
    r.push_back(789);

    std::vector<int> b = SwapOut(a, r);
    ASSERT_EQ(2U, a.size());
    EXPECT_EQ(456, a[0]);
    EXPECT_EQ(789, a[1]);

    // The following test is (most likely) not specified C++ behaviour, but
    // it would be a strange vector implementation that didn't implement a move
    // as a pointer move...
    EXPECT_EQ(1U, b.size());
    EXPECT_EQ(dataPtr, b.data());

    std::vector<int> c = SwapOut(b);
    EXPECT_TRUE(b.empty());
    EXPECT_EQ(1U, c.size());
    EXPECT_EQ(dataPtr, c.data());
}

TEST(dsb_util, OnScopeExit)
{
    int i = 0;
    {
        auto setToOne = OnScopeExit([&i]() { i = 1; });
        EXPECT_EQ(0, i);
    }
    EXPECT_EQ(1, i);
    try {
        auto setToTwo = OnScopeExit([&i]() { i = 2; });
        EXPECT_EQ(1, i);
        throw 0;
    } catch (...) {
        EXPECT_EQ(2, i);
        i = 3;
    }
    EXPECT_EQ(3, i);
}
<commit_msg>Added some missing dsb::util tests<commit_after>#include "gtest/gtest.h"
#include "dsb/util.hpp"
#include <vector>
#include "boost/filesystem.hpp"

using namespace dsb::util;

TEST(dsb_util, EncodeUint16) {
    char b[2] = { '\xFF', '\xFF' };

    EncodeUint16(0, b);
    EXPECT_EQ('\x00', b[0]);
    EXPECT_EQ('\x00', b[1]);

    EncodeUint16(65535, b);
    EXPECT_EQ('\xFF', b[0]);
    EXPECT_EQ('\xFF', b[1]);

    EncodeUint16(4608, b);
    EXPECT_EQ('\x00', b[0]);
    EXPECT_EQ('\x12', b[1]);

    EncodeUint16(63, b);
    EXPECT_EQ('\x3F', b[0]);
    EXPECT_EQ('\x00', b[1]);

    EncodeUint16(15238, b);
    EXPECT_EQ('\x86', b[0]);
    EXPECT_EQ('\x3B', b[1]);
}

TEST(dsb_util, DecodeUint16) {
    EXPECT_EQ(    0, DecodeUint16("\x00\x00"));
    EXPECT_EQ(65535, DecodeUint16("\xFF\xFF"));
    EXPECT_EQ( 4608, DecodeUint16("\x00\x12"));
    EXPECT_EQ(   63, DecodeUint16("\x3F\x00"));
    EXPECT_EQ(15238, DecodeUint16("\x86\x3B"));
}

TEST(dsb_util, RandomUUID)
{
    const auto u = RandomUUID();
    EXPECT_EQ(36U, u.size());
    EXPECT_NE(u, RandomUUID());
}

TEST(dsb_util, SwapOut_value)
{
    int a = 123;
    int b = SwapOut(a, 456);
    EXPECT_EQ(456, a);
    EXPECT_EQ(123, b);
    int c = SwapOut(b);
    EXPECT_EQ(0, b);
    EXPECT_EQ(123, c);
}

TEST(dsb_util, SwapOut_class)
{
    std::vector<int> a;
    a.push_back(123);
    const auto dataPtr = a.data();
    std::vector<int> r;
    r.push_back(456);
    r.push_back(789);

    std::vector<int> b = SwapOut(a, r);
    ASSERT_EQ(2U, a.size());
    EXPECT_EQ(456, a[0]);
    EXPECT_EQ(789, a[1]);

    // The following test is (most likely) not specified C++ behaviour, but
    // it would be a strange vector implementation that didn't implement a move
    // as a pointer move...
    EXPECT_EQ(1U, b.size());
    EXPECT_EQ(dataPtr, b.data());

    std::vector<int> c = SwapOut(b);
    EXPECT_TRUE(b.empty());
    EXPECT_EQ(1U, c.size());
    EXPECT_EQ(dataPtr, c.data());
}

TEST(dsb_util, OnScopeExit)
{
    int i = 0;
    {
        auto setToOne = OnScopeExit([&i]() { i = 1; });
        EXPECT_EQ(0, i);
    }
    EXPECT_EQ(1, i);
    try {
        auto setToTwo = OnScopeExit([&i]() { i = 2; });
        EXPECT_EQ(1, i);
        throw 0;
    } catch (...) {
        EXPECT_EQ(2, i);
        i = 3;
    }
    EXPECT_EQ(3, i);
}

TEST(dsb_util, TempDir)
{
    boost::filesystem::path d;
    {
        auto tmp = TempDir();
        d = tmp.Path();
        ASSERT_FALSE(d.empty());
        EXPECT_TRUE(boost::filesystem::is_directory(d));
        EXPECT_TRUE(boost::filesystem::is_empty(d));
    }
    EXPECT_FALSE(boost::filesystem::exists(d));
}

TEST(dsb_util, ThisExePath)
{
#ifdef _WIN32
    const auto expected = "dsb_test.exe";
#else
    const auto expected = "dsb_test";
#endif
    EXPECT_EQ(expected, ThisExePath().filename().string());
}
<|endoftext|>


<commit_before>//////////////////////////////////////////////////////////////////////////
//
//  Copyright (c) 2014, John Haddon. All rights reserved.
//
//  Redistribution and use in source and binary forms, with or without
//  modification, are permitted provided that the following conditions are
//  met:
//
//      * Redistributions of source code must retain the above
//        copyright notice, this list of conditions and the following
//        disclaimer.
//
//      * 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.
//
//      * Neither the name of John Haddon nor the names of
//        any other contributors to this software may be used to endorse or
//        promote products derived from this software without specific prior
//        written permission.
//
//  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 "GafferScene/Duplicate.h"

#include "GafferScene/SceneAlgo.h"

#include "Gaffer/StringPlug.h"

#include "IECore/StringAlgo.h"

using namespace std;
using namespace IECore;
using namespace Gaffer;
using namespace GafferScene;

IE_CORE_DEFINERUNTIMETYPED( Duplicate );

size_t Duplicate::g_firstPlugIndex = 0;

Duplicate::Duplicate( const std::string &name )
	:	BranchCreator( name )
{

	storeIndexOfNextChild( g_firstPlugIndex );

	addChild( new StringPlug( "target" ) );
	addChild( new IntPlug( "copies", Plug::In, 1, 0 ) );
	addChild( new StringPlug( "name" ) );
	addChild( new TransformPlug( "transform" ) );
	addChild( new StringPlug( "__outParent", Plug::Out ) );
	addChild( new InternedStringVectorDataPlug( "__outChildNames", Plug::Out, inPlug()->childNamesPlug()->defaultValue() ) );

	parentPlug()->setInput( outParentPlug() );
	parentPlug()->setFlags( Plug::Serialisable, false );

	// Since we don't introduce any new sets, but just duplicate parts
	// of existing ones, we can save the BranchCreator base class some
	// trouble by making the setNamesPlug into a pass-through.
	outPlug()->setNamesPlug()->setInput( inPlug()->setNamesPlug() );
}

Duplicate::~Duplicate()
{
}

Gaffer::StringPlug *Duplicate::targetPlug()
{
	return getChild<StringPlug>( g_firstPlugIndex );
}

const Gaffer::StringPlug *Duplicate::targetPlug() const
{
	return getChild<StringPlug>( g_firstPlugIndex );
}

Gaffer::IntPlug *Duplicate::copiesPlug()
{
	return getChild<IntPlug>( g_firstPlugIndex + 1 );
}

const Gaffer::IntPlug *Duplicate::copiesPlug() const
{
	return getChild<IntPlug>( g_firstPlugIndex + 1 );
}

Gaffer::StringPlug *Duplicate::namePlug()
{
	return getChild<StringPlug>( g_firstPlugIndex + 2 );
}

const Gaffer::StringPlug *Duplicate::namePlug() const
{
	return getChild<StringPlug>( g_firstPlugIndex + 2 );
}

Gaffer::TransformPlug *Duplicate::transformPlug()
{
	return getChild<TransformPlug>( g_firstPlugIndex + 3 );
}

const Gaffer::TransformPlug *Duplicate::transformPlug() const
{
	return getChild<TransformPlug>( g_firstPlugIndex + 3 );
}

Gaffer::StringPlug *Duplicate::outParentPlug()
{
	return getChild<StringPlug>( g_firstPlugIndex + 4 );
}

const Gaffer::StringPlug *Duplicate::outParentPlug() const
{
	return getChild<StringPlug>( g_firstPlugIndex + 4 );
}

Gaffer::InternedStringVectorDataPlug *Duplicate::childNamesPlug()
{
	return getChild<InternedStringVectorDataPlug>( g_firstPlugIndex + 5 );
}

const Gaffer::InternedStringVectorDataPlug *Duplicate::childNamesPlug() const
{
	return getChild<InternedStringVectorDataPlug>( g_firstPlugIndex + 5 );
}

void Duplicate::affects( const Gaffer::Plug *input, AffectedPlugsContainer &outputs ) const
{
	BranchCreator::affects( input, outputs );

	if( input == targetPlug() )
	{
		outputs.push_back( outParentPlug() );
		outputs.push_back( childNamesPlug() );
	}
	else if( input == copiesPlug() )
	{
		outputs.push_back( childNamesPlug() );
	}
	else if( input == namePlug() )
	{
		outputs.push_back( childNamesPlug() );
	}
	else if( input == childNamesPlug() )
	{
		outputs.push_back( outPlug()->childNamesPlug() );
	}
	else if( transformPlug()->isAncestorOf( input ) )
	{
		outputs.push_back( outPlug()->transformPlug() );
	}
}

void Duplicate::hash( const ValuePlug *output, const Context *context, IECore::MurmurHash &h ) const
{
	BranchCreator::hash( output, context, h );

	if( output == outParentPlug() )
	{
		targetPlug()->hash( h );
	}
	else if( output == childNamesPlug() )
	{
		ScenePath target;
		ScenePlug::stringToPath( targetPlug()->getValue(), target );
		if( !SceneAlgo::exists( inPlug(), target ) )
		{
			h = childNamesPlug()->defaultValue()->Object::hash();
			return;
		}
		h.append( target.data(), target.size() );
		copiesPlug()->hash( h );
		namePlug()->hash( h );
	}
}

void Duplicate::compute( ValuePlug *output, const Context *context ) const
{
	if( output == outParentPlug() )
	{
		ScenePath target;
		ScenePlug::stringToPath( targetPlug()->getValue(), target );
		string parent;
		for( size_t i = 0; i < target.size(); ++i )
		{
			parent += "/";
			if( i < target.size() - 1 )
			{
				parent += target[i];
			}
		}
		static_cast<StringPlug *>( output )->setValue( parent );
		return;
	}
	else if( output == childNamesPlug() )
	{
		// Get the path to our target, and check it exists.
		ScenePath target;
		ScenePlug::stringToPath( targetPlug()->getValue(), target );
		if( !SceneAlgo::exists( inPlug(), target ) )
		{
			output->setToDefault();
			return;
		}

		// go ahead and generate our childnames.
		// these are composed of a stem and possibly
		// a numeric suffix. we default to deriving
		// these from the name of the target.

		std::string stem;
		int suffix = StringAlgo::numericSuffix( target.back(), 0, &stem );
		suffix++;

		const int copies = copiesPlug()->getValue();
		const std::string name = namePlug()->getValue();

		// if a name is provided explicitly, then
		// it overrides the name and suffix derived
		// from the target.
		if( name.size() )
		{
			std::string nameStem;
			const int nameSuffix = StringAlgo::numericSuffix( name, &nameStem );
			stem = nameStem;
			suffix = copies == 1 ? nameSuffix : max( nameSuffix, 1 );
		}

		InternedStringVectorDataPtr childNamesData = new InternedStringVectorData;
		std::vector<InternedString> &childNames = childNamesData->writable();
		childNames.reserve( copies );

		if( suffix == -1 )
		{
			assert( copies == 1 );
			childNames.push_back( stem );
		}
		else
		{
			boost::format formatter( "%s%d" );
			for( int i = 0; i < copies; ++i )
			{
				childNames.push_back( boost::str( formatter % stem % suffix++ ) );
			}
		}

		static_cast<InternedStringVectorDataPlug *>( output )->setValue( childNamesData );
		return;
	}

	BranchCreator::compute( output, context );
}

void Duplicate::hashBranchBound( const ScenePath &parentPath, const ScenePath &branchPath, const Gaffer::Context *context, IECore::MurmurHash &h ) const
{
	ScenePath source;
	sourcePath( branchPath, source );
	h = inPlug()->boundHash( source );
}

Imath::Box3f Duplicate::computeBranchBound( const ScenePath &parentPath, const ScenePath &branchPath, const Gaffer::Context *context ) const
{
	ScenePath source;
	sourcePath( branchPath, source );
	return inPlug()->bound( source );
}

void Duplicate::hashBranchTransform( const ScenePath &parentPath, const ScenePath &branchPath, const Gaffer::Context *context, IECore::MurmurHash &h ) const
{
	ScenePath source;
	sourcePath( branchPath, source );
	if( branchPath.size() == 1 )
	{
		BranchCreator::hashBranchTransform( parentPath, branchPath, context, h );
		h.append( inPlug()->transformHash( source ) );
		transformPlug()->hash( h );
		childNamesPlug()->hash( h );
		h.append( branchPath[0] );
	}
	else
	{
		h = inPlug()->transformHash( source );
	}
}

Imath::M44f Duplicate::computeBranchTransform( const ScenePath &parentPath, const ScenePath &branchPath, const Gaffer::Context *context ) const
{
	ScenePath source;
	sourcePath( branchPath, source );
	Imath::M44f result = inPlug()->transform( source );
	if( branchPath.size() == 1 )
	{
		const Imath::M44f matrix = transformPlug()->matrix();
		ConstInternedStringVectorDataPtr childNamesData = childNamesPlug()->getValue();
		const vector<InternedString> &childNames = childNamesData->readable();

		size_t i = 0;
		do
		{
			result = result * matrix;
		} while( i < childNames.size() && branchPath[0] != childNames[i++] );
	}
	return result;
}

void Duplicate::hashBranchAttributes( const ScenePath &parentPath, const ScenePath &branchPath, const Gaffer::Context *context, IECore::MurmurHash &h ) const
{
	ScenePath source;
	sourcePath( branchPath, source );
	h = inPlug()->attributesHash( source );
}

IECore::ConstCompoundObjectPtr Duplicate::computeBranchAttributes( const ScenePath &parentPath, const ScenePath &branchPath, const Gaffer::Context *context ) const
{
	ScenePath source;
	sourcePath( branchPath, source );
	return inPlug()->attributes( source );
}

void Duplicate::hashBranchObject( const ScenePath &parentPath, const ScenePath &branchPath, const Gaffer::Context *context, IECore::MurmurHash &h ) const
{
	ScenePath source;
	sourcePath( branchPath, source );
	h = inPlug()->objectHash( source );
}

IECore::ConstObjectPtr Duplicate::computeBranchObject( const ScenePath &parentPath, const ScenePath &branchPath, const Gaffer::Context *context ) const
{
	ScenePath source;
	sourcePath( branchPath, source );
	return inPlug()->object( source );
}

void Duplicate::hashBranchChildNames( const ScenePath &parentPath, const ScenePath &branchPath, const Gaffer::Context *context, IECore::MurmurHash &h ) const
{
	if( branchPath.size() == 0 )
	{
		h = childNamesPlug()->hash();
	}
	else
	{
		ScenePath source;
		sourcePath( branchPath, source );
		h = inPlug()->childNamesHash( source );
	}
}

IECore::ConstInternedStringVectorDataPtr Duplicate::computeBranchChildNames( const ScenePath &parentPath, const ScenePath &branchPath, const Gaffer::Context *context ) const
{
	if( branchPath.size() == 0 )
	{
		return childNamesPlug()->getValue();
	}
	else
	{
		ScenePath source;
		sourcePath( branchPath, source );
		return inPlug()->childNames( source );
	}
}

void Duplicate::hashBranchSet( const ScenePath &parentPath, const IECore::InternedString &setName, const Gaffer::Context *context, IECore::MurmurHash &h ) const
{
	h.append( inPlug()->setHash( setName ) );
	targetPlug()->hash( h );
	childNamesPlug()->hash( h );
}

IECore::ConstPathMatcherDataPtr Duplicate::computeBranchSet( const ScenePath &parentPath, const IECore::InternedString &setName, const Gaffer::Context *context ) const
{
	ConstPathMatcherDataPtr inputSetData = inPlug()->set( setName );
	const PathMatcher &inputSet = inputSetData->readable();
	if( inputSet.isEmpty() )
	{
		return outPlug()->setPlug()->defaultValue();
	}

	PathMatcher subTree = inputSet.subTree( targetPlug()->getValue() );
	if( subTree.isEmpty() )
	{
		return outPlug()->setPlug()->defaultValue();
	}

	ConstInternedStringVectorDataPtr childNamesData = childNamesPlug()->getValue();
	const vector<InternedString> &childNames = childNamesData->readable();

	PathMatcherDataPtr resultData = new PathMatcherData;
	PathMatcher &result = resultData->writable();
	ScenePath prefix( 1 );
	for( vector<InternedString>::const_iterator it = childNames.begin(), eIt = childNames.end(); it != eIt; ++it )
	{
		prefix.back() = *it;
		result.addPaths( subTree, prefix );
	}

	return resultData;
}

void Duplicate::sourcePath( const ScenePath &branchPath, ScenePath &source ) const
{
	assert( branchPath.size() );
	ScenePlug::stringToPath( targetPlug()->getValue(), source );
	copy( ++branchPath.begin(), branchPath.end(), back_inserter( source ) );
}
<commit_msg>Duplicate : Hide filter plug<commit_after>//////////////////////////////////////////////////////////////////////////
//
//  Copyright (c) 2014, John Haddon. All rights reserved.
//
//  Redistribution and use in source and binary forms, with or without
//  modification, are permitted provided that the following conditions are
//  met:
//
//      * Redistributions of source code must retain the above
//        copyright notice, this list of conditions and the following
//        disclaimer.
//
//      * 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.
//
//      * Neither the name of John Haddon nor the names of
//        any other contributors to this software may be used to endorse or
//        promote products derived from this software without specific prior
//        written permission.
//
//  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 "GafferScene/Duplicate.h"

#include "GafferScene/SceneAlgo.h"

#include "Gaffer/StringPlug.h"

#include "IECore/StringAlgo.h"

using namespace std;
using namespace IECore;
using namespace Gaffer;
using namespace GafferScene;

IE_CORE_DEFINERUNTIMETYPED( Duplicate );

size_t Duplicate::g_firstPlugIndex = 0;

Duplicate::Duplicate( const std::string &name )
	:	BranchCreator( name )
{

	storeIndexOfNextChild( g_firstPlugIndex );

	addChild( new StringPlug( "target" ) );
	addChild( new IntPlug( "copies", Plug::In, 1, 0 ) );
	addChild( new StringPlug( "name" ) );
	addChild( new TransformPlug( "transform" ) );
	addChild( new StringPlug( "__outParent", Plug::Out ) );
	addChild( new InternedStringVectorDataPlug( "__outChildNames", Plug::Out, inPlug()->childNamesPlug()->defaultValue() ) );

	parentPlug()->setInput( outParentPlug() );
	parentPlug()->setFlags( Plug::Serialisable, false );

	// Make the filter plug private. We do want to support this one
	// day, but the filter should be specifying the objects to duplicate,
	// not the parent locations to duplicate them under. Until we implement
	// that, its best not to allow folks to become dependent upon behaviour
	// that will change.
	filterPlug()->setName( "__filter" );

	// Since we don't introduce any new sets, but just duplicate parts
	// of existing ones, we can save the BranchCreator base class some
	// trouble by making the setNamesPlug into a pass-through.
	outPlug()->setNamesPlug()->setInput( inPlug()->setNamesPlug() );
}

Duplicate::~Duplicate()
{
}

Gaffer::StringPlug *Duplicate::targetPlug()
{
	return getChild<StringPlug>( g_firstPlugIndex );
}

const Gaffer::StringPlug *Duplicate::targetPlug() const
{
	return getChild<StringPlug>( g_firstPlugIndex );
}

Gaffer::IntPlug *Duplicate::copiesPlug()
{
	return getChild<IntPlug>( g_firstPlugIndex + 1 );
}

const Gaffer::IntPlug *Duplicate::copiesPlug() const
{
	return getChild<IntPlug>( g_firstPlugIndex + 1 );
}

Gaffer::StringPlug *Duplicate::namePlug()
{
	return getChild<StringPlug>( g_firstPlugIndex + 2 );
}

const Gaffer::StringPlug *Duplicate::namePlug() const
{
	return getChild<StringPlug>( g_firstPlugIndex + 2 );
}

Gaffer::TransformPlug *Duplicate::transformPlug()
{
	return getChild<TransformPlug>( g_firstPlugIndex + 3 );
}

const Gaffer::TransformPlug *Duplicate::transformPlug() const
{
	return getChild<TransformPlug>( g_firstPlugIndex + 3 );
}

Gaffer::StringPlug *Duplicate::outParentPlug()
{
	return getChild<StringPlug>( g_firstPlugIndex + 4 );
}

const Gaffer::StringPlug *Duplicate::outParentPlug() const
{
	return getChild<StringPlug>( g_firstPlugIndex + 4 );
}

Gaffer::InternedStringVectorDataPlug *Duplicate::childNamesPlug()
{
	return getChild<InternedStringVectorDataPlug>( g_firstPlugIndex + 5 );
}

const Gaffer::InternedStringVectorDataPlug *Duplicate::childNamesPlug() const
{
	return getChild<InternedStringVectorDataPlug>( g_firstPlugIndex + 5 );
}

void Duplicate::affects( const Gaffer::Plug *input, AffectedPlugsContainer &outputs ) const
{
	BranchCreator::affects( input, outputs );

	if( input == targetPlug() )
	{
		outputs.push_back( outParentPlug() );
		outputs.push_back( childNamesPlug() );
	}
	else if( input == copiesPlug() )
	{
		outputs.push_back( childNamesPlug() );
	}
	else if( input == namePlug() )
	{
		outputs.push_back( childNamesPlug() );
	}
	else if( input == childNamesPlug() )
	{
		outputs.push_back( outPlug()->childNamesPlug() );
	}
	else if( transformPlug()->isAncestorOf( input ) )
	{
		outputs.push_back( outPlug()->transformPlug() );
	}
}

void Duplicate::hash( const ValuePlug *output, const Context *context, IECore::MurmurHash &h ) const
{
	BranchCreator::hash( output, context, h );

	if( output == outParentPlug() )
	{
		targetPlug()->hash( h );
	}
	else if( output == childNamesPlug() )
	{
		ScenePath target;
		ScenePlug::stringToPath( targetPlug()->getValue(), target );
		if( !SceneAlgo::exists( inPlug(), target ) )
		{
			h = childNamesPlug()->defaultValue()->Object::hash();
			return;
		}
		h.append( target.data(), target.size() );
		copiesPlug()->hash( h );
		namePlug()->hash( h );
	}
}

void Duplicate::compute( ValuePlug *output, const Context *context ) const
{
	if( output == outParentPlug() )
	{
		ScenePath target;
		ScenePlug::stringToPath( targetPlug()->getValue(), target );
		string parent;
		for( size_t i = 0; i < target.size(); ++i )
		{
			parent += "/";
			if( i < target.size() - 1 )
			{
				parent += target[i];
			}
		}
		static_cast<StringPlug *>( output )->setValue( parent );
		return;
	}
	else if( output == childNamesPlug() )
	{
		// Get the path to our target, and check it exists.
		ScenePath target;
		ScenePlug::stringToPath( targetPlug()->getValue(), target );
		if( !SceneAlgo::exists( inPlug(), target ) )
		{
			output->setToDefault();
			return;
		}

		// go ahead and generate our childnames.
		// these are composed of a stem and possibly
		// a numeric suffix. we default to deriving
		// these from the name of the target.

		std::string stem;
		int suffix = StringAlgo::numericSuffix( target.back(), 0, &stem );
		suffix++;

		const int copies = copiesPlug()->getValue();
		const std::string name = namePlug()->getValue();

		// if a name is provided explicitly, then
		// it overrides the name and suffix derived
		// from the target.
		if( name.size() )
		{
			std::string nameStem;
			const int nameSuffix = StringAlgo::numericSuffix( name, &nameStem );
			stem = nameStem;
			suffix = copies == 1 ? nameSuffix : max( nameSuffix, 1 );
		}

		InternedStringVectorDataPtr childNamesData = new InternedStringVectorData;
		std::vector<InternedString> &childNames = childNamesData->writable();
		childNames.reserve( copies );

		if( suffix == -1 )
		{
			assert( copies == 1 );
			childNames.push_back( stem );
		}
		else
		{
			boost::format formatter( "%s%d" );
			for( int i = 0; i < copies; ++i )
			{
				childNames.push_back( boost::str( formatter % stem % suffix++ ) );
			}
		}

		static_cast<InternedStringVectorDataPlug *>( output )->setValue( childNamesData );
		return;
	}

	BranchCreator::compute( output, context );
}

void Duplicate::hashBranchBound( const ScenePath &parentPath, const ScenePath &branchPath, const Gaffer::Context *context, IECore::MurmurHash &h ) const
{
	ScenePath source;
	sourcePath( branchPath, source );
	h = inPlug()->boundHash( source );
}

Imath::Box3f Duplicate::computeBranchBound( const ScenePath &parentPath, const ScenePath &branchPath, const Gaffer::Context *context ) const
{
	ScenePath source;
	sourcePath( branchPath, source );
	return inPlug()->bound( source );
}

void Duplicate::hashBranchTransform( const ScenePath &parentPath, const ScenePath &branchPath, const Gaffer::Context *context, IECore::MurmurHash &h ) const
{
	ScenePath source;
	sourcePath( branchPath, source );
	if( branchPath.size() == 1 )
	{
		BranchCreator::hashBranchTransform( parentPath, branchPath, context, h );
		h.append( inPlug()->transformHash( source ) );
		transformPlug()->hash( h );
		childNamesPlug()->hash( h );
		h.append( branchPath[0] );
	}
	else
	{
		h = inPlug()->transformHash( source );
	}
}

Imath::M44f Duplicate::computeBranchTransform( const ScenePath &parentPath, const ScenePath &branchPath, const Gaffer::Context *context ) const
{
	ScenePath source;
	sourcePath( branchPath, source );
	Imath::M44f result = inPlug()->transform( source );
	if( branchPath.size() == 1 )
	{
		const Imath::M44f matrix = transformPlug()->matrix();
		ConstInternedStringVectorDataPtr childNamesData = childNamesPlug()->getValue();
		const vector<InternedString> &childNames = childNamesData->readable();

		size_t i = 0;
		do
		{
			result = result * matrix;
		} while( i < childNames.size() && branchPath[0] != childNames[i++] );
	}
	return result;
}

void Duplicate::hashBranchAttributes( const ScenePath &parentPath, const ScenePath &branchPath, const Gaffer::Context *context, IECore::MurmurHash &h ) const
{
	ScenePath source;
	sourcePath( branchPath, source );
	h = inPlug()->attributesHash( source );
}

IECore::ConstCompoundObjectPtr Duplicate::computeBranchAttributes( const ScenePath &parentPath, const ScenePath &branchPath, const Gaffer::Context *context ) const
{
	ScenePath source;
	sourcePath( branchPath, source );
	return inPlug()->attributes( source );
}

void Duplicate::hashBranchObject( const ScenePath &parentPath, const ScenePath &branchPath, const Gaffer::Context *context, IECore::MurmurHash &h ) const
{
	ScenePath source;
	sourcePath( branchPath, source );
	h = inPlug()->objectHash( source );
}

IECore::ConstObjectPtr Duplicate::computeBranchObject( const ScenePath &parentPath, const ScenePath &branchPath, const Gaffer::Context *context ) const
{
	ScenePath source;
	sourcePath( branchPath, source );
	return inPlug()->object( source );
}

void Duplicate::hashBranchChildNames( const ScenePath &parentPath, const ScenePath &branchPath, const Gaffer::Context *context, IECore::MurmurHash &h ) const
{
	if( branchPath.size() == 0 )
	{
		h = childNamesPlug()->hash();
	}
	else
	{
		ScenePath source;
		sourcePath( branchPath, source );
		h = inPlug()->childNamesHash( source );
	}
}

IECore::ConstInternedStringVectorDataPtr Duplicate::computeBranchChildNames( const ScenePath &parentPath, const ScenePath &branchPath, const Gaffer::Context *context ) const
{
	if( branchPath.size() == 0 )
	{
		return childNamesPlug()->getValue();
	}
	else
	{
		ScenePath source;
		sourcePath( branchPath, source );
		return inPlug()->childNames( source );
	}
}

void Duplicate::hashBranchSet( const ScenePath &parentPath, const IECore::InternedString &setName, const Gaffer::Context *context, IECore::MurmurHash &h ) const
{
	h.append( inPlug()->setHash( setName ) );
	targetPlug()->hash( h );
	childNamesPlug()->hash( h );
}

IECore::ConstPathMatcherDataPtr Duplicate::computeBranchSet( const ScenePath &parentPath, const IECore::InternedString &setName, const Gaffer::Context *context ) const
{
	ConstPathMatcherDataPtr inputSetData = inPlug()->set( setName );
	const PathMatcher &inputSet = inputSetData->readable();
	if( inputSet.isEmpty() )
	{
		return outPlug()->setPlug()->defaultValue();
	}

	PathMatcher subTree = inputSet.subTree( targetPlug()->getValue() );
	if( subTree.isEmpty() )
	{
		return outPlug()->setPlug()->defaultValue();
	}

	ConstInternedStringVectorDataPtr childNamesData = childNamesPlug()->getValue();
	const vector<InternedString> &childNames = childNamesData->readable();

	PathMatcherDataPtr resultData = new PathMatcherData;
	PathMatcher &result = resultData->writable();
	ScenePath prefix( 1 );
	for( vector<InternedString>::const_iterator it = childNames.begin(), eIt = childNames.end(); it != eIt; ++it )
	{
		prefix.back() = *it;
		result.addPaths( subTree, prefix );
	}

	return resultData;
}

void Duplicate::sourcePath( const ScenePath &branchPath, ScenePath &source ) const
{
	assert( branchPath.size() );
	ScenePlug::stringToPath( targetPlug()->getValue(), source );
	copy( ++branchPath.begin(), branchPath.end(), back_inserter( source ) );
}
<|endoftext|>


<commit_before>/***************************************************************************
 *   Copyright (c) 2015 FreeCAD Developers                                 *
 *   Author: Przemo Firszt <przemo@firszt.eu>                              *
 *   Based on DlgToolbars.cpp file                                         *
 *                                                                         *
 *   This file is part of the FreeCAD CAx development system.              *
 *                                                                         *
 *   This library is free software; you can redistribute it and/or         *
 *   modify it under the terms of the GNU Library General Public           *
 *   License as published by the Free Software Foundation; either          *
 *   version 2 of the License, or (at your option) any later version.      *
 *                                                                         *
 *   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 Library General Public License for more details.                  *
 *                                                                         *
 *   You should have received a copy of the GNU Library General Public     *
 *   License along with this library; see the file COPYING.LIB. If not,    *
 *   write to the Free Software Foundation, Inc., 59 Temple Place,         *
 *   Suite 330, Boston, MA  02111-1307, USA                                *
 *                                                                         *
 ***************************************************************************/

#include "PreCompiled.h"
#ifndef _PreComp_
# include <QInputDialog>
#endif

#include "DlgWorkbenchesImp.h"
#include "Application.h"
#include "BitmapFactory.h"
#include "Command.h"
#include "MainWindow.h"
#include "Widgets.h"
#include "Workbench.h"
#include "WorkbenchManager.h"
#include "QListWidgetCustom.h"

using namespace Gui::Dialog;

const QString DlgWorkbenchesImp::all_workbenches = QString::fromAscii("ALL");

/* TRANSLATOR Gui::Dialog::DlgWorkbenches */

DlgWorkbenchesImp::DlgWorkbenchesImp(QWidget* parent)
    : CustomizeActionPage(parent)
{
    this->setupUi(this);
    set_lw_properties(lw_enabled_workbenches);
    set_lw_properties(lw_disabled_workbenches);
    const QString lw_disabled_name = QString::fromAscii("disabled workbenches");
    lw_disabled_workbenches->setAccessibleName(lw_disabled_name);
    lw_disabled_workbenches->setSortingEnabled(true);

    QStringList enabled_wbs_list = load_enabled_workbenches();
    QStringList workbenches = Application::Instance->workbenches();

    for (QStringList::Iterator it = enabled_wbs_list.begin(); it != enabled_wbs_list.end(); ++it) {
        if (workbenches.contains(*it)){
            QString wb = *it;
            add_workbench(lw_enabled_workbenches, wb);
        } else {
            qDebug("Ignoring unknown " + it->toLatin1() + " workbench found in user preferences.");
        }
    }
    for (QStringList::Iterator it = workbenches.begin(); it != workbenches.end(); ++it) {
        if (!enabled_wbs_list.contains(*it)){
            QString wb = *it;
            add_workbench(lw_disabled_workbenches, wb);
        }
    }
    lw_enabled_workbenches->setCurrentRow(0);
    lw_disabled_workbenches->setCurrentRow(0);
}

/** Destroys the object and frees any allocated resources */
DlgWorkbenchesImp::~DlgWorkbenchesImp()
{
}

void DlgWorkbenchesImp::set_lw_properties(QListWidgetCustom *lw)
{
    lw->setDragDropMode(QAbstractItemView::DragDrop);
    lw->setSelectionMode(QAbstractItemView::SingleSelection);
    lw->viewport()->setAcceptDrops(true);
    lw->setDropIndicatorShown(true);
    lw->setDragEnabled(true);
    lw->setDefaultDropAction(Qt::MoveAction);
}

void DlgWorkbenchesImp::add_workbench(QListWidgetCustom *lw, QString it)
{
    QPixmap px = Application::Instance->workbenchIcon(it);
    QString mt = Application::Instance->workbenchMenuText(it);
    QListWidgetItem *wi = (new QListWidgetItem(QIcon(px), mt));
    wi->setData(Qt::UserRole, QVariant(it));
    lw->addItem(wi);
}

void DlgWorkbenchesImp::changeEvent(QEvent *e)
{
    if (e->type() == QEvent::LanguageChange) {
        retranslateUi(this);
    }
    else {
        QWidget::changeEvent(e);
    }
}

void DlgWorkbenchesImp::hideEvent(QHideEvent * event)
{
    save_enabled_workbenches();
}

void DlgWorkbenchesImp::onAddMacroAction(const QByteArray& macro)
{
}

void DlgWorkbenchesImp::onRemoveMacroAction(const QByteArray& macro)
{
}

void DlgWorkbenchesImp::onModifyMacroAction(const QByteArray& macro)
{
}

void DlgWorkbenchesImp::move_workbench(QListWidgetCustom *lwc_dest,
                                       QListWidgetItem *wi)
{
    QListWidgetItem* item = wi->clone();
    lwc_dest->addItem(item);
    lwc_dest->setCurrentItem(item);
    delete wi;
}

void DlgWorkbenchesImp::on_add_to_enabled_workbenches_btn_clicked()
{
    QListWidgetItem* ci = lw_disabled_workbenches->currentItem();
    if (ci) {
	move_workbench(lw_enabled_workbenches, ci);
    }
}

void DlgWorkbenchesImp::on_remove_from_enabled_workbenches_btn_clicked()
{
    QListWidgetItem* ci = lw_enabled_workbenches->currentItem();
    if (ci) {
	move_workbench(lw_disabled_workbenches, ci);
    }
}

void DlgWorkbenchesImp::shift_workbench(bool up)
{
    int direction;
    if (up){
        direction = -1;
    } else {
        direction = 1;
    }
    if (lw_enabled_workbenches->currentItem()) {
        int index = lw_enabled_workbenches->currentRow();
        QListWidgetItem *item = lw_enabled_workbenches->takeItem(index);
        lw_enabled_workbenches->insertItem(index + direction, item);
        lw_enabled_workbenches->setCurrentRow(index + direction);
    }
}

void DlgWorkbenchesImp::on_shift_workbench_up_btn_clicked()
{
    shift_workbench(true);
}

void DlgWorkbenchesImp::on_shift_workbench_down_btn_clicked()
{
    shift_workbench(false);
}

void DlgWorkbenchesImp::on_sort_enabled_workbenches_btn_clicked()
{
    lw_enabled_workbenches->sortItems();
}

void DlgWorkbenchesImp::on_add_all_to_enabled_workbenches_btn_clicked()
{
    while (lw_disabled_workbenches->count() > 0) {
        QListWidgetItem* item = lw_disabled_workbenches->item(0);
        move_workbench(lw_enabled_workbenches, item);
    }
}

QStringList DlgWorkbenchesImp::load_enabled_workbenches()
{
    QString enabled_wbs;
    QStringList enabled_wbs_list;
    ParameterGrp::handle hGrp;

    hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Workbenches");
    enabled_wbs = QString::fromStdString(hGrp->GetASCII("Enabled", all_workbenches.toStdString().c_str()).c_str());
    enabled_wbs_list = enabled_wbs.split(QLatin1String(","), QString::SkipEmptyParts);

    if (enabled_wbs_list.at(0) == all_workbenches) {
        enabled_wbs_list.removeFirst();
        QStringList workbenches = Application::Instance->workbenches();
        for (QStringList::Iterator it = workbenches.begin(); it != workbenches.end(); ++it) {
            enabled_wbs_list.append(*it);
        }
        enabled_wbs_list.sort();
    }
    return enabled_wbs_list;
}

void DlgWorkbenchesImp::save_enabled_workbenches()
{
    QString enabled_wbs;
    ParameterGrp::handle hGrp;

    hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Workbenches");
    hGrp->Clear();

    if (lw_enabled_workbenches->count() == 0) {
        enabled_wbs.append(QString::fromAscii("NoneWorkbench"));
    } else {
        for (int i = 0; i < lw_enabled_workbenches->count(); i++) {
            QVariant item_data = lw_enabled_workbenches->item(i)->data(Qt::UserRole);
            QString name = item_data.toString();
            enabled_wbs.append(name + QString::fromAscii(","));
        }
    }
    hGrp->SetASCII("Enabled", enabled_wbs.toAscii());
}

#include "moc_DlgWorkbenchesImp.cpp"
<commit_msg>+ fix minor bug in string concatenation<commit_after>/***************************************************************************
 *   Copyright (c) 2015 FreeCAD Developers                                 *
 *   Author: Przemo Firszt <przemo@firszt.eu>                              *
 *   Based on DlgToolbars.cpp file                                         *
 *                                                                         *
 *   This file is part of the FreeCAD CAx development system.              *
 *                                                                         *
 *   This library is free software; you can redistribute it and/or         *
 *   modify it under the terms of the GNU Library General Public           *
 *   License as published by the Free Software Foundation; either          *
 *   version 2 of the License, or (at your option) any later version.      *
 *                                                                         *
 *   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 Library General Public License for more details.                  *
 *                                                                         *
 *   You should have received a copy of the GNU Library General Public     *
 *   License along with this library; see the file COPYING.LIB. If not,    *
 *   write to the Free Software Foundation, Inc., 59 Temple Place,         *
 *   Suite 330, Boston, MA  02111-1307, USA                                *
 *                                                                         *
 ***************************************************************************/

#include "PreCompiled.h"
#ifndef _PreComp_
# include <QInputDialog>
#endif

#include "DlgWorkbenchesImp.h"
#include "Application.h"
#include "BitmapFactory.h"
#include "Command.h"
#include "MainWindow.h"
#include "Widgets.h"
#include "Workbench.h"
#include "WorkbenchManager.h"
#include "QListWidgetCustom.h"

using namespace Gui::Dialog;

const QString DlgWorkbenchesImp::all_workbenches = QString::fromAscii("ALL");

/* TRANSLATOR Gui::Dialog::DlgWorkbenches */

DlgWorkbenchesImp::DlgWorkbenchesImp(QWidget* parent)
    : CustomizeActionPage(parent)
{
    this->setupUi(this);
    set_lw_properties(lw_enabled_workbenches);
    set_lw_properties(lw_disabled_workbenches);
    const QString lw_disabled_name = QString::fromAscii("disabled workbenches");
    lw_disabled_workbenches->setAccessibleName(lw_disabled_name);
    lw_disabled_workbenches->setSortingEnabled(true);

    QStringList enabled_wbs_list = load_enabled_workbenches();
    QStringList workbenches = Application::Instance->workbenches();

    for (QStringList::Iterator it = enabled_wbs_list.begin(); it != enabled_wbs_list.end(); ++it) {
        if (workbenches.contains(*it)) {
            QString wb = *it;
            add_workbench(lw_enabled_workbenches, wb);
        } else {
            qDebug() << "Ignoring unknown " << it->toLatin1() << " workbench found in user preferences.";
        }
    }
    for (QStringList::Iterator it = workbenches.begin(); it != workbenches.end(); ++it) {
        if (!enabled_wbs_list.contains(*it)){
            QString wb = *it;
            add_workbench(lw_disabled_workbenches, wb);
        }
    }
    lw_enabled_workbenches->setCurrentRow(0);
    lw_disabled_workbenches->setCurrentRow(0);
}

/** Destroys the object and frees any allocated resources */
DlgWorkbenchesImp::~DlgWorkbenchesImp()
{
}

void DlgWorkbenchesImp::set_lw_properties(QListWidgetCustom *lw)
{
    lw->setDragDropMode(QAbstractItemView::DragDrop);
    lw->setSelectionMode(QAbstractItemView::SingleSelection);
    lw->viewport()->setAcceptDrops(true);
    lw->setDropIndicatorShown(true);
    lw->setDragEnabled(true);
    lw->setDefaultDropAction(Qt::MoveAction);
}

void DlgWorkbenchesImp::add_workbench(QListWidgetCustom *lw, QString it)
{
    QPixmap px = Application::Instance->workbenchIcon(it);
    QString mt = Application::Instance->workbenchMenuText(it);
    QListWidgetItem *wi = (new QListWidgetItem(QIcon(px), mt));
    wi->setData(Qt::UserRole, QVariant(it));
    lw->addItem(wi);
}

void DlgWorkbenchesImp::changeEvent(QEvent *e)
{
    if (e->type() == QEvent::LanguageChange) {
        retranslateUi(this);
    }
    else {
        QWidget::changeEvent(e);
    }
}

void DlgWorkbenchesImp::hideEvent(QHideEvent * event)
{
    save_enabled_workbenches();
}

void DlgWorkbenchesImp::onAddMacroAction(const QByteArray& macro)
{
}

void DlgWorkbenchesImp::onRemoveMacroAction(const QByteArray& macro)
{
}

void DlgWorkbenchesImp::onModifyMacroAction(const QByteArray& macro)
{
}

void DlgWorkbenchesImp::move_workbench(QListWidgetCustom *lwc_dest,
                                       QListWidgetItem *wi)
{
    QListWidgetItem* item = wi->clone();
    lwc_dest->addItem(item);
    lwc_dest->setCurrentItem(item);
    delete wi;
}

void DlgWorkbenchesImp::on_add_to_enabled_workbenches_btn_clicked()
{
    QListWidgetItem* ci = lw_disabled_workbenches->currentItem();
    if (ci) {
	move_workbench(lw_enabled_workbenches, ci);
    }
}

void DlgWorkbenchesImp::on_remove_from_enabled_workbenches_btn_clicked()
{
    QListWidgetItem* ci = lw_enabled_workbenches->currentItem();
    if (ci) {
	move_workbench(lw_disabled_workbenches, ci);
    }
}

void DlgWorkbenchesImp::shift_workbench(bool up)
{
    int direction;
    if (up){
        direction = -1;
    } else {
        direction = 1;
    }
    if (lw_enabled_workbenches->currentItem()) {
        int index = lw_enabled_workbenches->currentRow();
        QListWidgetItem *item = lw_enabled_workbenches->takeItem(index);
        lw_enabled_workbenches->insertItem(index + direction, item);
        lw_enabled_workbenches->setCurrentRow(index + direction);
    }
}

void DlgWorkbenchesImp::on_shift_workbench_up_btn_clicked()
{
    shift_workbench(true);
}

void DlgWorkbenchesImp::on_shift_workbench_down_btn_clicked()
{
    shift_workbench(false);
}

void DlgWorkbenchesImp::on_sort_enabled_workbenches_btn_clicked()
{
    lw_enabled_workbenches->sortItems();
}

void DlgWorkbenchesImp::on_add_all_to_enabled_workbenches_btn_clicked()
{
    while (lw_disabled_workbenches->count() > 0) {
        QListWidgetItem* item = lw_disabled_workbenches->item(0);
        move_workbench(lw_enabled_workbenches, item);
    }
}

QStringList DlgWorkbenchesImp::load_enabled_workbenches()
{
    QString enabled_wbs;
    QStringList enabled_wbs_list;
    ParameterGrp::handle hGrp;

    hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Workbenches");
    enabled_wbs = QString::fromStdString(hGrp->GetASCII("Enabled", all_workbenches.toStdString().c_str()).c_str());
    enabled_wbs_list = enabled_wbs.split(QLatin1String(","), QString::SkipEmptyParts);

    if (enabled_wbs_list.at(0) == all_workbenches) {
        enabled_wbs_list.removeFirst();
        QStringList workbenches = Application::Instance->workbenches();
        for (QStringList::Iterator it = workbenches.begin(); it != workbenches.end(); ++it) {
            enabled_wbs_list.append(*it);
        }
        enabled_wbs_list.sort();
    }
    return enabled_wbs_list;
}

void DlgWorkbenchesImp::save_enabled_workbenches()
{
    QString enabled_wbs;
    ParameterGrp::handle hGrp;

    hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Workbenches");
    hGrp->Clear();

    if (lw_enabled_workbenches->count() == 0) {
        enabled_wbs.append(QString::fromAscii("NoneWorkbench"));
    } else {
        for (int i = 0; i < lw_enabled_workbenches->count(); i++) {
            QVariant item_data = lw_enabled_workbenches->item(i)->data(Qt::UserRole);
            QString name = item_data.toString();
            enabled_wbs.append(name + QString::fromAscii(","));
        }
    }
    hGrp->SetASCII("Enabled", enabled_wbs.toAscii());
}

#include "moc_DlgWorkbenchesImp.cpp"

<|endoftext|>


<commit_before>// Copyright Daniel Wallin 2006. Use, modification and distribution is
// subject to the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

#include <libtorrent/torrent_handle.hpp>
#include <boost/python.hpp>
#include <boost/lexical_cast.hpp>
#include "gil.hpp"

using namespace boost::python;
using namespace libtorrent;

namespace
{

  list url_seeds(torrent_handle& handle)
  {
      list ret;
      std::set<std::string> urls;
      {
          allow_threading_guard guard;
          urls = handle.url_seeds();
      }

      for (std::set<std::string>::iterator i(urls.begin())
          , end(urls.end()); i != end; ++i)
          ret.append(*i);
      return ret;
  }

  list piece_availability(torrent_handle& handle)
  {
      list ret;
      std::vector<int> avail;
      {
          allow_threading_guard guard;
          handle.piece_availability(avail);
      }

      for (std::vector<int>::iterator i(avail.begin())
          , end(avail.end()); i != end; ++i)
          ret.append(*i);
      return ret;
  }

  list piece_priorities(torrent_handle& handle)
  {
      list ret;
      std::vector<int> prio;
      {
          allow_threading_guard guard;
          prio = handle.piece_priorities();
      }

      for (std::vector<int>::iterator i(prio.begin())
          , end(prio.end()); i != end; ++i)
          ret.append(*i);
      return ret;
  }

  std::vector<announce_entry>::const_iterator begin_trackers(torrent_handle& i)
  {
      allow_threading_guard guard;
      return i.trackers().begin();
  }

  std::vector<announce_entry>::const_iterator end_trackers(torrent_handle& i)
  {
      allow_threading_guard guard;
      return i.trackers().end();
  }

} // namespace unnamed

list file_progress(torrent_handle& handle)
{
    std::vector<float> p;

    {
        allow_threading_guard guard;
        p.reserve(handle.get_torrent_info().num_files());
        handle.file_progress(p);
    }

    list result;

    for (std::vector<float>::iterator i(p.begin()), e(p.end()); i != e; ++i)
        result.append(*i);

    return result;
}

list get_peer_info(torrent_handle const& handle)
{
    std::vector<peer_info> pi;

    {
        allow_threading_guard guard;
        handle.get_peer_info(pi);
    }

    list result;

    for (std::vector<peer_info>::iterator i = pi.begin(); i != pi.end(); ++i)
        result.append(*i);

    return result;
}

void prioritize_pieces(torrent_handle& info, object o)
{
   std::vector<int> result;
   try
   {
      object iter_obj = object( handle<>( PyObject_GetIter( o.ptr() ) ));
      while( 1 )
      {
         object obj = extract<object>( iter_obj.attr( "next" )() );
         result.push_back(extract<int const>( obj ));
      }
   }
   catch( error_already_set )
   {
      PyErr_Clear();
      info.prioritize_pieces(result);
      return;
   }
}

void prioritize_files(torrent_handle& info, object o)
{
   std::vector<int> result;
   try
   {
      object iter_obj = object( handle<>( PyObject_GetIter( o.ptr() ) ));
      while( 1 )
      {
         object obj = extract<object>( iter_obj.attr( "next" )() );
         result.push_back(extract<int const>( obj ));
      }
   }
   catch( error_already_set )
   {
      PyErr_Clear();
      info.prioritize_files(result);
      return;
   }
}

void replace_trackers(torrent_handle& info, object trackers)
{
    object iter(trackers.attr("__iter__")());

    std::vector<announce_entry> result;

    for (;;)
    {
        handle<> entry(allow_null(PyIter_Next(iter.ptr())));

        if (entry == handle<>())
            break;

        result.push_back(extract<announce_entry const&>(object(entry)));
    }

    allow_threading_guard guard;
    info.replace_trackers(result);
}

list get_download_queue(torrent_handle& handle)
{
    list ret;

    std::vector<partial_piece_info> downloading;

    {
        allow_threading_guard guard;
        handle.get_download_queue(downloading);
    }

    for (std::vector<partial_piece_info>::iterator i = downloading.begin()
        , end(downloading.end()); i != end; ++i)
    {
        dict partial_piece;
        partial_piece["piece_index"] = i->piece_index;
        partial_piece["blocks_in_piece"] = i->blocks_in_piece;
        list block_list;
        for (int k = 0; k < i->blocks_in_piece; ++k)
        {
            dict block_info;
            block_info["state"] = i->blocks[k].state;
            block_info["num_peers"] = i->blocks[k].num_peers;
            block_info["bytes_progress"] = i->blocks[k].bytes_progress;
            block_info["block_size"] = i->blocks[k].block_size;
            block_info["peer"] = std::make_pair(
                boost::lexical_cast<std::string>(i->blocks[k].peer.address()), i->blocks[k].peer.port());
            block_list.append(block_info);
        }
        partial_piece["blocks"] = block_list;

        ret.append(partial_piece);
    }

    return ret;
}

namespace
{
    tcp::endpoint tuple_to_endpoint(tuple const& t)
    {
        return tcp::endpoint(address::from_string(extract<std::string>(t[0])), extract<int>(t[1]));
    }
}

void force_reannounce(torrent_handle& th, int s)
{
    th.force_reannounce(boost::posix_time::seconds(s));
}

void connect_peer(torrent_handle& th, tuple ip, int source)
{
    th.connect_peer(tuple_to_endpoint(ip), source);
}

void set_peer_upload_limit(torrent_handle& th, tuple const& ip, int limit)
{
    th.set_peer_upload_limit(tuple_to_endpoint(ip), limit);
}

void set_peer_download_limit(torrent_handle& th, tuple const& ip, int limit)
{
    th.set_peer_download_limit(tuple_to_endpoint(ip), limit);
}

void bind_torrent_handle()
{
    void (torrent_handle::*force_reannounce0)() const = &torrent_handle::force_reannounce;

    int (torrent_handle::*piece_priority0)(int) const = &torrent_handle::piece_priority;
    void (torrent_handle::*piece_priority1)(int, int) const = &torrent_handle::piece_priority;

#ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES
    bool (torrent_handle::*resolve_countries0)() const = &torrent_handle::resolve_countries;
    void (torrent_handle::*resolve_countries1)(bool) = &torrent_handle::resolve_countries;
#endif

    return_value_policy<copy_const_reference> copy;

#define _ allow_threads

    class_<torrent_handle>("torrent_handle")
        .def("get_peer_info", get_peer_info)
        .def("status", _(&torrent_handle::status))
        .def("get_download_queue", get_download_queue)
        .def("file_progress", file_progress)
        .def("trackers", range(begin_trackers, end_trackers))
        .def("replace_trackers", replace_trackers)
        .def("add_url_seed", _(&torrent_handle::add_url_seed))
        .def("remove_url_seed", _(&torrent_handle::remove_url_seed))
        .def("url_seeds", url_seeds)
        .def("has_metadata", _(&torrent_handle::has_metadata))
        .def("get_torrent_info", _(&torrent_handle::get_torrent_info), return_internal_reference<>())
        .def("is_valid", _(&torrent_handle::is_valid))
        .def("is_seed", _(&torrent_handle::is_seed))
        .def("is_finished", _(&torrent_handle::is_finished))
        .def("is_paused", _(&torrent_handle::is_paused))
        .def("pause", _(&torrent_handle::pause))
        .def("resume", _(&torrent_handle::resume))

        .def_readonly("is_auto_managed", _(&torrent_handle::is_auto_managed))
        .def("auto_managed", _(&torrent_handle::auto_managed))
        .def_readonly("queue_position", _(&torrent_handle::queue_position))
        .def("queue_position_up", _(&torrent_handle::queue_position_up))
        .def("queue_position_down", _(&torrent_handle::queue_position_down))
        .def("queue_position_top", _(&torrent_handle::queue_position_top))
        .def("queue_position_bottom", _(&torrent_handle::queue_position_bottom))

#ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES
        .def("resolve_countries", _(resolve_countries0))
        .def("resolve_countries", _(resolve_countries1))
#endif
        // deprecated
        .def("filter_piece", _(&torrent_handle::filter_piece))
        .def("is_piece_filtered", _(&torrent_handle::is_piece_filtered))

        .def("piece_availability", piece_availability)
        .def("piece_priority", _(piece_priority0))
        .def("piece_priority", _(piece_priority1))
        .def("prioritize_pieces", prioritize_pieces)
        .def("piece_prioritize", piece_priorities)
        .def("prioritize_files", prioritize_files)
        .def("use_interface", &torrent_handle::use_interface)
        .def("write_resume_data", _(&torrent_handle::write_resume_data))
        .def("save_resume_data", _(&torrent_handle::save_resume_data))
        .def("force_reannounce", _(force_reannounce0))
        .def("force_reannounce", force_reannounce)
        .def("scrape_tracker", _(&torrent_handle::scrape_tracker))
        .def("name", _(&torrent_handle::name))
        .def("set_upload_limit", _(&torrent_handle::set_upload_limit))
        .def("upload_limit", _(&torrent_handle::upload_limit))
        .def("set_download_limit", _(&torrent_handle::set_download_limit))
        .def("download_limit", _(&torrent_handle::download_limit))
        .def("set_sequential_download", _(&torrent_handle::set_sequential_download))
        .def("set_peer_upload_limit", set_peer_upload_limit)
        .def("set_peer_download_limit", set_peer_download_limit)
        .def("connect_peer", connect_peer)
        .def("set_ratio", _(&torrent_handle::set_ratio))
        .def("save_path", _(&torrent_handle::save_path))
        .def("set_max_uploads", _(&torrent_handle::set_max_uploads))
        .def("set_max_connections", _(&torrent_handle::set_max_connections))
        .def("set_tracker_login", _(&torrent_handle::set_tracker_login))
        .def("move_storage", _(&torrent_handle::move_storage))
        .def("info_hash", _(&torrent_handle::info_hash))
        ;
}

<commit_msg>Fix issue with is_auto_managed and queue_position<commit_after>// Copyright Daniel Wallin 2006. Use, modification and distribution is
// subject to the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

#include <libtorrent/torrent_handle.hpp>
#include <boost/python.hpp>
#include <boost/lexical_cast.hpp>
#include "gil.hpp"

using namespace boost::python;
using namespace libtorrent;

namespace
{

  list url_seeds(torrent_handle& handle)
  {
      list ret;
      std::set<std::string> urls;
      {
          allow_threading_guard guard;
          urls = handle.url_seeds();
      }

      for (std::set<std::string>::iterator i(urls.begin())
          , end(urls.end()); i != end; ++i)
          ret.append(*i);
      return ret;
  }

  list piece_availability(torrent_handle& handle)
  {
      list ret;
      std::vector<int> avail;
      {
          allow_threading_guard guard;
          handle.piece_availability(avail);
      }

      for (std::vector<int>::iterator i(avail.begin())
          , end(avail.end()); i != end; ++i)
          ret.append(*i);
      return ret;
  }

  list piece_priorities(torrent_handle& handle)
  {
      list ret;
      std::vector<int> prio;
      {
          allow_threading_guard guard;
          prio = handle.piece_priorities();
      }

      for (std::vector<int>::iterator i(prio.begin())
          , end(prio.end()); i != end; ++i)
          ret.append(*i);
      return ret;
  }

  std::vector<announce_entry>::const_iterator begin_trackers(torrent_handle& i)
  {
      allow_threading_guard guard;
      return i.trackers().begin();
  }

  std::vector<announce_entry>::const_iterator end_trackers(torrent_handle& i)
  {
      allow_threading_guard guard;
      return i.trackers().end();
  }

} // namespace unnamed

list file_progress(torrent_handle& handle)
{
    std::vector<float> p;

    {
        allow_threading_guard guard;
        p.reserve(handle.get_torrent_info().num_files());
        handle.file_progress(p);
    }

    list result;

    for (std::vector<float>::iterator i(p.begin()), e(p.end()); i != e; ++i)
        result.append(*i);

    return result;
}

list get_peer_info(torrent_handle const& handle)
{
    std::vector<peer_info> pi;

    {
        allow_threading_guard guard;
        handle.get_peer_info(pi);
    }

    list result;

    for (std::vector<peer_info>::iterator i = pi.begin(); i != pi.end(); ++i)
        result.append(*i);

    return result;
}

void prioritize_pieces(torrent_handle& info, object o)
{
   std::vector<int> result;
   try
   {
      object iter_obj = object( handle<>( PyObject_GetIter( o.ptr() ) ));
      while( 1 )
      {
         object obj = extract<object>( iter_obj.attr( "next" )() );
         result.push_back(extract<int const>( obj ));
      }
   }
   catch( error_already_set )
   {
      PyErr_Clear();
      info.prioritize_pieces(result);
      return;
   }
}

void prioritize_files(torrent_handle& info, object o)
{
   std::vector<int> result;
   try
   {
      object iter_obj = object( handle<>( PyObject_GetIter( o.ptr() ) ));
      while( 1 )
      {
         object obj = extract<object>( iter_obj.attr( "next" )() );
         result.push_back(extract<int const>( obj ));
      }
   }
   catch( error_already_set )
   {
      PyErr_Clear();
      info.prioritize_files(result);
      return;
   }
}

void replace_trackers(torrent_handle& info, object trackers)
{
    object iter(trackers.attr("__iter__")());

    std::vector<announce_entry> result;

    for (;;)
    {
        handle<> entry(allow_null(PyIter_Next(iter.ptr())));

        if (entry == handle<>())
            break;

        result.push_back(extract<announce_entry const&>(object(entry)));
    }

    allow_threading_guard guard;
    info.replace_trackers(result);
}

list get_download_queue(torrent_handle& handle)
{
    list ret;

    std::vector<partial_piece_info> downloading;

    {
        allow_threading_guard guard;
        handle.get_download_queue(downloading);
    }

    for (std::vector<partial_piece_info>::iterator i = downloading.begin()
        , end(downloading.end()); i != end; ++i)
    {
        dict partial_piece;
        partial_piece["piece_index"] = i->piece_index;
        partial_piece["blocks_in_piece"] = i->blocks_in_piece;
        list block_list;
        for (int k = 0; k < i->blocks_in_piece; ++k)
        {
            dict block_info;
            block_info["state"] = i->blocks[k].state;
            block_info["num_peers"] = i->blocks[k].num_peers;
            block_info["bytes_progress"] = i->blocks[k].bytes_progress;
            block_info["block_size"] = i->blocks[k].block_size;
            block_info["peer"] = std::make_pair(
                boost::lexical_cast<std::string>(i->blocks[k].peer.address()), i->blocks[k].peer.port());
            block_list.append(block_info);
        }
        partial_piece["blocks"] = block_list;

        ret.append(partial_piece);
    }

    return ret;
}

namespace
{
    tcp::endpoint tuple_to_endpoint(tuple const& t)
    {
        return tcp::endpoint(address::from_string(extract<std::string>(t[0])), extract<int>(t[1]));
    }
}

void force_reannounce(torrent_handle& th, int s)
{
    th.force_reannounce(boost::posix_time::seconds(s));
}

void connect_peer(torrent_handle& th, tuple ip, int source)
{
    th.connect_peer(tuple_to_endpoint(ip), source);
}

void set_peer_upload_limit(torrent_handle& th, tuple const& ip, int limit)
{
    th.set_peer_upload_limit(tuple_to_endpoint(ip), limit);
}

void set_peer_download_limit(torrent_handle& th, tuple const& ip, int limit)
{
    th.set_peer_download_limit(tuple_to_endpoint(ip), limit);
}

void bind_torrent_handle()
{
    void (torrent_handle::*force_reannounce0)() const = &torrent_handle::force_reannounce;

    int (torrent_handle::*piece_priority0)(int) const = &torrent_handle::piece_priority;
    void (torrent_handle::*piece_priority1)(int, int) const = &torrent_handle::piece_priority;

#ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES
    bool (torrent_handle::*resolve_countries0)() const = &torrent_handle::resolve_countries;
    void (torrent_handle::*resolve_countries1)(bool) = &torrent_handle::resolve_countries;
#endif

    return_value_policy<copy_const_reference> copy;

#define _ allow_threads

    class_<torrent_handle>("torrent_handle")
        .def("get_peer_info", get_peer_info)
        .def("status", _(&torrent_handle::status))
        .def("get_download_queue", get_download_queue)
        .def("file_progress", file_progress)
        .def("trackers", range(begin_trackers, end_trackers))
        .def("replace_trackers", replace_trackers)
        .def("add_url_seed", _(&torrent_handle::add_url_seed))
        .def("remove_url_seed", _(&torrent_handle::remove_url_seed))
        .def("url_seeds", url_seeds)
        .def("has_metadata", _(&torrent_handle::has_metadata))
        .def("get_torrent_info", _(&torrent_handle::get_torrent_info), return_internal_reference<>())
        .def("is_valid", _(&torrent_handle::is_valid))
        .def("is_seed", _(&torrent_handle::is_seed))
        .def("is_finished", _(&torrent_handle::is_finished))
        .def("is_paused", _(&torrent_handle::is_paused))
        .def("pause", _(&torrent_handle::pause))
        .def("resume", _(&torrent_handle::resume))

        .def("is_auto_managed", _(&torrent_handle::is_auto_managed))
        .def("auto_managed", _(&torrent_handle::auto_managed))
        .def("queue_position", _(&torrent_handle::queue_position))
        .def("queue_position_up", _(&torrent_handle::queue_position_up))
        .def("queue_position_down", _(&torrent_handle::queue_position_down))
        .def("queue_position_top", _(&torrent_handle::queue_position_top))
        .def("queue_position_bottom", _(&torrent_handle::queue_position_bottom))

#ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES
        .def("resolve_countries", _(resolve_countries0))
        .def("resolve_countries", _(resolve_countries1))
#endif
        // deprecated
        .def("filter_piece", _(&torrent_handle::filter_piece))
        .def("is_piece_filtered", _(&torrent_handle::is_piece_filtered))

        .def("piece_availability", piece_availability)
        .def("piece_priority", _(piece_priority0))
        .def("piece_priority", _(piece_priority1))
        .def("prioritize_pieces", prioritize_pieces)
        .def("piece_prioritize", piece_priorities)
        .def("prioritize_files", prioritize_files)
        .def("use_interface", &torrent_handle::use_interface)
        .def("write_resume_data", _(&torrent_handle::write_resume_data))
        .def("save_resume_data", _(&torrent_handle::save_resume_data))
        .def("force_reannounce", _(force_reannounce0))
        .def("force_reannounce", force_reannounce)
        .def("scrape_tracker", _(&torrent_handle::scrape_tracker))
        .def("name", _(&torrent_handle::name))
        .def("set_upload_limit", _(&torrent_handle::set_upload_limit))
        .def("upload_limit", _(&torrent_handle::upload_limit))
        .def("set_download_limit", _(&torrent_handle::set_download_limit))
        .def("download_limit", _(&torrent_handle::download_limit))
        .def("set_sequential_download", _(&torrent_handle::set_sequential_download))
        .def("set_peer_upload_limit", set_peer_upload_limit)
        .def("set_peer_download_limit", set_peer_download_limit)
        .def("connect_peer", connect_peer)
        .def("set_ratio", _(&torrent_handle::set_ratio))
        .def("save_path", _(&torrent_handle::save_path))
        .def("set_max_uploads", _(&torrent_handle::set_max_uploads))
        .def("set_max_connections", _(&torrent_handle::set_max_connections))
        .def("set_tracker_login", _(&torrent_handle::set_tracker_login))
        .def("move_storage", _(&torrent_handle::move_storage))
        .def("info_hash", _(&torrent_handle::info_hash))
        ;
}

<|endoftext|>