<commit_before>// Copyright (c) 2011 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.

// NB: Modelled after Mozilla's code (originally written by Pamela Greene,
// later modified by others), but almost entirely rewritten for Chrome.
//   (netwerk/dns/src/nsEffectiveTLDService.cpp)
/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is Mozilla Effective-TLD Service
 *
 * The Initial Developer of the Original Code is
 * Google Inc.
 * Portions created by the Initial Developer are Copyright (C) 2006
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *   Pamela Greene <pamg.bugs@gmail.com> (original author)
 *   Daniel Witte <dwitte@stanford.edu>
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

#include "net/base/registry_controlled_domain.h"

#include "base/logging.h"
#include "base/memory/singleton.h"
#include "base/string_util.h"
#include "base/utf_string_conversions.h"
#include "googleurl/src/gurl.h"
#include "googleurl/src/url_parse.h"
#include "net/base/net_module.h"
#include "net/base/net_util.h"

#include "effective_tld_names.cc"

namespace net {

namespace {

const int kExceptionRule = 1;
const int kWildcardRule = 2;

}  // namespace

RegistryControlledDomainService::FindDomainPtr
RegistryControlledDomainService::find_domain_function_ =
    Perfect_Hash::FindDomain;

// static
std::string RegistryControlledDomainService::GetDomainAndRegistry(
    const GURL& gurl) {
  const url_parse::Component host =
      gurl.parsed_for_possibly_invalid_spec().host;
  if ((host.len <= 0) || gurl.HostIsIPAddress())
    return std::string();
  return GetDomainAndRegistryImpl(std::string(
      gurl.possibly_invalid_spec().data() + host.begin, host.len));
}

// static
std::string RegistryControlledDomainService::GetDomainAndRegistry(
    const std::string& host) {
  url_canon::CanonHostInfo host_info;
  const std::string canon_host(CanonicalizeHost(host, &host_info));
  if (canon_host.empty() || host_info.IsIPAddress())
    return std::string();
  return GetDomainAndRegistryImpl(canon_host);
}

// static
bool RegistryControlledDomainService::SameDomainOrHost(const GURL& gurl1,
                                                       const GURL& gurl2) {
  // See if both URLs have a known domain + registry, and those values are the
  // same.
  const std::string domain1(GetDomainAndRegistry(gurl1));
  const std::string domain2(GetDomainAndRegistry(gurl2));
  if (!domain1.empty() || !domain2.empty())
    return domain1 == domain2;

  // No domains.  See if the hosts are identical.
  const url_parse::Component host1 =
      gurl1.parsed_for_possibly_invalid_spec().host;
  const url_parse::Component host2 =
      gurl2.parsed_for_possibly_invalid_spec().host;
  if ((host1.len <= 0) || (host1.len != host2.len))
    return false;
  return !strncmp(gurl1.possibly_invalid_spec().data() + host1.begin,
                  gurl2.possibly_invalid_spec().data() + host2.begin,
                  host1.len);
}

// static
size_t RegistryControlledDomainService::GetRegistryLength(
    const GURL& gurl,
    bool allow_unknown_registries) {
  const url_parse::Component host =
      gurl.parsed_for_possibly_invalid_spec().host;
  if (host.len <= 0)
    return std::string::npos;
  if (gurl.HostIsIPAddress())
    return 0;
  return GetRegistryLengthImpl(
      std::string(gurl.possibly_invalid_spec().data() + host.begin, host.len),
      allow_unknown_registries);
}

// static
size_t RegistryControlledDomainService::GetRegistryLength(
    const std::string& host,
    bool allow_unknown_registries) {
  url_canon::CanonHostInfo host_info;
  const std::string canon_host(CanonicalizeHost(host, &host_info));
  if (canon_host.empty())
    return std::string::npos;
  if (host_info.IsIPAddress())
    return 0;
  return GetRegistryLengthImpl(canon_host, allow_unknown_registries);
}

// static
void RegistryControlledDomainService::UseFindDomainFunction(
    FindDomainPtr function) {
  if (function) {
    find_domain_function_ = function;
  } else {
    find_domain_function_ = Perfect_Hash::FindDomain;
  }
}

// static
std::string RegistryControlledDomainService::GetDomainAndRegistryImpl(
    const std::string& host) {
  DCHECK(!host.empty());

  // Find the length of the registry for this host.
  const size_t registry_length = GetRegistryLengthImpl(host, true);
  if ((registry_length == std::string::npos) || (registry_length == 0))
    return std::string();  // No registry.
  // The "2" in this next line is 1 for the dot, plus a 1-char minimum preceding
  // subcomponent length.
  DCHECK(host.length() >= 2);
  if (registry_length > (host.length() - 2)) {
    NOTREACHED() <<
        "Host does not have at least one subcomponent before registry!";
    return std::string();
  }

  // Move past the dot preceding the registry, and search for the next previous
  // dot.  Return the host from after that dot, or the whole host when there is
  // no dot.
  const size_t dot = host.rfind('.', host.length() - registry_length - 2);
  if (dot == std::string::npos)
    return host;
  return host.substr(dot + 1);
}

size_t RegistryControlledDomainService::GetRegistryLengthImpl(
    const std::string& host,
    bool allow_unknown_registries) {
  DCHECK(!host.empty());

  // Skip leading dots.
  const size_t host_check_begin = host.find_first_not_of('.');
  if (host_check_begin == std::string::npos)
    return 0;  // Host is only dots.

  // A single trailing dot isn't relevant in this determination, but does need
  // to be included in the final returned length.
  size_t host_check_len = host.length();
  if (host[host_check_len - 1] == '.') {
    --host_check_len;
    DCHECK(host_check_len > 0);  // If this weren't true, the host would be ".",
                                 // and we'd have already returned above.
    if (host[host_check_len - 1] == '.')
      return 0;  // Multiple trailing dots.
  }

  // Walk up the domain tree, most specific to least specific,
  // looking for matches at each level.
  size_t prev_start = std::string::npos;
  size_t curr_start = host_check_begin;
  size_t next_dot = host.find('.', curr_start);
  if (next_dot >= host_check_len)  // Catches std::string::npos as well.
    return 0;  // This can't have a registry + domain.
  while (1) {
    const char* domain_str = host.data() + curr_start;
    int domain_length = host_check_len - curr_start;
    const DomainRule* rule = find_domain_function_(domain_str, domain_length);

    // We need to compare the string after finding a match because the
    // no-collisions of perfect hashing only refers to items in the set.  Since
    // we're searching for arbitrary domains, there could be collisions.
    if (rule &&
        base::strncasecmp(domain_str, rule->name, domain_length) == 0) {
      // Exception rules override wildcard rules when the domain is an exact
      // match, but wildcards take precedence when there's a subdomain.
      if (rule->type == kWildcardRule && (prev_start != std::string::npos)) {
        // If prev_start == host_check_begin, then the host is the registry
        // itself, so return 0.
        return (prev_start == host_check_begin) ?
            0 : (host.length() - prev_start);
      }

      if (rule->type == kExceptionRule) {
        if (next_dot == std::string::npos) {
          // If we get here, we had an exception rule with no dots (e.g.
          // "!foo").  This would only be valid if we had a corresponding
          // wildcard rule, which would have to be "*".  But we explicitly
          // disallow that case, so this kind of rule is invalid.
          NOTREACHED() << "Invalid exception rule";
          return 0;
        }
        return host.length() - next_dot - 1;
      }

      // If curr_start == host_check_begin, then the host is the registry
      // itself, so return 0.
      return (curr_start == host_check_begin) ?
          0 : (host.length() - curr_start);
    }

    if (next_dot >= host_check_len)  // Catches std::string::npos as well.
      break;

    prev_start = curr_start;
    curr_start = next_dot + 1;
    next_dot = host.find('.', curr_start);
  }

  // No rule found in the registry.  curr_start now points to the first
  // character of the last subcomponent of the host, so if we allow unknown
  // registries, return the length of this subcomponent.
  return allow_unknown_registries ? (host.length() - curr_start) : 0;
}

}  // namespace net
<commit_msg>Eliminate an unnecessary #include.<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.

// NB: Modelled after Mozilla's code (originally written by Pamela Greene,
// later modified by others), but almost entirely rewritten for Chrome.
//   (netwerk/dns/src/nsEffectiveTLDService.cpp)
/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is Mozilla Effective-TLD Service
 *
 * The Initial Developer of the Original Code is
 * Google Inc.
 * Portions created by the Initial Developer are Copyright (C) 2006
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *   Pamela Greene <pamg.bugs@gmail.com> (original author)
 *   Daniel Witte <dwitte@stanford.edu>
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

#include "net/base/registry_controlled_domain.h"

#include "base/logging.h"
#include "base/string_util.h"
#include "base/utf_string_conversions.h"
#include "googleurl/src/gurl.h"
#include "googleurl/src/url_parse.h"
#include "net/base/net_module.h"
#include "net/base/net_util.h"

#include "effective_tld_names.cc"

namespace net {

namespace {

const int kExceptionRule = 1;
const int kWildcardRule = 2;

}  // namespace

RegistryControlledDomainService::FindDomainPtr
RegistryControlledDomainService::find_domain_function_ =
    Perfect_Hash::FindDomain;

// static
std::string RegistryControlledDomainService::GetDomainAndRegistry(
    const GURL& gurl) {
  const url_parse::Component host =
      gurl.parsed_for_possibly_invalid_spec().host;
  if ((host.len <= 0) || gurl.HostIsIPAddress())
    return std::string();
  return GetDomainAndRegistryImpl(std::string(
      gurl.possibly_invalid_spec().data() + host.begin, host.len));
}

// static
std::string RegistryControlledDomainService::GetDomainAndRegistry(
    const std::string& host) {
  url_canon::CanonHostInfo host_info;
  const std::string canon_host(CanonicalizeHost(host, &host_info));
  if (canon_host.empty() || host_info.IsIPAddress())
    return std::string();
  return GetDomainAndRegistryImpl(canon_host);
}

// static
bool RegistryControlledDomainService::SameDomainOrHost(const GURL& gurl1,
                                                       const GURL& gurl2) {
  // See if both URLs have a known domain + registry, and those values are the
  // same.
  const std::string domain1(GetDomainAndRegistry(gurl1));
  const std::string domain2(GetDomainAndRegistry(gurl2));
  if (!domain1.empty() || !domain2.empty())
    return domain1 == domain2;

  // No domains.  See if the hosts are identical.
  const url_parse::Component host1 =
      gurl1.parsed_for_possibly_invalid_spec().host;
  const url_parse::Component host2 =
      gurl2.parsed_for_possibly_invalid_spec().host;
  if ((host1.len <= 0) || (host1.len != host2.len))
    return false;
  return !strncmp(gurl1.possibly_invalid_spec().data() + host1.begin,
                  gurl2.possibly_invalid_spec().data() + host2.begin,
                  host1.len);
}

// static
size_t RegistryControlledDomainService::GetRegistryLength(
    const GURL& gurl,
    bool allow_unknown_registries) {
  const url_parse::Component host =
      gurl.parsed_for_possibly_invalid_spec().host;
  if (host.len <= 0)
    return std::string::npos;
  if (gurl.HostIsIPAddress())
    return 0;
  return GetRegistryLengthImpl(
      std::string(gurl.possibly_invalid_spec().data() + host.begin, host.len),
      allow_unknown_registries);
}

// static
size_t RegistryControlledDomainService::GetRegistryLength(
    const std::string& host,
    bool allow_unknown_registries) {
  url_canon::CanonHostInfo host_info;
  const std::string canon_host(CanonicalizeHost(host, &host_info));
  if (canon_host.empty())
    return std::string::npos;
  if (host_info.IsIPAddress())
    return 0;
  return GetRegistryLengthImpl(canon_host, allow_unknown_registries);
}

// static
void RegistryControlledDomainService::UseFindDomainFunction(
    FindDomainPtr function) {
  find_domain_function_ = function ? function : Perfect_Hash::FindDomain;
}

// static
std::string RegistryControlledDomainService::GetDomainAndRegistryImpl(
    const std::string& host) {
  DCHECK(!host.empty());

  // Find the length of the registry for this host.
  const size_t registry_length = GetRegistryLengthImpl(host, true);
  if ((registry_length == std::string::npos) || (registry_length == 0))
    return std::string();  // No registry.
  // The "2" in this next line is 1 for the dot, plus a 1-char minimum preceding
  // subcomponent length.
  DCHECK(host.length() >= 2);
  if (registry_length > (host.length() - 2)) {
    NOTREACHED() <<
        "Host does not have at least one subcomponent before registry!";
    return std::string();
  }

  // Move past the dot preceding the registry, and search for the next previous
  // dot.  Return the host from after that dot, or the whole host when there is
  // no dot.
  const size_t dot = host.rfind('.', host.length() - registry_length - 2);
  if (dot == std::string::npos)
    return host;
  return host.substr(dot + 1);
}

size_t RegistryControlledDomainService::GetRegistryLengthImpl(
    const std::string& host,
    bool allow_unknown_registries) {
  DCHECK(!host.empty());

  // Skip leading dots.
  const size_t host_check_begin = host.find_first_not_of('.');
  if (host_check_begin == std::string::npos)
    return 0;  // Host is only dots.

  // A single trailing dot isn't relevant in this determination, but does need
  // to be included in the final returned length.
  size_t host_check_len = host.length();
  if (host[host_check_len - 1] == '.') {
    --host_check_len;
    DCHECK(host_check_len > 0);  // If this weren't true, the host would be ".",
                                 // and we'd have already returned above.
    if (host[host_check_len - 1] == '.')
      return 0;  // Multiple trailing dots.
  }

  // Walk up the domain tree, most specific to least specific,
  // looking for matches at each level.
  size_t prev_start = std::string::npos;
  size_t curr_start = host_check_begin;
  size_t next_dot = host.find('.', curr_start);
  if (next_dot >= host_check_len)  // Catches std::string::npos as well.
    return 0;  // This can't have a registry + domain.
  while (1) {
    const char* domain_str = host.data() + curr_start;
    int domain_length = host_check_len - curr_start;
    const DomainRule* rule = find_domain_function_(domain_str, domain_length);

    // We need to compare the string after finding a match because the
    // no-collisions of perfect hashing only refers to items in the set.  Since
    // we're searching for arbitrary domains, there could be collisions.
    if (rule &&
        base::strncasecmp(domain_str, rule->name, domain_length) == 0) {
      // Exception rules override wildcard rules when the domain is an exact
      // match, but wildcards take precedence when there's a subdomain.
      if (rule->type == kWildcardRule && (prev_start != std::string::npos)) {
        // If prev_start == host_check_begin, then the host is the registry
        // itself, so return 0.
        return (prev_start == host_check_begin) ?
            0 : (host.length() - prev_start);
      }

      if (rule->type == kExceptionRule) {
        if (next_dot == std::string::npos) {
          // If we get here, we had an exception rule with no dots (e.g.
          // "!foo").  This would only be valid if we had a corresponding
          // wildcard rule, which would have to be "*".  But we explicitly
          // disallow that case, so this kind of rule is invalid.
          NOTREACHED() << "Invalid exception rule";
          return 0;
        }
        return host.length() - next_dot - 1;
      }

      // If curr_start == host_check_begin, then the host is the registry
      // itself, so return 0.
      return (curr_start == host_check_begin) ?
          0 : (host.length() - curr_start);
    }

    if (next_dot >= host_check_len)  // Catches std::string::npos as well.
      break;

    prev_start = curr_start;
    curr_start = next_dot + 1;
    next_dot = host.find('.', curr_start);
  }

  // No rule found in the registry.  curr_start now points to the first
  // character of the last subcomponent of the host, so if we allow unknown
  // registries, return the length of this subcomponent.
  return allow_unknown_registries ? (host.length() - curr_start) : 0;
}

}  // namespace net
<|endoftext|>


