<commit_before>/*
 * Software License Agreement (BSD License)
 *
 *  Copyright (c) 2014 Max-Planck-Institute for Intelligent Systems,
 *                     University of Southern California
 *    Jan Issac (jan.issac@gmail.com)
 *    Manuel Wuthrich (manuel.wuthrich@gmail.com)
 *
 *
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions
 *  are met:
 *
 *   * Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *   * Redistributions in binary form must reproduce the above
 *     copyright notice, this list of conditions and the following
 *     disclaimer in the documentation and/or other materials provided
 *     with the distribution.
 *   * Neither the name of Willow Garage, Inc. nor the names of its
 *     contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 *  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 *  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 *  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 *
 */

/**
 * @date 2015
 * @author Manuel Wuthrich (manuel.wuthrich@gmail.com)
 * Max-Planck-Institute for Intelligent Systems
 */

#ifndef FL__FILTER__PARTICLE__PARTICLE_FILTER_HPP
#define FL__FILTER__PARTICLE__PARTICLE_FILTER_HPP

#include <fl/distribution/discrete_distribution.hpp>
#include <fl/distribution/standard_gaussian.hpp>

/// TODO: REMOVE UNNECESSARY INCLUDES

#include <map>
#include <tuple>
#include <memory>

#include <fl/util/meta.hpp>
#include <fl/util/traits.hpp>
#include <fl/util/profiling.hpp>

#include <fl/exception/exception.hpp>
#include <fl/filter/filter_interface.hpp>
#include <fl/filter/gaussian/point_set.hpp>
#include <fl/filter/gaussian/feature_policy.hpp>

#include <fl/model/observation/joint_observation_model.hpp>

namespace fl
{

template <typename...> class ParticleFilter;

/**
 * ParticleFilter Traits
 */
template <typename ProcessModel, typename ObservationModel>
struct Traits<ParticleFilter<ProcessModel, ObservationModel>>
{
    typedef typename Traits<ProcessModel>::State        State;
    typedef typename Traits<ProcessModel>::Input        Input;
    typedef typename Traits<ObservationModel>::Obsrv    Obsrv;

    typedef DiscreteDistribution<State> StateDistribution;

    /** \cond INTERNAL */
    typedef typename Traits<ProcessModel>::Noise      ProcessNoise;

    /// \todo this should come from traits
    typedef typename ObservationModel::Noise  ObsrvNoise;
};

template<typename ProcessModel, typename ObservationModel>
class ParticleFilter<ProcessModel, ObservationModel>
    :
    public FilterInterface<ParticleFilter<ProcessModel, ObservationModel>>
{
private:
    typedef ParticleFilter<ProcessModel, ObservationModel> This;

    typedef from_traits(ProcessNoise);
    typedef from_traits(ObsrvNoise);

public:
    typedef from_traits(State);
    typedef from_traits(Input);
    typedef from_traits(Obsrv);
    typedef from_traits(StateDistribution);

public:
    ParticleFilter(const ProcessModel& process_model,
                   const ObservationModel& obsrv_model,
                   const double& max_kl_divergence = 1.0)
        : process_model_(process_model),
          obsrv_model_(obsrv_model),
          process_noise_(process_model.noise_dimension()),
          obsrv_noise_(obsrv_model.noise_dimension()),
          max_kl_divergence_(max_kl_divergence)
    { }

    /// predict ****************************************************************
    virtual void predict(double delta_time,
                         const Input& input,
                         const StateDistribution& prior_dist,
                         StateDistribution& predicted_dist)
    {
        predicted_dist = prior_dist;
        for(size_t i = 0; i < predicted_dist.size(); i++)
        {
            predicted_dist.location(i) =
                    process_model_.predict_state(delta_time,
                                                 prior_dist.location(i),
                                                 process_noise_.sample(),
                                                 input);
        }
    }

    /// update *****************************************************************
    virtual void update(const Obsrv& obsrv,
                        const StateDistribution& predicted_dist,
                        StateDistribution& posterior_dist)
    {
        // if the samples are too concentrated then resample
        if(predicted_dist.kl_given_uniform() > max_kl_divergence_)
        {
            posterior_dist.from_distribution(predicted_dist,
                                             predicted_dist.size());
        }
        else
        {
            posterior_dist = predicted_dist;
        }

        // update the weights of the particles with the likelihoods
        posterior_dist.delta_log_prob_mass(
             obsrv_model_.log_probabilities(obsrv, predicted_dist.locations()));
    }

    /// predict and update *****************************************************
    virtual void predict_and_update(double delta_time,
                                    const Input& input,
                                    const Obsrv& observation,
                                    const StateDistribution& prior_dist,
                                    StateDistribution& posterior_dist)
    {
        predict(delta_time, input, prior_dist, posterior_dist);
        update(observation, posterior_dist, posterior_dist);
    }

    /// set and get ************************************************************
    ProcessModel& process_model()
    {
        return process_model_;
    }
    ObservationModel& obsrv_model()
    {
        return obsrv_model_;
    }

    const ProcessModel& process_model() const
    {
        return process_model_;
    }

    const ObservationModel& obsrv_model() const
    {
        return obsrv_model_;
    }

    /// \todo: should this function be here?
    virtual StateDistribution create_state_distribution() const
    {
        auto state_distr = StateDistribution(process_model().state_dimension());

        return state_distr;
    }

protected:
    ProcessModel process_model_;
    ObservationModel obsrv_model_;

    StandardGaussian<ProcessNoise> process_noise_;
    StandardGaussian<ObsrvNoise> obsrv_noise_;

    // when the KL divergence KL(p||u), where p is the particle distribution
    // and u is the uniform distribution, exceeds max_kl_divergence_, then there
    // is a resampling step. can be understood as -log(f) where f is the
    // fraction of nonzero particles.
    double max_kl_divergence_;
};

}

#endif
<commit_msg>Define Noise, Input, State and Obsrv directly from Models<commit_after>/*
 * Software License Agreement (BSD License)
 *
 *  Copyright (c) 2014 Max-Planck-Institute for Intelligent Systems,
 *                     University of Southern California
 *    Jan Issac (jan.issac@gmail.com)
 *    Manuel Wuthrich (manuel.wuthrich@gmail.com)
 *
 *
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions
 *  are met:
 *
 *   * Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *   * Redistributions in binary form must reproduce the above
 *     copyright notice, this list of conditions and the following
 *     disclaimer in the documentation and/or other materials provided
 *     with the distribution.
 *   * Neither the name of Willow Garage, Inc. nor the names of its
 *     contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 *  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 *  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 *  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 *
 */

/**
 * @date 2015
 * @author Manuel Wuthrich (manuel.wuthrich@gmail.com)
 * Max-Planck-Institute for Intelligent Systems
 */

#ifndef FL__FILTER__PARTICLE__PARTICLE_FILTER_HPP
#define FL__FILTER__PARTICLE__PARTICLE_FILTER_HPP

#include <fl/distribution/discrete_distribution.hpp>
#include <fl/distribution/standard_gaussian.hpp>

/// TODO: REMOVE UNNECESSARY INCLUDES

#include <map>
#include <tuple>
#include <memory>

#include <fl/util/meta.hpp>
#include <fl/util/traits.hpp>
#include <fl/util/profiling.hpp>

#include <fl/exception/exception.hpp>
#include <fl/filter/filter_interface.hpp>
#include <fl/filter/gaussian/point_set.hpp>
#include <fl/filter/gaussian/feature_policy.hpp>

#include <fl/model/observation/joint_observation_model.hpp>

namespace fl
{

template <typename...> class ParticleFilter;

/**
 * ParticleFilter Traits
 */
template <typename ProcessModel, typename ObservationModel>
struct Traits<ParticleFilter<ProcessModel, ObservationModel>>
{
    typedef typename ProcessModel::State        State;
    typedef typename ProcessModel::Input        Input;
    typedef typename ObservationModel::Obsrv    Obsrv;

    typedef DiscreteDistribution<State> StateDistribution;

    /** \cond INTERNAL */
    typedef typename ProcessModel::Noise      ProcessNoise;

    /// \todo this should come from traits
    typedef typename ObservationModel::Noise  ObsrvNoise;
};

template<typename ProcessModel, typename ObservationModel>
class ParticleFilter<ProcessModel, ObservationModel>
    :
    public FilterInterface<ParticleFilter<ProcessModel, ObservationModel>>
{
private:
    typedef ParticleFilter<ProcessModel, ObservationModel> This;

    typedef from_traits(ProcessNoise);
    typedef from_traits(ObsrvNoise);

public:
    typedef from_traits(State);
    typedef from_traits(Input);
    typedef from_traits(Obsrv);
    typedef from_traits(StateDistribution);

public:
    ParticleFilter(const ProcessModel& process_model,
                   const ObservationModel& obsrv_model,
                   const double& max_kl_divergence = 1.0)
        : process_model_(process_model),
          obsrv_model_(obsrv_model),
          process_noise_(process_model.noise_dimension()),
          obsrv_noise_(obsrv_model.noise_dimension()),
          max_kl_divergence_(max_kl_divergence)
    { }

    /// predict ****************************************************************
    virtual void predict(double delta_time,
                         const Input& input,
                         const StateDistribution& prior_dist,
                         StateDistribution& predicted_dist)
    {
        predicted_dist = prior_dist;
        for(size_t i = 0; i < predicted_dist.size(); i++)
        {
            predicted_dist.location(i) =
                    process_model_.predict_state(delta_time,
                                                 prior_dist.location(i),
                                                 process_noise_.sample(),
                                                 input);
        }
    }

    /// update *****************************************************************
    virtual void update(const Obsrv& obsrv,
                        const StateDistribution& predicted_dist,
                        StateDistribution& posterior_dist)
    {
        // if the samples are too concentrated then resample
        if(predicted_dist.kl_given_uniform() > max_kl_divergence_)
        {
            posterior_dist.from_distribution(predicted_dist,
                                             predicted_dist.size());
        }
        else
        {
            posterior_dist = predicted_dist;
        }

        // update the weights of the particles with the likelihoods
        posterior_dist.delta_log_prob_mass(
             obsrv_model_.log_probabilities(obsrv, predicted_dist.locations()));
    }

    /// predict and update *****************************************************
    virtual void predict_and_update(double delta_time,
                                    const Input& input,
                                    const Obsrv& observation,
                                    const StateDistribution& prior_dist,
                                    StateDistribution& posterior_dist)
    {
        predict(delta_time, input, prior_dist, posterior_dist);
        update(observation, posterior_dist, posterior_dist);
    }

    /// set and get ************************************************************
    ProcessModel& process_model()
    {
        return process_model_;
    }
    ObservationModel& obsrv_model()
    {
        return obsrv_model_;
    }

    const ProcessModel& process_model() const
    {
        return process_model_;
    }

    const ObservationModel& obsrv_model() const
    {
        return obsrv_model_;
    }

    /// \todo: should this function be here?
    virtual StateDistribution create_state_distribution() const
    {
        auto state_distr = StateDistribution(process_model().state_dimension());

        return state_distr;
    }

protected:
    ProcessModel process_model_;
    ObservationModel obsrv_model_;

    StandardGaussian<ProcessNoise> process_noise_;
    StandardGaussian<ObsrvNoise> obsrv_noise_;

    // when the KL divergence KL(p||u), where p is the particle distribution
    // and u is the uniform distribution, exceeds max_kl_divergence_, then there
    // is a resampling step. can be understood as -log(f) where f is the
    // fraction of nonzero particles.
    double max_kl_divergence_;
};

}

#endif
<|endoftext|>


<commit_before>#pragma once

#include <string>
#include <vector>
#include <map>
#include <set>

#include "acmacs-base/read-file.hh"
#include "acmacs-base/float.hh"
#include "acmacs-base/sfinae.hh"
#include "acmacs-base/rapidjson.hh"

// ----------------------------------------------------------------------
// Forward declarations
// ----------------------------------------------------------------------

