Language

The Free and Open Productivity Suite
Released: Apache OpenOffice 4.1.15

Some thoughts about Macro Recording


All StarOffice versions prior to the release of OpenOffice.org had a macro recorder. This tool was able to record actions of the user and translate them into StarBasic code. It was removed before the OO.o source code was released. To understand why it was removed and why it is so hard to create a replacement for it, it's necessary to learn something about the way it worked and about the internal command processing in OO.o applications.


The OO.o applications are based on the SFX application framework. This framework is not an UNO based one, it's pure C++. It's main operational area is the management of the user interface and the processing of commands created by user actions. Every functionality (functions, properties) provided by an SFX based application component is described by a structure called „slot“. It contains a number identifying the slot (the „slot id“), some additional information for internal processing inside SFX (some mostly boolean parameters describing the slot), a name (its „API name“) and the information wether this slot represents a function or a property. For functions it also contains a list of all parameters and their names and types and the type of the return value, for properties it contains the property type.


All the slots and their properties are known at compile time to the application module they belong to. Inside the application modules the slots are grouped together into data structures called „(slot) interface“. Every module knows several interfaces, each of them representing a „context“ of the user interface. Examples are the contexts „text document“, „impress slide view“, „table selection“, each of them is represented by a corresponding slot interface (or a group of them). The sum of all currently available contextes (slot interfaces) defines the available feature set of an SFX based application component.

The SFX application framework learns about the slots by being dynamically bound to the modules and slot interfaces at runtime. So it is able to browse through the whole available feature set of the application component. All currently bound slot interfaces are pushed on a stack provided by the SFX framework, thus defining an ordering mechanism for them. Pushing interfaces on the stack or removing interfaces from there is the way context switches in the user interface are reflected in the SFX.


All user interface elements written for the SFX (f.e. toolboxes, menus, keyboard shortcuts) don't call directly into the application code, they just call a very generic SFX based API to execute a slot. The slot is identified by its slot id and the SFX framework is able to find the right code that is able to process the slot by browsing through the bound slot interfaces in a well defined manner, using the stack of slot interfaces mentioned above. Many calls are executed without additional parameters, but many others also pass parameters while executing the slot. An example for the first case is a simple click on a menu entry. An example for the second case is when the user changes some values in a dialog and leaves the dialog by clicking its „OK“ button. Here all changed values in the dialog are sent as parameters of the slot command.


The generic API assures that every command goes through the same code in the SFX framework, and so this framework is able to translate every slot execution into a single StarBasic statement or a group of them, just by finding the slot structure belonging to the slot being executed, the slot interface it is belonging to, getting the slots' API name and identifying the parameters. (In the user interface usually no return values are processed.) Here a slot interface or a group of slot interfaces represents an object, every slot is a method or a property of such object. Both together form a StarBasic statement like „Object.Method( )“.

The Basic code generated this way used the so called „old“ SO-Basic API, that is not supported anymore in OO.o and SO6, and it is not in any way related to the „real“ API of the OO.o/SO applications.


The slot based API is still used for large parts of the internal command execution in OO.o (the communication between the application component and its user interface), but it is not possible to translate it into any UNO based API. To be honest, only a few slot calls will ever end up in calling a UNO based API of the underlying application component.


It is possible to transform every slot command into a call for the generic UNO based „dispatch API“, where every functionality is described by a command string (we call them „command URLs“, because they use the form „scheme:scheme dependent part“).


This API uses a chain of objects implementing the DispatchProvider service. If the first object in the chain is asked for a Dispatch object for an arbitrary command, it will check the command if it wants to handle it. If the check turns out to be positive, a Dispatch object is returned that is able to execute the command, otherwise the query is passed to the next chain object etc. If a Dispatch object was found anywhere in the chain, the command is executed by calling the dispatch method of the Dispatch object, where all parameters are passed as PropertyValue structs.


The result of a transformation from SFX slot execution to dispatch call is a call dispatching the command „slot:xxxx“ (xxxx=slot id), passing all necessary parameters as PropertyValues. Newer UI components are using this API instead of the slot based SFX API, just to have a UNO based interface to the application componnent and not a C++ based one. The SFX framework provides some wrapper code to translate between the slot based API and the dispatch framework for all these newer UI components if they are used for a SFX based application component.


But the dispatch API, though being UNO based, is only of limited practical use, because these calls are just textual representations of the binary slot data. If you want to write a program that uses OO.o API to work on OO.o documents, the dispatch API is definitely not the one you want to use (except in some special cases, where the functionality is not accessible by other APIs). The dispatch API is just thought as the generic programming interface for a mostly generic UI code.


It is possible to record macros just for automation purposes using the SFX or the dispatch API calls: take the internal binary representation of the slot commands (or the command URL and the parameters of the dispatch call), store it anywhere and execute it afterwards by using a „slot machine“ (only evil thinking persons will misunderstood this name in this context here ;-) ) or a „dispatch machine“. But usually a macro macro recorder is wanted to offer more than only automation: most people want to have created source code they can browse and modify. And this is exactly what you can't get from the SFX. Many (most?) user interactions in the OO.o applications are processed without any single UNO call, so there is nothing you can „record“.


So what do we need to have the „macro recording“ feature back?


First, every slot execution (or dispatch call) must be mapped to one or several API calls of the objects they are working on. This is hard work enough, don't expect this to happen in the near future.


But even if you have got this made, you will need something that translates the API calls made into source code of any (scripting) language supporting UNO. This can't be a built-in UNO feature, because you want only the „top level“ UNO calls to be recorded, not the subsequent calls, because you will get them called anyway when executing the macro. Perhaps the „top level“ calls could marked as recordable by the (AFAIK currently not accessible UNO context), but this looks „hacky“ and ugly to me.


I'd prefer an explicit solution, where every dispatch provider object that returns a dispatch object will be asked to record this call into one or several API statements, but I don't have any clever ideas for that.


So I'd appreciate any ideas from anybody who wants to contribute to that. May be it turns out that the „UNO built in“ solution is not that hacky as it seems, may be that there is something completely different that puts a new light on the problem.



Apache Software Foundation

Copyright & License | Privacy | Contact Us | Donate | Thanks

Apache, OpenOffice, OpenOffice.org and the seagull logo are registered trademarks of The Apache Software Foundation. The Apache feather logo is a trademark of The Apache Software Foundation. Other names appearing on the site may be trademarks of their respective owners.