<commit_before>/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the Qt Designer of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia.  For licensing terms and
** conditions see http://qt.digia.com/licensing.  For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** 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, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met: http://www.gnu.org/copyleft/gpl.html.
**
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include "qaxwidgetpropertysheet.h"
#include "qdesigneraxwidget.h"

#include <QtDesigner/QDesignerMemberSheetExtension>
#include <QtDesigner/QDesignerFormWindowInterface>
#include <QtDesigner/QDesignerFormEditorInterface>
#include <QtDesigner/QDesignerPropertyEditorInterface>

#include <QtDesigner/QExtensionManager>
#include <QtCore/QDebug>
#include <QtCore/QTimer>

static const char *geometryPropertyC = "geometry";

QT_BEGIN_NAMESPACE

const char *QAxWidgetPropertySheet::controlPropertyName = "control";

QAxWidgetPropertySheet::QAxWidgetPropertySheet(QDesignerAxWidget *object, QObject *parent) :
    QDesignerPropertySheet(object, parent),
    m_controlProperty(controlPropertyName),
    m_propertyGroup(QStringLiteral("QAxWidget"))
{
     if (!axWidget()->loaded()) { // For some obscure reason....
        const int controlIndex = QDesignerPropertySheet::indexOf(m_controlProperty);
        setPropertyGroup(controlIndex, m_propertyGroup);
    }
}

bool QAxWidgetPropertySheet::isEnabled(int index) const
{
    if (propertyName(index) == m_controlProperty)
        return false;
    return QDesignerPropertySheet::isEnabled(index);
}

bool QAxWidgetPropertySheet::dynamicPropertiesAllowed() const
{
    return false;
}

QDesignerAxWidget *QAxWidgetPropertySheet::axWidget() const
{
    return static_cast<QDesignerAxWidget*>(object());
}

// Reload as the meta object changes.
bool QAxWidgetPropertySheet::reset(int index)
{
    const QString name = propertyName(index);
    QMap<QString, QVariant>::iterator it = m_currentProperties.changedProperties.find(name);
    if (it !=  m_currentProperties.changedProperties.end())
        m_currentProperties.changedProperties.erase(it);
    if (name != m_controlProperty)
        return QDesignerPropertySheet::reset(index);
    axWidget()->resetControl();
    QTimer::singleShot(0, this, SLOT(updatePropertySheet()));
    return true;
}

void QAxWidgetPropertySheet::setProperty(int index, const QVariant &value)
{

    // take care of all changed properties
    const QString name = propertyName(index);
    m_currentProperties.changedProperties[name] = value;
    if (name != m_controlProperty) {
        QDesignerPropertySheet::setProperty(index, value);
        return;
    }
    // Loading forms: Reload
    if (name == m_controlProperty) {
        const QString clsid = value.toString();
        if (clsid.isEmpty() || !axWidget()->loadControl(clsid))
            reset(index);
        else
            QTimer::singleShot(100, this, SLOT(updatePropertySheet()));
    }
}

int QAxWidgetPropertySheet::indexOf(const QString &name) const
{
    const int index = QDesignerPropertySheet::indexOf(name);
    if (index != -1)
        return index;
    // Loading before recreation of sheet in timer slot: Add a fake property to store the value
    const QVariant dummValue(0);
    QAxWidgetPropertySheet *that = const_cast<QAxWidgetPropertySheet *>(this);
    const int newIndex = that->createFakeProperty(name, dummValue);
    that->setPropertyGroup(newIndex, m_propertyGroup);
    return newIndex;
}

void QAxWidgetPropertySheet::updatePropertySheet()
{
    // refresh the property sheet (we are deleting m_currentProperties)
    struct SavedProperties tmp = m_currentProperties;
    QDesignerAxWidget *axw = axWidget();
    QDesignerFormWindowInterface *formWin = QDesignerFormWindowInterface::findFormWindow(axw);
    Q_ASSERT(formWin != 0);
    tmp.widget = axw;
    tmp.clsid = axw->control();
    // Delete the sheets as they cache the meta object and other information
    delete this;
    delete qt_extension<QDesignerMemberSheetExtension *>(formWin->core()->extensionManager(), axw);
    reloadPropertySheet(tmp, formWin);
}

void QAxWidgetPropertySheet::reloadPropertySheet(const struct SavedProperties &properties, QDesignerFormWindowInterface *formWin)
{
    QDesignerFormEditorInterface *core = formWin->core();
    //Recreation of the property sheet
    QDesignerPropertySheetExtension *sheet = qt_extension<QDesignerPropertySheetExtension *>(core->extensionManager(), properties.widget);

    bool foundGeometry = false;
    const QString geometryProperty = QLatin1String(geometryPropertyC);
    const SavedProperties::NamePropertyMap::const_iterator cend = properties.changedProperties.constEnd();
    for (SavedProperties::NamePropertyMap::const_iterator i = properties.changedProperties.constBegin(); i != cend; ++i) {
        const QString name = i.key();
        const int index = sheet->indexOf(name);
        if (index == -1)
            continue;
        // filter out geometry as this will resize the control
        // to is default size even if it is attached to an layout
        // but set the changed flag to work around preview bug...
        if (name == geometryProperty) {
            sheet->setChanged(index, true);
            foundGeometry = true;
            continue;
        }
        if (name == QLatin1String(controlPropertyName))	 {
            sheet->setChanged(index, !i.value().toString().isEmpty());
            continue;
        }
        sheet->setChanged(index, true);
        sheet->setProperty(index, i.value());
    }

    if (!foundGeometry) // Make sure geometry is always changed in Designer
        sheet->setChanged(sheet->indexOf(geometryProperty), true);

    if (core->propertyEditor()->object() == properties.widget) {
        formWin->clearSelection(true);
        formWin->selectWidget(properties.widget);
    }
}

QT_END_NAMESPACE
<commit_msg>ActiveQt Designer plugin: Load control string correctly.<commit_after>/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the Qt Designer of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia.  For licensing terms and
** conditions see http://qt.digia.com/licensing.  For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** 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, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met: http://www.gnu.org/copyleft/gpl.html.
**
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include "qaxwidgetpropertysheet.h"
#include "qdesigneraxwidget.h"

#include <QtDesigner/QDesignerMemberSheetExtension>
#include <QtDesigner/QDesignerFormWindowInterface>
#include <QtDesigner/QDesignerFormEditorInterface>
#include <QtDesigner/QDesignerPropertyEditorInterface>

#include <QtDesigner/QExtensionManager>
#include <private/qdesigner_utils_p.h>
#include <QtCore/QDebug>
#include <QtCore/QTimer>

static const char *geometryPropertyC = "geometry";

QT_BEGIN_NAMESPACE

const char *QAxWidgetPropertySheet::controlPropertyName = "control";

QAxWidgetPropertySheet::QAxWidgetPropertySheet(QDesignerAxWidget *object, QObject *parent) :
    QDesignerPropertySheet(object, parent),
    m_controlProperty(controlPropertyName),
    m_propertyGroup(QStringLiteral("QAxWidget"))
{
     if (!axWidget()->loaded()) { // For some obscure reason....
        const int controlIndex = QDesignerPropertySheet::indexOf(m_controlProperty);
        setPropertyGroup(controlIndex, m_propertyGroup);
    }
}

bool QAxWidgetPropertySheet::isEnabled(int index) const
{
    if (propertyName(index) == m_controlProperty)
        return false;
    return QDesignerPropertySheet::isEnabled(index);
}

bool QAxWidgetPropertySheet::dynamicPropertiesAllowed() const
{
    return false;
}

QDesignerAxWidget *QAxWidgetPropertySheet::axWidget() const
{
    return static_cast<QDesignerAxWidget*>(object());
}

// Reload as the meta object changes.
bool QAxWidgetPropertySheet::reset(int index)
{
    const QString name = propertyName(index);
    QMap<QString, QVariant>::iterator it = m_currentProperties.changedProperties.find(name);
    if (it !=  m_currentProperties.changedProperties.end())
        m_currentProperties.changedProperties.erase(it);
    if (name != m_controlProperty)
        return QDesignerPropertySheet::reset(index);
    axWidget()->resetControl();
    QTimer::singleShot(0, this, SLOT(updatePropertySheet()));
    return true;
}

void QAxWidgetPropertySheet::setProperty(int index, const QVariant &value)
{

    // take care of all changed properties
    const QString name = propertyName(index);
    m_currentProperties.changedProperties[name] = value;
    if (name != m_controlProperty) {
        QDesignerPropertySheet::setProperty(index, value);
        return;
    }
    // Loading forms: Reload
    if (name == m_controlProperty) {
        const qdesigner_internal::PropertySheetStringValue sv = qvariant_cast<qdesigner_internal::PropertySheetStringValue>(value);
        const QString clsid = sv.value();
        if (clsid.isEmpty() || !axWidget()->loadControl(clsid))
            reset(index);
        else
            QTimer::singleShot(100, this, SLOT(updatePropertySheet()));
    }
}

int QAxWidgetPropertySheet::indexOf(const QString &name) const
{
    const int index = QDesignerPropertySheet::indexOf(name);
    if (index != -1)
        return index;
    // Loading before recreation of sheet in timer slot: Add a fake property to store the value
    const QVariant dummValue(0);
    QAxWidgetPropertySheet *that = const_cast<QAxWidgetPropertySheet *>(this);
    const int newIndex = that->createFakeProperty(name, dummValue);
    that->setPropertyGroup(newIndex, m_propertyGroup);
    return newIndex;
}

void QAxWidgetPropertySheet::updatePropertySheet()
{
    // refresh the property sheet (we are deleting m_currentProperties)
    struct SavedProperties tmp = m_currentProperties;
    QDesignerAxWidget *axw = axWidget();
    QDesignerFormWindowInterface *formWin = QDesignerFormWindowInterface::findFormWindow(axw);
    Q_ASSERT(formWin != 0);
    tmp.widget = axw;
    tmp.clsid = axw->control();
    // Delete the sheets as they cache the meta object and other information
    delete this;
    delete qt_extension<QDesignerMemberSheetExtension *>(formWin->core()->extensionManager(), axw);
    reloadPropertySheet(tmp, formWin);
}

