Event translation is the ability to customize what type of event will cause a given action within an interactor. InterViews Plus takes advantage of this feature primarily for providing keyboard accelerators, mnemonics, and keyboard binding capabilities. The ability to handle mouse events is also supported, but not used in the library itself.
Interactors start out with a hardcoded default translation table providing key bindings for each of the available actions defined in the interactor, if any. These default translations are documented in the "DEFAULT KEYBOARD TRANSLATIONS" section of each interactor’s manual page.
An event translation consists of an event specification and one or more actions. When an event is received by an interactor that matches one of its translation’s event specifications, each action string (there is typically only one) is sent to that interactor’s virtual Interpret() function. Interpret() compares the action string with its list of know actions, and if a match is made, executes the code defined for that action and returns a value of true. If no match is made, the interactor’s base class Interpret() is then called and the result of that call is the return value.
Extending the list of known actions for a given interactor would require deriving a new class and overriding the Interpret() function.
The translation table for each interactor is created during the configuration traversal that occurs when one of the World class’ Insert operations is called (e.g. InsertApplication()).
The first step in creating this table is to combine the interactor’s default translations with any specified by the X resource translations in accordance with the directive. The resulting string is then parsed and each translation is converted into an internal form and added to the translation table.
The directive is the first part of the translations X resource and can be one of: #replace, #augment, or #override. See the "TRANSLATION TABLE SYNTAX" section below.
A directive value of #replace will cause the default translations to be completely overwritten with the X resource value.
A value of #augment will append the X resource value to the default translations. This means that any conflicting event specifications will be resolved in favor of the default translations.
A value of #override will prefix the X resource value to the default translations. This means that any conflicting event specifications will be resolved in favor of the X resource value.
If no directive is specified, the default is #replace.
Once the application has begun, events are intercepted by the Interactor class’ Intercept() member function which is called by the Interactor class’ Run(). The incoming event is compared against the list of events in the translation table of the target interactor.
If a match is found, the associated action string is sent to that interactor’s Interpret() member function and the Handle() member function is not called. If no match is found the target’s parent scene is checked for a match and so on until the top level scene is reached. If no matches were found, the original target’s Handle() member function is then called.
This is by far the most common form of translation used in InterViews Plus. The translation string consists of an optional modifier list, and the literal followed by a keysym string.
The modifier list specifies what keyboard modifiers may or may not be present in order for the translation to match an incoming event. The list of modifiers are:
Modifier List | |
Modifier | Abbreviation |
Shift | |
Lock | |
Ctrl | |
Meta | |
Alt | |
None |
Specifying any of these modifiers or their corresponding abbreviations means that the modifier must be present. Noneis a special modifier meaning that no modifiers may be present. Prefixing a ~immediately before any of the modifiers means that the modifier may not be present. Two other symbols, a :or !may be present at the very beginning of the modifier list. The !means that the modifiers must match exactly. For example, if the translation were: !Ctrl<Key>c: Cancel()
the translation will match only if the Control key is being pressed at the time the c key is pressed. If the Shift key, or Caps Lock key were also pressed, the match would fail.
The : makes the case of the keysym significant. For example, you might want to specify two different actions for upper and lower case c:
:Ctrl<Key>c: Cancel() :Ctrl<Key>C: CancelAll()
The first translation will match only if the Shift and Lock modifiers are not present. The second translation will match if either are present.
A keysym is a hardware-independent symbolic representation of a key. The X server uses keysyms as a portable means of specifying keys based on the labels typically found on top of the keyboard’s keys. Additionally, InterViews Plus provides virtualkeysyms. This is another level of indirection that maps certain vendor specific keys to standard actions, such as osfCancel which is usually mapped to the Escape key. For a full discussion of virtual keysyms, see VirtualBindings(3X) .
Event translations based on mouse button events are not used within the InterViews Plus library itself. They are provided for use in application objects that wish to provide configurable mouse button bindings.
The only button events supported are:
Supported Button Events |
<Btn1Up> |
<Btn2Up> |
<Btn3Up> |
The modifier list is the same as for keyboard translations. An example translation for an interactor that cycles through a set of choices might be: None<Btn1Up>: CycleForward() None<Btn3Up>: CycleBackward()
Translations for enter and leave events are not used extensively in InterViews Plus. The main use of this functionality is to allow creation of an accelerator (see the InterViews Plus Programmer’s Guide). For example, you would like to ring the workstation’s bell (actually a beep) whenever the mouse sprite enters or leaves a message. You derive a button to ring the bell when pushed.
BellyButton* bellyButton = new BellyButton("Ding!"); Message* bellMsg = new Message("Ring in the New Year!"); bellMsg->SetAccelerator("<Enter>: Activate()", bellyButton); bellMsg->SetAccelerator("<Leave>: Activate()", bellyButton);
Now whenever the mouse sprite enters or leaves the message’s canvas, the belly button will be pushed, and the bell will ring.
When creating a new interactor, or deriving from an existing interactor, it is possible to define a set of default translations for it. For example, MyClass is derived from Interactor:
The first step is to define the translations.
static const char* DefaultTranslations \q " None<Key>Return: Activate() \n !Ctrl<Key>c: Cancel() \n Ctrl<Key>q: Quit()";
Next, this translation string is passed to the Interactor member function ProcessTranslations(). This is usually done in the interactor’s constructor.
MyClass::MyClass() { SetClassName("MyClass"); ProcessTranslations(DefaultTranslations); };
Now, whenever one of the events specified in the translation string is encountered, the corresponding action will be sent to MyClass::Interpret().
boolean MyClass::Interpret(const char* action) { if (strcmp(action, "Activate()") == 0) { printf("The Return key was pressed!"); return true; } if (strcmp(action, "Cancel()") == 0) { printf("A Control-C was pressed!"); return true; } if (strcmp(action, "Quit()") == 0) { printf("Quitting..."); World::current()->quit(); return true; } return Interactor::Interpret(action); }
The set of strings understood by the Interpret() function of this class and any above it in the class hierarchy determines the list of valid action strings for this interactor.
For library classes, the default translations are documented in the manual page under the section "DEFAULT KEYBOARD TRANSLATIONS." If the action string doesn’t correspond directly to an actual member function call, its functionality will be documented in the description of the Interpret() member function.
The translation table syntax defined below is actually the syntax for the string which is used to create the interactor’s translation table.
This string will be used either as a parameter to the Interactor member function ProcessTranslations(), or in an X resource file, such as .Xdefaults.
The syntax below is in Extended Backus Normal Form (EBNF ) notation.
- [ ]
- optional
- { }
- zero or more occurrences
- < >
- informal description
- " "
- literals
- ::-
- is defined as
- translation_table
- ::- [directive] {translation}
- directive
- ::- ("#replace" | "#augment" | "#override") "\n"
- translation
- ::-event ":" action "\n"
- event
- ::- key_event | button_event | other_event
- key_event
- ::- [modifiers] "<Key>"keysym
- modifiers
- ::- ([":"]["!"] {modifier}) | "None"
- modifier
- ::- ["~"]modifier_name
- modifier_name
- ::- ("Shift" | "s" | "Ctrl" | "c" | "Meta" | "m" | "Alt" | "a" | "Lock" | "l")
- keysym
- ::- <See keysym description above>
- button_event
- ::- [modifiers] "<Btn"button_description">"
- button_description
- ::- ("1" | "2" | "3")("Up" | "Down")
- other_event
- ::- ("<Enter>" | "Leave")
- action
- ::- <string of ISO Latin 1 characters>
The directive field is only valid when using the X resource translations to specify or modify the translation table in an X resource file.