<commit_before>/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

/*!
 * \file dot.cc
 * \brief CPU Implementation of matrix dot
 */

#include "./dot-inl.h"

namespace mxnet {
namespace op {
DMLC_REGISTER_PARAMETER(DotParam);

NNVM_REGISTER_OP(dot)
MXNET_ADD_SPARSE_OP_ALIAS(dot)
.describe(R"doc(Dot product of two arrays.

``dot``'s behavior depends on the input array dimensions:

- 1-D arrays: inner product of vectors
- 2-D arrays: matrix multiplication
- N-D arrays: a sum product over the last axis of the first input and the first
  axis of the second input

  For example, given 3-D ``x`` with shape `(n,m,k)` and ``y`` with shape `(k,r,s)`, the
  result array will have shape `(n,m,r,s)`. It is computed by::

    dot(x,y)[i,j,a,b] = sum(x[i,j,:]*y[:,a,b])

  Example::

    x = reshape([0,1,2,3,4,5,6,7], shape=(2,2,2))
    y = reshape([7,6,5,4,3,2,1,0], shape=(2,2,2))
    dot(x,y)[0,0,1,1] = 0
    sum(x[0,0,:]*y[:,1,1]) = 0

The storage type of ``dot`` output depends on storage types of inputs, transpose option and
forward_stype option for output storage type. Implemented sparse operations include:

- dot(default, default, transpose_a=True/False, transpose_b=True/False) = default
- dot(csr, default, transpose_a=True) = default
- dot(csr, default, transpose_a=True) = row_sparse
- dot(csr, default) = default
- dot(csr, row_sparse) = default
- dot(default, csr) = csr (CPU only)
- dot(default, csr, forward_stype='default') = default
- dot(default, csr, transpose_b=True, forward_stype='default') = default

If the combination of input storage types and forward_stype does not match any of the
above patterns, ``dot`` will fallback and generate output with default storage.

.. Note::

    If the storage type of the lhs is "csr", the storage type of gradient w.r.t rhs will be
    "row_sparse". Only a subset of optimizers support sparse gradients, including SGD, AdaGrad
    and Adam. Note that by default lazy updates is turned on, which may perform differently
    from standard updates. For more details, please check the Optimization API at:
    https://mxnet.incubator.apache.org/api/python/optimization/optimization.html

)doc" ADD_FILELINE)
.set_num_inputs(2)
.set_num_outputs(1)
.set_attr_parser(ParamParser<DotParam>)
.set_attr<nnvm::FListInputNames>("FListInputNames",
  [](const NodeAttrs& attrs) {
    return std::vector<std::string>{"lhs", "rhs"};
  })
.set_attr<mxnet::FInferShape>("FInferShape", DotShape)
.set_attr<nnvm::FInferType>("FInferType", ElemwiseType<2, 1>)
.set_attr<FInferStorageType>("FInferStorageType", DotForwardInferStorageType)
.set_attr<FResourceRequest>("FResourceRequest",
  [](const NodeAttrs& attrs) {
    return std::vector<ResourceRequest>{ResourceRequest::kTempSpace};
  })
.set_attr<FCompute>("FCompute<cpu>", DotForward_<cpu>)
.set_attr<FComputeEx>("FComputeEx<cpu>", DotForwardEx<cpu>)
.set_attr<nnvm::FGradient>("FGradient", ElemwiseGradUseIn{"_backward_dot"})
.add_argument("lhs", "NDArray-or-Symbol", "The first input")
.add_argument("rhs", "NDArray-or-Symbol", "The second input")
.add_arguments(DotParam::__FIELDS__());

NNVM_REGISTER_OP(_backward_dot)
.set_num_inputs(3)
.set_num_outputs(2)
.set_attr_parser(ParamParser<DotParam>)
.set_attr<nnvm::TIsBackward>("TIsBackward", true)
.set_attr<FInferStorageType>("FInferStorageType", DotBackwardInferStorageType)
.set_attr<FResourceRequest>("FResourceRequest",
  [](const NodeAttrs& attrs) {
    return std::vector<ResourceRequest>{ResourceRequest::kTempSpace};
  })
.set_attr<FCompute>("FCompute<cpu>", DotBackward_<cpu>)
.set_attr<FComputeEx>("FComputeEx<cpu>", DotBackwardEx<cpu>)
.add_arguments(DotParam::__FIELDS__());

NNVM_REGISTER_OP(batch_dot)
.add_alias("_npx_batch_dot")
.describe(R"doc(Batchwise dot product.

``batch_dot`` is used to compute dot product of ``x`` and ``y`` when ``x`` and
``y`` are data in batch, namely N-D (N >= 3) arrays in shape of `(B0, ..., B_i, :, :)`.

For example, given ``x`` with shape `(B_0, ..., B_i, N, M)` and ``y`` with shape
`(B_0, ..., B_i, M, K)`, the result array will have shape `(B_0, ..., B_i, N, K)`,
which is computed by::

   batch_dot(x,y)[b_0, ..., b_i, :, :] = dot(x[b_0, ..., b_i, :, :], y[b_0, ..., b_i, :, :])

)doc" ADD_FILELINE)
.set_num_inputs(2)
.set_num_outputs(1)
.set_attr_parser(ParamParser<DotParam>)
.set_attr<nnvm::FListInputNames>("FListInputNames",
  [](const NodeAttrs& attrs) {
    return std::vector<std::string>{"lhs", "rhs"};
  })
.set_attr<mxnet::FInferShape>("FInferShape", BatchDotShape)
.set_attr<nnvm::FInferType>("FInferType", ElemwiseType<2, 1>)
.set_attr<FResourceRequest>("FResourceRequest",
  [](const NodeAttrs& attrs) {
    return std::vector<ResourceRequest>{ResourceRequest::kTempSpace};
  })
.set_attr<FCompute>("FCompute<cpu>", BatchDotForward_<cpu>)
.set_attr<nnvm::FGradient>("FGradient",
    [](const nnvm::NodePtr& n,
       const std::vector<nnvm::NodeEntry>& ograds) {
  const DotParam& param = nnvm::get<DotParam>(n->attrs.parsed);
  nnvm::NodePtr lhs_grad;
  nnvm::NodePtr rhs_grad;
  std::string lhs_gnode_name = n->attrs.name + "_backward_lhs";
  std::string rhs_gnode_name = n->attrs.name + "_backward_rhs";
  if (param.transpose_a && param.transpose_b) {
    // Gradient of z = dot(x.T, y.T)
    // dx = dot(dz, y).T = dot(y.T, dz.T)
    // dy = dot(x, dz).T = dot(dz.T, x.T)
    lhs_grad = MakeNode("batch_dot", lhs_gnode_name,
                        {n->inputs[1], ograds[0]}, &(n->attrs.dict), &n);
    rhs_grad = MakeNode("batch_dot", rhs_gnode_name,
                        {ograds[0], n->inputs[0]}, &(n->attrs.dict), &n);
  } else if (!param.transpose_a && param.transpose_b) {
    // Gradient of z = dot(x, y.T)
    // dx = dot(dz, y)
    // dy = dot(x.T, dz).T = dot(dz.T, x)
    auto lhs_attrs_dict = n->attrs.dict;
    auto rhs_attrs_dict = n->attrs.dict;
    lhs_attrs_dict["transpose_a"] = "false";
    lhs_attrs_dict["transpose_b"] = "false";
    rhs_attrs_dict["transpose_a"] = "true";
    rhs_attrs_dict["transpose_b"] = "false";
    lhs_grad = MakeNode("batch_dot", lhs_gnode_name,
                        {ograds[0], n->inputs[1]}, &lhs_attrs_dict, &n);
    rhs_grad = MakeNode("batch_dot", rhs_gnode_name,
                        {ograds[0], n->inputs[0]}, &rhs_attrs_dict, &n);
  } else if (param.transpose_a && !param.transpose_b) {
    // Gradient of z = dot(x.T, y)
    // dx = dot(dz, y.T).T = dot(y, dz.T)
    // dy = dot(x, dz)
    auto lhs_attrs_dict = n->attrs.dict;
    auto rhs_attrs_dict = n->attrs.dict;
    lhs_attrs_dict["transpose_a"] = "false";
    lhs_attrs_dict["transpose_b"] = "true";
    rhs_attrs_dict["transpose_a"] = "false";
    rhs_attrs_dict["transpose_b"] = "false";
    lhs_grad = MakeNode("batch_dot", lhs_gnode_name,
                        {n->inputs[1], ograds[0]}, &lhs_attrs_dict, &n);
    rhs_grad = MakeNode("batch_dot", rhs_gnode_name,
                        {n->inputs[0], ograds[0]}, &rhs_attrs_dict, &n);
  } else {
    // Gradient of z = dot(x, y)
    // dx = dot(dz, y.T)
    // dy = dot(x.T, dz)
    auto lhs_attrs_dict = n->attrs.dict;
    auto rhs_attrs_dict = n->attrs.dict;
    lhs_attrs_dict["transpose_a"] = "false";
    lhs_attrs_dict["transpose_b"] = "true";
    rhs_attrs_dict["transpose_a"] = "true";
    rhs_attrs_dict["transpose_b"] = "false";
    lhs_grad = MakeNode("batch_dot", lhs_gnode_name,
                        {ograds[0], n->inputs[1]}, &lhs_attrs_dict, &n);
    rhs_grad = MakeNode("batch_dot", rhs_gnode_name,
                        {n->inputs[0], ograds[0]}, &rhs_attrs_dict, &n);
  }
  std::vector<nnvm::NodeEntry> ret;
  ret.emplace_back(nnvm::NodeEntry{lhs_grad, 0, 0});
  ret.emplace_back(nnvm::NodeEntry{rhs_grad, 0, 0});
  return ret;
})
//.set_attr<nnvm::FGradient>("FGradient", ElemwiseGradUseIn{"_backward_batch_dot"})
.add_argument("lhs", "NDArray-or-Symbol", "The first input")
.add_argument("rhs", "NDArray-or-Symbol", "The second input")
.add_arguments(DotParam::__FIELDS__());

}  // namespace op
}  // namespace mxnet
<commit_msg>remove unnecessary comment<commit_after>/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