namespace json_writer
{
    template <typename RW> class writer;
    class key;
}

template <typename RW, typename T> json_writer::writer<RW>& operator <<(json_writer::writer<RW>&, const std::vector<T>&);
template <typename RW> json_writer::writer<RW>& operator <<(json_writer::writer<RW>&, const char*);
template <typename RW> json_writer::writer<RW>& operator <<(json_writer::writer<RW>&, std::string);
template <typename RW> json_writer::writer<RW>& operator <<(json_writer::writer<RW>&, double);
template <typename RW> json_writer::writer<RW>& operator <<(json_writer::writer<RW>&, int);
template <typename RW> json_writer::writer<RW>& operator <<(json_writer::writer<RW>&, size_t);
template <typename RW> json_writer::writer<RW>& operator <<(json_writer::writer<RW>&, bool);
template <typename RW, typename Value> inline json_writer::writer<RW>& operator <<(json_writer::writer<RW>& aWriter, const std::vector<Value>& aList);
template <typename RW, typename Value> inline json_writer::writer<RW>& operator <<(json_writer::writer<RW>& aWriter, const std::set<Value>& aList);
template <typename RW, typename Value> json_writer::writer<RW>& operator <<(json_writer::writer<RW>& aWriter, const std::map<std::string, Value>& map_value);
template <typename RW> json_writer::writer<RW>& operator <<(json_writer::writer<RW>&, json_writer::key);

// ======================================================================

namespace json_writer
{
    template <typename RW> class writer : public RW // JsonWriterT
    {
     public:
        inline writer() : RW(mBuffer) {}
        inline std::string to_string() const { return mBuffer.GetString(); }

     private:
        rapidjson::StringBuffer mBuffer;
    };

      // ----------------------------------------------------------------------

    enum _StartArray { start_array };
    enum _EndArray { end_array };
    enum _StartObject { start_object };
    enum _EndObject { end_object };

    class key
    {
     public:
        inline key(const char* v) : value(v) {}
        inline key(std::string v) : value(v) {}
        inline key(char v) : value(1, v) {}
        inline operator const char*() const { return value.c_str(); }
        inline const char* as_char_ptr() const { return value.c_str(); }

     private:
        std::string value;
    };
}

// ----------------------------------------------------------------------

template <typename RW> inline json_writer::writer<RW>& operator <<(json_writer::writer<RW>& aWriter, json_writer::_StartArray) { aWriter.StartArray(); return aWriter; }
template <typename RW> inline json_writer::writer<RW>& operator <<(json_writer::writer<RW>& aWriter, json_writer::_EndArray) { aWriter.EndArray(); return aWriter; }
template <typename RW> inline json_writer::writer<RW>& operator <<(json_writer::writer<RW>& aWriter, json_writer::_StartObject) { aWriter.StartObject(); return aWriter; }
template <typename RW> inline json_writer::writer<RW>& operator <<(json_writer::writer<RW>& aWriter, json_writer::_EndObject) { aWriter.EndObject(); return aWriter; }

template <typename RW> inline json_writer::writer<RW>& operator <<(json_writer::writer<RW>& aWriter, json_writer::key value) { aWriter.Key(value.as_char_ptr()); return aWriter; }

// ----------------------------------------------------------------------
// ----------------------------------------------------------------------

namespace json_writer
{
    template <typename Key, typename Value> class _if_not_empty
    {
     public:
        inline _if_not_empty(Key&& aKey, Value&& aValue) : mKey(aKey), mValue(aValue) {}

        template <typename RW> friend inline writer<RW>& operator <<(writer<RW>& aWriter, const _if_not_empty<Key, Value>& data)
            {
                if (!data.mValue.empty())
                    aWriter << data.mKey << data.mValue;
                return aWriter;
            }

     private:
        Key mKey;
        Value mValue;
    };

    template <typename Key, typename Value> inline auto if_not_empty(Key&& aKey, Value&& aValue) { return _if_not_empty<Key, Value>(std::forward<Key>(aKey), std::forward<Value>(aValue)); }
    template <typename Value> inline auto if_not_empty(const char* aKey, Value&& aValue) { return _if_not_empty<key, Value>(key(aKey), std::forward<Value>(aValue)); }

// ----------------------------------------------------------------------

    template <typename Key, typename Value> class _if_non_negative
    {
     public:
        inline _if_non_negative(Key&& aKey, Value&& aValue) : mKey(aKey), mValue(aValue) {}

        template <typename RW> friend inline writer<RW>& operator <<(writer<RW>& aWriter, const _if_non_negative<Key, Value>& data)
            {
                if (data.mValue >= 0.0)
                    aWriter << data.mKey << data.mValue;
                return aWriter;
            }

     private:
        Key mKey;
        Value mValue;
    };

    template <typename Key, typename Value> inline auto if_non_negative(Key&& aKey, Value&& aValue) { return _if_non_negative<Key, Value>(std::forward<Key>(aKey), std::forward<Value>(aValue)); }
    template <typename Value> inline auto if_non_negative(const char* aKey, Value&& aValue) { return _if_non_negative<key, Value>(key(aKey), std::forward<Value>(aValue)); }

// ----------------------------------------------------------------------

    template <typename Key, typename Value> class _if_not
    {
     public:
        inline _if_not(Key&& aKey, Value&& aValue, bool aPred) : mKey(aKey), mValue(aValue), mPred(aPred) {}
        inline _if_not(Key&& aKey, Value aValue, bool aPred) : mKey(aKey), mValue(aValue), mPred(aPred) {}

        template <typename RW> friend inline writer<RW>& operator <<(writer<RW>& aWriter, const _if_not<Key, Value>& data)
            {
                if (!data.mPred)
                    aWriter << data.mKey << data.mValue;
                return aWriter;
            }

     private:
        Key mKey;
        Value mValue;
        bool mPred;
    };

    template <typename Key> inline auto if_not_zero(Key&& aKey, double aValue) { return _if_not<Key, double>(std::forward<Key>(aKey), aValue, float_zero(aValue)); }
    inline auto if_not_zero(const char* aKey, double aValue) { return _if_not<key, double>(key(aKey), aValue, float_zero(aValue)); }

    template <typename Key> inline auto if_not_one(Key&& aKey, double aValue) { return _if_not<Key, double>(std::forward<Key>(aKey), aValue, float_equal(aValue, 1.0)); }
    inline auto if_not_one(const char* aKey, double aValue) { return _if_not<key, double>(key(aKey), aValue, float_equal(aValue, 1.0)); }

    template <typename Key, typename Value> inline auto if_not_equal(Key&& aKey, Value&& aValue, Value aEq) { return _if_not<Key, Value>(std::forward<Key>(aKey), aValue, aValue == aEq); }
    template <typename Value> inline auto if_not_equal(const char* aKey, Value&& aValue, Value aEq) { return _if_not<key, Value>(key(aKey), aValue, aValue == aEq); }

    template <typename RW, typename List> inline writer<RW>& write_list(writer<RW>& aWriter, const List& aList)
    {
          // start_array and end_array do not work here due to namespace issue (in clang only?)
        aWriter.StartArray();
        for (const auto& e: aList)
            aWriter << e;
        aWriter.EndArray();
        return aWriter;
    }

    template <typename RW, typename List> inline writer<RW>& write_list(writer<RW>& aWriter, List&& aList)
    {
          // start_array and end_array do not work here due to namespace issue (in clang only?)
        aWriter.StartArray();
        for (auto& e: aList)
            aWriter << e;
        aWriter.EndArray();
        return aWriter;
    }

    template <typename RW, typename Object> inline writer<RW>& write_object(writer<RW>& aWriter, const Object& aObject)
    {
          // start_object and end_object do not work here due to namespace issue (in clang only?)
        aWriter.StartObject();
        for (const auto& e: aObject)
            aWriter << json_writer::key(e.first) << e.second;
        aWriter.EndObject();
        return aWriter;
    }

} // namespace json_writer

// ----------------------------------------------------------------------
// ----------------------------------------------------------------------

// to allow writer << func(writer, data) where func returns writer
template <typename RW> inline json_writer::writer<RW>& operator <<(json_writer::writer<RW>& aWriter, json_writer::writer<RW>&) { return aWriter; }

template <typename RW> inline json_writer::writer<RW>& operator <<(json_writer::writer<RW>& aWriter, const char* s) { aWriter.String(s, static_cast<unsigned>(strlen(s))); return aWriter; }
template <typename RW> inline json_writer::writer<RW>& operator <<(json_writer::writer<RW>& aWriter, std::string s) { aWriter.String(s.c_str(), static_cast<unsigned>(s.size())); return aWriter; }
template <typename RW> inline json_writer::writer<RW>& operator <<(json_writer::writer<RW>& aWriter, int value) { aWriter.Int(value); return aWriter; }
template <typename RW> inline json_writer::writer<RW>& operator <<(json_writer::writer<RW>& aWriter, size_t value) { aWriter.Uint64(value); return aWriter; }
template <typename RW> inline json_writer::writer<RW>& operator <<(json_writer::writer<RW>& aWriter, bool value) { aWriter.Bool(value); return aWriter; }
template <typename RW> inline json_writer::writer<RW>& operator <<(json_writer::writer<RW>& aWriter, double value) { aWriter.Double(value); return aWriter; }

template <typename RW, typename Value> inline json_writer::writer<RW>& operator <<(json_writer::writer<RW>& aWriter, const std::vector<Value>& aList)
{
    return json_writer::write_list(aWriter, aList);
}

template <typename RW, typename Value> inline json_writer::writer<RW>& operator <<(json_writer::writer<RW>& aWriter, const std::set<Value>& aList)
{
    return json_writer::write_list(aWriter, aList);
}

template <typename RW, typename Value> inline json_writer::writer<RW>& operator <<(json_writer::writer<RW>& aWriter, const std::map<std::string, Value>& map_value)
{
    return json_writer::write_object(aWriter, map_value);
}

// ----------------------------------------------------------------------
// ----------------------------------------------------------------------

namespace json_writer
{
    enum _Finalize { finalize };

    class pretty;
}

std::string operator << (json_writer::pretty& aWriter, json_writer::_Finalize);

namespace json_writer
{
    inline void insert_emacs_indent_hint(std::string& aJson, size_t aIndent)
    {
        if (aJson.size() > 2 && aJson[0] == '{') {
            const std::string ind(aIndent - 1, ' ');
            aJson.insert(1, ind + "\"_\": \"-*- js-indent-level: " + std::to_string(aIndent) + " -*-\",");
        }
    }

    using compact = writer<rapidjson::Writer<rapidjson::StringBuffer>>;

    class pretty : public writer<rapidjson::PrettyWriter<rapidjson::StringBuffer>>
    {
     public:
        inline pretty(size_t indent = 1) : writer<rapidjson::PrettyWriter<rapidjson::StringBuffer>>{}, mIndent{indent}
            {
                SetIndent(' ', static_cast<unsigned int>(indent));
            }

        inline size_t indent() const { return mIndent; }
        inline operator std::string() { return *this << finalize; }

     private:
        size_t mIndent;

    }; // class pretty

} // namespace json_writer

// ----------------------------------------------------------------------

inline std::string operator << (json_writer::compact& aWriter, json_writer::_Finalize)
{
    return aWriter.to_string();
}

inline std::string operator << (json_writer::pretty& aWriter, json_writer::_Finalize)
{
    std::string result = aWriter.to_string();
    json_writer::insert_emacs_indent_hint(result, aWriter.indent());
    return result;
}

// ----------------------------------------------------------------------

namespace json_writer
{
    template <typename V> inline std::string pretty_json(const V& value, size_t indent)
    {
        pretty aWriter{indent};
        aWriter << value;
        return aWriter << finalize;
    }

    template <typename V> inline std::string compact_json(const V& aValue)
    {
        compact aWriter;
        aWriter << aValue;
        return aWriter << finalize;
    }

    template <typename V> inline std::string json(const V& value, size_t indent)
    {
        if (indent)
            return pretty_json(value, indent);
        else
            return compact_json(value);
    }

