<commit_before>// Copyright (c) 2016 Alexander Gallego. All rights reserved.
//
#include "smf/rpc_server.h"

// seastar
#include <seastar/core/execution_stage.hh>
#include <seastar/core/metrics.hh>
#include <seastar/core/prometheus.hh>

#include "smf/histogram_seastar_utils.h"
#include "smf/log.h"
#include "smf/rpc_connection_limits.h"
#include "smf/rpc_envelope.h"
#include "smf/rpc_header_ostream.h"

namespace smf {
namespace stdx = std::experimental;

std::ostream &
operator<<(std::ostream &o, const smf::rpc_server &s) {
  o << "rpc_server{args.ip=" << s.args_.ip << ", args.flags=" << s.args_.flags
    << ", args.rpc_port=" << s.args_.rpc_port
    << ", args.http_port=" << s.args_.http_port << ", rpc_routes=" << s.routes_
    << ", limits=" << *s.limits_
    << ", incoming_filters=" << s.in_filters_.size()
    << ", outgoing_filters=" << s.out_filters_.size() << "}";
  return o;
}

rpc_server::rpc_server(rpc_server_args args)
  : args_(args), limits_(seastar::make_lw_shared<rpc_connection_limits>(
                   args.memory_avail_per_core, args.recv_timeout)) {
  namespace sm = seastar::metrics;
  metrics_.add_group(
    "smf::rpc_server",
    {
      sm::make_derive("active_connections", stats_->active_connections,
                      sm::description("Currently active connections")),
      sm::make_derive("total_connections", stats_->total_connections,
                      sm::description("Counts a total connetions")),
      sm::make_derive("incoming_bytes", stats_->in_bytes,
                      sm::description("Total bytes received of healthy "
                                      "connections - ignores bad connections")),
      sm::make_derive("outgoing_bytes", stats_->out_bytes,
                      sm::description("Total bytes sent to clients")),
      sm::make_derive("bad_requests", stats_->bad_requests,
                      sm::description("Bad requests")),
      sm::make_derive(
        "no_route_requests", stats_->no_route_requests,
        sm::description(
          "Requests made to this sersvice with correct header but no handler")),
      sm::make_derive("completed_requests", stats_->completed_requests,
                      sm::description("Correct round-trip returned responses")),
      sm::make_derive(
        "too_large_requests", stats_->too_large_requests,
        sm::description(
          "Requests made to this server larger than max allowedd (2GB)")),
    });
}

rpc_server::~rpc_server() {}

seastar::future<std::unique_ptr<smf::histogram>>
rpc_server::copy_histogram() {
  auto h = smf::histogram::make_unique();
  *h += *hist_;
  return seastar::make_ready_future<std::unique_ptr<smf::histogram>>(
    std::move(h));
}

void
rpc_server::start() {
  LOG_INFO("Starging server:{}", *this);
  if (!(args_.flags & rpc_server_flags_disable_http_server)) {
    LOG_INFO("Starting HTTP admin server on background future");
    admin_ = seastar::make_lw_shared<seastar::http_server>("smf admin server");
    LOG_INFO("HTTP server started, adding prometheus routes");
    seastar::prometheus::config conf;
    conf.metric_help = "smf rpc server statistics";
    conf.prefix = "smf";
    // start on background co-routine
    seastar::prometheus::add_prometheus_routes(*admin_, conf)
      .then([http_port = args_.http_port, admin = admin_, ip = args_.ip]() {
        return admin
          ->listen(seastar::make_ipv4_address(
            ip.empty() ? seastar::ipv4_addr{http_port}
                       : seastar::ipv4_addr{ip, http_port}))
          .handle_exception([](auto ep) {
            LOG_ERROR("Exception on HTTP Admin: {}", ep);
            return seastar::make_exception_future<>(ep);
          });
      });
  }
  LOG_INFO("Starting rpc server");
  seastar::listen_options lo;
  lo.reuse_address = true;
  listener_ = seastar::listen(
    seastar::make_ipv4_address(
      args_.ip.empty() ? seastar::ipv4_addr{args_.rpc_port}
                       : seastar::ipv4_addr{args_.ip, args_.rpc_port}),
    lo);
  seastar::keep_doing([this] {
    return listener_->accept().then(
      [this, stats = stats_, limits = limits_](
        seastar::connected_socket fd, seastar::socket_address addr) mutable {
        auto conn = seastar::make_lw_shared<rpc_server_connection>(
          std::move(fd), limits, addr, stats, ++connection_idx_);

        open_connections_.insert({connection_idx_, conn});

        // DO NOT return the future. Need to execute in parallel
        handle_client_connection(conn);
      });
  })
    .handle_exception([this](std::exception_ptr eptr) {
      stopped_.set_value();
      try {
        std::rethrow_exception(eptr);
      } catch (const std::system_error &e) {
        // Current and future \ref accept() calls will terminate immediately
        auto const err_value = e.code().value();
        if (err_value == 103 || err_value == 22) {
          LOG_INFO("Shutting down server with expected exit codes");
        } else {
          LOG_ERROR("Unknown system error: {}", e);
        }
      } catch (const std::exception &e) {
        LOG_ERROR("Abrupt server stop: {}", e);
      }
    });
}

seastar::future<>
rpc_server::stop() {
  LOG_INFO("Stopped seastar::accept() calls");
  listener_->abort_accept();
  return stopped_.get_future().then([this] {
    std::for_each(
      open_connections_.begin(), open_connections_.end(),
      [](auto &client_conn) {
        try {
          client_conn.second->conn.socket.shutdown_input();
        } catch (...) {
          LOG_ERROR("Detected error shutting down client connection: ignoring");
        }
      });
    return reply_gate_.close().then([admin = admin_ ? admin_ : nullptr] {
      if (!admin) { return seastar::make_ready_future<>(); }
      return admin->stop().handle_exception([](auto ep) {
        LOG_WARN("Warning (ignoring...) shutting down HTTP server: {}", ep);
        return seastar::make_ready_future<>();
      });
    });
  });
}

// NOTE!!
// Before you refactor this method, please note that parsing the body *MUST*
// come *right* after parsing the header in the same continuation chain.
// therwise you will run into incorrect parsing
//
seastar::future<>
rpc_server::handle_one_client_session(
  seastar::lw_shared_ptr<rpc_server_connection> conn) {
  return rpc_recv_context::parse_header(&conn->conn)
    .then([this, conn](stdx::optional<rpc::header> hdr) {
      if (!hdr) {
        conn->set_error("Error parsing connection header");
        return seastar::make_ready_future<>();
      }
      auto payload_size = hdr->size();
      return conn->limits()
        ->resources_available.wait(payload_size)
        .then([this, conn, h = hdr.value(), payload_size] {
          return rpc_recv_context::parse_payload(&conn->conn, std::move(h))
            .then([this, conn, payload_size](auto maybe_payload) {
              // Launch the actual processing on a background
              dispatch_rpc(payload_size, conn, std::move(maybe_payload));

              return seastar::make_ready_future<>();
            });
        });
    });
}

seastar::future<>
rpc_server::handle_client_connection(
  seastar::lw_shared_ptr<rpc_server_connection> conn) {
  return seastar::do_until(
           [conn] { return !conn->is_valid(); },
           [this, conn]() mutable { return handle_one_client_session(conn); })
    .handle_exception([this, conn](auto ptr) {
      LOG_INFO("Error with client rpc session: {}", ptr);
      conn->set_error("handling client session exception");
      return cleanup_dispatch_rpc(conn);
    });
}

seastar::future<>
rpc_server::dispatch_rpc(int32_t payload_size,
                         seastar::lw_shared_ptr<rpc_server_connection> conn,
                         stdx::optional<rpc_recv_context> ctx) {
  if (!ctx) {
    conn->limits()->resources_available.signal(payload_size);
    conn->set_error("Could not parse payload");
    return seastar::make_ready_future<>();
  }

  return seastar::with_gate(
    reply_gate_,
    [this, conn, context = std::move(ctx.value()), payload_size]() mutable {
      return do_dispatch_rpc(conn, std::move(context))
        .then([this, conn] { return cleanup_dispatch_rpc(conn); })
        .finally(
          [m = hist_->auto_measure(), limits = conn->limits(), payload_size] {
            // these limits are acquired *BEFORE* the call to dispatch_rpc()
            // happens. Critical to understand memory ownership since it happens
            // accross multiple futures.
            limits->resources_available.signal(payload_size);
          });
    });
}

seastar::future<>
rpc_server::do_dispatch_rpc(seastar::lw_shared_ptr<rpc_server_connection> conn,
                            rpc_recv_context &&ctx) {
  if (ctx.request_id() == 0) {
    conn->set_error("Missing request_id. Invalid request");
    return seastar::make_ready_future<>();
  }
  auto method_dispatch = routes_.get_handle_for_request(ctx.request_id());
  if (method_dispatch == nullptr) {
    conn->stats->no_route_requests++;
    conn->set_error("Can't find route for request. Invalid");
    return seastar::make_ready_future<>();
  }
  conn->stats->in_bytes += ctx.header.size() + ctx.payload.size();

  /// the request follow [filters] -> handle -> [filters]
  /// the only way for the handle not to receive the information is if
  /// the filters invalidate the request - they have full mutable access
  /// to it, or they throw an exception if they wish to interrupt the entire
  /// connection
  return stage_apply_incoming_filters(std::move(ctx))
    .then([this, conn, method_dispatch](auto ctx) {
      if (ctx.header.compression() !=
          rpc::compression_flags::compression_flags_none) {
        conn->set_error(fmt::format("There was no decompression filter for "
                                    "compression enum: {}",
                                    ctx.header.compression()));
        return seastar::make_ready_future<>();
      }
      return method_dispatch->apply(std::move(ctx))
        .then([this](rpc_envelope e) {
          return stage_apply_outgoing_filters(std::move(e));
        })
        .then([this, conn](rpc_envelope e) {
          if (!conn->is_valid()) {
            DLOG_INFO("Cannot send respond. client connection '{}' "
                      "is invalid. Skipping reply from server",
                      conn->id);
            return seastar::make_ready_future<>();
          }
          conn->stats->out_bytes += e.letter.size();
          return seastar::with_semaphore(
            conn->serialize_writes, 1, [conn, ee = std::move(e)]() mutable {
              return smf::rpc_envelope::send(&conn->conn.ostream,
                                             std::move(ee));
            });
        });
    })
    .then([this, conn] {
      if (conn->is_valid()) { return conn->conn.ostream.flush(); }
      return seastar::make_ready_future<>();
    });
}
seastar::future<>
rpc_server::cleanup_dispatch_rpc(
  seastar::lw_shared_ptr<rpc_server_connection> conn) {
  if (conn->has_error()) {
    auto it = open_connections_.find(conn->id);
    if (it != open_connections_.end()) {
      open_connections_.erase(it);
      LOG_ERROR("There was an error with the connection: {}",
                conn->get_error());
      conn->stats->bad_requests++;
      conn->stats->active_connections--;
      LOG_INFO("Closing connection for client: {}", conn->conn.remote_address);
      try {
        // after nice shutdow; force it
        conn->conn.disable();
        conn->conn.socket.shutdown_input();
        conn->conn.socket.shutdown_output();
      } catch (...) {}
    }
  } else {
    conn->stats->completed_requests++;
  }

  return seastar::make_ready_future<>();
}

static thread_local auto incoming_stage = seastar::make_execution_stage(
  "smf::rpc_server::incoming::filter", &rpc_server::apply_incoming_filters);

static thread_local auto outgoing_stage = seastar::make_execution_stage(
  "smf::rpc_server::outgoing::filter", &rpc_server::apply_outgoing_filters);

seastar::future<rpc_recv_context>
rpc_server::apply_incoming_filters(rpc_recv_context ctx) {
  return rpc_filter_apply(&in_filters_, std::move(ctx));
}
seastar::future<rpc_envelope>
rpc_server::apply_outgoing_filters(rpc_envelope e) {
  return rpc_filter_apply(&out_filters_, std::move(e));
}

seastar::future<rpc_recv_context>
rpc_server::stage_apply_incoming_filters(rpc_recv_context ctx) {
  return incoming_stage(this, std::move(ctx));
}
seastar::future<rpc_envelope>
rpc_server::stage_apply_outgoing_filters(rpc_envelope e) {
  return outgoing_stage(this, std::move(e));
}
}  // namespace smf
<commit_msg>fix typo<commit_after>// Copyright (c) 2016 Alexander Gallego. All rights reserved.
//
#include "smf/rpc_server.h"

