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 Rogue Wave® 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 Rogue Wave Views Studio.
The <ILVHOME>/samples directory of the Rogue Wave 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 Rogue Wave 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 Rogue Wave 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 Rogue Wave 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 Rogue Wave 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.
Version 6.0
Copyright © 2015, Rogue Wave Software, Inc. All Rights Reserved.