/* CONTROL-POLICY.hpp - detail definition of actual time changing functionality Copyright (C) Lumiera.org 2011, Hermann Vosseler This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /** @file control-policy.hpp ** Definition of special cases when imposing a change onto concrete time values. ** The time::Control element allows to impose modifications to a connected ** time value entity and at the same time publish the changes to registered ** listeners. Due to the various flavours of actual time value entities, this ** is a complex undertaking, which is implemented here based on policies and ** template metaprogramming. ** ** The header control-impl.hpp defines the building blocks for time::Control ** and then includes this header here to get the concrete template specialisations ** for time::mutation::Policy. This policy class is templated by time entity types ** - for \c TI, the \em nominal value type used on the time::Control interface ** - for \c SRC, the actual type of values to impose as \em change ** - for \c TAR, the target time value's type, receiving those changes. ** ** \par mutating a time value entity ** ** Actually imposing a change to the attached time value entity involves several ** steps. Each of these steps might be adapted specifically, in accordance to ** the concrete time value types involved. ** - TimeValue, Time ** - Offset ** - Duration ** - TimeSpan ** - QuTime (grid aligned time value) ** - QuTimeSpan (planned as of 6/2011) ** ** Moreover, the combination of types needs to be taken into account. For example, ** it doesn't make sense to apply a Duration value as change to a TimeValue, which ** has no duration (temporal extension). While a TimeSpan might receive a Duration ** change, but behaves differently when imposing a Time to manipulate the starting ** point of the time interval given by the TimeSpan. ** ** Incoming changes might be of any of the aforementioned types, and in addition, ** we might receive \em nudging, which means to increment or decrement the target ** time value in discrete steps. After maybe adapting these incoming change values, ** they may be actually \em imposed to the target. In all cases, this is delegated ** to the time::Mutation base class, which is declared fried to TimeValue and thus ** has the exceptional ability to manipulate time values, which otherwise are defined ** to be immutable. Additionally, these protected functions in the time::Mutation ** baseclass also know how to handle \em nudge values, either by using the native ** (embedded) time grid of a quantised time value, or by falling back to a standard ** nudging grid, defined in the session context (TODO as of 6/2011). //////////////////////TICKET #810 ** ** After (maybe) imposing a change to the target, the change \em notification value ** needs to be built. This is the time value entity to be forwarded to registered ** listeners. This notification value has to be given as the type \c TI, in accordance ** to the \c time::Control frontend definition used in the concrete usage situation. ** As this type \c TI might be different to the actual target type, and again different ** to the type of the change handed in, in some cases this involves a second conversion ** step, to represent the current state of the target \c TAR in terms of the interface ** type \c TI. ** ** \par changing quantised (grid aligned) time entities ** ** The time::Control element includes the capability to handle grid aligned time values, ** both as target and as change/notification value. This ability is compiled in conditionally, ** as including mutation.hpp causes several additional includes, which isn't desirable when ** it comes just to changing plain time values. Thus, to get these additional specialisations, ** the LIB_TIME_TIMEQUQNT_H header guard needs to be defined, which happens automatically ** if lib/time/mutation.hpp is included prior to lib/time/control.hpp. ** ** As a special convention, any \em quantised (grid aligned) types involved in these ** time changes will be \em materialised, whenever a type conversion happens. Generally ** speaking, a quantised time value contains an (opaque) raw time value, plus a reference ** to a time grid definition to apply. In this context \em materialising means actually ** to apply this time grid to yield a grid aligned value. Thus, when using a quantised ** value to impose as change (or to receive a change), its grid aligning nature ** becomes effective, by applying the \em current definition of the grid to ** create a fixed (materialised) time value, aligned to that current grid. ** ** @todo 6/2011 include all the special cases for QuTimeSpan ////////////////////TICKET #760 ** ** @see TimeControl_test ** */ #ifndef LIB_TIME_CONTROL_POLICY_H #define LIB_TIME_CONTROL_POLICY_H #include "lib/meta/util.hpp" #include "lib/time/mutation.hpp" #include "lib/time/timevalue.hpp" #include #include namespace lib { namespace time { namespace mutation { using boost::disable_if; using lumiera::typelist::is_sameType; using std::tr1::placeholders::_1; using std::tr1::function; using std::tr1::bind; using std::tr1::ref; namespace { // metaprogramming helpers to pick a suitable implementation branch... template inline bool isDuration() { return is_sameType::value; } template inline bool isTimeSpan() { return is_sameType::value; } template inline T const& maybeMaterialise (T const& non_grid_aligned_TimeValue) { return non_grid_aligned_TimeValue; } #ifdef LIB_TIME_TIMEQUQNT_H inline QuTime maybeMaterialise (QuTime const& alignedTime) { PQuant const& grid(alignedTime); return QuTime(grid->materialise(alignedTime), grid); } #endif //--quantised-time-support } /** * Implementation policy: how to build a new * notification value of type \c TI, given a * target time value entity of type \c TAR */ template struct Builder { static TI buildChangedValue (TAR const& target) { return TI(target); } }; template struct Builder { static TimeSpan buildChangedValue (TAR const& target) { return TimeSpan (target, Duration::NIL); } }; template<> struct Builder { static TimeSpan buildChangedValue (Duration const& targetDuration) { return TimeSpan (Time::ZERO, targetDuration); } }; template<> struct Builder { static TimeSpan buildChangedValue (TimeSpan const& target) { return target; } }; #ifdef LIB_TIME_TIMEQUQNT_H template struct Builder { static QuTime buildChangedValue (TAR const& target) { return QuTime (target ,getDefaultGridFallback() //////////////////TICKET #810 ); } }; template<> struct Builder { static QuTime buildChangedValue (QuTime const& target) { return target; } }; #endif //--quantised-time-support /** * Policy to tie the various detail policies together * for providing actual value change operations. * The standard case uses the (inherited) time::Mutation * base implementation to impose a new value onto the * target entity and then uses the Builder policy to * create a notification value reflecting this change. */ template struct Link : Mutator , Builder { template static TI processValueChange (TAR& target, SRC const& change) ///< standard case: plain value change { imposeChange (target, maybeMaterialise(change)); return buildChangedValue (maybeMaterialise(target)); } static TI useLengthAsChange (TAR& target, TimeSpan const& change) { return processValueChange(target, change.duration()); } static TI mutateLength (TimeSpan& target, Duration const& change) { Mutator::imposeChange (target.duration(), change); return Builder::buildChangedValue(target); } static TimeSpan mutateTimeSpan (TimeSpan& target, TimeSpan const& change) { Mutator::imposeChange (target.duration(), change.duration()); Mutator::imposeChange (target,change.start()); return Builder::buildChangedValue(target); } static TI dontChange (TAR& target) ///< @note: not touching the target { return buildChangedValue(target); } }; /** * Policy how to impose changes onto a connected target time value entity * This policy will be parametrised with the concrete time entity types * involved in the usage situation of time::Control. The purpose of the * policy is to \em bind a functor object to the concrete implementation * of the value change applicable for this combination of types. * This functor will then be stored within time::Control and * invoked for each actual value change. * @param TI the nominal (interface) type of the change, propagated to listeners * @param SRC the actual type of the change to be imposed * @param TAR the actual type of the target entity to receive the changes * @note typically either SRC is identical to TI, or it is an * time::Offset, or an int for \em nudging the target */ template struct Policy { static function buildChangeHandler (TAR& target) { return bind (Link::template processValueChange, ref(target), _1 ); } }; // special treatment of Durations as target------------------------------------ namespace { template struct canMutateDuration { static const bool value = is_sameType::value || is_sameType::value || is_sameType::value; }; template struct canReceiveDuration { static const bool value = is_sameType::value || is_sameType::value; }; } /** * special case: a Duration target value can't be changed by plain time values. * This specialisation is \em not used (\c disable_if ) when the given change (SRC) * is applicable to a Duration in a sensible way. We either define explicit * specialisations (for TimeSpan) or fall back to the default in these cases. */ template struct Policy, Duration>::type> { static function buildChangeHandler (Duration& target) { return bind (Link::dontChange, ref(target) ); } }; /** * special case: a Duration change value can't be imposed to a plain time value. * In these cases, we even propagate a Duration::ZERO to the listeners. * As above, there are exceptions to this behaviour, where a Duration change * can sensibly be applied. */ template struct Policy, Duration>::type, TAR> { static function buildChangeHandler (TAR&) { return bind ( ignore_change_and_return_Zero ); } static Duration ignore_change_and_return_Zero() { return Duration::NIL; } }; template struct Policy { static function buildChangeHandler (Duration& target) { return bind (Link::useLengthAsChange, ref(target), _1 ); } }; // special treatment for TimeSpan values--------------------------------------- template struct Policy { static function buildChangeHandler (TimeSpan& target) { return bind (Link::mutateLength, ref(target), _1 ); } }; template<> struct Policy { static function buildChangeHandler (TimeSpan& target) { return bind (Link::mutateTimeSpan, ref(target), _1 ); } }; }}} // namespace lib::time::mutation #endif