void QAxWidgetPropertySheet::reloadPropertySheet(const struct SavedProperties &properties, QDesignerFormWindowInterface *formWin)
{
    QDesignerFormEditorInterface *core = formWin->core();
    //Recreation of the property sheet
    QDesignerPropertySheetExtension *sheet = qt_extension<QDesignerPropertySheetExtension *>(core->extensionManager(), properties.widget);

    bool foundGeometry = false;
    const QString geometryProperty = QLatin1String(geometryPropertyC);
    const SavedProperties::NamePropertyMap::const_iterator cend = properties.changedProperties.constEnd();
    for (SavedProperties::NamePropertyMap::const_iterator i = properties.changedProperties.constBegin(); i != cend; ++i) {
        const QString name = i.key();
        const int index = sheet->indexOf(name);
        if (index == -1)
            continue;
        // filter out geometry as this will resize the control
        // to is default size even if it is attached to an layout
        // but set the changed flag to work around preview bug...
        if (name == geometryProperty) {
            sheet->setChanged(index, true);
            foundGeometry = true;
            continue;
        }
        if (name == QLatin1String(controlPropertyName))	 {
            sheet->setChanged(index, !i.value().toString().isEmpty());
            continue;
        }
        sheet->setChanged(index, true);
        sheet->setProperty(index, i.value());
    }

    if (!foundGeometry) // Make sure geometry is always changed in Designer
        sheet->setChanged(sheet->indexOf(geometryProperty), true);

    if (core->propertyEditor()->object() == properties.widget) {
        formWin->clearSelection(true);
        formWin->selectWidget(properties.widget);
    }
}

QT_END_NAMESPACE
<|endoftext|>


<commit_before>#ifndef _SDD_HOM_EVALUATION_HH_
#define _SDD_HOM_EVALUATION_HH_

#include <cassert>
#include <iosfwd>

#include "sdd/hom/context_fwd.hh"
#include "sdd/hom/rewrite.hh"
#include "sdd/order/order.hh"

namespace sdd { namespace hom {

/*------------------------------------------------------------------------------------------------*/

/// @internal
/// @brief Evaluate an homomorphism.
template <typename C>
struct evaluation
{
  /// @brief Used by util::variant.
  typedef SDD<C> result_type;

  /// @brief Terminal |0| case.
  ///
  /// It shall never be called as the |0| case is handled by the evaluation of
  /// homomorphism::operator().
  template <typename H>
  SDD<C>
  operator()( const H& h, const zero_terminal<C>&
            , const SDD<C>&, context<C>&, const order<C>& o, const homomorphism<C>&)
  const noexcept
  {
    assert(false);
    __builtin_unreachable();
  }

  /// @brief Terminal |1| case.
  template <typename H>
  SDD<C>
  operator()( const H& h, const one_terminal<C>&
            , const SDD<C>& x, context<C>& cxt, const order<C>& o
            , const homomorphism<C>&)
  const
  {
    return h(cxt, o, x);
  }

  /// @brief Dispatch evaluation to the concrete homomorphism.
  ///
  /// Implement a part of the automatic saturation: evaluation is propagated on successors
  /// whenever possible.
  template <typename H, typename Node>
  SDD<C>
  operator()( const H& h, const Node& node
            , const SDD<C>& x, context<C>& cxt, const order<C>& o
            , const homomorphism<C>& hom_proxy)
  const
  {
    assert(not o.empty() && "Empty order.");
    assert(o.variable() == node.variable() && "Different variables in order and SDD.");

    if (h.skip(o))
    {
      // The evaluated homomorphism skips the current level. We can thus forward its application
      // to the following levels.
      dd::square_union<C, typename Node::valuation_type> su;
      su.reserve(node.size());
      for (const auto& arc : node)
      {
        SDD<C> new_successor = hom_proxy(cxt, o.next(), arc.successor());
        if (not new_successor.empty())
        {
          su.add(new_successor, arc.valuation());
        }
      }
      return {node.variable(), su(cxt.sdd_context())};
    }
    else
    {
      return h(cxt, o, x);
    }
  }
};

/*------------------------------------------------------------------------------------------------*/

/// @internal
/// @brief Default traits for homomorphisms.
template <typename T>
struct homomorphism_traits
{
  static constexpr bool should_cache = true;
};

/*------------------------------------------------------------------------------------------------*/

/// @internal
/// @brief The evaluation of an homomorphism in the cache.
template <typename C>
struct cached_homomorphism
{
  /// @brief Needed by the cache.
  typedef SDD<C> result_type;

  /// @brief The current order position.
  const order<C>& ord;

  /// @brief The homomorphism to evaluate.
  const homomorphism<C> hom;

  /// @brief The homomorphism's operand.
  const SDD<C> sdd;

  /// @brief Constructor.
  cached_homomorphism(const order<C>& o, const homomorphism<C>& h, const SDD<C>& s)
    : ord(o)
    , hom(h)
    , sdd(s)
  {
  }

  /// @brief Launch the evaluation.
  SDD<C>
  operator()(context<C>& cxt)
  const
  {
    return apply_binary_visitor(evaluation<C>(), hom->data(), sdd->data(), sdd, cxt, ord, hom);
  }
};

/*------------------------------------------------------------------------------------------------*/

/// @internal
/// @related cached_homomorphism
/// We don't need to compare orders as the SDD operands bring the same information.
template <typename C>
inline
bool
operator==(const cached_homomorphism<C>& lhs, const cached_homomorphism<C>& rhs)
noexcept
{
  return lhs.hom == rhs.hom and lhs.sdd == rhs.sdd;
}

/// @internal
/// @related cached_homomorphism
template <typename C>
std::ostream&
operator<<(std::ostream& os, const cached_homomorphism<C>& ch)
{
  return os << ch.hom;
}

/*------------------------------------------------------------------------------------------------*/

/// @internal
/// @brief Used by the cache as a filter to know if an homomorphism should be cached.
template <typename C>
struct should_cache
{
  /// @brief Needed by variant.
  typedef bool result_type;

  /// @brief Dispatch to each homomorphism's trait.
  template <typename T>
  constexpr bool
  operator()(const T&)
  const noexcept
  {
    return homomorphism_traits<T>::should_cache;
  }

  /// @brief Application of should_cache.
  bool
  operator()(const cached_homomorphism<C>& ch)
  const noexcept
  {
    return apply_visitor(*this, ch.hom->data());
  }
};

/*------------------------------------------------------------------------------------------------*/

}} // namespace sdd::hom

namespace std {

/*------------------------------------------------------------------------------------------------*/

/// @internal
/// @brief Hash specialization for sdd::hom::cached_homomorphism
///
/// We don't need to hash order as the SDD operand brings the same information.
template <typename C>
struct hash<sdd::hom::cached_homomorphism<C>>
{
  std::size_t
  operator()(const sdd::hom::cached_homomorphism<C>& ch)
  const noexcept
  {
    std::size_t seed = 0;
    sdd::util::hash_combine(seed, ch.hom);
    sdd::util::hash_combine(seed, ch.sdd);
    return seed;
  }
};

/*------------------------------------------------------------------------------------------------*/

} // namespace std

#endif // _SDD_HOM_EVALUATION_HH_
<commit_msg>Copy order in cached_homomorphism.<commit_after>#ifndef _SDD_HOM_EVALUATION_HH_
#define _SDD_HOM_EVALUATION_HH_

#include <cassert>
#include <iosfwd>

#include "sdd/hom/context_fwd.hh"
#include "sdd/hom/rewrite.hh"
#include "sdd/order/order.hh"

namespace sdd { namespace hom {

/*------------------------------------------------------------------------------------------------*/

/// @internal
/// @brief Evaluate an homomorphism.
template <typename C>
struct evaluation
{
  /// @brief Used by util::variant.
  typedef SDD<C> result_type;

  /// @brief Terminal |0| case.
  ///
  /// It shall never be called as the |0| case is handled by the evaluation of
  /// homomorphism::operator().
  template <typename H>
  SDD<C>
  operator()( const H& h, const zero_terminal<C>&
            , const SDD<C>&, context<C>&, const order<C>& o, const homomorphism<C>&)
  const noexcept
  {
    assert(false);
    __builtin_unreachable();
  }

  /// @brief Terminal |1| case.
  template <typename H>
  SDD<C>
  operator()( const H& h, const one_terminal<C>&
            , const SDD<C>& x, context<C>& cxt, const order<C>& o
            , const homomorphism<C>&)
  const
  {
    return h(cxt, o, x);
  }

  /// @brief Dispatch evaluation to the concrete homomorphism.
  ///
  /// Implement a part of the automatic saturation: evaluation is propagated on successors
  /// whenever possible.
  template <typename H, typename Node>
  SDD<C>
  operator()( const H& h, const Node& node
            , const SDD<C>& x, context<C>& cxt, const order<C>& o
            , const homomorphism<C>& hom_proxy)
  const
  {
    assert(not o.empty() && "Empty order.");
    assert(o.variable() == node.variable() && "Different variables in order and SDD.");

    if (h.skip(o))
    {
      // The evaluated homomorphism skips the current level. We can thus forward its application
      // to the following levels.
      dd::square_union<C, typename Node::valuation_type> su;
      su.reserve(node.size());
      for (const auto& arc : node)
      {
        SDD<C> new_successor = hom_proxy(cxt, o.next(), arc.successor());
        if (not new_successor.empty())
        {
          su.add(new_successor, arc.valuation());
        }
      }
      return {node.variable(), su(cxt.sdd_context())};
    }
    else
    {
      return h(cxt, o, x);
    }
  }
};

/*------------------------------------------------------------------------------------------------*/

/// @internal
/// @brief Default traits for homomorphisms.
template <typename T>
struct homomorphism_traits
{
  static constexpr bool should_cache = true;
};

/*------------------------------------------------------------------------------------------------*/

/// @internal
/// @brief The evaluation of an homomorphism in the cache.
template <typename C>
struct cached_homomorphism
{
  /// @brief Needed by the cache.
  typedef SDD<C> result_type;

  /// @brief The current order position.
  const order<C> ord;

  /// @brief The homomorphism to evaluate.
  const homomorphism<C> hom;

  /// @brief The homomorphism's operand.
  const SDD<C> sdd;

  /// @brief Constructor.
  cached_homomorphism(const order<C>& o, const homomorphism<C>& h, const SDD<C>& s)
    : ord(o)
    , hom(h)
    , sdd(s)
  {
  }