      // ----------------------------------------------------------------------

    using acmacs::file::force_compression;

    template <typename V> inline void export_to_json(const V& value, std::string filename, size_t indent, force_compression a_force_compression = force_compression::no)
    {
        acmacs::file::write(filename, json(value, indent), a_force_compression);
    }

} // namespace json_writer

// ----------------------------------------------------------------------
/// Local Variables:
/// eval: (if (fboundp 'eu-rename-buffer) (eu-rename-buffer))
/// End:
<commit_msg>using std::string_view<commit_after>#pragma once

#include <string>
#include <vector>
#include <map>
#include <set>

#include "acmacs-base/read-file.hh"
#include "acmacs-base/float.hh"
#include "acmacs-base/sfinae.hh"
#include "acmacs-base/rapidjson.hh"

// ----------------------------------------------------------------------
// Forward declarations
// ----------------------------------------------------------------------

namespace json_writer
{
    template <typename RW> class writer;
    class key;
}

template <typename RW, typename T> json_writer::writer<RW>& operator <<(json_writer::writer<RW>&, const std::vector<T>&);
template <typename RW> json_writer::writer<RW>& operator <<(json_writer::writer<RW>&, const char*);
template <typename RW> json_writer::writer<RW>& operator <<(json_writer::writer<RW>&, std::string);
template <typename RW> json_writer::writer<RW>& operator <<(json_writer::writer<RW>&, double);
template <typename RW> json_writer::writer<RW>& operator <<(json_writer::writer<RW>&, int);
template <typename RW> json_writer::writer<RW>& operator <<(json_writer::writer<RW>&, size_t);
template <typename RW> json_writer::writer<RW>& operator <<(json_writer::writer<RW>&, bool);
template <typename RW, typename Value> inline json_writer::writer<RW>& operator <<(json_writer::writer<RW>& aWriter, const std::vector<Value>& aList);
template <typename RW, typename Value> inline json_writer::writer<RW>& operator <<(json_writer::writer<RW>& aWriter, const std::set<Value>& aList);
template <typename RW, typename Value> json_writer::writer<RW>& operator <<(json_writer::writer<RW>& aWriter, const std::map<std::string, Value>& map_value);
template <typename RW> json_writer::writer<RW>& operator <<(json_writer::writer<RW>&, json_writer::key);

// ======================================================================

namespace json_writer
{
    template <typename RW> class writer : public RW // JsonWriterT
    {
     public:
        inline writer() : RW(mBuffer) {}
        inline std::string to_string() const { return mBuffer.GetString(); }

     private:
        rapidjson::StringBuffer mBuffer;
    };

      // ----------------------------------------------------------------------

    enum _StartArray { start_array };
    enum _EndArray { end_array };
    enum _StartObject { start_object };
    enum _EndObject { end_object };

    class key
    {
     public:
        inline key(const char* v) : value(v) {}
        inline key(std::string v) : value(v) {}
        inline key(char v) : value(1, v) {}
        inline operator const char*() const { return value.c_str(); }
        inline const char* as_char_ptr() const { return value.c_str(); }

     private:
        std::string value;
    };
}

// ----------------------------------------------------------------------

template <typename RW> inline json_writer::writer<RW>& operator <<(json_writer::writer<RW>& aWriter, json_writer::_StartArray) { aWriter.StartArray(); return aWriter; }
template <typename RW> inline json_writer::writer<RW>& operator <<(json_writer::writer<RW>& aWriter, json_writer::_EndArray) { aWriter.EndArray(); return aWriter; }
template <typename RW> inline json_writer::writer<RW>& operator <<(json_writer::writer<RW>& aWriter, json_writer::_StartObject) { aWriter.StartObject(); return aWriter; }
template <typename RW> inline json_writer::writer<RW>& operator <<(json_writer::writer<RW>& aWriter, json_writer::_EndObject) { aWriter.EndObject(); return aWriter; }

template <typename RW> inline json_writer::writer<RW>& operator <<(json_writer::writer<RW>& aWriter, json_writer::key value) { aWriter.Key(value.as_char_ptr()); return aWriter; }

// ----------------------------------------------------------------------
// ----------------------------------------------------------------------

namespace json_writer
{
    template <typename Key, typename Value> class _if_not_empty
    {
     public:
        inline _if_not_empty(Key&& aKey, Value&& aValue) : mKey(aKey), mValue(aValue) {}

        template <typename RW> friend inline writer<RW>& operator <<(writer<RW>& aWriter, const _if_not_empty<Key, Value>& data)
            {
                if (!data.mValue.empty())
                    aWriter << data.mKey << data.mValue;
                return aWriter;
            }

     private:
        Key mKey;
        Value mValue;
    };

    template <typename Key, typename Value> inline auto if_not_empty(Key&& aKey, Value&& aValue) { return _if_not_empty<Key, Value>(std::forward<Key>(aKey), std::forward<Value>(aValue)); }
    template <typename Value> inline auto if_not_empty(const char* aKey, Value&& aValue) { return _if_not_empty<key, Value>(key(aKey), std::forward<Value>(aValue)); }

// ----------------------------------------------------------------------

    template <typename Key, typename Value> class _if_non_negative
    {
     public:
        inline _if_non_negative(Key&& aKey, Value&& aValue) : mKey(aKey), mValue(aValue) {}

        template <typename RW> friend inline writer<RW>& operator <<(writer<RW>& aWriter, const _if_non_negative<Key, Value>& data)
            {
                if (data.mValue >= 0.0)
                    aWriter << data.mKey << data.mValue;
                return aWriter;
            }

     private:
        Key mKey;
        Value mValue;
    };

    template <typename Key, typename Value> inline auto if_non_negative(Key&& aKey, Value&& aValue) { return _if_non_negative<Key, Value>(std::forward<Key>(aKey), std::forward<Value>(aValue)); }
    template <typename Value> inline auto if_non_negative(const char* aKey, Value&& aValue) { return _if_non_negative<key, Value>(key(aKey), std::forward<Value>(aValue)); }

// ----------------------------------------------------------------------

    template <typename Key, typename Value> class _if_not
    {
     public:
        inline _if_not(Key&& aKey, Value&& aValue, bool aPred) : mKey(aKey), mValue(aValue), mPred(aPred) {}
        inline _if_not(Key&& aKey, Value aValue, bool aPred) : mKey(aKey), mValue(aValue), mPred(aPred) {}

        template <typename RW> friend inline writer<RW>& operator <<(writer<RW>& aWriter, const _if_not<Key, Value>& data)
            {
                if (!data.mPred)
                    aWriter << data.mKey << data.mValue;
                return aWriter;
            }

     private:
        Key mKey;
        Value mValue;
        bool mPred;
    };

    template <typename Key> inline auto if_not_zero(Key&& aKey, double aValue) { return _if_not<Key, double>(std::forward<Key>(aKey), aValue, float_zero(aValue)); }
    inline auto if_not_zero(const char* aKey, double aValue) { return _if_not<key, double>(key(aKey), aValue, float_zero(aValue)); }

    template <typename Key> inline auto if_not_one(Key&& aKey, double aValue) { return _if_not<Key, double>(std::forward<Key>(aKey), aValue, float_equal(aValue, 1.0)); }
    inline auto if_not_one(const char* aKey, double aValue) { return _if_not<key, double>(key(aKey), aValue, float_equal(aValue, 1.0)); }

    template <typename Key, typename Value> inline auto if_not_equal(Key&& aKey, Value&& aValue, Value aEq) { return _if_not<Key, Value>(std::forward<Key>(aKey), aValue, aValue == aEq); }
    template <typename Value> inline auto if_not_equal(const char* aKey, Value&& aValue, Value aEq) { return _if_not<key, Value>(key(aKey), aValue, aValue == aEq); }

    template <typename RW, typename List> inline writer<RW>& write_list(writer<RW>& aWriter, const List& aList)
    {
          // start_array and end_array do not work here due to namespace issue (in clang only?)
        aWriter.StartArray();
        for (const auto& e: aList)
            aWriter << e;
        aWriter.EndArray();
        return aWriter;
    }

    template <typename RW, typename List> inline writer<RW>& write_list(writer<RW>& aWriter, List&& aList)
    {
          // start_array and end_array do not work here due to namespace issue (in clang only?)
        aWriter.StartArray();
        for (auto& e: aList)
            aWriter << e;
        aWriter.EndArray();
        return aWriter;
    }

    template <typename RW, typename Object> inline writer<RW>& write_object(writer<RW>& aWriter, const Object& aObject)
    {
          // start_object and end_object do not work here due to namespace issue (in clang only?)
        aWriter.StartObject();
        for (const auto& e: aObject)
            aWriter << json_writer::key(e.first) << e.second;
        aWriter.EndObject();
        return aWriter;
    }

} // namespace json_writer

// ----------------------------------------------------------------------
// ----------------------------------------------------------------------

// to allow writer << func(writer, data) where func returns writer
template <typename RW> inline json_writer::writer<RW>& operator <<(json_writer::writer<RW>& aWriter, json_writer::writer<RW>&) { return aWriter; }

template <typename RW> inline json_writer::writer<RW>& operator <<(json_writer::writer<RW>& aWriter, const char* s) { aWriter.String(s, static_cast<unsigned>(strlen(s))); return aWriter; }
template <typename RW> inline json_writer::writer<RW>& operator <<(json_writer::writer<RW>& aWriter, std::string s) { aWriter.String(s.c_str(), static_cast<unsigned>(s.size())); return aWriter; }
template <typename RW> inline json_writer::writer<RW>& operator <<(json_writer::writer<RW>& aWriter, int value) { aWriter.Int(value); return aWriter; }
template <typename RW> inline json_writer::writer<RW>& operator <<(json_writer::writer<RW>& aWriter, size_t value) { aWriter.Uint64(value); return aWriter; }
template <typename RW> inline json_writer::writer<RW>& operator <<(json_writer::writer<RW>& aWriter, bool value) { aWriter.Bool(value); return aWriter; }
template <typename RW> inline json_writer::writer<RW>& operator <<(json_writer::writer<RW>& aWriter, double value) { aWriter.Double(value); return aWriter; }

template <typename RW, typename Value> inline json_writer::writer<RW>& operator <<(json_writer::writer<RW>& aWriter, const std::vector<Value>& aList)
{
    return json_writer::write_list(aWriter, aList);
}

template <typename RW, typename Value> inline json_writer::writer<RW>& operator <<(json_writer::writer<RW>& aWriter, const std::set<Value>& aList)
{
    return json_writer::write_list(aWriter, aList);
}

template <typename RW, typename Value> inline json_writer::writer<RW>& operator <<(json_writer::writer<RW>& aWriter, const std::map<std::string, Value>& map_value)
{
    return json_writer::write_object(aWriter, map_value);
}

// ----------------------------------------------------------------------
// ----------------------------------------------------------------------

namespace json_writer
{
    enum _Finalize { finalize };

    class pretty;
}

std::string operator << (json_writer::pretty& aWriter, json_writer::_Finalize);

namespace json_writer
{
    inline void insert_emacs_indent_hint(std::string& aJson, size_t aIndent)
    {
        if (aJson.size() > 2 && aJson[0] == '{') {
            const std::string ind(aIndent - 1, ' ');
            aJson.insert(1, ind + "\"_\": \"-*- js-indent-level: " + std::to_string(aIndent) + " -*-\",");
        }
    }

    using compact = writer<rapidjson::Writer<rapidjson::StringBuffer>>;

    class pretty : public writer<rapidjson::PrettyWriter<rapidjson::StringBuffer>>
    {
     public:
        inline pretty(size_t indent = 1) : writer<rapidjson::PrettyWriter<rapidjson::StringBuffer>>{}, mIndent{indent}
            {
                SetIndent(' ', static_cast<unsigned int>(indent));
            }

        inline size_t indent() const { return mIndent; }
        inline operator std::string() { return *this << finalize; }

