256 lines
9.5 KiB
C++
256 lines
9.5 KiB
C++
/*
|
|
CONTROL.hpp - a life time control for feedback and mutation
|
|
|
|
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.hpp
|
|
** Manipulating and monitoring time entities with life changes.
|
|
** This is an control- and callback element to handle any kind of "running"
|
|
** time entities. This element is to be provided by the client and then attached
|
|
** to the target time entity as a time::Mutation. Internally, a life connection to
|
|
** the target is built, allowing both to
|
|
** - to manipulate the target by invoking the function operator
|
|
** - to receive change notifications by installing a callback functor.
|
|
**
|
|
** The actual type of the changes and modifications is specified as template parameter;
|
|
** when later attached to some time entity as a Mutation, the actual changes to be performed
|
|
** depend both on this change type and the type of the target time entity (double dispatch).
|
|
** The behaviour is similar to applying a static time::Mutation
|
|
**
|
|
** \par relevance
|
|
** This control element is intended to be used for all kinds of editing and monitoring
|
|
** of time-like entities -- be it the running time display in a GUI widget, a ruler marker
|
|
** which can be dragged, a modifiable selection or the animated playhead cursor.
|
|
**
|
|
** \par usage scenarios
|
|
** The time::Control element provides mediating functionality, but doesn't assume or provide
|
|
** anything special regarding the usage pattern or the lifecycle, beyond the ability to
|
|
** attach listeners, attach to a (different) target and to detach from all connections.
|
|
** Especially, no assumptions are made about which side is the server or the client
|
|
** and who owns the time::Control element.
|
|
**
|
|
** Thus an interface might accept a time::Control element \em reference (e.g. the
|
|
** lumiera::Play::Controller uses this pattern) -- meaning that the client owns the
|
|
** Control element and might attach listeners, while the implementation (server side)
|
|
** will attach the Control to mutate an time value entity otherwise not disclosed
|
|
** (e.g. the playhead position of the playback process). Of course, in this case
|
|
** the client is responsible for keeping the Control element and all listeners
|
|
** alive, and to invoke Control#disconnect prior to destroying the element.
|
|
**
|
|
** Of course, the reversed usage situation would be possible as well: an interface
|
|
** exposing a time::Control, thus allowing to attach target and listeners, while the
|
|
** actual changes will originate somewhere within the service implementation.
|
|
**
|
|
** Another usage pattern would be to expose a time::Control \c const&, allowing only to
|
|
** impose changes, but not to change the target or listener attachments. To the contrary,
|
|
** when exposing only a time::Mutation \c const& through an interface allows only to
|
|
** attach new target elements, but not to change listeners or feed any value changes.
|
|
**
|
|
** Using time::Control as an implementation building block and just exposing the
|
|
** change (function) operators or the listener attachment through an forwarding sub
|
|
** interface is another option.
|
|
**
|
|
** @note time::Control is default constructible and freely copyable.
|
|
**
|
|
**
|
|
** \par changing quantised (grid aligned) time entities
|
|
**
|
|
** The time::Control element includes the functionality 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.
|
|
**
|
|
** \par implementation notes
|
|
** - the validity of a given combination of change and target is checked immediately,
|
|
** when connecting to the target. Depending on the situation, the actual changes later
|
|
** are subject to specific treatment (e.g. frame quantisation)
|
|
** - by default time::Control is <b>not threadsafe</b>. But, as each change is basically
|
|
** processed within its own call context (function invocation), parallelism is only
|
|
** a concern with respect to the value finally visible within the target.
|
|
** - the change notification is processed right away, after applying the change to the
|
|
** target; of course there is a race between applying the value and building the
|
|
** response value passed on as notification. In all cases, the effective change
|
|
** notification value is built from the state of the target after applying
|
|
** the change, which might or might not reflect the change value passed in.
|
|
**
|
|
** @todo include support for QuTimeSpan values ////////////////////TICKET #760
|
|
**
|
|
** @see TimeControl_test
|
|
**
|
|
*/
|
|
|
|
#ifndef LIB_TIME_CONTROL_H
|
|
#define LIB_TIME_CONTROL_H
|
|
|
|
#include "lib/time/mutation.hpp"
|
|
#include "lib/time/timevalue.hpp"
|
|
#include "lib/time/control-impl.hpp"
|
|
|
|
|
|
|
|
namespace lib {
|
|
namespace time {
|
|
|
|
|
|
|
|
/**
|
|
* Frontend/Interface: controller-element to retrieve
|
|
* and change running time values. time::Control is
|
|
* a mediator element, which can be attached to some
|
|
* time value entities as \em mutation, and at the
|
|
* same time allows to register listeners. When
|
|
* configured this way, \em changes may be fed
|
|
* to the function operator(s). These changes
|
|
* will be imposed to the connected target
|
|
* and the result propagated to the listeners.
|
|
*
|
|
* @see time::Mutation
|
|
* @see time::TimeSpan#accept(Mutation const&)
|
|
*/
|
|
template<class TI>
|
|
class Control
|
|
: public mutation::Mutator<TI>
|
|
{
|
|
mutation::Propagator<TI> notifyListeners_;
|
|
|
|
virtual void change (Duration&) const;
|
|
virtual void change (TimeSpan&) const;
|
|
virtual void change (QuTime&) const;
|
|
|
|
public:
|
|
void operator() (TI const&) const;
|
|
void operator() (Offset const&) const;
|
|
void operator() (int) const;
|
|
|
|
|
|
/** install a callback functor to be invoked as notification
|
|
* for any changes imposed onto the observed time entity.
|
|
* @param toNotify object with \c operator()(TI const&) */
|
|
template<class SIG>
|
|
void connectChangeNotification (SIG const& toNotify);
|
|
|
|
/** disconnect from observed entity and
|
|
* cease any change notification */
|
|
void disconnect();
|
|
};
|
|
|
|
|
|
|
|
|
|
/* === forward to implementation === */
|
|
|
|
/** impose a new value to the connected target.
|
|
* If applicable, the target will afterwards reflect
|
|
* that change, and listeners will be notified, passing
|
|
* the target's new state.
|
|
* @throw error::State if not connected to a target
|
|
* @note the actual change in the target also depends
|
|
* on the concrete target type and the type of
|
|
* the change. By default, the time value is
|
|
* changed; this may include grid alignment.
|
|
*/
|
|
template<class TI>
|
|
void
|
|
Control<TI>::operator () (TI const& newValue) const
|
|
{
|
|
this->ensure_isArmed();
|
|
notifyListeners_(
|
|
this->setVal_(newValue));
|
|
}
|
|
|
|
/** impose an offset to the connected target.
|
|
* If applicable, the target will be adjusted by the
|
|
* time offset, and listeners will be notified.
|
|
* @throw error::State if not connected to a target
|
|
*/
|
|
template<class TI>
|
|
void
|
|
Control<TI>::operator () (Offset const& adjustment) const
|
|
{
|
|
this->ensure_isArmed();
|
|
notifyListeners_(
|
|
this->offset_(adjustment));
|
|
}
|
|
|
|
/** nudge the connected target by the given offset steps,
|
|
* using either the target's own grid (when quantised),
|
|
* or a 'natural' nudge grid
|
|
* @throw error::State if not connected to a target
|
|
*/
|
|
template<class TI>
|
|
void
|
|
Control<TI>::operator () (int offset_by_steps) const
|
|
{
|
|
this->ensure_isArmed();
|
|
notifyListeners_(
|
|
this->nudge_(offset_by_steps));
|
|
}
|
|
|
|
|
|
template<class TI>
|
|
void
|
|
Control<TI>::disconnect()
|
|
{
|
|
notifyListeners_.disconnect();
|
|
this->unbind();
|
|
}
|
|
|
|
template<class TI>
|
|
template<class SIG>
|
|
void
|
|
Control<TI>::connectChangeNotification (SIG const& toNotify)
|
|
{
|
|
if (this->offset_)
|
|
{ // we're already connected: thus propagate current value
|
|
TI currentValue = this->offset_(Offset::ZERO);
|
|
toNotify (currentValue);
|
|
}
|
|
notifyListeners_.attach (toNotify);
|
|
}
|
|
|
|
|
|
/* ==== Implementation of the Mutation interface ==== */
|
|
|
|
template<class TI>
|
|
void
|
|
Control<TI>::change (Duration& targetDuration) const
|
|
{
|
|
this->bind_to (targetDuration);
|
|
}
|
|
|
|
template<class TI>
|
|
void
|
|
Control<TI>::change (TimeSpan& targetInterval) const
|
|
{
|
|
this->bind_to (targetInterval);
|
|
}
|
|
|
|
template<class TI>
|
|
void
|
|
Control<TI>::change (QuTime& targetQuTime) const
|
|
{
|
|
this->bind_to (targetQuTime);
|
|
}
|
|
|
|
}} // lib::time
|
|
#endif
|