|Scintilla Component Design|
Scintilla consists of three major layers of C++ code
The primary purpose of this structure is to separate the platform dependent code from the platform independent core code. This makes it easier to port Scintilla to a new platform and ensures that most readers of the code do not have to deal with platform details. To minimise portability problems and avoid code bloat, a conservative subset of C++ is used in Scintilla with no exception handling, run time type information or use of the standard C++ library and with limited use of templates.
The currently supported platforms, Windows, GTK+/Linux and wxWindows are fairly similar in many ways. Each has windows, menus and bitmaps. These features generally work in similar ways so each has a way to move a window or draw a red line. Sometimes one platform requires a sequence of calls rather than a single call. At other times, the differences are more profound. Reading the Windows clipboard occurs synchronously but reading the GTK+ clipboard requires a request call that will be asynchronously answered with a message containing the clipboard data. The wxWindows platform is available from the wxWindows site
This is a fairly small and thin layer over the platform's native capabilities.
The portability library is defined in Platform.h and is implemented once for each platform. PlatWin.cxx defines the Windows variants of the methods and PlatGTK.cxx the GTK+ variants.
Several of the classes here hold platform specific object identifiers and act as proxies to these platform objects. Most client code can thus manipulate the platform objects without caring which is the current platform. Sometimes client code needs access to the underlying object identifiers and this is provided by the GetID method. The underlying types of the platform specific identifiers are typedefed to common names to allow them to be transferred around in client code where needed.
These are simple classes provided to hold the commonly used geometric primitives. A PRectangle follows the Mac / Windows convention of not including its bottom and right sides instead of including all its sides as is normal in GTK+. It is not called Rectangle as this may be the name of a macro on Windows.
Colour holds a platform specific colour identifier - COLORREF for Windows and GdkColor for GTK+. The red, green and blue components that make up the colour are limited to the 8 bits of precision available on Windows. ColourPairs are used because not all possible colours are always available. Using an 8 bit colour mode, which is a common setting for both Windows and GTK+, only 256 colours are possible on the display. Thus when an application asks for a dull red, say #400000, it may only be allocated an already available colour such as #800000 or #330000. With 16 or 2 colour modes even less choice is available and the application will have to use the limited set of already available colours.A Palette object holds a set of colour pairs and can make the appropriate calls to ask to allocate these colours and to see what the platform has decided will be allowed.
Font holds a platform specific font identifier - HFONT for Windows, GdkFont* for GTK+. It does not own the identifier and so will not delete the platform font object in its destructor. Client code should call Destroy at appropriate times.
Surface is an abstraction over each platform's concept of somewhere that graphical drawing operations can be done. It may wrap an already created drawing place such as a window or be used to create a bitmap that can be drawn into and later copied onto another surface. On Windows it wraps a HDC and possibly a HBITMAP. On GTK+ it wraps a GdkDrawable* and possibly a GdkPixmap*. Other platform specific objects are created (and correctly destroyed) whenever required to perform drawing actions.
Drawing operations provided include drawing filled and unfilled polygons, lines, rectangles, ellipses and text. The height and width of text as well as other details can be measured. Operations can be clipped to a rectangle. Most of the calls are stateless with all parameters being passed at each call. The exception to this is line drawing which is performed by calling MoveTo and then LineTo.
Window acts as a proxy to a platform window allowing operations such as showing, moving, redrawing, and destroying to be performed. It contains a platform specific window identifier - HWND for Windows, GtkWidget* for GTK+.
ListBox is a subclass of Window and acts as a proxy to a platform listbox adding methods for operations such as adding, retrieving, and selecting items.
Menu is a small helper class for constructing popup menus. It contains the platform specific menu identifier - HMENU for Windows, GtkItemFactory* for GTK+. Most of the work in constructing menus requires access to platform events and so is done in the Platform Events and API layer.
The Platform class is used to access the facilities of the platform. System wide parameters such as double click speed and chrome colour are available from Platform. Utility functions such as DebugPrintf are also available from Platform.
The bulk of Scintilla's code is platform independent. This is made up of the CellBuffer, ContractionState, Document, Editor, Indicator, LineMarker, Style, ViewStyle, KeyMap, ScintillaBase, CallTip, and AutoComplete primary classes.
A CellBuffer holds text and styling information, the undo stack, the assignment of line markers to lines, and the fold structure.
A cell contains a character byte and its associated style byte. The current state of the cell buffer is the sequence of cells that make up the text and a sequence of line information containing the starting position of each line and any markers assigned to each line.
The undo stack holds a sequence of actions on the cell buffer. Each action is one of a text insertion, a text deletion or an undo start action. The start actions are used to group sequences of text insertions and deletions together so they can be undone together. To perform an undo operation, each insertion or deletion is undone in reverse sequence. Similarly, redo reapplies each action to the buffer in sequence. Whenever a character is inserted in the buffer either directly through a call such as InsertString or through undo or redo, its styling byte is initially set to zero. Client code is responsible for styling each character whenever convenient. Styling information is not stored in undo actions.
A document contains a CellBuffer and deals with some higher level abstractions such as words, DBCS character sequences and line end character sequences. It is responsible for managing the styling process and for notifying other objects when changes occur to the document.
The Editor object is central to Scintilla. It is responsible for displaying a document and responding to user actions and requests from the container. It uses ContractionState, Indicator, LineMarker, Style, and ViewStyle objects to display the document and a KeyMap class to map key presses to functions. The visibility of each line is kept in the ContractionState which is also responsible for mapping from display lines to documents lines and vice versa.
There may be multiple Editor objects attached to one Document object. Changes to a document are broadcast to the editors through the DocWatcher mechanism.
ScintillaBase is a subclass of Editor and adds extra windowing features including display of calltips, autocompletion lists and context menus. These features use CallTip and AutoComplete objects. This class is optional so a lightweight implementation of Scintilla may bypass it if the added functionality is not required.
Each platform uses different mechanisms for receiving events. On Windows, events are received through messages and COM. On GTK+, callback functions are used.
For each platform, a class is derived from ScintillaBase (and thus from Editor). This is ScintillaWin on Windows and ScintillaGTK on GTK+. These classes are responsible for connecting to the platforms event mechanism and also to implement some virtual methods in Editor and ScintillaBase which are different on the platforms. For example, this layer has to support this difference between the synchronous Windows clipboard and the asynchronous GTK+ clipboard.
The external API is defined in this layer as each platform has different preferred styles of API - messages on Windows and function calls on GTK+. This also allows multiple APIs to be defined on a platform. The currently available API on GTK+ is similar to the Windows API and does not follow platform conventions well. A second API could be implemented here that did follow platform conventions.