// -*- c-basic-offset: 4 -*- /* Rosegarden A sequencer and musical notation editor. This program is Copyright 2000-2008 Guillaume Laurent , Chris Cannam , Richard Bown 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 #include #ifndef NDEBUG #include #include #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 typename PropertyDefn

::basic_type get(const PropertyName &name) const; // throw (NoData, BadType); // no throw, returns bool template bool get(const PropertyName &name, typename PropertyDefn

::basic_type &val) const; template bool isPersistent(const PropertyName &name) const; // throw (NoData); template 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 void set(const PropertyName &name, typename PropertyDefn

::basic_type value, bool persistent = true); // throw (BadType); // set non-persistent, but only if there's no persistent value already template void setMaybe(const PropertyName &name, typename PropertyDefn

::basic_type value); // throw (BadType); template 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 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(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 bool Event::get(const PropertyName &name, typename PropertyDefn

::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 *>(sb))->getData(); return true; } else { #ifndef NDEBUG std::cerr << "Event::get() Error: Attempt to get property \"" << name << "\" as " << PropertyDefn

::typeName() <<", actual type is " << sb->getTypeName() << std::endl; #endif return false; } } else { return false; } } template typename PropertyDefn

::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 *>(sb))->getData(); else { throw BadType(name.getName(), PropertyDefn

::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 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 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 void Event::set(const PropertyName &name, typename PropertyDefn

::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 *>(sb))->setData(value); } else { throw BadType(name.getName(), PropertyDefn

::typeName(), sb->getTypeName(), __FILE__, __LINE__); } } else { PropertyStoreBase *p = new PropertyStore

(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 void Event::setMaybe(const PropertyName &name, typename PropertyDefn

::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 *>(sb))->setData(value); } else { throw BadType(name.getName(), PropertyDefn

::typeName(), sb->getTypeName(), __FILE__, __LINE__); } } else { PropertyStoreBase *p = new PropertyStore

(value); insert(PropertyPair(name, p), false); } } template void Event::setFromString(const PropertyName &name, std::string value, bool persistent) // throw (BadType) { set

(name, PropertyDefn

::parse(value), persistent); } ////////////////////////////////////////////////////////////////////// } #endif