This page (and any of its child pages) contains my OO.o development note. I share my note in the hope that someone will find it useful when hacking OO.o, although its usefulness is in no ways guaranteed. This is also not an extensive documentation of Calc’s internals as I only write about the stuff I have worked on. Also, OO.o’s codebase is a moving target; the code is always changing, which means this note here may not be always up-to-date.
Range names and linked cell ranges
Calc provides a way to name a cell range so that it can be referred to by its name. For instance, You can associate $Sheet1.$A$1:$D:$5 to ‘MyRange’, and reference it in a formula e.g. =SUM(MyRange). Internally, Calc keeps track of all range names by an instance of ScRangeName, and the document instance (ScDocument) manages the ScRangeName instance throughout the session. There are two ways to define a range name:
- go to Insert – Names – Define… and define a new one in that dialog, or
- highlight a range to name, and put a desired name for that range in the name box located in the top toolbar on the left-hand side of the Function Wizard icon.
One interesting thing you can do with named ranges is that you can link these ranges from another document. Here is how you do it. First, open the source document with one or more named ranges, then open another document where you want to create a link to one of those named ranges in the source document. Then launch the navigator window while the destination document is active, switch the combo box value in the bottom of the navigator window to the name of the source document (it should have (inactive) in its name), and you should see all the named ranges listed under Range names. Now, notice there is a button with a down-arrow thing in the top part of the navigator window (second row at the right end). Click it to show the menu, and change it to Insert as Link. After that, just drag-n-drop the range you want to link to into the destination document, which will create a linked cell range.
Internally, all linked cell ranges are managed by the link manager (SvxLinkManager), which also manages all externally linked documents (explained below). Class ScAreaLink represents each linked range. When a named range is dropped into the destination document, first the ScGridWindow::ExecuteDrop method gets called to handle the drop, then eventually ScDocFunc::InsertAreaLink gets called to insert a new area link (as it’s called internally) into the link manager.
When it comes time to update, SvBaseLink::Update gets called which in turn calls its virtual method DataChanged. ScAreaLink overwrites this method, ScAreaLink’s DataChanged() method gets called instead which in the end calls ScAreaLink::Refresh to refresh the data. When refreshing the source data, Calc loads the source document using the stored file path and the filter name, creates a temporary source document instance, and obtains the data from the source. Once updated, the source document instance is destroyed.
External references
A cell in an external document can be referenced in a formula expression like this: ='file:///path/to/doc.ods'#$Sheet1.$E$15. Internally, Calc treats the path to such an external document as part of the sheet name, and assigns a sheet index higher than the last index of the internal sheets. For instance, if the current document has 3 sheets, then the first externally referenced sheet gets the sheet index of 3 since the last sheet index of the internal sheets is 2.
All externally referenced (or linked as the terms seems to be used more preferentially) documents are managed in the Edit Links dialog in the Edit – Links menu. This dialog is implemented by class SvBaseLinksDlg in the svx module. When the Links menu item is activated, ScTabViewShell::ExecDrawIns gets called with the slot ID of SID_LINKS. When launching this dialog, Calc’s document instance (ScDocument) passes its own link manager instance (implemented by class SvxLinkManager) to the dialog instance to have the dialog manage its content.
Another scenario where a new linked document may be created is when the user directly types its file path in a formula expression. When the entered document is not already on the list of linked documents, the document gets inserted to the link manager when the cell address is parsed via ScDocument::LinkExternalTab call. An instance of class ScTableLink represents each inserted external sheet. Class ScDocumentLoader loads the external document and provides its document model as another instance of ScDocument.
Internally, Calc inserts an invisible sheet copied from the externally linked document and uses it when an external cell or cell range is referenced. Such invisible sheet has a table ID greater than the last ID of the visible sheets.
Built-in cell formulas
Every built-in cell function has a corresponding enum value for OpCodeEnum type in sc/inc/opcode.hxx. Find the value for the cell function you are after (ocHyperLink for HYPERLINK function, for instance), and grep for its reference throughout the sc module. You’ll probably find one reference in ScInterpreter::Interpret (in sc/source/core/tool/interpr4.cxx), which calls an appropriate member function for the specified opcode enum value.
ScCompiler performs the translation of a raw formula string into an array of tokens. Since the function names are localized and are given by the internal resource manager, ScCompiler needs to have access to that information. Class ScCompiler::OpCodeMap takes care of mapping the localized strings and their internal token values. Class ScOpCodeList handles initialization of those OpCodeMap instances by querying the function name resource file via internal resource manager.
Compiling formula
When the cursor moves to a cell that contains a formula, ScFormulaCell::GetFormula gets called to reconstruct a string representation of the formula token array stored with that cell. Method ScCompiler::CreateStringFromToken checks each individual token and converts to their respective string. If the token is a cell reference, it expands it to an appropriate string based on the current formula convention i.e. whether the current mode is OOO_A1, XL_A1, XL_R1C1 or OOO_A1_XML.
When a new formula string is entered, the ScFormulaCell instance compiles that raw formula string into an array of tokens by method CompileString. ScCompiler::NextNewToken gives you an overview of how a formula expression is parsed and broken into a series of tokens. This is also where you need to modify in order to change the way formula expressions are parsed.
Cell values such as raw numbers, raw strings, or references are pushed on to the stack and substituted with the ocPush opcode.
Drawing layer
Text
Class ImpEditEngine in the svx module has method called Paint that draws rich text characters inside cells. ScOutputData::DrawEdit creates an instance of ScFieldEditEngine which is an indirect sub-class of EditEngine, and calls its Draw method which eventually calls ImpEditEngine::Paint to physically draw rich texts on screen.
While in edit mode, ScEditShell receives all the events that occur during edit mode. For example, when the user selects text or a part of the text while in edit mode and change its font, ScEditShell::ExecuteAttr gets called to handle that event with SID_ATTR_CHAR_FONT as the event ID. When not in edit mode, that is, when the cell cursor is still displayed at the current cell position, changing font triggers an event sent to ScFormatShell::ExecuteAttr.
Class ScEditCell stores rich text data for a cell when it contains rich text formatting. ScEditCell has an instance of class EditTextObject to store the rich text data. EditTextObject is just an abstract class which BinTextObject derives itself from to implement the interfaces. So far, BinTextObject appears to be the only class derived from EditTextObject base class.
Input handling
The ScGridWindow class represents the top-level view frame for Calc, and this is where user inputs, such as keyboard and mouse inputs, are handled. Since its parent class, Window, requires a set of virtual functions to be implemented by its child class, ScGridWindow implements them. There can be up to four instances of ScGridWindow when the view is split.
Formula cell reference handling
Some classes in the sc module use the listener-broadcaster framework for change notifications. Class ScFormulaCell uses this framework to implement proper propagation of cell value change through a chain of cell references in formulas. The underlying listener-broadcaster framework is implemented in the svtools module.
Underlying framework in svtools module
SvtListenerBase represents a node in the list of listener-broadcaster pair. This node keeps pointers to the left, right, and the next nodes relative to itself, and is used by SvtListener to keep track of which broadcasters a given listener class needs to be listening to. Any child class of SvtListener needs to overwrite the virtual method Notify to implement what to do when it receives a notification from one of its broadcasters. SvtListener also provides several concrete (i.e. non-virtual) methods to handle registering and unregistering broadcasters: EndListening, EndListeningAll, HasBroadcaster, IsListening, and StartListening. These methods do not need to be overwritten.
Class SvtBroadcaster implements the broadcasting functionality to the listeners. SvtBroadcaster is designed to be a stand-alone class; it should be used as a concrete class, although several classes in the sc modules derive from it to extend its functionality, such as ScAddInAsync, ScAddInListener, and ScDdeLink for reasons unknown.
Use in formula references
Classes ScBroadcastArea and ScBaseCell both have an instance of SvtBroadcaster as a data member so that they can function as broadcasters when needed. SvtBroadcaster’s Broadcast method, when called, goes through all the listener instances that are listening to itself and calls their Notify method when it’s time to broadcast to all of its listeners.
ScBaseCell has method named StartListeningTo, which essentially does nothing unless the cell is a formula cell. On formula cells, on the other hand, this method checks its formula expression token by token, and in presence of cell address references, it stores those cell instances as broadcasters, in order to allow receiving notification in case of value change in any of those cell instances. When the formula expression includes a range reference, then instead of storing all the individual cell instances in that range as broadcasters, it stores it as a single area and listens to that area instead. Class ScBroadcastAreaSlotMachine manages the collection of all broadcasting areas. There is only one instance of this class per document, and it is instantiated when the document instance (ScDocument) is constructed.
ScBroadcastAreaSlotMachine sub-divides the sheet into 16 column x 512 row slots (total of 8192 slots), and uses an instance of ScBroadcastAreaSlot to manage each slot. Now, each instance of ScBroadcastAreaSlot maintains a table of broadcast areas (i.e. referenced cell ranges) that are represented by ScBroadcastArea. Each broadcast area corresponds to a cell range that is being listened to by at least one cell instance, and is reference-counted. When broadcasting, an instance of class ScHint is used to specify which cell’s value has changed. Class ScHint holds the address of the modified cell as ScAddress, and a pointer to that cell instance, which can be accessed via GetAddress and GetCell methods, respectively. ScHint can announce three types of cell change: SC_HINT_DATACHANGED for normal value change, SC_HINT_DYING for cell deletion, and SC_HINT_TABLEOPDIRTY for multiple operations (can be found in Data – Multiple Operations).
Every time a value in a cell changes, ScDocument’s Broadcast method gets called. It checks if the instance of the modified cell has a broadcaster instance, and if there is one, it broadcasts the cell. (A cell having a broadcaster instance i.e. a non-empty pointer, means that there is at least one other cell that references that cell. If a cell is not referenced at all, it doesn’t have a broadcaster instance.) It also checks if the modified cell belongs to a referenced cell range. If it’s determined that the modified cell is referenced either as a single cell reference or as part of a larger cell range reference, it calls ScDocument’s TrackFormulas method to propagate the value change across the formula dependency tree.
Now, ScDocument instance has as a member variable pFormulaTrack, which points to the first node of the series of formula cell nodes referred to as the formula tracks. By the time the TrackFormulas is called, the document instance has accumulated the formula tracks, and the call simply walks through all the formula cell instances that are on these tracks and perform a single cell broadcast and a range broadcast in this order for each individual cell.
Cell edit mode
Cursor
What I refer to as the cursor here is that blinking little vertical bar inside a cell when the cell is in edit mode, indicating where the text would go when you type something. The actual cursor implementation is found in VCL’s Cursor class. Its method ImplDraw gets called for every blink of the cursor during cell edit mode, while ImplShow initializes the internal cursor data and the timer.
At higher level, when Cursor’s Show method gets called, the blinking timer starts and the cursor starts blinking under the hood. So, when using this class, you would only need to worry about calling either the Show method or the Hide method to turn on or off the blinking cursor.
Auto and standard filter
In Calc, a filtered range is represeted by class ScDBData. Each ScDBData instance has the affected cell range and the filtering information. There can be multiple instances of ScDBData per document, and the ScDocument class uses ScDBCollection (which is a member of ScDocument) to manage multiple instances of ScDBData throughout the lifetime of the document. To get a ScDBData instance associated with a given cell position, call ScDocument::GetDBAtCursor which returns the pointer to the ScDBData instance that encompasses the cell. If there is no filtered range available at the cursor location, a NULL pointer is returned.
When a cell range doesn’t already have an associated named range, Calc assigns an anonymous range to it. However, because Calc currently supports only one anonymous range per document, the second time an anonymous range is assigned, the old range is removed. This typically happens when you set the autofilter to an unnamed range then another autofilter to another unnamed range, which causes the first autofilter to disappear. Method ScDocShell::GetDBData handles assigning a new range to the anonymous range.
ScGridWindow::DoAutoFilterMenue (no this is not a typo) implements the popup of the autofilter box, which gets called when the user clicks on the arrow button at the top of a filtered range. This is also where the string entries in the autofilter box are fetched and inserted.
ScGridWindow::FilterSelect gets called with a numeric ID when the filtering value is entered in the autofilter popup box. The ID passed to this function represents the position of the selected item with the value of 0 being the topmost item. For applying the autofilter result, ScGridWindow’s ExecFilter gets called to execute the filtering.
Creating any types of reference dialogs (those dialogs that have a cell range reference control) is done in ScTabViewShell::CreateRefDialog. That’s also where the standard filter dialog (represented by class ScFilterDlg) is instantiated and its pointer is returned to the caller afterward.
ScTable::ValidQuery takes an array of bool variables to flag special treatment of query parameters when true. This is used for empty, non-empty value filtering.
Sorting
ScDocument::HasColHeader is called to determine whether a given cell range has a column header in the first row or not. This method is called when the sort dialog is being opened on an arbitrary cell range in order to decide whether to check that “Range contains column labels” check box.
Miscellaneous
Class SfxItemSet is used as an all-purpose container to pass a set of arbitrary (or pooled) items around. This is used especially with event objects. The header file sc/inc/scitems.hxx contains the IDs of items that can be stored in SfxItemSet within the sc module.
