Creating a New UI Service

A user interface service is responsible for creating a user interface element that is displayed when an iTool user takes some action. A simple UI service may do no more than display the "hourglass" cursor while an operation is being performed; more complicated UI services may be small applications unto themselves.

For simple operations the UI service routine can contain everything necessary to implement the UI service. For more complex interfaces, however, it is often practical to separate the actual user interface code (that is, the widget creation and event-handling routines) from the logic of the UI service itself. The latter is the strategy used by many of the UI services included with the standard iTools.

The process of creating a user interface service is outlined in the following sections:

Creating the UI Service Routine

The user interface service routine performs the following tasks:

To accomplish these things, the UI service routine needs a reference to the iTool component on which the service will act, and a reference to the IDLitUI object associated with the current iTool. As a result, the user interface service routine has the following signature:

FUNCTION ServiceName, oUI, oRequester 

where ServiceName is the name of the function, oUI is an object reference to the IDLitUI object associated with the iTool, and oRequester is an object reference to the iTool component specified in the call to the DoUIService method.

Note
ServiceName is not necessarily the same as the registered name of the service used in the call to the DoUIService method. The registered name is defined by the call to the IREGISTER procedure. See Registering a UI Service for details.

Return Value

The user interface service routine should return 1 if the action succeeds, or 0 otherwise.

Retrieving Property Information

The oRequester argument to the user interface service function contains an object reference to the iTool component on which the UI service was invoked. Use this reference to retrieve any properties of the object that are relevant to the operation being performed by the user interface.

For example, the standard RotateByAngle user interface service displays a dialog that lets the user set the ANGLE property of an object. The service uses the following statement to retrieve the current rotation angle from the selected object:

oRequester->GetProperty, ANGLE = angle 

Retrieving Widget Information

The oUI argument to the user interface service function contains an object reference to the IDLitUI object associated with the current iTool. You can use this reference to retrieve the IDL widget identifier of the widget that is the group leader of the iTool user interface itself (the iTool window); the ID is stored in the GROUP_LEADER property of the IDLitUI object. Having this widget ID allows you to retrieve screen geometry information that allows you to calculate the position at which your user interface should be displayed.

For example, the RotateByAngle user interface service uses the following code to calculate the X and Y offsets that will be used to position its own user interface over the current iTool:

; Retrieve widget ID of top-level base. 
oUI->GetProperty, GROUP_LEADER=groupLeader 
 
IF (WIDGET_INFO(groupleader, /VALID)) THEN BEGIN 
   screensize = GET_SCREEN_SIZE(RESOLUTION=resolution) 
   geom = WIDGET_INFO(groupLeader, /GEOM) 
   xoffset=(geom.scr_xsize+geom.xoffset-80)<(screensize[0]-100) 
   yoffset=geom.yoffset + (geom.ysize - 400)/2 
ENDIF 
 

The UI service goes on to use the calculated xoffset and yoffset values when positioning the IDL widgets that make up the interface displayed by the service.

Displaying the User Interface

If the user interface being displayed by the UI service is simple, it may be convenient to include the code for creating it directly in the definition of the user interface service itself. For example, the following is the complete definition of the HourGlassCursor user interface service:

FUNCTION IDLitUIHourGlass, oUI, oRequester 
   WIDGET_CONTROL, /HOURGLASS 
   RETURN, 1 
END 

As you can see, no information about the IDLitUI object or the selected iTool component is used, and the displayed item itself is very simple.

In most cases, the user interface service is significantly more complex. In these cases it is often useful to separate the routine that creates the service's user interface from the code that displays it. For example, the user interface for the RotateByAngle service is displayed by the following statement:

result = IDLitwdRotateByAngle(oUI, $ 
   GROUP_LEADER=groupLeader, $ 
   ANGLE=angle, $ 
   CANCEL=cancel, $ 
   XOFFSET=xoffset, $ 
   YOFFSET=yoffset) 
 

This statement calls another function — IDLitwdRotateByAngle — to actually display the required user interface elements, supplying the information retrieved by other portions of the user interface service routine. The IDLitwdRotateByAngle function returns the angle selected by the user. The process of creating user interface elements is discussed in greater detail in Creating Supporting User Interface Elements.

Setting Property Information

If the user has selected a new value for any of the object's properties, that value must be changed on the object by a call to the SetProperty method. In our example, if the user sets a new angle, the following statement updates the property value, notifies the selected object that the value has changed, and inserts the change into the undo-redo transaction buffer:

oRequester->SetProperty, ANGLE = angle 

Note that not every user interface will modify properties of the selected object.

Example

The following example routine is the full definition of the RotateByAngle user interface service described in the previous sections. It is presented here again for completeness, so you can see the entire function at once.

FUNCTION IDLituiRotateByAngle, oUI, oRequester 
 
   ; Retrieve widget ID of top-level base. 
   oUI->GetProperty, GROUP_LEADER=groupLeader 
 
   IF (WIDGET_INFO(groupleader, /VALID)) THEN BEGIN 
      screensize = GET_SCREEN_SIZE(RESOLUTION=resolution) 
      geom = WIDGET_INFO(groupLeader, /GEOM) 
      xoffset=(geom.scr_xsize+geom.xoffset-80)<(screensize[0]-100) 
      yoffset=geom.yoffset + (geom.ysize - 400)/2 
   ENDIF 
 
   ; Retrieve initial angle setting. 
   oRequester->GetProperty, ANGLE=angle, RELATIVE=relative 
 
   result = IDLitwdRotateByAngle(oUI, $ 
      GROUP_LEADER=groupLeader, $ 
      ANGLE=angle, $ 
      CANCEL=cancel, $ 
      XOFFSET=xoffset, $ 
      YOFFSET=yoffset) 
 
   IF (cancel) THEN $ 
      RETURN, 0 
 
   ; Convert from absolute to relative angle. 
   IF (KEYWORD_SET(relative)) THEN $ 
      result -= angle 
 
   ; Set desired angle setting. 
   oRequester->SetProperty, ANGLE=result 
 
   ; Return success. 
   RETURN, 1 
 
END 

Creating Supporting User Interface Elements

It is beyond the scope of this manual to provide general information on the creation of user interfaces. For information on creating a user interface using the IDL widget toolkit, see the User Interface Programming manual. The following are some suggestions for creating IDL widget interface code for iTool user interface services.

Place data collected by the user interface in the function's return value

Create your user interface routine (the routine that creates the IDL widgets that make up the user interface displayed by your UI service) as a function, returning the data values collected by the interface in the function's return value. If you are collecting several values of different data types, return a structure variable containing the data. The user interface and event-handling code should never change data or property values within the iTool itself; all changes should be made via the SetProperty mechanism.

Be sure to clean up heap variables when the user interface exits

If your user interface code creates pointer or object heap variables, be sure to destroy them before the interface code exits. If extra "hanging" heap variables are left undestroyed, IDL can potentially run out of resources if the interface is displayed numerous times.

Use the GROUP_LEADER property if it is available

Pass the widget ID contained in the GROUP_LEADER property of the IDLitUI object to your user interface code, and set the GROUP_LEADER keyword of the top-level base widget to this value. Setting the widget group leader to the leader of the iTool's own widget hierarchy ensures that your user interface will be destroyed if the iTool itself is destroyed.