To allow editor content to be saved to a file, the editor classes implement a special file format called WXME. (The format is used when cutting and pasting between appli-cations or eventspaces, too). The file format is not documented, except that it begins WXME01hdigitihdigiti ## . Otherwise, theload-fileandsave-filemethods define the format internally. The file format is the same for text and pasteboard editors. When a paste-board saves its content to a file, it saves the snips from front to back, and also includes extra location information. Thewxmelibrary provides utilities for manipulating WXME files.
Editor data is read and written usingeditor-stream-in%andeditor-stream-out% ob-jects. Editor information can only be read from or written to one stream at a time. To write one or more editors to a stream, first call the functionwrite-editor-global-headerto write initialization data into an output stream. When all editors are written to the stream, call write-editor-global-footer. Similarly, reading editors from a stream is initialized withread-editor-global-headerand finalized with read-editor-global-footer. Optionally, to support streams that span versions of Racket, usewrite-editor-version andread-editor-versionbefore the header operations.
The editor file data format can be embedded within another file, and it can be extended with new kinds of data. The editor file format can be extended in two ways: with snip- or content-specific data, and with editor-content-specific global data. These are described in the remainder of this section.
5.2.1 Encoding Snips
The generalized notion of a snip allows new snip types to be defined and immediately used in any editor class. Also, when two applications support the same kinds of snips, snip data can easily be cut and pasted between them, and the same data files will be readable by each program. This interoperability is due to a consistent encoding mechanism that is built into the snip system.
Graceful and extensible encoding of snips requires that two issues are addressed:
• The encoding function for a snip can be associated with the snip itself. To convert a snip from an encoded representation (e.g., as bytes in a file) to a memory object, a decoding function must be provided for each type of snip. Furthermore, a list of such decoders must be available to the high-level decoding process. This decoding mapping is defined by associating a snip class object to every snip. A snip class is an instance of thesnip-class%class.
• Some editors may require additional information to be stored about a snip; this in-formation is orthogonal to the type-specific inin-formation stored by the snip itself. For example, a pasteboard needs to remember a snip’s location, while a text editor does not need this information. If data is being cut and pasted from one pasteboard to another, then information about relative locations needs to be maintained, but this in-formation should not inhibit pasting into an editor. Extra data is associated with a snip through editor data objects, which are instances of theeditor-data%class; decod-ing requires that each editor data object has an editor data class, which is an instance of theeditor-data-class%class.
Snip classes, snip data, and snip data classes solve problems related to encoding and decod-ing snips. In an application that has no need for savdecod-ing files or cut-and-paste, these issues can be safely ignored.
Snip Classes
Each snip can be associated to a snip class. This “class” is not a class description in the programmer’s language; it is an object which provides a way to create new snips of the appropriate type from an encoded snip specification.
Snip class objects can be added to the eventspace-specific snip class list, which is returned by get-the-snip-class-list. When a snip is encoded, the snip’s class name is associated with the encoding; when the snip needs to be decoded, then the snip class list is searched by name to find the snip’s class. The snip class will then provide a decoding function that can create a new snip from the encoding.
If a snip class’s name is of the form "((lib ...) (lib ...))", then the snip class implementation can be loaded on demand. The name is parsed using read; if the result has the form((lib string ...) (lib string ...)), then the first element used with dynamic-requirealong with'snip-class. If thedynamic-requireresult is a snip-class%object, then it is inserted into the current eventspace’s snip class list, and loading or saving continues using the new class.
The secondlib form in"((lib ...) (lib ...))"supplies a reader for a text-only ver-sion of the snip. See §9.1 “Snip Class Mapping” for more information on how such snip-classes work (and generally see thewxmelibrary).
A snip class’s name can also be just"(lib ...)", which is used like the first part of the
two-lib form. However, this form provides no information for the text-onlywxmereader.
Editor Data
While a snip belongs to an editor, the editor may store extra information about a snip in some specialized way. When the snip is to be encoded, this extra information needs to be put into an editor data object so that the extra information can be encoded as well. In a text editor, extra information can be associated with ranges of items, as well as snips.
Just as a snip must be associated with a snip class to be decoded (see §5.2.1.1 “Snip Classes”), an editor data object needs an editor data class for decoding. Every editor data class object can be added to the eventspace-specific editor data class list, returned by get-the-editor-data-class-list. Alternatively, like snip classes (see §5.2.1.1 “Snip Classes”), editor data class names can use the form"((lib ...) (lib ...))"to enable on-demand loading. The corresponding module should export aneditor-data-class%
object named'editor-data-class.
To store and load information about a snip or region in an editor:
• derive new classes fromeditor-data%andeditor-data-class%.
• derive a new class from thetext% or pasteboard% class, and override the get-snip-dataandset-snip-datamethods and/or theget-region-dataand set-region-datamethods.
Note: theget-region-dataandset-region-datamethods are called for cut-and-paste encoding, but not for file-saving encoding; see §5.2.2 “Global Data: Headers and Footers” for information on extending the file format.
5.2.2 Global Data: Headers and Footers
The editor file format provides for adding extra global data in special header and footer sections. To save and load special header and/or footer records:
• Pick a name for each header/footer record. This name should not conflict with any other header/footer record name in use, and no one else should use these names. All names beginning with “wx” are reserved for internal use. By tagging extra header and footer records with a unique name, the file can be safely loaded in an installation that does not support the records.
• Derive a new class from thetext%orpasteboard%class, and override the write-headers-to-file, write-footers-to-file, read-header-from-fileand/or read-footer-from-filemethods.
When an editor is saved, the methods write-headers-to-fileand write-footers-to-fileare invoked; at this time, the derivedtext%orpasteboard%object has a chance
to save records. To write a header/footer record, first invoke the begin-write-header-footer-to-filemethod, at which point the record name is provided. Once the record is written, callend-write-header-footer-to-file.
When an editor is loaded and a header/footer record is encountered, the read-header-from-fileorread-footer-from-filemethod is invoked, with the record name as the argument. If the name matches a known record type, then the data can be loaded.
See alsowrite-headers-to-fileandread-header-from-file.