Creating a New Manipulator
The manipulator class definition file will have the following components:
- A Class Structure Definition — this creates an instance of the manipulator class and instantiates required instance data. See Creating the Manipulator Class Structure Definition.
- An Init method — this method initializes a manipulator object. See Creating a Manipulator Init Method.
- A Cleanup method — this method destroys pointers or objects created by the manipulator. See Creating a Cleanup Method.
- OnMouseDown, OnMouseUp, OnMouseMotion methods — these methods perform actions when the user activates the manipulator and interacts with the visualization using the mouse. See Creating Mouse Event Methods.
- An OnWheel method — this method links events generated by the mouse's scroll wheel to manipulator actions. See Creating an OnWheel Method.
- An OnKeyboard method — this method links keyboard events to manipulator actions. See Creating an OnKeyboard Method.
- A DoRegisterCursor method — this method lets you create and register a custom manipulator cursor that appears when the manipulator is activated. See Creating a RegisterCursor Method.
- GetProperty or SetProperty methods — these methods let you retrieve or configure properties of the manipulator or its superclasses. See Creating GetProperty or SetProperty Methods.
- Within appropriate components, invoke the manipulator's RecordUndoValues and CommitUndoValues methods — these methods call associated operation methods to support undo/redo system transactions. See Manipulators and the Undo/Redo System.
- Other methods specific to the manipulator.
Note
As the RecordUndoValues and CommitUndoValues methods help automate the transaction process, you would typically not need to override the default superclass methods.
Creating the Manipulator Class Structure Definition
When any IDL object is created, IDL looks for an IDL class structure definition that specifies the instance data fields needed by an instance of the object, along with the data types of those fields. The object class structure must be defined before any objects of the type are created. In practice, when the IDL OBJ_NEW function attempts to create an instance of a specified object class, it executes a procedure named ObjectClass__define (where ObjectClass is the name of the object), which is expected to define an IDL structure variable with the correct name and structure fields. For additional information on how IDL creates object instances, see The Object Lifecycle (Object Programming).
Note
The class structure definition is generally the last routine in the .pro file that defines an object class.
Subclassing From the IDLitManipulator Class
The IDLitManipulator class is the base class for all iTool manipulators. In almost all cases, new manipulators will be subclassed either from the IDLitManipulator class or from a class that is a subclass of IDLitManipulator.
Note
If you are implementing a number of manipulators that provide similar functionality, and you want the user to choose one out of the group of items, you may want to create a manipulator container. See Manipulators and Manipulator Containers for an introduction to these objects.
See "IDLitManipulator" (IDL Reference Guide) for details on the methods and properties available to classes that subclass from IDLitManipulator.
Hiding Compilation Messages
When IDL compiles an object class, it prints a compilation message similar to the following to the IDL Console:
To prevent the compilation message from appearing when the class is compiled, add the following line to the class structure definition:
Example Class Structure Definition
The following is the class structure definition for the ExampleManip operation class. This procedure should be the last procedure in a file named examplemanip__define.pro.
; Class Definition. PRO ExampleManip__define COMPILE_OPT hidden ; Define the MyManipulator class structure, which inherits the ; IDLitManipulator class. struct = { ExampleManip, $ INHERITS IDLitManipulator, $ Superclass oImage: OBJ_NEW(), $ Target image } END
Discussion
The purpose of the structure definition routine is to define a named IDL structure with structure fields that will contain the manipulator object instance data. The structure name should be the same as the manipulator's class name — in this case, ExampleManip.
Like many iTool manipulators, ExampleManip is created as a subclass of the IDLitManipulator class. The ExampleManip manipulator class includes one instance data field that will contain a reference to the target image object being manipulated.
Note
This example is intended to demonstrate how simple it can be to create a new manipulator class definition. While the class definition for a manipulator class with significant extra functionality will likely define additional structure fields, and may inherit from other iTool classes, the basic principles are the same. See Example: Color Table Manipulator for a more complex class structure definition.
Creating a Manipulator Init Method
The manipulator class Init method handles any initialization required by the manipulator object, and should do the following:
The Manipulator Init Function
Begin by defining the argument and keyword list for your Init method. The argument and keyword list defines positional parameters (arguments) accepted by your method, defines any keywords that will be handled directly by your method, and specifies whether keywords not explicitly handled by your method will be passed through to other routines called by your method via IDL's keyword inheritance mechanism.
Note
Because iTool manipulators are invoked by the user's interactive choice of a toolbar item, they generally do not accept any keywords of their own.
The function signature of an Init method for a manipulator generally looks something like this:
where MyManipulator is the name of your manipulator class.
Note
Always use keyword inheritance (the _REF_EXTRA keyword) to pass keyword parameters through to any called routines. See Keyword Inheritance (Application Programming) for details on IDL's keyword inheritance mechanism.
Superclass Initialization
The manipulator class Init method should call the Init method of any required superclass. For example, if your manipulator class is based on an existing manipulator, you would call that manipulator's Init method:
where SomeManipulatorClass is the class definition file for the manipulator on which your new manipulator is based. The variable success contains a 1 if the initialization was successful.
Note
Your manipulator class may have multiple superclasses. In general, each superclass' Init method should be invoked by your class' Init method.
Error Checking
Rather than simply calling the superclass Init method, it is a good idea to check whether the call to the superclass Init method succeeded. The following statement checks the value returned by the superclass Init method. If the returned value is 0 (indicating failure), the current Init method also immediately returns with a value of 0:
This convention is used in all manipulator classes included with IDL. We strongly suggest that you include similar checks in your own class definition files.
Keywords to the Init Method
Properties of the manipulator class can be set in the Init method by specifying the property names and values as IDL keyword-value pairs. In addition to any keywords implemented directly in the Init method of the superclass on which you base your class, the properties of the IDLitManipulator class, the IDLitIMessaging class, and the IDLitComponent class are available to any manipulator class. See IDLitManipulator Properties, IDLitIMessaging Properties, and "IDLitComponent Properties" (IDL Reference Guide).
Note
Always use keyword inheritance (the _EXTRA keyword) to pass keyword parameters through to the superclass. (See Keyword Inheritance (Application Programming) for details on IDL's keyword inheritance mechanism.)
Standard Base Class
While you can create your new manipulator class from any existing manipulator class, the manipulator classes you create will usually be subclassed directly from the base class, IDLitManipulator:
The IDLitManipulator class provides the base iTool functionality used in the manipulator classes created by ITT Visual Information Solutions. See Subclassing From the IDLitManipulator Class for details.
Return Value
If all of the routines and methods used in the Init method execute successfully, it should indicate successful initialization by returning 1. Other manipulator classes that subclass from your manipulator class may check this return value, as your routine should check the value returned by any superclass Init methods called.
Example Init Method
The following example code shows a very simple Init method for a manipulator named ExampleManip. This function would be included (along with the class structure definition routine and any other methods defined by the class) in a file named examplemanip__define.pro.
FUNCTION ExampleManip::Init, _REF_EXTRA = _extra ; Initialize the superclass. IF (self->IDLitManipulator::Init(TYPES=['IDLIMAGE'], $ NAME='Sample Manipulator', TRANSIENT_DEFAULT=1, $ OPERATION_IDENTIFIER='SET_PROPERTY', $ PARAMETER_IDENTIFIER='ALPHA_CHANNEL', $ _EXTRA = _extra) NE 1) THEN $ RETURN, 0 ; Call a custom method that registers a cursor for this ; manipulator. self->DoRegisterCursor ; Indicate success. RETURN, 1 END
Discussion
The ExampleManip class is based on the IDLitManipulator class (discussed in Subclassing From the IDLitManipulator Class). As a result, all of the standard features of an iTool manipulator are already present. We don't define any keyword values to be handled explicitly in the Init method, but we do use the keyword inheritance mechanism to pass keyword values through to methods called within the Init method. The ExampleManip Init method does the following things:
- Calls the Init method of the superclass, IDLitManipulator. Init method keywords are specified as follows:
- The TYPES keyword indicates the manipulator works on data that has the iTool data type of
IDLIMAGE. Allowable values for the TYPES keyword are those types returned by the GetTypes method of IDLitVisualization. See "IDLitVisualization::GetTypes" (IDL Reference Guide) for details. - The NAME keyword identifies the manipulator. If the IDENTIFIER keyword is not set, the manipulator's identifier is created from the name.
- The TRANSIENT_DEFAULT keyword indicates that this manipulator is transient, and that the default manipulator should be automatically started when this manipulator finishes (on mouse up).
- If the manipulator is to support undo/redo functionality, you must specify an operation associated with the manipulator as the OPERATION_IDENTIFIER keyword value. If the manipulator modifies a property of an object, set the OPERATION_IDENTIFIER equal to
'SET_PROPERTY', and the PROPERTY_IDENTIFIER keyword equal to the parameter identifier of the property. This example manipulator changes the opacity (ALPHA_CHANNEL) of an image. See Manipulators and the Undo/Redo System for more information. - The _EXTRA keyword inheritance mechanism passes through any keywords provided when the
ExampleManipInit method is called. - Calls a method, DoRegisterCursor, that creates a cursor for this manipulator using the IDLitManipulator::RegisterCursor method. See Creating a RegisterCursor Method for more information. If you prefer, you can use one of the predefined cursors instead of a custom cursor by setting the DEFAULT_CURSOR property. See the IDLitManipulator property "DEFAULT_CURSOR" (IDL Reference Guide) for a list of predefined cursors. When the mouse cursor is over a visualization of the appropriate type (as defined by the TYPE property), the manipulator cursor is shown.
- Returns the integer 1, indicating successful initialization.
Note
You can also examine the IDLitVis* classes in the lib/itools/framework subdirectory of the IDL installation directory. The TYPE defined during the IDLitVisualization initialization defines the visualization type. See Predefined iTool Visualization Classes for the visualization type of each visualization class.
The properties that support mouse and keyboard interaction are enabled by default. See "IDLitManipulator Properties" (IDL Reference Guide) for details.
Creating a Cleanup Method
The manipulator class Cleanup method handles any cleanup required by the manipulator object, and should do the following:
Calling the superclass' cleanup method will destroy any objects created when the superclass was initialized.
Note
If your manipulator class is based on the IDLitManipulator class, and does not create any pointers or objects of its own, the Cleanup method is not strictly required. It is always safest, however, to create a Cleanup method that calls the superclass' Cleanup method.
See "IDLitManipulator::Cleanup" (IDL Reference Guide) for additional details.
Example Cleanup Method
The following example code shows a very simple Cleanup method for the ExampleManip manipulator:
Discussion
Since our manipulator's instance data does not include any pointers or object references, the Cleanup method simply calls the superclass Cleanup method.
Creating Mouse Event Methods
Manipulators based on the IDLitManipulator class have the ability to respond to mouse events generated by the user. The OnMouseDown, OnMouseMotion, and OnMouseUp methods are invoked in response to mouse events in the iTool window. The functionality of an interactive manipulator can be divided among these events.
Implementing an OnMouseDown Method
The manipulator class OnMouseDown method is called when a mouse down event occurs on the target window. Calling the superclass IDLitManipulator::OnMouseDown method selects items at the mouse location and fills in the values of the ButtonPress, nSelectionList and pSelectionList instance data fields. See Using Manipulator Public Instance Data for more information on these fields. The x, y window coordinates of the cursor, which button is depressed when the mouse button is clicked, and related information are also provided through method parameters. Details on these method parameter values are provided in "IDLitManipulator::OnMouseDown" (IDL Reference Guide).
The actual processing performed by the OnMouseDown method depends entirely on the manipulator. If the manipulator action does not rely on mouse movements, the majority of your processing may occur in the OnMouseDown method. Regardless, you can use this method to determine if user selections meet requirements, or to set up initial values required for manipulator actions. If your manipulator calls a custom operation or the SET_PROPERTY operation, and you want to enable undo/redo support, call the RecordUndoValues method in the OnMouseDown method to record the initial values. See Manipulators and the Undo/Redo System for more information.
Example OnMouseDown Method
The following example code shows a simple OnMouseDown method for the ExampleManip manipulator. All this method does is set class structure fields.
PRO ExampleManip::OnMouseDown, oWin, x, y, iButton, $ KeyMods, nClicks ; Call our superclass. self->IDLitManipulator::OnMouseDown, $ oWin, x, y, iButton, KeyMods, nClicks ; Return if no visualization was selected. IF (self.nSelectionList EQ 0) THEN $ RETURN ; Access the first selected item and make sure it is an image. oImage = (*self.pSelectionList)[0] IF (OBJ_ISA(oImage,'IDLitVisImage')) THEN BEGIN ; Set the oImage field of the class structure to be ; the retrieved IDLitVisImage object. self.oImage = oImage ; Record the current values for the target objects. iStatus = self->RecordUndoValues() ENDIF END
Discussion
When the ExampleManip manipulator is activated and the user clicks in the iTool window, the OnMouseDown method calls the superclass (in order to update the public instance fields) and makes sure a visualization was selected. If the selected visualization is an image, store the image in the class structure field created when the ExampleManip class structure is defined. Call the RecordUndoValues method to support undo/redo functionality.
Implementing an OnMouseMotion Method
The manipulator class OnMouseMotion method is called when a mouse motion event occurs over the target window. This method provides access to the window object, the x, y window coordinates of the cursor, and which modifier key (if any) is depressed during mouse motion. The ButtonPress instance data field can be used to determine whether a button is pressed during mouse motion, or which button is pressed if this level of granularity is needed. See Using Manipulator Public Instance Data for details.
Example OnMouseMotion Method
The following example shows elements common in an interactive manipulator's OnMouseMotion method. For a complete working example, see Example: Color Table Manipulator.
; Configure mouse motion method. pro ExampleManip::OnMouseMotion, oWin, x, y, KeyMods ; If there is not a valid image, call superclass and return. IF (~OBJ_VALID(self.oImage)) THEN BEGIN ; Call our superclass. self->IDLitManipulator::OnMouseMotion, oWin, x, y, KeyMods RETURN ENDIF ; Activate if mouse button is held down. IF self.ButtonPress NE 0 THEN BEGIN ; Manipulate the visualization. ; ... ; Write manipulator information to the status bar ; using inherited IDLitIMessaging ProbeStatusMessage method. self->ProbeStatusMessage, 'Show user manipulator status' ; Update the window to reflect the changes made. oWin->Draw ENDIF ; Call our superclass. self->IDLitManipulator::OnMouseMotion, oWin, x, y, KeyMods END
Discussion
This OnMouseMotion method first verifies that there is a valid image, oImage, in the class structure field. If not, call the superclass and return. If the image is valid, make sure a mouse button is pressed during the mouse movement and modify the image in some fashion. The IDLitIMessaging class (a superclass of IDLitManipulator) provides access to the iTool status bar through the ProbeStatusMessage method. Write a simple message, and update the window, which can be accessed through the OnMouseMotion oWin parameter. Other available parameters include window coordinates of the cursor and modifier keys. See "IDLitManipulator::OnMouseMotion" (IDL Reference Guide) for details. Before exiting, call our superclass.
Implementing an OnMouseUp Method
The manipulator class OnMouseUp method is called when a mouse up event occurs over the target window. The method typically includes a call to the CommitUndoValues method to commit the user's changes during the mouse transaction. (This is only required to support undo/redo functionality. See Manipulators and the Undo/Redo System for details.)
Example OnMouseUp Method
This OnMouseUp method can be used to reset class structure fields and to close transactions.
; Configure the mouse up method PRO ExampleManip::OnMouseUp, oWin, x, y, iButton IF (OBJ_VALID(self.oImage)) THEN BEGIN ; Commit this transaction. iStatus = self->CommitUndoValues() ENDIF ; Reset the structure fields. self.oImage = OBJ_NEW() ; Call our superclass. self->IDLitManipulator::OnMouseUp, oWin, x, y, iButton END
Discussion
This example verifies that there is a valid image, oImage, in the class structure field. If so, call the CommitUndoValues, which in turn calls the RecordFinalValues method of the associated operation. Before exiting, call our superclass. This must be done to update the public instance data fields. Other available parameters include window coordinates of the cursor and mouse button information. See "IDLitManipulator::OnMouseUp" (IDL Reference Guide) for details.
Creating an OnWheel Method
Manipulators based on the IDLitManipulator class can respond to events generated by the scroll wheel on the user's mouse. The OnWheel method is invoked in response to wheel events in the iTool window.
If the manipulator supports undo/redo functionality, call RecordUndoValues prior to modifying the visualization in response to scroll wheel actions, and call CommitUndoValues prior to exiting the method. See Manipulators and the Undo/Redo System for details.
The parameters of the OnWheel method return information about the location of the mouse pointer when the scroll wheel is scrolled (X and Y), information about the direction and distance the wheel is scrolled (Delta), and information on any modifier keys the user held down while scrolling (Modifiers). See "IDLitManipulator::OnWheel" (IDL Reference Guide) for details.
Example OnWheel Method
The following example shows an OnWheel method that changes the zoom value of the current window. (Note that this behavior is the default when the Zoom manipulator is selected.)
PRO ExampleManip::OnWheel, oWin, X, Y, Delta, Modifiers ; Get the object reference to the current iTool. oTool = self->GetTool() IF (~OBJ_VALID(oTool)) THEN RETURN ; Get the object reference to the current view. oWin = oTool->GetCurrentWindow() oScene = OBJ_VALID(oWin) ? oWin->GetScene() : OBJ_NEW() oView = OBJ_VALID(oScene) ? oScene->GetCurrentView() : OBJ_NEW() IF (~OBJ_VALID(oView)) THEN RETURN ; Retrieve previous zoom factor. oView->GetProperty, CURRENT_ZOOM=zoom ; Increase or decrease the current zoom by a factor of ; 1.25, depending on which direction the scroll wheel was ; scrolled. zoomFactor = (delta GT 0) ? 1.25d : 1/1.25d zoom *= zoomFactor ; Perform the ViewZoom operation. zoom = STRTRIM(zoom*100, 2) + '%' void = oTool->DoAction('TOOLBAR/VIEW/VIEWZOOM', OPTION=zoom) END
Discussion
This OnWheel method simply increases or decreases the View Zoom when the mouse wheel is scrolled. The zoom is modified by the same factor (1.25) whenever a scroll wheel event is processed — the magnitude of the Delta parameter (indicating how far the wheel was scrolled) is ignored.
Creating an OnKeyboard Method
Once a manipulator has been started, and a mouse event has been registered in the iTool window, the OnKeyboard method can support additional user interaction through keyboard actions. The OnKeyboard event often includes execution logic from each of the mouse methods. For example, you will likely need to verify that a visualization has been selected (using the nSelectionList and pSelectionList instance data fields). If the visualization is the correct type, and the manipulator supports undo/redo functionality, call RecordUndoValues prior to modifying the visualization in response to keyboard actions, and call CommitUndoValues prior to exiting the method. See Manipulators and the Undo/Redo System for details.
The parameters of the OnKeyboard method return information about whether a key has been pressed (Press). If an ASCII character was selected (IsASCII), access the ASCII value (Character). If the key was not ASCII, you can return which symbol key was pressed (KeyValue). The OnKeyboard method also provides access to the window object (oWin), and the window coordinates of the cursor (x, y). See "IDLitManipulator::OnKeyboard" (IDL Reference Guide) for details.
Example OnKeyboard Method
The following example shows elements common to an OnKeyboard method, but not any specific manipulation of a visualization. See Example: Color Table Manipulator for a complete example.
; Configure the OnKeyboard method. pro ExampleManip::OnKeyboard, oWin, $ IsASCII, Character, KeyValue, X, Y, Press, Release, KeyMods ; If current event is not a key press, then return. IF (~Press) THEN $ RETURN ; Return if no visualization was selected. IF (self.nSelectionList EQ 0) THEN $ RETURN ; Access the first selected item and make sure it is an image. oImage = (*self.pSelectionList)[0] IF (OBJ_ISA(oImage,'IDLitVisImage')) THEN BEGIN ; Set the oImage field of the class structure to be ; the retrieved IDLitVisImage object. self.oImage = (*self.pSelectionList)[0] ENDIF ELSE BEGIN RETURN ENDELSE ; Record the current values for the selected images. iStatus = self->RecordUndoValues() ; *** Interact with the visualization based upon key press. ; ... ; Commit this transaction. iStatus = self->CommitUndoValues() ; Write information to the status bar ; using inherited IDLitIMessaging ProbeStatusMessage method. self->ProbeStatusMessage, 'Some manpulation information' ; Update the window to reflect the changes made. oWin->Draw END
Discussion
The OnKeyboard method will customarily contain portions of code from any implemented mouse transaction methods. In this example, if a button press event occurred, access the list of selected items and verify that the first item is an image. If so, call IDLitManipulator::RecordUndoValues, as was previously shown in the OnMouseDown method. Interact with the visualization as defined in an OnMouseDown or OnMouseMotion method. After making modifications, call CommitUndoValues to commit the transaction to the undo/redo buffer, previously shown in the OnMouseUp method. Use the IDLitIMessaging::ProbeStatusMessage method to write information to the status bar of the iTool and access the oWin parameter to update the window, as was previously shown in the OnMouseMotion method.
Creating a RegisterCursor Method
It is a useful visual indication to the user that a manipulator has been activated if the cursor changes. You can define a pre-existing cursor for a manipulator using the DEFAULT_CURSOR property during initialization as described in Example Init Method or using the SetProperty method. If none of the predefined cursors suit your needs, you can create a custom cursor by calling a method that includes the IDLitManpulator::RegisterCursor method. Call this method to register a custom cursor when the manipulator is initialized.
The RegisterCursor method accepts a 16-element string array of 16 characters each that defines the body, mask area, and hot spot of the cursor. See "IDLitManipulator::RegisterCursor" (IDL Reference Guide) for details. This lets you quickly configure a cursor without having to create and reference a separate bitmap file. The manipulator cursor is active when it is over a supported visualization type.
Note
You must set the DEFAULT keyword for a custom manipulator cursor when you use the RegisterCursor method to override the default system manipulator cursor.
Example DoRegisterCursor Method
The following example shows a custom cursor registration method, DoRegisterCursor, which implements the IDLitManipulator class RegisterCursor method to create a custom cursor. See Example: Color Table Manipulator for a complete example.
; Create and assign the default cursor for the manipulator. PRO ExampleManip::DoRegisterCursor ; Define the default cursor for this manipulator. strArray = [ $ ' ', $ ' ', $ ' ', $ ' ', $ ' ', $ ' .#. .#. ', $ ' .#..........#. ', $ '.##############.', $ '###....$.....###', $ '.##############.', $ ' .#..........#. ', $ ' .#. .#. ', $ ' ', $ ' ', $ ' ', $ ' '] ; Register the new cursor with the tool. self->RegisterCursor, strArray, 'LUT', /DEFAULT END
Discussion
This DoRegisterCursor method defines a 16-element string array of 16 characters each that represents the cursor. The strArray contains the following elements:
Pass the string array and cursor name (the Name argument value) to the RegisterCursor method. Set the DEFAULT keyword to indicate this is the default cursor for this manipulator.
Note
The Name argument specified here is the same as that returned by the GetCursorType method. See "IDLitManipulator::GetCursorType" (IDL Reference Guide) for more information.
Creating GetProperty or SetProperty Methods
The manipulator class GetProperty method retrieves property values from the manipulator object instance or from instance data of other associated objects. It should retrieve the requested property value, either from the manipulator object's instance data or by calling another class' GetProperty method. See "IDLitManipulator::GetProperty" (IDL Reference Guide) for additional details.
The manipulator class SetProperty method stores property values in the manipulator object's instance data or in properties of associated objects. It should set the specified property value, either by storing the value directly in the manipulator object's instance data or by calling another class' SetProperty method. See "IDLitManipulator::SetProperty" (IDL Reference Guide) for additional details.
Example GetProperty and SetProperty Methods
The following example code shows a very simple GetProperty method for the ExampleManip operation:
PRO ExampleManip::GetProperty, _REF_EXTRA = _extra ; Get superclass properties. IF (N_ELEMENTS(_extra) GT 0) THEN $ self->IDLitManipulator::GetProperty, _EXTRA = _extra END PRO ExampleManip::SetProperty, _REF_EXTRA = _extra IF (N_ELEMENTS(_extra) GT 0) THEN $ self->IDLitManipulator::SetProperty, _EXTRA = _extra END
Discussion
The GetProperty and SetProperty methods first define the keywords they will accept. There must be a keyword for each property of the manipulator type. The keyword inheritance mechanism allows properties to be retrieved from or set on the ExampleManip class' superclasses without knowing the names of the properties.
In this example, there are no properties specific to the ExampleManip object, so we simply use the N_ELEMENTS function to check whether the _extra structure contains any elements. If it does, we call the superclass' GetProperty and SetProperty methods, passing in all of the keywords stored in the _extra structure.