// seastar
#include <seastar/core/execution_stage.hh>
#include <seastar/core/metrics.hh>
#include <seastar/core/prometheus.hh>

#include "smf/histogram_seastar_utils.h"
#include "smf/log.h"
#include "smf/rpc_connection_limits.h"
#include "smf/rpc_envelope.h"
#include "smf/rpc_header_ostream.h"

namespace smf {
namespace stdx = std::experimental;

std::ostream &
operator<<(std::ostream &o, const smf::rpc_server &s) {
  o << "rpc_server{args.ip=" << s.args_.ip << ", args.flags=" << s.args_.flags
    << ", args.rpc_port=" << s.args_.rpc_port
    << ", args.http_port=" << s.args_.http_port << ", rpc_routes=" << s.routes_
    << ", limits=" << *s.limits_
    << ", incoming_filters=" << s.in_filters_.size()
    << ", outgoing_filters=" << s.out_filters_.size() << "}";
  return o;
}

rpc_server::rpc_server(rpc_server_args args)
  : args_(args), limits_(seastar::make_lw_shared<rpc_connection_limits>(
                   args.memory_avail_per_core, args.recv_timeout)) {
  namespace sm = seastar::metrics;
  metrics_.add_group(
    "smf::rpc_server",
    {
      sm::make_derive("active_connections", stats_->active_connections,
                      sm::description("Currently active connections")),
      sm::make_derive("total_connections", stats_->total_connections,
                      sm::description("Counts a total connetions")),
      sm::make_derive("incoming_bytes", stats_->in_bytes,
                      sm::description("Total bytes received of healthy "
                                      "connections - ignores bad connections")),
      sm::make_derive("outgoing_bytes", stats_->out_bytes,
                      sm::description("Total bytes sent to clients")),
      sm::make_derive("bad_requests", stats_->bad_requests,
                      sm::description("Bad requests")),
      sm::make_derive(
        "no_route_requests", stats_->no_route_requests,
        sm::description(
          "Requests made to this sersvice with correct header but no handler")),
      sm::make_derive("completed_requests", stats_->completed_requests,
                      sm::description("Correct round-trip returned responses")),
      sm::make_derive(
        "too_large_requests", stats_->too_large_requests,
        sm::description(
          "Requests made to this server larger than max allowedd (2GB)")),
    });
}

rpc_server::~rpc_server() {}

seastar::future<std::unique_ptr<smf::histogram>>
rpc_server::copy_histogram() {
  auto h = smf::histogram::make_unique();
  *h += *hist_;
  return seastar::make_ready_future<std::unique_ptr<smf::histogram>>(
    std::move(h));
}

void
rpc_server::start() {
  LOG_INFO("Starting server:{}", *this);
  if (!(args_.flags & rpc_server_flags_disable_http_server)) {
    LOG_INFO("Starting HTTP admin server on background future");
    admin_ = seastar::make_lw_shared<seastar::http_server>("smf admin server");
    LOG_INFO("HTTP server started, adding prometheus routes");
    seastar::prometheus::config conf;
    conf.metric_help = "smf rpc server statistics";
    conf.prefix = "smf";
    // start on background co-routine
    seastar::prometheus::add_prometheus_routes(*admin_, conf)
      .then([http_port = args_.http_port, admin = admin_, ip = args_.ip]() {
        return admin
          ->listen(seastar::make_ipv4_address(
            ip.empty() ? seastar::ipv4_addr{http_port}
                       : seastar::ipv4_addr{ip, http_port}))
          .handle_exception([](auto ep) {
            LOG_ERROR("Exception on HTTP Admin: {}", ep);
            return seastar::make_exception_future<>(ep);
          });
      });
  }
  LOG_INFO("Starting rpc server");
  seastar::listen_options lo;
  lo.reuse_address = true;
  listener_ = seastar::listen(
    seastar::make_ipv4_address(
      args_.ip.empty() ? seastar::ipv4_addr{args_.rpc_port}
                       : seastar::ipv4_addr{args_.ip, args_.rpc_port}),
    lo);
  seastar::keep_doing([this] {
    return listener_->accept().then(
      [this, stats = stats_, limits = limits_](
        seastar::connected_socket fd, seastar::socket_address addr) mutable {
        auto conn = seastar::make_lw_shared<rpc_server_connection>(
          std::move(fd), limits, addr, stats, ++connection_idx_);

        open_connections_.insert({connection_idx_, conn});

        // DO NOT return the future. Need to execute in parallel
        handle_client_connection(conn);
      });
  })
    .handle_exception([this](std::exception_ptr eptr) {
      stopped_.set_value();
      try {
        std::rethrow_exception(eptr);
      } catch (const std::system_error &e) {
        // Current and future \ref accept() calls will terminate immediately
        auto const err_value = e.code().value();
        if (err_value == 103 || err_value == 22) {
          LOG_INFO("Shutting down server with expected exit codes");
        } else {
          LOG_ERROR("Unknown system error: {}", e);
        }
      } catch (const std::exception &e) {
        LOG_ERROR("Abrupt server stop: {}", e);
      }
    });
}

seastar::future<>
rpc_server::stop() {
  LOG_INFO("Stopped seastar::accept() calls");
  listener_->abort_accept();
  return stopped_.get_future().then([this] {
    std::for_each(
      open_connections_.begin(), open_connections_.end(),
      [](auto &client_conn) {
        try {
          client_conn.second->conn.socket.shutdown_input();
        } catch (...) {
          LOG_ERROR("Detected error shutting down client connection: ignoring");
        }
      });
    return reply_gate_.close().then([admin = admin_ ? admin_ : nullptr] {
      if (!admin) { return seastar::make_ready_future<>(); }
      return admin->stop().handle_exception([](auto ep) {
        LOG_WARN("Warning (ignoring...) shutting down HTTP server: {}", ep);
        return seastar::make_ready_future<>();
      });
    });
  });
}

// NOTE!!
// Before you refactor this method, please note that parsing the body *MUST*
// come *right* after parsing the header in the same continuation chain.
// therwise you will run into incorrect parsing
//
seastar::future<>
rpc_server::handle_one_client_session(
  seastar::lw_shared_ptr<rpc_server_connection> conn) {
  return rpc_recv_context::parse_header(&conn->conn)
    .then([this, conn](stdx::optional<rpc::header> hdr) {
      if (!hdr) {
        conn->set_error("Error parsing connection header");
        return seastar::make_ready_future<>();
      }
      auto payload_size = hdr->size();
      return conn->limits()
        ->resources_available.wait(payload_size)
        .then([this, conn, h = hdr.value(), payload_size] {
          return rpc_recv_context::parse_payload(&conn->conn, std::move(h))
            .then([this, conn, payload_size](auto maybe_payload) {
              // Launch the actual processing on a background
              dispatch_rpc(payload_size, conn, std::move(maybe_payload));

              return seastar::make_ready_future<>();
            });
        });
    });
}

seastar::future<>
rpc_server::handle_client_connection(
  seastar::lw_shared_ptr<rpc_server_connection> conn) {
  return seastar::do_until(
           [conn] { return !conn->is_valid(); },
           [this, conn]() mutable { return handle_one_client_session(conn); })
    .handle_exception([this, conn](auto ptr) {
      LOG_INFO("Error with client rpc session: {}", ptr);
      conn->set_error("handling client session exception");
      return cleanup_dispatch_rpc(conn);
    });
}

seastar::future<>
rpc_server::dispatch_rpc(int32_t payload_size,
                         seastar::lw_shared_ptr<rpc_server_connection> conn,
                         stdx::optional<rpc_recv_context> ctx) {
  if (!ctx) {
    conn->limits()->resources_available.signal(payload_size);
    conn->set_error("Could not parse payload");
    return seastar::make_ready_future<>();
  }

  return seastar::with_gate(
    reply_gate_,
    [this, conn, context = std::move(ctx.value()), payload_size]() mutable {
      return do_dispatch_rpc(conn, std::move(context))
        .then([this, conn] { return cleanup_dispatch_rpc(conn); })
        .finally(
          [m = hist_->auto_measure(), limits = conn->limits(), payload_size] {
            // these limits are acquired *BEFORE* the call to dispatch_rpc()
            // happens. Critical to understand memory ownership since it happens
            // accross multiple futures.
            limits->resources_available.signal(payload_size);
          });
    });
}

seastar::future<>
rpc_server::do_dispatch_rpc(seastar::lw_shared_ptr<rpc_server_connection> conn,
                            rpc_recv_context &&ctx) {
  if (ctx.request_id() == 0) {
    conn->set_error("Missing request_id. Invalid request");
    return seastar::make_ready_future<>();
  }
  auto method_dispatch = routes_.get_handle_for_request(ctx.request_id());
  if (method_dispatch == nullptr) {
    conn->stats->no_route_requests++;
    conn->set_error("Can't find route for request. Invalid");
    return seastar::make_ready_future<>();
  }
  conn->stats->in_bytes += ctx.header.size() + ctx.payload.size();

  /// the request follow [filters] -> handle -> [filters]
  /// the only way for the handle not to receive the information is if
  /// the filters invalidate the request - they have full mutable access
  /// to it, or they throw an exception if they wish to interrupt the entire
  /// connection
  return stage_apply_incoming_filters(std::move(ctx))
    .then([this, conn, method_dispatch](auto ctx) {
      if (ctx.header.compression() !=
          rpc::compression_flags::compression_flags_none) {
        conn->set_error(fmt::format("There was no decompression filter for "
                                    "compression enum: {}",
                                    ctx.header.compression()));
        return seastar::make_ready_future<>();
      }
      return method_dispatch->apply(std::move(ctx))
        .then([this](rpc_envelope e) {
          return stage_apply_outgoing_filters(std::move(e));
        })
        .then([this, conn](rpc_envelope e) {
          if (!conn->is_valid()) {
            DLOG_INFO("Cannot send respond. client connection '{}' "
                      "is invalid. Skipping reply from server",
                      conn->id);
            return seastar::make_ready_future<>();
          }
          conn->stats->out_bytes += e.letter.size();
          return seastar::with_semaphore(
            conn->serialize_writes, 1, [conn, ee = std::move(e)]() mutable {
              return smf::rpc_envelope::send(&conn->conn.ostream,
                                             std::move(ee));
            });
        });
    })
    .then([this, conn] {
      if (conn->is_valid()) { return conn->conn.ostream.flush(); }
      return seastar::make_ready_future<>();
    });
}
seastar::future<>
rpc_server::cleanup_dispatch_rpc(
  seastar::lw_shared_ptr<rpc_server_connection> conn) {
  if (conn->has_error()) {
    auto it = open_connections_.find(conn->id);
    if (it != open_connections_.end()) {
      open_connections_.erase(it);
      LOG_ERROR("There was an error with the connection: {}",
                conn->get_error());
      conn->stats->bad_requests++;
      conn->stats->active_connections--;
      LOG_INFO("Closing connection for client: {}", conn->conn.remote_address);
      try {
        // after nice shutdow; force it
        conn->conn.disable();
        conn->conn.socket.shutdown_input();
        conn->conn.socket.shutdown_output();
      } catch (...) {}
    }
  } else {
    conn->stats->completed_requests++;
  }

  return seastar::make_ready_future<>();
}

static thread_local auto incoming_stage = seastar::make_execution_stage(
  "smf::rpc_server::incoming::filter", &rpc_server::apply_incoming_filters);

static thread_local auto outgoing_stage = seastar::make_execution_stage(
  "smf::rpc_server::outgoing::filter", &rpc_server::apply_outgoing_filters);

seastar::future<rpc_recv_context>
rpc_server::apply_incoming_filters(rpc_recv_context ctx) {
  return rpc_filter_apply(&in_filters_, std::move(ctx));
}
seastar::future<rpc_envelope>
rpc_server::apply_outgoing_filters(rpc_envelope e) {
  return rpc_filter_apply(&out_filters_, std::move(e));
}

seastar::future<rpc_recv_context>
rpc_server::stage_apply_incoming_filters(rpc_recv_context ctx) {
  return incoming_stage(this, std::move(ctx));
}
seastar::future<rpc_envelope>
rpc_server::stage_apply_outgoing_filters(rpc_envelope e) {
  return outgoing_stage(this, std::move(e));
}
}  // namespace smf
<|endoftext|>