  /// @brief Launch the evaluation.
  SDD<C>
  operator()(context<C>& cxt)
  const
  {
    return apply_binary_visitor(evaluation<C>(), hom->data(), sdd->data(), sdd, cxt, ord, hom);
  }
};

/*------------------------------------------------------------------------------------------------*/

/// @internal
/// @related cached_homomorphism
/// We don't need to compare orders as the SDD operands bring the same information.
template <typename C>
inline
bool
operator==(const cached_homomorphism<C>& lhs, const cached_homomorphism<C>& rhs)
noexcept
{
  return lhs.hom == rhs.hom and lhs.sdd == rhs.sdd;
}

/// @internal
/// @related cached_homomorphism
template <typename C>
std::ostream&
operator<<(std::ostream& os, const cached_homomorphism<C>& ch)
{
  return os << ch.hom;
}

/*------------------------------------------------------------------------------------------------*/

/// @internal
/// @brief Used by the cache as a filter to know if an homomorphism should be cached.
template <typename C>
struct should_cache
{
  /// @brief Needed by variant.
  typedef bool result_type;

  /// @brief Dispatch to each homomorphism's trait.
  template <typename T>
  constexpr bool
  operator()(const T&)
  const noexcept
  {
    return homomorphism_traits<T>::should_cache;
  }

  /// @brief Application of should_cache.
  bool
  operator()(const cached_homomorphism<C>& ch)
  const noexcept
  {
    return apply_visitor(*this, ch.hom->data());
  }
};

/*------------------------------------------------------------------------------------------------*/

}} // namespace sdd::hom

namespace std {

/*------------------------------------------------------------------------------------------------*/

/// @internal
/// @brief Hash specialization for sdd::hom::cached_homomorphism
///
/// We don't need to hash order as the SDD operand brings the same information.
template <typename C>
struct hash<sdd::hom::cached_homomorphism<C>>
{
  std::size_t
  operator()(const sdd::hom::cached_homomorphism<C>& ch)
  const noexcept
  {
    std::size_t seed = 0;
    sdd::util::hash_combine(seed, ch.hom);
    sdd::util::hash_combine(seed, ch.sdd);
    return seed;
  }
};

/*------------------------------------------------------------------------------------------------*/

} // namespace std

#endif // _SDD_HOM_EVALUATION_HH_
<|endoftext|>


<commit_before>// This file is part of MLDB. Copyright 2015 Datacratic. All rights reserved.

/** sql.cc
    Jeremy Barnes, 24 December 2014
    Copyright (c) 2014 Datacratic Inc.  All rights reserved.

    sqlite plugin.
*/

#include "mldb/server/plugin_collection.h"
#include "mldb/server/dataset_collection.h"
#include "mldb/server/mldb_server.h"
#include "mldb/recoset_sqlite/sqlite3.h"
#include "mldb/recoset_sqlite/sqlite3pp.h"
#include "mldb/arch/timers.h"

using namespace std;

namespace MLDB {

void dumpQuery(sqlite3pp::database & db, const std::string & queryStr)
{
    cerr << "dumping query " << queryStr << endl;

    sqlite3pp::query query(db, queryStr.c_str());

    Json::Value allRecords;

    for (sqlite3pp::query::iterator i = query.begin(); i != query.end(); ++i) {
        Json::Value record;
        for (int j = 0; j < query.column_count(); ++j) {
            const char * v = (*i).get<char const*>(j);
            record[query.column_name(j)] = v ? Json::Value(v) : Json::Value();
        }

        allRecords.append(record);
    }

    cerr << allRecords;
}

Json::Value toJson(sqlite3_value * arg)
{
    switch (sqlite3_value_type(arg)) {
    case SQLITE_INTEGER:
        return sqlite3_value_int64(arg);
    case SQLITE_FLOAT:
        return sqlite3_value_double(arg);
    case SQLITE_NULL:
        return Json::Value();
    case SQLITE_TEXT: {
        int len = sqlite3_value_bytes(arg);
        auto bytes = sqlite3_value_text(arg);
        return string(bytes, bytes + len);
    }
    case SQLITE_BLOB:
        return "<<BLOB>>";
    default:
        throw MLDB::Exception("Unknown type for column");
    }
}

CellValue toCell(sqlite3_value * arg)
{
    switch (sqlite3_value_type(arg)) {
    case SQLITE_INTEGER:
        return (int64_t)sqlite3_value_int64(arg);
    case SQLITE_FLOAT:
        return sqlite3_value_double(arg);
    case SQLITE_NULL:
        return CellValue();
    case SQLITE_TEXT: {
        int len = sqlite3_value_bytes(arg);
        auto bytes = sqlite3_value_text(arg);
        return string(bytes, bytes + len);
    }
    default:
        throw MLDB::Exception("Unknown type for column");
    }
}

void dumpQueryArray(sqlite3pp::database & db, const std::string & queryStr)
{
    Timer timer;
    cerr << "dumping query " << queryStr << endl;

    sqlite3pp::query query(db, queryStr.c_str());

    Json::Value allRecords;

    for (int j = 0; j < query.column_count(); ++j) {
        allRecords[0][j] = query.column_name(j);
    }

    for (sqlite3pp::query::iterator i = query.begin(); i != query.end(); ++i) {
        Json::Value record;
        for (int j = 0; j < query.column_count(); ++j) {
            switch ((*i).column_type(j)) {
            case SQLITE_INTEGER:
                record[j] = (*i).get<long long int>(j);
                break;
            case SQLITE_FLOAT:
                record[j] = (*i).get<double>(j);
                break;
            case SQLITE_NULL:
                break;
            case SQLITE_TEXT:
                record[j] = (*i).get<char const*>(j);;
                break;
            case SQLITE_BLOB:
                record[j] = "<<BLOB>>";
                break;
            default:
                throw MLDB::Exception("Unknown type for column");
            }
        }

        allRecords.append(record);
    }
    cerr << "query executed in " << timer.elapsed() << endl;

    cerr << allRecords;
}

void explainQuery(sqlite3pp::database & db, const std::string & queryStr)
{
    dumpQuery(db, "EXPLAIN QUERY PLAN " + queryStr);
}

std::string sqlEscape(const std::string & val)
{
    int numQuotes = 0;
    for (char c: val) {
        if (c == '\'')
            ++numQuotes;
        if (c < ' ' || c >= 127)
            throw MLDB::Exception("Non ASCII character in DB");
    }
    if (numQuotes == 0)
        return val;

    std::string result;
    result.reserve(val.size() + numQuotes);

    for (char c: val) {
        if (c == '\'')
            result += "\'\'";
        else result += c;
    }

    return result;
}

int sqlite3_result_ascii(sqlite3_context* context, const std::string & str)
{
    sqlite3_result_text(context, str.c_str(), str.length(), SQLITE_TRANSIENT);
    return SQLITE_OK;
}

int sqlite3_result_utf8(sqlite3_context* context, const Utf8String & str)
{
    sqlite3_result_text(context, str.rawData(), str.rawLength(), SQLITE_TRANSIENT);
    return SQLITE_OK;
}

namespace {
enum {
    TIMESTAMP_COLUMN = 0,
    ISO_TIMESTAMP_COLUMN = 1,
    ROW_HASH_COLUMN = 2,
    ROW_NAME_COLUMN = 3,
    NUM_FIXED_COLUMNS
};
} // file scope

static __thread MldbServer * currentMldb = nullptr;

struct BehaviourModule {

    struct OurVtab: public sqlite3_vtab {
        OurVtab(MldbServer * mldb)
            : mldb(mldb)
        {
        }

        MldbServer * mldb;
        std::shared_ptr<Dataset> dataset;
        std::vector<ColumnPath> columnNames;
        std::vector<ColumnHash> columnHashes;
    };

    static int Create(sqlite3 * db, void *pAux, int argc, const char * const * argv,
                      sqlite3_vtab ** ppVTab, char ** err)
    {
        try {
            cerr << "creating table" << endl;
            cerr << "argc = " << argc << endl;
            for (unsigned i = 0;  i < argc;  ++i)
                cerr << "  " << argv[i] << endl;

            ExcAssert(currentMldb);
            OurVtab * vtab;
            *ppVTab = vtab = new OurVtab(currentMldb);

            string datasetName = argv[2];

            // Get the dataset

            auto dataset = currentMldb->datasets->getExistingEntity(datasetName);

            vtab->dataset = dataset;

            string columnNameStr;

            vector<ColumnPath> columnNames = dataset->getColumnNames();
            vector<ColumnHash> columnHashes;

            for (auto & columnName : columnNames) {
                columnNameStr += ", '" + sqlEscape(columnName.toString()) + "'";
                columnHashes.emplace_back(columnName);
            }

            vtab->columnNames = columnNames;
            vtab->columnHashes = columnHashes;

            string statement = "CREATE TABLE " + sqlEscape(datasetName) + " ('Timestamp', 'TimestampIso8601', 'RowHash', 'RowName'" + columnNameStr + ")";

            cerr << "running statement " << statement << endl;

            int res = sqlite3_declare_vtab(db, statement.c_str());

            cerr << "res returned " << res << endl;

            cerr << "error " << sqlite3_errmsg(db) << endl;

            return res;
        } catch (const std::exception & exc) {
            *err = (char *)sqlite3_malloc(strlen(exc.what()) + 1);
            strcpy(*err, exc.what());
            return SQLITE_ERROR;
        } MLDB_CATCH_ALL {
            return SQLITE_ERROR;
        }
    }

    static int Connect(sqlite3 * db, void *pAux, int argc, const char * const * argv,
                       sqlite3_vtab ** ppVTab, char **)
    {
        cerr << "connecting to table" << endl;
        return SQLITE_OK;
    }