/*!
 * \file dot.cc
 * \brief CPU Implementation of matrix dot
 */

#include "./dot-inl.h"

namespace mxnet {
namespace op {
DMLC_REGISTER_PARAMETER(DotParam);

NNVM_REGISTER_OP(dot)
MXNET_ADD_SPARSE_OP_ALIAS(dot)
.describe(R"doc(Dot product of two arrays.

``dot``'s behavior depends on the input array dimensions:

- 1-D arrays: inner product of vectors
- 2-D arrays: matrix multiplication
- N-D arrays: a sum product over the last axis of the first input and the first
  axis of the second input

  For example, given 3-D ``x`` with shape `(n,m,k)` and ``y`` with shape `(k,r,s)`, the
  result array will have shape `(n,m,r,s)`. It is computed by::

    dot(x,y)[i,j,a,b] = sum(x[i,j,:]*y[:,a,b])

  Example::

    x = reshape([0,1,2,3,4,5,6,7], shape=(2,2,2))
    y = reshape([7,6,5,4,3,2,1,0], shape=(2,2,2))
    dot(x,y)[0,0,1,1] = 0
    sum(x[0,0,:]*y[:,1,1]) = 0

The storage type of ``dot`` output depends on storage types of inputs, transpose option and
forward_stype option for output storage type. Implemented sparse operations include:

- dot(default, default, transpose_a=True/False, transpose_b=True/False) = default
- dot(csr, default, transpose_a=True) = default
- dot(csr, default, transpose_a=True) = row_sparse
- dot(csr, default) = default
- dot(csr, row_sparse) = default
- dot(default, csr) = csr (CPU only)
- dot(default, csr, forward_stype='default') = default
- dot(default, csr, transpose_b=True, forward_stype='default') = default

If the combination of input storage types and forward_stype does not match any of the
above patterns, ``dot`` will fallback and generate output with default storage.

.. Note::

    If the storage type of the lhs is "csr", the storage type of gradient w.r.t rhs will be
    "row_sparse". Only a subset of optimizers support sparse gradients, including SGD, AdaGrad
    and Adam. Note that by default lazy updates is turned on, which may perform differently
    from standard updates. For more details, please check the Optimization API at:
    https://mxnet.incubator.apache.org/api/python/optimization/optimization.html

)doc" ADD_FILELINE)
.set_num_inputs(2)
.set_num_outputs(1)
.set_attr_parser(ParamParser<DotParam>)
.set_attr<nnvm::FListInputNames>("FListInputNames",
  [](const NodeAttrs& attrs) {
    return std::vector<std::string>{"lhs", "rhs"};
  })
.set_attr<mxnet::FInferShape>("FInferShape", DotShape)
.set_attr<nnvm::FInferType>("FInferType", ElemwiseType<2, 1>)
.set_attr<FInferStorageType>("FInferStorageType", DotForwardInferStorageType)
.set_attr<FResourceRequest>("FResourceRequest",
  [](const NodeAttrs& attrs) {
    return std::vector<ResourceRequest>{ResourceRequest::kTempSpace};
  })
.set_attr<FCompute>("FCompute<cpu>", DotForward_<cpu>)
.set_attr<FComputeEx>("FComputeEx<cpu>", DotForwardEx<cpu>)
.set_attr<nnvm::FGradient>("FGradient", ElemwiseGradUseIn{"_backward_dot"})
.add_argument("lhs", "NDArray-or-Symbol", "The first input")
.add_argument("rhs", "NDArray-or-Symbol", "The second input")
.add_arguments(DotParam::__FIELDS__());

NNVM_REGISTER_OP(_backward_dot)
.set_num_inputs(3)
.set_num_outputs(2)
.set_attr_parser(ParamParser<DotParam>)
.set_attr<nnvm::TIsBackward>("TIsBackward", true)
.set_attr<FInferStorageType>("FInferStorageType", DotBackwardInferStorageType)
.set_attr<FResourceRequest>("FResourceRequest",
  [](const NodeAttrs& attrs) {
    return std::vector<ResourceRequest>{ResourceRequest::kTempSpace};
  })
.set_attr<FCompute>("FCompute<cpu>", DotBackward_<cpu>)
.set_attr<FComputeEx>("FComputeEx<cpu>", DotBackwardEx<cpu>)
.add_arguments(DotParam::__FIELDS__());

NNVM_REGISTER_OP(batch_dot)
.add_alias("_npx_batch_dot")
.describe(R"doc(Batchwise dot product.

``batch_dot`` is used to compute dot product of ``x`` and ``y`` when ``x`` and
``y`` are data in batch, namely N-D (N >= 3) arrays in shape of `(B0, ..., B_i, :, :)`.

For example, given ``x`` with shape `(B_0, ..., B_i, N, M)` and ``y`` with shape
`(B_0, ..., B_i, M, K)`, the result array will have shape `(B_0, ..., B_i, N, K)`,
which is computed by::

   batch_dot(x,y)[b_0, ..., b_i, :, :] = dot(x[b_0, ..., b_i, :, :], y[b_0, ..., b_i, :, :])

)doc" ADD_FILELINE)
.set_num_inputs(2)
.set_num_outputs(1)
.set_attr_parser(ParamParser<DotParam>)
.set_attr<nnvm::FListInputNames>("FListInputNames",
  [](const NodeAttrs& attrs) {
    return std::vector<std::string>{"lhs", "rhs"};
  })
.set_attr<mxnet::FInferShape>("FInferShape", BatchDotShape)
.set_attr<nnvm::FInferType>("FInferType", ElemwiseType<2, 1>)
.set_attr<FResourceRequest>("FResourceRequest",
  [](const NodeAttrs& attrs) {
    return std::vector<ResourceRequest>{ResourceRequest::kTempSpace};
  })
.set_attr<FCompute>("FCompute<cpu>", BatchDotForward_<cpu>)
.set_attr<nnvm::FGradient>("FGradient",
    [](const nnvm::NodePtr& n,
       const std::vector<nnvm::NodeEntry>& ograds) {
  const DotParam& param = nnvm::get<DotParam>(n->attrs.parsed);
  nnvm::NodePtr lhs_grad;
  nnvm::NodePtr rhs_grad;
  std::string lhs_gnode_name = n->attrs.name + "_backward_lhs";
  std::string rhs_gnode_name = n->attrs.name + "_backward_rhs";
  if (param.transpose_a && param.transpose_b) {
    // Gradient of z = dot(x.T, y.T)
    // dx = dot(dz, y).T = dot(y.T, dz.T)
    // dy = dot(x, dz).T = dot(dz.T, x.T)
    lhs_grad = MakeNode("batch_dot", lhs_gnode_name,
                        {n->inputs[1], ograds[0]}, &(n->attrs.dict), &n);
    rhs_grad = MakeNode("batch_dot", rhs_gnode_name,
                        {ograds[0], n->inputs[0]}, &(n->attrs.dict), &n);
  } else if (!param.transpose_a && param.transpose_b) {
    // Gradient of z = dot(x, y.T)
    // dx = dot(dz, y)
    // dy = dot(x.T, dz).T = dot(dz.T, x)
    auto lhs_attrs_dict = n->attrs.dict;
    auto rhs_attrs_dict = n->attrs.dict;
    lhs_attrs_dict["transpose_a"] = "false";
    lhs_attrs_dict["transpose_b"] = "false";
    rhs_attrs_dict["transpose_a"] = "true";
    rhs_attrs_dict["transpose_b"] = "false";
    lhs_grad = MakeNode("batch_dot", lhs_gnode_name,
                        {ograds[0], n->inputs[1]}, &lhs_attrs_dict, &n);
    rhs_grad = MakeNode("batch_dot", rhs_gnode_name,
                        {ograds[0], n->inputs[0]}, &rhs_attrs_dict, &n);
  } else if (param.transpose_a && !param.transpose_b) {
    // Gradient of z = dot(x.T, y)
    // dx = dot(dz, y.T).T = dot(y, dz.T)
    // dy = dot(x, dz)
    auto lhs_attrs_dict = n->attrs.dict;
    auto rhs_attrs_dict = n->attrs.dict;
    lhs_attrs_dict["transpose_a"] = "false";
    lhs_attrs_dict["transpose_b"] = "true";
    rhs_attrs_dict["transpose_a"] = "false";
    rhs_attrs_dict["transpose_b"] = "false";
    lhs_grad = MakeNode("batch_dot", lhs_gnode_name,
                        {n->inputs[1], ograds[0]}, &lhs_attrs_dict, &n);
    rhs_grad = MakeNode("batch_dot", rhs_gnode_name,
                        {n->inputs[0], ograds[0]}, &rhs_attrs_dict, &n);
  } else {
    // Gradient of z = dot(x, y)
    // dx = dot(dz, y.T)
    // dy = dot(x.T, dz)
    auto lhs_attrs_dict = n->attrs.dict;
    auto rhs_attrs_dict = n->attrs.dict;
    lhs_attrs_dict["transpose_a"] = "false";
    lhs_attrs_dict["transpose_b"] = "true";
    rhs_attrs_dict["transpose_a"] = "true";
    rhs_attrs_dict["transpose_b"] = "false";
    lhs_grad = MakeNode("batch_dot", lhs_gnode_name,
                        {ograds[0], n->inputs[1]}, &lhs_attrs_dict, &n);
    rhs_grad = MakeNode("batch_dot", rhs_gnode_name,
                        {n->inputs[0], ograds[0]}, &rhs_attrs_dict, &n);
  }
  std::vector<nnvm::NodeEntry> ret;
  ret.emplace_back(nnvm::NodeEntry{lhs_grad, 0, 0});
  ret.emplace_back(nnvm::NodeEntry{rhs_grad, 0, 0});
  return ret;
})
.add_argument("lhs", "NDArray-or-Symbol", "The first input")
.add_argument("rhs", "NDArray-or-Symbol", "The second input")
.add_arguments(DotParam::__FIELDS__());

}  // namespace op
}  // namespace mxnet
<|endoftext|>


<commit_before>/**
 * This file is part of TelepathyQt4
 *
 * @copyright Copyright (C) 2009 Collabora Ltd. <http://www.collabora.co.uk/>
 * @copyright Copyright (C) 2009 Nokia Corporation
 * @license LGPL 2.1
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include <TelepathyQt4/PendingSendMessage>

#include "TelepathyQt4/_gen/pending-send-message.moc.hpp"

#include <TelepathyQt4/ContactMessenger>
#include <TelepathyQt4/Message>
#include <TelepathyQt4/TextChannel>

namespace Tp
{

struct TELEPATHY_QT4_NO_EXPORT PendingSendMessage::Private
{
    Private(const Message &message)
        : message(message)
    {
    }

    QString token;
    Message message;
};

PendingSendMessage::PendingSendMessage(const TextChannelPtr &channel, const Message &message)
    : PendingOperation(channel),
      mPriv(new Private(message))
{
}

PendingSendMessage::PendingSendMessage(const ContactMessengerPtr &messenger, const Message &message)
    : PendingOperation(messenger),
      mPriv(new Private(message))
{
}

PendingSendMessage::~PendingSendMessage()
{
    delete mPriv;
}

/**
 * Return the channel used to send the message if this instance was created using
 * TextChannel. If it was created using ContactMessenger, return a null TextChannelPtr.
 *
 * \return A TextChannelPtr object.
 */
TextChannelPtr PendingSendMessage::channel() const
{
    return TextChannelPtr(qobject_cast<TextChannel*>((TextChannel*) object().data()));
}

/**
 * Return the contact messenger used to send the message if this instance was created using
 * ContactMessenger. If it was created using TextChannel, return a null ContactMessengerPtr.
 *
 * \return A ContactMessengerPtr object.
 */
ContactMessengerPtr PendingSendMessage::messenger() const
{
    return ContactMessengerPtr(qobject_cast<ContactMessenger*>((ContactMessenger*) object().data()));
}

QString PendingSendMessage::sentMessageToken() const
{
    return mPriv->token;
}

