<commit_before>#include <iostream>
#include <cstring>

#include <openssl/md5.h>
#include <openssl/sha.h>

#include "args.hh"
#include "hash.hh"
#include "archive.hh"
#include "util.hh"
#include "istringstream_nocopy.hh"

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

namespace nix {

static size_t regularHashSize(HashType type) {
    switch (type) {
    case htMD5: return md5HashSize;
    case htSHA1: return sha1HashSize;
    case htSHA256: return sha256HashSize;
    case htSHA512: return sha512HashSize;
    }
    abort();
}

void Hash::init()
{
    hashSize = regularHashSize(type);
    assert(hashSize <= maxHashSize);
    memset(hash, 0, maxHashSize);
}

bool Hash::operator == (const Hash & h2) const
{
    if (hashSize != h2.hashSize) return false;
    for (unsigned int i = 0; i < hashSize; i++)
        if (hash[i] != h2.hash[i]) return false;
    return true;
}

bool Hash::operator != (const Hash & h2) const
{
    return !(*this == h2);
}

bool Hash::operator < (const Hash & h) const
{
    if (hashSize < h.hashSize) return true;
    if (hashSize > h.hashSize) return false;
    for (unsigned int i = 0; i < hashSize; i++) {
        if (hash[i] < h.hash[i]) return true;
        if (hash[i] > h.hash[i]) return false;
    }
    return false;
}

const string base16Chars = "0123456789abcdef";

static string printHash16(const Hash & hash)
{
    char buf[hash.hashSize * 2];
    for (unsigned int i = 0; i < hash.hashSize; i++) {
        buf[i * 2] = base16Chars[hash.hash[i] >> 4];
        buf[i * 2 + 1] = base16Chars[hash.hash[i] & 0x0f];
    }
    return string(buf, hash.hashSize * 2);
}

// omitted: E O U T
const string base32Chars = "0123456789abcdfghijklmnpqrsvwxyz";

static string printHash32(const Hash & hash)
{
    assert(hash.hashSize);
    size_t len = hash.base32Len();
    assert(len);

    string s;
    s.reserve(len);

    for (int n = (int) len - 1; n >= 0; n--) {
        unsigned int b = n * 5;
        unsigned int i = b / 8;
        unsigned int j = b % 8;
        unsigned char c =
            (hash.hash[i] >> j)
            | (i >= hash.hashSize - 1 ? 0 : hash.hash[i + 1] << (8 - j));
        s.push_back(base32Chars[c & 0x1f]);
    }

    return s;
}

string printHash16or32(const Hash & hash)
{
    assert(hash.type);
    return hash.to_string(hash.type == htMD5 ? Base16 : Base32, false);
}

std::string Hash::to_string(Base base, bool includeType) const
{
    std::string s;
    if (base == SRI || includeType) {
        s += printHashType(type);
        s += base == SRI ? '-' : ':';
    }
    switch (base) {
    case Base16:
        s += printHash16(*this);
        break;
    case Base32:
        s += printHash32(*this);
        break;
    case Base64:
    case SRI:
        s += base64Encode(std::string((const char *) hash, hashSize));
        break;
    }
    return s;
}

Hash::Hash(std::string_view s, HashType type) : Hash(s, std::optional { type }) { }
Hash::Hash(std::string_view s) : Hash(s, std::optional<HashType>{}) { }

Hash::Hash(std::string_view original, std::optional<HashType> optType)
{
    auto rest = original;

    size_t pos = 0;
    bool isSRI = false;

    // Parse the has type before the separater, if there was one.
    std::optional<HashType> optParsedType;
    {
        auto sep = rest.find(':');
        if (sep == std::string_view::npos) {
            sep = rest.find('-');
            if (sep != std::string_view::npos)
                isSRI = true;
        }
        if (sep != std::string_view::npos) {
            auto hashRaw = rest.substr(0, sep);
            optParsedType = parseHashType(hashRaw);
            rest = rest.substr(sep + 1);
        }
    }

    // Either the string or user must provide the type, if they both do they
    // must agree.
    if (!optParsedType && !optType) {
        throw BadHash("hash '%s' does not include a type, nor is the type otherwise known from context.", rest);
    } else {
        this->type = optParsedType ? *optParsedType : *optType;
        if (optParsedType && optType && *optParsedType != *optType)
            throw BadHash("hash '%s' should have type '%s'", original, printHashType(*optType));
    }

    init();

    if (!isSRI && rest.size() == base16Len()) {

        auto parseHexDigit = [&](char c) {
            if (c >= '0' && c <= '9') return c - '0';
            if (c >= 'A' && c <= 'F') return c - 'A' + 10;
            if (c >= 'a' && c <= 'f') return c - 'a' + 10;
            throw BadHash("invalid base-16 hash '%s'", original);
        };

        for (unsigned int i = 0; i < hashSize; i++) {
            hash[i] =
                parseHexDigit(rest[pos + i * 2]) << 4
                | parseHexDigit(rest[pos + i * 2 + 1]);
        }
    }

    else if (!isSRI && rest.size() == base32Len()) {

        for (unsigned int n = 0; n < rest.size(); ++n) {
            char c = rest[rest.size() - n - 1];
            unsigned char digit;
            for (digit = 0; digit < base32Chars.size(); ++digit) /* !!! slow */
                if (base32Chars[digit] == c) break;
            if (digit >= 32)
                throw BadHash("invalid base-32 hash '%s'", original);
            unsigned int b = n * 5;
            unsigned int i = b / 8;
            unsigned int j = b % 8;
            hash[i] |= digit << j;

            if (i < hashSize - 1) {
                hash[i + 1] |= digit >> (8 - j);
            } else {
                if (digit >> (8 - j))
                    throw BadHash("invalid base-32 hash '%s'", original);
            }
        }
    }

    else if (isSRI || rest.size() == base64Len()) {
        auto d = base64Decode(rest);
        if (d.size() != hashSize)
            throw BadHash("invalid %s hash '%s'", isSRI ? "SRI" : "base-64", original);
        assert(hashSize);
        memcpy(hash, d.data(), hashSize);
    }

    else
        throw BadHash("hash '%s' has wrong length for hash type '%s'", rest, printHashType(this->type));
}

Hash newHashAllowEmpty(std::string hashStr, std::optional<HashType> ht)
{
    if (hashStr.empty()) {
        if (!ht)
            throw BadHash("empty hash requires explicit hash type");
        Hash h(*ht);
        warn("found empty hash, assuming '%s'", h.to_string(SRI, true));
        return h;
    } else
        return Hash(hashStr, ht);
}

union Ctx
{
    MD5_CTX md5;
    SHA_CTX sha1;
    SHA256_CTX sha256;
    SHA512_CTX sha512;
};

static void start(HashType ht, Ctx & ctx)
{
    if (ht == htMD5) MD5_Init(&ctx.md5);
    else if (ht == htSHA1) SHA1_Init(&ctx.sha1);
    else if (ht == htSHA256) SHA256_Init(&ctx.sha256);
    else if (ht == htSHA512) SHA512_Init(&ctx.sha512);
}

static void update(HashType ht, Ctx & ctx,
    const unsigned char * bytes, size_t len)
{
    if (ht == htMD5) MD5_Update(&ctx.md5, bytes, len);
    else if (ht == htSHA1) SHA1_Update(&ctx.sha1, bytes, len);
    else if (ht == htSHA256) SHA256_Update(&ctx.sha256, bytes, len);
    else if (ht == htSHA512) SHA512_Update(&ctx.sha512, bytes, len);
}

static void finish(HashType ht, Ctx & ctx, unsigned char * hash)
{
    if (ht == htMD5) MD5_Final(hash, &ctx.md5);
    else if (ht == htSHA1) SHA1_Final(hash, &ctx.sha1);
    else if (ht == htSHA256) SHA256_Final(hash, &ctx.sha256);
    else if (ht == htSHA512) SHA512_Final(hash, &ctx.sha512);
}

Hash hashString(HashType ht, std::string_view s)
{
    Ctx ctx;
    Hash hash(ht);
    start(ht, ctx);
    update(ht, ctx, (const unsigned char *) s.data(), s.length());
    finish(ht, ctx, hash.hash);
    return hash;
}

Hash hashFile(HashType ht, const Path & path)
{
    HashSink sink(ht);
    readFile(path, sink);
    return sink.finish().first;
}

HashSink::HashSink(HashType ht) : ht(ht)
{
    ctx = new Ctx;
    bytes = 0;
    start(ht, *ctx);
}

HashSink::~HashSink()
{
    bufPos = 0;
    delete ctx;
}

void HashSink::write(const unsigned char * data, size_t len)
{
    bytes += len;
    update(ht, *ctx, data, len);
}

HashResult HashSink::finish()
{
    flush();
    Hash hash(ht);
    nix::finish(ht, *ctx, hash.hash);
    return HashResult(hash, bytes);
}

HashResult HashSink::currentHash()
{
    flush();
    Ctx ctx2 = *ctx;
    Hash hash(ht);
    nix::finish(ht, ctx2, hash.hash);
    return HashResult(hash, bytes);
}

HashResult hashPath(
    HashType ht, const Path & path, PathFilter & filter)
{
    HashSink sink(ht);
    dumpPath(path, sink, filter);
    return sink.finish();
}

Hash compressHash(const Hash & hash, unsigned int newSize)
{
    Hash h(hash.type);
    h.hashSize = newSize;
    for (unsigned int i = 0; i < hash.hashSize; ++i)
        h.hash[i % newSize] ^= hash.hash[i];
    return h;
}

std::optional<HashType> parseHashTypeOpt(std::string_view s)
{
    if (s == "md5") return htMD5;
    else if (s == "sha1") return htSHA1;
    else if (s == "sha256") return htSHA256;
    else if (s == "sha512") return htSHA512;
    else return std::optional<HashType> {};
}

HashType parseHashType(std::string_view s)
{
    auto opt_h = parseHashTypeOpt(s);
    if (opt_h)
        return *opt_h;
    else
        throw UsageError("unknown hash algorithm '%1%'", s);
}

string printHashType(HashType ht)
{
    switch (ht) {
    case htMD5: return "md5";
    case htSHA1: return "sha1";
    case htSHA256: return "sha256";
    case htSHA512: return "sha512";
    default:
        // illegal hash type enum value internally, as opposed to external input
        // which should be validated with nice error message.
        abort();
    }
}

}
<commit_msg>Factor the prefix splitting in hash<commit_after>#include <iostream>
#include <cstring>

#include <openssl/md5.h>
#include <openssl/sha.h>

#include "args.hh"
#include "hash.hh"
#include "archive.hh"
#include "parser.hh"
#include "util.hh"
#include "istringstream_nocopy.hh"

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

