<commit_before>/* Copyright 2021 The TensorFlow Authors. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/

#include "tensorflow/core/data/root_dataset.h"

#include "tensorflow/core/data/dataset_utils.h"
#include "tensorflow/core/data/name_utils.h"
#include "tensorflow/core/data/rewrite_utils.h"
#include "tensorflow/core/platform/errors.h"
#include "tensorflow/core/platform/stringprintf.h"

namespace tensorflow {
namespace data {
namespace {

constexpr char kDatasetType[] = "Root";
constexpr char kAlgorithm[] = "algorithm";
constexpr char kCpuBudget[] = "cpu_budget";
constexpr char kRamBudget[] = "ram_budget_bytes";
constexpr char kHillClimb[] = "hill_climb";
constexpr char kGradientDescent[] = "gradient_descent";
constexpr char kIntraOpParallelism[] = "intra_op_parallelism";
constexpr char kPrivateThreadpoolSize[] = "threadpool_size";
constexpr char kMemBandwidth[] = "mem_bw_used_megabytes_per_sec";

// Default share of available RAM that can be used by model's internal buffers.
constexpr double kRamBudgetShare = 0.5;

// If value `x` matches `y`, returns default value `z`. Otherwise, return `x`.
inline int64_t value_or_default(int64_t x, int64_t y, int64_t z) {
  return x == y ? z : x;
}

}  // namespace

// static
Status RootDataset::FromOptions(DatasetBase* input, DatasetBase** output) {
  const Options& options = input->options();
  Params params;
  if (ShouldConfigureMaxIntraOpParallelism(options)) {
    params.max_intra_op_parallelism =
        options.threading_options().max_intra_op_parallelism();
  }
  if (ShouldUsePrivateThreadPool(options)) {
    params.private_threadpool_size =
        options.threading_options().private_threadpool_size();
  }
  params.autotune = ShouldUseAutotuning(options);
  if (params.autotune) {
    params.autotune_algorithm = model::AutotuneAlgorithm::HILL_CLIMB;
    params.autotune_cpu_budget = value_or_default(
        options.autotune_options().cpu_budget(), 0, GetCpuBudget());
    params.autotune_ram_budget =
        value_or_default(options.autotune_options().ram_budget(), 0,
                         kRamBudgetShare * port::AvailableRam());
  }
  *output = new RootDataset(input, params);
  return Status::OK();
}

class RootDataset::Iterator : public DatasetIterator<RootDataset> {
 public:
  explicit Iterator(const Params& params)
      : DatasetIterator<RootDataset>(params) {
    if (dataset()->params_.autotune) {
      model_ = std::make_shared<model::Model>();
    }
    if (dataset()->params_.max_intra_op_parallelism >= 0) {
      max_intra_op_parallelism_ =
          value_or_default(dataset()->params_.max_intra_op_parallelism, 0,
                           port::MaxParallelism());
    }
    if (dataset()->params_.private_threadpool_size >= 0) {
      threadpool_size_ =
          value_or_default(dataset()->params_.private_threadpool_size, 0,
                           port::MaxParallelism());
      thread_pool_ = absl::make_unique<thread::ThreadPool>(
          Env::Default(), ThreadOptions{}, "data_private_threadpool",
          threadpool_size_);
    }
    cancellation_manager_ = absl::make_unique<CancellationManager>();
  }

  ~Iterator() override { cancellation_manager_->StartCancel(); }

  Status Initialize(IteratorContext* ctx) override {
    return dataset()->input_->MakeIterator(IteratorContext(CreateParams(ctx)),
                                           this, prefix(), &input_impl_);
  }

  Status GetNextInternal(IteratorContext* ctx, std::vector<Tensor>* out_tensors,
                         bool* end_of_sequence) override {
    if (dataset()->params_.autotune) {
      TF_RETURN_IF_ERROR(EnsureModelThreadStarted(ctx));
    }
    return input_impl_->GetNext(IteratorContext(CreateParams(ctx)), out_tensors,
                                end_of_sequence);
  }

 protected:
  std::shared_ptr<model::Node> CreateNode(
      IteratorContext* ctx, model::Node::Args args) const override {
    return model::MakeKnownRatioNode(std::move(args), /*ratio=*/1);
  }

  Status SaveInternal(SerializationContext* ctx,
                      IteratorStateWriter* writer) override {
    TF_RETURN_IF_ERROR(SaveInput(ctx, writer, input_impl_));
    return Status::OK();
  }

  Status RestoreInternal(IteratorContext* ctx,
                         IteratorStateReader* reader) override {
    TF_RETURN_IF_ERROR(
        RestoreInput(IteratorContext(CreateParams(ctx)), reader, input_impl_));
    return Status::OK();
  }

  TraceMeMetadata GetTraceMeMetadata() const override {
    tensorflow::data::TraceMeMetadata traceme_metadata =
        dataset()->traceme_metadata_;
    const int64_t mem_bw = port::GetMemoryBandwidthInfo().bw_used;
    if (mem_bw != INT64_MAX) {
      traceme_metadata.push_back(std::make_pair(
          kMemBandwidth,
          strings::Printf("%lld", static_cast<long long>(mem_bw))));
    }
    return traceme_metadata;
  }

 private:
  IteratorContext::Params CreateParams(IteratorContext* ctx) {
    IteratorContext::Params params(ctx);
    if (dataset()->params_.autotune) {
      params.model = model_;
    }
    if (dataset()->params_.private_threadpool_size >= 0) {
      params.runner = [pool = thread_pool_.get()](std::function<void()> c) {
        pool->Schedule(std::move(c));
      };
      params.runner_threadpool_size = threadpool_size_;
    }
    if (dataset()->params_.max_intra_op_parallelism >= 0) {
      params.runner =
          RunnerWithMaxParallelism(params.runner, max_intra_op_parallelism_);
    }
    return params;
  }

  Status EnsureModelThreadStarted(IteratorContext* ctx) {
    mutex_lock l(mu_);
    if (!model_thread_) {
      model_thread_ = ctx->StartThread("tf_data_model", [this]() {
        Status status =
            model_->OptimizeLoop(dataset()->params_.autotune_algorithm,
                                 dataset()->params_.autotune_cpu_budget,
                                 dataset()->params_.autotune_ram_budget,
                                 cancellation_manager_.get());
        if (!status.ok()) {
          LOG(WARNING) << "Optimization loop failed: " << status.ToString();
        }
      });
    }
    return Status::OK();
  }

  std::shared_ptr<model::Model> model_ = nullptr;
  // Controls cancellation of `model_thread_`. Must be ordered before
  // `model_thread_` so that `model_thread_` is destroyed first.
  std::unique_ptr<CancellationManager> cancellation_manager_;
  mutex mu_;
  std::unique_ptr<Thread> model_thread_ TF_GUARDED_BY(mu_);
  int64_t max_intra_op_parallelism_;
  int64_t threadpool_size_;
  std::unique_ptr<thread::ThreadPool> thread_pool_;

  // Must be ordered last as its execution may depend on other members.
  std::unique_ptr<IteratorBase> input_impl_;
};

RootDataset::RootDataset(const DatasetBase* input, Params params)
    : DatasetBase(DatasetContext({name_utils::OpName(kDatasetType),
                                  name_utils::OpName(kDatasetType)})),
      input_(input),
      params_(std::move(params)) {
  if (params_.autotune) {
    traceme_metadata_.push_back(std::make_pair(
        kAlgorithm,
        params_.autotune_algorithm == model::AutotuneAlgorithm::HILL_CLIMB
            ? kHillClimb
            : kGradientDescent));
    traceme_metadata_.push_back(std::make_pair(
        kCpuBudget, strings::Printf("%lld", static_cast<long long>(
                                                params_.autotune_cpu_budget))));
    traceme_metadata_.push_back(std::make_pair(
        kRamBudget, strings::Printf("%lld", static_cast<long long>(
                                                params_.autotune_ram_budget))));
  }
  if (params_.max_intra_op_parallelism >= 0) {
    traceme_metadata_.push_back(std::make_pair(
        kIntraOpParallelism,
        strings::Printf("%lld", static_cast<long long>(value_or_default(
                                    params_.max_intra_op_parallelism, 0,
                                    port::MaxParallelism())))));
  }
  if (params_.private_threadpool_size >= 0) {
    traceme_metadata_.push_back(std::make_pair(
        kPrivateThreadpoolSize,
        strings::Printf("%lld", static_cast<long long>(value_or_default(
                                    params_.private_threadpool_size, 0,
                                    port::MaxParallelism())))));
  }
  input_->Ref();
}

RootDataset::~RootDataset() { input_->Unref(); }

std::unique_ptr<IteratorBase> RootDataset::MakeIteratorInternal(
    const string& prefix) const {
  return absl::make_unique<Iterator>(
      Iterator::Params{this, name_utils::IteratorPrefix(kDatasetType, prefix)});
}

const DataTypeVector& RootDataset::output_dtypes() const {
  return input_->output_dtypes();
}

const std::vector<PartialTensorShape>& RootDataset::output_shapes() const {
  return input_->output_shapes();
}

string RootDataset::DebugString() const {
  return name_utils::DatasetDebugString(kDatasetType);
}

int64_t RootDataset::Cardinality() const { return input_->Cardinality(); }

Status RootDataset::InputDatasets(
    std::vector<const DatasetBase*>* inputs) const {
  inputs->push_back(input_);
  return Status::OK();
}

Status RootDataset::CheckExternalState() const {
  return input_->CheckExternalState();
}

Status RootDataset::AsGraphDefInternal(SerializationContext* ctx,
                                       DatasetGraphDefBuilder* b,
                                       Node** output) const {
  return errors::Unimplemented("RootDataset does not support serialization.");
}

