Writing New Accessor Classes
The Prototypes package contains many predefined accessor classes that allow you to define complex behaviors in your prototypes. You may, however, wish to implement specific behaviors for your particular needs. This section explains how you can extend the set of accessor classes you use to build your prototypes. It also explains how your new accessor classes are integrated into Views Studio.
To add a class of accessors, you simply have to write two classes:
*A subclass of IlvUserAccessor that defines the effect of your new accessor.
*A subclass of IlvAccessorDescriptor that defines the way your accessor will be edited in Views Studio.
The <ILVHOME>/samples directory of the Views distribution contains an example of a new accessor class (the gpacc.h and gpacc.cpp files). See the README file in that directory for more information.
Subclassing IlvUserAccessor
To define a new accessor class, you can either write a direct subclass of IlvUserAccessor or derive from an existing subclass that implements the features you want to extend. You may also want to make this class persistent.
Defining the Subclass
The declaration of a subclass of IlvUserAccessor will typically appear as follows:
class MyAccessor: public IlvUserAccessor {
public:
MyAccessor(const char* name,
const IlvValueTypeClass* type,
const char* param1,
const char* param2);
DeclareUserAccessorInfo();
DeclareUserAccessorIOConstructors(MyAccessor);
protected:
IlvSymbol* _param1;
IlvSymbol* _param1;
virtual IlBoolean changeValue(IlvAccessorHolder* object,
const IlvValue& val);
virtual IlvValue& queryValue(const IlvAccessorHolder* object,
IlvValue& val) const;
}
The following methods must be redefined to create a new accessor class:
*MyAccessor
MyAccessor(const char* name,
const IlvValueTypeClass* type,
const char* param1,
const char* param2);
This constructor is used to create an instance of your accessor by code. In Views Studio, only the input constructor will be used. The name parameter defines the name of the attribute that will be handled by the accessor and the type parameter defines the type of the attribute. Your constructor will probably have additional parameters, such as param1. These parameters are often character strings that correspond to the parameters that the user can input in Views Studio and that are evaluated at runtime.
*changeValue
virtual IlBoolean changeValue(IlvAccessorHolder* object,
const IlvValue& val);
The changeValue method is called when the attribute handled by the accessor is changed using a call to changeValue on the prototype or one of its instances. You use this method to define the effect of changing the value of your accessor. If your accessor uses parameters, you must evaluate these parameters. This can be done using the getValue method that evaluates a string containing either an immediate value or the name of another accessor.
The object parameter is the prototype or the prototype instance to which the accessor is attached. The val parameter contains the new value. The changeValue method must return IlTrue if the value was successfully changed, or IlFalse if an error occurred (for example, if one of the parameters could not be evaluated).
*queryValue
virtual IlvValue& queryValue(const IlvAccessorHolder* object,
IlvValue& val) const;
The queryValue method is called when the attribute handled by the accessor is retrieved using a call to queryValue on the prototype or one of its instances. This method must store the “current” value of the accessor in its val parameter (if doing so is appropriate). Some accessors store their current value, while others do not (for example, Condition accessors do not store their value). The current value is stored in the val parameter using the assignment operator of IlvValue. The method must return its val parameter.
*initialize
virtual void initialize(const IlvAccessorHolder* object);
The initialize method is called when the accessor object is associated with its prototype or prototype instance. You can redefine this method to perform any kind of initialization.
Making the IlvUserAccessor Subclass Persistent
Like graphic objects, accessor objects need to be persistent, which means they are saved to the prototype definition file and are read when the prototype is loaded. The persistence mechanism for accessor objects is very similar to the mechanism used for graphic objects.
First, in the .h file of your accessor class, you must call the following macros in the public section of the class declaration:
DeclareUserAccessorInfo();
DeclareUserAccessorIOConstructors(MyAccessor);
This automatically creates the Views runtime type information for your subclass and declares the persistence and copy methods.
In the .cpp file, you then have to write the following methods:
*MyAccessor(IlvDisplay* display, IlvGroupInputFile& f)
*MyAccessor::MyAccessor(const MyAccessor& source)
*MyAccessor::write(IlvGroupOututFile& f) const
This constructor reads the description of your accessor object from an input stream. The IlvGroupInputFile class is similar to IlvInputFile. Typically, you use only its getStream method. This returns a reference to an istream object from which you can read the description of your accessor object. However, the convenience method readValue can be used. The writeValue method puts quotation marks around strings containing spaces, and the readValue method checks for these quotation marks and reads the string correctly. Combined use of these methods avoids input/output errors. For example, the implementation of the method could be as follows:
MyAccessor::MyAccessor(IlvDisplay* display, IlvGroupInputFile& f)
: IlvUserAccessor(display, f)
{
_param1 = f.readValue();
_param2 = f.readValue();
}
Next, you have to write a copy constructor that will be called when the prototype is copied or when an instance of the prototype is created:
MyAccessor::MyAccessor(const MyAccessor& source)
: IlvUserAccessor(source)
{
_param1 = source._param1;
_param2 = source._param2;
}
The write method must be redefined to save the description of the accessor. The format used to save the parameters must match the format defined by the input constructor:
MyAccessor::write(IlvGroupOututFile& f) const
{
IlvUserAccessor::write(f);
f.writeValue(_param1); f << IlvSpc();
f.writeValue(_param2); f << endl;
}
Finally, the following macros must be called in the .cpp file:
IlvPredefinedUserAccessorIOMembers(MyAccessor)
IlvRegisterUserAccessorClass(MyAccessor,IlvUserAccessor);
Subclassing IlvAccessorDescriptor
Once you have written your subclass of IlvUserAccessor, you need to write another class, a subclass of IlvAccessorDescriptor. This class provides the information needed by the Group Inspector of Views Studio to edit the parameters of your accessor class.
The name of the IlvAccessorDescriptor subclass must match the name of the subclass of IlvUserAccessor. For example, if your accessor class is MyAccessor, the descriptor class must be called MyAccessorDescriptorClass.
You only need to declare the accessor descriptor class. An instance of it will be automatically created and associated with your user accessor subclass by the IlvRegisterUserAccessorClass macro.
Here is a typical example of a descriptor class:
class MyAccessorDescriptorClass :
public IlvAccessorDescriptor {
public:
MyAccessorDescriptorClass()
: IlvAccessorDescriptor("MyAccessor: an example",
Miscellaneous,
"example %s %s...",
IlFalse,
&IlvValueIntType,
0,
2,
"Parameter #1", &IlvValueParameterTypeString,
"Parameter #2", &IlvNodeNameParameterType) {}
};
The accessor descriptor class only requires a constructor with no arguments. It must call the IlvAccessorDescriptor constructor. For a detailed explanation of the parameters of this constructor, see the description of the IlvAccessorDescriptor class in the Views Prototypes Reference Manual.
Published date: 05/24/2022
Last modified date: 02/24/2022