benefits of using a newer compiler, yay! Explanation: the Link<...> template combines various policy templates and exposes a set of functions, which can be bound as functor into an concrete time::Control instance. Actually, we need to mix in only the Mutation baseclass, because we just want to inherit the privilege to change time values, which are otherwise immutable. We don't need to mix in the Mutator template anymore (this was a leftover from an earlier design)
400 lines
14 KiB
C++
400 lines
14 KiB
C++
/*
|
|
CONTROL-POLICY.hpp - detail definition of actual time changing functionality
|
|
|
|
Copyright (C) Lumiera.org
|
|
2011, Hermann Vosseler <Ichthyostega@web.de>
|
|
|
|
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<TI> 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 <boost/utility/enable_if.hpp>
|
|
#include <tr1/functional>
|
|
|
|
|
|
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<class T>
|
|
inline bool
|
|
isDuration()
|
|
{
|
|
return is_sameType<T,Duration>::value;
|
|
}
|
|
|
|
template<class T>
|
|
inline bool
|
|
isTimeSpan()
|
|
{
|
|
return is_sameType<T,TimeSpan>::value;
|
|
}
|
|
|
|
template<class T>
|
|
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<class TI, class TAR>
|
|
struct Builder
|
|
{
|
|
static TI
|
|
buildChangedValue (TAR const& target)
|
|
{
|
|
return TI(target);
|
|
}
|
|
};
|
|
template<class TAR>
|
|
struct Builder<TimeSpan, TAR>
|
|
{
|
|
static TimeSpan
|
|
buildChangedValue (TAR const& target)
|
|
{
|
|
return TimeSpan (target, Duration::NIL);
|
|
}
|
|
};
|
|
template<>
|
|
struct Builder<TimeSpan, Duration>
|
|
{
|
|
static TimeSpan
|
|
buildChangedValue (Duration const& targetDuration)
|
|
{
|
|
return TimeSpan (Time::ZERO, targetDuration);
|
|
}
|
|
};
|
|
template<>
|
|
struct Builder<TimeSpan, TimeSpan>
|
|
{
|
|
static TimeSpan
|
|
buildChangedValue (TimeSpan const& target)
|
|
{
|
|
return target;
|
|
}
|
|
};
|
|
#ifdef LIB_TIME_TIMEQUQNT_H
|
|
template<class TAR>
|
|
struct Builder<QuTime, TAR>
|
|
{
|
|
static QuTime
|
|
buildChangedValue (TAR const& target)
|
|
{
|
|
return QuTime (target
|
|
,getDefaultGridFallback() //////////////////TICKET #810
|
|
);
|
|
}
|
|
};
|
|
template<>
|
|
struct Builder<QuTime, QuTime>
|
|
{
|
|
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<class TI, class TAR>
|
|
struct Link
|
|
: Mutation
|
|
, Builder<TI,TAR>
|
|
{
|
|
|
|
template<class SRC>
|
|
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)
|
|
{
|
|
imposeChange (target.duration(), change);
|
|
return Builder<TI,TimeSpan>::buildChangedValue(target);
|
|
}
|
|
|
|
static TimeSpan
|
|
mutateTimeSpan (TimeSpan& target, TimeSpan const& change)
|
|
{
|
|
imposeChange (target.duration(), change.duration());
|
|
imposeChange (target,change.start());
|
|
return Builder<TimeSpan,TimeSpan>::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<class TI, class SRC, class TAR>
|
|
struct Policy
|
|
{
|
|
static function<TI(SRC const&)>
|
|
buildChangeHandler (TAR& target)
|
|
{
|
|
return bind (Link<TI,TAR>::template processValueChange<SRC>, ref(target), _1 );
|
|
}
|
|
};
|
|
|
|
|
|
// special treatment of Durations as target------------------------------------
|
|
|
|
namespace {
|
|
template<class T>
|
|
struct canMutateDuration
|
|
{
|
|
static const bool value = is_sameType<T,Duration>::value
|
|
|| is_sameType<T,Offset>::value
|
|
|| is_sameType<T,int>::value;
|
|
};
|
|
|
|
template<class T>
|
|
struct canReceiveDuration
|
|
{
|
|
static const bool value = is_sameType<T,Duration>::value
|
|
|| is_sameType<T,TimeSpan>::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<class TI, class SRC>
|
|
struct Policy<TI,SRC, typename disable_if< canMutateDuration<SRC>,
|
|
Duration>::type>
|
|
{
|
|
static function<TI(SRC const&)>
|
|
buildChangeHandler (Duration& target)
|
|
{
|
|
return bind (Link<TI,Duration>::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<class TAR>
|
|
struct Policy<Duration, typename disable_if< canReceiveDuration<TAR>,
|
|
Duration>::type, TAR>
|
|
{
|
|
static function<Duration(Duration const&)>
|
|
buildChangeHandler (TAR&)
|
|
{
|
|
return bind ( ignore_change_and_return_Zero );
|
|
}
|
|
|
|
static Duration
|
|
ignore_change_and_return_Zero()
|
|
{
|
|
return Duration::NIL;
|
|
}
|
|
};
|
|
|
|
template<class TI>
|
|
struct Policy<TI,TimeSpan,Duration>
|
|
{
|
|
static function<TI(TimeSpan const&)>
|
|
buildChangeHandler (Duration& target)
|
|
{
|
|
return bind (Link<TI,Duration>::useLengthAsChange, ref(target), _1 );
|
|
}
|
|
};
|
|
|
|
|
|
// special treatment for TimeSpan values---------------------------------------
|
|
|
|
template<class TI>
|
|
struct Policy<TI,Duration,TimeSpan>
|
|
{
|
|
static function<TI(Duration const&)>
|
|
buildChangeHandler (TimeSpan& target)
|
|
{
|
|
return bind (Link<TI,TimeSpan>::mutateLength, ref(target), _1 );
|
|
}
|
|
};
|
|
|
|
template<>
|
|
struct Policy<TimeSpan,TimeSpan,TimeSpan>
|
|
{
|
|
static function<TimeSpan(TimeSpan const&)>
|
|
buildChangeHandler (TimeSpan& target)
|
|
{
|
|
return bind (Link<TimeSpan,TimeSpan>::mutateTimeSpan, ref(target), _1 );
|
|
}
|
|
};
|
|
|
|
|
|
|
|
}}} // namespace lib::time::mutation
|
|
#endif
|