/* MUTATION.hpp - changing and adjusting time values 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 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 visitor pattern 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. ** ** @todo WIP-WIP-WIP ** */ #ifndef LIB_TIME_MUTATION_H #define LIB_TIME_MUTATION_H #include "lib/error.hpp" #include "lib/time/timevalue.hpp" #include "lib/polymorphic-value.hpp" #include "lib/symbol.hpp" #include //#include //#include //#include namespace lib { namespace time { using lib::Symbol; //using std::string; //using lib::Literal; 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 ClonableMutation; typedef lib::PolymorphicValue EncapsulatedMutation; /** * Interface: an opaque change imposed onto some time value. * * @see time::TimeSpan#accept(Mutation const&) * @todo WIP-WIP-WIP */ 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); 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_TIMEQUQNT_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_TIMEQUQNT_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); int64_t originalGridPoint = grid->gridPoint(target); int64_t adjustedGridPoint = originalGridPoint + steps; return imposeChange (target, grid->timeOf (adjustedGridPoint)); } #endif }} // lib::time #endif