<commit_before>/***************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (directui@nokia.com)
**
** This file is part of libmeegotouch.
**
** If you have questions regarding the use of this file, please contact
** Nokia at directui@nokia.com.
**
** 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
** and appearing in the file LICENSE.LGPL included in the packaging
** of this file.
**
****************************************************************************/

#include "mbuttonview.h"
#include "mbuttonview_p.h"

#include <QPainter>
#include <QGraphicsSceneMouseEvent>
#include <QFontMetricsF>
#include <QPixmap>
#include <QIcon>

#include "mbutton.h"
#include "mfeedback.h"
#include "mtheme.h"
#include "mscalableimage.h"
#include "mviewcreator.h"
#include "mscenemanager.h"
#include "mlabel.h"
#include "mdebug.h"
#include "mtimestamp.h"

// Distance in pixels from the widget bounding box inside which a release is still accepted
#define RELEASE_MISS_DELTA 30

MButtonViewPrivate::MButtonViewPrivate()
    : icon(0), toggledIcon(0), label(NULL), iconFromQIcon(false), toggledIconFromQIcon(false)
{
}

void MButtonViewPrivate::freeIcons()
{
    if (iconFromQIcon && icon) {
        delete icon;
    } else {
        MTheme::releasePixmap(icon);
    }

    if (toggledIconFromQIcon && toggledIcon) {
        delete toggledIcon;
    } else {
        MTheme::releasePixmap(toggledIcon);
    }

    icon = 0;
    toggledIcon = 0;
}

MButtonViewPrivate::~MButtonViewPrivate()
{
    freeIcons();
}

// As the condition of text color and background change for button
bool MButtonViewPrivate::toggleState() const
{
    Q_Q(const MButtonView);
    if (q->model()->checkable()) {
        bool state = (!q->model()->checked() && q->model()->down())
                     || (q->model()->checked() && !q->model()->down());
        return state;
    } else
        return q->model()->down();
}

void MButtonViewPrivate::refreshStyleMode()
{
    Q_Q(MButtonView);

    if (q->model()->down())
        q->style().setModePressed();
    else if (q->model()->checked())
        q->style().setModeSelected();
    else
        q->style().setModeDefault();

    label->setAlignment(q->style()->horizontalTextAlign() | q->style()->verticalTextAlign());
    label->setFont(q->style()->font());
    label->setColor(q->style()->textColor());

    //update the icons only if the iconSize in the style has changed
    QSize size = q->style()->iconSize();
    if (icon && icon->size() != size) {
        if (iconFromQIcon)
            loadIcon(q->model()->icon(), size);
        else
            loadIcon(q->model()->iconID(), size);
    }
    if (toggledIcon && toggledIcon->size() != size) {
        if (toggledIconFromQIcon)
            loadIcon(q->model()->icon(), size);
        else
            loadIcon(q->model()->toggledIconID(), size, QIcon::Selected);
    }

    calcIconTextRects();
}

void MButtonViewPrivate::calcIconTextRects()
{
    Q_Q(const MButtonView);

    //total horizontal and vertical text margins
    int hTextMargin = q->style()->textMarginLeft() + q->style()->textMarginRight();
    int vTextMargin = q->style()->textMarginTop() + q->style()->textMarginBottom();

    //total horizontal and vertical padding
    int hPadding = q->style()->paddingLeft() + q->style()->paddingRight();
    int vPadding = q->style()->paddingTop() + q->style()->paddingBottom();

    //area for the content (icon and text)
    QRect contentRect(q->style()->paddingLeft(), q->style()->paddingTop(),
                      q->size().width() - hPadding,
                      q->size().height() - vPadding);

    //text rect when there is no icon
    QRect textRect = QRect(contentRect.left() + q->style()->textMarginLeft(),
                           contentRect.top() + q->style()->textMarginTop(),
                           contentRect.width() - hTextMargin,
                           contentRect.height() - vTextMargin);

    //icon visible and valid?
    if (q->model()->iconVisible() && (icon || toggledIcon)) {

        int iconWidth = q->style()->iconSize().width();
        int iconHeight = q->style()->iconSize().height();

        //text visible and valid?
        if (q->model()->textVisible() && !q->model()->text().isEmpty()) {

            switch (q->style()->iconAlign()) {
                //icon on left and text on right
            case Qt::AlignLeft: {
                iconRect = QRectF(contentRect.left(), contentRect.center().y() - (iconHeight / 2), iconWidth, iconHeight);
                textRect.setX(iconRect.right() + q->style()->textMarginLeft());
                textRect.setWidth(contentRect.width() - iconWidth - hTextMargin);
                break;
            }

            //icon on right and text on left
            case Qt::AlignRight: {
                iconRect = QRectF(contentRect.right() - iconWidth, contentRect.center().y() - (iconHeight / 2), iconWidth, iconHeight);
                textRect.setWidth(contentRect.width() - iconWidth - hTextMargin);
                break;
            }

            //icon on bottom and text on top
            case Qt::AlignBottom: {
                iconRect = QRectF(contentRect.center().x() - (iconWidth / 2), contentRect.bottom() - iconHeight, iconWidth, iconHeight);
                textRect.setHeight(contentRect.height() - iconHeight - vTextMargin);
                break;
            }

            //icon on top and text on bottom
            default: {
                iconRect = QRectF(contentRect.center().x() - (iconWidth / 2), contentRect.top(), iconWidth, iconHeight);
                textRect.setY(iconRect.bottom() + q->style()->textMarginTop());
                textRect.setHeight(contentRect.height() - iconHeight - vTextMargin);
                break;
            }
            }
        }
        // no text
        else {
            //icon on center
            iconRect = QRectF(contentRect.center().x() - (iconWidth / 2), contentRect.center().y() - (iconHeight / 2), iconWidth, iconHeight);
        }
    }

    //adjust label with button margins
    label->setGeometry(textRect.translated(q->marginLeft(), q->marginTop()));
}

void MButtonViewPrivate::loadIcon(const QIcon &newQIcon, const QSize &newIconSize)
{
    freeIcons();

    icon = new QPixmap(newQIcon.pixmap(newIconSize));
    iconFromQIcon = true;

    toggledIcon = new QPixmap(newQIcon.pixmap(newIconSize, QIcon::Selected));
    if (toggledIcon && !toggledIcon->isNull()) {
        toggledIconFromQIcon = true;
    }
}

void MButtonViewPrivate::loadIcon(const QString &newIconId, const QSize &newIconSize, QIcon::Mode mode)
{
    const QPixmap **tmp;
    bool *fromQIcon;

    if (mode == QIcon::Selected)
    {
        fromQIcon = &toggledIconFromQIcon;
        tmp = &toggledIcon;
    }
    else
    {
        fromQIcon = &iconFromQIcon;
        tmp = &icon;
    }

    if (*tmp)
    {
        if (*fromQIcon)
            delete *tmp;
        else
            MTheme::releasePixmap(*tmp);
    }

    *fromQIcon = false;
    *tmp = 0;

    if (!newIconId.isEmpty())
        *tmp = MTheme::pixmap(newIconId, newIconSize);
}

MButtonView::MButtonView(MButton *controller) :
    MWidgetView(* new MButtonViewPrivate, controller)
{
    Q_D(MButtonView);
    d->label = new MLabel(controller);
    d->label->setParentItem(controller);
    d->label->setTextElide(true);
    d->label->setObjectName("ButtonLabel");
}

MButtonView::MButtonView(MButtonViewPrivate &dd, MButton *controller) :
    MWidgetView(dd, controller)
{
    Q_D(MButtonView);
    d->label = new MLabel(controller);
    d->label->setParentItem(controller);
    d->label->setTextElide(true);
    d->label->setObjectName("ButtonLabel");
}

MButtonView::~MButtonView()
{
    Q_D(MButtonView);
    if (d->label) {
        delete d->label;
        d->label = 0;
    }
}

void MButtonView::resizeEvent(QGraphicsSceneResizeEvent *event)
{
    Q_D(MButtonView);

    MWidgetView::resizeEvent(event);

    d->calcIconTextRects();
}

void MButtonView::drawContents(QPainter *painter, const QStyleOptionGraphicsItem *option) const
{
    Q_UNUSED(option);

    Q_D(const MButtonView);
    mTimestamp("MButtonView", QString("start text=%1").arg(model()->text()));
    drawIcon(painter, d->iconRect);
    mTimestamp("MButtonView", QString("end text=%1").arg(model()->text()));
}

