Table of Contents
From email, 2002 01 04
An Event is the object type in Rosegarden that corresponds to a musical note. It can also represent a rest, a time signature, a tempo change, a key signature. In fact, just about anything that lives at a particular time in the score is represented as an event.
How does Event manage to represent all those different things? Well, it doesn't use polymorphism - Event is a single C++ class. It uses properties.
Properties are stored inside events in a map from property name-string to property value. They are not C++ fields, and in fact you would have to dig fairly deep in the code to get their real representation.
There are four: Int, String, Bool, and RealTimeT.
Technically, they are template specializations on 4 enumeration values. They are defined in Property.h. Other than knowing the names, you probably don't need to pay attention to that part.
Event nominal type
An event also stores a string that declares its nominal type. This is a std::string. Its text isn't important, though it should be descriptive, but it should be unique.
Other data that event stores
Aside from the properties and type string, an Event also has an absolute time (the time in Rosegarden units – see ../data_struct/units.txt – at which the event begins), a duration (the “performance” duration of the event in the same units, so currently always zero for anything but note or rest events) and a sub-ordering value, which is used to order events that have the same absolute time (it defaults to, and most usually is, zero; but for some things like clef events it's a small negative number so as to ensure they get processed and displayed before any notes that appear at the same time). Unlike properties, the time and subordering values can only be set once, when you create the event – to modify them you must copy the event and pass new values into the copy constructor (see the header file for details, and see iterators.txt for discussion of how this affects Segment iterators that may be pointing at those events).
How to create a new sort of event
To create a new sort of event, you will need these things:
These things are typically provided by an event adapter, but we won't get into that here.
So, for example, let's say you want a new “cow” event and you want to give it a couple of properties, say a string property for moo type (Moo, Low or Croak – cows with colds sound terrible) and a bool to indicate whether it is in fact a bull. (There are three property types; the other's int, and they're defined in Property.h.)
All you have to do to create a cow and insert it in a segment is
Event *e = new Event("cow", 1000, 0); // abs time, duration e->set<String>("MooType", "Low"); e->set<Bool>("isBull", true); segment->insert(e);
The strings are fairly arbitrary – you just have to ensure that nobody else has already used an event called cow or properties called MooType and isBull. Of course, you don't see much real code using literal strings; the event types are generally just declared as string constants in various places (actually almost all in NotationTypes.h), but the property names are usually declared as constant PropertyName objects (in NotationTypes, BaseProperties or notationproperties, most usually) which is a class that's constructed from a string constant but interns to an integer for faster lookup, not that you need to deal with that at all.
As another example, say you want to add a property to note events that indicates the usual fret number. To do that, you could declare a FRET_NUMBER property name in BaseProperties or notationproperties (with a value of, say, “FretNumber”) and then do stuff like
Segment::iterator i = getIteratorFromSomewhere(); Event *e = (*i); e->set<Int>(BaseProperties::FRET_NUMBER, 4); ++i; long fret = (*i)->get<Int>(BaseProperties::FRET_NUMBER);
The get/set code is obviously more laborious than it would be with fixed accessor methods in classes, but it's good when you haven't quite decided which properties you need, and it means you can extend other peoples' events, and it means you get I/O done for you.
Another thing is, you may notice various calls to “setMaybe” on Events as well as to “set”. This is because Events distinguish between persistent properties (which have usually been set by the user through the GUI somehow) that get streamed out to the XML file, and non-persistent ones (usually resulting from caching following some calculation, and usually possible to recalculate whenever required if necessary) that don't. The set method can take an extra argument specifying whether you're setting a persistent or non-persistent property (the default is persistent), but it's rarely used: instead to set non-persistent properties we generally use setMaybe, which sets a non-persistent property only if there is no existing persistent property of the same name. This allows the user easily to override computed values, for example in the stem-direction menu functions on the score view (the user's settings are persistent, the computed ones are not).
Uniqueness of property names
We have a big potential problem with namespacing property names, as there's no central registry of names that have been used. I've been considering adding something that might help, because we do actually intern PropertyName strings, we just don't yet complain about duplicates definitions – but it won't prevent people from using literal strings that collide. And property names are global, they aren't local to a particular event type.
Saving and restoring properties
This part is easy. You don't have to do anything special when you use a new property type. Rosegarden automatically writes all of an event's properties to the savefile, and automatically reads all of them when you restore. See XML_format
When you adapt Event for a new purpose, it's often helpful to create an adapter class for them. If you examine the files NotationTypes.h and NotationTypes.cpp, you will notice a number of classes that adapt Event for time signature, key signature, etc.
One word: NotationTypes is already crowded. We have a policy of putting visible classes in a pair of files of their own name, but NotationTypes flagrantly violates that rule. Please don't follow our bad example.
Typically an adapter includes: