|C H A P T E R 6|
Activating the Interface: Adding Your Own Code
The aim of X-Designer is to let you develop as much of your application as possible without writing code. You still need to write code, however, to make your application work as you intend and to link it to the user interface. You must also write code to control the behavior of the user interface. The following X-Designer features make some of this easier:
This chapter uses the tutorial example built in the preceding chapters to demonstrate how to add a callback and how to add links to your design. The above topics are also examined separately in some detail.
A callback list is a list of one or more callback functions designated to be triggered by user actions in your application. User actions include mouse button presses, keyboard selections and movements of the pointer. By setting up a callback, you can instruct your interface to call the functions on the callback list whenever a certain user action occurs within a widget. The callbacks dialog is shown in FIGURE 6-1.
An "M" or "C" displayed to the right of a callback indicates that a method or callback respectively has been declared.
Callbacks which apply to Java code are listed with the letter `J' after them, as shown in FIGURE 6-1. Callback functions are not generated into the Java code, so you should use callback methods if you wish to use them in your Java application. For more information on using X-Designer to generate Java code, see Chapter 10.
An asterisk (*) next to a callback indicates that the callback is not supported by Microsoft Windows.
The area in the lower left of the Callbacks dialog allows you to set up a Smart Code callback. These are callbacks which give you toolkit independence by "wrapping" specified widgets in an extra layer of code. Smart Code is most useful when you are creating a thick client, or a thin client and server, from your design. See Chapter 16 for more information on Smart Code. For this tutorial, you will not be using Smart Code. However, there are a number of tutorials which do use Smart Code in later chapters.
In the following steps you will designate the simplest example of a callback by using the design from the tutorial of the preceding chapters. Clicking on the "Exit" button (exit_button) will trigger a callback list with just one function, quit(), which terminates the program.
The "Callbacks" dialog lets you associate lists of functions with user actions. You can associate quit() with exit_button now, even though quit() has not yet been written.
quit() and other callback routines are written in C or C++ and linked in with the code generated by X-Designer. You will write your quit() routine in Adding Callback Functionality. The topic of writing callbacks is discussed in greater depth in Callback Functions.
1. Open your saved tutorial design.
2. Click on the "Exit" button (exit_button).
3. Click on the "Callbacks" toolbar button or select "Callbacks" from the Widget menu.
You are going to associate quit() with the "Activate" callback. Activating means that the user presses and then releases a mouse button while the pointer is located inside the widget. The user can also activate with the <Return> key, or other keys as described in the Motif User Guide.
When a callback in the "Callbacks" dialog is selected, the list of callback functions you have associated with it are displayed. To add an Activate callback:
4. Click on "Activate" in the list of Callback types.
This displays, in the list of callback routines, both those callbacks local to the widget and those inherited by it. Inherited callbacks are explained in Inherited Callbacks.
You may only change callbacks which have not been inherited. FIGURE 6-2 shows two typical examples. Example A of the figure shows the callback list for the "Exit" button in the tutorial interface; Example B shows a slightly more complex list.
Widgets which are instances of definitions can inherit callbacks from the corresponding widget in the definition.
Inherited callbacks are shown enclosed in square brackets (), as shown in FIGURE 6-3.
In general, the syntax for each function call in the callback list is the same as C syntax. You do not, however, type them in as C code. Function brackets () are not required as these are added automatically.
When we specify the name of the callback, we must also choose which language we are using.
1. If it is not already selected, choose "Function name" from the option menu.
2. Click in the "Function name" text box.
3. Type: quit
4. Click on "Add".
Pressing Return has the same effect.
5. Close the dialog.
6. Save your design.
While the callback list looks like C code, it has no logical flow. This means that you can neither use C's logical operators such as if...else and while, nor can you rely on your callbacks being executed in any particular order. All routines in your list will be executed whenever the specified event occurs but not necessarily in the order you type them. If the execution sequence is important, you can write a single callback function which contains subroutine calls in the order you want.
The "Client Data" text box allows you to specify data to be passed in to the callback. It is better practice to use this mechanism when a callback needs to use some data than to use a global variable. Enter the string you wish to appear as the parameter here. You may also add a type cast in the usual C/C++ syntax. You do not, however, need to type the function parameter brackets () as these will be added for you automatically. See Callback Function Parameters for more details about the parameters passed to callback functions and an example.
Note - You can only add client data to function callbacks, not to method callbacks. See Callback Methods for more details about this.
If the selected widget is enclosed in a C++ class and "Method name:" is selected from the pulldown menu to the left of the name text field, a "Methods..." button is shown to the right of the name text box. With "Function name:" selected, this button is not shown. When pressed, the "Methods..." button displays a list of callback methods already defined for the selected widget's enclosing class.
Selecting one of these and pressing "OK" will place the selected item in the "Method name" text field. See C++ Classes for details about the way in which widgets are enclosed in classes.
Pressing this button allows you to edit your stubs file (the generated file containing the specified callbacks) without leaving X-Designer. This is dealt with in more detail in Adding Callback Functionality. See also Setting up Callback and Prelude Editing for details on how to set up the editing feature so that you can use the editor of your choice.
The option menu next to the "Edit Code" button contains the possible code generation "flavors". The options are:
Note - The last two options are only shown in Microsoft Windows mode. See The Flavor Menu for information on these.
The Flavor Option Menu works in conjunction with the "Edit Code" button. When you edit your stubs file, you must specify which language you are using. X-Designer will try to work this out for you and set the menu accordingly when you invoke the dialog. Sometimes this is not possible if, for example, you are working with two languages in the same design. You should, therefore, always check that the appropriate option is chosen from this menu.
The option menu shown in FIGURE 6-1 next to the "Flavor" option menu allows you to choose what is generated for your callback. The choices are:
Choosing "All" will generate the call to the toolkit to register the callback (XtAddCallback) and the stub routine for the callback. You may wish to select "Register only" if the callback is already defined in an external library. This may be the case if you are updating legacy code or migrating from another GUI builder, for example. Choosing "Stub only" is useful if you wish to register the callback at a later point in your application code.
The default for this menu is whatever option is selected from the equivalent "Callbacks" menu in the Code Generation Options dialog, described in Code Generation Options. If an option has been selected for an individual callback in this dialog, it will override the more general option in the Code Generation Options dialog.
Use the "Update" button to change the settings of an existing callback.
If you wish to change a non-Smart Code callback to a Smart Code callback you can only do so if the callback is used in just one place. This is because the same callback cannot be both at the same time. If you select a non-Smart Code callback that is being used elsewhere, the Smart Code toggle remains insensitive.
If an existing callback has been changed into a Smart Code callback and you have already generated a stubs file, you should go to the file and remove or rename the non-Smart Code callback stub so that X-Designer will generate the new Smart Code for you
Using the "Clear Settings" button is most useful if you have one or more Smart Code callbacks and you wish to add a new callback routine without accidentally picking up the Smart Code settings of the previously selected callback. "Clear Settings" deselects all callbacks and any other non-default settings.
The "Remove" button removes the currently selected callback. Be careful when using this button as the operation cannot be undone.
The tutorial example continues in Links. The chapter now continues with a more thorough explanation of callbacks and how they may be used.
Creation Procedures describes how X-Designer creates the widgets in your application and sets their initial resource values. However, it is the callback functions and translations that make the application work. See Translations and Actions for more information on translations.
Most callback functions have a similar structure. A typical callback function does some or all of the following:
A callback function receives three parameters:
The call data is a pointer to a data structure defined by the widget developer. Call data structures are described in your Motif documentation or documentation supplied by the developer of the widget toolkit. See Books on X and Motif for details of some useful Motif documentation.
The client data is a pointer that you can use to pass the address of any variable or structure. When you register a callback, you can specify the value for the client data parameter that is passed to the callback function.
In X-Designer, the client data is specified in the callback dialog as a single optional parameter of the callback function. See Client Data for details on how to do this. This could be a pointer to a structure, which can be defined and initialized in a suitable prelude. For example, a typical prelude might be:
/* Pre-manage prelude for main dialog Shell *//* Define and initialize client data for the rungrep callback */
The callback is specified as:
To enter this in the Callbacks dialog, the function name rungrep is typed into the text field labelled "Function name" and the parameter, including the cast, is typed into the "Client data" text field: (XtPointer)&rcdata.
The declaration of the structure rcdata_t would normally be in a header file that would be included in the generated code (by adding "#include ..." as the module prelude) and in the callback function module. The callback function can then cast the client data to (rcdata_t *) and so access the data.
Note that the structure rcdata is defined to contain pointers to the widget variables, rather than the values of the variables themselves. This lets rcdata be initialized before the widgets are created. You can also define a structure into which the values of the widget variables are copied. However, this cannot be initialized until all the widgets have been created, which can be tricky.
Ideally it would be desirable to add class member functions to widgets as callback functions. Unfortunately this is not possible because callback functions are called by a C library and they cannot provide the instance context (the this pointer) required by a class member function. X-Designer provides an automatic way of calling a class member function from a callback. These are called callback methods and are discussed in Callback Methods.
All callbacks are passed the address of the widget to which they belong. This is a variable of type Widget. In the X-Designer generated code, the variable name of the widget is used for the name of this pointer. If you want a callback function to access widgets in your design other than the widget to which the callback belongs, you have the following choices:
Pass the other widgets as part of the client data structure. See Callback Function Parameters for a description of this structure.
The simplest technique is to have X-Designer define the widget variables as global. You can then access them from a callback function by declaring them as extern in the callback function module. You could include X-Designer's generated "Externs" file in the stubs file in order to do this.
X-Designer declares named widgets as global by default. You can change this behavior by setting the Storage Class of the widget in the Core resource panel.
The strength of the global variable approach is its simplicity. However, having many global variables does nothing for the structure of your program and you must pay attention to naming conventions to ensure meaningful names and avoid duplicates.
You can reduce the need for global variables by including the primary module in the callback function module, using #include. The primary module should be generated without includes of the X and Motif header files.
If you do this, X-Designer still declares named widgets as global. You may want to change their storage class to static, which makes them local to the callback function module.
This technique works well where a callback function needs access to widgets that are all or mostly within a single design. In more complex situations, you can add accessor functions to the callback function module. A callback function that needs to manipulate a widget which is local to another callback module can do so via the accessor functions.
You can also access widgets using the X toolkit convenience function XtNameToWidget(). Pass the widget name to this function and a pointer to the widget is returned. See your X toolkit documentation for more information.
Accessing Widgets in Callbacks gives you some ways to access the widgets in your design. Once you have a widget, there are many ways you can manipulate it. This section outlines a few of them. It is not a detailed description, but is only intended to point you to the appropriate functions and their documentation.
The Motif toolkit provides a large number of convenience functions for getting and setting attributes of some widgets. These are all named after the widget class that they affect, such as XmTextSetString(), XmTextGetString(), XmToggleButtonGetState(). These are documented in the Motif Programmer's Reference.
Convenience functions are the first place to look. They are the easiest to use and are likely to be efficient.
One point to note is that convenience functions take a Widget parameter and expect this to be a pointer to a widget of the appropriate class. If the widget is of the wrong class, they commonly core dump. There are also both widget and gadget versions of some of the convenience functions and you may get a core dump if you use the wrong one.
If there is no convenience function, you may have to get or set one or more of the resources of the widget directly using XtGetValues() or XtSetValues(). This is fundamental to widget programming and any book on Xt or Motif should cover it adequately.
Not all resources can be set after a widget has been created. The Motif Programmer's Reference documents the access controls on each resource of every widget class.
To disable a widget (that is, to make it insensitive to user input), or enable it again, use XtSetSensitive(). You should not set the resource XmNsensitive directly.
When a widget becomes insensitive, so too do all its descendants. Insensitive widgets are usually grayed out.
If you make a Text or TextField widget insensitive, the user cannot use key input to pan and scroll the text and so has only a limited view. It may be better to set the resource XmNeditable to False.
There are two ways to make a widget appear or disappear: managing and mapping.
If a widget is unmanaged, its parent does not reserve any space for it and it is not visible on the screen. A widget is unmanaged using XtUnmanageChild() or XtUnmanageChildren() and managed using XtManageChild() or XtManageChildren(). X-Designer generates code to manage widgets after they have been created, but the Managed toggle in the Core resource panel changes this.
If a widget is managed but not mapped, its parent reserves space for it. However, it is still not visible; there is a blank hole. Widgets are normally mapped automatically when they become managed. This is controlled by the resource XmNmappedWhenManaged which can be found on the "Settings" page of the Core Resource panel.
Mapping and unmapping is commonly used to change the visibility of widgets within a dialog without causing its layout to change. Managing and unmanaging cause layout changes.
You can make a complete dialog appear or disappear by managing or unmanaging the child of the Dialog Shell. If the dialog uses a Top level Shell, use XtPopup() and XtPopdown() on the Shell instead.
Note - Links describes an automatic and dynamic means of showing and hiding widgets using X-Designer`s built-in links facility.
X-Designer generates code to create the widgets for your dialogs. The default main() program calls all the creation functions at start-up time. Since widget creation is relatively expensive, this may cause an unacceptable delay. It is common practice to defer creation of a dialog until the first time it is popped up. A static Boolean flag in the callback function that performs the popup can be used to determine if the dialog has already been created.
As well as generating code to create complete dialogs, X-Designer can generate creation functions for dialog fragments, as described in Children Only Place Holders. You can call these from a callback function, so as, for example, to create another instance of some reusable component.
To destroy a widget (and all its children), use XtDestroyWidget(). It is inefficient to destroy a widget and then recreate it; you should unmanage it, then manage it again when it is needed.
X-Designer has predefined callback procedures called links. There are six links available:
Only PushButtons, ArrowButtons and CascadeButtons can be the source of a link. All links are triggered by an "Activate" event. A link can show, hide, manage, unmanage, enable, or disable any widget in the design. One button can have multiple links.
Links are callbacks which X-Designer sets up for you. Unlike callbacks, however, links work in the dynamic display and can therefore be used for prototyping window behavior. When you generate code, you can either include links, which work exactly as they do in the dynamic display, or substitute more complex callbacks for the simple links.
Links can only be added if at least one of the following criteria is met. If none are met, the "Add" button is disabled and no links can be made. The requirements are:
You are now going to set a common configuration of links to display the help screen you have just built and make it disappear again at the proper time. To do this you will:
The "OK" PushButton is currently visible in the construction area and so begin by setting the "Hide" link on this PushButton.
1. Select the PushButton.
2. Double-click in the "Variable name" field.
3. Type: ok_button and press <Return> to register the new name.
4. Pull down the Widget Menu and select "Edit links".
This displays the panel shown in FIGURE 6-4.
The target of the "Hide" link should be the widget help_window so that when the "OK" button is activated, the entire help screen disappears.
To select the target widget:
5. Select the Shell help_window in the design hierarchy.
The name of the Shell, help_window, appears in the "Widget" field of the Links panel. However, the "Add" command is still disabled. This is because you have not yet named the DialogTemplate which is the immediate child of the Shell. As discussed above, the child of a Shell must be named explicitly before you can set a link to the Shell.
You can leave the Links panel open while you name the DialogTemplate:
6. Select the DialogTemplate.
7. Double-click in the "Variable name" field and type: dialog_2
8. Reselect the Shell help_window.
The Shell is now a valid target widget and so "Add" is enabled.
Now select the type of link:
9. Select "Hide" from option menu of link types.
10. Click on "Add".
The new link appears in the link display area, as shown in FIGURE 6-5.
11. Click on "Close".
To demonstrate the new link:
12. Click on the "OK" button in the dynamic display.
The help screen vanishes. You can restore it by resetting the Shell.
You can also set up a "Show" link to display the help screen when a button is pressed in the main window. To do this:
13. Click on the MyFirstShell icon in the window holding area.
The hierarchy for the main window is displayed in the construction area.
Set the new link on the PushButton in the Help Menu:
14. Select the help_button widget, the PushButton child of the second Menu.
The Links panel, unlike resource panels and the Layout Editor, does not automatically start adding links to the currently selected PushButton. To edit links for the currently selected button, you must:
15. Pull down the Widget Menu and select "Edit links".
The Links panel now displays the name and the links (none, so far) of the current PushButton. Select the target widget, which is the Shell for the help screen:
16. Click on the help_window icon in the window holding area.
In the Links panel:
17. Select the "Show" link type.
18. Click on "Add".
The new link appears in the link display area.
To demonstrate the behavior of these two links:
19. Click on the MyFirstShell icon in the window holding area.
20. Pull down the Help Menu in the dynamic display and select "About This Layout".
The Show link on this pushbutton makes the help screen appear.
21. Click on the "OK" button in the dynamic display of the help screen.
The Hide link on this pushbutton makes the help screen disappear. You can repeat the previous two steps as many times as you want.
22. Save your design.
To remove a link:
Select the link's icon in the link display area and click on "Remove".
The tutorial continues in Chapter 7. The remainder of this chapter looks at other ways of adding functionality to your application using X-Designer.
Drag and Drop is provided by the Uniform Transfer Model in Motif 2. This provides a standard way of implementing drag and drop, clipboards and selection, making implementation of these features much simpler for the programmer. Motif 1.2, however, did not include the Universal Transfer Model although it did provide some support for drag and drop. The following sections explain both the Motif 2 and the Motif 1.2 models for implementing drag and drop.
The Uniform Transfer Model, provided as a standard part of Motif 2, gives automatic support for data transfer between widgets - whether using drag and drop, the clipboard (for cut and paste) or primary and secondary selection. The only visible part of this model appears in the Callbacks dialog where there are two new callbacks - Destination and Convert.
The Destination callback is called by the Uniform Transfer Model to request data from the source of a drag, selection or clipboard. The Convert callback is called to export data from the source in the format required by the destination. You do not have to override these callbacks as the Uniform Transfer Model provides automatic support for standard types such as strings and pixmaps. If, however, you have defined your own type, you can override these callbacks to allow your type to be used for the functions that the Uniform Transfer Model provides.
Motif 1.2 provides a sophisticated drag and drop mechanism that lets applications communicate data via the X selection mechanism. X-Designer provides some simple support to let you specify drop sites in your application. Because the initialization of a drag is a dynamic function that would normally be done from within a callback or action function, X-Designer does not provide any explicit support.
A drop site is a widget that is prepared to receive certain types of data from the transfer mechanism. X-Designer provides its support through the Drop site page of the Core resource panel.
To designate a widget as a drop site, simply set the "Drop site" toggle on and specify the import targets and drop procedure. The "Import targets" field is a list of strings that are converted into atoms to designate types that can be handled by the drop procedure. The list is specified as strings separated by commas or spaces.
By default Motif makes Label (and derived) widgets start a drag operation to transfer the labelString or labelPixmap if Button 2 is pressed over them. X-Designer takes advantage of this by adding a drop procedure to the drop site widget that imports these types if specified in the import targets. The following tutorial lets you see how the drop site operates.
1. Create a dialog containing an Application Shell with a RowColumn containing two Push Buttons.
2. Name the widgets: shell, rowcolumn, MyButton1 and MyButton2 respectively.
3. Pop up the Drop site page of the core resources for MyButton1.
4. Set the drop site toggle on and set animation style to "shadow in".
5. In the "Import targets" field, type: COMPOUND_TEXT
6. In the "Drop procedure" field, type: drop_button1
7. Apply your changes and close the dialog.
The drop and drag procedure fields specify the names of functions to be called to handle the drop and dynamic drags respectively.
8. Try dragging the text from any label (press mouse button 2 and drag) across MyButton1 in the dynamic display window.
The button is "shadowed in" i.e looks selected, to indicate that it is a valid drop site for the target being dragged.
9. Release the mouse button to drop the text into the widget.
The drop procedure provided by X-Designer simply copies the label into the widget.
10. Select MyButton2 and repeat Step 4.
11. In the "Import targets" field, type: PIXMAP
12. In the "Drop procedure" field, type: drop_button2
13. Try dragging a pixmap from the tool bar across MyButton2 in the dynamic display window.
For further examples of using drop sites and for information on starting drags, refer to the Motif documentation.
Code is generated for C and C++, with a call to XmDropSiteRegister() being generated for widgets that are not normally drop sites. Text widgets are drop sites by default, which can import COMPOUND_TEXT. This can be disabled by setting the drop site toggle off, or modified by simply changing the appropriate resources.
You must write the drop procedures to handle the transfers. They are simply declared as external functions in the generated code.
Widgets have behavior. For example, when a user presses mouse button 1 over a PushButton, it highlights. When the user releases the mouse button, the PushButton's appearance reverts to normal and the functions on the Activate callback list are invoked.
This behavior is not hard-wired into the PushButton widget. Instead, it is determined by the widget's translation table, which maps events to the actions to be taken in response to the events. When a widget is created, its translation table is initialized to contain a default set of entries. For example, the PushButton widget's default translation table includes these entries:
To the left of the colon is an event specification; to the right are the names of the actions that the widget performs in response. A second table, the action table, is used to map the action name to the address of a function that performs it.
For example, the PushButton's default action table includes:
The first item in each entry is an action name and the second is the name of a function. Convention and common sense dictate that the action and function names should be the same, or at least related in a well-defined way.
You can change the translation table of any widget within X-Designer. You cannot change the action table of a widget. However, you can define new actions in an application-global action table which is searched after the one associated with the widget. This requires some coding, as described below.
Modifying the translation table of a widget in X-Designer is straightforward. To understand the procedure, do the following simple exercise in X-Designer.
1. Create a simple widget hierarchy containing a PushButton.
2. Select the PushButton icon in the widget hierarchy.
3. From the "Widget" menu, select "Translations...".
This displays the translations dialog, shown in FIGURE 6-7.
4. Click in the lower section, under "Augment", and type: Ctrl<Key>q: ArmAndActivate()
5. Click on "Apply".
This adds the new translation to the PushButton widget and you can now try its effect.
Note - You may notice an error dialog indicating that the action has not been found. This is for information only, the change has been taken. See Additional Actions for information about creating an action table within X-Designer.
6. Place the mouse pointer over the pushbutton in the dynamic display window.
7. Type: <Ctrl-Q>
The effect is identical to clicking with mouse button 1. Note that translations do not work if the window does not have the input focus and that the input focus behavior depends on the configuration of your window manager.
You can also change the translations you have specified so that the button triggers on other events. Note that if you do this, the previous translation remains effective in addition to the new one until you reset the widget.
The translations dialog has sections labeled "Override" and "Augment". You can enter new translations in either section or both. They only differ if you specify a translation with the same event specification as an existing translation. If you type the new translation into the "Override" box, your new translation replaces the existing one. If you use "Augment", the existing translation takes precedence.
The existing default translations for the widget are not affected when you add translations unless you override them. This is important because Motif widgets have many default translations that produce their expected behavior.
If you set the "Replace" toggle, however, all existing translations are removed and replaced by the translations you enter. Use "Replace" with caution. Do not confuse "Replace", which removes all the default translations, with "Override", which replaces them one by one.
The syntax of translation tables is complex. The following sections detail the syntax as used in X-Designer. For a complete and definitive description, consult the X toolkit documentation.
Each entry in a translation table has the form:
Square brackets () indicate that an item is optional; an ellipsis (...) indicates that the item may be repeated.
The modifier list represents the state (pressed or not pressed) of the modifier keys (such as Control and Shift) and the mouse buttons (X believes that a mouse has five buttons). The most useful modifiers are Ctrl, Shift, Alt and Meta. These can be abbreviated as c, s, a and m.
If the modifier list is omitted, the state of the modifiers is unimportant:
<Key>Q matches <Q>, <Ctrl-Q>, <Alt-Meta-Q>, etc.
If a particular modifier is not mentioned in the list, its state is unimportant:
Ctrl<Key>Q matches <Ctrl-Q>, <Ctrl-Meta-Q>, <Ctrl-Alt-Meta-Q>,...
You can specify multiple modifiers in the modifier list:
Ctrl Meta <Key>Q matches <Ctrl-Meta-Q> but not <Ctrl-Q> or <Meta-Q>
To specify that a modifier must not be pressed, precede it with a tilde (~):
Ctrl ~Meta<Key>Q matches <Ctrl-Q> but not <Ctrl-Meta-Q>
To specify that the modifiers pressed must exactly match what you specify, start the modifier list with an exclamation mark (!):
!Ctrl<Key>Q matches <Ctrl-Q> but not <Ctrl-Meta-Q> or <Ctrl-Q> with a mouse button pressed.
The modifier "None" means that there must be no modifiers pressed at all.
None<Key>Q matches <Q> but not <Ctrl-Q> or <Alt-Meta-Q>, etc.
Normally, translations are not case-sensitive. <Key>Q matches both <Q> and <q>. You can specify that a translation is case-sensitive by preceding it with a colon (:).
:<Key>Q matches <Q> but not <q>
The event can be the name of an X event, or one of a number of aliases. Some of the most useful events are Key (a key press), BtnDown and BtnUp (for any mouse button) and BtnNDown and BtnNUp (where N is between 1 and 5). For a complete list of events and aliases, see the Xt documentation.
<Key>a matches <a>
<Btn1Up> matches a release of mouse button 1
You can specify a sequence of events in a translation, separated by commas.
<Key>Q,<Key>A matches <Q> followed by <A>, with no intervening event.
<Btn1Down>,<Btn1Up> matches a click of mouse button 1.
The count can be used with button press and release events to detect multiple clicks. The count is a number from 1 to 9, possibly followed by a plus (+).
<Btn1Down>(2) matches two presses of mouse button 1
<Btn1Up>(3+) matches 3 or more releases of mouse button 1
If a count is used, the button events must come close together (usually within 200 milliseconds of each other), or there is no match.
The final field in the event specification is the detail. This is normally used only with key events, where the detail specifies which key is to be pressed.
The value specified in the detail field is a keysym, as in the header <X11/keysymdef.h>, with the XK_ prefix removed. For most keys, this is the same as the character on the key.
<Key>a matches <a>
For non-alphanumeric keys, check the name of the keysym. The keysym for "+" is XK_plus, so
<Key>plus matches <+>
Since matching is case-insensitive, this also matches the other symbol on the plus key, which is <=> on most keyboards.
Motif adds another level of complexity by translating certain incoming key events into Motif virtual keysyms. You should use these virtual keysyms in your translation tables instead of the X ones.
<Key>osfDelete, not <Key>Delete
The virtual keysyms are listed below. For details of their interpretation, see the VirtualBindings(3X) section of the Motif Programmer's Reference.
You can also use the detail field with mouse button events to specify a particular mouse button. This is not commonly done since it is easier to specify the mouse button in the event field.
<BtnDown>Button1 is the same as <Btn1Down>
The actions on the right side of the translation table entry are simple. Usually each action is just a name followed by parentheses. Although any number of string arguments can be given between the parentheses, most action routines expect no arguments. Arguments should not be quoted. Typical additional translations for a ScrollBar widget might be:
You can specify multiple actions or none at all. Overriding an existing translation with one that has the same event specification but no action is a useful way of disabling part of a widget's default behavior.
In many cases, the actions used are the ones predefined by the toolkit. The Additional Actions discusses how to add your own actions.
When an event is received, the translation table is searched from the top down. The search terminates at the first entry whose event specification matches the event. This means you should organize your translation table with the most specific events first. For example, a translation table might contain the following entries:
When the user types either <Q> or <Ctrl-Q>, the search terminates at the first entry and action1() is invoked in both cases. To make <Ctrl-Q> invoke action2, you must reverse the order of the entries.
For additional subtleties in ordering translation tables, see the X toolkit documentation.
By changing the translation table, you can make a widget perform actions in response to event sequences that would not normally trigger those actions. While you can write your own action routines, translations provide the most benefit when you can use one of the built-in actions of the widget.
The built-in actions of the Motif toolkit are documented in the Motif Programmer's Reference. Each widget description includes both the default translations and the actions they invoke. Some of the primitive widgets offer a particularly large set of actions.
If you add a translation that uses one of these actions, you can test it in X-Designer immediately. Alternatively, a few built-in actions, such as the PushButton's Activate() action, invoke the functions in one of the widget's callback lists. In this case, it may be easier to specify a translation to call that action on the appropriate event sequence and put the code in an ordinary callback function.
If you cannot find a built-in action to suit your needs, you can write your own action routine to perform the action. FIGURE 6-8 shows an example translation. The example shown says that when <Ctrl-e> is pressed, the action "doActionE" is triggered.
If you define your own action routine, it needs to be added to the application's "action table". X-Designer does this for you automatically when you define the action procedures in the Action Procedures dialog, displayed by selecting "Action Procedures..." from the Module menu, as shown in FIGURE 6-9.
In the example shown in FIGURE 6-8, "doActionE" is an action which needs to be defined for the application. FIGURE 6-9 shows the definition of this action. The "Action Name" is the name used in the Translations dialog, the "Procedure Name" is, as you might expect, the name of the procedure which will be called. In the interests of clarity, these names are usually the same.
Action procedures do not have any client data associated with them, but they are passed the parameters defined for the action in the Translations dialog. You can provide any number of parameters in that dialog and within the parentheses, so long as they are all strings.
You may have any number of action procedures defined within your design. X-Designer generates their stubs and the associated action table into the main code file.
X-Designer allows you to add the following types of Xt procedure:
This includes input sources and timeouts which may be treated as though they are the source of events, as well as Xt Work procedures which are called when there are no events to process.
One of these is called at the beginning of an X application as a means of customizing the localization of the application, although you may specify any number of them.
These procedures are called when one of a number of pre-defined actions occurs (e.g. "mouse button 1 pressed"). These bypass the translation table.
The first two types of Xt procedure listed above are specified on an application-wide basis. The fourth is specified on a per-widget basis. All are described in the following sub-sections.
An Xt (X toolkit) application normally waits for events from the X server. User actions, such as keyboard presses and mouse clicks, arrive at the Xt application via the X server. If, for some reason, a user is sitting quietly and not typing or clicking, then the application just waits. This means that an Xt application can spend a considerable amount of time waiting. For this reason, Xt allows you to register procedures to be called when there are no other events to process. You may also register procedures which respond to certain events not originating from the X server. Here are the "extra" procedure types which may be added:
As with widget callback procedures added inside X-Designer, any Xt procedures that you have added are generated as stubs into the stubs file. You may then edit them from within X-Designer in the same way as widget callbacks may be edited. The code for adding your procedures is generated by X-Designer into the main module.
These procedures are added by selecting the corresponding item in the Module menu. The following sub-sections describe each procedure type individually.
A work procedure is a function that is called when Xt has no other events to process. Work procedures are, therefore, a convenient means of setting up a background batch process without interfering with events from the X server. One common use of work procedures is in program initialization. There are often many widgets which need to be created when an application is started. Since this is a time-consuming process, using work procedures allows the application to respond to user actions almost immediately.
Work procedures should return True or False. A value of True indicates to the X toolkit that your procedure should be removed from the work queue once executed. False indicates that it should be called again the next time the queue is empty of user events. You can have any number of work procedures registered at any time.
To add a work procedure, select "Work Procedures..." from the Module menu. The dialog shown in FIGURE 6-10 appears. You may add any number of work procedures but you should remember that your application cannot handle any other event while executing a work procedure. For this reason, work procedures should return quickly. Priority is generally given to work procedures in the order registered.
Use input procedures to set up a file or pipe as a source of events. The input procedure is called when the file is ready for reading (or writing). To add an input procedure, select "Input Procedures..." from the Module menu. The dialog shown in FIGURE 6-11 is displayed.
To define an input procedure, you will need to provide the file descriptor of the file or pipe. The Input Mask specifies the type of access the file should have. When the file is ready for the specified access, the input procedure is called. You may define any number of input procedures, they will be added in turn.
A timeout procedure provides a means of performing a function after a specified amount of time has elapsed. Timeouts are called once only, so if you need a timeout procedure to be called at regular intervals, it will have to add itself as another timeout procedure before exiting. The following function call will do this:
XtAppAddTimeOut(appContext, timeoutPeriod, procedure, clientData);
To add a timeout procedure, select "Timeout Procedures..." from the Module menu. This causes the dialog shown in FIGURE 6-12 to be displayed.
The "Timeout Period" is specified in milliseconds.
Timeout procedures work better in an X environment than time-interrupt programming using signals, which is often the preferred method with UNIX. You may define any number of timeout procedures; X-Designer generates them all into the main code file.
An application operates within the context of a particular locale. The locale determines how to accept keyboard input, how to display characters and the format of date and time strings. This allows developers to customize their applications for use in different countries.
X-Designer generates a call to the X toolkit routine XtSetLanguageProc in the main code file. One of the parameters to this routine is the name of the procedure which will set up the locale. Xt provides a default language procedure, but you can define your own should you wish to have additional methods of setting the locale or only provide support for certain locales. To define a language procedure, choose "Language Procedures..." from the Module menu. The Language Procedures dialog, shown in FIGURE 6-13, is then displayed.
You may specify as many language procedures as you like, but only one will be effective (as only one can be passed as a parameter to XtSetLanguageProc). X-Designer chooses the procedure at the top of the list in the Language Procedures dialog as the procedure to pass in. You can change which is the topmost procedure in this list by using the arrow buttons underneath. Simply select the required procedure and press the up arrow until it is at the top. Stubs for all language procedures in the list are generated into the stubs file.
Event handlers provide an efficient means of performing low level input handling which bypasses the translation tables of the widget. They are particularly suited to high-volume events. A translation and associated action, however, can do almost anything that an event handler can do but may be easier to maintain.
Event handlers, unlike the other Xt procedures, are defined for individual widgets rather than for the whole application.
Add an event handler by selecting the widget whose events are wanted and then choosing "Event Handlers..." from the Widget menu. The dialog shown in FIGURE 6-14 is displayed.
In this dialog there are text fields for entering a procedure and an event mask. The text fields have corresponding buttons which, when pressed, display sub-dialogs showing the procedures and event masks available. You may define a new procedure but the event masks should come from the valid set displayed. The event masks are annotated with the Java "coffee cup" if they are applicable to Java code, and indicated with a tick if they are mapped to MFC for Windows, as shown in FIGURE 6-15.
If the "Non Maskable" toggle is set, the event handler will also receive the non-maskable events (ClientMessage, GraphicsExpose, MappingNotify, NoExpose, SelectionClear, SelectionNotify and SelectionRequest). This toggle would normally remain unset as these events are not particularly useful.
The "Raw" toggle is a means of telling X-Designer to add a raw event handler. This is an event handler which does not respond immediately to the events for which it is registered. Instead, the handler is triggered when its events are selected elsewhere (by another event handler, for example).
One possible use of raw event handlers is to "shadow" another event handler. If both are added with the same event mask, but one is "raw" and the other is not, then both handlers will be called when the appropriate events occur. The raw event handler can then log the events that the other handler receives.
As with callback functions, you can specify the same event handler for any number of widgets and then use the Client Data to set the context.
Typically, event handlers are used in situations where the widget has little in the way of built-in interaction support, or where large volume or crude input may need processing. DrawingAreas are obvious candidates for event handlers.
Event handlers are generated by X-Designer into the code file as part of the general widget configuration and a stub is generated into the stubs file.
There is a "Widget annotation" for event handlers. By selecting the event handler annotation from the pullright menu in the View menu, you can see at a glance which widgets have had event handlers defined for them.
If an X or X toolkit error is encountered while your application is running, it is reported to you - usually by way of a simple message dialog. In some cases, your application is made to exit. X-Designer allows you to control what happens when an error occurs. To do this, select "X errors..." from the Module menu. The X Errors dialog, shown in FIGURE 6-16, is displayed.
The procedures whose names you type in here will be called when the corresponding X or Xt error occurs. This allows you to decide whether to cause the application to quit, to report the error or simply ignore it. Stubs for these procedures are generated into the stubs file. As with all other callback procedures, you can edit the generated code by pressing the "Edit code" button.
The item labelled "Visuals..." in the Module menu displays the Visuals dialog as shown in FIGURE 6-17.
This dialog allows you to specify a private colormap and visual type for your application. For the visual type you can specify the preferred visual class and the preferred depth. The visual class can be one of "Static Gray", "Gray Scale", Static Color", "Pseudo Color", "True Color" or "Direct Color". For the depth you can choose between "8", "16" and "24". Consult your X documentation if you need more information on colormaps and visual types.
You may wish to refer to a pre-existing colormap or visual type. To generate these as external variables, generate the code file without the main program file.
As with callbacks, the generated stubs for Xt procedures can be edited from within X-Designer using your favorite configured editor.