You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
rosegarden/src/base/Event.h

582 lines
18 KiB

/*
Rosegarden
A sequencer and musical notation editor.
This program is Copyright 2000-2008
Guillaume Laurent <glaurent@telegraph-road.org>,
Chris Cannam <cannam@all-day-breakfast.com>,
Richard Bown <bownie@bownie.com>
The moral right of the authors to claim authorship of this work
has been asserted.
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. See the file
COPYING included with this distribution for more information.
*/
#ifndef _EVENT_H_
#define _EVENT_H_
#include "PropertyMap.h"
#include "Exception.h"
#include <string>
#include <vector>
#ifndef NDEBUG
#include <iostream>
#include <ctime>
#endif
namespace Rosegarden
{
typedef long timeT;
/**
* The Event class represents an event with some basic attributes and
* an arbitrary number of properties of dynamically-determined name
* and type.
*
* An Event has a type; a duration, often zero for events other than
* notes; an absolute time, the time at which the event begins, which
* is used to order events within a Segment; and a "sub-ordering", used
* to determine an order for events that have the same absolute time
* (for example to ensure that the clef always appears before the key
* signature at the start of a piece). Besides these, an event can
* have any number of properties, which are typed values stored and
* retrieved by name. Properties may be persistent or non-persistent,
* depending on whether they are saved to file with the rest of the
* event data or are considered to be only cached values that can be
* recomputed at will if necessary.
*/
class Event
{
public:
class NoData : public Exception {
public:
NoData(std::string property) :
Exception("No data found for property " + property) { }
NoData(std::string property, std::string file, int line) :
Exception("No data found for property " + property, file, line) { }
};
class BadType : public Exception {
public:
BadType(std::string property, std::string expected, std::string actl) :
Exception("Bad type for " + property + " (expected " +
expected + ", found " + actl + ")") { }
BadType(std::string property, std::string expected, std::string actual,
std::string file, int line) :
Exception("Bad type for " + property + " (expected " +
expected + ", found " + actual + ")", file, line) { }
};
Event(const std::string &type,
timeT absoluteTime, timeT duration = 0, short subOrdering = 0) :
m_data(new EventData(type, absoluteTime, duration, subOrdering)),
m_nonPersistentProperties(0) { }
Event(const std::string &type,
timeT absoluteTime, timeT duration, short subOrdering,
timeT notationAbsoluteTime, timeT notationDuration) :
m_data(new EventData(type, absoluteTime, duration, subOrdering)),
m_nonPersistentProperties(0) {
setNotationAbsoluteTime(notationAbsoluteTime);
setNotationDuration(notationDuration);
}
Event(const Event &e) :
m_nonPersistentProperties(0) { share(e); }
// these ctors can't use default args: default has to be obtained from e
Event(const Event &e, timeT absoluteTime) :
m_nonPersistentProperties(0) {
share(e);
unshare();
m_data->m_absoluteTime = absoluteTime;
setNotationAbsoluteTime(absoluteTime);
setNotationDuration(m_data->m_duration);
}
Event(const Event &e, timeT absoluteTime, timeT duration) :
m_nonPersistentProperties(0) {
share(e);
unshare();
m_data->m_absoluteTime = absoluteTime;
m_data->m_duration = duration;
setNotationAbsoluteTime(absoluteTime);
setNotationDuration(duration);
}
Event(const Event &e, timeT absoluteTime, timeT duration, short subOrdering):
m_nonPersistentProperties(0) {
share(e);
unshare();
m_data->m_absoluteTime = absoluteTime;
m_data->m_duration = duration;
m_data->m_subOrdering = subOrdering;
setNotationAbsoluteTime(absoluteTime);
setNotationDuration(duration);
}
Event(const Event &e, timeT absoluteTime, timeT duration, short subOrdering,
timeT notationAbsoluteTime) :
m_nonPersistentProperties(0) {
share(e);
unshare();
m_data->m_absoluteTime = absoluteTime;
m_data->m_duration = duration;
m_data->m_subOrdering = subOrdering;
setNotationAbsoluteTime(notationAbsoluteTime);
setNotationDuration(duration);
}
Event(const Event &e, timeT absoluteTime, timeT duration, short subOrdering,
timeT notationAbsoluteTime, timeT notationDuration) :
m_nonPersistentProperties(0) {
share(e);
unshare();
m_data->m_absoluteTime = absoluteTime;
m_data->m_duration = duration;
m_data->m_subOrdering = subOrdering;
setNotationAbsoluteTime(notationAbsoluteTime);
setNotationDuration(notationDuration);
}
~Event() { lose(); }
Event *copyMoving(timeT offset) const {
return new Event(*this,
m_data->m_absoluteTime + offset,
m_data->m_duration,
m_data->m_subOrdering,
getNotationAbsoluteTime() + offset,
getNotationDuration());
}
Event &operator=(const Event &e) {
if (&e != this) { lose(); share(e); }
return *this;
}
friend bool operator<(const Event&, const Event&);
// Accessors
const std::string &getType() const { return m_data->m_type; }
bool isa(const std::string &t) const { return (m_data->m_type == t); }
timeT getAbsoluteTime() const { return m_data->m_absoluteTime; }
timeT getDuration() const { return m_data->m_duration; }
short getSubOrdering() const { return m_data->m_subOrdering; }
bool has(const PropertyName &name) const;
template <PropertyType P>
typename PropertyDefn<P>::basic_type get(const PropertyName &name) const;
// throw (NoData, BadType);
// no throw, returns bool
template <PropertyType P>
bool get(const PropertyName &name, typename PropertyDefn<P>::basic_type &val) const;
template <PropertyType P>
bool isPersistent(const PropertyName &name) const;
// throw (NoData);
template <PropertyType P>
void setPersistence(const PropertyName &name, bool persistent);
// throw (NoData);
PropertyType getPropertyType(const PropertyName &name) const;
// throw (NoData);
std::string getPropertyTypeAsString(const PropertyName &name) const;
// throw (NoData);
std::string getAsString(const PropertyName &name) const;
// throw (NoData);
template <PropertyType P>
void set(const PropertyName &name, typename PropertyDefn<P>::basic_type value,
bool persistent = true);
// throw (BadType);
// set non-persistent, but only if there's no persistent value already
template <PropertyType P>
void setMaybe(const PropertyName &name, typename PropertyDefn<P>::basic_type value);
// throw (BadType);
template <PropertyType P>
void setFromString(const PropertyName &name, std::string value,
bool persistent = true);
// throw (BadType);
void unset(const PropertyName &name);
timeT getNotationAbsoluteTime() const { return m_data->getNotationTime(); }
timeT getNotationDuration() const { return m_data->getNotationDuration(); }
typedef std::vector<PropertyName> PropertyNames;
PropertyNames getPropertyNames() const;
PropertyNames getPersistentPropertyNames() const;
PropertyNames getNonPersistentPropertyNames() const;
void clearNonPersistentProperties();
struct EventCmp
{
bool operator()(const Event &e1, const Event &e2) const {
return e1 < e2;
}
bool operator()(const Event *e1, const Event *e2) const {
return *e1 < *e2;
}
};
struct EventEndCmp
{
bool operator()(const Event &e1, const Event &e2) const {
return e1.getAbsoluteTime() + e1.getDuration() <=
e2.getAbsoluteTime() + e2.getDuration();
}
bool operator()(const Event *e1, const Event *e2) const {
return e1->getAbsoluteTime() + e1->getDuration() <=
e2->getAbsoluteTime() + e2->getDuration();
}
};
static bool compareEvent2Time(const Event *e, timeT t) {
return e->getAbsoluteTime() < t;
}
static bool compareTime2Event(timeT t, const Event *e) {
return t < e->getAbsoluteTime();
}
// approximate, for debugging and inspection purposes
size_t getStorageSize() const;
/**
* Get the XML string representing the object.
*/
std::string toXmlString();
/**
* Get the XML string representing the object. If the absolute
* time of the event differs from the given absolute time, include
* the difference between the two as a timeOffset attribute.
* If expectedTime == 0, include an absoluteTime attribute instead.
*/
std::string toXmlString(timeT expectedTime);
#ifndef NDEBUG
void dump(std::ostream&) const;
#else
void dump(std::ostream&) const {}
#endif
static void dumpStats(std::ostream&);
protected:
// these are for subclasses such as XmlStorableEvent
Event() :
m_data(new EventData("", 0, 0, 0)),
m_nonPersistentProperties(0) { }
void setType(const std::string &t) { unshare(); m_data->m_type = t; }
void setAbsoluteTime(timeT t) { unshare(); m_data->m_absoluteTime = t; }
void setDuration(timeT d) { unshare(); m_data->m_duration = d; }
void setSubOrdering(short o) { unshare(); m_data->m_subOrdering = o; }
void setNotationAbsoluteTime(timeT t) { unshare(); m_data->setNotationTime(t); }
void setNotationDuration(timeT d) { unshare(); m_data->setNotationDuration(d); }
private:
bool operator==(const Event &); // not implemented
struct EventData // Data that are shared between shallow-copied instances
{
EventData(const std::string &type,
timeT absoluteTime, timeT duration, short subOrdering);
EventData(const std::string &type,
timeT absoluteTime, timeT duration, short subOrdering,
const PropertyMap *properties);
EventData *unshare();
~EventData();
unsigned int m_refCount;
std::string m_type;
timeT m_absoluteTime;
timeT m_duration;
short m_subOrdering;
PropertyMap *m_properties;
// These are properties because we don't care so much about
// raw speed in get/set, but we do care about storage size for
// events that don't have them or that have zero values:
timeT getNotationTime() const;
timeT getNotationDuration() const;
void setNotationTime(timeT t) {
setTime(NotationTime, t, m_absoluteTime);
}
void setNotationDuration(timeT d) {
setTime(NotationDuration, d, m_duration);
}
private:
EventData(const EventData &);
EventData &operator=(const EventData &);
static PropertyName NotationTime;
static PropertyName NotationDuration;
void setTime(const PropertyName &name, timeT value, timeT deft);
};
EventData *m_data;
PropertyMap *m_nonPersistentProperties; // Unique to an instance
void share(const Event &e) {
m_data = e.m_data;
m_data->m_refCount++;
}
bool unshare() { // returns true if unshare was necessary
if (m_data->m_refCount > 1) {
m_data = m_data->unshare();
return true;
} else {
return false;
}
}
void lose() {
if (--m_data->m_refCount == 0) delete m_data;
delete m_nonPersistentProperties;
m_nonPersistentProperties = 0;
}
// returned iterator (in i) only valid if return map value is non-zero
PropertyMap *find(const PropertyName &name, PropertyMap::iterator &i);
const PropertyMap *find(const PropertyName &name,
PropertyMap::const_iterator &i) const {
PropertyMap::iterator j;
PropertyMap *map = const_cast<Event *>(this)->find(name, j);
i = j;
return map;
}
PropertyMap::iterator insert(const PropertyPair &pair, bool persistent) {
PropertyMap **map =
(persistent ? &m_data->m_properties : &m_nonPersistentProperties);
if (!*map) *map = new PropertyMap();
return (*map)->insert(pair).first;
}
#ifndef NDEBUG
static int m_getCount;
static int m_setCount;
static int m_setMaybeCount;
static int m_hasCount;
static int m_unsetCount;
static clock_t m_lastStats;
#endif
};
template <PropertyType P>
bool
Event::get(const PropertyName &name, typename PropertyDefn<P>::basic_type &val) const
{
#ifndef NDEBUG
++m_getCount;
#endif
PropertyMap::const_iterator i;
const PropertyMap *map = find(name, i);
if (map) {
PropertyStoreBase *sb = i->second;
if (sb->getType() == P) {
val = (static_cast<PropertyStore<P> *>(sb))->getData();
return true;
}
else {
#ifndef NDEBUG
std::cerr << "Event::get() Error: Attempt to get property \"" << name
<< "\" as " << PropertyDefn<P>::typeName() <<", actual type is "
<< sb->getTypeName() << std::endl;
#endif
return false;
}
} else {
return false;
}
}
template <PropertyType P>
typename PropertyDefn<P>::basic_type
Event::get(const PropertyName &name) const
// throw (NoData, BadType)
{
#ifndef NDEBUG
++m_getCount;
#endif
PropertyMap::const_iterator i;
const PropertyMap *map = find(name, i);
if (map) {
PropertyStoreBase *sb = i->second;
if (sb->getType() == P)
return (static_cast<PropertyStore<P> *>(sb))->getData();
else {
throw BadType(name.getName(),
PropertyDefn<P>::typeName(), sb->getTypeName(),
__FILE__, __LINE__);
}
} else {
#ifndef NDEBUG
std::cerr << "Event::get(): Error dump follows:" << std::endl;
dump(std::cerr);
#endif
throw NoData(name.getName(), __FILE__, __LINE__);
}
}
template <PropertyType P>
bool
Event::isPersistent(const PropertyName &name) const
// throw (NoData)
{
PropertyMap::const_iterator i;
const PropertyMap *map = find(name, i);
if (map) {
return (map == m_data->m_properties);
} else {
throw NoData(name.getName(), __FILE__, __LINE__);
}
}
template <PropertyType P>
void
Event::setPersistence(const PropertyName &name, bool persistent)
// throw (NoData)
{
unshare();
PropertyMap::iterator i;
PropertyMap *map = find(name, i);
if (map) {
insert(*i, persistent);
map->erase(i);
} else {
throw NoData(name.getName(), __FILE__, __LINE__);
}
}
template <PropertyType P>
void
Event::set(const PropertyName &name, typename PropertyDefn<P>::basic_type value,
bool persistent)
// throw (BadType)
{
#ifndef NDEBUG
++m_setCount;
#endif
// this is a little slow, could bear improvement
unshare();
PropertyMap::iterator i;
PropertyMap *map = find(name, i);
if (map) {
bool persistentBefore = (map == m_data->m_properties);
if (persistentBefore != persistent) {
i = insert(*i, persistent);
map->erase(name);
}
PropertyStoreBase *sb = i->second;
if (sb->getType() == P) {
(static_cast<PropertyStore<P> *>(sb))->setData(value);
} else {
throw BadType(name.getName(),
PropertyDefn<P>::typeName(), sb->getTypeName(),
__FILE__, __LINE__);
}
} else {
PropertyStoreBase *p = new PropertyStore<P>(value);
insert(PropertyPair(name, p), persistent);
}
}
// setMaybe<> is actually called rather more frequently than set<>, so
// it makes sense for best performance to implement it separately
// rather than through calls to has, isPersistent and set<>
template <PropertyType P>
void
Event::setMaybe(const PropertyName &name, typename PropertyDefn<P>::basic_type value)
// throw (BadType)
{
#ifndef NDEBUG
++m_setMaybeCount;
#endif
unshare();
PropertyMap::iterator i;
PropertyMap *map = find(name, i);
if (map) {
if (map == m_data->m_properties) return; // persistent, so ignore it
PropertyStoreBase *sb = i->second;
if (sb->getType() == P) {
(static_cast<PropertyStore<P> *>(sb))->setData(value);
} else {
throw BadType(name.getName(),
PropertyDefn<P>::typeName(), sb->getTypeName(),
__FILE__, __LINE__);
}
} else {
PropertyStoreBase *p = new PropertyStore<P>(value);
insert(PropertyPair(name, p), false);
}
}
template <PropertyType P>
void
Event::setFromString(const PropertyName &name, std::string value, bool persistent)
// throw (BadType)
{
set<P>(name, PropertyDefn<P>::parse(value), persistent);
}
//////////////////////////////////////////////////////////////////////
}
#endif