#if !defined(IS_MOBILE_PLATFORM)
Status FinalizeDataset(OpKernelContext* ctx, DatasetBase* input,
                       DatasetBase** output) {
  const Options& options = input->options();
  absl::flat_hash_set<tstring> optimizations_enabled;
  absl::flat_hash_set<tstring> optimizations_disabled;
  absl::flat_hash_set<tstring> optimizations_default;
  GetOptimizations(options, &optimizations_enabled, &optimizations_disabled,
                   &optimizations_default);
  // Disable `enable_gradient_descent` as it assumes presence of ModelDatasetOp.
  optimizations_disabled.insert("enable_gradient_descent");

  auto experiments = GetExperiments();
  LogAndRecordExperiments(experiments);
  auto optimizations =
      SelectOptimizations(experiments, optimizations_enabled,
                          optimizations_disabled, optimizations_default);
  if (optimizations.empty()) {
    return RootDataset::FromOptions(input, output);
  }

  auto optimization_configs = CreateGraphRewriteConfigs(options);
  auto config_factory = [&optimizations, &optimization_configs]() {
    return CreateRewriterConfig(optimizations, optimization_configs);
  };
  Status s = RewriteDataset(ctx, input, std::move(config_factory),
                            /*record_fingerprint=*/true, output);
  if (errors::IsDeadlineExceeded(s)) {
    // Ignore DeadlineExceeded as it implies that the attempted rewrite took too
    // long which should not prevent further computation.
    LOG(WARNING) << s.ToString();
    return RootDataset::FromOptions(input, output);
  }
  if (!s.ok()) {
    return s;
  }
  input = *output;
  TF_RETURN_IF_ERROR(RootDataset::FromOptions(input, output));
  input->Unref();
  return Status::OK();
}
#else   // !IS_MOBILE_PLATFORM
Status FinalizeDataset(OpKernelContext* ctx, DatasetBase* input,
                       DatasetBase** output) {
  return RootDataset::FromOptions(input, output);
}
#endif  // !IS_MOBILE_PLATFORM

}  // namespace data
}  // namespace tensorflow
<commit_msg>[tf.data] Include active experiments in traceme of the Root transformation.<commit_after>/* Copyright 2021 The TensorFlow Authors. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/

#include "tensorflow/core/data/root_dataset.h"

#include "tensorflow/core/data/dataset_utils.h"
#include "tensorflow/core/data/name_utils.h"
#include "tensorflow/core/data/rewrite_utils.h"
#include "tensorflow/core/platform/errors.h"
#include "tensorflow/core/platform/stringprintf.h"

namespace tensorflow {
namespace data {
namespace {

constexpr char kDatasetType[] = "Root";

constexpr char kAlgorithm[] = "algorithm";
constexpr char kCpuBudget[] = "cpu_budget";
constexpr char kExperiments[] = "experiments";
constexpr char kMemBandwidth[] = "mem_bw_used_megabytes_per_sec";
constexpr char kIntraOpParallelism[] = "intra_op_parallelism";
constexpr char kPrivateThreadpoolSize[] = "threadpool_size";
constexpr char kRamBudget[] = "ram_budget_bytes";

constexpr char kHillClimb[] = "hill_climb";
constexpr char kGradientDescent[] = "gradient_descent";

// Default share of available RAM that can be used by model's internal buffers.
constexpr double kRamBudgetShare = 0.5;

// If value `x` matches `y`, returns default value `z`. Otherwise, return `x`.
inline int64_t value_or_default(int64_t x, int64_t y, int64_t z) {
  return x == y ? z : x;
}

}  // namespace

// static
Status RootDataset::FromOptions(DatasetBase* input, DatasetBase** output) {
  const Options& options = input->options();
  Params params;
  if (ShouldConfigureMaxIntraOpParallelism(options)) {
    params.max_intra_op_parallelism =
        options.threading_options().max_intra_op_parallelism();
  }
  if (ShouldUsePrivateThreadPool(options)) {
    params.private_threadpool_size =
        options.threading_options().private_threadpool_size();
  }
  params.autotune = ShouldUseAutotuning(options);
  if (params.autotune) {
    params.autotune_algorithm = model::AutotuneAlgorithm::HILL_CLIMB;
    params.autotune_cpu_budget = value_or_default(
        options.autotune_options().cpu_budget(), 0, GetCpuBudget());
    params.autotune_ram_budget =
        value_or_default(options.autotune_options().ram_budget(), 0,
                         kRamBudgetShare * port::AvailableRam());
  }
  *output = new RootDataset(input, params);
  return Status::OK();
}

class RootDataset::Iterator : public DatasetIterator<RootDataset> {
 public:
  explicit Iterator(const Params& params)
      : DatasetIterator<RootDataset>(params) {
    if (dataset()->params_.autotune) {
      model_ = std::make_shared<model::Model>();
    }
    if (dataset()->params_.max_intra_op_parallelism >= 0) {
      max_intra_op_parallelism_ =
          value_or_default(dataset()->params_.max_intra_op_parallelism, 0,
                           port::MaxParallelism());
    }
    if (dataset()->params_.private_threadpool_size >= 0) {
      threadpool_size_ =
          value_or_default(dataset()->params_.private_threadpool_size, 0,
                           port::MaxParallelism());
      thread_pool_ = absl::make_unique<thread::ThreadPool>(
          Env::Default(), ThreadOptions{}, "data_private_threadpool",
          threadpool_size_);
    }
    cancellation_manager_ = absl::make_unique<CancellationManager>();
  }

  ~Iterator() override { cancellation_manager_->StartCancel(); }

  Status Initialize(IteratorContext* ctx) override {
    return dataset()->input_->MakeIterator(IteratorContext(CreateParams(ctx)),
                                           this, prefix(), &input_impl_);
  }

  Status GetNextInternal(IteratorContext* ctx, std::vector<Tensor>* out_tensors,
                         bool* end_of_sequence) override {
    if (dataset()->params_.autotune) {
      TF_RETURN_IF_ERROR(EnsureModelThreadStarted(ctx));
    }
    return input_impl_->GetNext(IteratorContext(CreateParams(ctx)), out_tensors,
                                end_of_sequence);
  }

 protected:
  std::shared_ptr<model::Node> CreateNode(
      IteratorContext* ctx, model::Node::Args args) const override {
    return model::MakeKnownRatioNode(std::move(args), /*ratio=*/1);
  }

  Status SaveInternal(SerializationContext* ctx,
                      IteratorStateWriter* writer) override {
    TF_RETURN_IF_ERROR(SaveInput(ctx, writer, input_impl_));
    return Status::OK();
  }

  Status RestoreInternal(IteratorContext* ctx,
                         IteratorStateReader* reader) override {
    TF_RETURN_IF_ERROR(
        RestoreInput(IteratorContext(CreateParams(ctx)), reader, input_impl_));
    return Status::OK();
  }

  TraceMeMetadata GetTraceMeMetadata() const override {
    tensorflow::data::TraceMeMetadata traceme_metadata =
        dataset()->traceme_metadata_;
    const int64_t mem_bw = port::GetMemoryBandwidthInfo().bw_used;
    if (mem_bw != INT64_MAX) {
      traceme_metadata.push_back(std::make_pair(
          kMemBandwidth,
          strings::Printf("%lld", static_cast<long long>(mem_bw))));
    }
    return traceme_metadata;
  }

 private:
  IteratorContext::Params CreateParams(IteratorContext* ctx) {
    IteratorContext::Params params(ctx);
    if (dataset()->params_.autotune) {
      params.model = model_;
    }
    if (dataset()->params_.private_threadpool_size >= 0) {
      params.runner = [pool = thread_pool_.get()](std::function<void()> c) {
        pool->Schedule(std::move(c));
      };
      params.runner_threadpool_size = threadpool_size_;
    }
    if (dataset()->params_.max_intra_op_parallelism >= 0) {
      params.runner =
          RunnerWithMaxParallelism(params.runner, max_intra_op_parallelism_);
    }
    return params;
  }

  Status EnsureModelThreadStarted(IteratorContext* ctx) {
    mutex_lock l(mu_);
    if (!model_thread_) {
      model_thread_ = ctx->StartThread("tf_data_model", [this]() {
        Status status =
            model_->OptimizeLoop(dataset()->params_.autotune_algorithm,
                                 dataset()->params_.autotune_cpu_budget,
                                 dataset()->params_.autotune_ram_budget,
                                 cancellation_manager_.get());
        if (!status.ok()) {
          LOG(WARNING) << "Optimization loop failed: " << status.ToString();
        }
      });
    }
    return Status::OK();
  }

  std::shared_ptr<model::Model> model_ = nullptr;
  // Controls cancellation of `model_thread_`. Must be ordered before
  // `model_thread_` so that `model_thread_` is destroyed first.
  std::unique_ptr<CancellationManager> cancellation_manager_;
  mutex mu_;
  std::unique_ptr<Thread> model_thread_ TF_GUARDED_BY(mu_);
  int64_t max_intra_op_parallelism_;
  int64_t threadpool_size_;
  std::unique_ptr<thread::ThreadPool> thread_pool_;

  // Must be ordered last as its execution may depend on other members.
  std::unique_ptr<IteratorBase> input_impl_;
};

RootDataset::RootDataset(const DatasetBase* input, Params params)
    : DatasetBase(DatasetContext({name_utils::OpName(kDatasetType),
                                  name_utils::OpName(kDatasetType)})),
      input_(input),
      params_(std::move(params)) {
  if (params_.autotune) {
    traceme_metadata_.push_back(std::make_pair(
        kAlgorithm,
        params_.autotune_algorithm == model::AutotuneAlgorithm::HILL_CLIMB
            ? kHillClimb
            : kGradientDescent));
    traceme_metadata_.push_back(std::make_pair(
        kCpuBudget, strings::Printf("%lld", static_cast<long long>(
                                                params_.autotune_cpu_budget))));
    traceme_metadata_.push_back(std::make_pair(
        kRamBudget, strings::Printf("%lld", static_cast<long long>(
                                                params_.autotune_ram_budget))));
  }
  if (params_.max_intra_op_parallelism >= 0) {
    traceme_metadata_.push_back(std::make_pair(
        kIntraOpParallelism,
        strings::Printf("%lld", static_cast<long long>(value_or_default(
                                    params_.max_intra_op_parallelism, 0,
                                    port::MaxParallelism())))));
  }
  if (params_.private_threadpool_size >= 0) {
    traceme_metadata_.push_back(std::make_pair(
        kPrivateThreadpoolSize,
        strings::Printf("%lld", static_cast<long long>(value_or_default(
                                    params_.private_threadpool_size, 0,
                                    port::MaxParallelism())))));
  }
  auto experiments = GetExperiments();
  if (!experiments.empty()) {
    traceme_metadata_.push_back(
        std::make_pair(kExperiments, absl::StrJoin(experiments, " ")));
  }
  input_->Ref();
}

RootDataset::~RootDataset() { input_->Unref(); }

std::unique_ptr<IteratorBase> RootDataset::MakeIteratorInternal(
    const string& prefix) const {
  return absl::make_unique<Iterator>(
      Iterator::Params{this, name_utils::IteratorPrefix(kDatasetType, prefix)});
}

const DataTypeVector& RootDataset::output_dtypes() const {
  return input_->output_dtypes();
}

const std::vector<PartialTensorShape>& RootDataset::output_shapes() const {
  return input_->output_shapes();
}

string RootDataset::DebugString() const {
  return name_utils::DatasetDebugString(kDatasetType);
}

int64_t RootDataset::Cardinality() const { return input_->Cardinality(); }

Status RootDataset::InputDatasets(
    std::vector<const DatasetBase*>* inputs) const {
  inputs->push_back(input_);
  return Status::OK();
}

Status RootDataset::CheckExternalState() const {
  return input_->CheckExternalState();
}

Status RootDataset::AsGraphDefInternal(SerializationContext* ctx,
                                       DatasetGraphDefBuilder* b,
                                       Node** output) const {
  return errors::Unimplemented("RootDataset does not support serialization.");
}