     private:
        size_t mIndent;

    }; // class pretty

} // namespace json_writer

// ----------------------------------------------------------------------

inline std::string operator << (json_writer::compact& aWriter, json_writer::_Finalize)
{
    return aWriter.to_string();
}

inline std::string operator << (json_writer::pretty& aWriter, json_writer::_Finalize)
{
    std::string result = aWriter.to_string();
    json_writer::insert_emacs_indent_hint(result, aWriter.indent());
    return result;
}

// ----------------------------------------------------------------------

namespace json_writer
{
    template <typename V> inline std::string pretty_json(const V& value, size_t indent)
    {
        pretty aWriter{indent};
        aWriter << value;
        return aWriter << finalize;
    }

    template <typename V> inline std::string compact_json(const V& aValue)
    {
        compact aWriter;
        aWriter << aValue;
        return aWriter << finalize;
    }

    template <typename V> inline std::string json(const V& value, size_t indent)
    {
        if (indent)
            return pretty_json(value, indent);
        else
            return compact_json(value);
    }

      // ----------------------------------------------------------------------

    using acmacs::file::force_compression;

    template <typename V> inline void export_to_json(const V& value, std::string_view filename, size_t indent, force_compression a_force_compression = force_compression::no)
    {
        acmacs::file::write(filename, json(value, indent), a_force_compression);
    }

} // namespace json_writer

// ----------------------------------------------------------------------
/// Local Variables:
/// eval: (if (fboundp 'eu-rename-buffer) (eu-rename-buffer))
/// End:
<|endoftext|>


<commit_before>#pragma once

#include <iostream>
#include <iomanip>

#include "acmacs-base/date2.hh"
#include "acmacs-base/week2.hh"

// ----------------------------------------------------------------------

class Date
{
 public:
    enum Today { Today };

    Date() : date_(date::year(9999), date::month(99), date::day(99)) {}         // invalid date by default
    Date(const Date&) = default;
    Date(enum Today) : date_{date::floor<date::days>(std::chrono::system_clock::now())} {}
    Date(const char* aText) { from_string(std::string(aText)); }
    Date(std::string aText) { from_string(aText); }
    Date(std::string_view aText) { from_string(aText); }
    Date(const date::year_month_day& src) : date_(src) {}
    Date(const date::sys_days& src) : date_(src) {}
    Date(int year, unsigned month, unsigned day) : date_(date::year(year), date::month(month), date::day(day)) {}
    static inline Date today() { return Today; }

    Date& operator=(const Date&) = default;
    Date& operator=(std::string aText) { if (!aText.empty()) from_string(aText); return *this; }
    Date& operator=(std::string_view aText) { if (!aText.empty()) from_string(aText); return *this; }
    Date& operator=(const char* aText) { if (aText && *aText) from_string(std::string_view(aText)); return *this; }

    Date months_ago(int number_of_months) const { return date_ - date::months(number_of_months); }
    Date years_ago(int number_of_years) const { return date_ - date::years(number_of_years); }
    Date weeks_ago(int number_of_weeks) const { return static_cast<date::sys_days>(date_) - date::weeks(number_of_weeks); }

    bool operator < (const Date& d) const { return date_ < d.date_; }
    bool operator == (const Date& d) const { return date_ == d.date_; }
    bool operator >= (const Date& d) const { return date_ >= d.date_; }

    bool empty() const { return !date_.ok(); }
    operator bool() const { return date_.ok(); }
    auto year() const { return static_cast<int>(date_.year()); }
    auto month() const { return static_cast<unsigned>(date_.month()); }
    auto day() const { return static_cast<unsigned>(date_.day()); }

    std::string display() const { return date_.ok() ? date::format("%Y-%m-%d", date_) : std::string{"*invalid-date*"}; }
    operator std::string() const { return display(); }

      // returns date for the 1st day of the year-month stored in this
    Date beginning_of_month() const { return date::year_month_day(date_.year(), date_.month(), date::day{1}); }
    Date beginning_of_year() const { return date::year_month_day(date_.year(), date::month{1}, date::day{1}); }
    Date beginning_of_week() const
        {
            iso_week::year_weeknum_weekday yw(date_);
            return {iso_week::year_weeknum_weekday(yw.year(), yw.weeknum(), iso_week::weekday{date::Monday})};
        }

    Date& increment_month(int number_of_months = 1) { date_ += date::months(number_of_months); return *this; }
    Date& decrement_month(int number_of_months = 1) { date_ -= date::months(number_of_months); return *this; }
    Date& increment_year(int number_of_years = 1) { date_ += date::years(number_of_years); return *this; }
    Date& decrement_year(int number_of_years = 1) { date_ -= date::years(number_of_years); return *this; }
    Date& increment_week(int number_of_weeks = 1) { date_ = static_cast<date::sys_days>(date_) + date::weeks(number_of_weeks); return *this; }
    Date& decrement_week(int number_of_weeks = 1) { date_ = static_cast<date::sys_days>(date_) - date::weeks(number_of_weeks); return *this; }

    Date next_month() const { return date_ + date::months(1); }
    Date next_year() const { return date_ + date::years(1); }
    Date next_week() const { return {static_cast<date::sys_days>(date_) + date::weeks(1)}; }

    std::string month_3() const { return date::format("%b", date_); }
    std::string year_2() const { return date::format("%y", date_); }
    std::string year_4() const { return date::format("%Y", date_); }
    std::string month3_year2() const { return date::format("%b %y", date_); }
    std::string monthtext_year() const { return date::format("%B %Y", date_); }
    std::string year4_month2() const { return date::format("%Y-%m", date_); }
    std::string year4_month2_day2() const { return date::format("%Y-%m-%d", date_); }

      //? const auto& gregorian() const { return date_; }

 private:
    date::year_month_day date_;

    void from_string(std::string_view source)
        {
            from_string(std::string(source));
        }

    void from_string(std::string source)
        {
            using namespace date::literals;
            date_ = 1999_y/99/99;
            for (const char* format : {"%Y-%m-%d", "%m/%d/%Y", "%d/%m/%Y", "%B%n %d%n %Y", "%B %d,%n %Y", "%b%n %d%n %Y", "%b %d,%n %Y"}) {
                std::istringstream in(source);
                in >> date::parse(format, date_);
                if (in) {
                    if (date_.year() < date::year{30})
                        date_ += date::years(2000);
                    else if (date_.year() < date::year{100})
                        date_ += date::years(1900);
                    return;
                }
            }
            std::cerr << "ERROR: cannot parse date from \"" << source << "\"\n";
        }

 public:
    friend inline int days_between_dates(const Date& a, const Date& b)
        {
            return std::chrono::duration_cast<date::days>(static_cast<date::sys_days>(b.date_) - static_cast<date::sys_days>(a.date_)).count();
        }

    friend inline int weeks_between_dates(const Date& a, const Date& b)
        {
            return std::chrono::duration_cast<date::weeks>(static_cast<date::sys_days>(b.date_) - static_cast<date::sys_days>(a.date_)).count();
        }

    friend inline int months_between_dates(const Date& a, const Date& b)
        {
            return std::chrono::duration_cast<date::months>(static_cast<date::sys_days>(b.date_) - static_cast<date::sys_days>(a.date_)).count();
        }

    friend inline int years_between_dates(const Date& a, const Date& b)
        {
            return std::chrono::duration_cast<date::years>(static_cast<date::sys_days>(b.date_) - static_cast<date::sys_days>(a.date_)).count();
        }

}; // class Date

// ----------------------------------------------------------------------

inline std::ostream& operator << (std::ostream& out, const Date& aDate)
{
    return out << aDate.display();
}

// ----------------------------------------------------------------------

// https://stackoverflow.com/questions/17223096/outputting-date-and-time-in-c-using-stdchrono
inline std::string current_date_time()
{
    const auto now = std::chrono::system_clock::now();
    const auto in_time_t = std::chrono::system_clock::to_time_t(now);
    std::stringstream ss;
    ss << std::put_time(std::localtime(&in_time_t), "%Y-%m-%d %H:%M:%S %Z");
    return ss.str();
}

// ----------------------------------------------------------------------
/// Local Variables:
/// eval: (if (fboundp 'eu-rename-buffer) (eu-rename-buffer))
/// End:
<commit_msg>Date: added missing comparing operators<commit_after>#pragma once

#include <iostream>
#include <iomanip>

#include "acmacs-base/date2.hh"
#include "acmacs-base/week2.hh"

// ----------------------------------------------------------------------

class Date
{
 public:
    enum Today { Today };

    Date() : date_(date::year(9999), date::month(99), date::day(99)) {}         // invalid date by default
    Date(const Date&) = default;
    Date(enum Today) : date_{date::floor<date::days>(std::chrono::system_clock::now())} {}
    Date(const char* aText) { from_string(std::string(aText)); }
    Date(std::string aText) { from_string(aText); }
    Date(std::string_view aText) { from_string(aText); }
    Date(const date::year_month_day& src) : date_(src) {}
    Date(const date::sys_days& src) : date_(src) {}
    Date(int year, unsigned month, unsigned day) : date_(date::year(year), date::month(month), date::day(day)) {}
    static inline Date today() { return Today; }

    Date& operator=(const Date&) = default;
    Date& operator=(std::string aText) { if (!aText.empty()) from_string(aText); return *this; }
    Date& operator=(std::string_view aText) { if (!aText.empty()) from_string(aText); return *this; }
    Date& operator=(const char* aText) { if (aText && *aText) from_string(std::string_view(aText)); return *this; }

    Date months_ago(int number_of_months) const { return date_ - date::months(number_of_months); }
    Date years_ago(int number_of_years) const { return date_ - date::years(number_of_years); }
    Date weeks_ago(int number_of_weeks) const { return static_cast<date::sys_days>(date_) - date::weeks(number_of_weeks); }

    bool operator < (const Date& d) const { return date_ < d.date_; }
    bool operator > (const Date& d) const { return date_ > d.date_; }
    bool operator == (const Date& d) const { return date_ == d.date_; }
    bool operator != (const Date& d) const { return date_ != d.date_; }
    bool operator <= (const Date& d) const { return date_ <= d.date_; }
    bool operator >= (const Date& d) const { return date_ >= d.date_; }

    bool empty() const { return !date_.ok(); }
    operator bool() const { return date_.ok(); }
    auto year() const { return static_cast<int>(date_.year()); }
    auto month() const { return static_cast<unsigned>(date_.month()); }
    auto day() const { return static_cast<unsigned>(date_.day()); }

    std::string display() const { return date_.ok() ? date::format("%Y-%m-%d", date_) : std::string{"*invalid-date*"}; }
    operator std::string() const { return display(); }

      // returns date for the 1st day of the year-month stored in this
    Date beginning_of_month() const { return date::year_month_day(date_.year(), date_.month(), date::day{1}); }
    Date beginning_of_year() const { return date::year_month_day(date_.year(), date::month{1}, date::day{1}); }
    Date beginning_of_week() const
        {
            iso_week::year_weeknum_weekday yw(date_);
            return {iso_week::year_weeknum_weekday(yw.year(), yw.weeknum(), iso_week::weekday{date::Monday})};
        }

    Date& increment_month(int number_of_months = 1) { date_ += date::months(number_of_months); return *this; }
    Date& decrement_month(int number_of_months = 1) { date_ -= date::months(number_of_months); return *this; }
    Date& increment_year(int number_of_years = 1) { date_ += date::years(number_of_years); return *this; }
    Date& decrement_year(int number_of_years = 1) { date_ -= date::years(number_of_years); return *this; }
    Date& increment_week(int number_of_weeks = 1) { date_ = static_cast<date::sys_days>(date_) + date::weeks(number_of_weeks); return *this; }
    Date& decrement_week(int number_of_weeks = 1) { date_ = static_cast<date::sys_days>(date_) - date::weeks(number_of_weeks); return *this; }

