Build Environment Effort/HID List

From Apache OpenOffice Wiki
Jump to: navigation, search

Edit.png

Build Environment Effort

Quick Navigation

About this template


The problem

Function commands (e.g. entries in menus or toolbars) and GUI elements like controls, dialogs or windows in OOo have IDs. Such an ID is called a "help ID" (HID). The reason for that is - surprise! - that this ID is used to assign help content to the command or to the GUI element. As an example, a user can activate the "Extended Help" feature in OOo and click on a menu entry or a toolbar button. Then a HID is taken from the command that is bound to the selected menu entry or clicked toolbar button. In the same way a user can click on a button in a dialog. Then a HID is retrieved from the button that was clicked. In both cases the HID is "wrapped" into a help URL and the OOo Help Content Provider is asked to provide help content for this URL.

HIDs can also be used to automate OOo: the testtool application uses HIDs to instruct OOo to execute a command or operate a control. OOo contains a testtool client component that is instructed by the testtool application through a remote bridge. If this client receives a HID for a command, it executes this command using the Dispatch API of the Controller object representing the currently active document window. If the client receives a HID for a GUI element, it searches for the GUI element with that HID in the currently active window and performs an action on it.

How do these HIDs look like? In the testtool application and the executed test scripts HIDs are strings, sometimes called "long names". The help content provider also uses these "long names" to reference a particular help content. Inside of OOo the HIDs are integer values (with some exceptions explained below).

Obviously OOo and the HID "customers" (Help Content Provider, testtool application) speak different languages and so a translator is needed. This translator is the famous (notorious?) "HID list", hid.lst. It defines a mapping from the "long names" to the integer values. If a test script contains a "long name" of a GUI element or command, the test tool "translates" it into the assigned numerical ID and uses it to address the GUI element or execute the command. OTOH if help is requested for a GUI element or a command, the help content provider internally "translates" the ID in the help URL to the "long name" und uses that one to find the content assigned to it. So far, so good.

Unfortunately the hid.lst imposes some problems on our build process:

  • We have a hen and egg problem: the hid.lst is built at the end of the build, but it is needed to build the helpcontent2 module, where it is even committed to the SCM repository. So this list always is not the most current one. If this was the only problem, we could fix that by changing the build order (though this has some other drawbacks), but unfortunately there are more of them.
  • The testtool can't work correctly if the hid.lst from the CWS is not "delivered" correctly; there is always some confusion and uncertainty which list is the correct one to use and the whole process is prone to errors.
  • Some ID mappings are generated automatically; this mapping is susceptible for subtle changes that can happen unnoticed.
  • The HID list generation is an awkward and time consuming process.

So it would be desirable to get rid of the hid.lst by letting all participants (help, testtool, OOo itself) work with the same "long names".

The current process

Commands and GUI elements are treated differently.

Commands

In the olden days of OOo our commands have been represented by an integer number only. These numbers are called "slot IDs". In the source code we have a lot of #defines for slot IDs, e.g. in sfx2/inc/sfx2/sfxsids.hrc where we have code lines like

#define SID_SAVEDOC 5505

From the problem description above it shouldn't be surprising that "SID_SAVEDOC" is used as the long name of the command while 5505 is the internal numerical ID. Thus the hid.lst contains a line

SID_SAVEDOC 5505

that does the mapping.

When we moved our command dispatching to a UNO based framework that always uses URL style command strings, we first simply used URIs like "slot:5505" to execute the corresponding command. Later on we gave them "speaking" names. The command with slot ID 5505 e.g. now is called ".uno:Save"[1]. This gives us an opportunity to get rid of the ID mapping as explained below.

GUI elements

There are three ways how a control can get a HID:

Automatic HIDs