#if !defined(IS_MOBILE_PLATFORM)
Status FinalizeDataset(OpKernelContext* ctx, DatasetBase* input,
                       DatasetBase** output) {
  const Options& options = input->options();
  absl::flat_hash_set<tstring> optimizations_enabled;
  absl::flat_hash_set<tstring> optimizations_disabled;
  absl::flat_hash_set<tstring> optimizations_default;
  GetOptimizations(options, &optimizations_enabled, &optimizations_disabled,
                   &optimizations_default);
  // Disable `enable_gradient_descent` as it assumes presence of ModelDatasetOp.
  optimizations_disabled.insert("enable_gradient_descent");

  auto experiments = GetExperiments();
  LogAndRecordExperiments(experiments);
  auto optimizations =
      SelectOptimizations(experiments, optimizations_enabled,
                          optimizations_disabled, optimizations_default);
  if (optimizations.empty()) {
    return RootDataset::FromOptions(input, output);
  }

  auto optimization_configs = CreateGraphRewriteConfigs(options);
  auto config_factory = [&optimizations, &optimization_configs]() {
    return CreateRewriterConfig(optimizations, optimization_configs);
  };
  Status s = RewriteDataset(ctx, input, std::move(config_factory),
                            /*record_fingerprint=*/true, output);
  if (errors::IsDeadlineExceeded(s)) {
    // Ignore DeadlineExceeded as it implies that the attempted rewrite took too
    // long which should not prevent further computation.
    LOG(WARNING) << s.ToString();
    return RootDataset::FromOptions(input, output);
  }
  if (!s.ok()) {
    return s;
  }
  input = *output;
  TF_RETURN_IF_ERROR(RootDataset::FromOptions(input, output));
  input->Unref();
  return Status::OK();
}
#else   // !IS_MOBILE_PLATFORM
Status FinalizeDataset(OpKernelContext* ctx, DatasetBase* input,
                       DatasetBase** output) {
  return RootDataset::FromOptions(input, output);
}
#endif  // !IS_MOBILE_PLATFORM

}  // namespace data
}  // namespace tensorflow
<|endoftext|>


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

#include "RDTweakedNativeStyle.h"
#include <QDebug>
#include <QFontMetrics>
#include <QPainter>
#include <QPen>
#include <QStyleOption>

namespace Constants
{
static const int MenuBarItemHPadding = 4;
static const int MenuBarItemVPadding = 2;
static const int MenuBarItemSpacing = 4;
};

RDTweakedNativeStyle::RDTweakedNativeStyle(QStyle *parent) : QProxyStyle(parent)
{
}

RDTweakedNativeStyle::~RDTweakedNativeStyle()
{
}

QRect RDTweakedNativeStyle::subElementRect(SubElement element, const QStyleOption *opt,
                                           const QWidget *widget) const
{
  QRect ret = QProxyStyle::subElementRect(element, opt, widget);

  if(element == QStyle::SE_DockWidgetCloseButton || element == QStyle::SE_DockWidgetFloatButton)
  {
    int width = pixelMetric(QStyle::PM_TabCloseIndicatorWidth, opt, widget);
    int height = pixelMetric(QStyle::PM_TabCloseIndicatorHeight, opt, widget);

    QPoint c = ret.center();
    ret.setSize(QSize(width, height));
    ret.moveCenter(c);
  }

  return ret;
}

QSize RDTweakedNativeStyle::sizeFromContents(ContentsType type, const QStyleOption *opt,
                                             const QSize &size, const QWidget *widget) const
{
  QSize sz = size;

  // Toolbuttons are always at least icon sized, for consistency.
  if(type == QStyle::CT_ToolButton)
  {
    const QStyleOptionToolButton *toolbutton = qstyleoption_cast<const QStyleOptionToolButton *>(opt);
    if(toolbutton)
      sz = sz.expandedTo(toolbutton->iconSize);
  }

  // menu bar items can be sized for both the icon *and* the text
  if(type == CT_MenuBarItem)
  {
    const QStyleOptionMenuItem *menuopt = qstyleoption_cast<const QStyleOptionMenuItem *>(opt);
    int iconSize = pixelMetric(QStyle::PM_SmallIconSize, opt, widget);
    sz = menuopt->fontMetrics.size(Qt::TextShowMnemonic, menuopt->text);

    if(!menuopt->icon.isNull())
    {
      sz.setWidth(sz.width() + Constants::MenuBarItemSpacing + iconSize);
      sz = sz.expandedTo(QSize(1, iconSize));
    }

    sz += QSize(Constants::MenuBarItemHPadding * 2 + Constants::MenuBarItemSpacing * 2,
                Constants::MenuBarItemVPadding * 2);

    return sz;
  }

  return QProxyStyle::sizeFromContents(type, opt, sz, widget);
}

int RDTweakedNativeStyle::pixelMetric(PixelMetric metric, const QStyleOption *opt,
                                      const QWidget *widget) const
{
  // toolbuttons don't shift their text when clicked.
  if(metric == QStyle::PM_ButtonShiftHorizontal || metric == QStyle::PM_ButtonShiftVertical)
  {
    if(opt && (opt->state & State_AutoRaise))
      return 0;
  }

  return QProxyStyle::pixelMetric(metric, opt, widget);
}

int RDTweakedNativeStyle::styleHint(StyleHint stylehint, const QStyleOption *opt,
                                    const QWidget *widget, QStyleHintReturn *returnData) const
{
  if(stylehint == QStyle::SH_Menu_Scrollable)
    return 1;

  return QProxyStyle::styleHint(stylehint, opt, widget, returnData);
}

QIcon RDTweakedNativeStyle::standardIcon(StandardPixmap standardIcon, const QStyleOption *opt,
                                         const QWidget *widget) const
{
  if(standardIcon == QStyle::SP_TitleBarCloseButton)
  {
    int sz = pixelMetric(QStyle::PM_SmallIconSize);

    return QIcon(QPixmap(QSize(sz, sz)));
  }

  return QProxyStyle::standardIcon(standardIcon, opt, widget);
}

void RDTweakedNativeStyle::drawComplexControl(ComplexControl control, const QStyleOptionComplex *opt,
                                              QPainter *p, const QWidget *widget) const
{
  // autoraise toolbuttons are rendered flat with a semi-transparent highlight to show their state.
  if(control == QStyle::CC_ToolButton && (opt->state & State_AutoRaise))
  {
    QPen oldPen = p->pen();
    QColor backCol = opt->palette.color(QPalette::Normal, QPalette::Highlight);

    backCol.setAlphaF(0.2);
    QStyle::State masked = opt->state & (State_On | State_MouseOver);

    if(!(opt->state & State_Enabled))
      masked &= ~State_MouseOver;

    // when the mouse is over, make it a little stronger
    if(masked && (masked & State_MouseOver))
      backCol.setAlphaF(0.4);

    if(masked)
    {
      QRect rect = opt->rect.adjusted(0, 0, -1, -1);
      p->setPen(opt->palette.color(QPalette::Shadow));
      p->drawRect(rect);
      p->fillRect(rect, QBrush(backCol));
    }

    p->setPen(oldPen);

    const QStyleOptionToolButton *toolbutton = qstyleoption_cast<const QStyleOptionToolButton *>(opt);

    QStyleOptionToolButton labelTextIcon = *toolbutton;
    labelTextIcon.rect = subControlRect(control, opt, SC_ToolButton, widget);

    // draw the label text/icon
    drawControl(CE_ToolButtonLabel, &labelTextIcon, p, widget);

    // draw the menu arrow, if there is one
    if((toolbutton->subControls & SC_ToolButtonMenu) ||
       (toolbutton->features & QStyleOptionToolButton::MenuButtonPopup))
    {
      QStyleOptionToolButton menu = *toolbutton;
      menu.rect = subControlRect(control, opt, SC_ToolButtonMenu, widget);
      drawPrimitive(PE_IndicatorArrowDown, &menu, p, widget);
    }

    return;
  }

  return QProxyStyle::drawComplexControl(control, opt, p, widget);
}

void RDTweakedNativeStyle::drawPrimitive(PrimitiveElement element, const QStyleOption *opt,
                                         QPainter *p, const QWidget *widget) const
{
  if(element == QStyle::PE_IndicatorBranch)
  {
    QPen oldPen = p->pen();

    if(opt->state & State_Children)
    {
      bool aa = p->testRenderHint(QPainter::Antialiasing);
      p->setRenderHint(QPainter::Antialiasing);

      QColor col = opt->palette.color(QPalette::Text);

      if(opt->state & State_MouseOver)
      {
        QColor highlightCol = opt->palette.color(QPalette::Highlight);

        col.setRedF(col.redF() * 0.6 + highlightCol.redF() * 0.4);
        col.setGreenF(col.greenF() * 0.6 + highlightCol.greenF() * 0.4);
        col.setBlueF(col.blueF() * 0.6 + highlightCol.blueF() * 0.4);
      }

      p->setPen(QPen(col, 2.0));

      QPainterPath path;

      QPolygonF poly;

      QRectF rect = opt->rect;

      {
        qreal newdim = qMin(14.0, qMin(rect.height(), rect.width()));
        QPointF c = rect.center();
        rect.setTop(c.y() - newdim / 2);
        rect.setLeft(c.x() - newdim / 2);
        rect.setWidth(newdim);
        rect.setHeight(newdim);
      }

      rect = rect.adjusted(2, 2, -2, -2);

      if(opt->state & State_Open)
      {
        QPointF pt = rect.center();
        pt.setX(rect.left());
        poly << pt;

        pt = rect.center();
        pt.setY(rect.bottom());
        poly << pt;

        pt = rect.center();
        pt.setX(rect.right());
        poly << pt;

        path.addPolygon(poly);

        p->drawPath(path);
      }
      else
      {
        QPointF pt = rect.center();
        pt.setY(rect.top());
        poly << pt;

        pt = rect.center();
        pt.setX(rect.right());
        poly << pt;

        pt = rect.center();
        pt.setY(rect.bottom());
        poly << pt;

        path.addPolygon(poly);

        p->drawPath(path);
      }

      if(!aa)
        p->setRenderHint(QPainter::Antialiasing, false);
    }
    else if(opt->state & (State_Sibling | State_Item))
    {
      p->setPen(QPen(opt->palette.color(QPalette::Midlight), 1.0));

      int bottomY = opt->rect.center().y();

      if(opt->state & State_Sibling)
        bottomY = opt->rect.bottom();

      p->drawLine(QLine(opt->rect.center().x(), opt->rect.top(), opt->rect.center().x(), bottomY));

      if(opt->state & State_Item)
        p->drawLine(opt->rect.center(), QPoint(opt->rect.right(), opt->rect.center().y()));
    }
    p->setPen(oldPen);
    return;
  }
  else if(element == PE_IndicatorTabClose)
  {
    QPen oldPen = p->pen();
    bool aa = p->testRenderHint(QPainter::Antialiasing);
    p->setRenderHint(QPainter::Antialiasing);

    QColor col = opt->palette.color(QPalette::Text);

    QRectF rect = opt->rect.adjusted(1, 1, -1, -1);

    if(opt->state & (QStyle::State_Raised | QStyle::State_Sunken | QStyle::State_MouseOver))
    {
      QPointF c = rect.center();
      qreal radius = rect.width() / 2.0;

      col = opt->palette.color(QPalette::Base);

      QPainterPath path;

      path.addEllipse(c, radius, radius);

      QColor fillCol = QColor(Qt::red).darker(120);

      if(opt->state & QStyle::State_Sunken)
        fillCol = fillCol.darker(120);

      p->fillPath(path, fillCol);
    }

    p->setPen(QPen(col, 1.5));

    QPointF c = rect.center();

    qreal crossrad = rect.width() / 4.0;

    p->drawLine(c + QPointF(-crossrad, -crossrad), c + QPointF(crossrad, crossrad));
    p->drawLine(c + QPointF(-crossrad, crossrad), c + QPointF(crossrad, -crossrad));

    p->setPen(oldPen);
    if(!aa)
      p->setRenderHint(QPainter::Antialiasing, false);

    return;
  }

  QProxyStyle::drawPrimitive(element, opt, p, widget);
}