    Date next_month() const { return date_ + date::months(1); }
    Date next_year() const { return date_ + date::years(1); }
    Date next_week() const { return {static_cast<date::sys_days>(date_) + date::weeks(1)}; }

    std::string month_3() const { return date::format("%b", date_); }
    std::string year_2() const { return date::format("%y", date_); }
    std::string year_4() const { return date::format("%Y", date_); }
    std::string month3_year2() const { return date::format("%b %y", date_); }
    std::string monthtext_year() const { return date::format("%B %Y", date_); }
    std::string year4_month2() const { return date::format("%Y-%m", date_); }
    std::string year4_month2_day2() const { return date::format("%Y-%m-%d", date_); }

      //? const auto& gregorian() const { return date_; }

 private:
    date::year_month_day date_;

    void from_string(std::string_view source)
        {
            from_string(std::string(source));
        }

    void from_string(std::string source)
        {
            using namespace date::literals;
            date_ = 1999_y/99/99;
            for (const char* format : {"%Y-%m-%d", "%m/%d/%Y", "%d/%m/%Y", "%B%n %d%n %Y", "%B %d,%n %Y", "%b%n %d%n %Y", "%b %d,%n %Y"}) {
                std::istringstream in(source);
                in >> date::parse(format, date_);
                if (in) {
                    if (date_.year() < date::year{30})
                        date_ += date::years(2000);
                    else if (date_.year() < date::year{100})
                        date_ += date::years(1900);
                    return;
                }
            }
            std::cerr << "ERROR: cannot parse date from \"" << source << "\"\n";
        }

 public:
    friend inline int days_between_dates(const Date& a, const Date& b)
        {
            return std::chrono::duration_cast<date::days>(static_cast<date::sys_days>(b.date_) - static_cast<date::sys_days>(a.date_)).count();
        }

    friend inline int weeks_between_dates(const Date& a, const Date& b)
        {
            return std::chrono::duration_cast<date::weeks>(static_cast<date::sys_days>(b.date_) - static_cast<date::sys_days>(a.date_)).count();
        }

    friend inline int months_between_dates(const Date& a, const Date& b)
        {
            return std::chrono::duration_cast<date::months>(static_cast<date::sys_days>(b.date_) - static_cast<date::sys_days>(a.date_)).count();
        }

    friend inline int years_between_dates(const Date& a, const Date& b)
        {
            return std::chrono::duration_cast<date::years>(static_cast<date::sys_days>(b.date_) - static_cast<date::sys_days>(a.date_)).count();
        }

}; // class Date

// ----------------------------------------------------------------------

inline std::ostream& operator << (std::ostream& out, const Date& aDate)
{
    return out << aDate.display();
}

// ----------------------------------------------------------------------

// https://stackoverflow.com/questions/17223096/outputting-date-and-time-in-c-using-stdchrono
inline std::string current_date_time()
{
    const auto now = std::chrono::system_clock::now();
    const auto in_time_t = std::chrono::system_clock::to_time_t(now);
    std::stringstream ss;
    ss << std::put_time(std::localtime(&in_time_t), "%Y-%m-%d %H:%M:%S %Z");
    return ss.str();
}

// ----------------------------------------------------------------------
/// Local Variables:
/// eval: (if (fboundp 'eu-rename-buffer) (eu-rename-buffer))
/// End:
<|endoftext|>


<commit_before>/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * Copyright 2000, 2010 Oracle and/or its affiliates.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/

#include <svsys.h>

#include <vcl/menu.hxx>
#include <vcl/sysdata.hxx>

#include <win/wincomp.hxx>
#include <win/saldata.hxx>
#include <win/salinst.h>
#include <win/salframe.h>
#include <win/salmenu.h>

#include <impbmp.hxx>
#include <salgdi.hxx>

// uncomment the following line to have ownerdrawn menus, ie, with bitmaps
// however, this is incompatible with OLE inplace editing
// so it is not activated by default
//#define OWNERDRAW

static DWORD myerr=0;

// =======================================================================

sal_Bool SalData::IsKnownMenuHandle( HMENU hMenu )
{
    if( mhMenuSet.find( hMenu ) == mhMenuSet.end() )
        return FALSE;
    else
        return TRUE;
}

// =======================================================================

// WinSalInst factory methods

SalMenu* WinSalInstance::CreateMenu( sal_Bool bMenuBar, Menu* )
{
    WinSalMenu *pSalMenu = new WinSalMenu();

    pSalMenu->mbMenuBar = bMenuBar;
    pSalMenu->mhWnd     = NULL;
    if( bMenuBar )
        pSalMenu->mhMenu = ::CreateMenu();
    else
        pSalMenu->mhMenu = ::CreatePopupMenu();

    if( pSalMenu->mhMenu )
        GetSalData()->mhMenuSet.insert( pSalMenu->mhMenu );

    return pSalMenu;
}

void WinSalInstance::DestroyMenu( SalMenu* pSalMenu )
{
    delete pSalMenu;
}

SalMenuItem* WinSalInstance::CreateMenuItem( const SalItemParams* pItemData )
{
    if( !pItemData )
        return NULL;

    WinSalMenuItem *pSalMenuItem = new WinSalMenuItem();
    memset( &pSalMenuItem->mInfo, 0, sizeof( MENUITEMINFOW ) );
    pSalMenuItem->mInfo.cbSize = sizeof( MENUITEMINFOW );

    if( pItemData->eType == MENUITEM_SEPARATOR )
    {
        // separator
        pSalMenuItem->mInfo.fMask = MIIM_TYPE;
        pSalMenuItem->mInfo.fType = MFT_SEPARATOR;
    }
    else
    {
        // item
        pSalMenuItem->mText   = pItemData->aText;
        pSalMenuItem->mpMenu  = pItemData->pMenu;
        pSalMenuItem->maBitmap= !!pItemData->aImage ? pItemData->aImage.GetBitmapEx().GetBitmap() : Bitmap();
        pSalMenuItem->mnId    = pItemData->nId;

        // 'translate' mnemonics
        pSalMenuItem->mText.SearchAndReplace( '~', '&' );

        pSalMenuItem->mInfo.fMask = MIIM_TYPE | MIIM_STATE | MIIM_ID | MIIM_DATA;
        pSalMenuItem->mInfo.fType = MFT_STRING;
#ifdef OWNERDRAW
        if( pItemData->pMenu && !pItemData->pMenu->IsMenuBar() )
            pSalMenuItem->mInfo.fType |= MFT_OWNERDRAW;
        pSalMenuItem->mInfo.fState = MFS_ENABLED;
#endif
        pSalMenuItem->mInfo.dwTypeData = (LPWSTR) pSalMenuItem->mText.GetBuffer();
        pSalMenuItem->mInfo.cch = pSalMenuItem->mText.Len();

        pSalMenuItem->mInfo.wID = pItemData->nId;
        pSalMenuItem->mInfo.dwItemData = (ULONG_PTR) pSalMenuItem; // user data
    }

    return pSalMenuItem;
}

void WinSalInstance::DestroyMenuItem( SalMenuItem* pSalMenuItem )
{
    delete pSalMenuItem;
}

// =======================================================================

static void ImplDrawMenuBar( SalMenu *pMenu )
{
    if( pMenu->VisibleMenuBar() )
    {
        // redrawing the menubar all the time actually seems to be unnecessary (it just flickers)
        /*
        WinSalMenu *pMenuBar = ImplFindMenuBar( pMenu );
        if( pMenuBar && pMenuBar->mhWnd )
            ::DrawMenuBar( pMenuBar->mhWnd );
            */
    }
}

// =======================================================================

/*
 * WinSalMenu
 */

WinSalMenu::WinSalMenu()
{
    mhMenu       = NULL;
    mbMenuBar    = FALSE;
    mhWnd        = NULL;
    mpParentMenu = NULL;
}

WinSalMenu::~WinSalMenu()
{
    // only required if not associated to a window...
    GetSalData()->mhMenuSet.erase( mhMenu );
    ::DestroyMenu( mhMenu );
}

sal_Bool WinSalMenu::VisibleMenuBar()
{
    // The Win32 implementation never shows a native
    // menubar. Thus, native menus are only visible
    // when the menu is merged with an OLE container.
    // The reason are missing tooltips, ownerdraw
    // issues and accessibility which are better supported
    // by VCL menus.
    // Nevertheless, the native menus are always created
    // and the application will properly react to all native
    // menu messages.

    return FALSE;
}

void WinSalMenu::SetFrame( const SalFrame *pFrame )
{
    if( pFrame )
        mhWnd = static_cast<const WinSalFrame*>(pFrame)->mhWnd;
    else
        mhWnd = NULL;
}

void WinSalMenu::InsertItem( SalMenuItem* pSalMenuItem, unsigned nPos )
{
    if( pSalMenuItem )
    {
    WinSalMenuItem* pWItem = static_cast<WinSalMenuItem*>(pSalMenuItem);
        if( nPos == MENU_APPEND )
        {
            nPos = ::GetMenuItemCount( mhMenu );
            if( nPos == -1 )
                return;
        }

        if(!::InsertMenuItemW( mhMenu, nPos, TRUE, &pWItem->mInfo ))
            myerr = GetLastError();
        else
        {
            pWItem->mpSalMenu = this;
            ImplDrawMenuBar( this );
        }
    }
}

void WinSalMenu::RemoveItem( unsigned nPos )
{
    int num = ::GetMenuItemCount( mhMenu );
    if( num != -1 && nPos < (unsigned)num )
    {
        WinSalMenuItem *pSalMenuItem = NULL;

        MENUITEMINFOW mi;
        memset( &mi, 0, sizeof(mi) );
        mi.cbSize = sizeof( mi );
        mi.fMask = MIIM_DATA;
        if( !GetMenuItemInfoW( mhMenu, nPos, TRUE, &mi) )
            myerr = GetLastError();
        else
            pSalMenuItem = (WinSalMenuItem *) mi.dwItemData;

        if( !::RemoveMenu( mhMenu, nPos, MF_BYPOSITION ) )
            myerr = GetLastError();
        else
        {
            if( pSalMenuItem )
                pSalMenuItem->mpSalMenu = NULL;
            ImplDrawMenuBar( this );
        }
    }
}

void ImplRemoveItemById( WinSalMenu *pSalMenu, unsigned nItemId )
{
    if( !pSalMenu )
        return;

    WinSalMenuItem *pSalMenuItem = NULL;

    MENUITEMINFOW mi;
    memset( &mi, 0, sizeof(mi) );
    mi.cbSize = sizeof( mi );
    mi.fMask = MIIM_DATA;
    if( !GetMenuItemInfoW( pSalMenu->mhMenu, nItemId, FALSE, &mi) )
        myerr = GetLastError();
    else
        pSalMenuItem = (WinSalMenuItem *) mi.dwItemData;

    if( !::RemoveMenu( pSalMenu->mhMenu, nItemId, MF_BYCOMMAND ) )
        myerr = GetLastError();
    else
    {
        if( pSalMenuItem )
            pSalMenuItem->mpSalMenu = NULL;
        ImplDrawMenuBar( pSalMenu );
    }
}

void WinSalMenu::SetSubMenu( SalMenuItem* pSalMenuItem, SalMenu* pSubMenu, unsigned nPos )
{
    if( pSalMenuItem )
    {
        WinSalMenuItem* pWMenuItem = static_cast<WinSalMenuItem*>(pSalMenuItem);
        WinSalMenu* pWSubMenu = static_cast<WinSalMenu*>(pSubMenu);
        if( pWMenuItem->mInfo.hSubMenu )
        {
            GetSalData()->mhMenuSet.erase( pWMenuItem->mInfo.hSubMenu );
            ::DestroyMenu( pWMenuItem->mInfo.hSubMenu );
        }

        pWMenuItem->mInfo.fMask |= MIIM_SUBMENU;
        if( !pSubMenu )
            pWMenuItem->mInfo.hSubMenu = NULL;
        else
        {
            pWMenuItem->mInfo.hSubMenu = pWSubMenu->mhMenu;
            pWSubMenu->mpParentMenu = this;
        }

        if(!::SetMenuItemInfoW( mhMenu, nPos, TRUE, &pWMenuItem->mInfo ) )
            myerr = GetLastError();
        else
            ImplDrawMenuBar( this );
    }
}