    static int BestIndex(sqlite3_vtab *pVTab, sqlite3_index_info* info)
    {
        OurVtab * vtab = static_cast<OurVtab *>(pVTab);

        cerr << "BestIndex" << endl;
        cerr << "info.nConstraint = " << info->nConstraint << endl;
        cerr << "info.nOrderBy = " << info->nOrderBy << endl;
        cerr << "info.aConstraintUsage = " << info->aConstraintUsage << endl;

        Json::Value indexPlan;

        for (unsigned i = 0;  i < info->nConstraint;  ++i) {
            auto & constraint = info->aConstraint[i];
            cerr << "constraint " << i
                 << " column " << info->aConstraint[i].iColumn
                 << " op " << (int)info->aConstraint[i].op
                 << " usable " << (int)info->aConstraint[i].usable
                 << endl;

            if (!constraint.usable)
                continue;

            int col = -1;
            if (constraint.iColumn >= NUM_FIXED_COLUMNS) {
                col = constraint.iColumn - NUM_FIXED_COLUMNS;
                cerr << "column is " << vtab->columnNames[col] << endl;
            }

            if (col == -1)
                continue;

            string constraintStr;

            switch (constraint.op) {
            case SQLITE_INDEX_CONSTRAINT_EQ:
                constraintStr = "==";  break;
            case SQLITE_INDEX_CONSTRAINT_GT:
                constraintStr = ">";  break;
            case SQLITE_INDEX_CONSTRAINT_LE:
                constraintStr = "<=";  break;
            case SQLITE_INDEX_CONSTRAINT_LT:
                constraintStr = "<";  break;
            case SQLITE_INDEX_CONSTRAINT_GE:
                constraintStr = ">=";  break;
            case SQLITE_INDEX_CONSTRAINT_MATCH:
                constraintStr = "MATCHES";  break;
            default:
                throw MLDB::Exception("unknown constraint type");
            }

            cerr << "constraintStr = " << constraintStr << endl;

            indexPlan[i]["cIdx"] = col;
            indexPlan[i]["cName"] = vtab->columnNames[col].toString();
            indexPlan[i]["op"] = constraintStr;
            indexPlan[i]["arg"] = i;

            // We can do this!

            //constraintsOut += " " + vtab->columnNames[col].toString() + "," + constraintStr;

            info->aConstraintUsage[i].argvIndex = i + 1;

            // IN clauses can't be optimized without this
            info->aConstraintUsage[i].omit = 1;
        }

        info->idxStr = sqlite3_mprintf("%s", indexPlan.toStringNoNewLine().c_str());
        info->needToFreeIdxStr = true;
        info->estimatedCost = indexPlan.isNull() ? 10000000 : 0;  // TODO: something sensible

        cerr << "bestIndex: plan " << indexPlan.toStringNoNewLine() << " cost "
             << info->estimatedCost << endl;

        return SQLITE_OK;
    }

    static int Disconnect(sqlite3_vtab *pVTab)
    {
        delete static_cast<OurVtab *>(pVTab);
        return SQLITE_OK;
    }

    static int Destroy(sqlite3_vtab *pVTab)
    {
        return Disconnect(pVTab);
    }

    static void Free(void *)
    {
        cerr << "destroying" << endl;
    }

    struct Cursor: public sqlite3_vtab_cursor {
        OurVtab * vtab;
        std::unique_ptr<MatrixEventIterator> it;
        std::map<ColumnHash, CellValue> knownValues;
    };

    static int Open(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor)
    {
        cerr << "open" << endl;
        OurVtab * vtab = static_cast<OurVtab *>(pVTab);
        Cursor * cursor;
        *ppCursor = cursor = new Cursor();

        cursor->it = vtab->dataset->getMatrixView()->allEvents();
        cursor->vtab = vtab;

        return SQLITE_OK;
    }

    static int Close(sqlite3_vtab_cursor* vcursor)
    {
        Cursor * cursor = static_cast<Cursor *>(vcursor);
        delete static_cast<Cursor *>(cursor);
        return SQLITE_OK;
    }

    static int Filter(sqlite3_vtab_cursor* vcursor,
                      int idxNum,
                      const char *idxStr,
                      int argc, sqlite3_value **argv)
    {
        Cursor * cursor = static_cast<Cursor *>(vcursor);
        OurVtab * vtab = cursor->vtab;
        cerr << "filter" << endl;
        cerr << "idxNum = " << idxNum << endl;
        cerr << "idxStr = " << idxStr << endl;
        cerr << "argc = " << argc << endl;

        for (unsigned i = 0;  i < argc;  ++i) {
            cerr << "arg " << argc << " = " << toJson(argv[i]);
        }

        Json::Value plan = Json::parse(idxStr);

        if (plan.isNull()) {
            cursor->it = vtab->dataset->getMatrixView()->allEvents();
            return SQLITE_OK;
        }

        vector<pair<RowHash, Date> > events;

        std::map<ColumnHash, CellValue> knownValues;

        for (unsigned i = 0;  i < plan.size();  ++i) {
            const Json::Value & planVal = plan[i];
            //int colIdx = planVal["cIdx"].asInt();
            ColumnPath colName(planVal["cName"].asString());
            int argNum = planVal["arg"].asInt();
            string op = planVal["op"].asString();
            CellValue arg = toCell(argv[argNum]);
            knownValues[colName] = arg;

            if (op != "==")
                throw MLDB::Exception("non-equality not implemented");

            cerr << "Filtering on " << colName << " " << op << " " << arg
                 << endl;

            auto clauseEvents = vtab->dataset->getColumnIndex()->rowsWithColumnValue(colName, arg);
            std::sort(clauseEvents.begin(), clauseEvents.end());

            if (i == 0)
                events = std::move(clauseEvents);
            else {
                vector<pair<RowHash, Date> > intersectedEvents;
                std::set_intersection(clauseEvents.begin(), clauseEvents.end(),
                                      events.begin(), events.end(),
                                      back_inserter(intersectedEvents));
                events = std::move(intersectedEvents);
            }

            cerr << events.size() << " events matching after clause " << i << endl;
        }

        cursor->it = vtab->dataset->getMatrixView()->events(events);
        cursor->knownValues = knownValues;

        return SQLITE_OK;
    }

    static int Next(sqlite3_vtab_cursor* vcursor)
    {
        Cursor * cursor = static_cast<Cursor *>(vcursor);
        //cerr << "next" << endl;
        cursor->it->next();
        return SQLITE_OK;
    }

    static int Eof(sqlite3_vtab_cursor* vcursor)
    {
        //cerr << "eof" << endl;
        Cursor * cursor = static_cast<Cursor *>(vcursor);
        return cursor->it->eof();
    }

    static int Column(sqlite3_vtab_cursor* vcursor,
                      sqlite3_context* context,
                      int colNum)
    {
        Cursor * cursor = static_cast<Cursor *>(vcursor);

        //cerr << "column " << colNum << endl;
        //cerr << "cursor->vtab->columnHashes.size() = "
        //     << cursor->vtab->columnHashes.size() << endl;

        switch (colNum) {
        case TIMESTAMP_COLUMN:
            // timestamp column
            sqlite3_result_double(context, cursor->it->timestamp().secondsSinceEpoch());
            return SQLITE_OK;

        case ISO_TIMESTAMP_COLUMN:
            // timestamp utf column
            return sqlite3_result_ascii(context, cursor->it->timestamp().printIso8601());

        case ROW_HASH_COLUMN:
            // row ID column
            return sqlite3_result_ascii(context, cursor->it->rowName().toString());

        case ROW_NAME_COLUMN:
            // row ID column
            return sqlite3_result_ascii(context, cursor->it->rowHash().toString());

        default: {
            ColumnHash column = cursor->vtab->columnHashes[colNum - NUM_FIXED_COLUMNS];

            CellValue value;

            // If this is part of a constraint, then we know it without
            // needing to load the row
            auto it = cursor->knownValues.find(column);
            if (it != cursor->knownValues.end()) {
                value = it->second;
            }
            else {
                value = cursor->it->column(column);
            }

            switch (value.cellType()) {
            case CellValue::EMPTY:
                sqlite3_result_null(context);
                return SQLITE_OK;
            case CellValue::FLOAT:
                sqlite3_result_double(context, value.toDouble());
                return SQLITE_OK;
            case CellValue::INTEGER:
                sqlite3_result_int64(context, value.toInt());
                return SQLITE_OK;
            case CellValue::STRING:
                return sqlite3_result_ascii(context, value.toString());
            case CellValue::UTF8_STRING:
                return sqlite3_result_result_utf8(context, value.toUtf8String());
            default:
                throw MLDB::Exception("unknown cell type");
            }
        }
        }

        return SQLITE_ERROR;
    }

    static int Rowid(sqlite3_vtab_cursor* vcursor, sqlite3_int64 *pRowid)
    {
        cerr << "rowid" << endl;
        return SQLITE_ERROR;
    }

    static int Update(sqlite3_vtab *, int, sqlite3_value **, sqlite3_int64 *)
    {
        cerr << "update" << endl;
        return SQLITE_ERROR;
    }

    static void implementFunc(sqlite3_context * context,
                              int arity,
                              sqlite3_value ** vals)
    {
        cerr << "implementing function with arity " << arity << endl;

    }

    static int FindFunction(sqlite3_vtab *pVtab, int nArg, const char *zName,
                            void (**pxFunc)(sqlite3_context*,int,sqlite3_value**),
                            void **ppArg)
    {
        cerr << "FindFunction " << zName << " arity " << nArg << endl;
        *pxFunc = implementFunc;
        return SQLITE_OK;
    }
};

struct SqlitePlugin: public Plugin {

    SqlitePlugin(ServicePeer * server,
                 PolyConfig config,
                 std::function<bool (const Json::Value & progress)> onProgress)
        : Plugin(static_cast<MldbServer *>(server)),
          db(":memory:")
    {
        init();

        watchDatasets = this->server->datasets->watchElements("*", true, string("Sqlite Datasets Watch"));
        watchDatasets.bind(std::bind(&SqlitePlugin::onNewDataset, this, std::placeholders::_1));
    }

    sqlite3pp::database db;
    sqlite3_module module;

    void init()
    {
        module = {
            1, // version
            &BehaviourModule::Create,
            &BehaviourModule::Connect,
            &BehaviourModule::BestIndex,
            &BehaviourModule::Disconnect,
            &BehaviourModule::Destroy,
            &BehaviourModule::Open,
            &BehaviourModule::Close,
            &BehaviourModule::Filter,
            &BehaviourModule::Next,
            &BehaviourModule::Eof,
            &BehaviourModule::Column,
            &BehaviourModule::Rowid,
            nullptr /* update */,
            nullptr /* begin */,
            nullptr /* sync */,
            nullptr /* commit */,
            nullptr /* rollback */,
            &BehaviourModule::FindFunction,
            nullptr /* rename */
        };

        int res = sqlite3_create_module_v2(db, "mldb", &module, nullptr,
                                           &BehaviourModule::Free);
        if (res != SQLITE_OK) {
            throw sqlite3pp::database_error(db);
        }
    }

