Purpose Of Properties
Properties is a mechanism to interface information to and from a class in a specific manner. For instance a list of all properties on an object instance can be retrieved. (also derived properties). The normal way to get information is via member function. So why add another way to do more or less the same thing? Dynamically added (user) properties, and its data is one reason. Dynamic properties become very important to store information which is temporary. Like the editing of canvas objects requires extra data to be stored inside the canvas object to edit. But only during editing. So one does not want to extend the class to store all this extra information. But still it is needed. The solution is to store all that data into dynamic properties. After editing is done, all dynamic data is removed again. This keeps memory usage for canvas object low, while still being able to do many extra things on them. Users can use dynamic properties to store their own information in dynamic properties. All in all, the properties are essential to wxArt2D. In wxArt2D, properties are type save, and are named using Id's, which are unique. Only properties known to an object can be set to that object. But luckily one can dynamically add new propertyId's, which can be generated during run time, and use the new property Id's directly to set property values to those Id's. The property Id's on an object are stored as pointers in a static list. The property values are retrieved from a specific instance of an object using the property Id's. Dynamic property data ( data is not stored a field member ), is stored in a list containing a2dNamedProperty.
PropertyId's do have a string name set to it. This is used to save to formats that are ascii based. Still when loading one needs to check if a property id with that name exist on the object, and store the data using the found property id. Else the data can not be stored, since you will not know how. In short the property id is like a signature/declaration on how the property and its data needs to be set and retreived for an object.
Use Of Properties
wxArt2D has an extensive system for having properties on objects. The a2dObject, is the class which can hold dynamic properties in a list. The dynamic property is a a2dNamedProperty derived class, designed to hold a specific type of information. To name a view:
a2dBoolProperty => a2dPropertyIdBool
a2dStringProperty => a2dPropertyIdString
a2dVoidPtrProperty => a2dPropertyIdVoidPtr
a2dInt16Property => a2dPropertyIdInt16
a2dDoubleProperty => a2dPropertyIdDouble
a2dProperty => a2dPropertyIdRefObject
a2dArrayStringProperty => a2dPropertyIdArrayString
a2dDateTimeProperty => a2dPropertyIdDateTime
As you can see, any kind data from simple to complex types can be stored as a property. Important to understand is that each type of data, has a specific id class behind it. The a2dPropertyId holds information on the type of data, and how the property is stored. This can be as a dynamic property wrapping the property data into a a2dNamedProperty object, stored on a a2dObject, or it can directly call a member function or field. The functions to get the property value in case of a member function or field, are stored as pointers to those functions or members, again as part of the a2dPropertyId.
In a class, property id's are defined as static members, e.g.:
static a2dPropertyIdUint16* PROPID_Layer; static a2dPropertyIdBool* PROPID_Selected; static a2dPropertyIdCanvasObject* PROPID_Editcopy;
Each instance of this class can have the property set or not. How the property data is stored in the class, depends on how the property Id is defined. The property id static members or initialized to NULL, this is needed, since the are static. Later on in INITIALIZE_PROPERTIES the will be initialized properly.
a2dPropertyIdUint16* a2dCanvasObject::PROPID_Layer = NULL; a2dPropertyIdBool* a2dCanvasObject::PROPID_Selected = NULL; a2dPropertyIdCanvasObject* a2dCanvasObject::PROPID_Editcopy = NULL;
Next dynamic initialization includes information like its naming, and how the property value is retrieved and stored.
INITIALIZE_PROPERTIES( a2dCanvasObject, a2dObject ) { PROPID_Layer = new a2dPropertyIdUint16( wxT("Layer"), a2dPropertyId::flag_none, 0, a2dPropertyIdUint16::Get( &a2dCanvasObject::GetLayer ), a2dPropertyIdUint16::Set( &a2dCanvasObject::SetLayer ) ); AddPropertyId( PROPID_Layer ); PROPID_Selected = new a2dPropertyIdBool( wxT("Selected"), a2dPropertyId::flag_none, false, a2dPropertyIdBool::Get( &a2dCanvasObject::GetSelected ), a2dPropertyIdBool::Set( &a2dCanvasObject::SetSelected ) ); AddPropertyId( PROPID_Selected ); PROPID_Editcopy = new a2dPropertyIdCanvasObject( wxT("Editcopy"), a2dPropertyId::flag_temporary, 0 ); AddPropertyId( PROPID_Editcopy ); return true; }
The property data stored someway (dynamic,members,member function) as part of a class instance, can be set or retrieved using as a2dNamedProperty. You could say that the property its value, being a member function or field or a dynamic property, is wrapped into a a2dNamedProperty. This way of setting properties through a single interface has its advantages when making un-doable commands in an application. Instead of writing many commands for setting and getting the value of a property of a specific class, now only one is needed. The command holds the a2dNamedProperty for the value + id, and a pointer to the object from which it did get the property. All information needed to set/get the property again is available.
Another usage is that all properties on an object, no matter if they are dynamically stored in the propertylist of a a2dObject, or just a member field of the class, they can be retrieved as a list of a2dNamedProperty's. And as such a property dialog can use it to change the properties on an object and later store them again.
Properties are in general specific to one class and its derived classes. But what if you want to interface a value stored in one class to another unrelated class? You can define the same a2dPropertyId in one, also in the other class, the can share the same a2dPropertyId. The a2dPropertyId class is reference counted, and therefore deletion is automatic.
Dynamic User Property ID creation
As a developer or even as a user of your software, there is often the need to add properties which do not jet exist in wxArt2D. The strenght of having a2dPropertyId is that whatever user property you invent, it will be unique. If you try to use a property that already is available, it will be used. The code below demos how to achieve new property id's.
a2dCanvasObject* parent wxString name = wxT( "mySpecialUserProperty" ); wxString value = wxT("some value i need to store with parent canvasobject"); wxString type = wxT("string") ; //or other type as below if ( type == wxT("string") ) { a2dPropertyIdString* propid = (a2dPropertyIdString*) parent->HasPropertyId( name ); if ( !propid ) { propid = new a2dPropertyIdString( name, wxT(""), a2dPropertyId::flag_userDefined ); parent->AddPropertyId( propid ); } propid->SetPropertyToObject( parent, value ); } else if ( type == wxT("integer") ) { a2dPropertyIdInt32* propid = (a2dPropertyIdInt32*) parent->HasPropertyId( name ); if ( !propid ) { propid = new a2dPropertyIdInt32( name, 0, a2dPropertyId::flag_userDefined ); parent->AddPropertyId( propid ); } propid->SetPropertyToObject( parent, wxAtoi(value) ); } else if ( type == wxT("bool") ) { a2dPropertyIdBool* propid = (a2dPropertyIdBool*) parent->HasPropertyId( name ); if ( !propid ) { propid = new a2dPropertyIdBool( name, false, a2dPropertyId::flag_userDefined ); parent->AddPropertyId( propid ); } propid->SetPropertyToObject( parent, value.IsSameAs(wxT("true"), true )? true: false ); } else wxLogWarning(wxT("KEYIO : property type: %s is not implemented"), type.c_str() );