Message PendingSendMessage::message() const
{
    return mPriv->message;
}

void PendingSendMessage::onTextSent(QDBusPendingCallWatcher *watcher)
{
    QDBusPendingReply<> reply = *watcher;

    if (reply.isError()) {
        setFinishedWithError(reply.error());
    } else {
        setFinished();
    }
    watcher->deleteLater();
}

void PendingSendMessage::onMessageSent(QDBusPendingCallWatcher *watcher)
{
    QDBusPendingReply<QString> reply = *watcher;

    if (reply.isError()) {
        setFinishedWithError(reply.error());
    } else {
        mPriv->token = reply.value();
        setFinished();
    }
    watcher->deleteLater();
}

void PendingSendMessage::onCDMessageSent(QDBusPendingCallWatcher *watcher)
{
    QDBusPendingReply<QString> reply = *watcher;

    if (reply.isError()) {
        if (reply.error().name() == TP_QT4_DBUS_ERROR_UNKNOWN_METHOD) {
            setFinishedWithError(TP_QT4_ERROR_NOT_IMPLEMENTED,
                    QLatin1String("Channel Dispatcher implementation (e.g. mission-control), "
                        "does not support interface CD.I.Messages"));
        } else {
            setFinishedWithError(reply.error());
        }
    } else {
        mPriv->token = reply.value();
        setFinished();
    }
    watcher->deleteLater();
}

} // Tp
<commit_msg>PendingSendMessage: Finish with NOT_IMPLEMENTED if the CD.I.Messages.SendMessage call returned UNKNOWN_INTERFACE.<commit_after>/**
 * This file is part of TelepathyQt4
 *
 * @copyright Copyright (C) 2009 Collabora Ltd. <http://www.collabora.co.uk/>
 * @copyright Copyright (C) 2009 Nokia Corporation
 * @license LGPL 2.1
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include <TelepathyQt4/PendingSendMessage>

#include "TelepathyQt4/_gen/pending-send-message.moc.hpp"

#include <TelepathyQt4/ContactMessenger>
#include <TelepathyQt4/Message>
#include <TelepathyQt4/TextChannel>

namespace Tp
{

struct TELEPATHY_QT4_NO_EXPORT PendingSendMessage::Private
{
    Private(const Message &message)
        : message(message)
    {
    }

    QString token;
    Message message;
};

PendingSendMessage::PendingSendMessage(const TextChannelPtr &channel, const Message &message)
    : PendingOperation(channel),
      mPriv(new Private(message))
{
}

PendingSendMessage::PendingSendMessage(const ContactMessengerPtr &messenger, const Message &message)
    : PendingOperation(messenger),
      mPriv(new Private(message))
{
}

PendingSendMessage::~PendingSendMessage()
{
    delete mPriv;
}

/**
 * Return the channel used to send the message if this instance was created using
 * TextChannel. If it was created using ContactMessenger, return a null TextChannelPtr.
 *
 * \return A TextChannelPtr object.
 */
TextChannelPtr PendingSendMessage::channel() const
{
    return TextChannelPtr(qobject_cast<TextChannel*>((TextChannel*) object().data()));
}

/**
 * Return the contact messenger used to send the message if this instance was created using
 * ContactMessenger. If it was created using TextChannel, return a null ContactMessengerPtr.
 *
 * \return A ContactMessengerPtr object.
 */
ContactMessengerPtr PendingSendMessage::messenger() const
{
    return ContactMessengerPtr(qobject_cast<ContactMessenger*>((ContactMessenger*) object().data()));
}

QString PendingSendMessage::sentMessageToken() const
{
    return mPriv->token;
}

Message PendingSendMessage::message() const
{
    return mPriv->message;
}

void PendingSendMessage::onTextSent(QDBusPendingCallWatcher *watcher)
{
    QDBusPendingReply<> reply = *watcher;

    if (reply.isError()) {
        setFinishedWithError(reply.error());
    } else {
        setFinished();
    }
    watcher->deleteLater();
}

void PendingSendMessage::onMessageSent(QDBusPendingCallWatcher *watcher)
{
    QDBusPendingReply<QString> reply = *watcher;

    if (reply.isError()) {
        setFinishedWithError(reply.error());
    } else {
        mPriv->token = reply.value();
        setFinished();
    }
    watcher->deleteLater();
}

void PendingSendMessage::onCDMessageSent(QDBusPendingCallWatcher *watcher)
{
    QDBusPendingReply<QString> reply = *watcher;

    if (reply.isError()) {
        QDBusError error = reply.error();
        if (error.name() == TP_QT4_DBUS_ERROR_UNKNOWN_METHOD ||
            error.name() == TP_QT4_DBUS_ERROR_UNKNOWN_INTERFACE) {
            setFinishedWithError(TP_QT4_ERROR_NOT_IMPLEMENTED,
                    QLatin1String("Channel Dispatcher implementation (e.g. mission-control), "
                        "does not support interface CD.I.Messages"));
        } else {
            setFinishedWithError(error);
        }
    } else {
        mPriv->token = reply.value();
        setFinished();
    }
    watcher->deleteLater();
}

} // Tp
<|endoftext|>


<commit_before>//
// Structure3d.cpp
//

#include "vtlib/vtlib.h"

#include "Structure3d.h"
#include "Building3d.h"
#include "Fence3d.h"
#include "Terrain.h"

///////////////////////////////////////////////////////////////////////

vtStructInstance3d::vtStructInstance3d() : vtStructInstance()
{
	m_pContainer = NULL;
	m_pHighlight = NULL;
	m_pModel = NULL;
}

bool vtStructInstance3d::CreateShape(vtHeightField *pHeightField)
{
	if (!m_pContainer)
	{
		// constructing for the first time
		m_pContainer = new vtTransform();
	}

	const char *filename = GetValue("filename");
	if (filename)
	{
		m_pModel = vtLoadModel(filename);
		if (!m_pModel)
			return false;
		m_pContainer->AddChild(m_pModel);
	}
	const char *itemname = GetValue("itemname");
	if (itemname)
	{
		// TODO: use ContentManager to create model
	}
	const char *scale = GetValue("scale");
	if (scale)
	{
		double sc = atof(scale);
		if (sc != 0.0)
			m_fScale = sc;
	}

	UpdateTransform(pHeightField);
	return true;
}

void vtStructInstance3d::UpdateTransform(vtHeightField *pHeightField)
{
	m_pContainer->Identity();

	if (m_fScale != 1.0f)
		m_pContainer->Scale3(m_fScale, m_fScale, m_fScale);

	// try to work around 3DS coordinate axes difference problem
	vtString fname2 = GetValue("filename");
	int dot = fname2.ReverseFind('.');
	if (dot > 0)
	{
		vtString ext = fname2.Mid(dot+1);
		if (ext.CompareNoCase("3ds") == 0)
		{
			// Must rotate by 90 degrees for 3DS MAX -> OpenGL
			m_pContainer->Rotate2(FPoint3(1.0f, 0.0f, 0.0f), -PID2f);
		}
	}

	if (m_fRotation != 0.0f)
		m_pContainer->Rotate2(FPoint3(0,1,0), m_fRotation);

	FPoint3 surface_pos;
	pHeightField->ConvertEarthToSurfacePoint(m_p, surface_pos);
	m_pContainer->SetTrans(surface_pos);
}

/**
 * Display some bounding wires around the object to highlight it.
 */
void vtStructInstance3d::ShowBounds(bool bShow)
{
	if (bShow)
	{
		if (!m_pHighlight)
		{
			// the highlight geometry doesn't exist, so create it
			// get bounding sphere
			FSphere sphere;
			m_pModel->GetBoundSphere(sphere);

			m_pHighlight = CreateBoundSphereGeom(sphere);
			m_pContainer->AddChild(m_pHighlight);
		}
		m_pHighlight->SetEnabled(true);
	}
	else
	{
		if (m_pHighlight)
			m_pHighlight->SetEnabled(false);
	}
}

///////////////////////////////////////////////////////////////////////

vtStructure3d::vtStructure3d() : vtStructure()
{
	m_pNode = NULL;
}

bool vtStructure3d::CreateNode(vtHeightField *hf, const char *options)
{
	if (m_type == ST_BUILDING)
	{
		vtBuilding3d *bld = (vtBuilding3d *) m_pBuilding;
		bool roof = (strstr(options, "roof") != NULL);
		bool walls = (strstr(options, "walls") != NULL);
		bool details = (strstr(options, "detail") != NULL);
		bld->CreateShape(hf, roof, walls, details);
		return true;
	}
	if (m_type == ST_FENCE)
	{
		vtFence3d *fen = (vtFence3d *) m_pFence;
		return fen->CreateShape(hf);
	}
	if (m_type == ST_INSTANCE)
	{
		vtStructInstance3d *inst = (vtStructInstance3d *) m_pInstance;
		bool bSuccess = inst->CreateShape(hf);
		if (!bSuccess)
		{
			// try again with the supplied path
			vtTag *tag = inst->FindTag("filename");
			if (tag)
			{
				vtString fullpath = FindFileOnPaths(vtTerrain::m_DataPaths, tag->value);
				if (fullpath != "")
				{
					tag->value = fullpath;
					// try again
					bSuccess = inst->CreateShape(hf);
				}
			}
		}
		return bSuccess;
	}
	return false;
}

vtTransform *vtStructure3d::GetTransform()
{
	if (m_type == ST_BUILDING)
	{
		vtBuilding3d *bld = (vtBuilding3d *) m_pBuilding;
		return bld->GetTransform();
	}
	if (m_type == ST_INSTANCE)
	{
		vtStructInstance3d *inst = (vtStructInstance3d *) m_pInstance;
		return inst->GetTransform();
	}
	return NULL;
}

vtGeom *vtStructure3d::GetGeom()
{
	if (m_type == ST_FENCE)
	{
		vtFence3d *fen = (vtFence3d *) m_pFence;
		return fen->GetGeom();
	}
	return NULL;
}

void vtStructure3d::DeleteNode()
{
	// not needed for buildings - those are automatically deleted when needed
	// is needed for fences:
	if (m_type == ST_FENCE)
	{
		vtFence3d *fen = (vtFence3d *) m_pFence;
		fen->DestroyGeometry();
	}
}

vtBuilding3d *vtStructure3d::GetBuilding()
{
	return (vtBuilding3d *) vtStructure::GetBuilding();
}

vtFence3d *vtStructure3d::GetFence()
{
	return (vtFence3d *) vtStructure::GetFence();
}

vtStructInstance3d *vtStructure3d::GetInstance()
{
	return (vtStructInstance3d *) vtStructure::GetInstance();
}

/////////////////////////////////////////////////////////////////////

vtBuilding *vtStructureArray3d::NewBuilding()
{
	return new vtBuilding3d;
}

vtFence *vtStructureArray3d::NewFence()
{
	return new vtFence3d;
}

vtStructInstance *vtStructureArray3d::NewInstance()
{
	return new vtStructInstance3d;
}

bool vtStructureArray3d::ConstructStructure(vtStructure3d *str,
													const char *options)
{
	return str->CreateNode(m_pHeightField, options);
}

void vtStructureArray3d::ReConstructStructure(vtStructure3d *str,
											  const char *options)
{
	str->DeleteNode();
	str->CreateNode(m_pHeightField, options);
}

void vtStructureArray3d::OffsetSelectedStructures(const DPoint2 &offset)
{
	vtStructure *str;
	for (int i = 0; i < GetSize(); i++)
	{
		str = GetAt(i);
		if (!str->IsSelected())
			continue;
		if (str->GetType() == ST_BUILDING)
		{
			vtBuilding3d *bld = GetBuilding(i);
			bld->Offset(offset);
			bld->AdjustHeight(m_pHeightField);

			// Should really move the building to a new cell in the LOD
			// Grid, but unless it's moving really far we don't need to
			// worry about this.
		}
		if (str->GetType() == ST_FENCE)
		{
			vtFence3d *fen = GetFence(i);
			// TODO
		}
		if (str->GetType() == ST_INSTANCE)
		{
			vtStructInstance3d *inst = GetInstance(i);
			inst->Offset(offset);
			inst->UpdateTransform(m_pHeightField);
		}
	}
}

void vtStructureArray3d::VisualDeselectAll()
{
	for (int i = 0; i < GetSize(); i++)
	{
		vtStructure3d *str = (vtStructure3d *) GetAt(i);
		str->Select(false);

		vtBuilding3d *bld = str->GetBuilding();
		if (bld)
			bld->ShowBounds(false);

		// TODO: visual highlighting of fences

		vtStructInstance3d *inst = str->GetInstance();
		if (inst)
			inst->ShowBounds(false);
	}
}