    WatchT<DatasetCollection::ChildEvent> watchDatasets;

    void onNewDataset(const DatasetCollection::ChildEvent & dataset)
    {
        cerr << "SQL got new dataset " << dataset.key << endl;

        currentMldb = server;
        sqlite3pp::command command(db, ("CREATE VIRTUAL TABLE " + sqlEscape(dataset.key) + " USING mldb()").c_str());
        command.execute();
        currentMldb = nullptr;  // TODO: proper guard

        string query = "SELECT * FROM " + dataset.key + " WHERE aircrafttype IN ('RV6', 'RV4') ORDER BY Timestamp LIMIT 10";
        explainQuery(db, query);
        dumpQueryArray(db, query);

        //query = "SELECT status, COUNT(*) FROM " + dataset.key + " WHERE aircrafttype = 'A320' GROUP BY status";
        //query = "SELECT aircrafttype, status, COUNT(*) FROM " + dataset.key + " WHERE status = 'Approach' OR status = 'Landing' GROUP BY aircrafttype, status ORDER BY count(*) DESC";
        //query = "SELECT aircrafttype, COUNT(*) FROM " + dataset.key + " GROUP BY aircrafttype ORDER BY count(*) DESC";

        //query = "SELECT DISTINCT aircrafttype FROM " + dataset.key;

        //explainQuery(db, query);

        //for (unsigned i = 0;  i < 20;  ++i)
        //    dumpQueryArray(db, query);

    }

    Any getStatus() const
    {
        return Any();
    }

    RestRequestRouter router;

    virtual RestRequestMatchResult
    handleRequest(RestConnection & connection,
                  const RestRequest & request,
                  RestRequestParsingContext & context) const
    {
        cerr << "SqlitePlugin handling request " << request << endl;
        return router.processRequest(connection, request, context);
    }
};

namespace {

PluginCollection::RegisterType<SqlitePlugin> regSqlite("sql");

} // file scope

} // namespace MLDB

<commit_msg>rm sql.cc<commit_after><|endoftext|>


<commit_before>#include "PosixFileSystem.hpp"
#include "../File.hpp"
#include "../InputStream.hpp"
#include "../OutputStream.hpp"
#include "../EmptyFile.hpp"
#include "../PartFile.hpp"
#include "../Exception.hpp"
#include <limits>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <dirent.h>
#include <errno.h>
#include <stdlib.h>

BEGIN_INANITY_PLATFORM

class PosixFile : public File
{
private:
	void* data;
	size_t size;

public:
	PosixFile(void* data, size_t size)
	: data(data), size(size) {}

	~PosixFile()
	{
		munmap(data, size);
	}

	void* GetData() const
	{
		return data;
	}

	size_t GetSize() const
	{
		return size;
	}
};

class PosixInputStream : public InputStream
{
private:
	int fd;

public:
	PosixInputStream(int fd) : fd(fd) {}

	~PosixInputStream()
	{
		close(fd);
	}

	size_t Read(void* data, size_t size)
	{
		char* dataPtr = (char*)data;
		size_t resultSize = 0;
		while(size > 0)
		{
			ssize_t readSize = size;
			if(readSize > std::numeric_limits<ssize_t>::max())
				readSize = std::numeric_limits<ssize_t>::max();
			readSize = read(fd, dataPtr, readSize);
			if(readSize < 0)
				THROW("Disk read error");
			if(readSize == 0)
				break;
			resultSize += readSize;
			size -= readSize;
			dataPtr += readSize;
		}
		return resultSize;
	}

	size_t GetSize() const
	{
		struct stat st;
		if(fstat(fd, &st) != 0)
			THROW("Error getting size");
		return st.st_size;
	}
};

class PosixOutputStream : public OutputStream
{
private:
	int fd;

public:
	PosixOutputStream(int fd) : fd(fd) {}

	~PosixOutputStream()
	{
		close(fd);
	}

	void Write(const void* data, size_t size)
	{
		const char* dataPtr = (const char*)data;
		while(size > 0)
		{
			ssize_t written = write(fd, dataPtr, size);
			if(written < 0)
				THROW("Disk write error");
			size -= written;
			dataPtr += written;
		}
	}
};

PosixFileSystem::PosixFileSystem(const String& userFolderName)
{
	// if the name is absolute
	if(userFolderName.length() && userFolderName[0] == '/')
		folderName = userFolderName;
	// else it's relative
	else
	{
		// get full path of current directory
		char currentDirectory[1024];
		if(!getcwd(currentDirectory, sizeof(currentDirectory)))
			THROW_SECONDARY("Can't get current directory", Exception::SystemError());
		folderName = currentDirectory;

		// add specified user folder name
		if(userFolderName.length())
		{
			// add '/' to the end
			if(!folderName.length() || folderName[folderName.length() - 1] != '/')
				folderName += '/';
			folderName += userFolderName;
		}
	}

	// add '/' to the end
	if(!folderName.length() || folderName[folderName.length() - 1] != '/')
		folderName += '/';
}

PosixFileSystem::PosixFileSystem()
{
	//создать абсолютную файловую систему, то есть ничего не делать;
	//folderName - пустая строка
}

String PosixFileSystem::GetFullName(String fileName) const
{
	if(folderName.length())
	{
		if(!fileName.length() || fileName[0] != '/')
			THROW("File name should begin with slash");
		return folderName + fileName.substr(1);
	}
	else
		return fileName;
}

size_t PosixFileSystem::GetFileSize(const String& fileName)
{
	struct stat st;
	if(stat(fileName.c_str(), &st) != 0)
		THROW("Can't determine file size");

	return st.st_size;
}

ptr<File> PosixFileSystem::LoadPartOfFile(const String& fileName, long long mappingStart, size_t mappingSize)
{
	String name = GetFullName(fileName);
	try
	{
		int fd = open(name.c_str(), O_RDONLY, 0);
		if(fd < 0)
			THROW_SECONDARY("Can't open file", Exception::SystemError());
		size_t size;
		if(mappingSize)
			size = mappingSize;
		else
		{
			struct stat st;
			if(fstat(fd, &st) < 0)
				THROW_SECONDARY("Can't get file size", Exception::SystemError());
			size = st.st_size;
		}
		if(!size)
			return NEW(EmptyFile());

		//получить размер страницы
		static size_t pageSize = 0;
		if(!pageSize)
			pageSize = getpagesize();

		//округлить начало проекции вниз на размер страницы
		size_t realMappingStart = mappingStart & ~(pageSize - 1);
		//вычислить реальный размер
		size_t realMappingSize = size + (size_t)(mappingStart - realMappingStart);
		//спроецировать файл с учетом этого сдвига
		void* data = mmap(0, realMappingSize, PROT_READ, MAP_PRIVATE, fd, realMappingStart);
		if(data == (caddr_t)-1)
			THROW_SECONDARY("Can't map file", Exception::SystemError());

		//если сдвиг был
		if(realMappingStart < (unsigned long long)mappingStart)
			//вернуть указатель на частичный файл, с учетом сдвига
			return NEW(PartFile(NEW(PosixFile(data, realMappingSize)), (char*)data + (size_t)(mappingStart - realMappingStart), size));
		//иначе сдвига не было, просто вернуть файл
		return NEW(PosixFile(data, size));
	}
	catch(Exception* exception)
	{
		THROW_SECONDARY("Can't load file \"" + fileName + "\" as \"" + name + "\"", exception);
	}
}

void PosixFileSystem::SaveFile(ptr<File> file, const String& fileName)
{
	String name = GetFullName(fileName);
	try
	{
		int fd = open(fileName.c_str(), O_CREAT | O_TRUNC | O_WRONLY, 0644);
		if(fd < 0)
			THROW_SECONDARY("Can't open file", Exception::SystemError());

		const char* data = (const char*)file->GetData();
		size_t size = file->GetSize();

		while(size > 0)
		{
			ssize_t written = write(fd, data, size);
			if(written < 0)
			{
				ptr<Exception> exception = Exception::SystemError();
				close(fd);
				THROW_SECONDARY("Can't write file", exception);
			}
			size -= written;
			data += written;
		}
		close(fd);
	}
	catch(Exception* exception)
	{
		THROW_SECONDARY(String("Can't save file \"") + fileName + "\" as \"" + name + "\"", exception);
	}
}

ptr<InputStream> PosixFileSystem::LoadStream(const String& fileName)
{
	String name = GetFullName(fileName);
	try
	{
		int fd = open(name.c_str(), O_RDONLY, 0);
		if(fd < 0)
			THROW_SECONDARY("Can't open file", Exception::SystemError());
		return NEW(PosixInputStream(fd));
	}
	catch(Exception* exception)
	{
		THROW_SECONDARY(String("Can't load file \"") + fileName + "\" as \"" + name + "\" as stream", exception);
	}
}

ptr<OutputStream> PosixFileSystem::SaveStream(const String& fileName)
{
	String name = GetFullName(fileName);
	try
	{
		int fd = open(fileName.c_str(), O_CREAT | O_TRUNC | O_WRONLY, 0644);
		if(fd < 0)
			THROW_SECONDARY("Can't open file", Exception::SystemError());
		return NEW(PosixOutputStream(fd));
	}
	catch(Exception* exception)
	{
		THROW_SECONDARY(String("Can't save file \"") + fileName + "\" as \"" + name + "\" as stream", exception);
	}
}

void PosixFileSystem::GetDirectoryEntries(const String& directoryName, std::vector<String>& entries) const
{
	String fullDirectoryName = GetFullName(directoryName);
	DIR* dir = opendir(fullDirectoryName.c_str());

	while(struct dirent* ent = readdir(dir))
	{
		if(errno)
		{
			ptr<Exception> exception = Exception::SystemError();
			closedir(dir);
			THROW_SECONDARY("Can't read dir", exception);
		}

		String fileTitle = ent->d_name;
		String fullFileName = fullDirectoryName + fileTitle;
		struct stat st;
		if(stat(fullFileName.c_str(), &st) < 0)
		{
			ptr<Exception> exception = Exception::SystemError();
			closedir(dir);
			THROW_SECONDARY("Can't stat file " + fullFileName, exception);
		}

		// если файл скрыт (или ненужен: . или ..)
		if(!fileTitle.length() || fileTitle[0] == '.')
			// пропустить его
			continue;

		// если это каталог
		if((st.st_mode & S_IFMT) == S_IFDIR)
			// добавить слеш в конец
			fileTitle += '/';
		// добавить файл/каталог в результат
		entries.push_back(directoryName + fileTitle);
	}

	closedir(dir);
}

