From email, 2002 01 04
To create a new sort of event, you just create an Event with a type name that hasn't been used before, and to add properties, you just use a new property name.
(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. Sorry, I digress.)
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.)
Aside from the properties, 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).
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).
Rosegarden's file format is a gzipped XML file with a .rg extension. (We use zlib to read and write files.)
The most basic XML elements are event and property. Event has a type, subordering (sorry, forgot to describe that, it's not terribly important, it just indicates the ordering of events in a segment where the absolute times are equal – so we can display clef before key before any notes at that time, etc) and duration, and then the event element contains a series of property elements each of which specifies a single property by name and type. Events are assumed to start at the time at which the previous event ended, except when overridden by a chord element (within which all events start at once) or a resync element (which specifies the starting time of the following event, after which events continue to count from there – this is too verbose, we should change it to an optional absolute time property on the event element).