void vtStructureArray3d::VisualSelect(vtStructure3d *str)
{
	str->Select(true);

	vtBuilding3d *bld = str->GetBuilding();
	if (bld)
		bld->ShowBounds(true);

	// TODO: visual highlighting of fences

	vtStructInstance3d *inst = str->GetInstance();
	if (inst)
		inst->ShowBounds(true);
}

<commit_msg>tweak<commit_after>//
// Structure3d.cpp
//

#include "vtlib/vtlib.h"

#include "Structure3d.h"
#include "Building3d.h"
#include "Fence3d.h"
#include "Terrain.h"

///////////////////////////////////////////////////////////////////////

vtStructInstance3d::vtStructInstance3d() : vtStructInstance()
{
	m_pContainer = NULL;
	m_pHighlight = NULL;
	m_pModel = NULL;
}

bool vtStructInstance3d::CreateShape(vtHeightField *pHeightField)
{
	if (!m_pContainer)
	{
		// constructing for the first time
		m_pContainer = new vtTransform();
	}

	const char *filename = GetValue("filename");
	if (filename)
	{
		m_pModel = vtLoadModel(filename);
		if (!m_pModel)
			return false;
		m_pContainer->AddChild(m_pModel);
	}
	const char *itemname = GetValue("itemname");
	if (itemname)
	{
		// TODO: use ContentManager to create model
	}
	const char *scale = GetValue("scale");
	if (scale)
	{
		double sc = atof(scale);
		if (sc != 0.0)
			m_fScale = sc;
	}

	UpdateTransform(pHeightField);
	return true;
}

void vtStructInstance3d::UpdateTransform(vtHeightField *pHeightField)
{
	m_pContainer->Identity();

	if (m_fScale != 1.0f)
		m_pContainer->Scale3(m_fScale, m_fScale, m_fScale);

	// try to work around 3DS coordinate axes difference problem
	vtString fname2 = GetValue("filename");
	int dot = fname2.ReverseFind('.');
	if (dot > 0)
	{
		vtString ext = fname2.Mid(dot+1);
		if (ext.CompareNoCase("3ds") == 0)
		{
			// Must rotate by 90 degrees for 3DS MAX -> OpenGL
			m_pContainer->Rotate2(FPoint3(1.0f, 0.0f, 0.0f), -PID2f);
		}
	}

	if (m_fRotation != 0.0f)
		m_pContainer->Rotate2(FPoint3(0,1,0), m_fRotation);

	FPoint3 surface_pos;
	pHeightField->ConvertEarthToSurfacePoint(m_p, surface_pos);
	m_pContainer->SetTrans(surface_pos);
}

/**
 * Display some bounding wires around the object to highlight it.
 */
void vtStructInstance3d::ShowBounds(bool bShow)
{
	if (bShow)
	{
		if (!m_pHighlight)
		{
			// the highlight geometry doesn't exist, so create it
			// get bounding sphere
			FSphere sphere;
			m_pModel->GetBoundSphere(sphere);

			m_pHighlight = CreateBoundSphereGeom(sphere);
			m_pContainer->AddChild(m_pHighlight);
		}
		m_pHighlight->SetEnabled(true);
	}
	else
	{
		if (m_pHighlight)
			m_pHighlight->SetEnabled(false);
	}
}

///////////////////////////////////////////////////////////////////////

vtStructure3d::vtStructure3d() : vtStructure()
{
	m_pNode = NULL;
}

bool vtStructure3d::CreateNode(vtHeightField *hf, const char *options)
{
	if (m_type == ST_BUILDING)
	{
		vtBuilding3d *bld = (vtBuilding3d *) m_pBuilding;
		bool roof = (strstr(options, "roof") != NULL);
		bool walls = (strstr(options, "walls") != NULL);
		bool details = (strstr(options, "detail") != NULL);
		bld->CreateShape(hf, roof, walls, details);
		return true;
	}
	if (m_type == ST_FENCE)
	{
		vtFence3d *fen = (vtFence3d *) m_pFence;
		return fen->CreateShape(hf);
	}
	if (m_type == ST_INSTANCE)
	{
		vtStructInstance3d *inst = (vtStructInstance3d *) m_pInstance;
		bool bSuccess = inst->CreateShape(hf);
		if (!bSuccess)
		{
			// try again, looking on the standards data paths
			vtTag *tag = inst->FindTag("filename");
			if (tag)
			{
				vtString fullpath = FindFileOnPaths(vtTerrain::m_DataPaths, tag->value);
				if (fullpath != "")
				{
					tag->value = fullpath;
					// try again
					bSuccess = inst->CreateShape(hf);
				}
			}
		}
		return bSuccess;
	}
	return false;
}

vtTransform *vtStructure3d::GetTransform()
{
	if (m_type == ST_BUILDING)
	{
		vtBuilding3d *bld = (vtBuilding3d *) m_pBuilding;
		return bld->GetTransform();
	}
	if (m_type == ST_INSTANCE)
	{
		vtStructInstance3d *inst = (vtStructInstance3d *) m_pInstance;
		return inst->GetTransform();
	}
	return NULL;
}

vtGeom *vtStructure3d::GetGeom()
{
	if (m_type == ST_FENCE)
	{
		vtFence3d *fen = (vtFence3d *) m_pFence;
		return fen->GetGeom();
	}
	return NULL;
}

void vtStructure3d::DeleteNode()
{
	// not needed for buildings - those are automatically deleted when needed
	// is needed for fences:
	if (m_type == ST_FENCE)
	{
		vtFence3d *fen = (vtFence3d *) m_pFence;
		fen->DestroyGeometry();
	}
}

vtBuilding3d *vtStructure3d::GetBuilding()
{
	return (vtBuilding3d *) vtStructure::GetBuilding();
}

vtFence3d *vtStructure3d::GetFence()
{
	return (vtFence3d *) vtStructure::GetFence();
}

vtStructInstance3d *vtStructure3d::GetInstance()
{
	return (vtStructInstance3d *) vtStructure::GetInstance();
}

/////////////////////////////////////////////////////////////////////

vtBuilding *vtStructureArray3d::NewBuilding()
{
	return new vtBuilding3d;
}

vtFence *vtStructureArray3d::NewFence()
{
	return new vtFence3d;
}

vtStructInstance *vtStructureArray3d::NewInstance()
{
	return new vtStructInstance3d;
}

bool vtStructureArray3d::ConstructStructure(vtStructure3d *str,
													const char *options)
{
	return str->CreateNode(m_pHeightField, options);
}

void vtStructureArray3d::ReConstructStructure(vtStructure3d *str,
											  const char *options)
{
	str->DeleteNode();
	str->CreateNode(m_pHeightField, options);
}

void vtStructureArray3d::OffsetSelectedStructures(const DPoint2 &offset)
{
	vtStructure *str;
	for (int i = 0; i < GetSize(); i++)
	{
		str = GetAt(i);
		if (!str->IsSelected())
			continue;
		if (str->GetType() == ST_BUILDING)
		{
			vtBuilding3d *bld = GetBuilding(i);
			bld->Offset(offset);
			bld->AdjustHeight(m_pHeightField);

			// Should really move the building to a new cell in the LOD
			// Grid, but unless it's moving really far we don't need to
			// worry about this.
		}
		if (str->GetType() == ST_FENCE)
		{
			vtFence3d *fen = GetFence(i);
			// TODO
		}
		if (str->GetType() == ST_INSTANCE)
		{
			vtStructInstance3d *inst = GetInstance(i);
			inst->Offset(offset);
			inst->UpdateTransform(m_pHeightField);
		}
	}
}

void vtStructureArray3d::VisualDeselectAll()
{
	for (int i = 0; i < GetSize(); i++)
	{
		vtStructure3d *str = (vtStructure3d *) GetAt(i);
		str->Select(false);

		vtBuilding3d *bld = str->GetBuilding();
		if (bld)
			bld->ShowBounds(false);

		// TODO: visual highlighting of fences

		vtStructInstance3d *inst = str->GetInstance();
		if (inst)
			inst->ShowBounds(false);
	}
}

void vtStructureArray3d::VisualSelect(vtStructure3d *str)
{
	str->Select(true);

	vtBuilding3d *bld = str->GetBuilding();
	if (bld)
		bld->ShowBounds(true);

	// TODO: visual highlighting of fences

	vtStructInstance3d *inst = str->GetInstance();
	if (inst)
		inst->ShowBounds(true);
}

<|endoftext|>


<commit_before>#include <osg/ArgumentParser>
#include <osg/ApplicationUsage>
#include <osg/Notify>

#include <set>
#include <iostream>

using namespace osg;

ArgumentParser::ArgumentParser(int* argc,char **argv):
    _argc(argc),
    _argv(argv),
    _usage(ApplicationUsage::instance())
{
}

std::string ArgumentParser::getProgramName() const
{
    if (_argc>0) return std::string(_argv[0]);
    return "";
}

int ArgumentParser::find(const std::string& str) const
{
    for(int pos=1;pos<*_argc;++pos)
    {
        if (str==_argv[pos])
        {
            return pos;
        }
    }
    return 0;
}

bool ArgumentParser::match(int pos, const std::string& str) const
{
    return pos<*_argc && str==_argv[pos];
}

bool ArgumentParser::isOption(int pos) const
{
    return (pos<*_argc && _argv[pos][0]=='-');
}

bool ArgumentParser::isString(int pos) const
{
    return pos<*_argc && !isOption(pos);
}

bool ArgumentParser::isNumber(int pos) const
{
    if (pos>=*_argc) return false;

    bool hadPlusMinus = false;
    bool hadDecimalPlace = false;
    bool hadExponent = false;
    bool couldBeInt = true;
    bool couldBeFloat = true;
    int noZeroToNine = 0;

    const char* ptr = _argv[pos];

    // check if could be a hex number.
    if (strncmp(ptr,"0x",2)==0)
    {
        // skip over leading 0x, and then go through rest of string
        // checking to make sure all values are 0...9 or a..f.
        ptr+=2;
        while (
               *ptr!=0 &&
               ((*ptr>='0' && *ptr<='9') ||
                (*ptr>='a' && *ptr<='f') ||
                (*ptr>='A' && *ptr<='F'))
              )
        {
            ++ptr;
        }

        // got to end of string without failure, therefore must be a hex integer.
        if (*ptr==0) return true;
    }

    ptr = _argv[pos];
    // check if a float or an int.
    while (*ptr!=0 && couldBeFloat)
    {
        if (*ptr=='+' || *ptr=='-')
        {
            if (hadPlusMinus)
            {
                couldBeInt = false;
                couldBeFloat = false;
            } else hadPlusMinus = true;
        }
        else if (*ptr>='0' && *ptr<='9')
        {
            noZeroToNine++;
        }
        else if (*ptr=='.')
        {
            if (hadDecimalPlace)
            {
                couldBeInt = false;
                couldBeFloat = false;
            }
            else
            {
                hadDecimalPlace = true;
                couldBeInt = false;
            }
        }
        else if (*ptr=='e' || *ptr=='E')
        {
            if (hadExponent || noZeroToNine==0)
            {
                couldBeInt = false;
                couldBeFloat = false;
            }
            else
            {
                hadExponent = true;
                couldBeInt = false;
                hadDecimalPlace = false;
                hadPlusMinus = false;
                noZeroToNine=0;
            }
        }
        else
        {
            couldBeInt = false;
            couldBeFloat = false;
        }
        ++ptr;
    }

    if (couldBeInt && noZeroToNine>0) return true;
    if (couldBeFloat && noZeroToNine>0) return true;

    return false;

}

bool ArgumentParser::containsOptions() const
{
    for(int pos=1;pos<*_argc;++pos)
    {
        if (isOption(pos)) return true;
    }
    return false;
}

void ArgumentParser::remove(int pos,int num)
{
    if (num==0) return;

    for(;pos+num<*_argc;++pos)
    {
        _argv[pos]=_argv[pos+num];
    }
    for(;pos<*_argc;++pos)
    {
        _argv[pos]=0;
    }
    *_argc-=num;
}

bool ArgumentParser::read(const std::string& str)
{
    int pos=find(str);
    if (pos<=0) return false;
    remove(pos);
    return true;
}

bool ArgumentParser::read(const std::string& str,std::string& value1)
{
    int pos=find(str);
    if (pos<=0) return false;
    if (!isString(pos+1))
    {
        reportError("argument to `"+str+"` is missing");
        return false;
    }
    value1 = _argv[pos+1];
    remove(pos,2);
    return true;
}

