Word and RichEdit have stories, but you won’t find their story’s definition in a dictionary. Their story is an object that stores rich text in computer memory. Rich text consists of Unicode plain text, associated character and paragraph formatting, and embedded objects such as images. Such a story can contain the narrative of a traditional story, but more generally it can contain any arbitrary set of Unicode characters. A RichEdit control is created with a built-in story called the “main story”. Other stories may be created by the client or by RichEdit for special purposes. For example, when you select some text and hit Ctrl+c, the selected text range is noted, but for the sake of efficiency, no conversions take place until you paste a particular format or the RichEdit control is destroyed. If you start to modify the selected text, the text is first copied into a clipboard scratch story so that the text can continue to be pasted in any of the promised formats. This post describes how RichEdit stories are used and managed.
Client usage of multiple stories
Microsoft Word uses multiple stories in a document, such as a main story, a footnote story, a header story, a footer story, etc. A RichEdit client could do something similar in supporting a full-fledged document program. For example, OneNote instantiates a RichEdit control for editing and displaying a paragraph. Since a OneNote document can have many sections, each with many paragraphs, OneNote has a pool of RichEdit controls to be used as needed. A large OneNote document has many more paragraphs than RichEdit controls. An alternate approach would be to house each paragraph of a OneNote section in its own story all within a single control for the whole section. This approach might be more performant than using multiple relatively large controls since the stories package rich text efficiently, and paragraphs would only have to be loaded once.
Place-holder controls are used for user entry and need place-holder text to instruct the user what to do. As soon as the user types something, the place-holder text disappears, and what the user types is displayed instead. If the user deletes all the text, the place-holder text reappears. This behavior is accomplished by storing the place-holder text in its own story. The story that is currently active is displayed on screen.
Equation scratch story
The first use of stories in RichEdit was in RichEdit 6.0 (Office 2007) for converting UnicodeMath input into the internal OfficeMath format. In fact, the TOM2 ITextStrings interface handles a collection of rich-text strings useful for manipulating rich text, such as converting infix math expressions into the prefix OfficeMath form and vice versa. The ITextStrings strings collection is implemented by concatenating the strings together in a scratch story and maintaining an array of the string counts that delimit the strings. RichEdit also uses an ITextStrings scratch story in converting MathML and OMML into OfficeMath.
Stories can be programmed using the messages EM_SELECTSTORY, EM_GETSTORYTYPE, and EM_SETSTORYTYPE and by the methods in ITextStory, which is part of TOM Version 2. Stories can be “edited” simultaneously using ITextRange2’s and displayed independently of one another. A given ITextRange2 accesses one and only one story. In addition, only one story can be user active at a time, that is, only one story receives the keyboard and mouse input and is displayed. The EM_SELECTSTORY message isn’t documented elsewhere on the web, so here’s how it’s used. The message is defined by
#define EM_SELECTSTORY (WM_USER + 263)
The message selects the story with ID given by the wparam. It returns the active story ID. The lparam has the flag SSS_DONTDISPLAYSTORY defined by
#define SSS_DONTDISPLAYSTORY 1
The ITextDocument2 interface has methods to get the active story, to get a new story, and to get an existing story by story ID. The story facility needs a stories collection, since among other things before the parent ITextServices instance is deleted, it needs to zombie any unreleased ITextStory pointers and delete the story data. The stories collection is accessed externally via methods in ITextDocument2. RichEdit 6.0 (Office 2007) and later versions have a multistory facility (see ITextStoryRanges), but it is not sufficiently lightweight and convenient for clients, so we added the ITextStory approach in RichEdit 8 (in 2012). The ITextStoryRanges facility implements the original TOM version of stories as ranges and is oriented towards stories as used in Word. The current emphasis on a multitude of lightweight instances leads to a different model. A range adds extra instance size, so it saves space to instantiate ranges only when they are needed to manipulate stories.
To minimize the size of the story, it only contains rich-text structures that are used. For example, if a control only has default formatting, it has no character or paragraph format run arrays. Some languages like Thai need cluster and word breaking, but if a story doesn’t contain text of such languages, the cluster and word breaking structures are omitted. Similarly, if there are no embedded objects or images, there’s no object array.
The current RichEdit control has a single display that’s used by the active story. A more advanced model would offer multiple displays and an enhanced ITextHost interface with methods that specify the ITextStory to be displayed.