void RDTweakedNativeStyle::drawControl(ControlElement control, const QStyleOption *opt, QPainter *p,
                                       const QWidget *widget) const
{
  if(control == QStyle::CE_MenuBarItem)
  {
    // we can't take over control of just rendering the icon/text, so we call down to common style
    // to draw the background since then we know how to render matching text over the top.
    const QStyleOptionMenuItem *menuopt = qstyleoption_cast<const QStyleOptionMenuItem *>(opt);

    QRect rect =
        menuopt->rect.adjusted(Constants::MenuBarItemSpacing, 0, -Constants::MenuBarItemSpacing, 0);

    const bool selected = menuopt->state & State_Selected;
    const bool hovered = menuopt->state & State_MouseOver;
    const bool enabled = menuopt->state & State_Enabled;

    QPalette::ColorRole textRole = QPalette::ButtonText;

    if(enabled && (selected || hovered))
    {
      p->fillRect(rect, opt->palette.brush(QPalette::Highlight));
      textRole = QPalette::HighlightedText;
    }

    int flags = Qt::AlignCenter | Qt::TextShowMnemonic | Qt::TextDontClip | Qt::TextSingleLine;
    if(!styleHint(SH_UnderlineShortcut, opt, widget))
      flags |= Qt::TextHideMnemonic;

    rect.adjust(Constants::MenuBarItemHPadding, Constants::MenuBarItemVPadding,
                -Constants::MenuBarItemHPadding, -Constants::MenuBarItemVPadding);

    int iconSize = pixelMetric(QStyle::PM_SmallIconSize, opt, widget);

    QPixmap pix = menuopt->icon.pixmap(
        iconSize, iconSize, (menuopt->state & State_Enabled) ? QIcon::Normal : QIcon::Disabled);

    if(!pix.isNull())
    {
      QRect iconRect = rect;
      iconRect.setWidth(iconSize);
      drawItemPixmap(p, iconRect, flags, pix);
      rect.adjust(Constants::MenuBarItemSpacing + iconSize, 0, 0, 0);
    }

    drawItemText(p, rect, flags, menuopt->palette, enabled, menuopt->text, textRole);

    return;
  }

// https://bugreports.qt.io/browse/QTBUG-14949
// work around itemview rendering bug - the first line in a multi-line text that is elided stops
// all subsequent text from rendering. Should be fixed in 5.11, but for all other versions we need
// to manually step in. We manually elide the text before calling down to the style
#if(QT_VERSION < QT_VERSION_CHECK(5, 11, 0))
  if(control == QStyle::CE_ItemViewItem)
  {
    const QStyleOptionViewItem *viewopt = qstyleoption_cast<const QStyleOptionViewItem *>(opt);

    // only if we're eliding, not wrapping, and we have multiple lines
    if((viewopt->features & QStyleOptionViewItem::WrapText) == 0 &&
       viewopt->text.contains(QChar::LineSeparator))
    {
      const int hmargin = pixelMetric(QStyle::PM_FocusFrameHMargin, 0, widget) + 1;

      QRect textRect =
          subElementRect(SE_ItemViewItemText, viewopt, widget).adjusted(hmargin, 0, -hmargin, 0);

      QFontMetrics metrics(viewopt->font);

      QStringList lines = viewopt->text.split(QChar::LineSeparator);

      for(QString &line : lines)
        line = metrics.elidedText(line, viewopt->textElideMode, textRect.width(), 0);

      QStyleOptionViewItem elided = *viewopt;

      elided.text = lines.join(QChar::LineSeparator);

      QProxyStyle::drawControl(control, &elided, p, widget);
      return;
    }
  }
#endif

  QProxyStyle::drawControl(control, opt, p, widget);
}
<commit_msg>Draw a divider in toolbuttons with menu dropdowns<commit_after>/******************************************************************************
 * The MIT License (MIT)
 *
 * Copyright (c) 2016-2018 Baldur Karlsson
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 ******************************************************************************/

#include "RDTweakedNativeStyle.h"
#include <QDebug>
#include <QFontMetrics>
#include <QPainter>
#include <QPen>
#include <QStyleOption>

namespace Constants
{
static const int MenuBarItemHPadding = 4;
static const int MenuBarItemVPadding = 2;
static const int MenuBarItemSpacing = 4;
};

RDTweakedNativeStyle::RDTweakedNativeStyle(QStyle *parent) : QProxyStyle(parent)
{
}

RDTweakedNativeStyle::~RDTweakedNativeStyle()
{
}

QRect RDTweakedNativeStyle::subElementRect(SubElement element, const QStyleOption *opt,
                                           const QWidget *widget) const
{
  QRect ret = QProxyStyle::subElementRect(element, opt, widget);

  if(element == QStyle::SE_DockWidgetCloseButton || element == QStyle::SE_DockWidgetFloatButton)
  {
    int width = pixelMetric(QStyle::PM_TabCloseIndicatorWidth, opt, widget);
    int height = pixelMetric(QStyle::PM_TabCloseIndicatorHeight, opt, widget);

    QPoint c = ret.center();
    ret.setSize(QSize(width, height));
    ret.moveCenter(c);
  }

  return ret;
}

QSize RDTweakedNativeStyle::sizeFromContents(ContentsType type, const QStyleOption *opt,
                                             const QSize &size, const QWidget *widget) const
{
  QSize sz = size;

  // Toolbuttons are always at least icon sized, for consistency.
  if(type == QStyle::CT_ToolButton)
  {
    const QStyleOptionToolButton *toolbutton = qstyleoption_cast<const QStyleOptionToolButton *>(opt);
    if(toolbutton)
      sz = sz.expandedTo(toolbutton->iconSize);
  }

  // menu bar items can be sized for both the icon *and* the text
  if(type == CT_MenuBarItem)
  {
    const QStyleOptionMenuItem *menuopt = qstyleoption_cast<const QStyleOptionMenuItem *>(opt);
    int iconSize = pixelMetric(QStyle::PM_SmallIconSize, opt, widget);
    sz = menuopt->fontMetrics.size(Qt::TextShowMnemonic, menuopt->text);

    if(!menuopt->icon.isNull())
    {
      sz.setWidth(sz.width() + Constants::MenuBarItemSpacing + iconSize);
      sz = sz.expandedTo(QSize(1, iconSize));
    }

    sz += QSize(Constants::MenuBarItemHPadding * 2 + Constants::MenuBarItemSpacing * 2,
                Constants::MenuBarItemVPadding * 2);

    return sz;
  }

  return QProxyStyle::sizeFromContents(type, opt, sz, widget);
}

int RDTweakedNativeStyle::pixelMetric(PixelMetric metric, const QStyleOption *opt,
                                      const QWidget *widget) const
{
  // toolbuttons don't shift their text when clicked.
  if(metric == QStyle::PM_ButtonShiftHorizontal || metric == QStyle::PM_ButtonShiftVertical)
  {
    if(opt && (opt->state & State_AutoRaise))
      return 0;
  }

  return QProxyStyle::pixelMetric(metric, opt, widget);
}

int RDTweakedNativeStyle::styleHint(StyleHint stylehint, const QStyleOption *opt,
                                    const QWidget *widget, QStyleHintReturn *returnData) const
{
  if(stylehint == QStyle::SH_Menu_Scrollable)
    return 1;

  return QProxyStyle::styleHint(stylehint, opt, widget, returnData);
}

QIcon RDTweakedNativeStyle::standardIcon(StandardPixmap standardIcon, const QStyleOption *opt,
                                         const QWidget *widget) const
{
  if(standardIcon == QStyle::SP_TitleBarCloseButton)
  {
    int sz = pixelMetric(QStyle::PM_SmallIconSize);

    return QIcon(QPixmap(QSize(sz, sz)));
  }

  return QProxyStyle::standardIcon(standardIcon, opt, widget);
}

void RDTweakedNativeStyle::drawComplexControl(ComplexControl control, const QStyleOptionComplex *opt,
                                              QPainter *p, const QWidget *widget) const
{
  // autoraise toolbuttons are rendered flat with a semi-transparent highlight to show their state.
  if(control == QStyle::CC_ToolButton && (opt->state & State_AutoRaise))
  {
    const QStyleOptionToolButton *toolbutton = qstyleoption_cast<const QStyleOptionToolButton *>(opt);

    QPen oldPen = p->pen();
    QColor backCol = opt->palette.color(QPalette::Normal, QPalette::Highlight);
    backCol.setAlphaF(0.2);

    QStyleOptionToolButton menu;

    // draw the menu arrow, if there is one
    if((toolbutton->subControls & SC_ToolButtonMenu) ||
       (toolbutton->features & QStyleOptionToolButton::MenuButtonPopup))
    {
      menu = *toolbutton;
      menu.rect = subControlRect(control, opt, SC_ToolButtonMenu, widget);
    }

    QStyle::State masked = opt->state & (State_On | State_MouseOver);

    if(!(opt->state & State_Enabled))
      masked &= ~State_MouseOver;

    if(masked)
    {
      QRect rect = opt->rect.adjusted(0, 0, -1, -1);
      p->setPen(opt->palette.color(QPalette::Shadow));
      p->drawRect(rect);
      if(menu.rect.isValid())
        p->drawLine(menu.rect.topLeft(), menu.rect.bottomLeft());

      // when the mouse is over, make it a little stronger
      if(masked & State_MouseOver)
        backCol.setAlphaF(0.4);

      p->fillRect(rect, QBrush(backCol));
    }

    p->setPen(oldPen);

    QStyleOptionToolButton labelTextIcon = *toolbutton;
    labelTextIcon.rect = subControlRect(control, opt, SC_ToolButton, widget);

    // draw the label text/icon
    drawControl(CE_ToolButtonLabel, &labelTextIcon, p, widget);

    if(menu.rect.isValid())
    {
      menu.rect.adjust(2, 0, 0, 0);
      drawPrimitive(PE_IndicatorArrowDown, &menu, p, widget);
    }

    return;
  }

  return QProxyStyle::drawComplexControl(control, opt, p, widget);
}

