When I did my extended error reporting modification to LyX, I added Inset Visitors. I found them to be very useful...
For those unfamiliar with the Visitor pattern, I strongly suggest you grab a copy of "Design Patterns", by Gamma et al. See page 331. Well worth the read... but, I'll attempt to summarise the properties of the pattern here. The Visitor pattern is useful for processing different elements in a polymorphic structure (eg the document tree). It allows you to define operations upon different classes (eg Inset types) differently depending upon the class type, without modifying the original class. Consider the following infrastructure: class InsetVisitor; class Inset { // blah blah virtual void Accept(InsetVisitor&) = 0; }; class InsetA : public Inset { // blah blah virtual void Accept(InsetVisitor& iv ) { iv.VisitInsetA(*this); } }; class InsetB : public Inset { // blah blah virtual void Accept(InsetVisitor& iv ) { iv.VisitInsetB(*this); } }; class InsetVisitor { // blah blah virtual void VisitInsetA( InsetA& ) = 0; virtual void VisitInsetB( InsetB& ) = 0; }; I may now extract the, for example, spell-checking methods from LyXParagraph, and define a single visitor which knows what to do to any particular kind of inset to perform spell-checking on it (that was a bad example to pick, maybe someone else can come up with a better one...): class SpellCheckInsetVisitor : public InsetVisitor { // blah blah virtual void VisitInsetA( InsetA& i ) { // Carry out spellcheck on contents of InsetA } virtual void VisitInsetB( InsetB& i ) { // InsetB doesn't have any text that you can spellcheck, so do nothing. } }; This means that all the 'spellchecking' (or whatever) code related to a single kind of operation is contained within a single visitor class instead of being strewn all over the code in methods of individual insets. To use it, the spell checker driver does this: SpellCheckerVisitor spell; for( InsetIterator i = insets.begin(); i != insets.end(); ++i ) { i->Accept(spell); } (I usually define some fancier ways of doing this so that information can be passed to the spell checker object, but this is simplest for illustration.) The call to Inset::Accept bounces to the visitor's VisitInsetA or VisitInsetB method depending on what type of inset it is, according to the vtable ie it's efficient. I actually used these for traversing the document and collecting labels, citations, bibliography boxes, and ERTs for use in error reporting, but they have so many other uses as well. The wonderful thing about it is that you only need to add one method to every inset - the Accept method. From then on, you are free to define new operations upon insets without touching the insets themselves... Note that the operations contained within the visitor classes can only utilise public interfaces on the insets they manipulate. This means that the Inset interface must be complete. - no fiddling with private innards may be done this way. This pattern does have a disadvantage: If you keep adding new insets types, you have to go and add methods for them all to all the different kinds of inset visitors. This may be overcome by creating an inset visitor class which does nothing - ie all abstract methods from the base class are implemented as no-ops. Actual operation visitors would then inherit from this and only override the methods that they need. However, this leads to 'forgetting' to add the required methods to the visitors in the few cases where they are really necessary, as the compiler will no longer complain... you will just get a silent nop where perhaps you expected to spellcheck the contents of a caption. These kinds of problems are easy to fix, just not so easy to detect. Anyway, I found that using this pattern gave me incredible flexibility in implementing my extended error handling code, and I wondered if anyone else thought there might be applications for it in the current LyX code base. Ben.