void MButtonView::drawIcon(QPainter *painter, const QRectF &iconRect) const
{
    if (model()->iconVisible()) {
        Q_D(const MButtonView);

        bool toggleState = d->toggleState();

        const QPixmap *pixmap = NULL;
        if (toggleState && d->toggledIcon)
            pixmap = d->toggledIcon;
        else
            pixmap = d->icon;

        if (pixmap)
            painter->drawPixmap(iconRect, *pixmap, QRectF(pixmap->rect()));
    }
}

/*MLabel* MButtonView::label()
{
    Q_D(const MButtonView);
    return d->label;
}*/

void MButtonView::applyStyle()
{
    Q_D(MButtonView);

    MWidgetView::applyStyle();

    d->refreshStyleMode();

    update();
}

void MButtonView::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
    Q_UNUSED(event);

    if (model()->down()) {
        return;
    }
    model()->setDown(true);

    style()->pressFeedback().play();
}

void MButtonView::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
    Q_D(MButtonView);

    QPointF touch = event->scenePos();
    QRectF rect = d->controller->sceneBoundingRect();
    rect.adjust(-RELEASE_MISS_DELTA, -RELEASE_MISS_DELTA,
                RELEASE_MISS_DELTA, RELEASE_MISS_DELTA);

    bool pressed = rect.contains(touch);

    if ( pressed != model()->down()) {
        model()->setDown(pressed);
    }
}

void MButtonView::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
    Q_D(MButtonView);

    if (!model()->down()) {
        return;
    }
    model()->setDown(false);

    style()->releaseFeedback().play();

    QPointF touch = event->scenePos();
    QRectF rect = d->controller->sceneBoundingRect();
    rect.adjust(-RELEASE_MISS_DELTA, -RELEASE_MISS_DELTA,
                RELEASE_MISS_DELTA, RELEASE_MISS_DELTA);
    if (rect.contains(touch))
        model()->click();
}

void MButtonView::cancelEvent(MCancelEvent *event)
{
    Q_UNUSED(event);

    if (!model()->down()) {
        return;
    }
    model()->setDown(false);
}

void MButtonView::updateData(const QList<const char *>& modifications)
{
    Q_D(MButtonView);

    MWidgetView::updateData(modifications);
    const char *member;
    foreach(member, modifications) {
        if (member == MButtonModel::Text) {
            d->label->setText(model()->text());
            d->calcIconTextRects();
        } else if (member == MButtonModel::TextVisible) {
            d->label->setVisible(model()->textVisible());
            d->calcIconTextRects();
        } else if (member == MButtonModel::IconID) {
            d->loadIcon(model()->iconID(), style()->iconSize());
            d->calcIconTextRects();
        } else if (member == MButtonModel::ToggledIconID) {
            d->loadIcon(model()->toggledIconID(), style()->iconSize(), QIcon::Selected);
            d->calcIconTextRects();
        } else if (member == MButtonModel::Icon) {
            d->loadIcon(model()->icon(), style()->iconSize());
            d->calcIconTextRects();
        } else if (member == MButtonModel::IconVisible) {
            d->calcIconTextRects();
        } else if (member == MButtonModel::Down || member == MButtonModel::Checked ||
                   member == MButtonModel::Checkable) {
            d->refreshStyleMode();
        }
    }
    update();
}

void MButtonView::setupModel()
{
    Q_D(MButtonView);

    MWidgetView::setupModel();
    QList<const char *> members;
    if (model()->icon().isNull())
        members << MButtonModel::IconID;
    else
        members << MButtonModel::Icon;
    if (!model()->toggledIconID().isEmpty())
        members << MButtonModel::ToggledIconID;

    updateData(members);

    d->label->setText(model()->text());
    d->label->setVisible(model()->textVisible());

    update();
}

QSizeF MButtonView::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const
{
    Q_D(const MButtonView);

    if (which == Qt::MinimumSize || which == Qt::MaximumSize)
        return MWidgetView::sizeHint(which, constraint);

    if (style()->preferredSize().isValid())
        return style()->preferredSize();

    QSizeF iconSize(0, 0);
    if (model()->iconVisible() && d->icon)
        iconSize = d->icon->size();

    QSizeF textSize(0, 0);
    if (model()->textVisible() && !model()->text().isEmpty()) {
        QFontMetricsF fm(style()->font());
        textSize = fm.size(0, model()->text());
        textSize += QSizeF(style()->textMarginLeft() + style()->textMarginRight(), style()->textMarginTop() + style()->textMarginBottom());
    }

    qreal width = 0, height = 0;
    if (style()->iconAlign() == Qt::AlignTop || style()->iconAlign() == Qt::AlignBottom) {
        width  = qMax(iconSize.width(), textSize.width());
        height = iconSize.height() + textSize.height();
    } else {
        width  = iconSize.width() + textSize.width();
        height = qMax(iconSize.height(), textSize.height());
    }

    return QSizeF(width + style()->paddingLeft() + style()->paddingRight(), height + style()->paddingTop() + style()->paddingBottom());
}

M_REGISTER_VIEW_NEW(MButtonView, MButton)
<commit_msg>Changes: Tuned feedbacks of MButton<commit_after>/***************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (directui@nokia.com)
**
** This file is part of libmeegotouch.
**
** If you have questions regarding the use of this file, please contact
** Nokia at directui@nokia.com.
**
** 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
** and appearing in the file LICENSE.LGPL included in the packaging
** of this file.
**
****************************************************************************/

#include "mbuttonview.h"
#include "mbuttonview_p.h"

#include <QPainter>
#include <QGraphicsSceneMouseEvent>
#include <QFontMetricsF>
#include <QPixmap>
#include <QIcon>

#include "mbutton.h"
#include "mfeedback.h"
#include "mtheme.h"
#include "mscalableimage.h"
#include "mviewcreator.h"
#include "mscenemanager.h"
#include "mlabel.h"
#include "mdebug.h"
#include "mtimestamp.h"

// Distance in pixels from the widget bounding box inside which a release is still accepted
#define RELEASE_MISS_DELTA 30

MButtonViewPrivate::MButtonViewPrivate()
    : icon(0), toggledIcon(0), label(NULL), iconFromQIcon(false), toggledIconFromQIcon(false)
{
}

void MButtonViewPrivate::freeIcons()
{
    if (iconFromQIcon && icon) {
        delete icon;
    } else {
        MTheme::releasePixmap(icon);
    }

    if (toggledIconFromQIcon && toggledIcon) {
        delete toggledIcon;
    } else {
        MTheme::releasePixmap(toggledIcon);
    }

    icon = 0;
    toggledIcon = 0;
}

MButtonViewPrivate::~MButtonViewPrivate()
{
    freeIcons();
}

// As the condition of text color and background change for button
bool MButtonViewPrivate::toggleState() const
{
    Q_Q(const MButtonView);
    if (q->model()->checkable()) {
        bool state = (!q->model()->checked() && q->model()->down())
                     || (q->model()->checked() && !q->model()->down());
        return state;
    } else
        return q->model()->down();
}

void MButtonViewPrivate::refreshStyleMode()
{
    Q_Q(MButtonView);

    if (q->model()->down())
        q->style().setModePressed();
    else if (q->model()->checked())
        q->style().setModeSelected();
    else
        q->style().setModeDefault();

    label->setAlignment(q->style()->horizontalTextAlign() | q->style()->verticalTextAlign());
    label->setFont(q->style()->font());
    label->setColor(q->style()->textColor());

    //update the icons only if the iconSize in the style has changed
    QSize size = q->style()->iconSize();
    if (icon && icon->size() != size) {
        if (iconFromQIcon)
            loadIcon(q->model()->icon(), size);
        else
            loadIcon(q->model()->iconID(), size);
    }
    if (toggledIcon && toggledIcon->size() != size) {
        if (toggledIconFromQIcon)
            loadIcon(q->model()->icon(), size);
        else
            loadIcon(q->model()->toggledIconID(), size, QIcon::Selected);
    }

    calcIconTextRects();
}

void MButtonViewPrivate::calcIconTextRects()
{
    Q_Q(const MButtonView);

    //total horizontal and vertical text margins
    int hTextMargin = q->style()->textMarginLeft() + q->style()->textMarginRight();
    int vTextMargin = q->style()->textMarginTop() + q->style()->textMarginBottom();

    //total horizontal and vertical padding
    int hPadding = q->style()->paddingLeft() + q->style()->paddingRight();
    int vPadding = q->style()->paddingTop() + q->style()->paddingBottom();

    //area for the content (icon and text)
    QRect contentRect(q->style()->paddingLeft(), q->style()->paddingTop(),
                      q->size().width() - hPadding,
                      q->size().height() - vPadding);

    //text rect when there is no icon
    QRect textRect = QRect(contentRect.left() + q->style()->textMarginLeft(),
                           contentRect.top() + q->style()->textMarginTop(),
                           contentRect.width() - hTextMargin,
                           contentRect.height() - vTextMargin);

    //icon visible and valid?
    if (q->model()->iconVisible() && (icon || toggledIcon)) {

        int iconWidth = q->style()->iconSize().width();
        int iconHeight = q->style()->iconSize().height();

        //text visible and valid?
        if (q->model()->textVisible() && !q->model()->text().isEmpty()) {

            switch (q->style()->iconAlign()) {
                //icon on left and text on right
            case Qt::AlignLeft: {
                iconRect = QRectF(contentRect.left(), contentRect.center().y() - (iconHeight / 2), iconWidth, iconHeight);
                textRect.setX(iconRect.right() + q->style()->textMarginLeft());
                textRect.setWidth(contentRect.width() - iconWidth - hTextMargin);
                break;
            }

            //icon on right and text on left
            case Qt::AlignRight: {
                iconRect = QRectF(contentRect.right() - iconWidth, contentRect.center().y() - (iconHeight / 2), iconWidth, iconHeight);
                textRect.setWidth(contentRect.width() - iconWidth - hTextMargin);
                break;
            }

            //icon on bottom and text on top
            case Qt::AlignBottom: {
                iconRect = QRectF(contentRect.center().x() - (iconWidth / 2), contentRect.bottom() - iconHeight, iconWidth, iconHeight);
                textRect.setHeight(contentRect.height() - iconHeight - vTextMargin);
                break;
            }

            //icon on top and text on bottom
            default: {
                iconRect = QRectF(contentRect.center().x() - (iconWidth / 2), contentRect.top(), iconWidth, iconHeight);
                textRect.setY(iconRect.bottom() + q->style()->textMarginTop());
                textRect.setHeight(contentRect.height() - iconHeight - vTextMargin);
                break;
            }
            }
        }
        // no text
        else {
            //icon on center
            iconRect = QRectF(contentRect.center().x() - (iconWidth / 2), contentRect.center().y() - (iconHeight / 2), iconWidth, iconHeight);
        }
    }

    //adjust label with button margins
    label->setGeometry(textRect.translated(q->marginLeft(), q->marginTop()));
}