void RDTweakedNativeStyle::drawPrimitive(PrimitiveElement element, const QStyleOption *opt,
                                         QPainter *p, const QWidget *widget) const
{
  if(element == QStyle::PE_IndicatorBranch)
  {
    QPen oldPen = p->pen();

    if(opt->state & State_Children)
    {
      bool aa = p->testRenderHint(QPainter::Antialiasing);
      p->setRenderHint(QPainter::Antialiasing);

      QColor col = opt->palette.color(QPalette::Text);

      if(opt->state & State_MouseOver)
      {
        QColor highlightCol = opt->palette.color(QPalette::Highlight);

        col.setRedF(col.redF() * 0.6 + highlightCol.redF() * 0.4);
        col.setGreenF(col.greenF() * 0.6 + highlightCol.greenF() * 0.4);
        col.setBlueF(col.blueF() * 0.6 + highlightCol.blueF() * 0.4);
      }

      p->setPen(QPen(col, 2.0));

      QPainterPath path;

      QPolygonF poly;

      QRectF rect = opt->rect;

      {
        qreal newdim = qMin(14.0, qMin(rect.height(), rect.width()));
        QPointF c = rect.center();
        rect.setTop(c.y() - newdim / 2);
        rect.setLeft(c.x() - newdim / 2);
        rect.setWidth(newdim);
        rect.setHeight(newdim);
      }

      rect = rect.adjusted(2, 2, -2, -2);

      if(opt->state & State_Open)
      {
        QPointF pt = rect.center();
        pt.setX(rect.left());
        poly << pt;

        pt = rect.center();
        pt.setY(rect.bottom());
        poly << pt;

        pt = rect.center();
        pt.setX(rect.right());
        poly << pt;

        path.addPolygon(poly);

        p->drawPath(path);
      }
      else
      {
        QPointF pt = rect.center();
        pt.setY(rect.top());
        poly << pt;

        pt = rect.center();
        pt.setX(rect.right());
        poly << pt;

        pt = rect.center();
        pt.setY(rect.bottom());
        poly << pt;

        path.addPolygon(poly);

        p->drawPath(path);
      }

      if(!aa)
        p->setRenderHint(QPainter::Antialiasing, false);
    }
    else if(opt->state & (State_Sibling | State_Item))
    {
      p->setPen(QPen(opt->palette.color(QPalette::Midlight), 1.0));

      int bottomY = opt->rect.center().y();

      if(opt->state & State_Sibling)
        bottomY = opt->rect.bottom();

      p->drawLine(QLine(opt->rect.center().x(), opt->rect.top(), opt->rect.center().x(), bottomY));

      if(opt->state & State_Item)
        p->drawLine(opt->rect.center(), QPoint(opt->rect.right(), opt->rect.center().y()));
    }
    p->setPen(oldPen);
    return;
  }
  else if(element == PE_IndicatorTabClose)
  {
    QPen oldPen = p->pen();
    bool aa = p->testRenderHint(QPainter::Antialiasing);
    p->setRenderHint(QPainter::Antialiasing);

    QColor col = opt->palette.color(QPalette::Text);

    QRectF rect = opt->rect.adjusted(1, 1, -1, -1);

    if(opt->state & (QStyle::State_Raised | QStyle::State_Sunken | QStyle::State_MouseOver))
    {
      QPointF c = rect.center();
      qreal radius = rect.width() / 2.0;

      col = opt->palette.color(QPalette::Base);

      QPainterPath path;

      path.addEllipse(c, radius, radius);

      QColor fillCol = QColor(Qt::red).darker(120);

      if(opt->state & QStyle::State_Sunken)
        fillCol = fillCol.darker(120);

      p->fillPath(path, fillCol);
    }

    p->setPen(QPen(col, 1.5));

    QPointF c = rect.center();

    qreal crossrad = rect.width() / 4.0;

    p->drawLine(c + QPointF(-crossrad, -crossrad), c + QPointF(crossrad, crossrad));
    p->drawLine(c + QPointF(-crossrad, crossrad), c + QPointF(crossrad, -crossrad));

    p->setPen(oldPen);
    if(!aa)
      p->setRenderHint(QPainter::Antialiasing, false);

    return;
  }

  QProxyStyle::drawPrimitive(element, opt, p, widget);
}

void RDTweakedNativeStyle::drawControl(ControlElement control, const QStyleOption *opt, QPainter *p,
                                       const QWidget *widget) const
{
  if(control == QStyle::CE_MenuBarItem)
  {
    // we can't take over control of just rendering the icon/text, so we call down to common style
    // to draw the background since then we know how to render matching text over the top.
    const QStyleOptionMenuItem *menuopt = qstyleoption_cast<const QStyleOptionMenuItem *>(opt);

    QRect rect =
        menuopt->rect.adjusted(Constants::MenuBarItemSpacing, 0, -Constants::MenuBarItemSpacing, 0);

    const bool selected = menuopt->state & State_Selected;
    const bool hovered = menuopt->state & State_MouseOver;
    const bool enabled = menuopt->state & State_Enabled;

    QPalette::ColorRole textRole = QPalette::ButtonText;

    if(enabled && (selected || hovered))
    {
      p->fillRect(rect, opt->palette.brush(QPalette::Highlight));
      textRole = QPalette::HighlightedText;
    }

    int flags = Qt::AlignCenter | Qt::TextShowMnemonic | Qt::TextDontClip | Qt::TextSingleLine;
    if(!styleHint(SH_UnderlineShortcut, opt, widget))
      flags |= Qt::TextHideMnemonic;

    rect.adjust(Constants::MenuBarItemHPadding, Constants::MenuBarItemVPadding,
                -Constants::MenuBarItemHPadding, -Constants::MenuBarItemVPadding);

    int iconSize = pixelMetric(QStyle::PM_SmallIconSize, opt, widget);

    QPixmap pix = menuopt->icon.pixmap(
        iconSize, iconSize, (menuopt->state & State_Enabled) ? QIcon::Normal : QIcon::Disabled);

    if(!pix.isNull())
    {
      QRect iconRect = rect;
      iconRect.setWidth(iconSize);
      drawItemPixmap(p, iconRect, flags, pix);
      rect.adjust(Constants::MenuBarItemSpacing + iconSize, 0, 0, 0);
    }

    drawItemText(p, rect, flags, menuopt->palette, enabled, menuopt->text, textRole);

    return;
  }

// https://bugreports.qt.io/browse/QTBUG-14949
// work around itemview rendering bug - the first line in a multi-line text that is elided stops
// all subsequent text from rendering. Should be fixed in 5.11, but for all other versions we need
// to manually step in. We manually elide the text before calling down to the style
#if(QT_VERSION < QT_VERSION_CHECK(5, 11, 0))
  if(control == QStyle::CE_ItemViewItem)
  {
    const QStyleOptionViewItem *viewopt = qstyleoption_cast<const QStyleOptionViewItem *>(opt);

    // only if we're eliding, not wrapping, and we have multiple lines
    if((viewopt->features & QStyleOptionViewItem::WrapText) == 0 &&
       viewopt->text.contains(QChar::LineSeparator))
    {
      const int hmargin = pixelMetric(QStyle::PM_FocusFrameHMargin, 0, widget) + 1;

      QRect textRect =
          subElementRect(SE_ItemViewItemText, viewopt, widget).adjusted(hmargin, 0, -hmargin, 0);

      QFontMetrics metrics(viewopt->font);

      QStringList lines = viewopt->text.split(QChar::LineSeparator);

      for(QString &line : lines)
        line = metrics.elidedText(line, viewopt->textElideMode, textRect.width(), 0);

      QStyleOptionViewItem elided = *viewopt;

      elided.text = lines.join(QChar::LineSeparator);

      QProxyStyle::drawControl(control, &elided, p, widget);
      return;
    }
  }
#endif

  QProxyStyle::drawControl(control, opt, p, widget);
}
<|endoftext|>


<commit_before>
// #include <linux/kvm.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <errno.h>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <string>

#include <sys/types.h>
#include <unistd.h>

#include <map>
#include <string>
#include <vector>
#include <memory>
#include <iostream>

#include "Thread/Thread.hpp"
#include "Thread/Mutex.hpp"
#include "Vm/Controller.hpp"
#include "Net/Socket.hpp"
#include "Net/ServerSocket.hpp"
#include "Util/Logger.hpp"

extern "C" {
	#include "libkvm.h"
}

using std::string;
using std::vector;
using std::to_string;
using std::shared_ptr;
using std::make_shared;
using std::stoi;

Thread::Mutex ctrlMutex;
shared_ptr<Vm::Controller> controller;
shared_ptr<Net::ServerSocket> ssocket;
shared_ptr<Util::Logger> 	   logger;

class CommandHandler: public Thread::Thread<Thread::DetachState::Detached, Thread::CancelType::Deferred> {

private:
	std::shared_ptr<Net::Socket> socket;

public:
	CommandHandler(std::shared_ptr<Net::Socket> _socket)
		: socket(_socket) { }

	virtual void run() {

		for(;;) {

		auto commands = socket->read();
		auto cmd = commands.at(0);

		logger->log("processing request: " + cmd);

		if (cmd == "templates") {
			for (auto& kv : controller->getTemplates())
				socket->write(vector<string>{kv.second->getName()});
		}

		else if (cmd == "instances") {
			if (controller->getInstances().size() == 0) {
				socket->write(vector<string>{"no running instances"});
			} else {
				for (auto& kv : controller->getInstances())
					socket->write(vector<string>{
						"id: " + to_string(kv.first) + ", " +
						"mem: " + to_string(kv.second->getMemory()) + ", " +
						"cpus: " + to_string(kv.second->getCpus()) + ", " +
						"ssh: " + to_string(kv.second->getSshPort()) + ", " +
						(kv.second->isRunning() ? "ONLINE" : "OFFLINE")
					});
			}
		}

		else if (cmd == "new") {
			try {
				auto name = commands.at(1);
				int mem = stoi(commands.at(2));
				int cpus = stoi(commands.at(3));

				auto tmpls = controller->getTemplates();

				if (tmpls.find(name) == tmpls.end()) {
					socket->write(vector<string>{"template not found"});
				} else {
					auto tmpl = tmpls[name];
					auto inst = controller->instantiate(tmpl, mem, cpus);
					inst->run();
					socket->write(vector<string>{"instantiated and listening at " + to_string(inst->getSshPort())});
				}

			} catch(...) {
				socket->write(vector<string>{"invalid arguments for 'new'"});
			}
		}

		else {
			socket->write(vector<string>{"unknown command"});
		}

		socket->write(vector<string>{""});

		// socket->close();
		}
	}
};

int main(int argc, char** argv) {

	logger 		= make_shared<Util::Logger>(std::cout);

	if (argc < 4) {
		logger->log("usage: controller <control-port> <initial-id> <initial-ssh-port>");
		return -1;
	}

	ssocket 	= make_shared<Net::ServerSocket>(std::stoi(argv[1]));
	controller 	= make_shared<Vm::Controller>(stoi(argv[2]), stoi(argv[3]), logger);

	logger->log("VM Controller started!");

	for (;;) {
		try {
			auto socket = ssocket->accept();
			auto handler = new CommandHandler(socket);
			handler->start();
		}
		catch(Util::Exception& ex) {
			logger->log(ex.what());
		}
	}

	ssocket->close();

	return 0;
}
<commit_msg>SIGCHLD ignored to prevent zobie-qemu<commit_after>
// #include <linux/kvm.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <errno.h>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <string>

#include <sys/types.h>
#include <signal.h>
#include <unistd.h>

#include <map>
#include <string>
#include <vector>
#include <memory>
#include <iostream>

#include "Thread/Thread.hpp"
#include "Thread/Mutex.hpp"
#include "Vm/Controller.hpp"
#include "Net/Socket.hpp"
#include "Net/ServerSocket.hpp"
#include "Util/Logger.hpp"

extern "C" {
	#include "libkvm.h"
}

using std::string;
using std::vector;
using std::to_string;
using std::shared_ptr;
using std::make_shared;
using std::stoi;

