Creating a New Manipulator

The manipulator class definition file will have the following components:

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:

% Compiled module: EXAMPLEMANIP__DEFINE. 

To prevent the compilation message from appearing when the class is compiled, add the following line to the class structure definition:

COMPILE_OPT hidden 

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:

FUNCTION MyManipulator::Init, _REF_EXTRA = _extra 

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:

success = self->SomeManipulatorClass::Init(_EXTRA = _extra) 

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:

IF ( self->SomeManipulatorClass::Init(_EXTRA = _extra) EQ 0) THEN 
$ 
   RETURN, 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:

IF (self->IDLitManipulator::Init(_EXTRA = _extra) EQ 0) $ 
   THEN RETURN, 0 

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:

  1. 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.
    • 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 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 ExampleManip Init method is called.
  2. 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.
  3. Returns the integer 1, indicating successful initialization.

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:

PRO ExampleManip::Cleanup 
 
   ; Clean up superclass.  
   self->IDLitManipulator::Cleanup 
 
END 

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.