bool ArgumentParser::read(const std::string& str,std::string& value1,std::string& value2)
{
    int pos=find(str);
    if (pos<=0) return false;
    if (!isString(pos+1) || !isString(pos+2) )
    {
        reportError("argument to `"+str+"` is missing");
        return false;
    }
    value1 = _argv[pos+1];
    value2 = _argv[pos+2];
    remove(pos,3);
    return true;
}

bool ArgumentParser::read(const std::string& str,std::string& value1,std::string& value2,std::string& value3)
{
    int pos=find(str);
    if (pos<=0) return false;
    if (!isString(pos+1) || !isString(pos+2)  || !isString(pos+3))
    {
        reportError("argument to `"+str+"` is missing");
        return false;
    }
    value1 = _argv[pos+1];
    value2 = _argv[pos+2];
    value3 = _argv[pos+3];
    remove(pos,4);
    return true;
}

bool ArgumentParser::read(const std::string& str,float& value1)
{
    int pos=find(str);
    if (pos<=0) return false;
    if (!isNumber(pos+1))
    {
        reportError("argument to `"+str+"` is missing");
        return false;
    }
    value1 = atof(_argv[pos+1]);
    remove(pos,2);
    return true;
}

bool ArgumentParser::read(const std::string& str,float& value1,float& value2)
{
    int pos=find(str);
    if (pos<=0) return false;
    if (!isNumber(pos+1) || !isNumber(pos+2) )
    {
        reportError("argument to `"+str+"` is missing");
        return false;
    }
    value1 = atof(_argv[pos+1]);
    value2 = atof(_argv[pos+2]);
    remove(pos,3);
    return true;
}

bool ArgumentParser::read(const std::string& str,float& value1,float& value2,float& value3)
{
    int pos=find(str);
    if (pos<=0) return false;
    if (!isNumber(pos+1) || !isNumber(pos+2)  || !isNumber(pos+3))
    {
        reportError("argument to `"+str+"` is missing");
        return false;
    }
    value1 = atof(_argv[pos+1]);
    value2 = atof(_argv[pos+2]);
    value3 = atof(_argv[pos+3]);
    remove(pos,4);
    return true;
}

bool ArgumentParser::errors(ErrorSeverity severity) const
{
    for(ErrorMessageMap::const_iterator itr=_errorMessageMap.begin();
        itr!=_errorMessageMap.end();
        ++itr)
    {
        if (itr->second>=severity) return true;
    }
    return false;
}

void ArgumentParser::reportError(const std::string& message,ErrorSeverity severity)
{
    _errorMessageMap[message]=severity;
}

void ArgumentParser::reportRemainingOptionsAsUnrecognized(ErrorSeverity severity)
{
    std::set<std::string> options;
    if (_usage)
    {
        // parse the usage options to get all the option that the application can potential handle.
        for(ApplicationUsage::UsageMap::const_iterator itr=_usage->getCommandLineOptions().begin();
            itr!=_usage->getCommandLineOptions().end();
            ++itr)
        {
            const std::string& option = itr->first;
            unsigned int prevpos = 0, pos = 0;
            while ((pos=option.find(' ',prevpos))!=std::string::npos)
            {
                if (option[prevpos]=='-')
                {
                    // verbose approach implemented for debugging string(const string&,unsigned int,unsigned int) operation on x86-64
                    notify(INFO)<<"option=\""<<option<<"\" \tprevpos="<<prevpos<<" \tn="<<pos-prevpos<<"\tnpos="<<std::string::npos;

                    std::string str(option,prevpos,pos-prevpos);

                    notify(INFO)<<" \tstr=\""<<str<<"\"";

                    options.insert(str);

                    notify(INFO)<<" \tinserted into options set"<<std::endl;

                    // original op which causes a crash under x86-64
                    //options.insert(std::string(option,prevpos,pos-prevpos));
                }
                prevpos=pos+1;
            }
            if (option[prevpos]=='-')
            {

                // verbose approach implemented for debugging string(const string&,unsigned int,unsigned int) operation on x86-64
                notify(INFO)<<"option=\""<<option<<"\"  \tprevpos="<<prevpos<<" \tn=npos="<<std::string::npos;

                std::string str(option,prevpos,pos-prevpos);

                notify(INFO)<<" \tstr=\""<<str<<"\"";

                options.insert(str);

                notify(INFO)<<" \tinserted into options set"<<std::endl;

                // original op
                //options.insert(std::string(option,prevpos,std::string::npos));
            }
        }

    }

    for(int pos=1;pos<argc();++pos)
    {
        // if an option and havn't been previous querried for report as unrecognized.
        if (isOption(pos) && options.find(_argv[pos])==options.end())
        {
            reportError(getProgramName() +": unrceognized option "+_argv[pos],severity);
        }
    }
}
void ArgumentParser::writeErrorMessages(std::ostream& output,ErrorSeverity severity)
{
    for(ErrorMessageMap::iterator itr=_errorMessageMap.begin();
        itr!=_errorMessageMap.end();
        ++itr)
    {
        if (itr->second>=severity)
        {
            output<< getProgramName() << ": " << itr->first << std::endl;
        }
    }
}
<commit_msg>Changed unsigned int to std::string::size_type.<commit_after>#include <osg/ArgumentParser>
#include <osg/ApplicationUsage>
#include <osg/Notify>

#include <set>
#include <iostream>

using namespace osg;

ArgumentParser::ArgumentParser(int* argc,char **argv):
    _argc(argc),
    _argv(argv),
    _usage(ApplicationUsage::instance())
{
}

std::string ArgumentParser::getProgramName() const
{
    if (_argc>0) return std::string(_argv[0]);
    return "";
}

int ArgumentParser::find(const std::string& str) const
{
    for(int pos=1;pos<*_argc;++pos)
    {
        if (str==_argv[pos])
        {
            return pos;
        }
    }
    return 0;
}

bool ArgumentParser::match(int pos, const std::string& str) const
{
    return pos<*_argc && str==_argv[pos];
}

bool ArgumentParser::isOption(int pos) const
{
    return (pos<*_argc && _argv[pos][0]=='-');
}

bool ArgumentParser::isString(int pos) const
{
    return pos<*_argc && !isOption(pos);
}

bool ArgumentParser::isNumber(int pos) const
{
    if (pos>=*_argc) return false;

    bool hadPlusMinus = false;
    bool hadDecimalPlace = false;
    bool hadExponent = false;
    bool couldBeInt = true;
    bool couldBeFloat = true;
    int noZeroToNine = 0;

    const char* ptr = _argv[pos];

    // check if could be a hex number.
    if (strncmp(ptr,"0x",2)==0)
    {
        // skip over leading 0x, and then go through rest of string
        // checking to make sure all values are 0...9 or a..f.
        ptr+=2;
        while (
               *ptr!=0 &&
               ((*ptr>='0' && *ptr<='9') ||
                (*ptr>='a' && *ptr<='f') ||
                (*ptr>='A' && *ptr<='F'))
              )
        {
            ++ptr;
        }

        // got to end of string without failure, therefore must be a hex integer.
        if (*ptr==0) return true;
    }

    ptr = _argv[pos];
    // check if a float or an int.
    while (*ptr!=0 && couldBeFloat)
    {
        if (*ptr=='+' || *ptr=='-')
        {
            if (hadPlusMinus)
            {
                couldBeInt = false;
                couldBeFloat = false;
            } else hadPlusMinus = true;
        }
        else if (*ptr>='0' && *ptr<='9')
        {
            noZeroToNine++;
        }
        else if (*ptr=='.')
        {
            if (hadDecimalPlace)
            {
                couldBeInt = false;
                couldBeFloat = false;
            }
            else
            {
                hadDecimalPlace = true;
                couldBeInt = false;
            }
        }
        else if (*ptr=='e' || *ptr=='E')
        {
            if (hadExponent || noZeroToNine==0)
            {
                couldBeInt = false;
                couldBeFloat = false;
            }
            else
            {
                hadExponent = true;
                couldBeInt = false;
                hadDecimalPlace = false;
                hadPlusMinus = false;
                noZeroToNine=0;
            }
        }
        else
        {
            couldBeInt = false;
            couldBeFloat = false;
        }
        ++ptr;
    }

    if (couldBeInt && noZeroToNine>0) return true;
    if (couldBeFloat && noZeroToNine>0) return true;

    return false;

}

bool ArgumentParser::containsOptions() const
{
    for(int pos=1;pos<*_argc;++pos)
    {
        if (isOption(pos)) return true;
    }
    return false;
}

void ArgumentParser::remove(int pos,int num)
{
    if (num==0) return;

    for(;pos+num<*_argc;++pos)
    {
        _argv[pos]=_argv[pos+num];
    }
    for(;pos<*_argc;++pos)
    {
        _argv[pos]=0;
    }
    *_argc-=num;
}

bool ArgumentParser::read(const std::string& str)
{
    int pos=find(str);
    if (pos<=0) return false;
    remove(pos);
    return true;
}

bool ArgumentParser::read(const std::string& str,std::string& value1)
{
    int pos=find(str);
    if (pos<=0) return false;
    if (!isString(pos+1))
    {
        reportError("argument to `"+str+"` is missing");
        return false;
    }
    value1 = _argv[pos+1];
    remove(pos,2);
    return true;
}

bool ArgumentParser::read(const std::string& str,std::string& value1,std::string& value2)
{
    int pos=find(str);
    if (pos<=0) return false;
    if (!isString(pos+1) || !isString(pos+2) )
    {
        reportError("argument to `"+str+"` is missing");
        return false;
    }
    value1 = _argv[pos+1];
    value2 = _argv[pos+2];
    remove(pos,3);
    return true;
}

bool ArgumentParser::read(const std::string& str,std::string& value1,std::string& value2,std::string& value3)
{
    int pos=find(str);
    if (pos<=0) return false;
    if (!isString(pos+1) || !isString(pos+2)  || !isString(pos+3))
    {
        reportError("argument to `"+str+"` is missing");
        return false;
    }
    value1 = _argv[pos+1];
    value2 = _argv[pos+2];
    value3 = _argv[pos+3];
    remove(pos,4);
    return true;
}

bool ArgumentParser::read(const std::string& str,float& value1)
{
    int pos=find(str);
    if (pos<=0) return false;
    if (!isNumber(pos+1))
    {
        reportError("argument to `"+str+"` is missing");
        return false;
    }
    value1 = atof(_argv[pos+1]);
    remove(pos,2);
    return true;
}

bool ArgumentParser::read(const std::string& str,float& value1,float& value2)
{
    int pos=find(str);
    if (pos<=0) return false;
    if (!isNumber(pos+1) || !isNumber(pos+2) )
    {
        reportError("argument to `"+str+"` is missing");
        return false;
    }
    value1 = atof(_argv[pos+1]);
    value2 = atof(_argv[pos+2]);
    remove(pos,3);
    return true;
}

bool ArgumentParser::read(const std::string& str,float& value1,float& value2,float& value3)
{
    int pos=find(str);
    if (pos<=0) return false;
    if (!isNumber(pos+1) || !isNumber(pos+2)  || !isNumber(pos+3))
    {
        reportError("argument to `"+str+"` is missing");
        return false;
    }
    value1 = atof(_argv[pos+1]);
    value2 = atof(_argv[pos+2]);
    value3 = atof(_argv[pos+3]);
    remove(pos,4);
    return true;
}

bool ArgumentParser::errors(ErrorSeverity severity) const
{
    for(ErrorMessageMap::const_iterator itr=_errorMessageMap.begin();
        itr!=_errorMessageMap.end();
        ++itr)
    {
        if (itr->second>=severity) return true;
    }
    return false;
}

void ArgumentParser::reportError(const std::string& message,ErrorSeverity severity)
{
    _errorMessageMap[message]=severity;
}