ptr<File> PosixFileSystem::LoadFile(const String& fileName)
{
	return LoadPartOfFile(fileName, 0, 0);
}

ptr<PosixFileSystem> PosixFileSystem::GetNativeFileSystem()
{
	//этот метод сделан просто для лучшей понятности
	return NEW(PosixFileSystem());
}

void PosixFileSystem::GetFileNames(std::vector<String>& fileNames) const
{
	//только если файловая система не абсолютная
	if(folderName.length())
		GetAllDirectoryEntries("/", fileNames);
}

END_INANITY_PLATFORM
<commit_msg>fix PosixFileSystem issue with wrong filenames<commit_after>#include "PosixFileSystem.hpp"
#include "../File.hpp"
#include "../InputStream.hpp"
#include "../OutputStream.hpp"
#include "../EmptyFile.hpp"
#include "../PartFile.hpp"
#include "../Exception.hpp"
#include <limits>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <dirent.h>
#include <errno.h>
#include <stdlib.h>

BEGIN_INANITY_PLATFORM

class PosixFile : public File
{
private:
	void* data;
	size_t size;

public:
	PosixFile(void* data, size_t size)
	: data(data), size(size) {}

	~PosixFile()
	{
		munmap(data, size);
	}

	void* GetData() const
	{
		return data;
	}

	size_t GetSize() const
	{
		return size;
	}
};

class PosixInputStream : public InputStream
{
private:
	int fd;

public:
	PosixInputStream(int fd) : fd(fd) {}

	~PosixInputStream()
	{
		close(fd);
	}

	size_t Read(void* data, size_t size)
	{
		char* dataPtr = (char*)data;
		size_t resultSize = 0;
		while(size > 0)
		{
			ssize_t readSize = size;
			if(readSize > std::numeric_limits<ssize_t>::max())
				readSize = std::numeric_limits<ssize_t>::max();
			readSize = read(fd, dataPtr, readSize);
			if(readSize < 0)
				THROW("Disk read error");
			if(readSize == 0)
				break;
			resultSize += readSize;
			size -= readSize;
			dataPtr += readSize;
		}
		return resultSize;
	}

	size_t GetSize() const
	{
		struct stat st;
		if(fstat(fd, &st) != 0)
			THROW("Error getting size");
		return st.st_size;
	}
};

class PosixOutputStream : public OutputStream
{
private:
	int fd;

public:
	PosixOutputStream(int fd) : fd(fd) {}

	~PosixOutputStream()
	{
		close(fd);
	}

	void Write(const void* data, size_t size)
	{
		const char* dataPtr = (const char*)data;
		while(size > 0)
		{
			ssize_t written = write(fd, dataPtr, size);
			if(written < 0)
				THROW("Disk write error");
			size -= written;
			dataPtr += written;
		}
	}
};

PosixFileSystem::PosixFileSystem(const String& userFolderName)
{
	// if the name is absolute
	if(userFolderName.length() && userFolderName[0] == '/')
		folderName = userFolderName;
	// else it's relative
	else
	{
		// get full path of current directory
		char currentDirectory[1024];
		if(!getcwd(currentDirectory, sizeof(currentDirectory)))
			THROW_SECONDARY("Can't get current directory", Exception::SystemError());
		folderName = currentDirectory;

		// add specified user folder name
		if(userFolderName.length())
		{
			// add '/' to the end
			if(!folderName.length() || folderName[folderName.length() - 1] != '/')
				folderName += '/';
			folderName += userFolderName;
		}
	}

	// add '/' to the end
	if(!folderName.length() || folderName[folderName.length() - 1] != '/')
		folderName += '/';
}

PosixFileSystem::PosixFileSystem()
{
	//создать абсолютную файловую систему, то есть ничего не делать;
	//folderName - пустая строка
}

String PosixFileSystem::GetFullName(String fileName) const
{
	if(folderName.length())
	{
		if(!fileName.length() || fileName[0] != '/')
			THROW("File name should begin with slash");
		return folderName + fileName.substr(1);
	}
	else
		return fileName;
}

size_t PosixFileSystem::GetFileSize(const String& fileName)
{
	struct stat st;
	if(stat(fileName.c_str(), &st) != 0)
		THROW("Can't determine file size");

	return st.st_size;
}

ptr<File> PosixFileSystem::LoadPartOfFile(const String& fileName, long long mappingStart, size_t mappingSize)
{
	String name = GetFullName(fileName);
	try
	{
		int fd = open(name.c_str(), O_RDONLY, 0);
		if(fd < 0)
			THROW_SECONDARY("Can't open file", Exception::SystemError());
		size_t size;
		if(mappingSize)
			size = mappingSize;
		else
		{
			struct stat st;
			if(fstat(fd, &st) < 0)
				THROW_SECONDARY("Can't get file size", Exception::SystemError());
			size = st.st_size;
		}
		if(!size)
			return NEW(EmptyFile());

		//получить размер страницы
		static size_t pageSize = 0;
		if(!pageSize)
			pageSize = getpagesize();

		//округлить начало проекции вниз на размер страницы
		size_t realMappingStart = mappingStart & ~(pageSize - 1);
		//вычислить реальный размер
		size_t realMappingSize = size + (size_t)(mappingStart - realMappingStart);
		//спроецировать файл с учетом этого сдвига
		void* data = mmap(0, realMappingSize, PROT_READ, MAP_PRIVATE, fd, realMappingStart);
		if(data == (caddr_t)-1)
			THROW_SECONDARY("Can't map file", Exception::SystemError());

		//если сдвиг был
		if(realMappingStart < (unsigned long long)mappingStart)
			//вернуть указатель на частичный файл, с учетом сдвига
			return NEW(PartFile(NEW(PosixFile(data, realMappingSize)), (char*)data + (size_t)(mappingStart - realMappingStart), size));
		//иначе сдвига не было, просто вернуть файл
		return NEW(PosixFile(data, size));
	}
	catch(Exception* exception)
	{
		THROW_SECONDARY("Can't load file \"" + fileName + "\" as \"" + name + "\"", exception);
	}
}

void PosixFileSystem::SaveFile(ptr<File> file, const String& fileName)
{
	String name = GetFullName(fileName);
	try
	{
		int fd = open(name.c_str(), O_CREAT | O_TRUNC | O_WRONLY, 0644);
		if(fd < 0)
			THROW_SECONDARY("Can't open file", Exception::SystemError());

		const char* data = (const char*)file->GetData();
		size_t size = file->GetSize();

		while(size > 0)
		{
			ssize_t written = write(fd, data, size);
			if(written < 0)
			{
				ptr<Exception> exception = Exception::SystemError();
				close(fd);
				THROW_SECONDARY("Can't write file", exception);
			}
			size -= written;
			data += written;
		}
		close(fd);
	}
	catch(Exception* exception)
	{
		THROW_SECONDARY(String("Can't save file \"") + fileName + "\" as \"" + name + "\"", exception);
	}
}

ptr<InputStream> PosixFileSystem::LoadStream(const String& fileName)
{
	String name = GetFullName(fileName);
	try
	{
		int fd = open(name.c_str(), O_RDONLY, 0);
		if(fd < 0)
			THROW_SECONDARY("Can't open file", Exception::SystemError());
		return NEW(PosixInputStream(fd));
	}
	catch(Exception* exception)
	{
		THROW_SECONDARY(String("Can't load file \"") + fileName + "\" as \"" + name + "\" as stream", exception);
	}
}

ptr<OutputStream> PosixFileSystem::SaveStream(const String& fileName)
{
	String name = GetFullName(fileName);
	try
	{
		int fd = open(name.c_str(), O_CREAT | O_TRUNC | O_WRONLY, 0644);
		if(fd < 0)
			THROW_SECONDARY("Can't open file", Exception::SystemError());
		return NEW(PosixOutputStream(fd));
	}
	catch(Exception* exception)
	{
		THROW_SECONDARY(String("Can't save file \"") + fileName + "\" as \"" + name + "\" as stream", exception);
	}
}

void PosixFileSystem::GetDirectoryEntries(const String& directoryName, std::vector<String>& entries) const
{
	String fullDirectoryName = GetFullName(directoryName);
	DIR* dir = opendir(fullDirectoryName.c_str());

	while(struct dirent* ent = readdir(dir))
	{
		if(errno)
		{
			ptr<Exception> exception = Exception::SystemError();
			closedir(dir);
			THROW_SECONDARY("Can't read dir", exception);
		}

		String fileTitle = ent->d_name;
		String fullFileName = fullDirectoryName + fileTitle;
		struct stat st;
		if(stat(fullFileName.c_str(), &st) < 0)
		{
			ptr<Exception> exception = Exception::SystemError();
			closedir(dir);
			THROW_SECONDARY("Can't stat file " + fullFileName, exception);
		}

		// если файл скрыт (или ненужен: . или ..)
		if(!fileTitle.length() || fileTitle[0] == '.')
			// пропустить его
			continue;

		// если это каталог
		if((st.st_mode & S_IFMT) == S_IFDIR)
			// добавить слеш в конец
			fileTitle += '/';
		// добавить файл/каталог в результат
		entries.push_back(directoryName + fileTitle);
	}

	closedir(dir);
}

ptr<File> PosixFileSystem::LoadFile(const String& fileName)
{
	return LoadPartOfFile(fileName, 0, 0);
}

ptr<PosixFileSystem> PosixFileSystem::GetNativeFileSystem()
{
	//этот метод сделан просто для лучшей понятности
	return NEW(PosixFileSystem());
}

void PosixFileSystem::GetFileNames(std::vector<String>& fileNames) const
{
	//только если файловая система не абсолютная
	if(folderName.length())
		GetAllDirectoryEntries("/", fileNames);
}

END_INANITY_PLATFORM
<|endoftext|>


<commit_before>#include "stdafx.h"

#if defined(_WIN32_WCE)
#include <soundfile.h>
#endif

#include <common/RhodesApp.h>
#include <common/rhoparams.h>

#include "Alert.h"
#include "MainWindow.h"
#include "Vibrate.h"

extern "C" HWND getMainWnd();

/**
 ********************************************************************************
 * CAlertDialog members.
 ********************************************************************************
 */

