2020-02-02 18:29:33 +00:00
|
|
|
/*
|
|
|
|
* This program source code file is part of KiCad, a free EDA CAD application.
|
|
|
|
*
|
2023-11-11 15:29:34 +00:00
|
|
|
* Copyright (C) 2020-2023 CERN
|
2023-11-10 18:36:02 +00:00
|
|
|
* Copyright (C) 2021-2023 KiCad Developers, see AUTHORS.txt for contributors.
|
2021-01-25 12:42:36 +00:00
|
|
|
*
|
2020-02-02 18:29:33 +00:00
|
|
|
* @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
|
|
|
|
* @author Maciej Suminski <maciej.suminski@cern.ch>
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
* as published by the Free Software Foundation; either version 3
|
|
|
|
* of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License along
|
|
|
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifndef PROPERTY_MGR_H
|
|
|
|
#define PROPERTY_MGR_H
|
|
|
|
|
2021-03-20 15:35:37 +00:00
|
|
|
#include <core/wx_stl_compat.h> // Needed for stl hash extensions
|
|
|
|
|
2020-02-02 18:29:33 +00:00
|
|
|
#include <wx/string.h>
|
|
|
|
|
2022-12-08 12:27:30 +00:00
|
|
|
#include <functional>
|
2022-11-28 03:40:14 +00:00
|
|
|
#include <list>
|
2020-02-02 18:29:33 +00:00
|
|
|
#include <map>
|
|
|
|
#include <unordered_map>
|
2020-02-05 14:59:45 +00:00
|
|
|
#include <set>
|
2020-02-02 18:29:33 +00:00
|
|
|
#include <vector>
|
|
|
|
#include <memory>
|
2020-10-24 14:45:37 +00:00
|
|
|
#include <eda_units.h>
|
2020-02-02 18:29:33 +00:00
|
|
|
|
|
|
|
class PROPERTY_BASE;
|
|
|
|
class TYPE_CAST_BASE;
|
2022-10-31 01:09:59 +00:00
|
|
|
class ORIGIN_TRANSFORMS;
|
2022-12-08 05:09:39 +00:00
|
|
|
class INSPECTABLE;
|
2023-11-29 02:57:41 +00:00
|
|
|
class COMMIT;
|
2020-02-02 18:29:33 +00:00
|
|
|
|
2021-01-25 12:42:36 +00:00
|
|
|
///< Unique type identifier
|
2020-02-02 18:29:33 +00:00
|
|
|
using TYPE_ID = size_t;
|
|
|
|
|
|
|
|
using PROPERTY_LIST = std::vector<PROPERTY_BASE*>;
|
|
|
|
|
2020-02-05 14:59:45 +00:00
|
|
|
using PROPERTY_SET = std::set<std::pair<size_t, wxString>>;
|
|
|
|
|
2022-12-08 05:09:39 +00:00
|
|
|
template<typename ValueType>
|
|
|
|
using PROPERTY_MAP = std::map<std::pair<size_t, wxString>, ValueType>;
|
|
|
|
|
|
|
|
using PROPERTY_FUNCTOR_MAP = PROPERTY_MAP<std::function<bool( INSPECTABLE* )>>;
|
|
|
|
|
2022-11-28 03:40:14 +00:00
|
|
|
using PROPERTY_DISPLAY_ORDER = std::map<PROPERTY_BASE*, int>;
|
|
|
|
|
2023-11-29 02:57:41 +00:00
|
|
|
using PROPERTY_LISTENER = std::function<void( INSPECTABLE*, PROPERTY_BASE*, COMMIT* )>;
|
|
|
|
|
|
|
|
class PROPERTY_COMMIT_HANDLER
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
PROPERTY_COMMIT_HANDLER( COMMIT* aCommit );
|
|
|
|
|
|
|
|
~PROPERTY_COMMIT_HANDLER();
|
|
|
|
};
|
|
|
|
|
2020-02-02 18:29:33 +00:00
|
|
|
/**
|
2021-01-25 12:42:36 +00:00
|
|
|
* Provide class metadata. Each class handled by PROPERTY_MANAGER
|
2020-02-02 18:29:33 +00:00
|
|
|
* needs to be described using AddProperty(), AddTypeCast() and InheritsAfter() methods.
|
|
|
|
*
|
|
|
|
* Enum types use a dedicated property type (PROPERTY_ENUM), define its possible values
|
|
|
|
* with ENUM_MAP class, then describe the type using macros:
|
|
|
|
* - DECLARE_ENUM_TO_WXANY (in header files)
|
|
|
|
* - IMPLEMENT_ENUM_TO_WXANY (in source files)
|
|
|
|
* - ENUM_TO_WXANY (*most often used*; combines DECLARE and IMPLEMENT macros,
|
|
|
|
* if there is no need to share the description using header files)
|
|
|
|
*
|
|
|
|
* Once all classes are described, the property list must be build using
|
|
|
|
* Rebuild() method.
|
|
|
|
*/
|
|
|
|
class PROPERTY_MANAGER
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
static PROPERTY_MANAGER& Instance()
|
|
|
|
{
|
|
|
|
static PROPERTY_MANAGER pm;
|
|
|
|
return pm;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2021-01-25 12:42:36 +00:00
|
|
|
* Associate a name with a type.
|
2020-02-02 18:29:33 +00:00
|
|
|
*
|
2021-01-25 12:42:36 +00:00
|
|
|
* Build a map to provide faster type look-up.
|
2020-02-02 18:29:33 +00:00
|
|
|
*
|
|
|
|
* @param aType is the type identifier (obtained using TYPE_HASH()).
|
|
|
|
* @param aName is the type name.
|
|
|
|
*/
|
|
|
|
void RegisterType( TYPE_ID aType, const wxString& aName );
|
|
|
|
|
|
|
|
/**
|
2021-01-25 12:42:36 +00:00
|
|
|
* Return name of a type.
|
2020-02-02 18:29:33 +00:00
|
|
|
*
|
|
|
|
* @param aType is the type identifier (obtained using TYPE_HASH()).
|
|
|
|
* @return Name of the type or empty string, if not available.
|
|
|
|
*/
|
|
|
|
const wxString& ResolveType( TYPE_ID aType ) const;
|
|
|
|
|
|
|
|
/**
|
2021-01-25 12:42:36 +00:00
|
|
|
* Return a property for a specific type.
|
2020-02-02 18:29:33 +00:00
|
|
|
*
|
|
|
|
* @param aType is the type identifier (obtained using TYPE_HASH()).
|
|
|
|
* @param aProperty is the property name used during class registration.
|
|
|
|
* @return Requested property or null pointer if requested property does not exist.
|
|
|
|
*/
|
|
|
|
PROPERTY_BASE* GetProperty( TYPE_ID aType, const wxString& aProperty ) const;
|
|
|
|
|
|
|
|
/**
|
2021-01-25 12:42:36 +00:00
|
|
|
* Return all properties for a specific type.
|
2020-02-02 18:29:33 +00:00
|
|
|
*
|
|
|
|
* @param aType is the type identifier (obtained using TYPE_HASH()).
|
|
|
|
* @return Vector storing all properties of the requested type.
|
|
|
|
*/
|
|
|
|
const PROPERTY_LIST& GetProperties( TYPE_ID aType ) const;
|
|
|
|
|
2022-11-28 03:40:14 +00:00
|
|
|
const PROPERTY_DISPLAY_ORDER& GetDisplayOrder( TYPE_ID aType ) const;
|
|
|
|
|
|
|
|
const std::vector<wxString>& GetGroupDisplayOrder( TYPE_ID aType ) const;
|
|
|
|
|
2020-02-02 18:29:33 +00:00
|
|
|
/**
|
2021-01-25 12:42:36 +00:00
|
|
|
* Cast a type to another type. Used for correct type-casting of types with
|
2020-02-02 18:29:33 +00:00
|
|
|
* multi-inheritance. Requires registration of an appropriate converter (AddTypeCast).
|
|
|
|
*
|
|
|
|
* @param aSource is a pointer to the casted object.
|
|
|
|
* @param aBase is aSource type identifier (obtained using TYPE_HASH()).
|
|
|
|
* @param aTarget is the desired type identifier (obtained using TYPE_HASH()).
|
|
|
|
* @return Properly casted pointer of aTarget type. *
|
|
|
|
*
|
|
|
|
* @see AddTypeCast
|
|
|
|
*/
|
|
|
|
const void* TypeCast( const void* aSource, TYPE_ID aBase, TYPE_ID aTarget ) const;
|
|
|
|
|
|
|
|
void* TypeCast( void* aSource, TYPE_ID aBase, TYPE_ID aTarget ) const
|
|
|
|
{
|
|
|
|
return const_cast<void*>( TypeCast( (const void*) aSource, aBase, aTarget ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2021-01-25 12:42:36 +00:00
|
|
|
* Register a property.
|
2022-11-28 03:40:14 +00:00
|
|
|
* Properties for a given item will be shown in the order they are added.
|
|
|
|
* If a group name is supplied, the group will be created if it does not yet exists.
|
|
|
|
* Groups will likewise be shown in the order they are added (so, groups first added by a base
|
|
|
|
* class will appear before those of a child class).
|
2020-02-02 18:29:33 +00:00
|
|
|
*
|
|
|
|
* @param aProperty is the property to register.
|
2022-11-28 03:40:14 +00:00
|
|
|
* @param aGroup is an optional grouping key for the property
|
2020-02-02 18:29:33 +00:00
|
|
|
*/
|
2023-02-10 01:45:23 +00:00
|
|
|
PROPERTY_BASE& AddProperty( PROPERTY_BASE* aProperty, const wxString& aGroup = wxEmptyString );
|
2020-02-02 18:29:33 +00:00
|
|
|
|
2020-02-05 14:59:45 +00:00
|
|
|
/**
|
2021-01-25 12:42:36 +00:00
|
|
|
* Replace an existing property for a specific type.
|
2020-02-05 14:59:45 +00:00
|
|
|
*
|
|
|
|
* It is used to modify a property that has been inherited from a base class.
|
|
|
|
* This method is used instead of AddProperty().
|
|
|
|
*
|
|
|
|
* @param aBase is the base class type the delivers the original property.
|
|
|
|
* @param aName is the name of the replaced property.
|
|
|
|
* @param aNew is the property replacing the inherited one.
|
2022-11-28 03:40:14 +00:00
|
|
|
* @param aGroup is the group to set for the replaced property.
|
2020-02-05 14:59:45 +00:00
|
|
|
*/
|
2023-02-10 01:45:23 +00:00
|
|
|
PROPERTY_BASE& ReplaceProperty( size_t aBase, const wxString& aName, PROPERTY_BASE* aNew,
|
|
|
|
const wxString& aGroup = wxEmptyString );
|
2020-02-05 14:59:45 +00:00
|
|
|
|
2020-02-02 18:29:33 +00:00
|
|
|
/**
|
2021-01-25 12:42:36 +00:00
|
|
|
* Register a type converter. Required prior TypeCast() usage.
|
2020-02-02 18:29:33 +00:00
|
|
|
*
|
|
|
|
* @param aCast is the type converter to register.
|
|
|
|
*/
|
|
|
|
void AddTypeCast( TYPE_CAST_BASE* aCast );
|
|
|
|
|
|
|
|
/**
|
2021-01-25 12:42:36 +00:00
|
|
|
* Declare an inheritance relationship between types.
|
2020-02-02 18:29:33 +00:00
|
|
|
*
|
|
|
|
* @param aBase is the base type identifier (obtained using TYPE_HASH()).
|
|
|
|
* @param aDerived is the derived type identifier (obtained using TYPE_HASH()).
|
|
|
|
*/
|
|
|
|
void InheritsAfter( TYPE_ID aDerived, TYPE_ID aBase );
|
|
|
|
|
2022-12-02 02:54:12 +00:00
|
|
|
/**
|
|
|
|
* Sets a base class property as masked in a derived class. Masked properties are hidden from
|
|
|
|
* the list of editable properties for this class.
|
2022-12-08 05:09:39 +00:00
|
|
|
*
|
2022-12-02 02:54:12 +00:00
|
|
|
* @param aDerived is the type to apply the mask for.
|
|
|
|
* @param aBase is the type that aName belongs to.
|
2022-12-08 05:09:39 +00:00
|
|
|
* @param aName is the name of a property on the base class.
|
2022-12-02 02:54:12 +00:00
|
|
|
*/
|
|
|
|
void Mask( TYPE_ID aDerived, TYPE_ID aBase, const wxString& aName );
|
|
|
|
|
2022-12-08 05:09:39 +00:00
|
|
|
/**
|
|
|
|
* Sets an override availability functor for a base class property of a given derived class.
|
|
|
|
*
|
|
|
|
* @param aDerived is the type to apply the mask for.
|
|
|
|
* @param aBase is the type that aName belongs to.
|
|
|
|
* @param aName is the name of a property on the base class.
|
|
|
|
* @param aFunc is the new availability functor to apply.
|
|
|
|
*/
|
|
|
|
void OverrideAvailability( TYPE_ID aDerived, TYPE_ID aBase, const wxString& aName,
|
|
|
|
std::function<bool( INSPECTABLE* )> aFunc );
|
|
|
|
|
2023-07-12 15:02:41 +00:00
|
|
|
/**
|
|
|
|
* Sets an override writeability functor for a base class property of a given derived class.
|
|
|
|
*
|
|
|
|
* @param aDerived is the type to apply the mask for.
|
|
|
|
* @param aBase is the type that aName belongs to.
|
|
|
|
* @param aName is the name of a property on the base class.
|
|
|
|
* @param aFunc is the new availability functor to apply.
|
|
|
|
*/
|
|
|
|
void OverrideWriteability( TYPE_ID aDerived, TYPE_ID aBase, const wxString& aName,
|
|
|
|
std::function<bool( INSPECTABLE* )> aFunc );
|
|
|
|
|
2022-12-08 05:09:39 +00:00
|
|
|
/**
|
|
|
|
* Checks overriden availability and original availability of a property, returns false
|
|
|
|
* if the property is unavailable in either case.
|
|
|
|
*
|
|
|
|
* TODO: This isn't the cleanest API, consider how to merge with PROPERTY_BASE::Available
|
|
|
|
*/
|
|
|
|
bool IsAvailableFor( TYPE_ID aItemClass, PROPERTY_BASE* aProp, INSPECTABLE* aItem );
|
|
|
|
|
2023-07-12 15:02:41 +00:00
|
|
|
/**
|
|
|
|
* Checks overriden availability and original availability of a property, returns false
|
|
|
|
* if the property is unavailable in either case.
|
|
|
|
*
|
|
|
|
* TODO: This isn't the cleanest API, consider how to merge with PROPERTY_BASE::Writeable
|
|
|
|
*/
|
|
|
|
bool IsWriteableFor( TYPE_ID aItemClass, PROPERTY_BASE* aProp, INSPECTABLE* aItem );
|
|
|
|
|
2020-02-02 18:29:33 +00:00
|
|
|
/**
|
2021-01-25 12:42:36 +00:00
|
|
|
* Return true if aDerived is inherited from aBase.
|
2020-02-02 18:29:33 +00:00
|
|
|
*/
|
|
|
|
bool IsOfType( TYPE_ID aDerived, TYPE_ID aBase ) const;
|
|
|
|
|
|
|
|
/**
|
2021-01-25 12:42:36 +00:00
|
|
|
* Rebuild the list of all registered properties. Needs to be called
|
2020-02-02 18:29:33 +00:00
|
|
|
* once before GetProperty()/GetProperties() are used.
|
|
|
|
*/
|
|
|
|
void Rebuild();
|
|
|
|
|
2020-06-07 21:51:07 +00:00
|
|
|
struct CLASS_INFO
|
|
|
|
{
|
|
|
|
wxString name;
|
|
|
|
TYPE_ID type;
|
|
|
|
std::vector<PROPERTY_BASE*> properties;
|
|
|
|
};
|
|
|
|
|
|
|
|
typedef std::vector<CLASS_INFO> CLASSES_INFO;
|
|
|
|
|
|
|
|
CLASSES_INFO GetAllClasses();
|
|
|
|
|
|
|
|
std::vector<TYPE_ID> GetMatchingClasses( PROPERTY_BASE* aProperty );
|
|
|
|
|
2023-11-29 02:57:41 +00:00
|
|
|
/**
|
|
|
|
* Callback to alert the notification system that a property has changed
|
|
|
|
* @param aObject is the object whose property just changed
|
|
|
|
* @param aProperty is the property that changed
|
|
|
|
*/
|
|
|
|
void PropertyChanged( INSPECTABLE* aObject, PROPERTY_BASE* aProperty );
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Registers a listener for the given type
|
|
|
|
* @param aType is the type to add the listener for
|
|
|
|
* @param aListenerFunc will be called every time a property on aType is changed
|
|
|
|
*/
|
|
|
|
void RegisterListener( TYPE_ID aType, PROPERTY_LISTENER aListenerFunc )
|
|
|
|
{
|
|
|
|
m_listeners[aType].emplace_back( aListenerFunc );
|
|
|
|
}
|
|
|
|
|
2024-05-17 00:49:27 +00:00
|
|
|
void UnregisterListeners( TYPE_ID aType )
|
|
|
|
{
|
|
|
|
m_listeners[aType].clear();
|
|
|
|
}
|
|
|
|
|
2020-02-02 18:29:33 +00:00
|
|
|
private:
|
2022-10-31 01:09:59 +00:00
|
|
|
PROPERTY_MANAGER() :
|
2023-11-29 02:57:41 +00:00
|
|
|
m_dirty( false ),
|
|
|
|
m_managedCommit( nullptr )
|
2020-02-02 18:29:33 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2023-11-29 02:57:41 +00:00
|
|
|
friend class PROPERTY_COMMIT_HANDLER;
|
|
|
|
|
2021-01-25 12:42:36 +00:00
|
|
|
///< Structure holding type meta-data
|
2020-02-02 18:29:33 +00:00
|
|
|
struct CLASS_DESC
|
|
|
|
{
|
|
|
|
CLASS_DESC( TYPE_ID aId )
|
|
|
|
: m_id( aId )
|
|
|
|
{
|
2022-11-28 03:40:14 +00:00
|
|
|
m_groupDisplayOrder.emplace_back( wxEmptyString );
|
|
|
|
m_groups.insert( wxEmptyString );
|
2020-02-02 18:29:33 +00:00
|
|
|
}
|
|
|
|
|
2021-01-25 12:42:36 +00:00
|
|
|
///< Unique type identifier (obtained using TYPE_HASH)
|
2020-02-02 18:29:33 +00:00
|
|
|
const TYPE_ID m_id;
|
|
|
|
|
2021-01-25 12:42:36 +00:00
|
|
|
///< Types after which this type inherits
|
2020-02-02 18:29:33 +00:00
|
|
|
std::vector<std::reference_wrapper<CLASS_DESC>> m_bases;
|
|
|
|
|
2021-01-25 12:42:36 +00:00
|
|
|
///< Properties unique to this type (i.e. not inherited)
|
2020-02-02 18:29:33 +00:00
|
|
|
std::map<wxString, std::unique_ptr<PROPERTY_BASE>> m_ownProperties;
|
|
|
|
|
2021-01-25 12:42:36 +00:00
|
|
|
///< Type converters available for this type
|
2020-02-02 18:29:33 +00:00
|
|
|
std::map<TYPE_ID, std::unique_ptr<TYPE_CAST_BASE>> m_typeCasts;
|
|
|
|
|
2022-12-02 02:54:12 +00:00
|
|
|
///< Properties from bases that should be masked (hidden) on this subclass
|
|
|
|
PROPERTY_SET m_maskedBaseProperties;
|
|
|
|
|
2022-12-08 05:09:39 +00:00
|
|
|
///< Overrides for base class property availabilities
|
|
|
|
PROPERTY_FUNCTOR_MAP m_availabilityOverrides;
|
|
|
|
|
2023-07-12 15:02:41 +00:00
|
|
|
///< Overrides for base class property writeable status
|
|
|
|
PROPERTY_FUNCTOR_MAP m_writeabilityOverrides;
|
|
|
|
|
2021-01-25 12:42:36 +00:00
|
|
|
///< All properties (both unique to the type and inherited)
|
2020-02-02 18:29:33 +00:00
|
|
|
std::vector<PROPERTY_BASE*> m_allProperties;
|
|
|
|
|
2022-12-04 22:20:34 +00:00
|
|
|
///< Compiled display order for all properties
|
2022-11-28 03:40:14 +00:00
|
|
|
PROPERTY_DISPLAY_ORDER m_displayOrder;
|
|
|
|
|
2022-12-04 22:20:34 +00:00
|
|
|
///< List of property groups provided by this class in display order
|
2022-11-28 03:40:14 +00:00
|
|
|
std::vector<wxString> m_groupDisplayOrder;
|
|
|
|
|
2022-12-04 22:20:34 +00:00
|
|
|
///< Non-owning list of classes's direct properties in display order
|
|
|
|
std::vector<PROPERTY_BASE*> m_ownDisplayOrder;
|
|
|
|
|
|
|
|
///< The property groups provided by this class
|
2022-11-28 03:40:14 +00:00
|
|
|
std::set<wxString> m_groups;
|
2022-11-25 21:29:56 +00:00
|
|
|
|
2021-01-25 12:42:36 +00:00
|
|
|
///< Replaced properties (TYPE_ID / name)
|
2020-02-05 14:59:45 +00:00
|
|
|
PROPERTY_SET m_replaced;
|
|
|
|
|
2021-01-25 12:42:36 +00:00
|
|
|
///< Recreates the list of properties
|
2020-02-02 18:29:33 +00:00
|
|
|
void rebuild();
|
|
|
|
|
2021-01-25 12:42:36 +00:00
|
|
|
///< Traverses the class inheritance hierarchy bottom-to-top, gathering
|
|
|
|
///< all properties available to a type
|
2022-11-28 03:40:14 +00:00
|
|
|
void collectPropsRecur( PROPERTY_LIST& aResult, PROPERTY_SET& aReplaced,
|
2022-12-02 02:54:12 +00:00
|
|
|
PROPERTY_DISPLAY_ORDER& aDisplayOrder,
|
2023-06-21 01:57:20 +00:00
|
|
|
PROPERTY_SET& aMasked ) const;
|
2020-02-02 18:29:33 +00:00
|
|
|
};
|
|
|
|
|
2021-01-25 12:42:36 +00:00
|
|
|
///< Returns metadata for a specific type
|
2020-02-02 18:29:33 +00:00
|
|
|
CLASS_DESC& getClass( TYPE_ID aTypeId );
|
|
|
|
|
|
|
|
std::unordered_map<TYPE_ID, wxString> m_classNames;
|
|
|
|
|
2021-01-25 12:42:36 +00:00
|
|
|
///< Map of all available types
|
2020-02-02 18:29:33 +00:00
|
|
|
std::unordered_map<TYPE_ID, CLASS_DESC> m_classes;
|
|
|
|
|
|
|
|
/// Flag indicating that the list of properties needs to be rebuild (RebuildProperties())
|
|
|
|
bool m_dirty;
|
2023-11-29 02:57:41 +00:00
|
|
|
|
|
|
|
std::map<TYPE_ID, std::vector<PROPERTY_LISTENER>> m_listeners;
|
|
|
|
|
|
|
|
COMMIT* m_managedCommit;
|
2020-02-02 18:29:33 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
2021-01-25 12:42:36 +00:00
|
|
|
///< Helper macro to map type hashes to names
|
2020-02-02 18:29:33 +00:00
|
|
|
#define REGISTER_TYPE(x) PROPERTY_MANAGER::Instance().RegisterType(TYPE_HASH(x), TYPE_NAME(x))
|
|
|
|
|
|
|
|
#endif /* PROPERTY_MGR_H */
|