LUMIERA.clone/src/lib/time/mutation.hpp
Ichthyostega 685a9b84ee Library: replace boost::noncopyable by our own library solution
Benefits
 - get rid of yet another pervasive Boost dependency
 - define additional more fine grained policies (move only, clonable)
2018-03-24 05:35:13 +01:00

189 lines
7.1 KiB
C++

/*
MUTATION.hpp - changing and adjusting time values
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 mutation.hpp
** Modifying time and timecode values.
** Basically, time values are designed to be immutable. Under some circumstances
** we need a means to change existing time values though. Moreover, actually performing
** these changes gets quite involved at the implementation level, due to time / timecode
** values being \em quantised (grid aligned). To keep this implementation logic separate
** from the simple time values, and to allow for later extensions and more elaborate
** change schemes, we choose the <b>visitor pattern</b> for implementing these
** time mutating operations.
**
** \par usage situations
**
** There are four basic usage situations causing an existing time value to be mutated:
**
** - dragging an object or object boundary (resizing)
** - nudging
** - placing an object (e.g. a clip)
** - update of a timecode display in the GUI
**
** All these include sending a change message to the original time spec, while the
** actual content of the change message determines the concrete behaviour. Especially,
** when the target time to be changed or the message content are quantised, an interference
** between multiple time grids may be happen, possibly resulting in a re-quantisation of
** the message into the target object's own time grid.
**
** @note this header is defined such as to be used with or without including the time quantisation
** facility, which is a more heavyweight inclusion regarding code size and compilation times. For
** this toggle to work, the header `timequant.hpp` needs to be included \em prior to `mutation.hpp`
** Obviously, you can't define any mutation involving quantised (grid aligned) values without this.
** @warning when defining more fancy kinds of concrete mutations using the technique with
** the EncapsulatedMutation to hide the implementation, then please be sure to understand
** the size limitation for the implementation and maybe adjust MUTATION_IMPL_SIZE accordingly.
** @see TimeMutation_test
** @see TimeControl_test
**
*/
#ifndef LIB_TIME_MUTATION_H
#define LIB_TIME_MUTATION_H
#include "lib/error.hpp"
#include "lib/nocopy.hpp"
#include "lib/time/timevalue.hpp"
#include "lib/polymorphic-value.hpp"
#include "lib/symbol.hpp"
namespace lib {
namespace time {
using lib::Symbol;
LUMIERA_ERROR_DECLARE (INVALID_MUTATION); ///< Changing a time value in this way was not designated
class QuTime;
class Mutation;
/* The following typedefs allow to hand out
* "unspecific" mutation value objects from
* factory functions, without disclosing any
* implementation details here in this header.
*/
enum{ MUTATION_IMPL_SIZE = sizeof(TimeValue) };
typedef lib::polyvalue::CloneValueSupport<Mutation> ClonableMutation;
typedef lib::PolymorphicValue<Mutation, MUTATION_IMPL_SIZE, ClonableMutation> EncapsulatedMutation;
/**
* Interface: an opaque change imposed onto some time value.
*
* @see time::TimeSpan#accept(Mutation const&)
* @see TimeMutation_test
* @see polymorphic-value.hpp
*/
class Mutation
{
public:
virtual ~Mutation();
virtual void change (Duration&) const =0;
virtual void change (TimeSpan&) const =0;
virtual void change (QuTime&) const =0;
/* === convenience shortcuts for simple cases === */
static EncapsulatedMutation changeTime (Time);
static EncapsulatedMutation changeDuration (Duration);
static EncapsulatedMutation adjust (Offset);
static EncapsulatedMutation materialise (QuTime const&);
static EncapsulatedMutation nudge (int adjustment);
static EncapsulatedMutation nudge (int adjustment, Symbol gridID); ///<@note defined in common-services.cpp
#ifdef LIB_TIME_TIMEQUANT_H
static EncapsulatedMutation nudge (int adjustment, PQuant const& grid);
#endif
protected:
static TimeValue& imposeChange (TimeValue&, TimeValue const&);
static TimeValue& imposeChange (TimeValue&, Offset const&);
static TimeValue& imposeChange (TimeValue&, int);
static TimeValue& imposeChange (QuTime&, int);
};
/* === defining the visitation responses === */
inline void Duration::accept (Mutation const& muta) { muta.change (*this); }
inline void TimeSpan::accept (Mutation const& muta) { muta.change (*this); }
#ifdef LIB_TIME_TIMEQUANT_H
inline void QuTime::accept (Mutation const& muta) { muta.change (*this); }
#endif
/* === implementing the actual changes === */
/** @internal actually force a change into a target time entity to mutate.
* Mutation is declared fried to TimeValue and thus is allowed to influence
* the basic value stored in each time entity
*/
inline TimeValue&
Mutation::imposeChange (TimeValue& target, TimeValue const& valueToSet)
{
return target = valueToSet;
}
/** @internal variation to mutate a target time value by applying an offset */
inline TimeValue&
Mutation::imposeChange (TimeValue& target, Offset const& offset)
{
return imposeChange (target, TimeVar(target) += offset);
}
/** @internal nudge a target time value by a step wise offset.
* The standard case uses a fixed offset of 1 second per step //////////////////TICKET #810
*/
inline TimeValue&
Mutation::imposeChange (TimeValue& target, int steps)
{
return imposeChange (target, TimeVar(target) += Time(FSecs(steps)));
}
#ifdef LIB_TIME_TIMEQUANT_H
/** @internal Special treatment for quantised target values:
* use the quantised time's own grid; retrieve the corresponding grid point,
* offset it by the step-parameter, then retrieve the corresponding time from
* the quantised time's underlying quantiser (grid) and impose that as change.
* @note when the #steps parameter is zero, what happens here effectively
* is the materialisation of the quantised target time, i.e. making
* the quantisation explicit and storing the resulting value. */
inline TimeValue&
Mutation::imposeChange (QuTime& target, int steps)
{
PQuant const& grid (target);
FrameCnt originalGridPoint = grid->gridPoint(target);
FrameCnt adjustedGridPoint = originalGridPoint + steps;
return imposeChange (target, grid->timeOf (adjustedGridPoint));
}
#endif
}} // lib::time
#endif