Thread::Mutex ctrlMutex;
shared_ptr<Vm::Controller> controller;
shared_ptr<Net::ServerSocket> ssocket;
shared_ptr<Util::Logger> 	   logger;

class CommandHandler: public Thread::Thread<Thread::DetachState::Detached, Thread::CancelType::Deferred> {

private:
	std::shared_ptr<Net::Socket> socket;

public:
	CommandHandler(std::shared_ptr<Net::Socket> _socket)
		: socket(_socket) { }

	virtual void run() {

		for(;;) {

		auto commands = socket->read();
		auto cmd = commands.at(0);

		logger->log("processing request: " + cmd);

		if (cmd == "templates") {
			for (auto& kv : controller->getTemplates())
				socket->write(vector<string>{kv.second->getName()});
		}

		else if (cmd == "instances") {
			if (controller->getInstances().size() == 0) {
				socket->write(vector<string>{"no running instances"});
			} else {
				for (auto& kv : controller->getInstances())
					socket->write(vector<string>{
						"id: " + to_string(kv.first) + ", " +
						"mem: " + to_string(kv.second->getMemory()) + ", " +
						"cpus: " + to_string(kv.second->getCpus()) + ", " +
						"ssh: " + to_string(kv.second->getSshPort()) + ", " +
						(kv.second->isRunning() ? "ONLINE" : "OFFLINE")
					});
			}
		}

		else if (cmd == "new") {
			try {
				auto name = commands.at(1);
				int mem = stoi(commands.at(2));
				int cpus = stoi(commands.at(3));

				auto tmpls = controller->getTemplates();

				if (tmpls.find(name) == tmpls.end()) {
					socket->write(vector<string>{"template not found"});
				} else {
					auto tmpl = tmpls[name];
					auto inst = controller->instantiate(tmpl, mem, cpus);
					inst->run();
					socket->write(vector<string>{"instantiated and listening at " + to_string(inst->getSshPort())});
				}

			} catch(...) {
				socket->write(vector<string>{"invalid arguments for 'new'"});
			}
		}

		else {
			socket->write(vector<string>{"unknown command"});
		}

		socket->write(vector<string>{""});

		// socket->close();
		}
	}
};

int main(int argc, char** argv) {

	// http://stackoverflow.com/questions/17015830/how-can-i-prevent-zombie-child-processes
	struct sigaction sigchld_action;
	sigchld_action.sa_handler = SIG_DFL;
	sigchld_action.sa_flags = SA_NOCLDWAIT;
	sigaction(SIGCHLD, &sigchld_action, nullptr);

	logger 		= make_shared<Util::Logger>(std::cout);

	if (argc < 4) {
		logger->log("usage: controller <control-port> <initial-id> <initial-ssh-port>");
		return -1;
	}

	ssocket 	= make_shared<Net::ServerSocket>(std::stoi(argv[1]));
	controller 	= make_shared<Vm::Controller>(stoi(argv[2]), stoi(argv[3]), logger);

	logger->log("VM Controller started!");

	for (;;) {
		try {
			auto socket = ssocket->accept();
			auto handler = new CommandHandler(socket);
			handler->start();
		}
		catch(Util::Exception& ex) {
			logger->log(ex.what());
		}
	}

	ssocket->close();

	return 0;
}
<|endoftext|>


<commit_before>/*
 * XML2SMS.cpp
 *
 *  Created on: Nov 1, 2016
 *      Author: sifff
 */

#include <src/XML2SMS.h>
#include <QtXml/QDomDocument>
#include <QtCore/QFile>
#include <QDebug>
#include <QDateTime>
#include <QStringList>
#include <QMap>
#include <bb/pim/message/MessageService>
#include <bb/pim/message/MessageFolder>
#include <bb/pim/message/MessageStatus>
#include <bb/pim/account/AccountService>
#include <bb/pim/account/Account>
#include <bb/pim/account/Service>
#include <bb/pim/message/ConversationBuilder>
#include <bb/pim/message/MessageBuilder>
#include <bb/pim/message/MessageFilter>
#include <bb/pim/message/MessageContact>
#include <bb/pim/message/Conversation>

#include <bb/pim/contacts/ContactService>
#include <bb/pim/contacts/Contact>
#include <bb/pim/contacts/ContactSearchFilters>
#include <bb/pim/contacts/ContactConsts>
#include <bb/pim/account/Result>

using namespace bb::pim;

XML2SMS::XML2SMS(QObject* parent) : QObject(parent){}

XML2SMS::~XML2SMS()
{
    // TODO Auto-generated destructor stub
}

void XML2SMS::onXMLFileSelect(const QString qs)
{
    QDomDocument doc;
qDebug()<<qs;
        QFile file(qs);
        if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
        {
            qDebug() <<"Failed to open/access file containing SMS";
            return;
        }
        else
        {
            if(!doc.setContent(&file))
            {
                qDebug() << "failed to read the file containing the SMS";
//                return -2;
            }
            else
                qDebug()<<"success loading file!";
            file.close();
        }
        QMap<QString, message::ConversationKey> conversationidmap;
        message::MessageService messageService;
        account::AccountService accountService;
        //Get the SMS/MMS account
        QList<account::Account> accountList = accountService.accounts(account::Service::Messages,"sms-mms");
        account::AccountKey smsaccount =  accountList.first().id();
        QList<account::AccountKey> accountidlist;
        accountidlist.append(smsaccount);
        QDomElement root = doc.firstChildElement();
        QDomNodeList nodes = root.elementsByTagName("sms");
        qDebug() <<"number of nodes "<<nodes.count();
        for (int i = 0; i < /*nodes.count()*/ 15; ++i)
        {
            QDomNode elm = nodes.at(i);
            if(elm.isElement())
            {
            message::ConversationBuilder* conversationBuilder = message::ConversationBuilder::create();
            // Make a Message object for the sms-mms account
            message::MessageBuilder* msg_builder = message::MessageBuilder::create(smsaccount);
            conversationBuilder->accountId(smsaccount);
            QList<message::MessageContact> participants;
            QDomElement e = elm.toElement();
            //retrieve a list contaning the phonenumber(s)
            QStringList phonenumbers = (e.attribute("address")).split(";");
            for(typename QStringList::const_iterator pn = phonenumbers.begin(); pn != phonenumbers.end(); ++pn)
            {
                message::MessageContact recipient = message::MessageContact(-1,
                                                         message::MessageContact::To, *pn, *pn);
                participants.append(recipient);
                // Specify the recipient of the message (i.e. the destination address/MSISDN)
                msg_builder->addRecipient(recipient);
            }
            conversationBuilder->participants(participants);
            message::Conversation conversation = *conversationBuilder;
            //try to find a chat containing the single participant. Else we create a new one.
            message::ConversationKey conversationid;
            if(phonenumbers.size() == 1)
            {

            QMap<QString, message::ConversationKey>::iterator it = conversationidmap.find(phonenumbers.first());
                                if(it != conversationidmap.end())
                                    conversationid = it.value();
                                else
                                {
                                    conversationid = messageService.save(smsaccount, conversation);
                                    conversationidmap.insert(phonenumbers.first(), conversationid);
                                }
            }
            else
            {
                conversationid = messageService.save(smsaccount, conversation);
            }
            // Set the message’s conversation ID
            msg_builder->conversationId(conversationid);

            // Set the message body (message text) as an Attachment to the Message object
            msg_builder->addAttachment(message::Attachment("text/plain", "", e.attribute("body")));

            //add the proper date of sending
            QDateTime date;
            date.setMSecsSinceEpoch(e.attribute("date").toLongLong());
            msg_builder->deviceTimestamp(date);

//prepare to finally store everything
            message::Message message;
            message = *msg_builder;
            message::MessageKey messagekey = messageService.save(smsaccount, message);
            //add the state wether we sent it or we have received it
/*            switch(e.attribute("type").toInt())
            {
                case -1:
                    messageService.setStatus(smsaccount, messagekey, message::MessageStatus::Broadcast);
                    break;
                    case 1:
                        messageService.setStatus(smsaccount, messagekey, message::MessageStatus::Read);
                        break;
                    case 2:
                        messageService.setStatus(smsaccount, messagekey, message::MessageStatus::Sent);
                        break;
                    default:
                        break;
            }
            messageService.syncAccounts(accountidlist);
            account::Result res = accountService.syncAccounts(account::Service::Messages);*/
            }
        }
        qDebug()<<"Done loading SMS";
}
<commit_msg>SMS import seems to work so far.<commit_after>/*
 * XML2SMS.cpp
 *
 *  Created on: Nov 1, 2016
 *      Author: sifff
 */

#include <src/XML2SMS.h>
#include <QtXml/QDomDocument>
#include <QtCore/QFile>
#include <QDebug>
#include <QDateTime>
#include <QStringList>
#include <QMap>
#include <bb/pim/message/MessageService>
#include <bb/pim/message/MessageFolder>
#include <bb/pim/message/MessageStatus>
#include <bb/pim/account/AccountService>
#include <bb/pim/account/Account>
#include <bb/pim/account/Service>
#include <bb/pim/message/ConversationBuilder>
#include <bb/pim/message/MessageBuilder>
#include <bb/pim/message/MessageFilter>
#include <bb/pim/message/MessageContact>
#include <bb/pim/message/Conversation>

#include <bb/pim/contacts/ContactService>
#include <bb/pim/contacts/Contact>
#include <bb/pim/contacts/ContactSearchFilters>
#include <bb/pim/contacts/ContactConsts>
#include <bb/pim/account/Result>

#include <bb/system/SystemToast>

using namespace bb::pim;

XML2SMS::XML2SMS(QObject* parent) : QObject(parent){}

XML2SMS::~XML2SMS()
{
    // TODO Auto-generated destructor stub
}