//TODO: smart alignment and win32

typedef CWinTraits <WS_CAPTION  | WS_VISIBLE | WS_POPUP | DS_CENTER> CAlertDialogTraits;

CAlertDialog::CAlertDialog(Params *params)
{
	m_title    = params->m_title;
	m_message  = params->m_message;
	m_callback = params->m_callback;
	m_icon     = params->m_icon;

	int id = ID_ALERT_DLG_BUTTON_FIRST;
	for (Hashtable<String, String>::iterator itr = params->m_buttons.begin(); itr != params->m_buttons.end(); ++itr)
		m_buttons.addElement(CustomButton(itr->first, itr->second, id++));
}

CAlertDialog::~CAlertDialog()
{
}

void CAlertDialog::DoInitTemplate()
{
#ifdef OS_WINCE
	int initialWidth  = GetSystemMetrics(SM_CXSCREEN)/3;
	int initialHeight = initialWidth/2;

	m_Template.Create(false, convertToStringW(m_title).c_str(),
						GetSystemMetrics(SM_CXSCREEN)/2 - initialWidth/2,
						GetSystemMetrics(SM_CYSCREEN)/2 - initialHeight/2,
						initialWidth,
						initialHeight,
						CAlertDialogTraits::GetWndStyle(0),
						CAlertDialogTraits::GetWndExStyle(0));
#endif
}

void CAlertDialog::DoInitControls()
{
}

LRESULT CAlertDialog::OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL&bHandled)
{
#ifdef OS_WINCE
	int indent   = 10;
	unsigned int maxWidth  = GetSystemMetrics(SM_CXSCREEN) - (indent * 2);
	unsigned int maxHeight = GetSystemMetrics(SM_CYSCREEN) - (indent * 2);
	unsigned int msgWidth  = 0, msgHeight = 0;
	CClientDC dc(m_hWnd);
	TEXTMETRIC tm = {0};
	POINT point = { 5,  5};
	SIZE  size  = { 0, 0 };
	unsigned int iconHeight = 42;

	int iconId = MB_ICONWARNING;
	HMODULE hGWES = LoadLibraryEx( L"gwes.exe", NULL, LOAD_LIBRARY_AS_DATAFILE );
	HICON hIcon = LoadIcon(hGWES, MAKEINTRESOURCE(iconId));
	if (hIcon == NULL) {
		LOG(ERROR) + "Failed to load icon";
	} else {
		size.cx = iconHeight; size.cy = iconHeight;
		m_iconCtrl.Create(m_hWnd, CRect(point, size), NULL, WS_CHILD | WS_VISIBLE);
		if (m_iconCtrl.SetIcon(hIcon) == NULL)
			LOG(INFO) + ": failed to set icon";
	}

	dc.GetTextMetrics(&tm);
	if ((m_message.length() * tm.tmAveCharWidth) > maxWidth) {
		msgWidth = maxWidth - indent - size.cy;
		msgHeight = (((m_message.length() * tm.tmAveCharWidth * 2) / msgWidth))
					* (tm.tmHeight + tm.tmExternalLeading + tm.tmInternalLeading);

		RECT rect;
		GetWindowRect(&rect);
		MoveWindow(indent, rect.top, maxWidth, msgHeight + tm.tmHeight * 5);

		point.x = size.cx; point.y = 5;
		size.cx = msgWidth; size.cy = msgHeight;
	} else {
		point.x = size.cx + 4; point.y = 5;

		msgWidth = m_message.length() * tm.tmAveCharWidth * 2;
		msgHeight = (tm.tmHeight + tm.tmExternalLeading + tm.tmInternalLeading);

		size.cx = msgWidth; size.cy = msgHeight;
	}

	m_messageCtrl.Create(m_hWnd, CRect(point, size), NULL, WS_CHILD | WS_VISIBLE);
	m_messageCtrl.SetWindowText(convertToStringW(m_message).c_str());

	int bntNum = m_buttons.size();

	point.x = indent, point.y = msgHeight + 2;
	if (iconHeight > msgHeight)
		point.y = iconHeight + 6;

	unsigned int btnWidth = 0, btnHeight = 0;
	for (Vector<CustomButton>::iterator itr = m_buttons.begin(); itr != m_buttons.end(); ++itr) {

		btnWidth = (itr->m_title.length() * tm.tmAveCharWidth * 2) + 6;
		btnHeight = tm.tmHeight + 4;

		size.cx = btnWidth; size.cy = btnHeight;
		itr->Create(m_hWnd, CRect(point, size),
					convertToStringW(itr->m_title).c_str(),
					WS_CHILD | WS_VISIBLE, 0,
					itr->m_numId);

		point.x += btnWidth + 4;
	}
#endif
	return bHandled = FALSE;
}

bool CAlertDialog::findButton(int id, CustomButton &btn)
{
	for (Vector<CustomButton>::iterator itr = m_buttons.begin(); itr != m_buttons.end(); ++itr) {
		if (itr->m_numId == id) {
			btn = *itr;
			return true;
		}
	}

	return false;
}

LRESULT CAlertDialog::OnAlertDialogButton (WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
{
	CustomButton cbtn;
	if (findButton((int) wID, cbtn))
		rho_rhodesapp_callPopupCallback(m_callback.c_str(), cbtn.m_strId.c_str(), cbtn.m_title.c_str());
	else
		LOG(ERROR) + "internal error";

	EndDialog(wID);
	return 0;
}

/**
 ********************************************************************************
 * CAlert members.
 ********************************************************************************
 */

IMPLEMENT_LOGCLASS(CAlert, "Alert");

void CAlert::showPopup(CAlertDialog::Params *params)
{
    HWND main_wnd = getMainWnd();
	::PostMessage(main_wnd, WM_ALERT_SHOWPOPUP, 0, (LPARAM ) params);
}

#if defined(_WIN32_WCE)
void CAlert::vibrate()
{
    CVibrate::getCVibrate().toggle();
}

void CAlert::playFile(String fileName)
{
    rho::String path = RHODESAPP().getRhoRootPath() + "apps" + fileName;
    HSOUND hSound;

    rho::String::size_type pos = 0;
    while ( (pos = path.find('/', pos)) != rho::String::npos ) {
        path.replace( pos, 1, "\\");
        pos++;
    }

    USES_CONVERSION;
    //SndPlaySync(A2T(path.c_str()),  SND_PLAY_IGNOREUSERSETTINGS);
    HRESULT hr = SndOpen(A2T(path.c_str()), &hSound);
    hr = SndPlayAsync (hSound, 0);

    if (hr != S_OK) {
        LOG(WARNING) + "OnAlertPlayFile: failed to play file";
    }

    WaitForSingleObject(hSound, INFINITE);

    hr = SndClose(hSound);
    SndStop(SND_SCOPE_PROCESS, NULL);
}

#endif //_WIN32_WCE

extern "C" void alert_show_popup(rho_param *p)
{
	if (p->type == RHO_PARAM_STRING) {
		CAlert::showPopup(new CAlertDialog::Params(String(p->v.string)));
	} else if (p->type == RHO_PARAM_HASH) {
		String title, message, callback, icon;
		String btnId, btnTitle;
		Hashtable<String, String> buttons;

		for (int i = 0, lim = p->v.hash->size; i < lim; ++i) {
			char *name = p->v.hash->name[i];
            rho_param *value = p->v.hash->value[i];

			if (strcasecmp(name, "title") == 0) {
                if (value->type != RHO_PARAM_STRING) {
                    RAWLOG_ERROR("'title' should be string");
                    continue;
                }
                title = value->v.string;
            }
			else if (strcasecmp(name, "message") == 0) {
                if (value->type != RHO_PARAM_STRING) {
                    RAWLOG_ERROR("'message' should be string");
                    continue;
                }
                message = value->v.string;
            }
            else if (strcasecmp(name, "callback") == 0) {
                if (value->type != RHO_PARAM_STRING) {
                    RAWLOG_ERROR("'callback' should be string");
                    continue;
                }
                callback = value->v.string;
            }
            else if (strcasecmp(name, "buttons") == 0) {
				if (value->type != RHO_PARAM_ARRAY) {
                    RAWLOG_ERROR("'buttons' should be array");
                    continue;
                }
				for (int j = 0, limj = value->v.array->size; j < limj; ++j) {
					rho_param *arrValue = value->v.array->value[j];
					switch (arrValue->type) {
						case RHO_PARAM_STRING:
							btnId    = arrValue->v.string;
							btnTitle = arrValue->v.string;
							break;
						case RHO_PARAM_HASH:
							for (int k = 0, limk = arrValue->v.hash->size; k < limk; ++k) {
                                char *sName = arrValue->v.hash->name[k];
                                rho_param *sValue = arrValue->v.hash->value[k];
                                if (sValue->type != RHO_PARAM_STRING) {
                                    RAWLOG_ERROR("Illegal type of button item's value");
                                    continue;
                                }
                                if (strcasecmp(sName, "id") == 0)
                                    btnId = sValue->v.string;
                                else if (strcasecmp(sName, "title") == 0)
                                    btnTitle = sValue->v.string;
                            }
							break;
						default:
							RAWLOG_ERROR("Illegal type of button item");
							continue;
					}
				}
				if (btnId == "" || btnTitle == "") {
					RAWLOG_ERROR("Incomplete button item");
					continue;
				}

				buttons.put(btnTitle, btnId);
			}//buttons
		}

		CAlert::showPopup(new CAlertDialog::Params(title, message, icon, callback, buttons));
	}
}

extern "C" void alert_vibrate(void*) {
#if defined(_WIN32_WCE)
    CAlert::vibrate();
#endif
}

extern "C" void alert_play_file(char* file_name, ...) {
#if defined(_WIN32_WCE)
    CAlert::playFile(file_name);
#endif
}

extern "C" void alert_hide_popup()
{
}<commit_msg>WM: fix icon in customized alerts.<commit_after>#include "stdafx.h"

#if defined(_WIN32_WCE)
#include <soundfile.h>
#endif

#include <common/RhodesApp.h>
#include <common/rhoparams.h>

#include "Alert.h"
#include "MainWindow.h"
#include "Vibrate.h"

extern "C" HWND getMainWnd();

/**
 ********************************************************************************
 * CAlertDialog members.
 ********************************************************************************
 */

//TODO: smart alignment and win32

typedef CWinTraits <WS_CAPTION  | WS_VISIBLE | WS_POPUP | DS_CENTER> CAlertDialogTraits;

CAlertDialog::CAlertDialog(Params *params)
{
	m_title    = params->m_title;
	m_message  = params->m_message;
	m_callback = params->m_callback;
	m_icon     = params->m_icon;

	int id = ID_ALERT_DLG_BUTTON_FIRST;
	for (Hashtable<String, String>::iterator itr = params->m_buttons.begin(); itr != params->m_buttons.end(); ++itr)
		m_buttons.addElement(CustomButton(itr->first, itr->second, id++));
}

CAlertDialog::~CAlertDialog()
{
}

void CAlertDialog::DoInitTemplate()
{
#ifdef OS_WINCE
	int initialWidth  = GetSystemMetrics(SM_CXSCREEN)/3;
	int initialHeight = initialWidth/2;

	m_Template.Create(false, convertToStringW(m_title).c_str(),
						GetSystemMetrics(SM_CXSCREEN)/2 - initialWidth/2,
						GetSystemMetrics(SM_CYSCREEN)/2 - initialHeight/2,
						initialWidth,
						initialHeight,
						CAlertDialogTraits::GetWndStyle(0),
						CAlertDialogTraits::GetWndExStyle(0));
#endif
}

void CAlertDialog::DoInitControls()
{
}

LRESULT CAlertDialog::OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL&bHandled)
{
#ifdef OS_WINCE
	int indent   = 10;
	unsigned int maxWidth  = GetSystemMetrics(SM_CXSCREEN) - (indent * 2);
	unsigned int maxHeight = GetSystemMetrics(SM_CYSCREEN) - (indent * 2);
	unsigned int msgWidth  = 0, msgHeight = 0;
	CClientDC dc(m_hWnd);
	TEXTMETRIC tm = {0};
	POINT point = { 5,  5};
	SIZE  size  = { 0, 0 };
	unsigned int iconHeight = 42;
	int iconId = 0;

	/**
	 * Icon.
	 */
	if (m_icon == "alert")
		iconId = MB_ICONWARNING;
	else if (m_icon == "question")
		iconId = MB_ICONQUESTION;
	else if (m_icon == "info")
		iconId = MB_ICONINFORMATION;

	if (iconId != 0) {
		HMODULE hGWES = LoadLibraryEx( L"gwes.exe", NULL, LOAD_LIBRARY_AS_DATAFILE );
		HICON hIcon = LoadIcon(hGWES, MAKEINTRESOURCE(iconId));

		if (hIcon == NULL) {
			LOG(ERROR) + "Failed to load icon";
		} else {
			size.cx = iconHeight; size.cy = iconHeight;
			m_iconCtrl.Create(m_hWnd, CRect(point, size), NULL, WS_CHILD | WS_VISIBLE | SS_ICON, 0);
			if (m_iconCtrl.SetIcon(hIcon) == NULL)
				LOG(INFO) + "Failed to set icon";
		}
	}

	/**
	 * Message.
	 */
	dc.GetTextMetrics(&tm);
	if ((m_message.length() * tm.tmAveCharWidth) > maxWidth) {
		msgWidth = maxWidth - indent - size.cy;
		msgHeight = (((m_message.length() * tm.tmAveCharWidth * 2) / msgWidth))
					* (tm.tmHeight + tm.tmExternalLeading + tm.tmInternalLeading);

		RECT rect;
		GetWindowRect(&rect);
		MoveWindow(indent, rect.top, maxWidth, msgHeight + tm.tmHeight * 5);

		point.x = size.cx; point.y = 5;
		size.cx = msgWidth; size.cy = msgHeight;
	} else {
		point.x = size.cx + 4; point.y = 5;

		msgWidth = m_message.length() * tm.tmAveCharWidth * 2;
		msgHeight = (tm.tmHeight + tm.tmExternalLeading + tm.tmInternalLeading);

		size.cx = msgWidth; size.cy = msgHeight;
	}

	m_messageCtrl.Create(m_hWnd, CRect(point, size), NULL, WS_CHILD | WS_VISIBLE);
	m_messageCtrl.SetWindowText(convertToStringW(m_message).c_str());

	/**
	 * Buttons.
	 */
	int bntNum = m_buttons.size();

	point.x = indent, point.y = iconHeight > msgHeight ? point.y = iconHeight + 6 : msgHeight + 2;

	unsigned int btnWidth = 0, btnHeight = 0;
	for (Vector<CustomButton>::iterator itr = m_buttons.begin(); itr != m_buttons.end(); ++itr) {

		btnWidth = (itr->m_title.length() * tm.tmAveCharWidth * 2) + 6;
		btnHeight = tm.tmHeight + 4;

		size.cx = btnWidth; size.cy = btnHeight;
		itr->Create(m_hWnd, CRect(point, size),
					convertToStringW(itr->m_title).c_str(),
					WS_CHILD | WS_VISIBLE, 0,
					itr->m_numId);

		point.x += btnWidth + 4;
	}
#endif
	return bHandled = FALSE;
}