void ArgumentParser::reportRemainingOptionsAsUnrecognized(ErrorSeverity severity)
{
    std::set<std::string> options;
    if (_usage)
    {
        // parse the usage options to get all the option that the application can potential handle.
        for(ApplicationUsage::UsageMap::const_iterator itr=_usage->getCommandLineOptions().begin();
            itr!=_usage->getCommandLineOptions().end();
            ++itr)
        {
            const std::string& option = itr->first;
            std::string::size_type prevpos = 0, pos = 0;
            while ((pos=option.find(' ',prevpos))!=std::string::npos)
            {
                if (option[prevpos]=='-')
                {
                    // verbose approach implemented for debugging string(const string&,unsigned int,unsigned int) operation on x86-64
                    notify(INFO)<<"option=\""<<option<<"\" \tprevpos="<<prevpos<<" \tn="<<pos-prevpos<<"\tnpos="<<std::string::npos;

                    std::string str(option,prevpos,pos-prevpos);

                    notify(INFO)<<" \tstr=\""<<str<<"\"";

                    options.insert(str);

                    notify(INFO)<<" \tinserted into options set"<<std::endl;

                    // original op which causes a crash under x86-64
                    //options.insert(std::string(option,prevpos,pos-prevpos));
                }
                prevpos=pos+1;
            }
            if (option[prevpos]=='-')
            {

                // verbose approach implemented for debugging string(const string&,unsigned int,unsigned int) operation on x86-64
                notify(INFO)<<"option=\""<<option<<"\"  \tprevpos="<<prevpos<<" \tn=npos="<<std::string::npos;

                std::string str(option,prevpos,pos-prevpos);

                notify(INFO)<<" \tstr=\""<<str<<"\"";

                options.insert(str);

                notify(INFO)<<" \tinserted into options set"<<std::endl;

                // original op
                //options.insert(std::string(option,prevpos,std::string::npos));
            }
        }

    }

    for(int pos=1;pos<argc();++pos)
    {
        // if an option and havn't been previous querried for report as unrecognized.
        if (isOption(pos) && options.find(_argv[pos])==options.end())
        {
            reportError(getProgramName() +": unrceognized option "+_argv[pos],severity);
        }
    }
}
void ArgumentParser::writeErrorMessages(std::ostream& output,ErrorSeverity severity)
{
    for(ErrorMessageMap::iterator itr=_errorMessageMap.begin();
        itr!=_errorMessageMap.end();
        ++itr)
    {
        if (itr->second>=severity)
        {
            output<< getProgramName() << ": " << itr->first << std::endl;
        }
    }
}
<|endoftext|>


<commit_before><commit_msg>Suppression fichier inutilisé.<commit_after><|endoftext|>


<commit_before>#include <StdAfx.h>
#include <UI/Controls/PaneHeader/PaneHeader.h>
#include <UI/UIFunctions.h>
#include <core/utility/strings.h>
#include <UI/DoubleBuffer.h>
#include <core/utility/output.h>
#include <core/utility/registry.h>

namespace controls
{
	static std::wstring CLASS = L"PaneHeader";

	PaneHeader::~PaneHeader()
	{
		TRACE_DESTRUCTOR(CLASS);
		CWnd::DestroyWindow();
	}

	void PaneHeader::Initialize(HWND hWnd, _In_opt_ HDC hdc, _In_ UINT nid)
	{
		m_hWndParent = hWnd;

		// Assign a nID to the collapse button that is IDD_COLLAPSE more than the control's nID
		m_nID = nid;
		m_nIDCollapse = nid + IDD_COLLAPSE;

		WNDCLASSEX wc = {};
		const auto hInst = AfxGetInstanceHandle();
		if (!::GetClassInfoEx(hInst, _T("PaneHeader"), &wc)) // STRING_OK
		{
			wc.cbSize = sizeof wc;
			wc.style = 0; // not passing CS_VREDRAW | CS_HREDRAW fixes flicker
			wc.lpszClassName = _T("PaneHeader"); // STRING_OK
			wc.lpfnWndProc = ::DefWindowProc;
			wc.hbrBackground = GetSysBrush(ui::uiColor::Background); // helps spot flashing

			RegisterClassEx(&wc);
		}

		// WS_CLIPCHILDREN is used to reduce flicker
		EC_B_S(CreateEx(
			0,
			_T("PaneHeader"), // STRING_OK
			_T("PaneHeader"), // STRING_OK
			WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_VISIBLE,
			0,
			0,
			0,
			0,
			m_hWndParent,
			reinterpret_cast<HMENU>(static_cast<INT_PTR>(IDC_PANE_HEADER)),
			nullptr));

		// We hang if we do this:
		// Necessary for TAB to work. Without this, all TABS get stuck on the control
		// instead of passing to the children.
		//EC_B_S(ModifyStyleEx(0, WS_EX_CONTROLPARENT));

		const auto sizeText = ui::GetTextExtentPoint32(hdc, m_szLabel);
		m_iLabelWidth = sizeText.cx;
		output::DebugPrint(
			output::dbgLevel::Draw,
			L"PaneHeader::Initialize m_iLabelWidth:%d \"%ws\"\n",
			m_iLabelWidth,
			m_szLabel.c_str());
	}

	BEGIN_MESSAGE_MAP(PaneHeader, CWnd)
	ON_WM_CREATE()
	ON_WM_WINDOWPOSCHANGED()
	END_MESSAGE_MAP()

	int PaneHeader::OnCreate(LPCREATESTRUCT /*lpCreateStruct*/)
	{
		EC_B_S(m_leftLabel.Create(
			WS_CHILD | WS_CLIPSIBLINGS | ES_READONLY | WS_VISIBLE | WS_TABSTOP, CRect(0, 0, 0, 0), this, m_nID));
		::SetWindowTextW(m_leftLabel.m_hWnd, m_szLabel.c_str());
		ui::SubclassLabel(m_leftLabel.m_hWnd);
		if (m_bCollapsible)
		{
			StyleLabel(m_leftLabel.m_hWnd, ui::uiLabelStyle::PaneHeaderLabel);
		}

		EC_B_S(m_rightLabel.Create(
			WS_CHILD | WS_CLIPSIBLINGS | ES_READONLY | WS_VISIBLE | WS_TABSTOP,
			CRect(0, 0, 0, 0),
			this,
			IDD_COUNTLABEL));
		ui::SubclassLabel(m_rightLabel.m_hWnd);
		StyleLabel(m_rightLabel.m_hWnd, ui::uiLabelStyle::PaneHeaderText);

		if (m_bCollapsible)
		{
			EC_B_S(m_CollapseButton.Create(
				nullptr, WS_TABSTOP | WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE, CRect(0, 0, 0, 0), this, m_nIDCollapse));
			StyleButton(
				m_CollapseButton.m_hWnd, m_bCollapsed ? ui::uiButtonStyle::UpArrow : ui::uiButtonStyle::DownArrow);
		}

		// If we need an action button, go ahead and create it
		if (m_nIDAction)
		{
			EC_B_S(m_actionButton.Create(
				strings::wstringTotstring(m_szActionButton).c_str(),
				WS_TABSTOP | WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE,
				CRect(0, 0, 0, 0),
				this,
				m_nIDAction));
			StyleButton(m_actionButton.m_hWnd, ui::uiButtonStyle::Unstyled);
		}

		m_bInitialized = true;
		return 0;
	}

	LRESULT PaneHeader::WindowProc(const UINT message, const WPARAM wParam, const LPARAM lParam)
	{
		LRESULT lRes = 0;
		if (ui::HandleControlUI(message, wParam, lParam, &lRes)) return lRes;

		switch (message)
		{
		case WM_PAINT:
			auto ps = PAINTSTRUCT{};
			::BeginPaint(m_hWnd, &ps);
			if (ps.hdc)
			{
				auto rcWin = RECT{};
				::GetClientRect(m_hWnd, &rcWin);
				const auto background = registry::uiDiag ? ui::GetSysBrush(ui::uiColor::TestGreen)
														 : ui::GetSysBrush(ui::uiColor::Background);
				FillRect(ps.hdc, &rcWin, background);
			}

			::EndPaint(m_hWnd, &ps);
			return 0;
		case WM_WINDOWPOSCHANGED:
			RecalcLayout();
			return 0;
		case WM_CLOSE:
			::SendMessage(m_hWndParent, message, wParam, lParam);
			return true;
		case WM_HELP:
			return true;
		case WM_COMMAND:
		{
			const auto nCode = HIWORD(wParam);
			// Pass button clicks up to parent
			if (BN_CLICKED == nCode)
			{
				::SendMessage(m_hWndParent, message, wParam, lParam);
			}

			break;
		}
		}

		return CWnd::WindowProc(message, wParam, lParam);
	}

	// Position collapse button, labels, action button.
	void PaneHeader::RecalcLayout()
	{
		if (!m_bInitialized) return;
		auto hWinPosInfo = WC_D(HDWP, BeginDeferWindowPos(4));
		if (hWinPosInfo)
		{
			auto rcWin = RECT{};
			::GetClientRect(m_hWnd, &rcWin);
			const auto width = rcWin.right - rcWin.left;
			const auto height = rcWin.bottom - rcWin.top;
			output::DebugPrint(
				output::dbgLevel::Draw,
				L"PaneHeader::DeferWindowPos width:%d height:%d v:%d\n",
				width,
				height,
				IsWindowVisible());
			auto curX = 0;
			const auto actionButtonWidth = m_actionButtonWidth ? m_actionButtonWidth + 2 * m_iMargin : 0;
			const auto actionButtonAndGutterWidth = actionButtonWidth ? actionButtonWidth + m_iSideMargin : 0;
			if (m_bCollapsible)
			{
				hWinPosInfo = ui::DeferWindowPos(
					hWinPosInfo,
					m_CollapseButton.GetSafeHwnd(),
					curX,
					0,
					width - actionButtonAndGutterWidth,
					height,
					L"PaneHeader::DeferWindowPos::collapseButton");
				curX += m_iButtonHeight;
			}

			hWinPosInfo = ui::DeferWindowPos(
				hWinPosInfo,
				m_leftLabel.GetSafeHwnd(),
				curX,
				0,
				m_iLabelWidth,
				height,
				L"PaneHeader::DeferWindowPos::leftLabel");

			if (!m_bCollapsed)
			{
				// Drop the count on top of the label we drew above
				if (m_rightLabel.GetSafeHwnd())
				{
					hWinPosInfo = ui::DeferWindowPos(
						hWinPosInfo,
						m_rightLabel.GetSafeHwnd(),
						width - m_rightLabelWidth - actionButtonAndGutterWidth,
						0,
						m_rightLabelWidth,
						height,
						L"PaneHeader::DeferWindowPos::rightLabel");
				}
			}

			if (m_nIDAction)
			{
				// Drop the action button next to the label we drew above
				hWinPosInfo = ui::DeferWindowPos(
					hWinPosInfo,
					m_actionButton.GetSafeHwnd(),
					width - actionButtonWidth,
					0,
					actionButtonWidth,
					height,
					L"PaneHeader::DeferWindowPos::actionButton");
			}

			output::DebugPrint(output::dbgLevel::Draw, L"PaneHeader::DeferWindowPos end\n");
			EC_B_S(EndDeferWindowPos(hWinPosInfo));
		}
	}

	int PaneHeader::GetMinWidth()
	{
		auto cx = m_iLabelWidth;
		if (m_bCollapsible) cx += m_iButtonHeight;
		if (m_rightLabelWidth)
		{
			cx += m_iSideMargin;
			cx += m_rightLabelWidth;
		}

		if (m_actionButtonWidth)
		{
			cx += m_iSideMargin;
			cx += m_actionButtonWidth + 2 * m_iMargin;
		}

		return cx;
	}

	void PaneHeader::SetRightLabel(const std::wstring szLabel)
	{
		if (!m_bInitialized) return;
		EC_B_S(::SetWindowTextW(m_rightLabel.m_hWnd, szLabel.c_str()));

		const auto hdc = ::GetDC(m_rightLabel.GetSafeHwnd());
		const auto hfontOld = SelectObject(hdc, ui::GetSegoeFont());
		const auto sizeText = ui::GetTextExtentPoint32(hdc, szLabel);
		static_cast<void>(SelectObject(hdc, hfontOld));
		::ReleaseDC(m_rightLabel.GetSafeHwnd(), hdc);
		m_rightLabelWidth = sizeText.cx;

		RecalcLayout();
	}

	void PaneHeader::SetActionButton(const std::wstring szActionButton)
	{
		// Don't bother if we never enabled the button
		if (m_nIDAction == 0) return;

		EC_B_S(::SetWindowTextW(m_actionButton.GetSafeHwnd(), szActionButton.c_str()));

		m_szActionButton = szActionButton;
		const auto hdc = ::GetDC(m_actionButton.GetSafeHwnd());
		const auto hfontOld = SelectObject(hdc, ui::GetSegoeFont());
		const auto sizeText = ui::GetTextExtentPoint32(hdc, szActionButton);
		static_cast<void>(SelectObject(hdc, hfontOld));
		::ReleaseDC(m_actionButton.GetSafeHwnd(), hdc);
		m_actionButtonWidth = sizeText.cx;

		RecalcLayout();
	}

	bool PaneHeader::HandleChange(UINT nID)
	{
		// Collapse buttons have a nID IDD_COLLAPSE higher than nID of the pane they toggle.
		// So if we get asked about one that matches, we can assume it's time to toggle our collapse.
		if (m_nIDCollapse == nID)
		{
			OnToggleCollapse();
			return true;
		}

		return false;
	}