void WinSalMenu::CheckItem( unsigned nPos, sal_Bool bCheck )
{
    if( -1 != ::CheckMenuItem( mhMenu, nPos, MF_BYPOSITION|(bCheck ? MF_CHECKED : MF_UNCHECKED) ) )
        ImplDrawMenuBar( this );
}

void WinSalMenu::EnableItem( unsigned nPos, sal_Bool bEnable )
{
    if( -1 != ::EnableMenuItem( mhMenu, nPos, MF_BYPOSITION|(bEnable ? MF_ENABLED : (MF_DISABLED|MF_GRAYED) ) ) )
        ImplDrawMenuBar( this );
}

void WinSalMenu::SetItemImage( unsigned /*nPos*/, SalMenuItem* pSalMenuItem, const Image& rImage )
{
    if( pSalMenuItem )
    {
    WinSalMenuItem* pWItem = static_cast<WinSalMenuItem*>(pSalMenuItem);
        if( !!rImage )
            pWItem->maBitmap = rImage.GetBitmapEx().GetBitmap();
        else
            pWItem->maBitmap = Bitmap();
    }
}

void WinSalMenu::SetItemText( unsigned nPos, SalMenuItem* pSalMenuItem, const rtl::OUString& rText )
{
    if( pSalMenuItem )
    {
    WinSalMenuItem* pWItem = static_cast<WinSalMenuItem*>(pSalMenuItem);
        pWItem->mText = rText;
        // 'translate' mnemonics
        pWItem->mText.SearchAndReplace( '~', '&' );
        pWItem->mInfo.fMask = MIIM_TYPE | MIIM_DATA;
        pWItem->mInfo.fType = MFT_STRING;
#ifdef OWNERDRAW
        if( pWItem->mpMenu && !((Menu*) pWItem->mpMenu)->IsMenuBar() )
            pWItem->mInfo.fType |= MFT_OWNERDRAW;
#endif

        // combine text and accelerator text
        XubString aStr( pWItem->mText );
        if( pWItem->mAccelText.Len() )
        {
            aStr.AppendAscii("\t");
            aStr.Append( pWItem->mAccelText );
        }
        pWItem->mInfo.dwTypeData = (LPWSTR) aStr.GetBuffer();
        pWItem->mInfo.cch = aStr.Len();

        if(!::SetMenuItemInfoW( mhMenu, nPos, TRUE, &pWItem->mInfo ))
            myerr = GetLastError();
        else
            ImplDrawMenuBar( this );
    }
}

void WinSalMenu::SetAccelerator( unsigned nPos, SalMenuItem* pSalMenuItem, const KeyCode&, const rtl::OUString& rKeyName )
{
    if( pSalMenuItem )
    {
    WinSalMenuItem* pWItem = static_cast<WinSalMenuItem*>(pSalMenuItem);
        pWItem->mAccelText = rKeyName;
        pWItem->mInfo.fMask = MIIM_TYPE | MIIM_DATA;
        pWItem->mInfo.fType = MFT_STRING;
#ifdef OWNERDRAW
        if( pWItem->mpMenu && !((Menu*)pWItem->mpMenu)->IsMenuBar() )
            pWItem->mInfo.fType |= MFT_OWNERDRAW;
#endif
        // combine text and accelerator text
        XubString aStr( pWItem->mText );
        if( pWItem->mAccelText.Len() )
        {
            aStr.AppendAscii("\t");
            aStr.Append( pWItem->mAccelText );
        }
        pWItem->mInfo.dwTypeData = (LPWSTR) aStr.GetBuffer();
        pWItem->mInfo.cch = aStr.Len();

        if(!::SetMenuItemInfoW( mhMenu, nPos, TRUE, &pWItem->mInfo ))
            myerr = GetLastError();
        else
            ImplDrawMenuBar( this );
    }
}

void WinSalMenu::GetSystemMenuData( SystemMenuData* pData )
{
    if( pData )
        pData->hMenu = mhMenu;
}

// =======================================================================

/*
 * SalMenuItem
 */

WinSalMenuItem::WinSalMenuItem()
{
    memset( &mInfo, 0, sizeof( MENUITEMINFOW ) );
    mpMenu = NULL;
    mnId  = 0xFFFF;
    mpSalMenu = NULL;
}

WinSalMenuItem::~WinSalMenuItem()
{
    if( mpSalMenu )
        ImplRemoveItemById( mpSalMenu, mnId );
}

// -------------------------------------------------------------------

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
<commit_msg>WaE: comparison between signed and unsigned integer expressions<commit_after>/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * Copyright 2000, 2010 Oracle and/or its affiliates.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/

#include <svsys.h>

#include <vcl/menu.hxx>
#include <vcl/sysdata.hxx>

#include <win/wincomp.hxx>
#include <win/saldata.hxx>
#include <win/salinst.h>
#include <win/salframe.h>
#include <win/salmenu.h>

#include <impbmp.hxx>
#include <salgdi.hxx>

// uncomment the following line to have ownerdrawn menus, ie, with bitmaps
// however, this is incompatible with OLE inplace editing
// so it is not activated by default
//#define OWNERDRAW

static DWORD myerr=0;

// =======================================================================

sal_Bool SalData::IsKnownMenuHandle( HMENU hMenu )
{
    if( mhMenuSet.find( hMenu ) == mhMenuSet.end() )
        return FALSE;
    else
        return TRUE;
}

// =======================================================================

// WinSalInst factory methods

SalMenu* WinSalInstance::CreateMenu( sal_Bool bMenuBar, Menu* )
{
    WinSalMenu *pSalMenu = new WinSalMenu();

    pSalMenu->mbMenuBar = bMenuBar;
    pSalMenu->mhWnd     = NULL;
    if( bMenuBar )
        pSalMenu->mhMenu = ::CreateMenu();
    else
        pSalMenu->mhMenu = ::CreatePopupMenu();

    if( pSalMenu->mhMenu )
        GetSalData()->mhMenuSet.insert( pSalMenu->mhMenu );

    return pSalMenu;
}

void WinSalInstance::DestroyMenu( SalMenu* pSalMenu )
{
    delete pSalMenu;
}

SalMenuItem* WinSalInstance::CreateMenuItem( const SalItemParams* pItemData )
{
    if( !pItemData )
        return NULL;

    WinSalMenuItem *pSalMenuItem = new WinSalMenuItem();
    memset( &pSalMenuItem->mInfo, 0, sizeof( MENUITEMINFOW ) );
    pSalMenuItem->mInfo.cbSize = sizeof( MENUITEMINFOW );

    if( pItemData->eType == MENUITEM_SEPARATOR )
    {
        // separator
        pSalMenuItem->mInfo.fMask = MIIM_TYPE;
        pSalMenuItem->mInfo.fType = MFT_SEPARATOR;
    }
    else
    {
        // item
        pSalMenuItem->mText   = pItemData->aText;
        pSalMenuItem->mpMenu  = pItemData->pMenu;
        pSalMenuItem->maBitmap= !!pItemData->aImage ? pItemData->aImage.GetBitmapEx().GetBitmap() : Bitmap();
        pSalMenuItem->mnId    = pItemData->nId;

        // 'translate' mnemonics
        pSalMenuItem->mText.SearchAndReplace( '~', '&' );

        pSalMenuItem->mInfo.fMask = MIIM_TYPE | MIIM_STATE | MIIM_ID | MIIM_DATA;
        pSalMenuItem->mInfo.fType = MFT_STRING;
#ifdef OWNERDRAW
        if( pItemData->pMenu && !pItemData->pMenu->IsMenuBar() )
            pSalMenuItem->mInfo.fType |= MFT_OWNERDRAW;
        pSalMenuItem->mInfo.fState = MFS_ENABLED;
#endif
        pSalMenuItem->mInfo.dwTypeData = (LPWSTR) pSalMenuItem->mText.GetBuffer();
        pSalMenuItem->mInfo.cch = pSalMenuItem->mText.Len();

        pSalMenuItem->mInfo.wID = pItemData->nId;
        pSalMenuItem->mInfo.dwItemData = (ULONG_PTR) pSalMenuItem; // user data
    }

    return pSalMenuItem;
}

void WinSalInstance::DestroyMenuItem( SalMenuItem* pSalMenuItem )
{
    delete pSalMenuItem;
}

// =======================================================================

static void ImplDrawMenuBar( SalMenu *pMenu )
{
    if( pMenu->VisibleMenuBar() )
    {
        // redrawing the menubar all the time actually seems to be unnecessary (it just flickers)
        /*
        WinSalMenu *pMenuBar = ImplFindMenuBar( pMenu );
        if( pMenuBar && pMenuBar->mhWnd )
            ::DrawMenuBar( pMenuBar->mhWnd );
            */
    }
}

// =======================================================================

/*
 * WinSalMenu
 */

WinSalMenu::WinSalMenu()
{
    mhMenu       = NULL;
    mbMenuBar    = FALSE;
    mhWnd        = NULL;
    mpParentMenu = NULL;
}

WinSalMenu::~WinSalMenu()
{
    // only required if not associated to a window...
    GetSalData()->mhMenuSet.erase( mhMenu );
    ::DestroyMenu( mhMenu );
}

sal_Bool WinSalMenu::VisibleMenuBar()
{
    // The Win32 implementation never shows a native
    // menubar. Thus, native menus are only visible
    // when the menu is merged with an OLE container.
    // The reason are missing tooltips, ownerdraw
    // issues and accessibility which are better supported
    // by VCL menus.
    // Nevertheless, the native menus are always created
    // and the application will properly react to all native
    // menu messages.

    return FALSE;
}

void WinSalMenu::SetFrame( const SalFrame *pFrame )
{
    if( pFrame )
        mhWnd = static_cast<const WinSalFrame*>(pFrame)->mhWnd;
    else
        mhWnd = NULL;
}

void WinSalMenu::InsertItem( SalMenuItem* pSalMenuItem, unsigned nPos )
{
    if( pSalMenuItem )
    {
    WinSalMenuItem* pWItem = static_cast<WinSalMenuItem*>(pSalMenuItem);
        if( nPos == MENU_APPEND )
        {
            nPos = ::GetMenuItemCount( mhMenu );
            if( nPos == static_cast<unsigned>( -1 ) )
                return;
        }

        if(!::InsertMenuItemW( mhMenu, nPos, TRUE, &pWItem->mInfo ))
            myerr = GetLastError();
        else
        {
            pWItem->mpSalMenu = this;
            ImplDrawMenuBar( this );
        }
    }
}

void WinSalMenu::RemoveItem( unsigned nPos )
{
    int num = ::GetMenuItemCount( mhMenu );
    if( num != -1 && nPos < (unsigned)num )
    {
        WinSalMenuItem *pSalMenuItem = NULL;

        MENUITEMINFOW mi;
        memset( &mi, 0, sizeof(mi) );
        mi.cbSize = sizeof( mi );
        mi.fMask = MIIM_DATA;
        if( !GetMenuItemInfoW( mhMenu, nPos, TRUE, &mi) )
            myerr = GetLastError();
        else
            pSalMenuItem = (WinSalMenuItem *) mi.dwItemData;

        if( !::RemoveMenu( mhMenu, nPos, MF_BYPOSITION ) )
            myerr = GetLastError();
        else
        {
            if( pSalMenuItem )
                pSalMenuItem->mpSalMenu = NULL;
            ImplDrawMenuBar( this );
        }
    }
}