bool CAlertDialog::findButton(int id, CustomButton &btn)
{
	for (Vector<CustomButton>::iterator itr = m_buttons.begin(); itr != m_buttons.end(); ++itr) {
		if (itr->m_numId == id) {
			btn = *itr;
			return true;
		}
	}

	return false;
}

LRESULT CAlertDialog::OnAlertDialogButton (WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
{
	CustomButton cbtn;
	if (findButton((int) wID, cbtn))
		rho_rhodesapp_callPopupCallback(m_callback.c_str(), cbtn.m_strId.c_str(), cbtn.m_title.c_str());
	else
		LOG(ERROR) + "internal error";

	EndDialog(wID);
	return 0;
}

/**
 ********************************************************************************
 * CAlert members.
 ********************************************************************************
 */

IMPLEMENT_LOGCLASS(CAlert, "Alert");

void CAlert::showPopup(CAlertDialog::Params *params)
{
    HWND main_wnd = getMainWnd();
	::PostMessage(main_wnd, WM_ALERT_SHOWPOPUP, 0, (LPARAM ) params);
}

#if defined(_WIN32_WCE)
void CAlert::vibrate()
{
    CVibrate::getCVibrate().toggle();
}

void CAlert::playFile(String fileName)
{
    rho::String path = RHODESAPP().getRhoRootPath() + "apps" + fileName;
    HSOUND hSound;

    rho::String::size_type pos = 0;
    while ( (pos = path.find('/', pos)) != rho::String::npos ) {
        path.replace( pos, 1, "\\");
        pos++;
    }

    USES_CONVERSION;
    //SndPlaySync(A2T(path.c_str()),  SND_PLAY_IGNOREUSERSETTINGS);
    HRESULT hr = SndOpen(A2T(path.c_str()), &hSound);
    hr = SndPlayAsync (hSound, 0);

    if (hr != S_OK) {
        LOG(WARNING) + "OnAlertPlayFile: failed to play file";
    }

    WaitForSingleObject(hSound, INFINITE);

    hr = SndClose(hSound);
    SndStop(SND_SCOPE_PROCESS, NULL);
}

#endif //_WIN32_WCE

extern "C" void alert_show_popup(rho_param *p)
{
	if (p->type == RHO_PARAM_STRING) {
		CAlert::showPopup(new CAlertDialog::Params(String(p->v.string)));
	} else if (p->type == RHO_PARAM_HASH) {
		String title, message, callback, icon;
		String btnId, btnTitle;
		Hashtable<String, String> buttons;

		for (int i = 0, lim = p->v.hash->size; i < lim; ++i) {
			char *name = p->v.hash->name[i];
            rho_param *value = p->v.hash->value[i];

			if (strcasecmp(name, "title") == 0) {
                if (value->type != RHO_PARAM_STRING) {
                    RAWLOG_ERROR("'title' should be string");
                    continue;
                }
                title = value->v.string;
            }
			else if (strcasecmp(name, "message") == 0) {
                if (value->type != RHO_PARAM_STRING) {
                    RAWLOG_ERROR("'message' should be string");
                    continue;
                }
                message = value->v.string;
            }
            else if (strcasecmp(name, "callback") == 0) {
                if (value->type != RHO_PARAM_STRING) {
                    RAWLOG_ERROR("'callback' should be string");
                    continue;
                }
                callback = value->v.string;
            } else if (strcasecmp(name, "icon") == 0) {
                if (value->type != RHO_PARAM_STRING) {
                    RAWLOG_ERROR("'title' should be string");
                    continue;
                }
                icon = value->v.string;
            }

            else if (strcasecmp(name, "buttons") == 0) {
				if (value->type != RHO_PARAM_ARRAY) {
                    RAWLOG_ERROR("'buttons' should be array");
                    continue;
                }
				for (int j = 0, limj = value->v.array->size; j < limj; ++j) {
					rho_param *arrValue = value->v.array->value[j];
					switch (arrValue->type) {
						case RHO_PARAM_STRING:
							btnId    = arrValue->v.string;
							btnTitle = arrValue->v.string;
							break;
						case RHO_PARAM_HASH:
							for (int k = 0, limk = arrValue->v.hash->size; k < limk; ++k) {
                                char *sName = arrValue->v.hash->name[k];
                                rho_param *sValue = arrValue->v.hash->value[k];
                                if (sValue->type != RHO_PARAM_STRING) {
                                    RAWLOG_ERROR("Illegal type of button item's value");
                                    continue;
                                }
                                if (strcasecmp(sName, "id") == 0)
                                    btnId = sValue->v.string;
                                else if (strcasecmp(sName, "title") == 0)
                                    btnTitle = sValue->v.string;
                            }
							break;
						default:
							RAWLOG_ERROR("Illegal type of button item");
							continue;
					}
				}
				if (btnId == "" || btnTitle == "") {
					RAWLOG_ERROR("Incomplete button item");
					continue;
				}

				buttons.put(btnTitle, btnId);
			}//buttons
		}

		CAlert::showPopup(new CAlertDialog::Params(title, message, icon, callback, buttons));
	}
}

extern "C" void alert_vibrate(void*) {
#if defined(_WIN32_WCE)
    CAlert::vibrate();
#endif
}

extern "C" void alert_play_file(char* file_name, ...) {
#if defined(_WIN32_WCE)
    CAlert::playFile(file_name);
#endif
}

extern "C" void alert_hide_popup()
{
}<|endoftext|>