| Story Library |
Either visitor will visit ranges. If, for example, you use Story::CVisitorLogical::VisitSelection() and the selection covers just part of a line, VisitLine() will be called with iterators describing the part involved. You can then use these iterators to visit every glyph. The methods which visit an entire container take no iterators but invoke the methods which do, passing them the container's begin() and end().
By convention, the class names of visitors begins with "CV". Public examples include Story::CVHitTest, which helps convert mouse coordinates in a Story::CLayPos, and Story::CVRenderer which helps draw the text. There are many private visitors used behind the scenes, too. For example, Story::CCmdApplyGlyphDelta uses a couple to change the glyph attributes of selected text, and Story::CExportFormatRtf uses one to visit the parts of the story being exported. Recall that commands are given a Story::CSelection argument to work on when they are executed, so many of them use a visitor as a convenient way to process the selected text.
Visitor base classes don't inherit from CObject, which makes them easy to combine with classes that do (or with each other) by using multiple inheritance. For example, to count the selected letter 'e's you could make a subclass of both Story::CVisitorGlyph and Story::CVisitorLogical, and use the VisitChar() method describe earlier and a method like:
void VisitParagraph( Story::CParagraph *pParagraph, iterator first, iterator last ) { for (iterator i = first; i != last; ++i) i->Accept( this ); }
to call it. This method overrides a Story::CVisitorLogical routine and iterates over the selected part of the paragraph, invoking the glyph visitor on each glyph.
You can restrict visits to a branch of the container hierarchy, eg to a single column, by invoking the visitor just on that column instead of on CLayout. You can truncate an iteration, eg so that it visits columns but not lines, by overriding the Visit( Story::CColumn * ) routine and returning without calling the base-class version.
Sometimes inheritance is inconvenient (eg if you need several different visitors at once). The engine provides two templated forwarding classes to help, called Story::CVisitorLogicalCallback1 and Story::CVisitorLogicalCallback3. This takes a pointer to a member function of your class which will be called by VisitParagraph. The suffix "1" or "3" indicates how many arguments the member function takes. For example:
void MyClass::ApplySelection( CSelection *pSelection )
{
CVisitorLogicalCallback3<MyClass> visitor( this, &MyClass::ApplyParagraph );
visitor.VisitSelection( pSelection );
}
void MyClass::ApplyParagraph( CParagraph *pParagraph, iterator first, iterator last )
{
for (iterator i = first; i != last; ++i)
i->Accept( this );
}
There are several other ways to iterate over stories, apart from visitors. If you just want to get at, say, all the columns in a given layout, you can use the STL-style iterators which each of the containers supplies. For example, Story::CLayout::begin() and Story::CLayout::end(). This is quite efficient and is what the visitors do internally.
The container iterators for Story::CParagraph and Story::CLinePart, which iterate over glyph states, are used especially widely within the engine. They are typedef'd as Story::LogGlyphStateIterator and Story::LayGlyphStateIterator (with const variants), and forward-declared in GlyphStateIterator.h, to reduce dependancies on their respective containers.
Story::WidthGlyphStateIterator is a variant of Story::LayGlyphStateIterator that keeps track of the total width of the glyphs over which it has moved. It is used by the compositor.
Another way of iterating is to get hold of the first container you are interested in (eg a Story::CLinePart) and then call methods like GetNextLine() or GetPrevLine().The containers provide methods like GetFirstLine() and GetLastLine() to provide starting points. GetNextLine() on the last line of a column will return the first line of the next column; these methods will cross container boundaries. This scheme is less efficient than using visitors or STL-style iterators.
The final way of getting at the glyphs of a story is to use one of many whole-story iterator objects. The simplest is Story::CLogPos. This is effectively an STL-style iterator object for the entire story and you can think of it as a pointer to a Story::CLogGlyphState. As such it provides access to a glyph, its formatting as well as to the paragraph it is in and its story. All the containers provide gbegin() and gend() methods, which return a Story::CLogPos for the first and one-past-the-last glyph in the container. Internally they use GetNextXXX() calls and are therefore less efficient than the visitors. They are mainly used for temporarily indicating positions in the text.
Story::CLayPos is like a Story::CLogPos but specific to a particular layout. It extends Story::CLogPos with accessors for the line, column and layout. For every physical position there is a logical position (and Story::CLayPos::GetLogPos() will return it), but the converse may not be true if the story has not been fully composed. Also, even where the Story::CLayPos exists it may be in an overflow column (which means it is nowhere displayed on screen). With those ceveats, you can use a Story::CLayPos constructor to make a physical position from a logical one and a layout.
Sometimes several layout positions will correspond to one logical position. This is because powerfields, paragraph numbering, hyphenation and other processes insert temporary glyphs into the layout text stream. Because these glyphs are not logically "there", the selection may not rest on them. Other times glyphs which are logically part of the story are hidden in a layout and these may not be selected either. Story::CSelectPos is a variant of CLayPos which skips over unselectable glyphs. It is used extensively by the Story::CSelecter subclasses, but application code is unlikely to need it.
The various kinds of iterator described so far are only useful for transient references into the story. They are invalidated by changes to their underlying container. If you need a long-lived reference, use a Story::CAnchorPos. A CAnchorPos is like a CLogPos which tracks changes to the story. For example, if you insert a glyph before an anchor the anchor will shift down so that it still points to the same logical place. However, these are fairly expensive to maintain internally, so only use them when you really need to. The engine uses long-lived references for the selection, and most commands just use a Story::CSelection to describe input and output ranges and the other iterators for their working. Story::CAnchorPos cannot be serialised.
Story::CLogRange is effectively a pair of Story::CLogPos, and Story::CLayRange is a pair of Story::CLayPos. These are often more convenient than passing 2 objects by hand.
Story::CUndoRange is a variant of CLogRange for use within the undo/redo system. It is not invalidated even if the text it refers to is deleted, provided the text is restored again before the range is used.
See also Navigation Classes.