void ImplRemoveItemById( WinSalMenu *pSalMenu, unsigned nItemId )
{
    if( !pSalMenu )
        return;

    WinSalMenuItem *pSalMenuItem = NULL;

    MENUITEMINFOW mi;
    memset( &mi, 0, sizeof(mi) );
    mi.cbSize = sizeof( mi );
    mi.fMask = MIIM_DATA;
    if( !GetMenuItemInfoW( pSalMenu->mhMenu, nItemId, FALSE, &mi) )
        myerr = GetLastError();
    else
        pSalMenuItem = (WinSalMenuItem *) mi.dwItemData;

    if( !::RemoveMenu( pSalMenu->mhMenu, nItemId, MF_BYCOMMAND ) )
        myerr = GetLastError();
    else
    {
        if( pSalMenuItem )
            pSalMenuItem->mpSalMenu = NULL;
        ImplDrawMenuBar( pSalMenu );
    }
}

void WinSalMenu::SetSubMenu( SalMenuItem* pSalMenuItem, SalMenu* pSubMenu, unsigned nPos )
{
    if( pSalMenuItem )
    {
        WinSalMenuItem* pWMenuItem = static_cast<WinSalMenuItem*>(pSalMenuItem);
        WinSalMenu* pWSubMenu = static_cast<WinSalMenu*>(pSubMenu);
        if( pWMenuItem->mInfo.hSubMenu )
        {
            GetSalData()->mhMenuSet.erase( pWMenuItem->mInfo.hSubMenu );
            ::DestroyMenu( pWMenuItem->mInfo.hSubMenu );
        }

        pWMenuItem->mInfo.fMask |= MIIM_SUBMENU;
        if( !pSubMenu )
            pWMenuItem->mInfo.hSubMenu = NULL;
        else
        {
            pWMenuItem->mInfo.hSubMenu = pWSubMenu->mhMenu;
            pWSubMenu->mpParentMenu = this;
        }

        if(!::SetMenuItemInfoW( mhMenu, nPos, TRUE, &pWMenuItem->mInfo ) )
            myerr = GetLastError();
        else
            ImplDrawMenuBar( this );
    }
}

void WinSalMenu::CheckItem( unsigned nPos, sal_Bool bCheck )
{
    if( static_cast<unsigned>( -1 ) != ::CheckMenuItem( mhMenu, nPos, MF_BYPOSITION|(bCheck ? MF_CHECKED : MF_UNCHECKED) ) )
        ImplDrawMenuBar( this );
}

void WinSalMenu::EnableItem( unsigned nPos, sal_Bool bEnable )
{
    if( -1 != ::EnableMenuItem( mhMenu, nPos, MF_BYPOSITION|(bEnable ? MF_ENABLED : (MF_DISABLED|MF_GRAYED) ) ) )
        ImplDrawMenuBar( this );
}

void WinSalMenu::SetItemImage( unsigned /*nPos*/, SalMenuItem* pSalMenuItem, const Image& rImage )
{
    if( pSalMenuItem )
    {
    WinSalMenuItem* pWItem = static_cast<WinSalMenuItem*>(pSalMenuItem);
        if( !!rImage )
            pWItem->maBitmap = rImage.GetBitmapEx().GetBitmap();
        else
            pWItem->maBitmap = Bitmap();
    }
}

void WinSalMenu::SetItemText( unsigned nPos, SalMenuItem* pSalMenuItem, const rtl::OUString& rText )
{
    if( pSalMenuItem )
    {
    WinSalMenuItem* pWItem = static_cast<WinSalMenuItem*>(pSalMenuItem);
        pWItem->mText = rText;
        // 'translate' mnemonics
        pWItem->mText.SearchAndReplace( '~', '&' );
        pWItem->mInfo.fMask = MIIM_TYPE | MIIM_DATA;
        pWItem->mInfo.fType = MFT_STRING;
#ifdef OWNERDRAW
        if( pWItem->mpMenu && !((Menu*) pWItem->mpMenu)->IsMenuBar() )
            pWItem->mInfo.fType |= MFT_OWNERDRAW;
#endif

        // combine text and accelerator text
        XubString aStr( pWItem->mText );
        if( pWItem->mAccelText.Len() )
        {
            aStr.AppendAscii("\t");
            aStr.Append( pWItem->mAccelText );
        }
        pWItem->mInfo.dwTypeData = (LPWSTR) aStr.GetBuffer();
        pWItem->mInfo.cch = aStr.Len();

        if(!::SetMenuItemInfoW( mhMenu, nPos, TRUE, &pWItem->mInfo ))
            myerr = GetLastError();
        else
            ImplDrawMenuBar( this );
    }
}

void WinSalMenu::SetAccelerator( unsigned nPos, SalMenuItem* pSalMenuItem, const KeyCode&, const rtl::OUString& rKeyName )
{
    if( pSalMenuItem )
    {
    WinSalMenuItem* pWItem = static_cast<WinSalMenuItem*>(pSalMenuItem);
        pWItem->mAccelText = rKeyName;
        pWItem->mInfo.fMask = MIIM_TYPE | MIIM_DATA;
        pWItem->mInfo.fType = MFT_STRING;
#ifdef OWNERDRAW
        if( pWItem->mpMenu && !((Menu*)pWItem->mpMenu)->IsMenuBar() )
            pWItem->mInfo.fType |= MFT_OWNERDRAW;
#endif
        // combine text and accelerator text
        XubString aStr( pWItem->mText );
        if( pWItem->mAccelText.Len() )
        {
            aStr.AppendAscii("\t");
            aStr.Append( pWItem->mAccelText );
        }
        pWItem->mInfo.dwTypeData = (LPWSTR) aStr.GetBuffer();
        pWItem->mInfo.cch = aStr.Len();

        if(!::SetMenuItemInfoW( mhMenu, nPos, TRUE, &pWItem->mInfo ))
            myerr = GetLastError();
        else
            ImplDrawMenuBar( this );
    }
}

void WinSalMenu::GetSystemMenuData( SystemMenuData* pData )
{
    if( pData )
        pData->hMenu = mhMenu;
}

// =======================================================================

/*
 * SalMenuItem
 */

WinSalMenuItem::WinSalMenuItem()
{
    memset( &mInfo, 0, sizeof( MENUITEMINFOW ) );
    mpMenu = NULL;
    mnId  = 0xFFFF;
    mpSalMenu = NULL;
}

WinSalMenuItem::~WinSalMenuItem()
{
    if( mpSalMenu )
        ImplRemoveItemById( mpSalMenu, mnId );
}

// -------------------------------------------------------------------

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
<|endoftext|>


<commit_before>#pragma once

#include <string>
#include <vector>
#include <memory>

#include "acmacs-base/throw.hh"
#include "acmacs-base/range.hh"
#ifdef ACMACS_TARGET_OS
#include "acmacs-base/timeit.hh"
#include "acmacs-chart/chart.hh"
using Chart_Type = Chart;
#else
#include "acmacs-chart/chart-base.hh"
using Chart_Type = ChartBase;
#endif
#include "acmacs-draw/viewport.hh"
#include "acmacs-map-draw/point-style-draw.hh"
#include "acmacs-map-draw/map-elements.hh"
#include "acmacs-map-draw/labels.hh"

class Surface;

// ----------------------------------------------------------------------

class DrawingOrder : public std::vector<size_t>
{
 public:
    DrawingOrder(Chart_Type& aChart);
    inline DrawingOrder& operator=(const std::vector<size_t>& aSource) { std::vector<size_t>::operator=(aSource); return *this; }

    void raise(size_t aPointNo);
    void lower(size_t aPointNo);

}; // class DrawingOrder

// ----------------------------------------------------------------------

class ChartDraw
{
 public:
    ChartDraw(Chart_Type& aChart, size_t aProjectionNo);

    void prepare();
    void draw(Surface& aSurface) const;
#ifdef ACMACS_TARGET_OS
    void draw(std::string aFilename, double aSize, report_time aTimer = report_time::No) const;
#endif
    void calculate_viewport(bool verbose = true);

    inline const std::vector<PointStyleDraw>& point_styles() const { return mPointStyles; }
    inline std::vector<PointStyle> point_styles_base() const { std::vector<PointStyle> ps{mPointStyles.begin(), mPointStyles.end()}; return ps; }
    inline auto& chart() { return mChart; }
    inline const auto& chart() const { return mChart; }
    inline auto projection_no() const { return mProjectionNo; }
    inline const auto& layout() const { return chart().projection(projection_no()).layout(); }
    inline auto& layout() { return chart().projection(projection_no()).layout(); }

    template <typename index_type> inline void modify(index_type aIndex, const PointStyle& aStyle, PointDrawingOrder aPointDrawingOrder = PointDrawingOrder::NoChange)
        {
            const auto index = static_cast<size_t>(aIndex);
            mPointStyles[index] = aStyle;
            switch (aPointDrawingOrder) {
              case PointDrawingOrder::Raise:
                  drawing_order().raise(index);
                  break;
              case PointDrawingOrder::Lower:
                  drawing_order().lower(index);
                  break;
              case PointDrawingOrder::NoChange:
                  break;
            }
        }

    template <typename index_type> inline void modify_serum(index_type aSerumNo, const PointStyle& aStyle, PointDrawingOrder aPointDrawingOrder = PointDrawingOrder::NoChange)
        {
            modify(static_cast<size_t>(aSerumNo) + number_of_antigens(), aStyle, aPointDrawingOrder);
        }

    template <typename IndexIterator> inline void modify(IndexIterator first, IndexIterator last, const PointStyle& aStyle, PointDrawingOrder aPointDrawingOrder = PointDrawingOrder::NoChange)
        {
            for (; first != last; ++first)
                modify(*first, aStyle, aPointDrawingOrder);
        }

    inline void modify(acmacs::IndexGenerator&& aGen, const PointStyle& aStyle, PointDrawingOrder aPointDrawingOrder = PointDrawingOrder::NoChange)
        {
            for (auto index: aGen)
                modify(index, aStyle, aPointDrawingOrder);
        }

    template <typename IndexIterator> inline void modify_sera(IndexIterator first, IndexIterator last, const PointStyle& aStyle, PointDrawingOrder aPointDrawingOrder = PointDrawingOrder::NoChange)
        {
            for (; first != last; ++first)
                modify_serum(*first, aStyle, aPointDrawingOrder);
        }

    void hide_all_except(const std::vector<size_t>& aNotHide);
    void mark_egg_antigens();
    void mark_reassortant_antigens();
    void mark_all_grey(Color aColor);
    void scale_points(double aPointScale, double aOulineScale);
    void modify_all_sera(const PointStyle& aStyle, PointDrawingOrder aPointDrawingOrder = PointDrawingOrder::NoChange);

    inline void rotate(double aAngle)
        {
            // std::cout << "INFO: rotate " << aAngle << " radians = " << (180.0 * aAngle / M_PI) << " degrees" << std::endl;
            log("rotate ", aAngle, " radians = ", 180.0 * aAngle / M_PI, " degrees ", aAngle > 0 ? "counter-" : "", "clockwise");
            mTransformation.rotate(aAngle);
        }

    inline void flip(double aX, double aY)
        {
            // std::cout << "INFO: flip " << aX << " " << aY << std::endl;
            log("flip ", aX, " ", aY);
            mTransformation.flip(aX, aY); // reflect about a line specified with vector [aX, aY]
        }
    inline const Transformation& transformation() const { return mTransformation; }

    inline void viewport(double aX, double aY, double aSize) { mViewport.set(aX, aY, aSize); }
    inline void viewport(const Viewport& aViewport) { mViewport = aViewport; }
    inline const Viewport& viewport() const { return mViewport; }

    DrawingOrder& drawing_order() { return mDrawingOrder; }