namespace nix {

static size_t regularHashSize(HashType type) {
    switch (type) {
    case htMD5: return md5HashSize;
    case htSHA1: return sha1HashSize;
    case htSHA256: return sha256HashSize;
    case htSHA512: return sha512HashSize;
    }
    abort();
}

void Hash::init()
{
    hashSize = regularHashSize(type);
    assert(hashSize <= maxHashSize);
    memset(hash, 0, maxHashSize);
}

bool Hash::operator == (const Hash & h2) const
{
    if (hashSize != h2.hashSize) return false;
    for (unsigned int i = 0; i < hashSize; i++)
        if (hash[i] != h2.hash[i]) return false;
    return true;
}

bool Hash::operator != (const Hash & h2) const
{
    return !(*this == h2);
}

bool Hash::operator < (const Hash & h) const
{
    if (hashSize < h.hashSize) return true;
    if (hashSize > h.hashSize) return false;
    for (unsigned int i = 0; i < hashSize; i++) {
        if (hash[i] < h.hash[i]) return true;
        if (hash[i] > h.hash[i]) return false;
    }
    return false;
}

const string base16Chars = "0123456789abcdef";

static string printHash16(const Hash & hash)
{
    char buf[hash.hashSize * 2];
    for (unsigned int i = 0; i < hash.hashSize; i++) {
        buf[i * 2] = base16Chars[hash.hash[i] >> 4];
        buf[i * 2 + 1] = base16Chars[hash.hash[i] & 0x0f];
    }
    return string(buf, hash.hashSize * 2);
}

// omitted: E O U T
const string base32Chars = "0123456789abcdfghijklmnpqrsvwxyz";

static string printHash32(const Hash & hash)
{
    assert(hash.hashSize);
    size_t len = hash.base32Len();
    assert(len);

    string s;
    s.reserve(len);

    for (int n = (int) len - 1; n >= 0; n--) {
        unsigned int b = n * 5;
        unsigned int i = b / 8;
        unsigned int j = b % 8;
        unsigned char c =
            (hash.hash[i] >> j)
            | (i >= hash.hashSize - 1 ? 0 : hash.hash[i + 1] << (8 - j));
        s.push_back(base32Chars[c & 0x1f]);
    }

    return s;
}

string printHash16or32(const Hash & hash)
{
    assert(hash.type);
    return hash.to_string(hash.type == htMD5 ? Base16 : Base32, false);
}

std::string Hash::to_string(Base base, bool includeType) const
{
    std::string s;
    if (base == SRI || includeType) {
        s += printHashType(type);
        s += base == SRI ? '-' : ':';
    }
    switch (base) {
    case Base16:
        s += printHash16(*this);
        break;
    case Base32:
        s += printHash32(*this);
        break;
    case Base64:
    case SRI:
        s += base64Encode(std::string((const char *) hash, hashSize));
        break;
    }
    return s;
}

Hash::Hash(std::string_view s, HashType type) : Hash(s, std::optional { type }) { }
Hash::Hash(std::string_view s) : Hash(s, std::optional<HashType>{}) { }

Hash::Hash(std::string_view original, std::optional<HashType> optType)
{
    auto rest = original;

    size_t pos = 0;
    bool isSRI = false;

    // Parse the has type before the separater, if there was one.
    std::optional<HashType> optParsedType;
    {
        auto hashRaw = splitPrefix(rest, ':');

        if (!hashRaw) {
            hashRaw = splitPrefix(rest, '-');
            if (hashRaw)
                isSRI = true;
        }
        if (hashRaw)
            optParsedType = parseHashType(*hashRaw);
    }

    // Either the string or user must provide the type, if they both do they
    // must agree.
    if (!optParsedType && !optType) {
        throw BadHash("hash '%s' does not include a type, nor is the type otherwise known from context.", rest);
    } else {
        this->type = optParsedType ? *optParsedType : *optType;
        if (optParsedType && optType && *optParsedType != *optType)
            throw BadHash("hash '%s' should have type '%s'", original, printHashType(*optType));
    }

    init();

    if (!isSRI && rest.size() == base16Len()) {

        auto parseHexDigit = [&](char c) {
            if (c >= '0' && c <= '9') return c - '0';
            if (c >= 'A' && c <= 'F') return c - 'A' + 10;
            if (c >= 'a' && c <= 'f') return c - 'a' + 10;
            throw BadHash("invalid base-16 hash '%s'", original);
        };

        for (unsigned int i = 0; i < hashSize; i++) {
            hash[i] =
                parseHexDigit(rest[pos + i * 2]) << 4
                | parseHexDigit(rest[pos + i * 2 + 1]);
        }
    }

    else if (!isSRI && rest.size() == base32Len()) {

        for (unsigned int n = 0; n < rest.size(); ++n) {
            char c = rest[rest.size() - n - 1];
            unsigned char digit;
            for (digit = 0; digit < base32Chars.size(); ++digit) /* !!! slow */
                if (base32Chars[digit] == c) break;
            if (digit >= 32)
                throw BadHash("invalid base-32 hash '%s'", original);
            unsigned int b = n * 5;
            unsigned int i = b / 8;
            unsigned int j = b % 8;
            hash[i] |= digit << j;

            if (i < hashSize - 1) {
                hash[i + 1] |= digit >> (8 - j);
            } else {
                if (digit >> (8 - j))
                    throw BadHash("invalid base-32 hash '%s'", original);
            }
        }
    }

    else if (isSRI || rest.size() == base64Len()) {
        auto d = base64Decode(rest);
        if (d.size() != hashSize)
            throw BadHash("invalid %s hash '%s'", isSRI ? "SRI" : "base-64", original);
        assert(hashSize);
        memcpy(hash, d.data(), hashSize);
    }

    else
        throw BadHash("hash '%s' has wrong length for hash type '%s'", rest, printHashType(this->type));
}

Hash newHashAllowEmpty(std::string hashStr, std::optional<HashType> ht)
{
    if (hashStr.empty()) {
        if (!ht)
            throw BadHash("empty hash requires explicit hash type");
        Hash h(*ht);
        warn("found empty hash, assuming '%s'", h.to_string(SRI, true));
        return h;
    } else
        return Hash(hashStr, ht);
}

union Ctx
{
    MD5_CTX md5;
    SHA_CTX sha1;
    SHA256_CTX sha256;
    SHA512_CTX sha512;
};

static void start(HashType ht, Ctx & ctx)
{
    if (ht == htMD5) MD5_Init(&ctx.md5);
    else if (ht == htSHA1) SHA1_Init(&ctx.sha1);
    else if (ht == htSHA256) SHA256_Init(&ctx.sha256);
    else if (ht == htSHA512) SHA512_Init(&ctx.sha512);
}

static void update(HashType ht, Ctx & ctx,
    const unsigned char * bytes, size_t len)
{
    if (ht == htMD5) MD5_Update(&ctx.md5, bytes, len);
    else if (ht == htSHA1) SHA1_Update(&ctx.sha1, bytes, len);
    else if (ht == htSHA256) SHA256_Update(&ctx.sha256, bytes, len);
    else if (ht == htSHA512) SHA512_Update(&ctx.sha512, bytes, len);
}

static void finish(HashType ht, Ctx & ctx, unsigned char * hash)
{
    if (ht == htMD5) MD5_Final(hash, &ctx.md5);
    else if (ht == htSHA1) SHA1_Final(hash, &ctx.sha1);
    else if (ht == htSHA256) SHA256_Final(hash, &ctx.sha256);
    else if (ht == htSHA512) SHA512_Final(hash, &ctx.sha512);
}

Hash hashString(HashType ht, std::string_view s)
{
    Ctx ctx;
    Hash hash(ht);
    start(ht, ctx);
    update(ht, ctx, (const unsigned char *) s.data(), s.length());
    finish(ht, ctx, hash.hash);
    return hash;
}

Hash hashFile(HashType ht, const Path & path)
{
    HashSink sink(ht);
    readFile(path, sink);
    return sink.finish().first;
}

HashSink::HashSink(HashType ht) : ht(ht)
{
    ctx = new Ctx;
    bytes = 0;
    start(ht, *ctx);
}

HashSink::~HashSink()
{
    bufPos = 0;
    delete ctx;
}

void HashSink::write(const unsigned char * data, size_t len)
{
    bytes += len;
    update(ht, *ctx, data, len);
}

HashResult HashSink::finish()
{
    flush();
    Hash hash(ht);
    nix::finish(ht, *ctx, hash.hash);
    return HashResult(hash, bytes);
}

HashResult HashSink::currentHash()
{
    flush();
    Ctx ctx2 = *ctx;
    Hash hash(ht);
    nix::finish(ht, ctx2, hash.hash);
    return HashResult(hash, bytes);
}

HashResult hashPath(
    HashType ht, const Path & path, PathFilter & filter)
{
    HashSink sink(ht);
    dumpPath(path, sink, filter);
    return sink.finish();
}

Hash compressHash(const Hash & hash, unsigned int newSize)
{
    Hash h(hash.type);
    h.hashSize = newSize;
    for (unsigned int i = 0; i < hash.hashSize; ++i)
        h.hash[i % newSize] ^= hash.hash[i];
    return h;
}

std::optional<HashType> parseHashTypeOpt(std::string_view s)
{
    if (s == "md5") return htMD5;
    else if (s == "sha1") return htSHA1;
    else if (s == "sha256") return htSHA256;
    else if (s == "sha512") return htSHA512;
    else return std::optional<HashType> {};
}

HashType parseHashType(std::string_view s)
{
    auto opt_h = parseHashTypeOpt(s);
    if (opt_h)
        return *opt_h;
    else
        throw UsageError("unknown hash algorithm '%1%'", s);
}

string printHashType(HashType ht)
{
    switch (ht) {
    case htMD5: return "md5";
    case htSHA1: return "sha1";
    case htSHA256: return "sha256";
    case htSHA512: return "sha512";
    default:
        // illegal hash type enum value internally, as opposed to external input
        // which should be validated with nice error message.
        abort();
    }
}

}
<|endoftext|>


<commit_before>#include <stan/math/prim.hpp>
#include <test/unit/math/prim/util.hpp>
#include <boost/random/mersenne_twister.hpp>
#include <boost/math/distributions/chi_squared.hpp>
#include <gtest/gtest.h>
#include <stdexcept>
#include <vector>

TEST(ProbDistributionsWishartCholesky, rng) {
  using Eigen::Dynamic;
  using Eigen::Matrix;
  using Eigen::MatrixXd;

  using stan::math::wishart_cholesky_rng;

  boost::random::mt19937 rng;

  MatrixXd omega(3, 4);
  EXPECT_THROW(wishart_cholesky_rng(3.0, omega, rng), std::domain_error);

  MatrixXd sigma(3, 3);
  sigma << 9.0, -3.0, 0.0, -3.0, 4.0, 1.0, 0.0, 1.0, 3.0;

  Matrix<double, Dynamic, Dynamic> LS = sigma.llt().matrixL();

  EXPECT_NO_THROW(wishart_cholesky_rng(3.0, LS, rng));
  EXPECT_THROW(wishart_cholesky_rng(2, LS, rng), std::domain_error);
  EXPECT_THROW(wishart_cholesky_rng(-1, LS, rng), std::domain_error);
  LS(2, 2) = -1.0;
  EXPECT_THROW(wishart_cholesky_rng(3.0, LS, rng), std::domain_error);
}

TEST(ProbDistributionsWishartCholesky, rng_pos_def) {
  using Eigen::MatrixXd;
  using stan::math::wishart_cholesky_rng;

  boost::random::mt19937 rng;

  MatrixXd Sigma(2, 2);
  MatrixXd Sigma_non_pos_def(2, 2);

  Sigma << 1, 0, 0, 1;
  Sigma_non_pos_def << -1, 0, 0, 1;

  unsigned int dof = 5;

  EXPECT_NO_THROW(wishart_cholesky_rng(dof, Sigma, rng));
  EXPECT_THROW(wishart_cholesky_rng(dof, Sigma_non_pos_def, rng),
               std::domain_error);
}

TEST(ProbDistributionsWishartCholesky, cholesky_factor_check) {
  using Eigen::MatrixXd;
  using stan::math::identity_matrix;
  using stan::math::wishart_cholesky_rng;
  using stan::test::unit::expect_symmetric;
  using stan::test::unit::spd_rng;

  boost::random::mt19937 rng;

  for (int k = 1; k < 20; ++k)
    for (double nu = k - 0.9; nu < k + 10; ++nu)
      for (int n = 0; n < 10; ++n)
        expect_symmetric(
            stan::math::multiply_lower_tri_self_transpose(wishart_cholesky_rng(
                nu, stan::math::cholesky_decompose(spd_rng(k, rng)), rng)));
}

TEST(ProbDistributionsWishartCholesky, marginalTwoChiSquareGoodnessFitTest) {
  using boost::math::chi_squared;
  using boost::math::digamma;
  using Eigen::MatrixXd;
  using stan::math::determinant;
  using stan::math::wishart_cholesky_rng;
  using std::log;

  boost::random::mt19937 rng;
  MatrixXd sigma(3, 3);
  sigma << 9.0, -3.0, 2.0, -3.0, 4.0, 0.0, 2.0, 0.0, 3.0;
  int N = 10000;

  double avg = 0;
  double expect = sigma.rows() * log(2.0) + log(determinant(sigma))
                  + digamma(5.0 / 2.0) + digamma(4.0 / 2.0)
                  + digamma(3.0 / 2.0);

  MatrixXd a(sigma.rows(), sigma.rows());
  for (int count = 0; count < N; ++count) {
    a = wishart_cholesky_rng(5.0, stan::math::cholesky_decompose(sigma), rng);
    avg += stan::math::sum(stan::math::log(a.diagonal()));
  }
  avg /= N;
  double chi = (expect - avg) * (expect - avg) / expect;
  chi_squared mydist(1);
  EXPECT_TRUE(chi < quantile(complement(mydist, 1e-6)));
}

TEST(ProbDistributionsWishartCholesky, SpecialRNGTest) {
  // For any vector C != 0
  // (C' * W * C) / (C' * S * C)
  // must be chi-square distributed with df = k
  // which has mean = k and variance = 2k

  using Eigen::Dynamic;
  using Eigen::Matrix;
  using Eigen::MatrixXd;
  using Eigen::VectorXd;
  using stan::math::multiply_lower_tri_self_transpose;
  using stan::math::wishart_cholesky_rng;

  boost::random::mt19937 rng(1234);

  MatrixXd sigma(3, 3);

  sigma << 9.0, 2.0, 2.0, 2.0, 4.0, 1.0, 2.0, 1.0, 3.0;

  Matrix<double, Dynamic, Dynamic> LS = sigma.llt().matrixL();

  VectorXd C(3);
  C << 2, 1, 3;

  size_t N = 1e4;
  int k = 20;
  // tolerance for variance
  double tol = 0.2;
  std::vector<double> acum;
  acum.reserve(N);
  for (size_t i = 0; i < N; i++)
    acum.push_back(
        (C.transpose()
         * multiply_lower_tri_self_transpose(wishart_cholesky_rng(k, LS, rng))
         * C)(0)
        / (C.transpose() * sigma * C)(0));

  EXPECT_NEAR(1, stan::math::mean(acum) / k, tol * tol);
  EXPECT_NEAR(1, stan::math::variance(acum) / (2 * k), tol);
}
<commit_msg>remove unnecssary test<commit_after>#include <stan/math/prim.hpp>
#include <test/unit/math/prim/util.hpp>
#include <boost/random/mersenne_twister.hpp>
#include <boost/math/distributions/chi_squared.hpp>
#include <gtest/gtest.h>
#include <stdexcept>
#include <vector>

TEST(ProbDistributionsWishartCholesky, rng) {
  using Eigen::Dynamic;
  using Eigen::Matrix;
  using Eigen::MatrixXd;

  using stan::math::wishart_cholesky_rng;

  boost::random::mt19937 rng;

  MatrixXd omega(3, 4);
  EXPECT_THROW(wishart_cholesky_rng(3.0, omega, rng), std::domain_error);

  MatrixXd sigma(3, 3);
  sigma << 9.0, -3.0, 0.0, -3.0, 4.0, 1.0, 0.0, 1.0, 3.0;

  Matrix<double, Dynamic, Dynamic> LS = sigma.llt().matrixL();

  EXPECT_NO_THROW(wishart_cholesky_rng(3.0, LS, rng));
  EXPECT_THROW(wishart_cholesky_rng(2, LS, rng), std::domain_error);
  EXPECT_THROW(wishart_cholesky_rng(-1, LS, rng), std::domain_error);
  LS(2, 2) = -1.0;
  EXPECT_THROW(wishart_cholesky_rng(3.0, LS, rng), std::domain_error);
}

TEST(ProbDistributionsWishartCholesky, rng_pos_def) {
  using Eigen::MatrixXd;
  using stan::math::wishart_cholesky_rng;

  boost::random::mt19937 rng;

  MatrixXd Sigma(2, 2);
  MatrixXd Sigma_non_pos_def(2, 2);

  Sigma << 1, 0, 0, 1;
  Sigma_non_pos_def << -1, 0, 0, 1;

  unsigned int dof = 5;

  EXPECT_NO_THROW(wishart_cholesky_rng(dof, Sigma, rng));
  EXPECT_THROW(wishart_cholesky_rng(dof, Sigma_non_pos_def, rng),
               std::domain_error);
}

TEST(ProbDistributionsWishartCholesky, marginalTwoChiSquareGoodnessFitTest) {
  using boost::math::chi_squared;
  using boost::math::digamma;
  using Eigen::MatrixXd;
  using stan::math::determinant;
  using stan::math::wishart_cholesky_rng;
  using std::log;

  boost::random::mt19937 rng;
  MatrixXd sigma(3, 3);
  sigma << 9.0, -3.0, 2.0, -3.0, 4.0, 0.0, 2.0, 0.0, 3.0;
  int N = 10000;

  double avg = 0;
  double expect = sigma.rows() * log(2.0) + log(determinant(sigma))
                  + digamma(5.0 / 2.0) + digamma(4.0 / 2.0)
                  + digamma(3.0 / 2.0);

  MatrixXd a(sigma.rows(), sigma.rows());
  for (int count = 0; count < N; ++count) {
    a = wishart_cholesky_rng(5.0, stan::math::cholesky_decompose(sigma), rng);
    avg += stan::math::sum(stan::math::log(a.diagonal()));
  }
  avg /= N;
  double chi = (expect - avg) * (expect - avg) / expect;
  chi_squared mydist(1);
  EXPECT_TRUE(chi < quantile(complement(mydist, 1e-6)));
}

TEST(ProbDistributionsWishartCholesky, SpecialRNGTest) {
  // For any vector C != 0
  // (C' * W * C) / (C' * S * C)
  // must be chi-square distributed with df = k
  // which has mean = k and variance = 2k

  using Eigen::Dynamic;
  using Eigen::Matrix;
  using Eigen::MatrixXd;
  using Eigen::VectorXd;
  using stan::math::multiply_lower_tri_self_transpose;
  using stan::math::wishart_cholesky_rng;

  boost::random::mt19937 rng(1234);

  MatrixXd sigma(3, 3);

  sigma << 9.0, 2.0, 2.0, 2.0, 4.0, 1.0, 2.0, 1.0, 3.0;

  Matrix<double, Dynamic, Dynamic> LS = sigma.llt().matrixL();

  VectorXd C(3);
  C << 2, 1, 3;

  size_t N = 1e4;
  int k = 20;
  // tolerance for variance
  double tol = 0.2;
  std::vector<double> acum;
  acum.reserve(N);
  for (size_t i = 0; i < N; i++)
    acum.push_back(
        (C.transpose()
         * multiply_lower_tri_self_transpose(wishart_cholesky_rng(k, LS, rng))
         * C)(0)
        / (C.transpose() * sigma * C)(0));

  EXPECT_NEAR(1, stan::math::mean(acum) / k, tol * tol);
  EXPECT_NEAR(1, stan::math::variance(acum) / (2 * k), tol);
}
<|endoftext|>


<commit_before>#include "ovpCSignChangeDetector.h"
#include <cstdlib>

using namespace OpenViBE;
using namespace OpenViBE::Kernel;
using namespace OpenViBE::Plugins;

using namespace OpenViBEPlugins;
using namespace OpenViBEPlugins::Stimulation;

boolean CSignChangeDetector::initialize(void)
{
	CString l_sSettingValue;

	// we read the settings:
	// The stimulations names:
	getStaticBoxContext().getSettingValue(0, l_sSettingValue);
	m_ui64OnStimulationId=getTypeManager().getEnumerationEntryValueFromName(OV_TypeId_Stimulation, l_sSettingValue);
	getStaticBoxContext().getSettingValue(1, l_sSettingValue);
	m_ui64OffStimulationId=getTypeManager().getEnumerationEntryValueFromName(OV_TypeId_Stimulation, l_sSettingValue);

	m_ui64ChannelIndex=FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 2);
	// The refractory periode

	m_f64Lastsample=0;
	m_bNextStimIsOn=true;

	m_pStreamedMatrixDecoder=&this->getAlgorithmManager().getAlgorithm(this->getAlgorithmManager().createAlgorithm(OVP_GD_ClassId_Algorithm_StreamedMatrixStreamDecoder));
	m_pStreamedMatrixDecoder->initialize();
	op_pMatrix.initialize(m_pStreamedMatrixDecoder->getOutputParameter(OVP_GD_Algorithm_StreamedMatrixStreamDecoder_OutputParameterId_Matrix));

	m_pStimulationEncoder=&this->getAlgorithmManager().getAlgorithm(this->getAlgorithmManager().createAlgorithm(OVP_GD_ClassId_Algorithm_StimulationStreamEncoder));
	m_pStimulationEncoder->initialize();
	ip_pStimulationSet.initialize(m_pStimulationEncoder->getInputParameter(OVP_GD_Algorithm_StimulationStreamEncoder_InputParameterId_StimulationSet));

	m_bError=false;
	return true;
}

boolean CSignChangeDetector::uninitialize(void)
{
	op_pMatrix.uninitialize();
	m_pStreamedMatrixDecoder->uninitialize();
	this->getAlgorithmManager().releaseAlgorithm(*m_pStreamedMatrixDecoder);

	ip_pStimulationSet.uninitialize();
	m_pStimulationEncoder->uninitialize();
	this->getAlgorithmManager().releaseAlgorithm(*m_pStimulationEncoder);

	return true;
}

boolean CSignChangeDetector::processInput(uint32 ui32InputIndex)
{
	getBoxAlgorithmContext()->markAlgorithmAsReadyToProcess();
	return true;
}

boolean CSignChangeDetector::process(void)
{
	IBoxIO& l_rDynamicBoxContext=this->getDynamicBoxContext();

	TParameterHandler < IMemoryBuffer* > op_pMemoryBuffer(m_pStimulationEncoder->getOutputParameter(OVP_GD_Algorithm_StimulationStreamEncoder_OutputParameterId_EncodedMemoryBuffer));
	op_pMemoryBuffer=l_rDynamicBoxContext.getOutputChunk(0);

	// We decode the stream matrix
	for(uint i=0; i<l_rDynamicBoxContext.getInputChunkCount(0); i++)
	{
		uint64 l_ui64StartTime=l_rDynamicBoxContext.getInputChunkStartTime(0, i);
		uint64 l_ui64EndTime=l_rDynamicBoxContext.getInputChunkEndTime(0, i);

		TParameterHandler < const IMemoryBuffer* > ip_pMemoryBuffer(m_pStreamedMatrixDecoder->getInputParameter(OVP_GD_Algorithm_StreamedMatrixStreamDecoder_InputParameterId_MemoryBufferToDecode));
		ip_pMemoryBuffer=l_rDynamicBoxContext.getInputChunk(0, i);
		m_pStreamedMatrixDecoder->process();

		// if  we received the header
		if(m_pStreamedMatrixDecoder->isOutputTriggerActive(OVP_GD_Algorithm_StreamedMatrixStreamDecoder_OutputTriggerId_ReceivedHeader))
		{
			//we analyse the header (meaning the input matrix size)
			if (op_pMatrix->getDimensionCount() != 2)
			{
				this->getLogManager() << LogLevel_ImportantWarning << "Streamed matrix must have exactly 2 dimensions\n";
				return false;
			}
			else
			{
				if (m_ui64ChannelIndex > op_pMatrix->getDimensionSize(0) - 1)
				{
					this->getLogManager() << LogLevel_Info << "Channel Index out of bounds. Incoming matrix has fewer channels than specified index.";
					return false;
				}

				m_ui64SamplesPerChannel = op_pMatrix->getDimensionSize(1);
			}

			// we send a header on the stimulation output:
			m_pStimulationEncoder->process(OVP_GD_Algorithm_StimulationStreamEncoder_InputTriggerId_EncodeHeader);
			l_rDynamicBoxContext.markOutputAsReadyToSend(0, l_ui64StartTime,l_ui64EndTime );
		}

		// if we received a buffer
		if(m_pStreamedMatrixDecoder->isOutputTriggerActive(OVP_GD_Algorithm_StreamedMatrixStreamDecoder_OutputTriggerId_ReceivedBuffer))
		{
			ip_pStimulationSet->setStimulationCount(0);
			float64* l_pData = op_pMatrix->getBuffer();
			// for each data sample of the buffer we look for sign change

			for (uint j=0;j< op_pMatrix->getDimensionSize(1);j++)
			{
				// if m_bNextStimIsOn==true we look for the signal getting positive.
				if ( m_bNextStimIsOn && ( ( (j==0) ? (m_f64Lastsample) : (l_pData[(m_ui64ChannelIndex * m_ui64SamplesPerChannel) + j-1]) ) <0) && (l_pData[(m_ui64ChannelIndex * m_ui64SamplesPerChannel) + j]>=0) )
				{
					uint64 l_ui64Time = l_ui64StartTime + (l_ui64EndTime-l_ui64StartTime)*j/m_ui64SamplesPerChannel;

					ip_pStimulationSet->appendStimulation(m_ui64OnStimulationId, l_ui64Time, 0);
					m_bNextStimIsOn=false;
				}

				// if m_bNextStimIsOn==false we look for the signal getting negative.
				if ( !m_bNextStimIsOn && ( ( (j==0) ? (m_f64Lastsample) : (l_pData[(m_ui64ChannelIndex * m_ui64SamplesPerChannel) + j-1]) ) >=0) && (l_pData[(m_ui64ChannelIndex * m_ui64SamplesPerChannel) + j]<0) )
				{
					uint64 l_ui64Time = l_ui64StartTime + (l_ui64EndTime-l_ui64StartTime)*j/m_ui64SamplesPerChannel;
					ip_pStimulationSet->appendStimulation(m_ui64OffStimulationId, l_ui64Time, 0);
					m_bNextStimIsOn=true;
				}
			}

			m_f64Lastsample=l_pData[(m_ui64ChannelIndex * m_ui64SamplesPerChannel) +m_ui64SamplesPerChannel-1]; // we save the last sample of the buffer to compare it to the first one of the next buffer
			m_pStimulationEncoder->process(OVP_GD_Algorithm_StimulationStreamEncoder_InputTriggerId_EncodeBuffer);
			l_rDynamicBoxContext.markOutputAsReadyToSend(0, l_ui64StartTime, l_ui64EndTime);
		}

		// if we received the End
		if(m_pStreamedMatrixDecoder->isOutputTriggerActive(OVP_GD_Algorithm_StreamedMatrixStreamDecoder_OutputTriggerId_ReceivedEnd))
		{
			// we send the End signal to the stimulation output:
			m_pStimulationEncoder->process(OVP_GD_Algorithm_StimulationStreamEncoder_InputTriggerId_EncodeEnd);
		}

		// The stream matrix chunk i has been process
		l_rDynamicBoxContext.markInputAsDeprecated(0, i);
	}

	return true;
}
<commit_msg>openvibe-plugins/stimulation: * fixed a compilation bug on windows in Sign Change Detector<commit_after>#include "ovpCSignChangeDetector.h"
#include <cstdlib>

using namespace OpenViBE;
using namespace OpenViBE::Kernel;
using namespace OpenViBE::Plugins;

using namespace OpenViBEPlugins;
using namespace OpenViBEPlugins::Stimulation;

boolean CSignChangeDetector::initialize(void)
{
	CString l_sSettingValue;

	// we read the settings:
	// The stimulations names:
	getStaticBoxContext().getSettingValue(0, l_sSettingValue);
	m_ui64OnStimulationId=getTypeManager().getEnumerationEntryValueFromName(OV_TypeId_Stimulation, l_sSettingValue);
	getStaticBoxContext().getSettingValue(1, l_sSettingValue);
	m_ui64OffStimulationId=getTypeManager().getEnumerationEntryValueFromName(OV_TypeId_Stimulation, l_sSettingValue);

	m_ui64ChannelIndex=FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 2);
	// The refractory periode

	m_f64Lastsample=0;
	m_bNextStimIsOn=true;

	m_pStreamedMatrixDecoder=&this->getAlgorithmManager().getAlgorithm(this->getAlgorithmManager().createAlgorithm(OVP_GD_ClassId_Algorithm_StreamedMatrixStreamDecoder));
	m_pStreamedMatrixDecoder->initialize();
	op_pMatrix.initialize(m_pStreamedMatrixDecoder->getOutputParameter(OVP_GD_Algorithm_StreamedMatrixStreamDecoder_OutputParameterId_Matrix));

	m_pStimulationEncoder=&this->getAlgorithmManager().getAlgorithm(this->getAlgorithmManager().createAlgorithm(OVP_GD_ClassId_Algorithm_StimulationStreamEncoder));
	m_pStimulationEncoder->initialize();
	ip_pStimulationSet.initialize(m_pStimulationEncoder->getInputParameter(OVP_GD_Algorithm_StimulationStreamEncoder_InputParameterId_StimulationSet));

	m_bError=false;
	return true;
}

boolean CSignChangeDetector::uninitialize(void)
{
	op_pMatrix.uninitialize();
	m_pStreamedMatrixDecoder->uninitialize();
	this->getAlgorithmManager().releaseAlgorithm(*m_pStreamedMatrixDecoder);

	ip_pStimulationSet.uninitialize();
	m_pStimulationEncoder->uninitialize();
	this->getAlgorithmManager().releaseAlgorithm(*m_pStimulationEncoder);

	return true;
}

boolean CSignChangeDetector::processInput(uint32 ui32InputIndex)
{
	getBoxAlgorithmContext()->markAlgorithmAsReadyToProcess();
	return true;
}

boolean CSignChangeDetector::process(void)
{
	IBoxIO& l_rDynamicBoxContext=this->getDynamicBoxContext();

	TParameterHandler < IMemoryBuffer* > op_pMemoryBuffer(m_pStimulationEncoder->getOutputParameter(OVP_GD_Algorithm_StimulationStreamEncoder_OutputParameterId_EncodedMemoryBuffer));
	op_pMemoryBuffer=l_rDynamicBoxContext.getOutputChunk(0);

	// We decode the stream matrix
	for(uint32 i=0; i<l_rDynamicBoxContext.getInputChunkCount(0); i++)
	{
		uint64 l_ui64StartTime=l_rDynamicBoxContext.getInputChunkStartTime(0, i);
		uint64 l_ui64EndTime=l_rDynamicBoxContext.getInputChunkEndTime(0, i);

		TParameterHandler < const IMemoryBuffer* > ip_pMemoryBuffer(m_pStreamedMatrixDecoder->getInputParameter(OVP_GD_Algorithm_StreamedMatrixStreamDecoder_InputParameterId_MemoryBufferToDecode));
		ip_pMemoryBuffer=l_rDynamicBoxContext.getInputChunk(0, i);
		m_pStreamedMatrixDecoder->process();

		// if  we received the header
		if(m_pStreamedMatrixDecoder->isOutputTriggerActive(OVP_GD_Algorithm_StreamedMatrixStreamDecoder_OutputTriggerId_ReceivedHeader))
		{
			//we analyse the header (meaning the input matrix size)
			if (op_pMatrix->getDimensionCount() != 2)
			{
				this->getLogManager() << LogLevel_ImportantWarning << "Streamed matrix must have exactly 2 dimensions\n";
				return false;
			}
			else
			{
				if (m_ui64ChannelIndex > op_pMatrix->getDimensionSize(0) - 1)
				{
					this->getLogManager() << LogLevel_Info << "Channel Index out of bounds. Incoming matrix has fewer channels than specified index.";
					return false;
				}

				m_ui64SamplesPerChannel = op_pMatrix->getDimensionSize(1);
			}

			// we send a header on the stimulation output:
			m_pStimulationEncoder->process(OVP_GD_Algorithm_StimulationStreamEncoder_InputTriggerId_EncodeHeader);
			l_rDynamicBoxContext.markOutputAsReadyToSend(0, l_ui64StartTime,l_ui64EndTime );
		}

		// if we received a buffer
		if(m_pStreamedMatrixDecoder->isOutputTriggerActive(OVP_GD_Algorithm_StreamedMatrixStreamDecoder_OutputTriggerId_ReceivedBuffer))
		{
			ip_pStimulationSet->setStimulationCount(0);
			float64* l_pData = op_pMatrix->getBuffer();
			// for each data sample of the buffer we look for sign change

			for (uint32 j=0;j< op_pMatrix->getDimensionSize(1);j++)
			{
				// if m_bNextStimIsOn==true we look for the signal getting positive.
				if ( m_bNextStimIsOn && ( ( (j==0) ? (m_f64Lastsample) : (l_pData[(m_ui64ChannelIndex * m_ui64SamplesPerChannel) + j-1]) ) <0) && (l_pData[(m_ui64ChannelIndex * m_ui64SamplesPerChannel) + j]>=0) )
				{
					uint64 l_ui64Time = l_ui64StartTime + (l_ui64EndTime-l_ui64StartTime)*j/m_ui64SamplesPerChannel;

					ip_pStimulationSet->appendStimulation(m_ui64OnStimulationId, l_ui64Time, 0);
					m_bNextStimIsOn=false;
				}

				// if m_bNextStimIsOn==false we look for the signal getting negative.
				if ( !m_bNextStimIsOn && ( ( (j==0) ? (m_f64Lastsample) : (l_pData[(m_ui64ChannelIndex * m_ui64SamplesPerChannel) + j-1]) ) >=0) && (l_pData[(m_ui64ChannelIndex * m_ui64SamplesPerChannel) + j]<0) )
				{
					uint64 l_ui64Time = l_ui64StartTime + (l_ui64EndTime-l_ui64StartTime)*j/m_ui64SamplesPerChannel;
					ip_pStimulationSet->appendStimulation(m_ui64OffStimulationId, l_ui64Time, 0);
					m_bNextStimIsOn=true;
				}
			}

			m_f64Lastsample=l_pData[(m_ui64ChannelIndex * m_ui64SamplesPerChannel) +m_ui64SamplesPerChannel-1]; // we save the last sample of the buffer to compare it to the first one of the next buffer
			m_pStimulationEncoder->process(OVP_GD_Algorithm_StimulationStreamEncoder_InputTriggerId_EncodeBuffer);
			l_rDynamicBoxContext.markOutputAsReadyToSend(0, l_ui64StartTime, l_ui64EndTime);
		}

		// if we received the End
		if(m_pStreamedMatrixDecoder->isOutputTriggerActive(OVP_GD_Algorithm_StreamedMatrixStreamDecoder_OutputTriggerId_ReceivedEnd))
		{
			// we send the End signal to the stimulation output:
			m_pStimulationEncoder->process(OVP_GD_Algorithm_StimulationStreamEncoder_InputTriggerId_EncodeEnd);
		}

		// The stream matrix chunk i has been process
		l_rDynamicBoxContext.markInputAsDeprecated(0, i);
	}

	return true;
}
<|endoftext|>


<commit_before>/*
/*
* Copyright (c) 2003-2008 Rony Shapiro <ronys@users.sourceforge.net>.
* All rights reserved. Use of the code is allowed under the
* Artistic License 2.0 terms, as specified in the LICENSE file
* distributed with this code, or available from
* http://www.opensource.org/licenses/artistic-license-2.0.php
*/

// PWFiltersDlg.cpp : implementation file
//

#include <algorithm>

#include "../stdafx.h"
#include "../ThisMfcApp.h"
#include "PWFiltersDlg.h"

#include "../resource2.h"
#include "../resource3.h"
#include "../corelib/corelib.h"
#include "../corelib/PWSFilters.h"

// CPWFiltersDlg dialog

IMPLEMENT_DYNAMIC(CPWFiltersDlg, CPWResizeDialog)

CPWFiltersDlg::CPWFiltersDlg(CWnd* pParent /*=NULL*/,
                             const FilterType &filtertype /*=DFTYPE_MAIN*/,
                             const CString &filtername /*=_T("")*/)
  : CPWResizeDialog(CPWFiltersDlg::IDD, pParent),
  m_numfilters(0), m_iType(filtertype), m_hAccel(NULL),
  m_bStopChange(false), m_filtername(filtername)
{
}

CPWFiltersDlg::~CPWFiltersDlg()
{
}

BOOL CPWFiltersDlg::OnInitDialog()
{
  std::vector<UINT> vibottombtns;
  UINT main_bns[] = {IDC_APPLY, IDOK, IDCANCEL, ID_HELP};
  UINT other_bns[] = {IDOK, IDCANCEL, ID_HELP};

  if (m_iType == DFTYPE_MAIN)
    vibottombtns.assign(main_bns,
                        main_bns + sizeof(main_bns)/sizeof(main_bns[0]));
  else
    vibottombtns.assign(other_bns,
                        other_bns + sizeof(other_bns)/sizeof(other_bns[0]));

  AddMainCtrlID(IDC_FILTERLC);
  AddBtnsCtrlIDs(vibottombtns);

  UINT statustext[1] = {IDS_BLANK};
  SetStatusBar(&statustext[0], 1);

  CPWResizeDialog::OnInitDialog();

  SetWindowText(m_cstitle);

  m_hAccel = ::LoadAccelerators(AfxGetResourceHandle(),
                                MAKEINTRESOURCE(IDR_FILTERACCELERATOR));

  DWORD dwExStyle = m_FilterLC.GetExtendedStyle();
  dwExStyle |= LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES;
  m_FilterLC.SetExtendedStyle(dwExStyle);

  m_FilterLC.Init(this, m_pfilters, m_iType);
  if (m_filtername.IsEmpty() || m_pfilters->fname.IsEmpty())
    m_filtername = _T("Filter1");
  else
    m_filtername = m_pfilters->fname;

  m_bStopChange = true;

  CRect rect;
  // Move over History & Policy dialogs so as not to obscure main filter dialog
  if (m_iType != DFTYPE_MAIN) {
    GetParent()->GetWindowRect(&rect);
    SetWindowPos(NULL, rect.left + rect.Width() / 3, rect.top + rect.Width() / 3,
                 0, 0, SWP_NOSIZE | SWP_NOZORDER);
  }

  // Main window has Apply, OK, Cancel buttons
  // History/Policy have OK, Cancel, Help buttons
  // but only one dialog definition in resource file for both in
  // order to ensure that the dialogs look exactly the same and also
  // easier to maintain
  if (m_iType != DFTYPE_MAIN) {
    // Change buttons if History/Policy dialog
    GetDlgItem(IDC_APPLY)->EnableWindow(FALSE);
    GetDlgItem(IDC_APPLY)->ShowWindow(SW_HIDE);
    GetDlgItem(IDC_FILTERNAME)->EnableWindow(FALSE);
  }

  int itotalwidth = 0;
  for (int i = 0; i < FLC_NUM_COLUMNS; i++) {
    itotalwidth += m_FilterLC.GetColumnWidth(i);
  }

  int iMaxWidth = itotalwidth + 16;
  int iMaxHeight = 1024;
  SetMaxHeightWidth(iMaxHeight, iMaxWidth);

  // Update dialog window text
  UpdateStatusText();
  UpdateData(FALSE);

  if (m_iType == DFTYPE_MAIN)
    GetDlgItem(IDC_APPLY)->EnableWindow(m_pfilters->num_Mactive == 0 ? FALSE : TRUE);

  return FALSE;
}