void XML2SMS::onXMLFileSelect(const QString qs)
{
    bb::system::SystemToast toast;
    QDomDocument doc;
qDebug()<<qs;
        QFile file(qs);
        if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
        {
            qDebug() <<"Failed to open/access file containing SMS";
            return;
        }
        else
        {
            if(!doc.setContent(&file))
            {
                qDebug() << "failed to read the file containing the SMS";
//                return -2;
            }
            else
                qDebug()<<"success loading file!";
            file.close();
        }
        QMap<QString, message::ConversationKey> conversationidmap;
        message::MessageService messageService;
        account::AccountService accountService;
        //Get the SMS/MMS account
        QList<account::Account> accountList = accountService.accounts(account::Service::Messages,"sms-mms");
        account::AccountKey smsaccount =  accountList.first().id();
        QList<account::AccountKey> accountidlist;
        accountidlist.append(smsaccount);
        QDomElement root = doc.firstChildElement();
        QDomNodeList nodes = root.elementsByTagName("sms");
        if (nodes.count() == 0)
        {
            toast.setBody("No SMS found in file.");
            toast.setPosition(bb::system::SystemUiPosition::MiddleCenter);
            toast.exec();
            return;
        }
        qDebug() <<"number of nodes "<<nodes.count();
        int importedsms = 0;
        for (int i = 0; i < /*nodes.count()*/ 15; ++i)
        {
            QDomNode elm = nodes.at(i);
            if(elm.isElement())
            {
            message::ConversationBuilder* conversationBuilder = message::ConversationBuilder::create();
            // Make a Message object for the sms-mms account
            message::MessageBuilder* msg_builder = message::MessageBuilder::create(smsaccount);
            conversationBuilder->accountId(smsaccount);
            QList<message::MessageContact> participants;
            QDomElement e = elm.toElement();
            //retrieve a list contaning the phonenumber(s)
            QStringList phonenumbers = (e.attribute("address")).split(";");
            for(typename QStringList::const_iterator pn = phonenumbers.begin(); pn != phonenumbers.end(); ++pn)
            {
                contacts::ContactService contactservice;
                contacts::ContactSearchFilters filter;
                filter.setLimit(1);
                QList< contacts::SearchField::Type > fields;
                fields << contacts::SearchField::Phone;
                filter.setSearchFields(fields);
                filter.setSearchValue(*pn);
                QList<contacts::Contact> contact = contactservice.searchContactsByPhoneNumber(filter);
                if (contact.size() >= 1)
qDebug()<<contact.size()<<" "<<contact.first().displayName();
                if(e.attribute("type").toInt() == 1)
                {
                    qDebug()<<"Received from "<<*pn;
                    if (contact.size() >= 1)
                        qDebug()<<"Whois "<<contact.first().displayName();
                    else
                        qDebug()<<"not known";
                    message::MessageContact sender = message::MessageContact(contact.size() != 1?-1:contact.first().id(),
                                            message::MessageContact::From
                                                                             , *pn, *pn);
                    participants.append(sender);
                    msg_builder->sender(sender);
                }
                else
                {
                    qDebug()<<"Sent to "<<*pn;
                    if (contact.size() >= 1)
                        qDebug()<<"Whois "<<contact.first().displayName();
                    else
                        qDebug()<<"not known";
                    message::MessageContact recipient = message::MessageContact(contact.size() != 1?-1:contact.first().id(),
                                            message::MessageContact::To
                                                                             , *pn, *pn);
                    participants.append(recipient);
                    // Specify the recipient of the message (i.e. the destination address/MSISDN)
                    msg_builder->addRecipient(recipient);
                }
                message::MessageContact recipient = message::MessageContact(contact.size() != 1?-1:contact.first().id(),
                        e.attribute("type").toInt()==1?message::MessageContact::From : message::MessageContact::To
                                                         , *pn, *pn);
            }
            conversationBuilder->participants(participants);
            message::Conversation conversation = *conversationBuilder;
            //try to find a chat containing the single participant. Else we create a new one.
            message::ConversationKey conversationid;
            if(phonenumbers.size() == 1)
            {
                QMap<QString, message::ConversationKey>::iterator it = conversationidmap.find(phonenumbers.first());
                if(it != conversationidmap.end())
                    conversationid = it.value();
                else
                {
                    qDebug()<<"Number "<<phonenumbers.first()<<"not known";
                    contacts::ContactService contactservice;
                    contacts::ContactSearchFilters filter;
                    filter.setLimit(1);
                    QList< contacts::SearchField::Type > fields;
                    fields << contacts::SearchField::Phone;
                    filter.setSearchFields(fields);
                    filter.setSearchValue(phonenumbers.first());
                    QList<contacts::Contact> contact = contactservice.searchContactsByPhoneNumber(filter);
                    qDebug()<<"matching contacts: "<<contact.count();
                    if(contact.count() == 1)
                    {
                        message::MessageFilter mf;
                        mf.insert(message::MessageFilter::ContactId, QVariant(contact.first().id()));
                        QList<message::ConversationKey> filteredKeys = messageService.conversationKeys(smsaccount, mf);
                        qDebug()<<"matching conversations "<<filteredKeys.count();
                        if(filteredKeys.count() < 1)
                        {
                            conversationid = messageService.save(smsaccount, conversation);
                            conversationidmap.insert(phonenumbers.first(), conversationid);
                        }
                        else
                        {
                            conversationid = filteredKeys.first();
                            conversationidmap.insert(phonenumbers.first(), conversationid);
                        }
                    }
                    else// multiple accounts match that number
                    {
                        conversationid = messageService.save(smsaccount, conversation);
                        conversationidmap.insert(phonenumbers.first(), conversationid);
                    }
//                    conversationid = messageService.save(smsaccount, conversation);
//                    conversationidmap.insert(phonenumbers.first(), conversationid);
                }

            }
            else
            {
                conversationid = messageService.save(smsaccount, conversation);
            }

            // Set the message’s conversation ID
            msg_builder->conversationId(conversationid);

            // Set the message body (message text) as an Attachment to the Message object
            msg_builder->addAttachment(message::Attachment("text/plain", "", e.attribute("body")));

            //add the proper date of sending
            QDateTime date;
            date.setMSecsSinceEpoch(e.attribute("date").toLongLong());
            msg_builder->deviceTimestamp(date);

//prepare to finally store everything
            message::Message message;
            message = *msg_builder;
            message::MessageKey messagekey = messageService.save(smsaccount, message);
            ++importedsms;
            //add the state wether we sent it or we have received it
            switch(e.attribute("type").toInt())
            {
                case -1:
                    messageService.setStatus(smsaccount, messagekey, message::MessageStatus::Broadcast);
                    break;
                    case 1:
                        messageService.setStatus(smsaccount, messagekey, message::MessageStatus::Read);
                        break;
                    case 2:
                        messageService.setStatus(smsaccount, messagekey, message::MessageStatus::Sent);
                        break;
                    default:
                        break;
            }
//            messageService.syncAccounts(accountidlist);
//            account::Result res = accountService.syncAccounts(account::Service::Messages);
            }
        }
        QString msg("Done importing SMS. Count: ");
        msg += QString::number(importedsms) + " of " + QString::number(nodes.count());
        toast.setBody(msg);
        toast.setPosition(bb::system::SystemUiPosition::MiddleCenter);
        toast.exec();
}
<|endoftext|>


<commit_before>// Main
//
// (C) Stefan Hoops 2002
//

#ifdef WIN32
# include <windows.h>
# include <winbase.h>
# include <direct.h>
# define getcwd _getcwd
# ifdef ERROR
#  undef ERROR
# endif
#else
# include <unistd.h>
#endif

#include <stdlib.h>
#include <sstream>
#include <string>
#include <iostream>

#define COPASI_MAIN
#define COPASI_TRACE_CONSTRUCTION

#include "copasi.h"
#include "utilities/CGlobals.h"
#include "COptionParser.h"
#include "COptions.h"

C_INT32 TestOptimization(void);

C_INT main(C_INT argc, char *argv[])
{
  char PrgName[512];

#ifdef WIN32
  GetModuleFileName(NULL, PrgName, 512);
#else
  strcpy(PrgName, argv[0]);
#endif
  std::cout << "Starting main program: " << PrgName << std::endl;
  Copasi = new CGlobals;

  std::string line;
  std::ifstream in(".copasirc");
  getline(in, line);

  try
    {
      COptions::init(argc, argv);
      std::cout << "Default: key1 = '" << COptions::getDefault("key1")
      << "'" << std::endl;
      std::cout << "Default: key2 = '" << COptions::getDefault("key2")
      << "'" << std::endl;
    }

  catch (copasi::autoexcept &e)
    {
      switch (e.get_autothrow_id())
        {
        case copasi::autothrow_help:
          std::cout << "Usage: " << argv[0] << " [options]\n";
          std::cout << e.what();
        }
    }

  catch (copasi::option_error &e)
    {
      std::cerr << argv[0] << ": " << e.what() << "\n";
      std::cerr << e.get_help_comment() << std::endl;
    }

  catch (CCopasiException Exception)
    {
      std::cout << Exception.getMessage().getText() << std::endl;
    }

  delete Copasi;
  std::cout << "Leaving main program." << std::endl;
  return 0;
}<commit_msg>Fixes to work under Visual C++.<commit_after>// Main
//
// (C) Stefan Hoops 2002
//

#ifdef WIN32
# include <windows.h>
# include <winbase.h>
# include <direct.h>
# define getcwd _getcwd
# ifdef ERROR
#  undef ERROR
# endif
#else
# include <unistd.h>
#endif

#include <stdlib.h>
#include <sstream>
#include <string>
#include <iostream>

#define COPASI_MAIN
#define COPASI_TRACE_CONSTRUCTION

#include "copasi.h"
#include "utilities/CGlobals.h"
#include "COptionParser.h"
#include "COptions.h"

C_INT32 TestOptimization(void);

C_INT main(C_INT argc, char *argv[])
{
  char PrgName[512];

#ifdef WIN32
  GetModuleFileName(NULL, PrgName, 512);
#else
  strcpy(PrgName, argv[0]);
#endif
  std::cout << "Starting main program: " << PrgName << std::endl;
  Copasi = new CGlobals;

  std::string line;
  std::ifstream in(".copasirc");
  std::getline(in, line);

  try
    {
      COptions::init(argc, argv);
      std::cout << "Default: key1 = '" << COptions::getDefault("key1")
      << "'" << std::endl;
      std::cout << "Default: key2 = '" << COptions::getDefault("key2")
      << "'" << std::endl;
    }

  catch (copasi::autoexcept &e)
    {
      switch (e.get_autothrow_id())
        {
        case copasi::autothrow_help:
          std::cout << "Usage: " << argv[0] << " [options]\n";
          std::cout << e.what();
        }
    }

  catch (copasi::option_error &e)
    {
      std::cerr << argv[0] << ": " << e.what() << "\n";
      std::cerr << e.get_help_comment() << std::endl;
    }

  catch (CCopasiException Exception)
    {
      std::cout << Exception.getMessage().getText() << std::endl;
    }

  delete Copasi;
  std::cout << "Leaving main program." << std::endl;
  return 0;
}
<|endoftext|>


<commit_before><commit_msg>Store getColumnCount()/getRowCount() results in sal_Int32<commit_after><|endoftext|>


<commit_before><commit_msg>flatten and simplify<commit_after><|endoftext|>


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

// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_sw.hxx"

#include "swabstdlg.hxx"
#include "swuilib.hxx"

#include <osl/module.hxx>
#include <tools/string.hxx>

typedef SwAbstractDialogFactory* (__LOADONCALLAPI *SwFuncPtrCreateDialogFactory)();

SwAbstractDialogFactory* SwAbstractDialogFactory::Create()
{
    SwFuncPtrCreateDialogFactory fp = 0;
    static ::osl::Module aDialogLibrary;
    if ( aDialogLibrary.is() || aDialogLibrary.load( String( RTL_CONSTASCII_USTRINGPARAM( DLL_NAME ) ) ) )
        fp = ( SwAbstractDialogFactory* (__LOADONCALLAPI*)() )
            aDialogLibrary.getFunctionSymbol( ::rtl::OUString::createFromAscii("CreateDialogFactory") );
    if ( fp )
        return fp();
    return 0;
}
<commit_msg>INTEGRATION: CWS sb71 (1.5.26); FILE MERGED 2007/10/12 08:41:45 sb 1.5.26.1: #i75466# osl_loadModule without path no longer works on Mac OS X.<commit_after>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: swabstdlg.cxx,v $
 *
 *  $Revision: 1.6 $
 *
 *  last change: $Author: vg $ $Date: 2007-10-15 12:15:35 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_sw.hxx"