void MButtonViewPrivate::loadIcon(const QIcon &newQIcon, const QSize &newIconSize)
{
    freeIcons();

    icon = new QPixmap(newQIcon.pixmap(newIconSize));
    iconFromQIcon = true;

    toggledIcon = new QPixmap(newQIcon.pixmap(newIconSize, QIcon::Selected));
    if (toggledIcon && !toggledIcon->isNull()) {
        toggledIconFromQIcon = true;
    }
}

void MButtonViewPrivate::loadIcon(const QString &newIconId, const QSize &newIconSize, QIcon::Mode mode)
{
    const QPixmap **tmp;
    bool *fromQIcon;

    if (mode == QIcon::Selected)
    {
        fromQIcon = &toggledIconFromQIcon;
        tmp = &toggledIcon;
    }
    else
    {
        fromQIcon = &iconFromQIcon;
        tmp = &icon;
    }

    if (*tmp)
    {
        if (*fromQIcon)
            delete *tmp;
        else
            MTheme::releasePixmap(*tmp);
    }

    *fromQIcon = false;
    *tmp = 0;

    if (!newIconId.isEmpty())
        *tmp = MTheme::pixmap(newIconId, newIconSize);
}

MButtonView::MButtonView(MButton *controller) :
    MWidgetView(* new MButtonViewPrivate, controller)
{
    Q_D(MButtonView);
    d->label = new MLabel(controller);
    d->label->setParentItem(controller);
    d->label->setTextElide(true);
    d->label->setObjectName("ButtonLabel");
}

MButtonView::MButtonView(MButtonViewPrivate &dd, MButton *controller) :
    MWidgetView(dd, controller)
{
    Q_D(MButtonView);
    d->label = new MLabel(controller);
    d->label->setParentItem(controller);
    d->label->setTextElide(true);
    d->label->setObjectName("ButtonLabel");
}

MButtonView::~MButtonView()
{
    Q_D(MButtonView);
    if (d->label) {
        delete d->label;
        d->label = 0;
    }
}

void MButtonView::resizeEvent(QGraphicsSceneResizeEvent *event)
{
    Q_D(MButtonView);

    MWidgetView::resizeEvent(event);

    d->calcIconTextRects();
}

void MButtonView::drawContents(QPainter *painter, const QStyleOptionGraphicsItem *option) const
{
    Q_UNUSED(option);

    Q_D(const MButtonView);
    mTimestamp("MButtonView", QString("start text=%1").arg(model()->text()));
    drawIcon(painter, d->iconRect);
    mTimestamp("MButtonView", QString("end text=%1").arg(model()->text()));
}

void MButtonView::drawIcon(QPainter *painter, const QRectF &iconRect) const
{
    if (model()->iconVisible()) {
        Q_D(const MButtonView);

        bool toggleState = d->toggleState();

        const QPixmap *pixmap = NULL;
        if (toggleState && d->toggledIcon)
            pixmap = d->toggledIcon;
        else
            pixmap = d->icon;

        if (pixmap)
            painter->drawPixmap(iconRect, *pixmap, QRectF(pixmap->rect()));
    }
}

/*MLabel* MButtonView::label()
{
    Q_D(const MButtonView);
    return d->label;
}*/

void MButtonView::applyStyle()
{
    Q_D(MButtonView);

    MWidgetView::applyStyle();

    d->refreshStyleMode();

    update();
}

void MButtonView::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
    Q_UNUSED(event);

    if (model()->down()) {
        return;
    }
    model()->setDown(true);

    style()->pressFeedback().play();
}

void MButtonView::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
    Q_D(MButtonView);

    QPointF touch = event->scenePos();
    QRectF rect = d->controller->sceneBoundingRect();
    rect.adjust(-RELEASE_MISS_DELTA, -RELEASE_MISS_DELTA,
                RELEASE_MISS_DELTA, RELEASE_MISS_DELTA);

    if (rect.contains(touch)) {
        if (!model()->down()) {
            model()->setDown(true);
            style()->pressFeedback().play();
        }
    } else {
        if (model()->down()) {
            model()->setDown(false);
            style()->cancelFeedback().play();
        }
    }

}

void MButtonView::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
    Q_D(MButtonView);

    if (!model()->down()) {
        return;
    }
    model()->setDown(false);

    style()->releaseFeedback().play();

    QPointF touch = event->scenePos();
    QRectF rect = d->controller->sceneBoundingRect();
    rect.adjust(-RELEASE_MISS_DELTA, -RELEASE_MISS_DELTA,
                RELEASE_MISS_DELTA, RELEASE_MISS_DELTA);
    if (rect.contains(touch))
        model()->click();
}

void MButtonView::cancelEvent(MCancelEvent *event)
{
    Q_UNUSED(event);

    if (!model()->down()) {
        return;
    }

    style()->cancelFeedback().play();

    model()->setDown(false);
}

void MButtonView::updateData(const QList<const char *>& modifications)
{
    Q_D(MButtonView);

    MWidgetView::updateData(modifications);
    const char *member;
    foreach(member, modifications) {
        if (member == MButtonModel::Text) {
            d->label->setText(model()->text());
            d->calcIconTextRects();
        } else if (member == MButtonModel::TextVisible) {
            d->label->setVisible(model()->textVisible());
            d->calcIconTextRects();
        } else if (member == MButtonModel::IconID) {
            d->loadIcon(model()->iconID(), style()->iconSize());
            d->calcIconTextRects();
        } else if (member == MButtonModel::ToggledIconID) {
            d->loadIcon(model()->toggledIconID(), style()->iconSize(), QIcon::Selected);
            d->calcIconTextRects();
        } else if (member == MButtonModel::Icon) {
            d->loadIcon(model()->icon(), style()->iconSize());
            d->calcIconTextRects();
        } else if (member == MButtonModel::IconVisible) {
            d->calcIconTextRects();
        } else if (member == MButtonModel::Down || member == MButtonModel::Checked ||
                   member == MButtonModel::Checkable) {
            d->refreshStyleMode();
        }
    }
    update();
}

void MButtonView::setupModel()
{
    Q_D(MButtonView);

    MWidgetView::setupModel();
    QList<const char *> members;
    if (model()->icon().isNull())
        members << MButtonModel::IconID;
    else
        members << MButtonModel::Icon;
    if (!model()->toggledIconID().isEmpty())
        members << MButtonModel::ToggledIconID;

    updateData(members);

    d->label->setText(model()->text());
    d->label->setVisible(model()->textVisible());

    update();
}

QSizeF MButtonView::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const
{
    Q_D(const MButtonView);

    if (which == Qt::MinimumSize || which == Qt::MaximumSize)
        return MWidgetView::sizeHint(which, constraint);

    if (style()->preferredSize().isValid())
        return style()->preferredSize();

    QSizeF iconSize(0, 0);
    if (model()->iconVisible() && d->icon)
        iconSize = d->icon->size();

    QSizeF textSize(0, 0);
    if (model()->textVisible() && !model()->text().isEmpty()) {
        QFontMetricsF fm(style()->font());
        textSize = fm.size(0, model()->text());
        textSize += QSizeF(style()->textMarginLeft() + style()->textMarginRight(), style()->textMarginTop() + style()->textMarginBottom());
    }

    qreal width = 0, height = 0;
    if (style()->iconAlign() == Qt::AlignTop || style()->iconAlign() == Qt::AlignBottom) {
        width  = qMax(iconSize.width(), textSize.width());
        height = iconSize.height() + textSize.height();
    } else {
        width  = iconSize.width() + textSize.width();
        height = qMax(iconSize.height(), textSize.height());
    }

    return QSizeF(width + style()->paddingLeft() + style()->paddingRight(), height + style()->paddingTop() + style()->paddingBottom());
}

M_REGISTER_VIEW_NEW(MButtonView, MButton)
<|endoftext|>


<commit_before>/*
 * Distributed under the OSI-approved Apache License, Version 2.0.  See
 * accompanying file Copyright.txt for details.
 */
#include <cstdint>
#include <cstring>

#include <limits>
#include <stdexcept>

#include <adios2.h>
#include <adios2/common/ADIOSTypes.h>

#include <gtest/gtest.h>

class ADIOSHierarchicalReadVariableTest : public ::testing::Test
{
public:
    ADIOSHierarchicalReadVariableTest() = default;
};

TEST_F(ADIOSHierarchicalReadVariableTest, Read)
{
    std::string filename = "ADIOSHierarchicalReadVariable.bp";

    // Number of steps
    const std::size_t NSteps = 2;

    long unsigned int rank, size;

#if ADIOS2_USE_MPI
    MPI_Comm_rank(MPI_COMM_WORLD, &rank);
    MPI_Comm_size(MPI_COMM_WORLD, &size);
#else
    rank = 0;
    size = 1;
#endif

    // Write test data using BP
    {
#if ADIOS2_USE_MPI
        adios2::ADIOS adios(MPI_COMM_WORLD);
#else
        adios2::ADIOS adios;
#endif
        adios2::IO io = adios.DeclareIO("TestIO");
        io.SetEngine("BPFile");

        io.AddTransport("file");
        adios2::Engine engine = io.Open(filename, adios2::Mode::Write);
        const std::size_t Nx = 10;
        const adios2::Dims shape = {size * Nx};
        const adios2::Dims start = {rank * Nx};
        const adios2::Dims count = {Nx};

        auto var1 = io.DefineVariable<int32_t>(
            "group1/group2/group3/group4/variable1", shape, start, count);
        auto var2 = io.DefineVariable<int32_t>(
            "group1/group2/group3/group4/variable2", shape, start, count);
        auto var3 = io.DefineVariable<int32_t>(
            "group1/group2/group3/group4/variable3", shape, start, count);
        auto var4 = io.DefineVariable<int32_t>(
            "group1/group2/group3/group4/variable4", shape, start, count);
        auto var5 = io.DefineVariable<int32_t>(
            "group1/group2/group3/group4/variable5", shape, start, count);
        auto var6 =
            io.DefineVariable<int32_t>("variable6", shape, start, count);
        std::vector<int32_t> Ints = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};

        for (size_t step = 0; step < NSteps; ++step)
        {
            engine.BeginStep();

            engine.Put(var1, Ints.data());
            engine.Put(var2, Ints.data());
            engine.Put(var3, Ints.data());
            engine.Put(var4, Ints.data());
            engine.Put(var5, Ints.data());
            engine.Put(var6, Ints.data());

            engine.EndStep();
        }
        engine.Close();

        engine = io.Open(filename, adios2::Mode::Read);
        for (size_t step = 0; step < NSteps; step++)
        {
            engine.BeginStep();
            auto g = io.InquireGroup('/');
            auto res = g.AvailableGroups();
            EXPECT_EQ(res[0], "group1");
            res = g.AvailableVariables();
            EXPECT_EQ(res[0], "variable6");
            g.setPath("group1/group2");
            res = g.AvailableGroups();
            EXPECT_EQ(res[0], "group3");
            g.setPath("group1/group2/group3");
            res = g.AvailableGroups();
            EXPECT_EQ(res[0], "group4");
            g.setPath("group1/group2/group3/group4");
            res = g.AvailableGroups();
            EXPECT_EQ(res.size(), 0);
            res = g.AvailableVariables();
            EXPECT_EQ(res.size(), 5);
            res = g.AvailableAttributes();
            EXPECT_EQ(res.size(), 0);
            engine.EndStep();
        }
        for (size_t step = 0; step < NSteps; step++)
        {
            engine.BeginStep();
            auto g = io.InquireGroup('/');
            auto res = g.AvailableGroups();
            EXPECT_EQ(res[0], "group1");
            res = g.AvailableVariables();
            EXPECT_EQ(res[0], "variable6");
            engine.EndStep();
        }
        for (size_t step = 0; step < NSteps; step++)
        {
            auto g = io.InquireGroup('/');
            auto var = g.InquireVariable<int32_t>("variable6");
            EXPECT_TRUE(var);
            if (var)
            {
                std::vector<int32_t> myInts;
                var.SetSelection({{Nx * rank}, {Nx}});
                engine.Get<int32_t>(var, myInts, adios2::Mode::Sync);
                EXPECT_EQ(Ints, myInts);
            }
        }
        for (size_t step = 0; step < NSteps; step++)
        {
            auto g = io.InquireGroup('/');
            g.setPath("group1/group2/group3/group4");
            auto var = g.InquireVariable<int32_t>("variable1");
            EXPECT_TRUE(var);
            if (var)
            {
                std::vector<int32_t> myInts;
                var.SetSelection({{Nx * rank}, {Nx}});
                engine.Get<int32_t>(var, myInts, adios2::Mode::Sync);

                EXPECT_EQ(Ints, myInts);
            }
        }
        engine.Close();
    }
}
int main(int argc, char **argv)
{
    ::testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}