void CPWFiltersDlg::DoDataExchange(CDataExchange* pDX)
{
  CPWResizeDialog::DoDataExchange(pDX);

  //{{AFX_DATA_MAP(CPWFiltersDlg)
  DDX_Text(pDX, IDC_FILTERNAME, m_filtername);
  DDX_Control(pDX, IDC_FILTERNAME, m_FNameEdit);
  DDX_Control(pDX, IDC_FILTERLC, m_FilterLC);
  DDV_MaxChars(pDX, m_filtername, 32);
  //{{AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CPWFiltersDlg, CPWResizeDialog)
  ON_WM_SIZE()
  ON_BN_CLICKED(IDOK, OnOk)
  ON_EN_KILLFOCUS(IDC_FILTERNAME, OnFNameKillFocus)
  ON_NOTIFY(HDN_BEGINTRACK, IDC_FILTERLC_HEADER, OnBeginTrack)
  ON_NOTIFY(HDN_ITEMCHANGING, IDC_FILTERLC_HEADER, OnItemchanging)
  ON_COMMAND_RANGE(ID_FLC_CRITERIA, ID_FLC_NEXT, OnProcessKey)
END_MESSAGE_MAP()

void CPWFiltersDlg::OnOk()
{
  if (UpdateData(TRUE) == FALSE)
    return;

  if (m_iType == DFTYPE_MAIN && m_filtername.IsEmpty()) {
    AfxMessageBox(IDS_FILTERNAMEEMPTY);
    return;
  }

  if (!VerifyFilters())
    return;

  if (m_iType == DFTYPE_MAIN)
    m_pfilters->fname = m_filtername;

  CPWResizeDialog::OnOK();
}

// Small validation functor for finding
// first row that fails validation
struct FilterValidator
{
  FilterValidator(CString &text, int &iHistory, int &iPolicy)
    : i(0), text(text), iHistory(iHistory), iPolicy(iPolicy) {}
  bool operator()(const st_FilterData &st_fldata) {
    // return true if FAILS validation, so that find_if will
    // "find" it.
    if (st_fldata.bFilterActive &&
        (st_fldata.mtype != PWSMatch::MT_PWHIST &&
         st_fldata.mtype != PWSMatch::MT_POLICY) &&
        (st_fldata.mtype == PWSMatch::MT_INVALID ||
         st_fldata.rule == PWSMatch::MR_INVALID)) {
      text.Format(IDS_FILTERINCOMPLETE, i + 1);
      return true;
    }
    if (st_fldata.mtype == PWSMatch::MT_PWHIST)
      iHistory = i;
    if (st_fldata.mtype == PWSMatch::MT_POLICY)
      iPolicy = i;
    i++;
    return false;
  }
private:
  int i;
  int &iHistory;
  int &iPolicy;
  CString &text;
};

bool CPWFiltersDlg::VerifyFilters()
{
  // Verify that the active filters have a criterion set
  if (UpdateData(TRUE) == FALSE)
    return false;

  // First non-History/non-Policy filters on the main filter dialog
  vfilterdata *pvfilterdata(NULL);
  switch (m_iType) {
  case DFTYPE_MAIN:
    pvfilterdata = &m_pfilters->vMfldata;
    break;
  case DFTYPE_PWHISTORY:
    pvfilterdata = &m_pfilters->vHfldata;
      break;
    case DFTYPE_PWPOLICY:
      pvfilterdata = &m_pfilters->vPfldata;
      break;
    default:
      VERIFY(0);
  }

  CString cs_text;
  int iHistory(-1), iPolicy(-1);
  FilterValidator fv(cs_text, iHistory, iPolicy);
  if (find_if(pvfilterdata->begin(), pvfilterdata->end(), fv) !=
      pvfilterdata->end()) {
      AfxMessageBox(cs_text);
      return false;
    }
  if (m_iType == DFTYPE_MAIN) {
    // Now check that the filters were correct on
    // History/Policy sub-filter dialogs
    if (m_FilterLC.IsPWHIST_Set() && !m_FilterLC.IsHistoryGood()) {
      cs_text.Format(IDS_FILTERINCOMPLETE, iHistory + 1);
      AfxMessageBox(cs_text);
      return false;
    }
    if (m_FilterLC.IsPOLICY_Set() && !m_FilterLC.IsPolicyGood()) {
      cs_text.Format(IDS_FILTERINCOMPLETE, iPolicy + 1);
      AfxMessageBox(cs_text);
      return false;
    }
  }

  return true;
}

void CPWFiltersDlg::OnHelp()
{
  CString cs_HelpTopic;
  cs_HelpTopic = app.GetHelpFileName() + _T("::/html/filters.html");
  HtmlHelp(DWORD_PTR((LPCTSTR)cs_HelpTopic), HH_DISPLAY_TOPIC);
}

void CPWFiltersDlg::OnFNameKillFocus()
{
  // Make sure the filter name is updated
  UpdateData(TRUE);
}

void CPWFiltersDlg::UpdateStatusText()
{
  // Update the status bar with number of filters defined and number active
  if (!IsStatusBarOK())
    return;

  CString s;
  int numfilters;

  switch (m_iType) {
    case DFTYPE_MAIN:
      numfilters = (int)m_pfilters->vMfldata.size();
      if (numfilters == 0)
        s.LoadString(IDS_FILTERINFO_NONE);
      else {
        int numactive = m_pfilters->num_Mactive;
        // Make totla number correct (i.e. if Policy on main filter
        // and 2 of these set then total is 2 not "1 + 2"
        if (m_FilterLC.IsPWHIST_Set() && m_FilterLC.IsHistoryGood()) {
          numactive += (m_pfilters->num_Hactive - 1);
          numfilters += ((int)m_pfilters->vHfldata.size() - 1);
        }
        if (m_FilterLC.IsPOLICY_Set() && m_FilterLC.IsPolicyGood()) {
          numactive += (m_pfilters->num_Pactive - 1);
          numfilters += ((int)m_pfilters->vPfldata.size() - 1);
        }
        s.Format(IDS_FILTERINFO, numfilters, numactive);
      }
      break;
    case DFTYPE_PWHISTORY:
      s.Format(IDS_HFILTERINFO, (int)m_pfilters->vHfldata.size(), m_pfilters->num_Hactive);
      break;
    case DFTYPE_PWPOLICY:
      s.Format(IDS_PFILTERINFO, (int)m_pfilters->vPfldata.size(), m_pfilters->num_Pactive);
      break;
    default:
      ASSERT(0);
  }

  m_statusBar.SetPaneText(0, s, TRUE);
  m_statusBar.SetPaneInfo(0, m_statusBar.GetItemID(0), SBPS_STRETCH, NULL);
  m_statusBar.UpdateWindow();
}

void CPWFiltersDlg::OnBeginTrack(NMHDR * /*pNotifyStruct*/, LRESULT* pResult)
{
  // Don't allow user to change the size of any columns!
  *pResult = TRUE;
}

void CPWFiltersDlg::OnItemchanging(NMHDR * pNotifyStruct, LRESULT* pResult)
{
  NMHEADER *pHdr = reinterpret_cast<NMHEADER *>(pNotifyStruct);

  // Only allow last column to resize - it has variable data (filter description)
  if (pHdr->iItem == FLC_CRITERIA_TEXT)
    *pResult = FALSE;
  else
    *pResult = m_bStopChange ? TRUE : FALSE;
}

BOOL CPWFiltersDlg::PreTranslateMessage(MSG* pMsg)
{
  // CListCtrl accelerator processing
  if (pMsg->hwnd == m_FilterLC.m_hWnd &&
      pMsg->message >= WM_KEYFIRST &&
      pMsg->message <= WM_KEYLAST) {
    if (m_hAccel && ::TranslateAccelerator(m_hWnd, m_hAccel, pMsg))
      return TRUE;

    // Listctrl Up & Down arrows are only processed by me via Accelerator table
    if (pMsg->wParam == VK_UP || pMsg->wParam == VK_DOWN)
      return TRUE;
  }

  // Escape does not close dialog
  if (pMsg->message == WM_KEYDOWN) {
    if (pMsg->wParam == VK_ESCAPE || pMsg->wParam == VK_CANCEL)
      return TRUE;
  }

  // Make sure ComboBox messages go to ComboBox
  if (m_FilterLC.m_pComboBox &&
      pMsg->hwnd == m_FilterLC.m_pComboBox->m_hWnd)
    return CWnd::PreTranslateMessage(pMsg);

  // Otherwise - give to the Dialog!
  return CPWResizeDialog::PreTranslateMessage(pMsg);
}

void CPWFiltersDlg::OnProcessKey(UINT nID)
{
  switch (nID) {
    case ID_FLC_CRITERIA:
    case ID_FLC_ENABLE:
    case ID_FLC_FIELD:
    case ID_FLC_LOGIC:
    case ID_FLC_SELECT:
    case ID_FLC_DELETE:
    case ID_FLC_INSERT:
    case ID_FLC_PREVIOUS:
    case ID_FLC_NEXT:
      m_FilterLC.OnProcessKey(nID);
      break;
    default:
      ASSERT(0);
  }
}

void CPWFiltersDlg::UpdateDialogMaxWidth()
{
  int itotalwidth = 0;
  for (int i = 0; i < FLC_NUM_COLUMNS - 1; i++) {
    m_FilterLC.SetColumnWidth(i, LVSCW_AUTOSIZE);
    int iw1 =  m_FilterLC.GetColumnWidth(i);
    m_FilterLC.SetColumnWidth(i, LVSCW_AUTOSIZE_USEHEADER);
    int iw2 =  m_FilterLC.GetColumnWidth(i);
    m_FilterLC.SetColumnWidth(i, max(iw1, iw2));
    itotalwidth += max(iw1, iw2);
  }

  m_FilterLC.SetColumnWidth(FLC_NUM_COLUMNS - 1, LVSCW_AUTOSIZE_USEHEADER);
  itotalwidth += m_FilterLC.GetColumnWidth(FLC_NUM_COLUMNS - 1);

  int iMaxWidth = itotalwidth + 32;
  int iMaxHeight = 1024;
  SetMaxHeightWidth(iMaxHeight, iMaxWidth);
}

void CPWFiltersDlg::OnSize(UINT nType, int cx, int cy)
{
  CPWResizeDialog::OnSize(nType, cx, cy);

  if (!IsWindow(m_FilterLC.GetSafeHwnd()))
    return;

  // As main control is a CListCtrl, need to do this on the last column
  m_FilterLC.SetColumnWidth(FLC_NUM_COLUMNS - 1, LVSCW_AUTOSIZE_USEHEADER);
}
<commit_msg>Filter dbox help now works.<commit_after>/*
/*
* Copyright (c) 2003-2008 Rony Shapiro <ronys@users.sourceforge.net>.
* All rights reserved. Use of the code is allowed under the
* Artistic License 2.0 terms, as specified in the LICENSE file
* distributed with this code, or available from
* http://www.opensource.org/licenses/artistic-license-2.0.php
*/

// PWFiltersDlg.cpp : implementation file
//

#include <algorithm>

#include "../stdafx.h"
#include "../ThisMfcApp.h"
#include "PWFiltersDlg.h"

#include "../resource2.h"
#include "../resource3.h"
#include "../corelib/corelib.h"
#include "../corelib/PWSFilters.h"

// CPWFiltersDlg dialog

IMPLEMENT_DYNAMIC(CPWFiltersDlg, CPWResizeDialog)

CPWFiltersDlg::CPWFiltersDlg(CWnd* pParent /*=NULL*/,
                             const FilterType &filtertype /*=DFTYPE_MAIN*/,
                             const CString &filtername /*=_T("")*/)
  : CPWResizeDialog(CPWFiltersDlg::IDD, pParent),
  m_numfilters(0), m_iType(filtertype), m_hAccel(NULL),
  m_bStopChange(false), m_filtername(filtername)
{
}

CPWFiltersDlg::~CPWFiltersDlg()
{
}

BOOL CPWFiltersDlg::OnInitDialog()
{
  std::vector<UINT> vibottombtns;
  UINT main_bns[] = {IDC_APPLY, IDOK, IDCANCEL, ID_HELP};
  UINT other_bns[] = {IDOK, IDCANCEL, ID_HELP};

  if (m_iType == DFTYPE_MAIN)
    vibottombtns.assign(main_bns,
                        main_bns + sizeof(main_bns)/sizeof(main_bns[0]));
  else
    vibottombtns.assign(other_bns,
                        other_bns + sizeof(other_bns)/sizeof(other_bns[0]));

  AddMainCtrlID(IDC_FILTERLC);
  AddBtnsCtrlIDs(vibottombtns);

  UINT statustext[1] = {IDS_BLANK};
  SetStatusBar(&statustext[0], 1);

  CPWResizeDialog::OnInitDialog();

  SetWindowText(m_cstitle);

  m_hAccel = ::LoadAccelerators(AfxGetResourceHandle(),
                                MAKEINTRESOURCE(IDR_FILTERACCELERATOR));

  DWORD dwExStyle = m_FilterLC.GetExtendedStyle();
  dwExStyle |= LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES;
  m_FilterLC.SetExtendedStyle(dwExStyle);

  m_FilterLC.Init(this, m_pfilters, m_iType);
  if (m_filtername.IsEmpty() || m_pfilters->fname.IsEmpty())
    m_filtername = _T("Filter1");
  else
    m_filtername = m_pfilters->fname;

  m_bStopChange = true;

  CRect rect;
  // Move over History & Policy dialogs so as not to obscure main filter dialog
  if (m_iType != DFTYPE_MAIN) {
    GetParent()->GetWindowRect(&rect);
    SetWindowPos(NULL, rect.left + rect.Width() / 3, rect.top + rect.Width() / 3,
                 0, 0, SWP_NOSIZE | SWP_NOZORDER);
  }

  // Main window has Apply, OK, Cancel buttons
  // History/Policy have OK, Cancel, Help buttons
  // but only one dialog definition in resource file for both in
  // order to ensure that the dialogs look exactly the same and also
  // easier to maintain
  if (m_iType != DFTYPE_MAIN) {
    // Change buttons if History/Policy dialog
    GetDlgItem(IDC_APPLY)->EnableWindow(FALSE);
    GetDlgItem(IDC_APPLY)->ShowWindow(SW_HIDE);
    GetDlgItem(IDC_FILTERNAME)->EnableWindow(FALSE);
  }

  int itotalwidth = 0;
  for (int i = 0; i < FLC_NUM_COLUMNS; i++) {
    itotalwidth += m_FilterLC.GetColumnWidth(i);
  }

  int iMaxWidth = itotalwidth + 16;
  int iMaxHeight = 1024;
  SetMaxHeightWidth(iMaxHeight, iMaxWidth);

  // Update dialog window text
  UpdateStatusText();
  UpdateData(FALSE);

  if (m_iType == DFTYPE_MAIN)
    GetDlgItem(IDC_APPLY)->EnableWindow(m_pfilters->num_Mactive == 0 ? FALSE : TRUE);

  return FALSE;
}

void CPWFiltersDlg::DoDataExchange(CDataExchange* pDX)
{
  CPWResizeDialog::DoDataExchange(pDX);

  //{{AFX_DATA_MAP(CPWFiltersDlg)
  DDX_Text(pDX, IDC_FILTERNAME, m_filtername);
  DDX_Control(pDX, IDC_FILTERNAME, m_FNameEdit);
  DDX_Control(pDX, IDC_FILTERLC, m_FilterLC);
  DDV_MaxChars(pDX, m_filtername, 32);
  //{{AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CPWFiltersDlg, CPWResizeDialog)
  ON_WM_SIZE()
  ON_BN_CLICKED(IDOK, OnOk)
  ON_COMMAND(ID_HELP, OnHelp)
  ON_EN_KILLFOCUS(IDC_FILTERNAME, OnFNameKillFocus)
  ON_NOTIFY(HDN_BEGINTRACK, IDC_FILTERLC_HEADER, OnBeginTrack)
  ON_NOTIFY(HDN_ITEMCHANGING, IDC_FILTERLC_HEADER, OnItemchanging)
  ON_COMMAND_RANGE(ID_FLC_CRITERIA, ID_FLC_NEXT, OnProcessKey)
END_MESSAGE_MAP()

void CPWFiltersDlg::OnOk()
{
  if (UpdateData(TRUE) == FALSE)
    return;

  if (m_iType == DFTYPE_MAIN && m_filtername.IsEmpty()) {
    AfxMessageBox(IDS_FILTERNAMEEMPTY);
    return;
  }

  if (!VerifyFilters())
    return;

  if (m_iType == DFTYPE_MAIN)
    m_pfilters->fname = m_filtername;

  CPWResizeDialog::OnOK();
}

// Small validation functor for finding
// first row that fails validation
struct FilterValidator
{
  FilterValidator(CString &text, int &iHistory, int &iPolicy)
    : i(0), text(text), iHistory(iHistory), iPolicy(iPolicy) {}
  bool operator()(const st_FilterData &st_fldata) {
    // return true if FAILS validation, so that find_if will
    // "find" it.
    if (st_fldata.bFilterActive &&
        (st_fldata.mtype != PWSMatch::MT_PWHIST &&
         st_fldata.mtype != PWSMatch::MT_POLICY) &&
        (st_fldata.mtype == PWSMatch::MT_INVALID ||
         st_fldata.rule == PWSMatch::MR_INVALID)) {
      text.Format(IDS_FILTERINCOMPLETE, i + 1);
      return true;
    }
    if (st_fldata.mtype == PWSMatch::MT_PWHIST)
      iHistory = i;
    if (st_fldata.mtype == PWSMatch::MT_POLICY)
      iPolicy = i;
    i++;
    return false;
  }
private:
  int i;
  int &iHistory;
  int &iPolicy;
  CString &text;
};

bool CPWFiltersDlg::VerifyFilters()
{
  // Verify that the active filters have a criterion set
  if (UpdateData(TRUE) == FALSE)
    return false;

  // First non-History/non-Policy filters on the main filter dialog
  vfilterdata *pvfilterdata(NULL);
  switch (m_iType) {
  case DFTYPE_MAIN:
    pvfilterdata = &m_pfilters->vMfldata;
    break;
  case DFTYPE_PWHISTORY:
    pvfilterdata = &m_pfilters->vHfldata;
      break;
    case DFTYPE_PWPOLICY:
      pvfilterdata = &m_pfilters->vPfldata;
      break;
    default:
      VERIFY(0);
  }

  CString cs_text;
  int iHistory(-1), iPolicy(-1);
  FilterValidator fv(cs_text, iHistory, iPolicy);
  if (find_if(pvfilterdata->begin(), pvfilterdata->end(), fv) !=
      pvfilterdata->end()) {
      AfxMessageBox(cs_text);
      return false;
    }
  if (m_iType == DFTYPE_MAIN) {
    // Now check that the filters were correct on
    // History/Policy sub-filter dialogs
    if (m_FilterLC.IsPWHIST_Set() && !m_FilterLC.IsHistoryGood()) {
      cs_text.Format(IDS_FILTERINCOMPLETE, iHistory + 1);
      AfxMessageBox(cs_text);
      return false;
    }
    if (m_FilterLC.IsPOLICY_Set() && !m_FilterLC.IsPolicyGood()) {
      cs_text.Format(IDS_FILTERINCOMPLETE, iPolicy + 1);
      AfxMessageBox(cs_text);
      return false;
    }
  }

  return true;
}

void CPWFiltersDlg::OnHelp()
{
  CString cs_HelpTopic;
  cs_HelpTopic = app.GetHelpFileName() + _T("::/html/filters.html");
  HtmlHelp(DWORD_PTR((LPCTSTR)cs_HelpTopic), HH_DISPLAY_TOPIC);
}

void CPWFiltersDlg::OnFNameKillFocus()
{
  // Make sure the filter name is updated
  UpdateData(TRUE);
}

void CPWFiltersDlg::UpdateStatusText()
{
  // Update the status bar with number of filters defined and number active
  if (!IsStatusBarOK())
    return;

  CString s;
  int numfilters;

  switch (m_iType) {
    case DFTYPE_MAIN:
      numfilters = (int)m_pfilters->vMfldata.size();
      if (numfilters == 0)
        s.LoadString(IDS_FILTERINFO_NONE);
      else {
        int numactive = m_pfilters->num_Mactive;
        // Make totla number correct (i.e. if Policy on main filter
        // and 2 of these set then total is 2 not "1 + 2"
        if (m_FilterLC.IsPWHIST_Set() && m_FilterLC.IsHistoryGood()) {
          numactive += (m_pfilters->num_Hactive - 1);
          numfilters += ((int)m_pfilters->vHfldata.size() - 1);
        }
        if (m_FilterLC.IsPOLICY_Set() && m_FilterLC.IsPolicyGood()) {
          numactive += (m_pfilters->num_Pactive - 1);
          numfilters += ((int)m_pfilters->vPfldata.size() - 1);
        }
        s.Format(IDS_FILTERINFO, numfilters, numactive);
      }
      break;
    case DFTYPE_PWHISTORY:
      s.Format(IDS_HFILTERINFO, (int)m_pfilters->vHfldata.size(), m_pfilters->num_Hactive);
      break;
    case DFTYPE_PWPOLICY:
      s.Format(IDS_PFILTERINFO, (int)m_pfilters->vPfldata.size(), m_pfilters->num_Pactive);
      break;
    default:
      ASSERT(0);
  }

  m_statusBar.SetPaneText(0, s, TRUE);
  m_statusBar.SetPaneInfo(0, m_statusBar.GetItemID(0), SBPS_STRETCH, NULL);
  m_statusBar.UpdateWindow();
}

void CPWFiltersDlg::OnBeginTrack(NMHDR * /*pNotifyStruct*/, LRESULT* pResult)
{
  // Don't allow user to change the size of any columns!
  *pResult = TRUE;
}

void CPWFiltersDlg::OnItemchanging(NMHDR * pNotifyStruct, LRESULT* pResult)
{
  NMHEADER *pHdr = reinterpret_cast<NMHEADER *>(pNotifyStruct);

  // Only allow last column to resize - it has variable data (filter description)
  if (pHdr->iItem == FLC_CRITERIA_TEXT)
    *pResult = FALSE;
  else
    *pResult = m_bStopChange ? TRUE : FALSE;
}

BOOL CPWFiltersDlg::PreTranslateMessage(MSG* pMsg)
{
  // CListCtrl accelerator processing
  if (pMsg->hwnd == m_FilterLC.m_hWnd &&
      pMsg->message >= WM_KEYFIRST &&
      pMsg->message <= WM_KEYLAST) {
    if (m_hAccel && ::TranslateAccelerator(m_hWnd, m_hAccel, pMsg))
      return TRUE;

    // Listctrl Up & Down arrows are only processed by me via Accelerator table
    if (pMsg->wParam == VK_UP || pMsg->wParam == VK_DOWN)
      return TRUE;
  }

  // Escape does not close dialog
  if (pMsg->message == WM_KEYDOWN) {
    if (pMsg->wParam == VK_ESCAPE || pMsg->wParam == VK_CANCEL)
      return TRUE;
  }

  // Make sure ComboBox messages go to ComboBox
  if (m_FilterLC.m_pComboBox &&
      pMsg->hwnd == m_FilterLC.m_pComboBox->m_hWnd)
    return CWnd::PreTranslateMessage(pMsg);

  // Otherwise - give to the Dialog!
  return CPWResizeDialog::PreTranslateMessage(pMsg);
}

void CPWFiltersDlg::OnProcessKey(UINT nID)
{
  switch (nID) {
    case ID_FLC_CRITERIA:
    case ID_FLC_ENABLE:
    case ID_FLC_FIELD:
    case ID_FLC_LOGIC:
    case ID_FLC_SELECT:
    case ID_FLC_DELETE:
    case ID_FLC_INSERT:
    case ID_FLC_PREVIOUS:
    case ID_FLC_NEXT:
      m_FilterLC.OnProcessKey(nID);
      break;
    default:
      ASSERT(0);
  }
}

void CPWFiltersDlg::UpdateDialogMaxWidth()
{
  int itotalwidth = 0;
  for (int i = 0; i < FLC_NUM_COLUMNS - 1; i++) {
    m_FilterLC.SetColumnWidth(i, LVSCW_AUTOSIZE);
    int iw1 =  m_FilterLC.GetColumnWidth(i);
    m_FilterLC.SetColumnWidth(i, LVSCW_AUTOSIZE_USEHEADER);
    int iw2 =  m_FilterLC.GetColumnWidth(i);
    m_FilterLC.SetColumnWidth(i, max(iw1, iw2));
    itotalwidth += max(iw1, iw2);
  }

  m_FilterLC.SetColumnWidth(FLC_NUM_COLUMNS - 1, LVSCW_AUTOSIZE_USEHEADER);
  itotalwidth += m_FilterLC.GetColumnWidth(FLC_NUM_COLUMNS - 1);

  int iMaxWidth = itotalwidth + 32;
  int iMaxHeight = 1024;
  SetMaxHeightWidth(iMaxHeight, iMaxWidth);
}

void CPWFiltersDlg::OnSize(UINT nType, int cx, int cy)
{
  CPWResizeDialog::OnSize(nType, cx, cy);

  if (!IsWindow(m_FilterLC.GetSafeHwnd()))
    return;

  // As main control is a CListCtrl, need to do this on the last column
  m_FilterLC.SetColumnWidth(FLC_NUM_COLUMNS - 1, LVSCW_AUTOSIZE_USEHEADER);
}
<|endoftext|>


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

#include "ui/views/controls/scrollbar/native_scroll_bar_views.h"

#include "base/logging.h"
#include "ui/base/keycodes/keyboard_codes.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/path.h"
#include "ui/views/controls/button/custom_button.h"
#include "ui/views/controls/focusable_border.h"
#include "ui/views/controls/scrollbar/base_scroll_bar_button.h"
#include "ui/views/controls/scrollbar/base_scroll_bar_thumb.h"
#include "ui/views/controls/scrollbar/native_scroll_bar.h"
#include "ui/views/controls/scrollbar/scroll_bar.h"

namespace views {

namespace {

// Wrapper for the scroll buttons.
class ScrollBarButton : public BaseScrollBarButton {
 public:
  enum Type {
    UP,
    DOWN,
    LEFT,
    RIGHT,
  };

  ScrollBarButton(ButtonListener* listener, Type type);
  virtual ~ScrollBarButton();

  virtual gfx::Size GetPreferredSize() OVERRIDE;
  virtual std::string GetClassName() const OVERRIDE {
    return "views/ScrollBarButton";
  }

 protected:
  virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE;

 private:
  ui::NativeTheme::ExtraParams GetNativeThemeParams() const;
  ui::NativeTheme::Part GetNativeThemePart() const;
  ui::NativeTheme::State GetNativeThemeState() const;

  Type type_;
};

// Wrapper for the scroll thumb
class ScrollBarThumb : public BaseScrollBarThumb {
 public:
  explicit ScrollBarThumb(BaseScrollBar* scroll_bar);
  virtual ~ScrollBarThumb();

  virtual gfx::Size GetPreferredSize() OVERRIDE;
  virtual std::string GetClassName() const OVERRIDE {
    return "views/ScrollBarThumb";
  }

 protected:
  virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE;

 private:
  ui::NativeTheme::ExtraParams GetNativeThemeParams() const;
  ui::NativeTheme::Part GetNativeThemePart() const;
  ui::NativeTheme::State GetNativeThemeState() const;

  ScrollBar* scroll_bar_;
};

/////////////////////////////////////////////////////////////////////////////
// ScrollBarButton

ScrollBarButton::ScrollBarButton(
    ButtonListener* listener,
    Type type)
    : BaseScrollBarButton(listener),
      type_(type) {
  set_focusable(false);
  set_accessibility_focusable(false);
}

ScrollBarButton::~ScrollBarButton() {
}

gfx::Size ScrollBarButton::GetPreferredSize() {
  return GetNativeTheme()->GetPartSize(GetNativeThemePart(),
                                       GetNativeThemeState(),
                                       GetNativeThemeParams());
}

void ScrollBarButton::OnPaint(gfx::Canvas* canvas) {
  gfx::Rect bounds(GetPreferredSize());
  GetNativeTheme()->Paint(canvas->sk_canvas(), GetNativeThemePart(),
                          GetNativeThemeState(), bounds,
                          GetNativeThemeParams());
}

ui::NativeTheme::ExtraParams
    ScrollBarButton::GetNativeThemeParams() const {
  ui::NativeTheme::ExtraParams params;

  switch (state_) {
    case CustomButton::STATE_HOVERED:
      params.scrollbar_arrow.is_hovering = true;
      break;
    default:
      params.scrollbar_arrow.is_hovering = false;
      break;
  }

  return params;
}

ui::NativeTheme::Part
    ScrollBarButton::GetNativeThemePart() const {
  switch (type_) {
    case UP:
      return ui::NativeTheme::kScrollbarUpArrow;
    case DOWN:
      return ui::NativeTheme::kScrollbarDownArrow;
    case LEFT:
      return ui::NativeTheme::kScrollbarLeftArrow;
    case RIGHT:
      return ui::NativeTheme::kScrollbarRightArrow;
    default:
      return ui::NativeTheme::kScrollbarUpArrow;
  }
}

ui::NativeTheme::State
    ScrollBarButton::GetNativeThemeState() const {
  ui::NativeTheme::State state;

  switch (state_) {
    case CustomButton::STATE_HOVERED:
      state = ui::NativeTheme::kHovered;
      break;
    case CustomButton::STATE_PRESSED:
      state = ui::NativeTheme::kPressed;
      break;
    case CustomButton::STATE_DISABLED:
      state = ui::NativeTheme::kDisabled;
      break;
    case CustomButton::STATE_NORMAL:
    default:
      state = ui::NativeTheme::kNormal;
      break;
  }

  return state;
}

/////////////////////////////////////////////////////////////////////////////
// ScrollBarThumb

ScrollBarThumb::ScrollBarThumb(BaseScrollBar* scroll_bar)
    : BaseScrollBarThumb(scroll_bar),
      scroll_bar_(scroll_bar) {
  set_focusable(false);
  set_accessibility_focusable(false);
}

ScrollBarThumb::~ScrollBarThumb() {
}

gfx::Size ScrollBarThumb::GetPreferredSize() {
  return GetNativeTheme()->GetPartSize(GetNativeThemePart(),
                                       GetNativeThemeState(),
                                       GetNativeThemeParams());
}

void ScrollBarThumb::OnPaint(gfx::Canvas* canvas) {
  const gfx::Rect local_bounds(GetLocalBounds());
  const ui::NativeTheme::State theme_state = GetNativeThemeState();
  const ui::NativeTheme::ExtraParams extra_params(GetNativeThemeParams());
  GetNativeTheme()->Paint(canvas->sk_canvas(),
                          GetNativeThemePart(),
                          theme_state,
                          local_bounds,
                          extra_params);
  const ui::NativeTheme::Part gripper_part = scroll_bar_->IsHorizontal() ?
      ui::NativeTheme::kScrollbarHorizontalGripper :
      ui::NativeTheme::kScrollbarVerticalGripper;
  GetNativeTheme()->Paint(canvas->sk_canvas(), gripper_part, theme_state,
                          local_bounds, extra_params);
}

ui::NativeTheme::ExtraParams ScrollBarThumb::GetNativeThemeParams() const {
  // This gives the behavior we want.
  ui::NativeTheme::ExtraParams params;
  params.scrollbar_thumb.is_hovering =
      (GetState() != CustomButton::STATE_HOVERED);
  return params;
}

ui::NativeTheme::Part ScrollBarThumb::GetNativeThemePart() const {
  if (scroll_bar_->IsHorizontal())
    return ui::NativeTheme::kScrollbarHorizontalThumb;
  return ui::NativeTheme::kScrollbarVerticalThumb;
}

ui::NativeTheme::State ScrollBarThumb::GetNativeThemeState() const {
  ui::NativeTheme::State state;

  switch (GetState()) {
    case CustomButton::STATE_HOVERED:
      state = ui::NativeTheme::kHovered;
      break;
    case CustomButton::STATE_PRESSED:
      state = ui::NativeTheme::kPressed;
      break;
    case CustomButton::STATE_DISABLED:
      state = ui::NativeTheme::kDisabled;
      break;
    case CustomButton::STATE_NORMAL:
    default:
      state = ui::NativeTheme::kNormal;
      break;
  }

  return state;
}

}  // namespace

////////////////////////////////////////////////////////////////////////////////
// NativeScrollBarViews, public:

const char NativeScrollBarViews::kViewClassName[] =
    "views/NativeScrollBarViews";

NativeScrollBarViews::NativeScrollBarViews(NativeScrollBar* scroll_bar)
    : BaseScrollBar(scroll_bar->IsHorizontal(),
                    new ScrollBarThumb(this)),
      native_scroll_bar_(scroll_bar) {
  set_controller(native_scroll_bar_->controller());

  if (native_scroll_bar_->IsHorizontal()) {
    prev_button_ = new ScrollBarButton(this, ScrollBarButton::LEFT);
    next_button_ = new ScrollBarButton(this, ScrollBarButton::RIGHT);

    part_ = ui::NativeTheme::kScrollbarHorizontalTrack;
  } else {
    prev_button_ = new ScrollBarButton(this, ScrollBarButton::UP);
    next_button_ = new ScrollBarButton(this, ScrollBarButton::DOWN);

    part_ = ui::NativeTheme::kScrollbarVerticalTrack;
  }

  state_ = ui::NativeTheme::kNormal;

  AddChildView(prev_button_);
  AddChildView(next_button_);

  prev_button_->set_context_menu_controller(this);
  next_button_->set_context_menu_controller(this);
}

NativeScrollBarViews::~NativeScrollBarViews() {
}

////////////////////////////////////////////////////////////////////////////////
// NativeScrollBarViews, View overrides:

void NativeScrollBarViews::Layout() {
  gfx::Size size = prev_button_->GetPreferredSize();
  prev_button_->SetBounds(0, 0, size.width(), size.height());

  if (native_scroll_bar_->IsHorizontal()) {
    next_button_->SetBounds(width() - size.width(), 0,
                            size.width(), size.height());
  } else {
    next_button_->SetBounds(0, height() - size.height(),
                            size.width(), size.height());
  }

  GetThumb()->SetBoundsRect(GetTrackBounds());
}

void NativeScrollBarViews::OnPaint(gfx::Canvas* canvas) {
  gfx::Rect bounds = GetTrackBounds();

  if (bounds.IsEmpty())
    return;

  params_.scrollbar_track.track_x = bounds.x();
  params_.scrollbar_track.track_y = bounds.y();
  params_.scrollbar_track.track_width = bounds.width();
  params_.scrollbar_track.track_height = bounds.height();

  GetNativeTheme()->Paint(canvas->sk_canvas(), part_, state_, bounds, params_);
}

gfx::Size NativeScrollBarViews::GetPreferredSize() {
  const ui::NativeTheme* theme = native_scroll_bar_->GetNativeTheme();
  if (native_scroll_bar_->IsHorizontal())
    return gfx::Size(0, GetHorizontalScrollBarHeight(theme));
  return gfx::Size(GetVerticalScrollBarWidth(theme), 0);
}

std::string NativeScrollBarViews::GetClassName() const {
  return kViewClassName;
}

int NativeScrollBarViews::GetLayoutSize() const {
  gfx::Size size = prev_button_->GetPreferredSize();
  return IsHorizontal() ? size.height() : size.width();
}

void NativeScrollBarViews::ScrollToPosition(int position) {
  controller()->ScrollToPosition(native_scroll_bar_, position);
}

int NativeScrollBarViews::GetScrollIncrement(bool is_page, bool is_positive) {
  return controller()->GetScrollIncrement(native_scroll_bar_,
                                          is_page,
                                          is_positive);
}

//////////////////////////////////////////////////////////////////////////////
// BaseButton::ButtonListener overrides:

void NativeScrollBarViews::ButtonPressed(Button* sender,
                                         const ui::Event& event) {
  if (sender == prev_button_) {
    ScrollByAmount(SCROLL_PREV_LINE);
  } else if (sender == next_button_) {
    ScrollByAmount(SCROLL_NEXT_LINE);
  }
}

////////////////////////////////////////////////////////////////////////////////
// NativeScrollBarViews, NativeScrollBarWrapper overrides:

int NativeScrollBarViews::GetPosition() const {
  return BaseScrollBar::GetPosition();
}

View* NativeScrollBarViews::GetView() {
  return this;
}

void NativeScrollBarViews::Update(int viewport_size,
                                  int content_size,
                                  int current_pos) {
  BaseScrollBar::Update(viewport_size, content_size, current_pos);
}

////////////////////////////////////////////////////////////////////////////////
// NativeScrollBarViews, private:

gfx::Rect NativeScrollBarViews::GetTrackBounds() const {
  gfx::Rect bounds = GetLocalBounds();
  gfx::Size size = prev_button_->GetPreferredSize();
  BaseScrollBarThumb* thumb = GetThumb();

  if (native_scroll_bar_->IsHorizontal()) {
    bounds.set_x(bounds.x() + size.width());
    bounds.set_width(std::max(0, bounds.width() - 2 * size.width()));
    bounds.set_height(thumb->GetPreferredSize().height());
  } else {
    bounds.set_y(bounds.y() + size.height());
    bounds.set_height(std::max(0, bounds.height() - 2 * size.height()));
    bounds.set_width(thumb->GetPreferredSize().width());
  }

  return bounds;
}

////////////////////////////////////////////////////////////////////////////////
// NativewScrollBarWrapper, public:

// static
NativeScrollBarWrapper* NativeScrollBarWrapper::CreateWrapper(
    NativeScrollBar* scroll_bar) {
  return new NativeScrollBarViews(scroll_bar);
}

// static
int NativeScrollBarWrapper::GetHorizontalScrollBarHeight(
    const ui::NativeTheme* theme) {
  ui::NativeTheme::ExtraParams button_params;
  button_params.scrollbar_arrow.is_hovering = false;
  gfx::Size button_size = theme->GetPartSize(
      ui::NativeTheme::kScrollbarLeftArrow,
      ui::NativeTheme::kNormal,
      button_params);

  ui::NativeTheme::ExtraParams thumb_params;
  thumb_params.scrollbar_thumb.is_hovering = false;
  gfx::Size track_size = theme->GetPartSize(
      ui::NativeTheme::kScrollbarHorizontalThumb,
      ui::NativeTheme::kNormal,
      thumb_params);

  return std::max(track_size.height(), button_size.height());
}

// static
int NativeScrollBarWrapper::GetVerticalScrollBarWidth(
    const ui::NativeTheme* theme) {
  ui::NativeTheme::ExtraParams button_params;
  button_params.scrollbar_arrow.is_hovering = false;
  gfx::Size button_size = theme->GetPartSize(
      ui::NativeTheme::kScrollbarUpArrow,
      ui::NativeTheme::kNormal,
      button_params);

  ui::NativeTheme::ExtraParams thumb_params;
  thumb_params.scrollbar_thumb.is_hovering = false;
  gfx::Size track_size = theme->GetPartSize(
      ui::NativeTheme::kScrollbarVerticalThumb,
      ui::NativeTheme::kNormal,
      thumb_params);

  return std::max(track_size.width(), button_size.width());
}

}  // namespace views
<commit_msg>Add NULL check for NativeTheme in scrollbar methods. A NULL theme can happen with some code paths.<commit_after>// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ui/views/controls/scrollbar/native_scroll_bar_views.h"

#include "base/logging.h"
#include "ui/base/keycodes/keyboard_codes.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/path.h"
#include "ui/views/controls/button/custom_button.h"
#include "ui/views/controls/focusable_border.h"
#include "ui/views/controls/scrollbar/base_scroll_bar_button.h"
#include "ui/views/controls/scrollbar/base_scroll_bar_thumb.h"
#include "ui/views/controls/scrollbar/native_scroll_bar.h"
#include "ui/views/controls/scrollbar/scroll_bar.h"

namespace views {

namespace {

// Wrapper for the scroll buttons.
class ScrollBarButton : public BaseScrollBarButton {
 public:
  enum Type {
    UP,
    DOWN,
    LEFT,
    RIGHT,
  };

  ScrollBarButton(ButtonListener* listener, Type type);
  virtual ~ScrollBarButton();

  virtual gfx::Size GetPreferredSize() OVERRIDE;
  virtual std::string GetClassName() const OVERRIDE {
    return "views/ScrollBarButton";
  }

 protected:
  virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE;

 private:
  ui::NativeTheme::ExtraParams GetNativeThemeParams() const;
  ui::NativeTheme::Part GetNativeThemePart() const;
  ui::NativeTheme::State GetNativeThemeState() const;

  Type type_;
};

// Wrapper for the scroll thumb
class ScrollBarThumb : public BaseScrollBarThumb {
 public:
  explicit ScrollBarThumb(BaseScrollBar* scroll_bar);
  virtual ~ScrollBarThumb();

  virtual gfx::Size GetPreferredSize() OVERRIDE;
  virtual std::string GetClassName() const OVERRIDE {
    return "views/ScrollBarThumb";
  }

 protected:
  virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE;

 private:
  ui::NativeTheme::ExtraParams GetNativeThemeParams() const;
  ui::NativeTheme::Part GetNativeThemePart() const;
  ui::NativeTheme::State GetNativeThemeState() const;

  ScrollBar* scroll_bar_;
};

/////////////////////////////////////////////////////////////////////////////
// ScrollBarButton

ScrollBarButton::ScrollBarButton(
    ButtonListener* listener,
    Type type)
    : BaseScrollBarButton(listener),
      type_(type) {
  set_focusable(false);
  set_accessibility_focusable(false);
}

ScrollBarButton::~ScrollBarButton() {
}

gfx::Size ScrollBarButton::GetPreferredSize() {
  return GetNativeTheme()->GetPartSize(GetNativeThemePart(),
                                       GetNativeThemeState(),
                                       GetNativeThemeParams());
}

void ScrollBarButton::OnPaint(gfx::Canvas* canvas) {
  gfx::Rect bounds(GetPreferredSize());
  GetNativeTheme()->Paint(canvas->sk_canvas(), GetNativeThemePart(),
                          GetNativeThemeState(), bounds,
                          GetNativeThemeParams());
}

ui::NativeTheme::ExtraParams
    ScrollBarButton::GetNativeThemeParams() const {
  ui::NativeTheme::ExtraParams params;

  switch (state_) {
    case CustomButton::STATE_HOVERED:
      params.scrollbar_arrow.is_hovering = true;
      break;
    default:
      params.scrollbar_arrow.is_hovering = false;
      break;
  }

  return params;
}

ui::NativeTheme::Part
    ScrollBarButton::GetNativeThemePart() const {
  switch (type_) {
    case UP:
      return ui::NativeTheme::kScrollbarUpArrow;
    case DOWN:
      return ui::NativeTheme::kScrollbarDownArrow;
    case LEFT:
      return ui::NativeTheme::kScrollbarLeftArrow;
    case RIGHT:
      return ui::NativeTheme::kScrollbarRightArrow;
    default:
      return ui::NativeTheme::kScrollbarUpArrow;
  }
}

ui::NativeTheme::State
    ScrollBarButton::GetNativeThemeState() const {
  ui::NativeTheme::State state;

  switch (state_) {
    case CustomButton::STATE_HOVERED:
      state = ui::NativeTheme::kHovered;
      break;
    case CustomButton::STATE_PRESSED:
      state = ui::NativeTheme::kPressed;
      break;
    case CustomButton::STATE_DISABLED:
      state = ui::NativeTheme::kDisabled;
      break;
    case CustomButton::STATE_NORMAL:
    default:
      state = ui::NativeTheme::kNormal;
      break;
  }

  return state;
}

/////////////////////////////////////////////////////////////////////////////
// ScrollBarThumb

ScrollBarThumb::ScrollBarThumb(BaseScrollBar* scroll_bar)
    : BaseScrollBarThumb(scroll_bar),
      scroll_bar_(scroll_bar) {
  set_focusable(false);
  set_accessibility_focusable(false);
}

ScrollBarThumb::~ScrollBarThumb() {
}

gfx::Size ScrollBarThumb::GetPreferredSize() {
  return GetNativeTheme()->GetPartSize(GetNativeThemePart(),
                                       GetNativeThemeState(),
                                       GetNativeThemeParams());
}

void ScrollBarThumb::OnPaint(gfx::Canvas* canvas) {
  const gfx::Rect local_bounds(GetLocalBounds());
  const ui::NativeTheme::State theme_state = GetNativeThemeState();
  const ui::NativeTheme::ExtraParams extra_params(GetNativeThemeParams());
  GetNativeTheme()->Paint(canvas->sk_canvas(),
                          GetNativeThemePart(),
                          theme_state,
                          local_bounds,
                          extra_params);
  const ui::NativeTheme::Part gripper_part = scroll_bar_->IsHorizontal() ?
      ui::NativeTheme::kScrollbarHorizontalGripper :
      ui::NativeTheme::kScrollbarVerticalGripper;
  GetNativeTheme()->Paint(canvas->sk_canvas(), gripper_part, theme_state,
                          local_bounds, extra_params);
}

ui::NativeTheme::ExtraParams ScrollBarThumb::GetNativeThemeParams() const {
  // This gives the behavior we want.
  ui::NativeTheme::ExtraParams params;
  params.scrollbar_thumb.is_hovering =
      (GetState() != CustomButton::STATE_HOVERED);
  return params;
}

ui::NativeTheme::Part ScrollBarThumb::GetNativeThemePart() const {
  if (scroll_bar_->IsHorizontal())
    return ui::NativeTheme::kScrollbarHorizontalThumb;
  return ui::NativeTheme::kScrollbarVerticalThumb;
}

ui::NativeTheme::State ScrollBarThumb::GetNativeThemeState() const {
  ui::NativeTheme::State state;

  switch (GetState()) {
    case CustomButton::STATE_HOVERED:
      state = ui::NativeTheme::kHovered;
      break;
    case CustomButton::STATE_PRESSED:
      state = ui::NativeTheme::kPressed;
      break;
    case CustomButton::STATE_DISABLED:
      state = ui::NativeTheme::kDisabled;
      break;
    case CustomButton::STATE_NORMAL:
    default:
      state = ui::NativeTheme::kNormal;
      break;
  }

  return state;
}

}  // namespace

////////////////////////////////////////////////////////////////////////////////
// NativeScrollBarViews, public:

const char NativeScrollBarViews::kViewClassName[] =
    "views/NativeScrollBarViews";

NativeScrollBarViews::NativeScrollBarViews(NativeScrollBar* scroll_bar)
    : BaseScrollBar(scroll_bar->IsHorizontal(),
                    new ScrollBarThumb(this)),
      native_scroll_bar_(scroll_bar) {
  set_controller(native_scroll_bar_->controller());

  if (native_scroll_bar_->IsHorizontal()) {
    prev_button_ = new ScrollBarButton(this, ScrollBarButton::LEFT);
    next_button_ = new ScrollBarButton(this, ScrollBarButton::RIGHT);

    part_ = ui::NativeTheme::kScrollbarHorizontalTrack;
  } else {
    prev_button_ = new ScrollBarButton(this, ScrollBarButton::UP);
    next_button_ = new ScrollBarButton(this, ScrollBarButton::DOWN);

    part_ = ui::NativeTheme::kScrollbarVerticalTrack;
  }

  state_ = ui::NativeTheme::kNormal;

  AddChildView(prev_button_);
  AddChildView(next_button_);

  prev_button_->set_context_menu_controller(this);
  next_button_->set_context_menu_controller(this);
}

NativeScrollBarViews::~NativeScrollBarViews() {
}

////////////////////////////////////////////////////////////////////////////////
// NativeScrollBarViews, View overrides:

void NativeScrollBarViews::Layout() {
  gfx::Size size = prev_button_->GetPreferredSize();
  prev_button_->SetBounds(0, 0, size.width(), size.height());

  if (native_scroll_bar_->IsHorizontal()) {
    next_button_->SetBounds(width() - size.width(), 0,
                            size.width(), size.height());
  } else {
    next_button_->SetBounds(0, height() - size.height(),
                            size.width(), size.height());
  }

  GetThumb()->SetBoundsRect(GetTrackBounds());
}

void NativeScrollBarViews::OnPaint(gfx::Canvas* canvas) {
  gfx::Rect bounds = GetTrackBounds();

  if (bounds.IsEmpty())
    return;

  params_.scrollbar_track.track_x = bounds.x();
  params_.scrollbar_track.track_y = bounds.y();
  params_.scrollbar_track.track_width = bounds.width();
  params_.scrollbar_track.track_height = bounds.height();

  GetNativeTheme()->Paint(canvas->sk_canvas(), part_, state_, bounds, params_);
}

gfx::Size NativeScrollBarViews::GetPreferredSize() {
  const ui::NativeTheme* theme = native_scroll_bar_->GetNativeTheme();
  if (native_scroll_bar_->IsHorizontal())
    return gfx::Size(0, GetHorizontalScrollBarHeight(theme));
  return gfx::Size(GetVerticalScrollBarWidth(theme), 0);
}

std::string NativeScrollBarViews::GetClassName() const {
  return kViewClassName;
}

int NativeScrollBarViews::GetLayoutSize() const {
  gfx::Size size = prev_button_->GetPreferredSize();
  return IsHorizontal() ? size.height() : size.width();
}

void NativeScrollBarViews::ScrollToPosition(int position) {
  controller()->ScrollToPosition(native_scroll_bar_, position);
}

int NativeScrollBarViews::GetScrollIncrement(bool is_page, bool is_positive) {
  return controller()->GetScrollIncrement(native_scroll_bar_,
                                          is_page,
                                          is_positive);
}

//////////////////////////////////////////////////////////////////////////////
// BaseButton::ButtonListener overrides:

void NativeScrollBarViews::ButtonPressed(Button* sender,
                                         const ui::Event& event) {
  if (sender == prev_button_) {
    ScrollByAmount(SCROLL_PREV_LINE);
  } else if (sender == next_button_) {
    ScrollByAmount(SCROLL_NEXT_LINE);
  }
}

////////////////////////////////////////////////////////////////////////////////
// NativeScrollBarViews, NativeScrollBarWrapper overrides:

int NativeScrollBarViews::GetPosition() const {
  return BaseScrollBar::GetPosition();
}

View* NativeScrollBarViews::GetView() {
  return this;
}

void NativeScrollBarViews::Update(int viewport_size,
                                  int content_size,
                                  int current_pos) {
  BaseScrollBar::Update(viewport_size, content_size, current_pos);
}

////////////////////////////////////////////////////////////////////////////////
// NativeScrollBarViews, private:

gfx::Rect NativeScrollBarViews::GetTrackBounds() const {
  gfx::Rect bounds = GetLocalBounds();
  gfx::Size size = prev_button_->GetPreferredSize();
  BaseScrollBarThumb* thumb = GetThumb();

  if (native_scroll_bar_->IsHorizontal()) {
    bounds.set_x(bounds.x() + size.width());
    bounds.set_width(std::max(0, bounds.width() - 2 * size.width()));
    bounds.set_height(thumb->GetPreferredSize().height());
  } else {
    bounds.set_y(bounds.y() + size.height());
    bounds.set_height(std::max(0, bounds.height() - 2 * size.height()));
    bounds.set_width(thumb->GetPreferredSize().width());
  }

  return bounds;
}

////////////////////////////////////////////////////////////////////////////////
// NativewScrollBarWrapper, public:

// static
NativeScrollBarWrapper* NativeScrollBarWrapper::CreateWrapper(
    NativeScrollBar* scroll_bar) {
  return new NativeScrollBarViews(scroll_bar);
}

// static
int NativeScrollBarWrapper::GetHorizontalScrollBarHeight(
    const ui::NativeTheme* theme) {
  if (!theme)
    theme = ui::NativeTheme::instance();
  ui::NativeTheme::ExtraParams button_params;
  button_params.scrollbar_arrow.is_hovering = false;
  gfx::Size button_size = theme->GetPartSize(
      ui::NativeTheme::kScrollbarLeftArrow,
      ui::NativeTheme::kNormal,
      button_params);

  ui::NativeTheme::ExtraParams thumb_params;
  thumb_params.scrollbar_thumb.is_hovering = false;
  gfx::Size track_size = theme->GetPartSize(
      ui::NativeTheme::kScrollbarHorizontalThumb,
      ui::NativeTheme::kNormal,
      thumb_params);

  return std::max(track_size.height(), button_size.height());
}

// static
int NativeScrollBarWrapper::GetVerticalScrollBarWidth(
    const ui::NativeTheme* theme) {
  if (!theme)
    theme = ui::NativeTheme::instance();
  ui::NativeTheme::ExtraParams button_params;
  button_params.scrollbar_arrow.is_hovering = false;
  gfx::Size button_size = theme->GetPartSize(
      ui::NativeTheme::kScrollbarUpArrow,
      ui::NativeTheme::kNormal,
      button_params);

  ui::NativeTheme::ExtraParams thumb_params;
  thumb_params.scrollbar_thumb.is_hovering = false;
  gfx::Size track_size = theme->GetPartSize(
      ui::NativeTheme::kScrollbarVerticalThumb,
      ui::NativeTheme::kNormal,
      thumb_params);

  return std::max(track_size.width(), button_size.width());
}

}  // namespace views
<|endoftext|>


<commit_before>// Copyright (C) 2015 Jérôme Leclercq
// This file is part of the "Nazara Engine - Graphics module"
// For conditions of distribution and use, see copyright notice in Config.hpp

#pragma once

#ifndef NAZARA_PARTICLEGROUP_HPP
#define NAZARA_PARTICLEGROUP_HPP

#include <Nazara/Prerequesites.hpp>
#include <Nazara/Core/signal.hpp>
#include <Nazara/Graphics/ParticleController.hpp>
#include <Nazara/Graphics/ParticleDeclaration.hpp>
#include <Nazara/Graphics/ParticleEmitter.hpp>
#include <Nazara/Graphics/ParticleGenerator.hpp>
#include <Nazara/Graphics/ParticleRenderer.hpp>
#include <Nazara/Graphics/Renderable.hpp>
#include <Nazara/Math/BoundingVolume.hpp>
#include <functional>
#include <memory>
#include <set>
#include <vector>

namespace Nz
{
	class NAZARA_GRAPHICS_API ParticleGroup : public Renderable
	{
		public:
			ParticleGroup(unsigned int maxParticleCount, ParticleLayout layout);
			ParticleGroup(unsigned int maxParticleCount, ParticleDeclarationConstRef declaration);
			ParticleGroup(const ParticleGroup& emitter);
			~ParticleGroup();

			void AddController(ParticleControllerRef controller);
			void AddEmitter(ParticleEmitter* emitter);
			void AddGenerator(ParticleGeneratorRef generator);
			void AddToRenderQueue(AbstractRenderQueue* renderQueue, const Matrix4f& transformMatrix) const override;

			void ApplyControllers(ParticleMapper& mapper, unsigned int particleCount, float elapsedTime);

			void* CreateParticle();
			void* CreateParticles(unsigned int count);

			void* GenerateParticle();
			void* GenerateParticles(unsigned int count);

			const ParticleDeclarationConstRef& GetDeclaration() const;
			unsigned int GetMaxParticleCount() const;
			unsigned int GetParticleCount() const;
			unsigned int GetParticleSize() const;

			void KillParticle(unsigned int index);
			void KillParticles();

			void RemoveController(ParticleController* controller);
			void RemoveEmitter(ParticleEmitter* emitter);
			void RemoveGenerator(ParticleGenerator* generator);

			void SetRenderer(ParticleRenderer* renderer);

			void Update(float elapsedTime);
			void UpdateBoundingVolume(const Matrix4f& transformMatrix) override;

			ParticleGroup& operator=(const ParticleGroup& emitter);

			// Signals:
			NazaraSignal(OnParticleGroupRelease, const ParticleGroup* /*particleGroup*/);

		private:
			void MakeBoundingVolume() const override;
			void OnEmitterMove(ParticleEmitter* oldEmitter, ParticleEmitter* newEmitter);
			void OnEmitterRelease(const ParticleEmitter* emitter);
			void ResizeBuffer();

			struct EmitterEntry
			{
				NazaraSlot(ParticleEmitter, OnParticleEmitterMove, moveSlot);
				NazaraSlot(ParticleEmitter, OnParticleEmitterRelease, releaseSlot);

				ParticleEmitter* emitter;
			};

			std::set<unsigned int, std::greater<unsigned int>> m_dyingParticles;
			mutable std::vector<UInt8> m_buffer;
			std::vector<ParticleControllerRef> m_controllers;
			std::vector<EmitterEntry> m_emitters;
			std::vector<ParticleGeneratorRef> m_generators;
			ParticleDeclarationConstRef m_declaration;
			ParticleRendererRef m_renderer;
			bool m_processing;
			unsigned int m_maxParticleCount;
			unsigned int m_particleCount;
			unsigned int m_particleSize;
	};
}

#endif // NAZARA_PARTICLEGROUP_HPP
<commit_msg>Graphics/ParticleGroup: Fix include name<commit_after>// Copyright (C) 2015 Jérôme Leclercq
// This file is part of the "Nazara Engine - Graphics module"
// For conditions of distribution and use, see copyright notice in Config.hpp

#pragma once

#ifndef NAZARA_PARTICLEGROUP_HPP
#define NAZARA_PARTICLEGROUP_HPP

#include <Nazara/Prerequesites.hpp>
#include <Nazara/Core/Signal.hpp>
#include <Nazara/Graphics/ParticleController.hpp>
#include <Nazara/Graphics/ParticleDeclaration.hpp>
#include <Nazara/Graphics/ParticleEmitter.hpp>
#include <Nazara/Graphics/ParticleGenerator.hpp>
#include <Nazara/Graphics/ParticleRenderer.hpp>
#include <Nazara/Graphics/Renderable.hpp>
#include <Nazara/Math/BoundingVolume.hpp>
#include <functional>
#include <memory>
#include <set>
#include <vector>

namespace Nz
{
	class NAZARA_GRAPHICS_API ParticleGroup : public Renderable
	{
		public:
			ParticleGroup(unsigned int maxParticleCount, ParticleLayout layout);
			ParticleGroup(unsigned int maxParticleCount, ParticleDeclarationConstRef declaration);
			ParticleGroup(const ParticleGroup& emitter);
			~ParticleGroup();

			void AddController(ParticleControllerRef controller);
			void AddEmitter(ParticleEmitter* emitter);
			void AddGenerator(ParticleGeneratorRef generator);
			void AddToRenderQueue(AbstractRenderQueue* renderQueue, const Matrix4f& transformMatrix) const override;

			void ApplyControllers(ParticleMapper& mapper, unsigned int particleCount, float elapsedTime);

			void* CreateParticle();
			void* CreateParticles(unsigned int count);

			void* GenerateParticle();
			void* GenerateParticles(unsigned int count);

			const ParticleDeclarationConstRef& GetDeclaration() const;
			unsigned int GetMaxParticleCount() const;
			unsigned int GetParticleCount() const;
			unsigned int GetParticleSize() const;

			void KillParticle(unsigned int index);
			void KillParticles();

			void RemoveController(ParticleController* controller);
			void RemoveEmitter(ParticleEmitter* emitter);
			void RemoveGenerator(ParticleGenerator* generator);

			void SetRenderer(ParticleRenderer* renderer);

			void Update(float elapsedTime);
			void UpdateBoundingVolume(const Matrix4f& transformMatrix) override;

			ParticleGroup& operator=(const ParticleGroup& emitter);

			// Signals:
			NazaraSignal(OnParticleGroupRelease, const ParticleGroup* /*particleGroup*/);

		private:
			void MakeBoundingVolume() const override;
			void OnEmitterMove(ParticleEmitter* oldEmitter, ParticleEmitter* newEmitter);
			void OnEmitterRelease(const ParticleEmitter* emitter);
			void ResizeBuffer();

			struct EmitterEntry
			{
				NazaraSlot(ParticleEmitter, OnParticleEmitterMove, moveSlot);
				NazaraSlot(ParticleEmitter, OnParticleEmitterRelease, releaseSlot);

				ParticleEmitter* emitter;
			};

			std::set<unsigned int, std::greater<unsigned int>> m_dyingParticles;
			mutable std::vector<UInt8> m_buffer;
			std::vector<ParticleControllerRef> m_controllers;
			std::vector<EmitterEntry> m_emitters;
			std::vector<ParticleGeneratorRef> m_generators;
			ParticleDeclarationConstRef m_declaration;
			ParticleRendererRef m_renderer;
			bool m_processing;
			unsigned int m_maxParticleCount;
			unsigned int m_particleCount;
			unsigned int m_particleSize;
	};
}

#endif // NAZARA_PARTICLEGROUP_HPP
<|endoftext|>


<commit_before>/*

Copyright (c) 2007, Arvid Norberg
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 the author nor the names of its
      contributors 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.

*/

#ifndef TORRENT_INTRUSIVE_PTR_BASE
#define TORRENT_INTRUSIVE_PTR_BASE

#include <boost/detail/atomic_count.hpp>
#include "libtorrent/config.hpp"
#include "libtorrent/assert.hpp"

namespace libtorrent
{
	template<class T>
	struct intrusive_ptr_base
	{
		intrusive_ptr_base(intrusive_ptr_base<T> const&)
			: m_refs(0) {}

		intrusive_ptr_base& operator=(intrusive_ptr_base const& rhs)
		{ return *this; }

		friend void intrusive_ptr_add_ref(intrusive_ptr_base<T> const* s)
		{
			TORRENT_ASSERT(s->m_refs >= 0);
			TORRENT_ASSERT(s != 0);
			++s->m_refs;
		}

		friend void intrusive_ptr_release(intrusive_ptr_base<T> const* s)
		{
			TORRENT_ASSERT(s->m_refs > 0);
			TORRENT_ASSERT(s != 0);
			if (--s->m_refs == 0)
				delete static_cast<T const*>(s);
		}

		boost::intrusive_ptr<T> self()
		{ return boost::intrusive_ptr<T>((T*)this); }

		boost::intrusive_ptr<const T> self() const
		{ return boost::intrusive_ptr<const T>((T const*)this); }

		int refcount() const { return m_refs; }

		intrusive_ptr_base(): m_refs(0) {}
	private:
		// reference counter for intrusive_ptr
		mutable boost::detail::atomic_count m_refs;
	};

}

#endif

<commit_msg>uses checked_delete instead of plain delete<commit_after>/*

Copyright (c) 2007, Arvid Norberg
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 the author nor the names of its
      contributors 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.

*/

#ifndef TORRENT_INTRUSIVE_PTR_BASE
#define TORRENT_INTRUSIVE_PTR_BASE

#include <boost/detail/atomic_count.hpp>
#include <boost/checked_delete.hpp>
#include "libtorrent/config.hpp"
#include "libtorrent/assert.hpp"

namespace libtorrent
{
	template<class T>
	struct intrusive_ptr_base
	{
		intrusive_ptr_base(intrusive_ptr_base<T> const&)
			: m_refs(0) {}

		intrusive_ptr_base& operator=(intrusive_ptr_base const& rhs)
		{ return *this; }

		friend void intrusive_ptr_add_ref(intrusive_ptr_base<T> const* s)
		{
			TORRENT_ASSERT(s->m_refs >= 0);
			TORRENT_ASSERT(s != 0);
			++s->m_refs;
		}

		friend void intrusive_ptr_release(intrusive_ptr_base<T> const* s)
		{
			TORRENT_ASSERT(s->m_refs > 0);
			TORRENT_ASSERT(s != 0);
			if (--s->m_refs == 0)
				boost::checked_delete(static_cast<T const*>(s));
		}

		boost::intrusive_ptr<T> self()
		{ return boost::intrusive_ptr<T>((T*)this); }

		boost::intrusive_ptr<const T> self() const
		{ return boost::intrusive_ptr<const T>((T const*)this); }

		int refcount() const { return m_refs; }

		intrusive_ptr_base(): m_refs(0) {}
	private:
		// reference counter for intrusive_ptr
		mutable boost::detail::atomic_count m_refs;
	};

}

#endif

<|endoftext|>