#include "swabstdlg.hxx"
#include "swuilib.hxx"

#include <osl/module.hxx>
#include <tools/string.hxx>

typedef SwAbstractDialogFactory* (__LOADONCALLAPI *SwFuncPtrCreateDialogFactory)();

extern "C" { static void SAL_CALL thisModule() {} }

SwAbstractDialogFactory* SwAbstractDialogFactory::Create()
{
    SwFuncPtrCreateDialogFactory fp = 0;
    static ::osl::Module aDialogLibrary;
    if ( aDialogLibrary.is() || aDialogLibrary.loadRelative( &thisModule, String( RTL_CONSTASCII_USTRINGPARAM( DLL_NAME ) ) ) )
        fp = ( SwAbstractDialogFactory* (__LOADONCALLAPI*)() )
            aDialogLibrary.getFunctionSymbol( ::rtl::OUString::createFromAscii("CreateDialogFactory") );
    if ( fp )
        return fp();
    return 0;
}
<|endoftext|>


<commit_before>/**
    Пример на использование статических массивов.

    Программа для игры в блэкджек (упрощённая версия: 1 игрок против комьютера, который и делает раздачу карт)
    ((подробней о правилах: https://ru.wikipedia.org/wiki/%D0%91%D0%BB%D1%8D%D0%BA%D0%B4%D0%B6%D0%B5%D0%BA))

    Что было переделано, по сравнению с аудиторной версией:
        - печать статистики переехала в функцию *show_stat*
        - несколько поменялись условия после окончания добора карт игроком

    ЧТО НУЖНО СДЕЛАТЬ подгруппе прикладников к 26.10.2018:
        1. Когда карта выбирается, желательно знать её индивидуальный вес для просчёта дальнейших шагов.
           Продумать и добавить вывод веса карты в текстовый терминал при каждом вызове функции **get_card*

        2. Добавить возможность игры последовательно в несколько сеансов раздачи карт.
           Это означает, что после окончания одного розыгрыша, спросить игрока о том,
           желает ли он повторить. В случае утвердительного ответа, вернуть массив,
           отвечающий за колоду в первоначальное состояние и повторить игровой сеанс.

        3*. Продумать и реализовать аналог ставок.
            Скажем, банк может ставить случайную сумму от 5 до 20 условных единиц.
            Игрок задаёт свой баланс, и он в ставке произволен - от 1 до максимума, который
            им же и задан. Тогда, в случае выйгрыша - игрок получает свою ставку плюс ставку банка,
            в случае проигрыша - обе ставки получает банк, в случае ничьей - ставки возращаются
            каждой стороне.
            Если получится реализовать пункт 2, то в конце всех сеансов подбить итоги: сколько выйграл/проиграл игрок,
            сколько банк.
            Договоримся, что игрок может уходить в минус не более, чем на 50 условных единиц.
            Банк считаем бездонным.

        Звёздочка "*" в третьем заданнии означает, что продумавание этого пункта - обязательно (то есть - какие
        переменные и/или функции нам нужны, чтобы реализовать написанные в пункте хотелки). Реализация - желательно,
        но не критично, если не будет сделана.
*/
#include "ffhelpers.h"

using namespace std;
using namespace ffomsu;

/**
    Функция, принимающая индекс карты в массиве и возращающая её вес.

    Входные параметры:
        *index* - собственно индекс массива карт.

    Возращаемое значение:
        вес карты, расположенной на месте, имеющм индекс - *index*
*/
int vibor( size_t index)
{
    if (index <= 8) {
        return index + 2;
    }

    if (index==12) {
        return 11;
    }

    return 10;
}

/**
    Функция, отвечающая выбирающая случайным образом карту из колоды.
    Следит за тем, чтобы каждая карты из 52 была выбрана только один раз.

    Входные параметры:
        *cld* - массив карт на 52 элемента.

    Возращаемое значение:
        вес случайно выбранной карты.
*/
int get_card(bool (&cld)[52])
{
    size_t index;
    while (true)
    {
        index=rand_a_b_incl(1, 52);

        if (cld[index-1])
        {
            continue;
        }
        else
        {
            cld[index-1]=true;
            break;
        }
    }
    return vibor(index % 13);
}

/**
    Функция для вывода текущего счёта в текстовый терминал.

    Входные параметры:
        *sum_player* - текущий счёт игрока
        *sum_bank*   - он же, но для банка

    Возращаемое значение:
        отсутствует
*/
void show_stat(int sum_player, int sum_bank)
{
    print("Игрок: ", sum_player, " Банк: ", sum_bank, "\n");
}

int main()
{
    /**
        Массив из 52-элементов, соответствующих колоду карт.
        Каждый элемент может быть равен false, если карты не была ещё выбрана,
        иначе он равен true.
    */
    bool coloda[52] = {false};
    int summa_player = 0, summa_bank = 0;

    summa_player = get_card(coloda) + get_card(coloda);
    summa_bank = get_card(coloda);
    show_stat(summa_player, summa_bank);

    if (summa_player==21 or summa_player==22) {
        print("игрок победил\n");
    } else {
        char choice = 'y';
        while(choice != 'n') {
            print("Продолжим?(y/n): ");
            get_value(choice);
            if (choice=='n') {
                break;
            }

            summa_player += get_card(coloda);
            show_stat(summa_player, summa_bank);
            if (summa_player>21) {
                break;
            }
        }

        if (summa_player == 21) {
            print("Безоговорачная победа игрока!\n");
        } else if (summa_player <= 21) {
            summa_bank+=get_card(coloda);

            while(true) {
                summa_bank+=get_card(coloda);
                show_stat(summa_player, summa_bank);

                if (summa_bank>=17) {
                    break;
                }
            }

            if (summa_player==summa_bank) {
                print("ничья\n");
            } else {
                /// Всё-таки в следующем if-е надо проверять на отрицательность
                if ( (summa_player-summa_bank) < 0 ) {
                    print("Ура, победа!");
                } else {
                    print("Проиграл ");
                }
            }

        } else {
            print("Проиграл ");
        }
    }
}
<commit_msg>small update in fp_181012.cpp example<commit_after>/**
    Пример на использование статических массивов.

    Программа для игры в блэкджек (упрощённая версия: 1 игрок против комьютера, который и делает раздачу карт)
    ((подробней о правилах: https://ru.wikipedia.org/wiki/%D0%91%D0%BB%D1%8D%D0%BA%D0%B4%D0%B6%D0%B5%D0%BA))

    Что было переделано, по сравнению с аудиторной версией:
        - печать статистики переехала в функцию *show_stat*
        - несколько поменялись условия после окончания добора карт игроком

    ЧТО НУЖНО СДЕЛАТЬ подгруппе прикладников к 26.10.2018:
        1. Когда карта выбирается, желательно знать её индивидуальный вес для просчёта дальнейших шагов.
           Продумать и добавить вывод веса карты в текстовый терминал при каждом вызове функции **get_card*

        2. Добавить возможность игры последовательно в несколько сеансов раздачи карт.
           Это означает, что после окончания одного розыгрыша, спросить игрока о том,
           желает ли он повторить. В случае утвердительного ответа, вернуть массив,
           отвечающий за колоду в первоначальное состояние и повторить игровой сеанс.

        3*. Продумать и реализовать аналог ставок.
            Скажем, банк может ставить случайную сумму от 5 до 20 условных единиц.
            Игрок задаёт свой баланс, и он в ставке произволен - от 1 до максимума, который
            им же и задан. Тогда, в случае выйгрыша - игрок получает свою ставку плюс ставку банка,
            в случае проигрыша - обе ставки получает банк, в случае ничьей - ставки возращаются
            каждой стороне.
            Если получится реализовать пункт 2, то в конце всех сеансов подбить итоги: сколько выйграл/проиграл игрок,
            сколько банк.
            Договоримся, что игрок может уходить в минус не более, чем на 50 условных единиц.
            Банк считаем бездонным.

        Звёздочка "*" в третьем заданнии означает, что продумавание этого пункта - обязательно (то есть - какие
        переменные и/или функции нам нужны, чтобы реализовать написанные в пункте хотелки). Реализация - желательно,
        но не критично, если не будет сделана.

        4[опционально]. Сделать более эстетичный вывод сообщений
*/
#include "ffhelpers.h"

using namespace std;
using namespace ffomsu;

/**
    Функция, принимающая индекс карты в массиве и возращающая её вес.

    Входные параметры:
        *index* - собственно индекс массива карт.

    Возращаемое значение:
        вес карты, расположенной на месте, имеющм индекс - *index*
*/
int vibor( size_t index)
{
    if (index <= 8) {
        return index + 2;
    }

    if (index==12) {
        return 11;
    }

    return 10;
}

/**
    Функция, отвечающая выбирающая случайным образом карту из колоды.
    Следит за тем, чтобы каждая карты из 52 была выбрана только один раз.

    Входные параметры:
        *cld* - массив карт на 52 элемента.

    Возращаемое значение:
        вес случайно выбранной карты.
*/
int get_card(bool (&cld)[52])
{
    size_t index;
    while (true)
    {
        index=rand_a_b_incl(1, 52);

        if (cld[index-1])
        {
            continue;
        }
        else
        {
            cld[index-1]=true;
            break;
        }
    }
    return vibor(index % 13);
}

/**
    Функция для вывода текущего счёта в текстовый терминал.

    Входные параметры:
        *sum_player* - текущий счёт игрока
        *sum_bank*   - он же, но для банка

    Возращаемое значение:
        отсутствует
*/
void show_stat(int sum_player, int sum_bank)
{
    print("Игрок: ", sum_player, " Банк: ", sum_bank, "\n");
}

int main()
{
    /**
        Массив из 52-элементов, соответствующих колоду карт.
        Каждый элемент может быть равен false, если карты не была ещё выбрана,
        иначе он равен true.
    */
    bool coloda[52] = {false};
    int summa_player = 0, summa_bank = 0;

    summa_player = get_card(coloda) + get_card(coloda);
    summa_bank = get_card(coloda);
    show_stat(summa_player, summa_bank);

    if (summa_player==21 or summa_player==22) {
        print("игрок победил\n");
    } else {
        char choice = 'y';
        while(choice != 'n') {
            print("Продолжим?(y/n): ");
            get_value(choice);
            if (choice=='n') {
                break;
            }

            summa_player += get_card(coloda);
            show_stat(summa_player, summa_bank);
            if (summa_player>21) {
                break;
            }
        }

        if (summa_player == 21) {
            print("Безоговорачная победа игрока!\n");
        } else if (summa_player <= 21) {
            summa_bank+=get_card(coloda);

            while(true) {
                summa_bank+=get_card(coloda);
                show_stat(summa_player, summa_bank);

                if (summa_bank>=17) {
                    break;
                }
            }

            if (summa_player==summa_bank) {
                print("ничья\n");
            } else {
                /// Всё-таки в следующем if-е надо проверять на отрицательность
                if ( (summa_player-summa_bank) < 0 ) {
                    print("Ура, победа!");
                } else {
                    print("Проиграл ");
                }
            }

        } else {
            print("Проиграл ");
        }
    }
}
<|endoftext|>