<commit_msg>Fix Hierarchical test<commit_after>/*
 * Distributed under the OSI-approved Apache License, Version 2.0.  See
 * accompanying file Copyright.txt for details.
 */
#include <cstdint>
#include <cstring>

#include <limits>
#include <stdexcept>

#include <adios2.h>
#include <adios2/common/ADIOSTypes.h>

#include <gtest/gtest.h>

class ADIOSHierarchicalReadVariableTest : public ::testing::Test
{
public:
    ADIOSHierarchicalReadVariableTest() = default;
};

TEST_F(ADIOSHierarchicalReadVariableTest, Read)
{
    std::string filename = "ADIOSHierarchicalReadVariable.bp";

    // Number of steps
    const std::size_t NSteps = 2;

    long unsigned int rank, size;

#if ADIOS2_USE_MPI
    MPI_Comm_rank(MPI_COMM_WORLD, &rank);
    MPI_Comm_size(MPI_COMM_WORLD, &size);
#else
    rank = 0;
    size = 1;
#endif

    // Write test data using BP
    {
#if ADIOS2_USE_MPI
        adios2::ADIOS adios(MPI_COMM_WORLD);
#else
        adios2::ADIOS adios;
#endif
        adios2::IO io = adios.DeclareIO("TestIO");
        io.SetEngine("BPFile");

        io.AddTransport("file");
        adios2::Engine engine = io.Open(filename, adios2::Mode::Write);
        const std::size_t Nx = 10;
        const adios2::Dims shape = {size * Nx};
        const adios2::Dims start = {rank * Nx};
        const adios2::Dims count = {Nx};

        auto var1 = io.DefineVariable<int32_t>(
            "group1/group2/group3/group4/variable1", shape, start, count);
        auto var2 = io.DefineVariable<int32_t>(
            "group1/group2/group3/group4/variable2", shape, start, count);
        auto var3 = io.DefineVariable<int32_t>(
            "group1/group2/group3/group4/variable3", shape, start, count);
        auto var4 = io.DefineVariable<int32_t>(
            "group1/group2/group3/group4/variable4", shape, start, count);
        auto var5 = io.DefineVariable<int32_t>(
            "group1/group2/group3/group4/variable5", shape, start, count);
        auto var6 =
            io.DefineVariable<int32_t>("variable6", shape, start, count);
        std::vector<int32_t> Ints = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};

        for (size_t step = 0; step < NSteps; ++step)
        {
            engine.BeginStep();

            engine.Put(var1, Ints.data());
            engine.Put(var2, Ints.data());
            engine.Put(var3, Ints.data());
            engine.Put(var4, Ints.data());
            engine.Put(var5, Ints.data());
            engine.Put(var6, Ints.data());

            engine.EndStep();
        }
        engine.Close();

        engine = io.Open(filename, adios2::Mode::Read);
        for (size_t step = 0; step < NSteps; step++)
        {
            engine.BeginStep();
            auto g = io.InquireGroup('/');
            auto res = g.AvailableGroups();
            EXPECT_EQ(res[0], "group1");
            res = g.AvailableVariables();
            EXPECT_EQ(res[0], "variable6");
            g.setPath("group1/group2");
            res = g.AvailableGroups();
            EXPECT_EQ(res[0], "group3");
            g.setPath("group1/group2/group3");
            res = g.AvailableGroups();
            EXPECT_EQ(res[0], "group4");
            g.setPath("group1/group2/group3/group4");
            res = g.AvailableGroups();
            EXPECT_EQ(res.size(), 0);
            res = g.AvailableVariables();
            EXPECT_EQ(res.size(), 5);
            res = g.AvailableAttributes();
            EXPECT_EQ(res.size(), 0);

            g = io.InquireGroup('/');
            auto var = g.InquireVariable<int32_t>("variable6");
            EXPECT_TRUE(var);
            if (var)
            {
                std::vector<int32_t> myInts;
                var.SetSelection({{Nx * rank}, {Nx}});
                engine.Get<int32_t>(var, myInts, adios2::Mode::Sync);
                EXPECT_EQ(Ints, myInts);
            }
            g.setPath("group1/group2/group3/group4");
            var = g.InquireVariable<int32_t>("variable1");
            EXPECT_TRUE(var);
            if (var)
            {
                std::vector<int32_t> myInts;
                var.SetSelection({{Nx * rank}, {Nx}});
                engine.Get<int32_t>(var, myInts, adios2::Mode::Sync);

                EXPECT_EQ(Ints, myInts);
            }
            engine.EndStep();
        }
        engine.Close();
    }
}
int main(int argc, char **argv)
{
    ::testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}
<|endoftext|>