    inline void background_color(Color aBackgroud) { DYNAMIC_CAST(map_elements::BackgroundBorderGrid&, (mMapElements["background-border-grid"])).background_color(aBackgroud); }
    inline void grid(Color aGridColor, double aGridLineWidth) { DYNAMIC_CAST(map_elements::BackgroundBorderGrid&, (mMapElements["background-border-grid"])).grid(aGridColor, aGridLineWidth); }
    inline void border(Color aBorderColor, double aBorderWidth) { DYNAMIC_CAST(map_elements::BackgroundBorderGrid&, (mMapElements["background-border-grid"])).border(aBorderColor, aBorderWidth); }
    inline void continent_map(const Location& aOffset, Pixels aWidth) { DYNAMIC_CAST(map_elements::ContinentMap&, (mMapElements["continent-map"])).offset_width(aOffset, aWidth); }
    inline map_elements::LegendPointLabel& legend(const Location& aOffset) { auto& legend = DYNAMIC_CAST(map_elements::LegendPointLabel&, (mMapElements["legend-point-label"])); legend.offset(aOffset); return legend; }
    inline map_elements::LegendPointLabel& legend() { return DYNAMIC_CAST(map_elements::LegendPointLabel&, (mMapElements["legend-point-label"])); }
    inline void remove_legend() { mMapElements.remove("legend-point-label"); }
    inline map_elements::Title& title(const Location& aOffset) { auto& title = DYNAMIC_CAST(map_elements::Title&, (mMapElements["title"])); title.offset(aOffset); return title; }
    inline map_elements::Title& title() { return DYNAMIC_CAST(map_elements::Title&, (mMapElements["title"])); }
    inline map_elements::Labels& labels() { return mLabels; }
    inline map_elements::Label& add_label(size_t aIndex) { return mLabels.add(aIndex, mChart); }
    inline void remove_label(size_t aIndex) { return mLabels.remove(aIndex); }
    map_elements::SerumCircle& serum_circle(size_t aSerumNo, Scaled aRadius);
    map_elements::Line& line(const Location& aBegin, const Location& aEnd);
    map_elements::Arrow& arrow(const Location& aBegin, const Location& aEnd);
    map_elements::Point& point(const Location& aCenter, Pixels aSize);
    map_elements::Rectangle& rectangle(const Location& aCorner1, const Location& aCorner2);
    map_elements::Circle& circle(const Location& aCenter, Scaled aSize);
    void remove_serum_circles();

    inline const LayoutBase& transformed_layout() const
        {
            if (!mTransformedLayout || mTransformedLayout->empty()) {
                mTransformedLayout = std::unique_ptr<LayoutBase>(mChart.projection(mProjectionNo).layout().clone());
                mTransformedLayout->transform(mTransformation);
            }
            return *mTransformedLayout;
        }

    size_t number_of_antigens() const;
    size_t number_of_sera() const;
    inline size_t number_of_points() const { return number_of_antigens() + number_of_sera(); }

    void export_ace(std::string aFilename);
    void export_lispmds(std::string aFilename);

 private:
    Chart_Type& mChart;
    size_t mProjectionNo;
    Viewport mViewport;
    Transformation mTransformation;
    std::vector<PointStyleDraw> mPointStyles;
    DrawingOrder mDrawingOrder;
    map_elements::Elements mMapElements;
    map_elements::Labels mLabels;
    mutable std::unique_ptr<LayoutBase> mTransformedLayout;

}; // class ChartDraw

// ----------------------------------------------------------------------
/// Local Variables:
/// eval: (if (fboundp 'eu-rename-buffer) (eu-rename-buffer))
/// End:
<commit_msg>debugging<commit_after>#pragma once

#include <string>
#include <vector>
#include <memory>

#include "acmacs-base/throw.hh"
#include "acmacs-base/range.hh"
#ifdef ACMACS_TARGET_OS
#include "acmacs-base/timeit.hh"
#include "acmacs-chart/chart.hh"
using Chart_Type = Chart;
#else
#include "acmacs-chart/chart-base.hh"
using Chart_Type = ChartBase;
#endif
#include "acmacs-draw/viewport.hh"
#include "acmacs-map-draw/point-style-draw.hh"
#include "acmacs-map-draw/map-elements.hh"
#include "acmacs-map-draw/labels.hh"

class Surface;

// ----------------------------------------------------------------------

class DrawingOrder : public std::vector<size_t>
{
 public:
    DrawingOrder(Chart_Type& aChart);
    inline DrawingOrder& operator=(const std::vector<size_t>& aSource) { std::vector<size_t>::operator=(aSource); return *this; }

    void raise(size_t aPointNo);
    void lower(size_t aPointNo);

}; // class DrawingOrder

// ----------------------------------------------------------------------

class ChartDraw
{
 public:
    ChartDraw(Chart_Type& aChart, size_t aProjectionNo);

    void prepare();
    void draw(Surface& aSurface) const;
#ifdef ACMACS_TARGET_OS
    void draw(std::string aFilename, double aSize, report_time aTimer = report_time::No) const;
#endif
    void calculate_viewport(bool verbose = true);

    inline const std::vector<PointStyleDraw>& point_styles() const { return mPointStyles; }
    inline std::vector<PointStyle> point_styles_base() const { std::vector<PointStyle> ps{mPointStyles.begin(), mPointStyles.end()}; return ps; }
    inline auto& chart() { return mChart; }
    inline const auto& chart() const { return mChart; }
    inline auto projection_no() const { return mProjectionNo; }
    inline const auto& layout() const { return chart().projection(projection_no()).layout(); }
    inline auto& layout() { return chart().projection(projection_no()).layout(); }

    template <typename index_type> inline void modify(index_type aIndex, const PointStyle& aStyle, PointDrawingOrder aPointDrawingOrder = PointDrawingOrder::NoChange)
        {
            const auto index = static_cast<size_t>(aIndex);
            mPointStyles[index] = aStyle;
              // std::cerr << "DEBUG: modified " << index << ' ' << mPointStyles[index] << '\n';
            switch (aPointDrawingOrder) {
              case PointDrawingOrder::Raise:
                  drawing_order().raise(index);
                  break;
              case PointDrawingOrder::Lower:
                  drawing_order().lower(index);
                  break;
              case PointDrawingOrder::NoChange:
                  break;
            }
        }

    template <typename index_type> inline void modify_serum(index_type aSerumNo, const PointStyle& aStyle, PointDrawingOrder aPointDrawingOrder = PointDrawingOrder::NoChange)
        {
            modify(static_cast<size_t>(aSerumNo) + number_of_antigens(), aStyle, aPointDrawingOrder);
        }

    template <typename IndexIterator> inline void modify(IndexIterator first, IndexIterator last, const PointStyle& aStyle, PointDrawingOrder aPointDrawingOrder = PointDrawingOrder::NoChange)
        {
            for (; first != last; ++first)
                modify(*first, aStyle, aPointDrawingOrder);
        }

    inline void modify(acmacs::IndexGenerator&& aGen, const PointStyle& aStyle, PointDrawingOrder aPointDrawingOrder = PointDrawingOrder::NoChange)
        {
            for (auto index: aGen)
                modify(index, aStyle, aPointDrawingOrder);
        }

    template <typename IndexIterator> inline void modify_sera(IndexIterator first, IndexIterator last, const PointStyle& aStyle, PointDrawingOrder aPointDrawingOrder = PointDrawingOrder::NoChange)
        {
            for (; first != last; ++first)
                modify_serum(*first, aStyle, aPointDrawingOrder);
        }

    void hide_all_except(const std::vector<size_t>& aNotHide);
    void mark_egg_antigens();
    void mark_reassortant_antigens();
    void mark_all_grey(Color aColor);
    void scale_points(double aPointScale, double aOulineScale);
    void modify_all_sera(const PointStyle& aStyle, PointDrawingOrder aPointDrawingOrder = PointDrawingOrder::NoChange);

    inline void rotate(double aAngle)
        {
            // std::cout << "INFO: rotate " << aAngle << " radians = " << (180.0 * aAngle / M_PI) << " degrees" << std::endl;
            log("rotate ", aAngle, " radians = ", 180.0 * aAngle / M_PI, " degrees ", aAngle > 0 ? "counter-" : "", "clockwise");
            mTransformation.rotate(aAngle);
        }

    inline void flip(double aX, double aY)
        {
            // std::cout << "INFO: flip " << aX << " " << aY << std::endl;
            log("flip ", aX, " ", aY);
            mTransformation.flip(aX, aY); // reflect about a line specified with vector [aX, aY]
        }
    inline const Transformation& transformation() const { return mTransformation; }

    inline void viewport(double aX, double aY, double aSize) { mViewport.set(aX, aY, aSize); }
    inline void viewport(const Viewport& aViewport) { mViewport = aViewport; }
    inline const Viewport& viewport() const { return mViewport; }

    DrawingOrder& drawing_order() { return mDrawingOrder; }

    inline void background_color(Color aBackgroud) { DYNAMIC_CAST(map_elements::BackgroundBorderGrid&, (mMapElements["background-border-grid"])).background_color(aBackgroud); }
    inline void grid(Color aGridColor, double aGridLineWidth) { DYNAMIC_CAST(map_elements::BackgroundBorderGrid&, (mMapElements["background-border-grid"])).grid(aGridColor, aGridLineWidth); }
    inline void border(Color aBorderColor, double aBorderWidth) { DYNAMIC_CAST(map_elements::BackgroundBorderGrid&, (mMapElements["background-border-grid"])).border(aBorderColor, aBorderWidth); }
    inline void continent_map(const Location& aOffset, Pixels aWidth) { DYNAMIC_CAST(map_elements::ContinentMap&, (mMapElements["continent-map"])).offset_width(aOffset, aWidth); }
    inline map_elements::LegendPointLabel& legend(const Location& aOffset) { auto& legend = DYNAMIC_CAST(map_elements::LegendPointLabel&, (mMapElements["legend-point-label"])); legend.offset(aOffset); return legend; }
    inline map_elements::LegendPointLabel& legend() { return DYNAMIC_CAST(map_elements::LegendPointLabel&, (mMapElements["legend-point-label"])); }
    inline void remove_legend() { mMapElements.remove("legend-point-label"); }
    inline map_elements::Title& title(const Location& aOffset) { auto& title = DYNAMIC_CAST(map_elements::Title&, (mMapElements["title"])); title.offset(aOffset); return title; }
    inline map_elements::Title& title() { return DYNAMIC_CAST(map_elements::Title&, (mMapElements["title"])); }
    inline map_elements::Labels& labels() { return mLabels; }
    inline map_elements::Label& add_label(size_t aIndex) { return mLabels.add(aIndex, mChart); }
    inline void remove_label(size_t aIndex) { return mLabels.remove(aIndex); }
    map_elements::SerumCircle& serum_circle(size_t aSerumNo, Scaled aRadius);
    map_elements::Line& line(const Location& aBegin, const Location& aEnd);
    map_elements::Arrow& arrow(const Location& aBegin, const Location& aEnd);
    map_elements::Point& point(const Location& aCenter, Pixels aSize);
    map_elements::Rectangle& rectangle(const Location& aCorner1, const Location& aCorner2);
    map_elements::Circle& circle(const Location& aCenter, Scaled aSize);
    void remove_serum_circles();

    inline const LayoutBase& transformed_layout() const
        {
            if (!mTransformedLayout || mTransformedLayout->empty()) {
                mTransformedLayout = std::unique_ptr<LayoutBase>(mChart.projection(mProjectionNo).layout().clone());
                mTransformedLayout->transform(mTransformation);
            }
            return *mTransformedLayout;
        }

    size_t number_of_antigens() const;
    size_t number_of_sera() const;
    inline size_t number_of_points() const { return number_of_antigens() + number_of_sera(); }

    void export_ace(std::string aFilename);
    void export_lispmds(std::string aFilename);

 private:
    Chart_Type& mChart;
    size_t mProjectionNo;
    Viewport mViewport;
    Transformation mTransformation;
    std::vector<PointStyleDraw> mPointStyles;
    DrawingOrder mDrawingOrder;
    map_elements::Elements mMapElements;
    map_elements::Labels mLabels;
    mutable std::unique_ptr<LayoutBase> mTransformedLayout;

}; // class ChartDraw

// ----------------------------------------------------------------------
/// Local Variables:
/// eval: (if (fboundp 'eu-rename-buffer) (eu-rename-buffer))
/// End:
<|endoftext|>