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.
582 lines
18 KiB
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
|