GUI elements created from a resource file can get "automatic" HIDs when the resource is loaded. The corresponding "long names" of course must be created in the build process. Otherwise they couldn't get into the hid.lst. These IDs are generated in a special context (the particular resource) and so without further actions can't be used in a global list because of possible name clashes. We avoid these clashes in the same way as we do it in C++ source code: by using name spaces. Here's an example for an automatic HID:

sfx2:CheckBox:RID_DLG_ALIEN_WARNING:CB_WARNING_OFF 1111557136 

It defines the HID for the checkbox labeled CB_WARNING_OFF that belongs to the dialog RID_DLG_ALIEN_WARNING that is defined in the code module sfx2. The numerical value is calculated by the resource compiler (it doesn't matter here how this is done). Here's an explanation for the construction of that "long name":

RID_ALIEN_WARNING is the resource ID of the dialog. It is defined in a globally accessible header file as

#define RID_DLG_ALIEN_WARNING           ( RC_DIALOG_BEGIN + 0)

CB_WARNING_OFF defines a sub resource that is only valid in the context of the global resource RID_DLG_ALIEN_WARNING. It is defined in an internal header file that is only included by the resource (rsc) file defining the dialog and the code (cxx) implementing it:

#define CB_WARNING_OFF  16

How can we verify that the "long name" shown above is globally unique?

Usually several resource source files (src) are compiled into one binary resource (res) file. The resource compiler guarantees that

  • in one binary resource file a particular resource ID value can not be used for more than one resource elements of the same type (here: a dialog)
  • in one resource element a particular resource ID value can not be used for more than one sub resource elements of the same type (here: a checkbox)

So we can make the "long name" unique for the whole compiled binary resource file if we never use the same define for different numerical values in the same context (binary resource file or single resource), something that can be controlled easily. To make the "long name" globally unique, we add a prefix that identifies the resource file. In sfx2 we have only one binary resource file, so the module name is sufficient.

HIDs assigned by resource files

A HID can be assigned explicitly in the resource file:

ModalDialog RID_DLG_ALIEN_WARNING
{
   HelpId = HID_WARNING_ALIENFORMAT;
   (...)
};

defines a HID for the whole dialog. The hid.lst file contains

HID_WARNING_ALIENFORMAT 33388

The numerical value is taken from a header file that defines HIDs. Each code module where HIDs are used has its reserved integer value range:

#define HID_WARNING_ALIENFORMAT                     (HID_SFX_START + 320)

HIDs assigned by code

Not every GUI element is created from a resource file, some are only handled by code. So also the HIDs are assigned in the C++ code by calling the SetHelpId() method of the CUI element, inherited from the vcl class Window. As an example, here's the code that assigns a HID to the Navigator window:

pWindow->SetHelpId ( HID_NAVIGATOR_WINDOW );

Again the HID is taken from a header file:

#define HID_NAVIGATOR_WINDOW                        (HID_SFX_START +  86)

and ends up in the hid.lst as

HID_NAVIGATOR_WINDOW 33154

The generation of the hid.lst is completely based on resource (rsc) files. So the HID of the Navigator would be invisible for the build if we didn't apply a trick: each HID used in code only is collected in a special resource file called "hidother.src" that is processed by the HID compilation, but not included in the binary resource file. For the Navigator it contains

hidspecial HID_NAVIGATOR_WINDOW                 { HelpID = HID_NAVIGATOR_WINDOW; };

The proposal

Commands and GUI elements must be treated differently and code changes will happen at different places. So my proposal has two parts.

Commands

With the mentioned ".uno:..." command names we have a string that can be used as a long name for a command that is directly understood by the OOo code without any "tranlastion" (mapping). And indeed for some commands they are already used in the test scripts. By converting all tests and all help indices to use these ".uno:..." names we can get rid of all command HID mappings in the hid.lst. This will require changes only in the automation(?), testautomation and helpcontent2 modules.

So the proposal for command HIDs is:

Convert all help content and test scripts to use the ".uno:..." long names instead of the "SID_..." long names and always use the the command names (right hand part of the command "URLs") instead of the SlotIDs inside of help content URLs.

This will require changes in helpcontent2, testautomation and sfx2 (where the help URL construction is implemented).

GUI elements

GUI elements all derive from the Window class in vcl. This class has two methods that deal with HelpIds:

void SetHelpId ( ULONG nId );
ULONG GetHelpId() const;

All three described methods to get a HID operate on this property. So this is what we must replace by something that references the "long names".

We also have methods dealing with "SmartIds" that more or less are Strings that have been planned to replace numeric HelpIDs. It might be tempting to use them for direct "long name" support, but below I will explain why I don't think that Strings should be used but const char* instead.

Let's start with the case where my proposed change can be understood most easily: the setting of HIDs by code.

Currently we have code like

// found in a header (hrc) file 
#define HID_CONTROL_ID (HID_SOMETHING_START + 42)

// found in a cxx file 
pControl->SetHelpId( HID_CONTROL_ID ); 

The simplest change to get the long name into the code is:

#define HID_CONTROL_ID     "HID_CONTROL_ID"

This can easily be achieved by some scripting.

Now we just need to change the help ID member in the Window class in vcl and the Get/SetHelpId methods so that they can take or return ByteStrings. A String obviously wouldn't be a good choice as it would convert the 8 bit character constant into a 16 bit UniCode value, while using a const const char* encapsulated in a refcounted ByteString just would require a simple pointer assignment, but no string copying. So the String based solution would use three times the memory as the ByteString based solution where memory would be used only in the code or data segment.

With the new #defines as show above, the rsc compiler now also would see character constants where formerly it saw integer values:

ModalDialog RID_DLG_ALIEN_WARNING
{
   HelpId = HID_WARNING_ALIENFORMAT;
   (...)
};

So the rsc code also needs to be changed. The rsc compiler will save the HelpId member of controls as a UTF-8 coded strings into the binary resource file. The ResourceManager must become aware of the new data type and handle it accordingly, means: get the alphanumeric HIDs and assign them to the HelpId members of the controls.

Finally we have the "automatic" HIDs. They are constructed in the following way:

name_of_module:resource_type_of_control:symbolic_resource_id_of_global_resource:symbolic_sub_resource_id_of_control 

The resource manager must be able to construct this (byte) string when the resource is loaded. The first and second part are known, though for the first one we have the name of the resource file, not the name of the modukle, but that's exchangeable. The other two parts are known only at build time. So me must store them in the binary resource files. The obvious way would be to have HelpId fragments assigned to top level resources and sub resources in the rsc files. This is ugly and enlarges both the rsc and the res files.

Another option would be to simply change the automatic names, making them less human readable, but saving us from storing additional information in the resource files. By using the string representation of the numerical values of the resource ids instead of the symbolic names of them, we wouldn't any information that isn't currently available in the resource files anyway! We would get smaller res files and didn't have to change our rsc files, but we would need to replace HIDs in test scripts and help content. This can be done easily by scripting. If we wanted to keep the better readability for humans (is is really that much useful here?), we had to change rsc files and will get larger res files.

Now an automatic HID like

sfx2:CheckBox:DLG_NEW_FILE:CB_FRAME_STYLE

would be

sfx:CheckBox:4242:42 

assuming that DLG_NEW_FILE is defined to have the value 4242 and CD_FRAME_STYLE is defined to have the value 42. Mind the change from module name (sfx2) to resource name (sfx).

So the proposal for GUI element HIDs is:

'Convert all HID defines from integer values to string constants and all automatic HIDs from using symbolic resource defines as sub-strings to using string r'epresentations of the integer values of these defines instead. Adjust vcl, resource compiler and resource manager to handle the new data type and the new way to create automatic HIDs. Convert test scripts and help content to use the new automatic HIDs.


  1. Strictly speaking .uno:Save is not an URI anymore, as the protocol part is not allowed to start with a dot. But these strings are only used internally.
Personal tools