	void PaneHeader::OnToggleCollapse()
	{
		m_bCollapsed = !m_bCollapsed;

		StyleButton(m_CollapseButton.m_hWnd, m_bCollapsed ? ui::uiButtonStyle::UpArrow : ui::uiButtonStyle::DownArrow);
		WC_B_S(m_rightLabel.ShowWindow(m_bCollapsed ? SW_HIDE : SW_SHOW));

		// Trigger a redraw
		::PostMessage(m_hWndParent, WM_COMMAND, IDD_RECALCLAYOUT, NULL);
	}

	void PaneHeader::SetMargins(
		int iMargin,
		int iSideMargin,
		int iLabelHeight, // Height of the label
		int iButtonHeight) // Height of button
	{
		m_iMargin = iMargin;
		m_iSideMargin = iSideMargin;
		m_iLabelHeight = iLabelHeight;
		m_iButtonHeight = iButtonHeight;
	}
} // namespace controls<commit_msg>remove extra map<commit_after>#include <StdAfx.h>
#include <UI/Controls/PaneHeader/PaneHeader.h>
#include <UI/UIFunctions.h>
#include <core/utility/strings.h>
#include <UI/DoubleBuffer.h>
#include <core/utility/output.h>
#include <core/utility/registry.h>

namespace controls
{
	static std::wstring CLASS = L"PaneHeader";

	PaneHeader::~PaneHeader()
	{
		TRACE_DESTRUCTOR(CLASS);
		CWnd::DestroyWindow();
	}

	void PaneHeader::Initialize(HWND hWnd, _In_opt_ HDC hdc, _In_ UINT nid)
	{
		m_hWndParent = hWnd;

		// Assign a nID to the collapse button that is IDD_COLLAPSE more than the control's nID
		m_nID = nid;
		m_nIDCollapse = nid + IDD_COLLAPSE;

		WNDCLASSEX wc = {};
		const auto hInst = AfxGetInstanceHandle();
		if (!::GetClassInfoEx(hInst, _T("PaneHeader"), &wc)) // STRING_OK
		{
			wc.cbSize = sizeof wc;
			wc.style = 0; // not passing CS_VREDRAW | CS_HREDRAW fixes flicker
			wc.lpszClassName = _T("PaneHeader"); // STRING_OK
			wc.lpfnWndProc = ::DefWindowProc;
			wc.hbrBackground = GetSysBrush(ui::uiColor::Background); // helps spot flashing

			RegisterClassEx(&wc);
		}

		// WS_CLIPCHILDREN is used to reduce flicker
		EC_B_S(CreateEx(
			0,
			_T("PaneHeader"), // STRING_OK
			_T("PaneHeader"), // STRING_OK
			WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_VISIBLE,
			0,
			0,
			0,
			0,
			m_hWndParent,
			reinterpret_cast<HMENU>(static_cast<INT_PTR>(IDC_PANE_HEADER)),
			nullptr));

		// We hang if we do this:
		// Necessary for TAB to work. Without this, all TABS get stuck on the control
		// instead of passing to the children.
		//EC_B_S(ModifyStyleEx(0, WS_EX_CONTROLPARENT));

		const auto sizeText = ui::GetTextExtentPoint32(hdc, m_szLabel);
		m_iLabelWidth = sizeText.cx;
		output::DebugPrint(
			output::dbgLevel::Draw,
			L"PaneHeader::Initialize m_iLabelWidth:%d \"%ws\"\n",
			m_iLabelWidth,
			m_szLabel.c_str());
	}

	BEGIN_MESSAGE_MAP(PaneHeader, CWnd)
	ON_WM_CREATE()
	END_MESSAGE_MAP()

	int PaneHeader::OnCreate(LPCREATESTRUCT /*lpCreateStruct*/)
	{
		EC_B_S(m_leftLabel.Create(
			WS_CHILD | WS_CLIPSIBLINGS | ES_READONLY | WS_VISIBLE | WS_TABSTOP, CRect(0, 0, 0, 0), this, m_nID));
		::SetWindowTextW(m_leftLabel.m_hWnd, m_szLabel.c_str());
		ui::SubclassLabel(m_leftLabel.m_hWnd);
		if (m_bCollapsible)
		{
			StyleLabel(m_leftLabel.m_hWnd, ui::uiLabelStyle::PaneHeaderLabel);
		}

		EC_B_S(m_rightLabel.Create(
			WS_CHILD | WS_CLIPSIBLINGS | ES_READONLY | WS_VISIBLE | WS_TABSTOP,
			CRect(0, 0, 0, 0),
			this,
			IDD_COUNTLABEL));
		ui::SubclassLabel(m_rightLabel.m_hWnd);
		StyleLabel(m_rightLabel.m_hWnd, ui::uiLabelStyle::PaneHeaderText);

		if (m_bCollapsible)
		{
			EC_B_S(m_CollapseButton.Create(
				nullptr, WS_TABSTOP | WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE, CRect(0, 0, 0, 0), this, m_nIDCollapse));
			StyleButton(
				m_CollapseButton.m_hWnd, m_bCollapsed ? ui::uiButtonStyle::UpArrow : ui::uiButtonStyle::DownArrow);
		}

		// If we need an action button, go ahead and create it
		if (m_nIDAction)
		{
			EC_B_S(m_actionButton.Create(
				strings::wstringTotstring(m_szActionButton).c_str(),
				WS_TABSTOP | WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE,
				CRect(0, 0, 0, 0),
				this,
				m_nIDAction));
			StyleButton(m_actionButton.m_hWnd, ui::uiButtonStyle::Unstyled);
		}

		m_bInitialized = true;
		return 0;
	}

	LRESULT PaneHeader::WindowProc(const UINT message, const WPARAM wParam, const LPARAM lParam)
	{
		LRESULT lRes = 0;
		if (ui::HandleControlUI(message, wParam, lParam, &lRes)) return lRes;

		switch (message)
		{
		case WM_PAINT:
			auto ps = PAINTSTRUCT{};
			::BeginPaint(m_hWnd, &ps);
			if (ps.hdc)
			{
				auto rcWin = RECT{};
				::GetClientRect(m_hWnd, &rcWin);
				const auto background = registry::uiDiag ? ui::GetSysBrush(ui::uiColor::TestGreen)
														 : ui::GetSysBrush(ui::uiColor::Background);
				FillRect(ps.hdc, &rcWin, background);
			}

			::EndPaint(m_hWnd, &ps);
			return 0;
		case WM_WINDOWPOSCHANGED:
			RecalcLayout();
			return 0;
		case WM_CLOSE:
			::SendMessage(m_hWndParent, message, wParam, lParam);
			return true;
		case WM_HELP:
			return true;
		case WM_COMMAND:
		{
			const auto nCode = HIWORD(wParam);
			// Pass button clicks up to parent
			if (BN_CLICKED == nCode)
			{
				::SendMessage(m_hWndParent, message, wParam, lParam);
			}

			break;
		}
		}

		return CWnd::WindowProc(message, wParam, lParam);
	}

	// Position collapse button, labels, action button.
	void PaneHeader::RecalcLayout()
	{
		if (!m_bInitialized) return;
		auto hWinPosInfo = WC_D(HDWP, BeginDeferWindowPos(4));
		if (hWinPosInfo)
		{
			auto rcWin = RECT{};
			::GetClientRect(m_hWnd, &rcWin);
			const auto width = rcWin.right - rcWin.left;
			const auto height = rcWin.bottom - rcWin.top;
			output::DebugPrint(
				output::dbgLevel::Draw,
				L"PaneHeader::DeferWindowPos width:%d height:%d v:%d\n",
				width,
				height,
				IsWindowVisible());
			auto curX = 0;
			const auto actionButtonWidth = m_actionButtonWidth ? m_actionButtonWidth + 2 * m_iMargin : 0;
			const auto actionButtonAndGutterWidth = actionButtonWidth ? actionButtonWidth + m_iSideMargin : 0;
			if (m_bCollapsible)
			{
				hWinPosInfo = ui::DeferWindowPos(
					hWinPosInfo,
					m_CollapseButton.GetSafeHwnd(),
					curX,
					0,
					width - actionButtonAndGutterWidth,
					height,
					L"PaneHeader::DeferWindowPos::collapseButton");
				curX += m_iButtonHeight;
			}

			hWinPosInfo = ui::DeferWindowPos(
				hWinPosInfo,
				m_leftLabel.GetSafeHwnd(),
				curX,
				0,
				m_iLabelWidth,
				height,
				L"PaneHeader::DeferWindowPos::leftLabel");

			if (!m_bCollapsed)
			{
				// Drop the count on top of the label we drew above
				if (m_rightLabel.GetSafeHwnd())
				{
					hWinPosInfo = ui::DeferWindowPos(
						hWinPosInfo,
						m_rightLabel.GetSafeHwnd(),
						width - m_rightLabelWidth - actionButtonAndGutterWidth,
						0,
						m_rightLabelWidth,
						height,
						L"PaneHeader::DeferWindowPos::rightLabel");
				}
			}

			if (m_nIDAction)
			{
				// Drop the action button next to the label we drew above
				hWinPosInfo = ui::DeferWindowPos(
					hWinPosInfo,
					m_actionButton.GetSafeHwnd(),
					width - actionButtonWidth,
					0,
					actionButtonWidth,
					height,
					L"PaneHeader::DeferWindowPos::actionButton");
			}

			output::DebugPrint(output::dbgLevel::Draw, L"PaneHeader::DeferWindowPos end\n");
			EC_B_S(EndDeferWindowPos(hWinPosInfo));
		}
	}

	int PaneHeader::GetMinWidth()
	{
		auto cx = m_iLabelWidth;
		if (m_bCollapsible) cx += m_iButtonHeight;
		if (m_rightLabelWidth)
		{
			cx += m_iSideMargin;
			cx += m_rightLabelWidth;
		}

		if (m_actionButtonWidth)
		{
			cx += m_iSideMargin;
			cx += m_actionButtonWidth + 2 * m_iMargin;
		}

		return cx;
	}

	void PaneHeader::SetRightLabel(const std::wstring szLabel)
	{
		if (!m_bInitialized) return;
		EC_B_S(::SetWindowTextW(m_rightLabel.m_hWnd, szLabel.c_str()));

		const auto hdc = ::GetDC(m_rightLabel.GetSafeHwnd());
		const auto hfontOld = SelectObject(hdc, ui::GetSegoeFont());
		const auto sizeText = ui::GetTextExtentPoint32(hdc, szLabel);
		static_cast<void>(SelectObject(hdc, hfontOld));
		::ReleaseDC(m_rightLabel.GetSafeHwnd(), hdc);
		m_rightLabelWidth = sizeText.cx;

		RecalcLayout();
	}

	void PaneHeader::SetActionButton(const std::wstring szActionButton)
	{
		// Don't bother if we never enabled the button
		if (m_nIDAction == 0) return;

		EC_B_S(::SetWindowTextW(m_actionButton.GetSafeHwnd(), szActionButton.c_str()));

		m_szActionButton = szActionButton;
		const auto hdc = ::GetDC(m_actionButton.GetSafeHwnd());
		const auto hfontOld = SelectObject(hdc, ui::GetSegoeFont());
		const auto sizeText = ui::GetTextExtentPoint32(hdc, szActionButton);
		static_cast<void>(SelectObject(hdc, hfontOld));
		::ReleaseDC(m_actionButton.GetSafeHwnd(), hdc);
		m_actionButtonWidth = sizeText.cx;

		RecalcLayout();
	}

	bool PaneHeader::HandleChange(UINT nID)
	{
		// Collapse buttons have a nID IDD_COLLAPSE higher than nID of the pane they toggle.
		// So if we get asked about one that matches, we can assume it's time to toggle our collapse.
		if (m_nIDCollapse == nID)
		{
			OnToggleCollapse();
			return true;
		}

		return false;
	}

	void PaneHeader::OnToggleCollapse()
	{
		m_bCollapsed = !m_bCollapsed;

		StyleButton(m_CollapseButton.m_hWnd, m_bCollapsed ? ui::uiButtonStyle::UpArrow : ui::uiButtonStyle::DownArrow);
		WC_B_S(m_rightLabel.ShowWindow(m_bCollapsed ? SW_HIDE : SW_SHOW));

		// Trigger a redraw
		::PostMessage(m_hWndParent, WM_COMMAND, IDD_RECALCLAYOUT, NULL);
	}

	void PaneHeader::SetMargins(
		int iMargin,
		int iSideMargin,
		int iLabelHeight, // Height of the label
		int iButtonHeight) // Height of button
	{
		m_iMargin = iMargin;
		m_iSideMargin = iSideMargin;
		m_iLabelHeight = iLabelHeight;
		m_iButtonHeight = iButtonHeight;
	}
} // namespace controls<|endoftext|>