<commit_before>/****************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the Qt Mobility Components.
**
** $QT_BEGIN_LICENSE:LGPL$
** No Commercial Usage
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights.  These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
**
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/

//TESTED_COMPONENT=src/documentgallery

#include <qdocumentgallery.h>

#include <QtTest/QtTest>

QTM_USE_NAMESPACE

Q_DECLARE_METATYPE(QGalleryProperty::Attributes)

class tst_QDocumentGallery : public QObject
{
    Q_OBJECT
private Q_SLOTS:
    void isRequestSupported();
    void itemTypeProperties_data();
    void itemTypeProperties();
    void propertyAttributes_data();
    void propertyAttributes();

private:
    QDocumentGallery gallery;
};

void tst_QDocumentGallery::isRequestSupported()
{
#if defined(Q_OS_UNIX) && !defined(QT_NO_DBUS) || defined (Q_OS_SYMBIAN)
    const bool platformSupported = true;
#else
    const bool platformSupported = false;
#endif

    QCOMPARE(gallery.isRequestSupported(QGalleryAbstractRequest::QueryRequest), platformSupported);
    QCOMPARE(gallery.isRequestSupported(QGalleryAbstractRequest::ItemRequest), platformSupported);
    QCOMPARE(gallery.isRequestSupported(QGalleryAbstractRequest::TypeRequest), platformSupported);
    QCOMPARE(gallery.isRequestSupported(QGalleryAbstractRequest::RequestType(1000)), false);
}

void tst_QDocumentGallery::itemTypeProperties_data()
{
    QTest::addColumn<QString>("itemType");
    QTest::addColumn<QStringList>("propertyNames");

    QTest::newRow("null item type") << QString() << QStringList();
    QTest::newRow("non-existent item type") << QString::fromLatin1("Hello") << QStringList();

    const QStringList fileProperties = QStringList()
#if defined(Q_WS_MAEMO_6)
            << QDocumentGallery::author
            << QDocumentGallery::comments
            << QDocumentGallery::copyright
            << QDocumentGallery::description
            << QDocumentGallery::fileExtension
            << QDocumentGallery::fileName
            << QDocumentGallery::filePath
            << QDocumentGallery::fileSize
            << QDocumentGallery::keywords
            << QDocumentGallery::language
            << QDocumentGallery::lastAccessed
            << QDocumentGallery::lastModified
            << QDocumentGallery::mimeType
            << QDocumentGallery::path
            << QDocumentGallery::rating
            << QDocumentGallery::subject
            << QDocumentGallery::title
            << QDocumentGallery::url;
#elif defined(Q_OS_UNIX) && !defined(QT_NO_DBUS)
            << QDocumentGallery::copyright
            << QDocumentGallery::fileName
            << QDocumentGallery::path
            << QDocumentGallery::filePath
            << QDocumentGallery::url
            << QDocumentGallery::fileSize
            << QDocumentGallery::language
            << QDocumentGallery::lastAccessed
            << QDocumentGallery::lastModified
            << QDocumentGallery::mimeType;
#elif defined (Q_OS_SYMBIAN)
            << QDocumentGallery::url
            << QDocumentGallery::fileName
            << QDocumentGallery::filePath
            << QDocumentGallery::fileSize
            << QDocumentGallery::lastModified
            << QDocumentGallery::title
            << QDocumentGallery::mimeType
            << QDocumentGallery::author
            << QDocumentGallery::copyright
            << QDocumentGallery::description
            << QDocumentGallery::comments
            << QDocumentGallery::rating
#endif
            ;
    QTest::newRow("File") << QString(QDocumentGallery::File) << (QStringList(fileProperties)
#if !defined(Q_WS_MAEMO_6) && defined(Q_OS_UNIX) && !defined(QT_NO_DBUS)
            << QDocumentGallery::author
            << QDocumentGallery::description
            << QDocumentGallery::keywords
            << QDocumentGallery::rating
            << QDocumentGallery::subject
            << QDocumentGallery::title
#endif
        );

    QTest::newRow("Audio") << QString(QDocumentGallery::Audio) << (QStringList(fileProperties)
#if defined(Q_OS_UNIX) && !defined(QT_NO_DBUS)
            << QDocumentGallery::albumArtist
            << QDocumentGallery::albumTitle
            << QDocumentGallery::artist
            << QDocumentGallery::audioBitRate
            << QDocumentGallery::audioCodec
            << QDocumentGallery::channelCount
            << QDocumentGallery::discNumber
            << QDocumentGallery::duration
            << QDocumentGallery::genre
            << QDocumentGallery::lastPlayed
            << QDocumentGallery::lyrics
            << QDocumentGallery::playCount
            << QDocumentGallery::sampleRate
            << QDocumentGallery::trackNumber
            << QDocumentGallery::performer
#if defined(Q_WS_MAEMO_6)
            << QDocumentGallery::composer
            << QDocumentGallery::resumePosition
#else
            << QDocumentGallery::description
            << QDocumentGallery::title
#endif
#elif defined (Q_OS_SYMBIAN)
            << QDocumentGallery::duration
            << QDocumentGallery::performer
            << QDocumentGallery::audioCodec
            << QDocumentGallery::audioBitRate
            << QDocumentGallery::playCount
            << QDocumentGallery::sampleRate
            << QDocumentGallery::albumTitle
            << QDocumentGallery::trackNumber
            << QDocumentGallery::albumArtist
            << QDocumentGallery::artist
            << QDocumentGallery::composer
            << QDocumentGallery::genre
#endif
    );

    QTest::newRow("Album") << QString(QDocumentGallery::Album) << (QStringList()
#if defined(Q_OS_UNIX) && !defined(QT_NO_DBUS)
            << QDocumentGallery::albumArtist
            << QDocumentGallery::albumTitle
            << QDocumentGallery::artist
            << QDocumentGallery::duration
#if !defined(Q_WS_MAEMO_6)
            << QDocumentGallery::rating
#endif
            << QDocumentGallery::title
            << QDocumentGallery::trackCount
#endif
    );
    QTest::newRow("PhotoAlbum") << QString(QDocumentGallery::PhotoAlbum) << (QStringList()
#if defined(Q_OS_UNIX) && !defined(QT_NO_DBUS)
                << QDocumentGallery::title
#if defined (Q_WS_MAEMO_6)
                << QDocumentGallery::count
#else
                << QDocumentGallery::trackCount
#endif
#elif defined (Q_OS_SYMBIAN)
                << QDocumentGallery::url
                << QDocumentGallery::fileSize
                << QDocumentGallery::lastModified
                << QDocumentGallery::title
                << QDocumentGallery::mimeType
#endif
    );

#if defined (Q_OS_SYMBIAN)
    QTest::newRow("Image") << QString(QDocumentGallery::Image) << (QStringList(fileProperties)
            << QDocumentGallery::duration
            << QDocumentGallery::performer
            << QDocumentGallery::playCount
            << QDocumentGallery::width
            << QDocumentGallery::height
            << QDocumentGallery::orientation
            << QDocumentGallery::dateTaken
            << QDocumentGallery::cameraManufacturer
            << QDocumentGallery::cameraModel
            << QDocumentGallery::exposureProgram
            << QDocumentGallery::exposureTime
            << QDocumentGallery::fNumber
            << QDocumentGallery::flashEnabled
            << QDocumentGallery::focalLength
            << QDocumentGallery::meteringMode
            << QDocumentGallery::whiteBalance
    );
    QTest::newRow("Video") << QString(QDocumentGallery::Video) << (QStringList(fileProperties)
            << QDocumentGallery::duration
            << QDocumentGallery::performer
            << QDocumentGallery::videoBitRate
            << QDocumentGallery::playCount
            << QDocumentGallery::width
            << QDocumentGallery::height
            << QDocumentGallery::language
            << QDocumentGallery::frameRate
            << QDocumentGallery::resumePosition
    );
#endif

}

void tst_QDocumentGallery::itemTypeProperties()
{
    QFETCH(QString, itemType);
    QFETCH(QStringList, propertyNames);

    QStringList galleryPropertyNames = gallery.itemTypePropertyNames(itemType);
    propertyNames.sort();
    galleryPropertyNames.sort();

    QCOMPARE(galleryPropertyNames, propertyNames);
}

void tst_QDocumentGallery::propertyAttributes_data()
{
    QTest::addColumn<QString>("itemType");
    QTest::addColumn<QString>("propertyName");
    QTest::addColumn<QGalleryProperty::Attributes>("propertyAttributes");

    QTest::newRow("Null itemType, propertyName")
            << QString()
            << QString()
            << QGalleryProperty::Attributes();
    QTest::newRow("Null itemType, invalid propertyName")
            << QString()
            << QString::fromLatin1("Goodbye")
            << QGalleryProperty::Attributes();
    QTest::newRow("Null itemType, valid propertyName")
            << QString()
            << QString(QDocumentGallery::fileName)
            << QGalleryProperty::Attributes();
    QTest::newRow("Invalid itemType, invalid propertyName")
            << QString::fromLatin1("Hello")
            << QString::fromLatin1("Goodbye")
            << QGalleryProperty::Attributes();
    QTest::newRow("Invalid itemType, valid propertyName")
            << QString::fromLatin1("Hello")
            << QString(QDocumentGallery::fileName)
            << QGalleryProperty::Attributes();
    QTest::newRow("Valid itemType, invalid propertyName")
            << QString(QDocumentGallery::File)
            << QString::fromLatin1("Goodbye")
            << QGalleryProperty::Attributes();
    QTest::newRow("File.fileName")
            << QString(QDocumentGallery::File)
            << QString(QDocumentGallery::fileName)
#if defined(Q_OS_UNIX) && !defined(QT_NO_DBUS) || defined (Q_OS_SYMBIAN)
            << (QGalleryProperty::CanRead
                    | QGalleryProperty::CanFilter
                    | QGalleryProperty::CanSort);
#else
            << QGalleryProperty::Attributes();
#endif
    QTest::newRow("File.filePath")
                    << QString(QDocumentGallery::File)
                    << QString(QDocumentGallery::filePath)
#if defined(Q_OS_UNIX) && !defined(QT_NO_DBUS) || defined (Q_OS_SYMBIAN)
                    << (QGalleryProperty::CanRead | QGalleryProperty::CanFilter);
#else
                    << QGalleryProperty::Attributes();
#endif

    QTest::newRow("Audio.albumTitle")
            << QString(QDocumentGallery::Audio)
            << QString(QDocumentGallery::albumTitle)
#if defined(Q_OS_UNIX) && !defined(QT_NO_DBUS) || defined (Q_OS_SYMBIAN)
            << (QGalleryProperty::CanRead
#if !defined(Q_WS_MAEMO_6)
                    | QGalleryProperty::CanWrite
#endif
                    | QGalleryProperty::CanFilter
                    | QGalleryProperty::CanSort);
#else
            << QGalleryProperty::Attributes();
#endif
    QTest::newRow("Album.duration")
            << QString(QDocumentGallery::Album)
            << QString(QDocumentGallery::duration)
#if defined(Q_WS_MAEMO_6)
            << (QGalleryProperty::CanRead
                    | QGalleryProperty::CanFilter
                    | QGalleryProperty::CanSort);
#elif defined(Q_OS_UNIX) && !defined(QT_NO_DBUS)
            << QGalleryProperty::Attributes(QGalleryProperty::CanRead);
#else
            << QGalleryProperty::Attributes();
#endif
}

void tst_QDocumentGallery::propertyAttributes()
{
    QFETCH(QString, itemType);
    QFETCH(QString, propertyName);
    QFETCH(QGalleryProperty::Attributes, propertyAttributes);

    QCOMPARE(int(gallery.propertyAttributes(propertyName, itemType)), int(propertyAttributes));
}

#include "tst_qdocumentgallery.moc"

QTEST_MAIN(tst_QDocumentGallery)
<commit_msg>Fix test failure.<commit_after>/****************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the Qt Mobility Components.
**
** $QT_BEGIN_LICENSE:LGPL$
** No Commercial Usage
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights.  These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
**
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/

//TESTED_COMPONENT=src/documentgallery

#include <qdocumentgallery.h>

#include <QtTest/QtTest>

QTM_USE_NAMESPACE

Q_DECLARE_METATYPE(QGalleryProperty::Attributes)

class tst_QDocumentGallery : public QObject
{
    Q_OBJECT
private Q_SLOTS:
    void isRequestSupported();
    void itemTypeProperties_data();
    void itemTypeProperties();
    void propertyAttributes_data();
    void propertyAttributes();

private:
    QDocumentGallery gallery;
};

void tst_QDocumentGallery::isRequestSupported()
{
#if defined(Q_OS_UNIX) && !defined(QT_NO_DBUS) || defined (Q_OS_SYMBIAN)
    const bool platformSupported = true;
#else
    const bool platformSupported = false;
#endif

    QCOMPARE(gallery.isRequestSupported(QGalleryAbstractRequest::QueryRequest), platformSupported);
    QCOMPARE(gallery.isRequestSupported(QGalleryAbstractRequest::ItemRequest), platformSupported);
    QCOMPARE(gallery.isRequestSupported(QGalleryAbstractRequest::TypeRequest), platformSupported);
    QCOMPARE(gallery.isRequestSupported(QGalleryAbstractRequest::RequestType(1000)), false);
}

void tst_QDocumentGallery::itemTypeProperties_data()
{
    QTest::addColumn<QString>("itemType");
    QTest::addColumn<QStringList>("propertyNames");

    QTest::newRow("null item type") << QString() << QStringList();
    QTest::newRow("non-existent item type") << QString::fromLatin1("Hello") << QStringList();

    const QStringList fileProperties = QStringList()
#if defined(Q_WS_MAEMO_6)
            << QDocumentGallery::author
            << QDocumentGallery::comments
            << QDocumentGallery::copyright
            << QDocumentGallery::description
            << QDocumentGallery::fileExtension
            << QDocumentGallery::fileName
            << QDocumentGallery::filePath
            << QDocumentGallery::fileSize
            << QDocumentGallery::keywords
            << QDocumentGallery::language
            << QDocumentGallery::lastAccessed
            << QDocumentGallery::lastModified
            << QDocumentGallery::mimeType
            << QDocumentGallery::path
            << QDocumentGallery::rating
            << QDocumentGallery::subject
            << QDocumentGallery::title
            << QDocumentGallery::url;
#elif defined(Q_OS_UNIX) && !defined(QT_NO_DBUS)
            << QDocumentGallery::copyright
            << QDocumentGallery::fileName
            << QDocumentGallery::path
            << QDocumentGallery::filePath
            << QDocumentGallery::url
            << QDocumentGallery::fileSize
            << QDocumentGallery::language
            << QDocumentGallery::lastAccessed
            << QDocumentGallery::lastModified
            << QDocumentGallery::mimeType;
#elif defined (Q_OS_SYMBIAN)
            << QDocumentGallery::url
            << QDocumentGallery::fileName
            << QDocumentGallery::filePath
            << QDocumentGallery::fileSize
            << QDocumentGallery::lastModified
            << QDocumentGallery::title
            << QDocumentGallery::mimeType
            << QDocumentGallery::author
            << QDocumentGallery::copyright
            << QDocumentGallery::description
            << QDocumentGallery::comments
            << QDocumentGallery::rating
#endif
            ;
    QTest::newRow("File") << QString(QDocumentGallery::File) << (QStringList(fileProperties)
#if !defined(Q_WS_MAEMO_6) && defined(Q_OS_UNIX) && !defined(QT_NO_DBUS)
            << QDocumentGallery::author
            << QDocumentGallery::description
            << QDocumentGallery::keywords
            << QDocumentGallery::rating
            << QDocumentGallery::subject
            << QDocumentGallery::title
#endif
        );

    QTest::newRow("Audio") << QString(QDocumentGallery::Audio) << (QStringList(fileProperties)
#if defined(Q_OS_UNIX) && !defined(QT_NO_DBUS)
            << QDocumentGallery::albumArtist
            << QDocumentGallery::albumTitle
            << QDocumentGallery::artist
            << QDocumentGallery::audioBitRate
            << QDocumentGallery::audioCodec
            << QDocumentGallery::channelCount
            << QDocumentGallery::discNumber
            << QDocumentGallery::duration
            << QDocumentGallery::genre
            << QDocumentGallery::lastPlayed
            << QDocumentGallery::lyrics
            << QDocumentGallery::playCount
            << QDocumentGallery::sampleRate
            << QDocumentGallery::trackNumber
            << QDocumentGallery::performer
#if defined(Q_WS_MAEMO_6)
            << QDocumentGallery::composer
            << QDocumentGallery::resumePosition
#else
            << QDocumentGallery::description
            << QDocumentGallery::title
#endif
#elif defined (Q_OS_SYMBIAN)
            << QDocumentGallery::duration
            << QDocumentGallery::performer
            << QDocumentGallery::audioCodec
            << QDocumentGallery::audioBitRate
            << QDocumentGallery::playCount
            << QDocumentGallery::sampleRate
            << QDocumentGallery::albumTitle
            << QDocumentGallery::trackNumber
            << QDocumentGallery::albumArtist
            << QDocumentGallery::artist
            << QDocumentGallery::composer
            << QDocumentGallery::genre
#endif
    );

    QTest::newRow("Album") << QString(QDocumentGallery::Album) << (QStringList()
#if defined(Q_OS_UNIX) && !defined(QT_NO_DBUS)
            << QDocumentGallery::albumArtist
            << QDocumentGallery::albumTitle
            << QDocumentGallery::artist
            << QDocumentGallery::duration
            << QDocumentGallery::title
            << QDocumentGallery::trackCount
#endif
    );
    QTest::newRow("PhotoAlbum") << QString(QDocumentGallery::PhotoAlbum) << (QStringList()
#if defined(Q_OS_UNIX) && !defined(QT_NO_DBUS)
                << QDocumentGallery::title
#if defined (Q_WS_MAEMO_6)
                << QDocumentGallery::count
#else
                << QDocumentGallery::trackCount
#endif
#elif defined (Q_OS_SYMBIAN)
                << QDocumentGallery::url
                << QDocumentGallery::fileSize
                << QDocumentGallery::lastModified
                << QDocumentGallery::title
                << QDocumentGallery::mimeType
#endif
    );

#if defined (Q_OS_SYMBIAN)
    QTest::newRow("Image") << QString(QDocumentGallery::Image) << (QStringList(fileProperties)
            << QDocumentGallery::duration
            << QDocumentGallery::performer
            << QDocumentGallery::playCount
            << QDocumentGallery::width
            << QDocumentGallery::height
            << QDocumentGallery::orientation
            << QDocumentGallery::dateTaken
            << QDocumentGallery::cameraManufacturer
            << QDocumentGallery::cameraModel
            << QDocumentGallery::exposureProgram
            << QDocumentGallery::exposureTime
            << QDocumentGallery::fNumber
            << QDocumentGallery::flashEnabled
            << QDocumentGallery::focalLength
            << QDocumentGallery::meteringMode
            << QDocumentGallery::whiteBalance
    );
    QTest::newRow("Video") << QString(QDocumentGallery::Video) << (QStringList(fileProperties)
            << QDocumentGallery::duration
            << QDocumentGallery::performer
            << QDocumentGallery::videoBitRate
            << QDocumentGallery::playCount
            << QDocumentGallery::width
            << QDocumentGallery::height
            << QDocumentGallery::language
            << QDocumentGallery::frameRate
            << QDocumentGallery::resumePosition
    );
#endif

}

void tst_QDocumentGallery::itemTypeProperties()
{
    QFETCH(QString, itemType);
    QFETCH(QStringList, propertyNames);

    QStringList galleryPropertyNames = gallery.itemTypePropertyNames(itemType);
    propertyNames.sort();
    galleryPropertyNames.sort();

    QCOMPARE(galleryPropertyNames, propertyNames);
}

void tst_QDocumentGallery::propertyAttributes_data()
{
    QTest::addColumn<QString>("itemType");
    QTest::addColumn<QString>("propertyName");
    QTest::addColumn<QGalleryProperty::Attributes>("propertyAttributes");

    QTest::newRow("Null itemType, propertyName")
            << QString()
            << QString()
            << QGalleryProperty::Attributes();
    QTest::newRow("Null itemType, invalid propertyName")
            << QString()
            << QString::fromLatin1("Goodbye")
            << QGalleryProperty::Attributes();
    QTest::newRow("Null itemType, valid propertyName")
            << QString()
            << QString(QDocumentGallery::fileName)
            << QGalleryProperty::Attributes();
    QTest::newRow("Invalid itemType, invalid propertyName")
            << QString::fromLatin1("Hello")
            << QString::fromLatin1("Goodbye")
            << QGalleryProperty::Attributes();
    QTest::newRow("Invalid itemType, valid propertyName")
            << QString::fromLatin1("Hello")
            << QString(QDocumentGallery::fileName)
            << QGalleryProperty::Attributes();
    QTest::newRow("Valid itemType, invalid propertyName")
            << QString(QDocumentGallery::File)
            << QString::fromLatin1("Goodbye")
            << QGalleryProperty::Attributes();
    QTest::newRow("File.fileName")
            << QString(QDocumentGallery::File)
            << QString(QDocumentGallery::fileName)
#if defined(Q_OS_UNIX) && !defined(QT_NO_DBUS) || defined (Q_OS_SYMBIAN)
            << (QGalleryProperty::CanRead
                    | QGalleryProperty::CanFilter
                    | QGalleryProperty::CanSort);
#else
            << QGalleryProperty::Attributes();
#endif
    QTest::newRow("File.filePath")
                    << QString(QDocumentGallery::File)
                    << QString(QDocumentGallery::filePath)
#if defined(Q_OS_UNIX) && !defined(QT_NO_DBUS) || defined (Q_OS_SYMBIAN)
                    << (QGalleryProperty::CanRead | QGalleryProperty::CanFilter);
#else
                    << QGalleryProperty::Attributes();
#endif

    QTest::newRow("Audio.albumTitle")
            << QString(QDocumentGallery::Audio)
            << QString(QDocumentGallery::albumTitle)
#if defined(Q_OS_UNIX) && !defined(QT_NO_DBUS) || defined (Q_OS_SYMBIAN)
            << (QGalleryProperty::CanRead
#if !defined(Q_WS_MAEMO_6)
                    | QGalleryProperty::CanWrite
#endif
                    | QGalleryProperty::CanFilter
                    | QGalleryProperty::CanSort);
#else
            << QGalleryProperty::Attributes();
#endif
    QTest::newRow("Album.duration")
            << QString(QDocumentGallery::Album)
            << QString(QDocumentGallery::duration)
#if defined(Q_WS_MAEMO_6)
            << (QGalleryProperty::CanRead
                    | QGalleryProperty::CanFilter
                    | QGalleryProperty::CanSort);
#elif defined(Q_OS_UNIX) && !defined(QT_NO_DBUS)
            << QGalleryProperty::Attributes(QGalleryProperty::CanRead);
#else
            << QGalleryProperty::Attributes();
#endif
}

void tst_QDocumentGallery::propertyAttributes()
{
    QFETCH(QString, itemType);
    QFETCH(QString, propertyName);
    QFETCH(QGalleryProperty::Attributes, propertyAttributes);

    QCOMPARE(int(gallery.propertyAttributes(propertyName, itemType)), int(propertyAttributes));
}

#include "tst_qdocumentgallery.moc"

QTEST_MAIN(tst_QDocumentGallery)
<|endoftext|>


<commit_before>#include "file.hh"

#include "buffer.hh"
#include "buffer_manager.hh"
#include "assert.hh"

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

namespace Kakoune
{

std::string read_file(const std::string& filename)
{
   int fd = open(filename.c_str(), O_RDONLY);
    if (fd == -1)
    {
        if (errno == ENOENT)
            throw file_not_found(filename);

        throw file_access_error(filename, strerror(errno));
    }

    std::string content;
    char buf[256];
    while (true)
    {
        ssize_t size = read(fd, buf, 256);
        if (size == -1 or size == 0)
            break;

        content += std::string(buf, size);
    }
    close(fd);
    return content;
}

Buffer* create_buffer_from_file(const std::string& filename)
{
    std::string content = read_file(filename);

    if (Buffer* buffer = BufferManager::instance().get_buffer(filename))
        BufferManager::instance().delete_buffer(buffer);

    return new Buffer(filename, Buffer::Type::File, content);
}

void write_buffer_to_file(const Buffer& buffer, const std::string& filename)
{
    int fd = open(filename.c_str(), O_CREAT | O_WRONLY | O_TRUNC, 0644);
    if (fd == -1)
        throw file_access_error(filename, strerror(errno));

    const BufferString& content = buffer.content();
    ssize_t count = content.length() * sizeof(BufferChar);
    const char* ptr = content.c_str();

    while (count)
    {
        ssize_t written = write(fd, ptr, count);
        ptr += written;
        count -= written;

        if (written == -1)
            throw file_access_error(filename, strerror(errno));
    }
    close(fd);
}

}
<commit_msg>whitespace fix<commit_after>#include "file.hh"

#include "buffer.hh"
#include "buffer_manager.hh"
#include "assert.hh"

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

namespace Kakoune
{

std::string read_file(const std::string& filename)
{
   int fd = open(filename.c_str(), O_RDONLY);
    if (fd == -1)
    {
        if (errno == ENOENT)
            throw file_not_found(filename);

        throw file_access_error(filename, strerror(errno));
    }

    std::string content;
    char buf[256];
    while (true)
    {
        ssize_t size = read(fd, buf, 256);
        if (size == -1 or size == 0)
            break;

        content += std::string(buf, size);
    }
    close(fd);
    return content;
}

Buffer* create_buffer_from_file(const std::string& filename)
{
    std::string content = read_file(filename);

    if (Buffer* buffer = BufferManager::instance().get_buffer(filename))
        BufferManager::instance().delete_buffer(buffer);

    return new Buffer(filename, Buffer::Type::File, content);
}

void write_buffer_to_file(const Buffer& buffer, const std::string& filename)
{
    int fd = open(filename.c_str(), O_CREAT | O_WRONLY | O_TRUNC, 0644);
    if (fd == -1)
        throw file_access_error(filename, strerror(errno));

    const BufferString& content = buffer.content();
    ssize_t count = content.length() * sizeof(BufferChar);
    const char* ptr = content.c_str();

    while (count)
    {
        ssize_t written = write(fd, ptr, count);
        ptr += written;
        count -= written;

        if (written == -1)
            throw file_access_error(filename, strerror(errno));
    }
    close(fd);
}

}
<|endoftext|>