diff --git a/src/App/Application.cpp b/src/App/Application.cpp index 8e89022a06..fdbac649a5 100644 --- a/src/App/Application.cpp +++ b/src/App/Application.cpp @@ -154,8 +154,8 @@ namespace sp = std::placeholders; // Application //========================================================================== -ParameterManager *App::Application::_pcSysParamMngr; -ParameterManager *App::Application::_pcUserParamMngr; +Base::Reference App::Application::_pcSysParamMngr; +Base::Reference App::Application::_pcUserParamMngr; Base::ConsoleObserverStd *Application::_pConsoleObserverStd = nullptr; Base::ConsoleObserverFile *Application::_pConsoleObserverFile = nullptr; @@ -1215,21 +1215,22 @@ ParameterManager & Application::GetUserParameter() ParameterManager * Application::GetParameterSet(const char* sName) const { - std::map::const_iterator it = mpcPramManager.find(sName); + auto it = mpcPramManager.find(sName); if ( it != mpcPramManager.end() ) return it->second; else return nullptr; } -const std::map & Application::GetParameterSetList() const +const std::map> & +Application::GetParameterSetList(void) const { return mpcPramManager; } void Application::AddParameterSet(const char* sName) { - std::map::const_iterator it = mpcPramManager.find(sName); + auto it = mpcPramManager.find(sName); if ( it != mpcPramManager.end() ) return; mpcPramManager[sName] = new ParameterManager(); @@ -1237,11 +1238,10 @@ void Application::AddParameterSet(const char* sName) void Application::RemoveParameterSet(const char* sName) { - std::map::iterator it = mpcPramManager.find(sName); + auto it = mpcPramManager.find(sName); // Must not delete user or system parameter if ( it == mpcPramManager.end() || it->second == _pcUserParamMngr || it->second == _pcSysParamMngr ) return; - delete it->second; mpcPramManager.erase(it); } @@ -1260,7 +1260,7 @@ Base::Reference Application::GetParameterGroupByPath(const char* cName.erase(0,pos+1); // test if name is valid - std::map::iterator It = mpcPramManager.find(cTemp.c_str()); + auto It = mpcPramManager.find(cTemp.c_str()); if (It == mpcPramManager.end()) throw Base::ValueError("Application::GetParameterGroupByPath() unknown parameter set name specified"); @@ -1651,8 +1651,8 @@ void Application::destruct() Base::Console().Log("Saving user parameter...done\n"); // now save all other parameter files - std::map& paramMgr = _pcSingleton->mpcPramManager; - for (std::map::iterator it = paramMgr.begin(); it != paramMgr.end(); ++it) { + auto& paramMgr = _pcSingleton->mpcPramManager; + for (auto it = paramMgr.begin(); it != paramMgr.end(); ++it) { if ((it->second != _pcSysParamMngr) && (it->second != _pcUserParamMngr)) { if (it->second->HasSerializer()) { Base::Console().Log("Saving %s...\n", it->first.c_str()); @@ -1660,9 +1660,6 @@ void Application::destruct() Base::Console().Log("Saving %s...done\n", it->first.c_str()); } } - - // clean up - delete it->second; } paramMgr.clear(); diff --git a/src/App/Application.h b/src/App/Application.h index 1cd0fcf2e2..a1616bad1a 100644 --- a/src/App/Application.h +++ b/src/App/Application.h @@ -332,7 +332,7 @@ public: Base::Reference GetParameterGroupByPath(const char* sName); ParameterManager * GetParameterSet(const char* sName) const; - const std::map & GetParameterSetList() const; + const std::map> & GetParameterSetList(void) const; void AddParameterSet(const char* sName); void RemoveParameterSet(const char* sName); //@} @@ -503,8 +503,8 @@ private: /** @name member for parameter */ //@{ - static ParameterManager *_pcSysParamMngr; - static ParameterManager *_pcUserParamMngr; + static Base::Reference _pcSysParamMngr; + static Base::Reference _pcUserParamMngr; //@} //--------------------------------------------------------------------- @@ -605,7 +605,7 @@ private: std::vector _mExportTypes; std::map DocMap; mutable std::map DocFileMap; - std::map mpcPramManager; + std::map> mpcPramManager; std::map &_mConfig; App::Document* _pActiveDoc; diff --git a/src/Base/Handle.cpp b/src/Base/Handle.cpp index c02189b83f..a74f76c376 100644 --- a/src/Base/Handle.cpp +++ b/src/Base/Handle.cpp @@ -64,6 +64,13 @@ void Handled::unref() const } } +int Handled::unrefNoDelete() const +{ + int res = _lRefCount->deref(); + assert(res>=0); + return res; +} + int Handled::getRefCount() const { return static_cast(*_lRefCount); diff --git a/src/Base/Handle.h b/src/Base/Handle.h index c3e05ffb80..0816719462 100644 --- a/src/Base/Handle.h +++ b/src/Base/Handle.h @@ -115,21 +115,6 @@ public: return _toHandle; } - /** Lower operator, needed for sorting in maps and sets */ - bool operator<(const Reference& p) const { - return _toHandle < p._toHandle; - } - - /** Equal operator */ - bool operator==(const Reference& p) const { - return _toHandle == p._toHandle; - } - - bool operator!=(const Reference& p) const { - return _toHandle != p._toHandle; - } - - //************************************************************************** // checking on the state @@ -165,6 +150,7 @@ public: void ref() const; void unref() const; + int unrefNoDelete() const; int getRefCount() const; const Handled& operator = (const Handled&); diff --git a/src/Base/Parameter.cpp b/src/Base/Parameter.cpp index e96793a276..75a23dc648 100644 --- a/src/Base/Parameter.cpp +++ b/src/Base/Parameter.cpp @@ -44,11 +44,15 @@ # include #endif +#include + #include "Parameter.h" #include "Parameter.inl" #include "Console.h" #include "Exception.h" +#include "Tools.h" +FC_LOG_LEVEL_INIT("Parameter", true, true) //#ifdef XERCES_HAS_CPP_NAMESPACE // using namespace xercesc; @@ -173,25 +177,41 @@ inline bool DOMTreeErrorReporter::getSawErrors() const /** Default construction */ -ParameterGrp::ParameterGrp(XERCES_CPP_NAMESPACE_QUALIFIER DOMElement *GroupNode,const char* sName) - : Base::Handled(), Subject(),_pGroupNode(GroupNode) +ParameterGrp::ParameterGrp(XERCES_CPP_NAMESPACE_QUALIFIER DOMElement *GroupNode, + const char* sName, + ParameterGrp *Parent) + : Base::Handled(), Subject() + , _pGroupNode(GroupNode) + , _Parent(Parent) { if (sName) _cName=sName; + if (_Parent) _Manager = _Parent->_Manager; } /** Destruction * complete destruction of the object */ -ParameterGrp::~ParameterGrp() = default; +ParameterGrp::~ParameterGrp() +{ + for (auto &v : _GroupMap) { + v.second->_Parent = nullptr; + v.second->_Manager = nullptr; + } + if (_Detached && _pGroupNode) + _pGroupNode->release(); +} //************************************************************************** // Access methods void ParameterGrp::copyTo(Base::Reference Grp) { + if (Grp == this) + return; + // delete previous content - Grp->Clear(); + Grp->Clear(true); // copy all insertTo(Grp); @@ -199,6 +219,9 @@ void ParameterGrp::copyTo(Base::Reference Grp) void ParameterGrp::insertTo(Base::Reference Grp) { + if (Grp == this) + return; + // copy group std::vector > Grps = GetGroups(); std::vector >::iterator It1; @@ -242,10 +265,13 @@ void ParameterGrp::exportTo(const char* FileName) Mngr.CreateDocument(); + Mngr.ref(); + // copy all into the new document - insertTo(Mngr.GetGroup("BaseApp")); + insertTo(&Mngr); Mngr.SaveDocument(FileName); + Mngr.unrefNoDelete(); } void ParameterGrp::importFrom(const char* FileName) @@ -255,7 +281,9 @@ void ParameterGrp::importFrom(const char* FileName) if (Mngr.LoadDocument(FileName) != 1) throw FileException("ParameterGrp::import() cannot load document", FileName); - Mngr.GetGroup("BaseApp")->copyTo(Base::Reference(this)); + ref(); + Mngr.copyTo(Base::Reference(this)); + unrefNoDelete(); } void ParameterGrp::insert(const char* FileName) @@ -265,69 +293,169 @@ void ParameterGrp::insert(const char* FileName) if (Mngr.LoadDocument(FileName) != 1) throw FileException("ParameterGrp::import() cannot load document", FileName); - Mngr.GetGroup("root")->insertTo(Base::Reference(this)); + ref(); + Mngr.insertTo(Base::Reference(this)); + unrefNoDelete(); +} + +void ParameterGrp::revert(const char* FileName) +{ + ParameterManager Mngr; + + if (Mngr.LoadDocument(FileName) != 1) + throw FileException("ParameterGrp::revert() cannot load document", FileName); + + Mngr.ref(); + revert(Base::Reference(&Mngr)); + Mngr.unrefNoDelete(); +} + +void ParameterGrp::revert(Base::Reference Grp) +{ + if (Grp == this) + return; + + for (auto &grp : Grp->GetGroups()) { + if (HasGroup(grp->GetGroupName())) + GetGroup(grp->GetGroupName())->revert(grp); + } + + for (const auto &v : Grp->GetASCIIMap()) { + if (GetASCII(v.first.c_str(), v.second.c_str()) == v.second) + RemoveASCII(v.first.c_str()); + } + + for (const auto &v : Grp->GetBoolMap()) { + if (GetBool(v.first.c_str(), v.second) == v.second) + RemoveBool(v.first.c_str()); + } + + for (const auto &v : Grp->GetIntMap()) { + if (GetInt(v.first.c_str(), v.second) == v.second) + RemoveInt(v.first.c_str()); + } + + for (const auto &v : Grp->GetUnsignedMap()) { + if (GetUnsigned(v.first.c_str(), v.second) == v.second) + RemoveUnsigned(v.first.c_str()); + } + + for (const auto &v : Grp->GetFloatMap()) { + if (GetFloat(v.first.c_str(), v.second) == v.second) + RemoveFloat(v.first.c_str()); + } +} + +XERCES_CPP_NAMESPACE_QUALIFIER DOMElement * +ParameterGrp::CreateElement(XERCES_CPP_NAMESPACE_QUALIFIER DOMElement *Start, const char* Type, const char* Name) +{ + if (XMLString::compareString(Start->getNodeName(), XStr("FCParamGroup").unicodeForm()) != 0 && + XMLString::compareString(Start->getNodeName(), XStr("FCParameters").unicodeForm()) != 0) { + Base::Console().Warning("CreateElement: %s cannot have the element %s of type %s\n", StrX(Start->getNodeName()).c_str(), Name, Type); + return nullptr; + } + + if (_Detached && _Parent) { + // re-attach the group + _Parent->_GetGroup(_cName.c_str()); + } + + XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument *pDocument = Start->getOwnerDocument(); + + auto pcElem = pDocument->createElement(XStr(Type).unicodeForm()); + pcElem-> setAttribute(XStr("Name").unicodeForm(), XStr(Name).unicodeForm()); + Start->appendChild(pcElem); + + return pcElem; } Base::Reference ParameterGrp::GetGroup(const char* Name) { - std::string cName = Name; - if (cName.empty()) + if (!Name) throw Base::ValueError("Empty group name"); - // Remove all leading slashes - std::string::size_type beg = cName.find_first_not_of('/'); - if (beg > 0) { - cName.erase(0, beg); - } - - // Remove all trailing slashes - std::string::size_type end = cName.find_last_not_of('/'); - if (end+1 < cName.size()) { - cName.erase(end+1); - } - - std::string::size_type pos = cName.find('/'); - - // is there a path separator ? - if (pos == std::string::npos) { - return _GetGroup(cName.c_str()); - } - else { - // path, split the first path - std::string cTemp; - // getting the first part - cTemp.assign(cName, 0, pos); - // removing the first part from the original - cName.erase(0, pos+1); - //subsequent call - return _GetGroup(cTemp.c_str())->GetGroup(cName.c_str()); + Base::Reference hGrp = this; + std::vector tokens; + boost::split(tokens, Name, boost::is_any_of("/")); + for (auto &token : tokens) { + boost::trim(token); + if (token.empty()) + continue; + hGrp = hGrp->_GetGroup(token.c_str()); + if (!hGrp) { + // The group is clearing. Return some dummy group to avoid caller + // crashing for backward compatibility. + hGrp = new ParameterGrp(); + hGrp->_cName = Name; + break; + } } + if (hGrp == this) + throw Base::ValueError("Empty group name"); + return hGrp; } Base::Reference ParameterGrp::_GetGroup(const char* Name) { Base::Reference rParamGrp; - - // already created? - if ((rParamGrp=_GroupMap[Name]).isValid()) { - // just return the already existing Group handle + if (!_pGroupNode) { + if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) + FC_WARN("Adding group " << Name << " in an orphan group " << _cName); + return rParamGrp; + } + if (_Clearing) { + if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) + FC_WARN("Adding group " << Name << " while clearing " << GetPath()); return rParamGrp; } - // search if Group node already there - DOMElement *pcTemp = FindOrCreateElement(_pGroupNode,"FCParamGroup",Name); + DOMElement *pcTemp; - // create and register handle - rParamGrp = Base::Reference (new ParameterGrp(pcTemp,Name)); - _GroupMap[Name] = rParamGrp; + // search if Group node already there + pcTemp = FindElement(_pGroupNode,"FCParamGroup",Name); + + // already created? + if (!(rParamGrp=_GroupMap[Name]).isValid()) { + if (!pcTemp) + pcTemp = CreateElement(_pGroupNode,"FCParamGroup",Name); + // create and register handle + rParamGrp = Base::Reference (new ParameterGrp(pcTemp,Name,this)); + _GroupMap[Name] = rParamGrp; + } else if (!pcTemp) { + _pGroupNode->appendChild(rParamGrp->_pGroupNode); + rParamGrp->_Detached = false; + if (this->_Detached && this->_Parent) { + // Re-attach the group. Note that this may fail if the parent is + // clearing. That's why we check this->_Detached below. + this->_Parent->_GetGroup(_cName.c_str()); + } + } + + if (!pcTemp && !this->_Detached) + _Notify(ParamType::FCGroup, Name, Name); return rParamGrp; } +std::string ParameterGrp::GetPath() const +{ + std::string path; + if (_Parent && _Parent != _Manager) + path = _Parent->GetPath(); + if (path.size() && _cName.size()) + path += "/"; + path += _cName; + return path; +} + std::vector > ParameterGrp::GetGroups() { Base::Reference rParamGrp; std::vector > vrParamGrp; + + if (!_pGroupNode) + return vrParamGrp; + std::string Name; DOMElement *pcTemp = FindElement(_pGroupNode,"FCParamGroup"); @@ -335,7 +463,7 @@ std::vector > ParameterGrp::GetGroups() Name = StrX(pcTemp->getAttributes()->getNamedItem(XStr("Name").unicodeForm())->getNodeValue()).c_str(); // already created? if (!(rParamGrp=_GroupMap[Name]).isValid()) { - rParamGrp = Base::Reference (new ParameterGrp(pcTemp,Name.c_str())); + rParamGrp = Base::Reference (new ParameterGrp(pcTemp,Name.c_str(),this)); _GroupMap[Name] = rParamGrp; } vrParamGrp.push_back( rParamGrp ); @@ -349,7 +477,7 @@ std::vector > ParameterGrp::GetGroups() /// test if this group is empty bool ParameterGrp::IsEmpty() const { - if (_pGroupNode->getFirstChild()) + if ( _pGroupNode && _pGroupNode->getFirstChild() ) return false; else return true; @@ -361,14 +489,189 @@ bool ParameterGrp::HasGroup(const char* Name) const if (_GroupMap.find(Name) != _GroupMap.end()) return true; - if (FindElement(_pGroupNode,"FCParamGroup",Name)) + if (_pGroupNode && FindElement(_pGroupNode,"FCParamGroup",Name) != 0) return true; return false; } +const char *ParameterGrp::TypeName(ParamType Type) +{ + switch(Type) { + case ParamType::FCBool: + return "FCBool"; + case ParamType::FCInt: + return "FCInt"; + case ParamType::FCUInt: + return "FCUInt"; + case ParamType::FCText: + return "FCText"; + case ParamType::FCFloat: + return "FCFloat"; + case ParamType::FCGroup: + return "FCParamGroup"; + default: + return nullptr; + } +} + +ParameterGrp::ParamType ParameterGrp::TypeValue(const char *Name) +{ + if (Name) { + if (boost::equals(Name, "FCBool")) + return ParamType::FCBool; + if (boost::equals(Name, "FCInt")) + return ParamType::FCInt; + if (boost::equals(Name, "FCUInt")) + return ParamType::FCUInt; + if (boost::equals(Name, "FCText")) + return ParamType::FCText; + if (boost::equals(Name, "FCFloat")) + return ParamType::FCFloat; + if (boost::equals(Name, "FCParamGroup")) + return ParamType::FCGroup; + } + return ParamType::FCInvalid; +} + +void ParameterGrp::SetAttribute(ParamType Type, const char *Name, const char *Value) +{ + switch(Type) { + case ParamType::FCBool: + case ParamType::FCInt: + case ParamType::FCUInt: + case ParamType::FCFloat: + return _SetAttribute(Type, Name, Value); + case ParamType::FCText: + return SetASCII(Name, Value); + case ParamType::FCGroup: + RenameGrp(Name, Value); + break; + default: + break; + } +} + +void ParameterGrp::RemoveAttribute(ParamType Type, const char *Name) +{ + switch(Type) { + case ParamType::FCBool: + return RemoveBool(Name); + case ParamType::FCInt: + return RemoveInt(Name); + case ParamType::FCUInt: + return RemoveUnsigned(Name); + case ParamType::FCText: + return RemoveASCII(Name); + case ParamType::FCFloat: + return RemoveFloat(Name); + case ParamType::FCGroup: + return RemoveGrp(Name); + default: + break; + } +} + +const char * +ParameterGrp::GetAttribute(ParamType Type, + const char *Name, + std::string &Value, + const char *Default) const +{ + if (!_pGroupNode) + return Default; + + const char *T = TypeName(Type); + if (!T) + return Default; + + DOMElement *pcElem = FindElement(_pGroupNode,T,Name); + if (!pcElem) + return Default; + + if (Type == ParamType::FCText) + Value = GetASCII(Name, Default); + else if (Type != ParamType::FCGroup) + Value = StrX(pcElem->getAttribute(XStr("Value").unicodeForm())).c_str(); + return Value.c_str(); +} + +std::vector> +ParameterGrp::GetAttributeMap(ParamType Type, const char *sFilter) const +{ + std::vector> res; + if (!_pGroupNode) + return res; + + const char *T = TypeName(Type); + if (!T) + return res; + + std::string Name; + + DOMElement *pcTemp = FindElement(_pGroupNode, T); + while ( pcTemp) { + Name = StrX(static_cast( + pcTemp)->getAttributes()->getNamedItem( + XStr("Name").unicodeForm())->getNodeValue()).c_str(); + // check on filter condition + if (!sFilter || Name.find(sFilter)!= std::string::npos) { + if (Type == ParamType::FCGroup) + res.emplace_back(Name, std::string()); + else + res.emplace_back(Name, StrX(static_cast(pcTemp)->getAttribute( + XStr("Value").unicodeForm())).c_str()); + } + pcTemp = FindNextElement(pcTemp,T); + } + return res; +} + +void ParameterGrp::_Notify(ParamType Type, const char *Name, const char *Value) +{ + if (_Manager) + _Manager->signalParamChanged(this, Type, Name, Value); +} + +void ParameterGrp::_SetAttribute(ParamType T, const char *Name, const char *Value) +{ + const char *Type = TypeName(T); + if (!Type) + return; + if (!_pGroupNode) { + if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) + FC_WARN("Setting attribute " << Type << ":" + << Name << " in an orphan group " << _cName); + return; + } + if (_Clearing) { + if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) + FC_WARN("Adding attribute " << Type << ":" + << Name << " while clearing " << GetPath()); + return; + } + + // find or create the Element + DOMElement *pcElem = FindOrCreateElement(_pGroupNode,Type,Name); + if (pcElem) { + XStr attr("Value"); + // set the value only if different + if (strcmp(StrX(pcElem->getAttribute(attr.unicodeForm())).c_str(),Value)!=0) { + pcElem->setAttribute(attr.unicodeForm(), XStr(Value).unicodeForm()); + // trigger observer + _Notify(T, Name, Value); + } + // For backward compatibility, old observer gets notified regardless of + // value changes or not. + Notify(Name); + } +} + bool ParameterGrp::GetBool(const char* Name, bool bPreset) const { + if (!_pGroupNode) + return bPreset; + // check if Element in group DOMElement *pcElem = FindElement(_pGroupNode,"FCBool",Name); // if not return preset @@ -383,19 +686,15 @@ bool ParameterGrp::GetBool(const char* Name, bool bPreset) const void ParameterGrp::SetBool(const char* Name, bool bValue) { - // find or create the Element - DOMElement *pcElem = FindOrCreateElement(_pGroupNode,"FCBool",Name); - if (pcElem) { - // and set the value - pcElem->setAttribute(XStr("Value").unicodeForm(), XStr(bValue?"1":"0").unicodeForm()); - // trigger observer - Notify(Name); - } + _SetAttribute(ParamType::FCBool, Name, bValue?"1":"0"); } std::vector ParameterGrp::GetBools(const char * sFilter) const { std::vector vrValues; + if (!_pGroupNode) + return vrValues; + std::string Name; DOMElement *pcTemp = FindElement(_pGroupNode,"FCBool"); @@ -417,6 +716,9 @@ std::vector ParameterGrp::GetBools(const char * sFilter) const std::vector > ParameterGrp::GetBoolMap(const char * sFilter) const { std::vector > vrValues; + if (!_pGroupNode) + return vrValues; + std::string Name; DOMElement *pcTemp = FindElement(_pGroupNode,"FCBool"); @@ -437,6 +739,9 @@ std::vector > ParameterGrp::GetBoolMap(const char * long ParameterGrp::GetInt(const char* Name, long lPreset) const { + if (!_pGroupNode) + return lPreset; + // check if Element in group DOMElement *pcElem = FindElement(_pGroupNode,"FCInt",Name); // if not return preset @@ -449,20 +754,16 @@ long ParameterGrp::GetInt(const char* Name, long lPreset) const void ParameterGrp::SetInt(const char* Name, long lValue) { char cBuf[256]; - // find or create the Element - DOMElement *pcElem = FindOrCreateElement(_pGroupNode,"FCInt",Name); - if (pcElem) { - // and set the value - sprintf(cBuf,"%li",lValue); - pcElem->setAttribute(XStr("Value").unicodeForm(), XStr(cBuf).unicodeForm()); - // trigger observer - Notify(Name); - } + sprintf(cBuf,"%li",lValue); + _SetAttribute(ParamType::FCInt, Name, cBuf); } std::vector ParameterGrp::GetInts(const char * sFilter) const { std::vector vrValues; + if (!_pGroupNode) + return vrValues; + std::string Name; DOMElement *pcTemp = FindElement(_pGroupNode,"FCInt") ; @@ -481,6 +782,9 @@ std::vector ParameterGrp::GetInts(const char * sFilter) const std::vector > ParameterGrp::GetIntMap(const char * sFilter) const { std::vector > vrValues; + if (!_pGroupNode) + return vrValues; + std::string Name; DOMElement *pcTemp = FindElement(_pGroupNode,"FCInt") ; @@ -499,6 +803,9 @@ std::vector > ParameterGrp::GetIntMap(const char * s unsigned long ParameterGrp::GetUnsigned(const char* Name, unsigned long lPreset) const { + if (!_pGroupNode) + return lPreset; + // check if Element in group DOMElement *pcElem = FindElement(_pGroupNode,"FCUInt",Name); // if not return preset @@ -511,20 +818,16 @@ unsigned long ParameterGrp::GetUnsigned(const char* Name, unsigned long lPreset) void ParameterGrp::SetUnsigned(const char* Name, unsigned long lValue) { char cBuf[256]; - // find or create the Element - DOMElement *pcElem = FindOrCreateElement(_pGroupNode,"FCUInt",Name); - if (pcElem) { - // and set the value - sprintf(cBuf,"%lu",lValue); - pcElem->setAttribute(XStr("Value").unicodeForm(), XStr(cBuf).unicodeForm()); - // trigger observer - Notify(Name); - } + sprintf(cBuf,"%lu",lValue); + _SetAttribute(ParamType::FCUInt, Name, cBuf); } std::vector ParameterGrp::GetUnsigneds(const char * sFilter) const { std::vector vrValues; + if (!_pGroupNode) + return vrValues; + std::string Name; DOMElement *pcTemp = FindElement(_pGroupNode,"FCUInt"); @@ -543,6 +846,9 @@ std::vector ParameterGrp::GetUnsigneds(const char * sFilter) cons std::vector > ParameterGrp::GetUnsignedMap(const char * sFilter) const { std::vector > vrValues; + if (!_pGroupNode) + return vrValues; + std::string Name; DOMElement *pcTemp = FindElement(_pGroupNode,"FCUInt"); @@ -561,6 +867,9 @@ std::vector > ParameterGrp::GetUnsignedMap( double ParameterGrp::GetFloat(const char* Name, double dPreset) const { + if (!_pGroupNode) + return dPreset; + // check if Element in group DOMElement *pcElem = FindElement(_pGroupNode,"FCFloat",Name); // if not return preset @@ -573,20 +882,16 @@ double ParameterGrp::GetFloat(const char* Name, double dPreset) const void ParameterGrp::SetFloat(const char* Name, double dValue) { char cBuf[256]; - // find or create the Element - DOMElement *pcElem = FindOrCreateElement(_pGroupNode,"FCFloat",Name); - if (pcElem) { - // and set the value - sprintf(cBuf,"%.12f",dValue); // use %.12f instead of %f to handle values < 1.0e-6 - pcElem->setAttribute(XStr("Value").unicodeForm(), XStr(cBuf).unicodeForm()); - // trigger observer - Notify(Name); - } + sprintf(cBuf,"%.12f",dValue); // use %.12f instead of %f to handle values < 1.0e-6 + _SetAttribute(ParamType::FCFloat, Name, cBuf); } std::vector ParameterGrp::GetFloats(const char * sFilter) const { std::vector vrValues; + if (!_pGroupNode) + return vrValues; + std::string Name; DOMElement *pcTemp = FindElement(_pGroupNode,"FCFloat") ; @@ -605,6 +910,9 @@ std::vector ParameterGrp::GetFloats(const char * sFilter) const std::vector > ParameterGrp::GetFloatMap(const char * sFilter) const { std::vector > vrValues; + if (!_pGroupNode) + return vrValues; + std::string Name; DOMElement *pcTemp = FindElement(_pGroupNode,"FCFloat") ; @@ -635,8 +943,25 @@ void ParameterGrp::GetBlob(const char* /*Name*/, void* /*pBuf*/, long /*lMaxLeng void ParameterGrp::SetASCII(const char* Name, const char *sValue) { - // find or create the Element - DOMElement *pcElem = FindOrCreateElement(_pGroupNode,"FCText",Name); + if (!_pGroupNode) { + if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) + FC_WARN("Setting attribute " << "FCText:" + << Name << " in an orphan group " << _cName); + return; + } + if (_Clearing) { + if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) + FC_WARN("Adding attribute " << "FCText:" + << Name << " while clearing " << GetPath()); + return; + } + + bool isNew = false; + DOMElement *pcElem = FindElement(_pGroupNode,"FCText",Name); + if (!pcElem) { + pcElem = CreateElement(_pGroupNode,"FCText",Name); + isNew = true; + } if (pcElem) { // and set the value DOMNode *pcElem2 = pcElem->getFirstChild(); @@ -644,9 +969,12 @@ void ParameterGrp::SetASCII(const char* Name, const char *sValue) XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument *pDocument = _pGroupNode->getOwnerDocument(); DOMText *pText = pDocument->createTextNode(XUTF8Str(sValue).unicodeForm()); pcElem->appendChild(pText); + if (isNew || sValue[0]!=0) + _Notify(ParamType::FCText, Name, sValue); } - else { + else if (strcmp(StrXUTF8(pcElem2->getNodeValue()).c_str(), sValue)!=0) { pcElem2->setNodeValue(XUTF8Str(sValue).unicodeForm()); + _Notify(ParamType::FCText, Name, sValue); } // trigger observer Notify(Name); @@ -655,6 +983,9 @@ void ParameterGrp::SetASCII(const char* Name, const char *sValue) std::string ParameterGrp::GetASCII(const char* Name, const char * pPreset) const { + if (!_pGroupNode) + return pPreset ? pPreset : ""; + // check if Element in group DOMElement *pcElem = FindElement(_pGroupNode,"FCText",Name); // if not return preset @@ -675,6 +1006,9 @@ std::string ParameterGrp::GetASCII(const char* Name, const char * pPreset) const std::vector ParameterGrp::GetASCIIs(const char * sFilter) const { std::vector vrValues; + if (!_pGroupNode) + return vrValues; + std::string Name; DOMElement *pcTemp = FindElement(_pGroupNode,"FCText"); @@ -698,6 +1032,9 @@ std::vector ParameterGrp::GetASCIIs(const char * sFilter) const std::vector > ParameterGrp::GetASCIIMap(const char * sFilter) const { std::vector > vrValues; + if (!_pGroupNode) + return vrValues; + std::string Name; DOMElement *pcTemp = FindElement(_pGroupNode,"FCText"); @@ -723,6 +1060,9 @@ std::vector > ParameterGrp::GetASCIIMap(const void ParameterGrp::RemoveASCII(const char* Name) { + if (!_pGroupNode) + return; + // check if Element in group DOMElement *pcElem = FindElement(_pGroupNode,"FCText",Name); // if not return @@ -733,11 +1073,15 @@ void ParameterGrp::RemoveASCII(const char* Name) node->release(); // trigger observer + _Notify(ParamType::FCText, Name, nullptr); Notify(Name); } void ParameterGrp::RemoveBool(const char* Name) { + if (!_pGroupNode) + return; + // check if Element in group DOMElement *pcElem = FindElement(_pGroupNode,"FCBool",Name); // if not return @@ -748,6 +1092,7 @@ void ParameterGrp::RemoveBool(const char* Name) node->release(); // trigger observer + _Notify(ParamType::FCBool, Name, nullptr); Notify(Name); } @@ -766,6 +1111,9 @@ void ParameterGrp::RemoveBlob(const char* /*Name*/) void ParameterGrp::RemoveFloat(const char* Name) { + if (!_pGroupNode) + return; + // check if Element in group DOMElement *pcElem = FindElement(_pGroupNode,"FCFloat",Name); // if not return @@ -776,11 +1124,15 @@ void ParameterGrp::RemoveFloat(const char* Name) node->release(); // trigger observer + _Notify(ParamType::FCFloat,Name, nullptr); Notify(Name); } void ParameterGrp::RemoveInt(const char* Name) { + if (!_pGroupNode) + return; + // check if Element in group DOMElement *pcElem = FindElement(_pGroupNode,"FCInt",Name); // if not return @@ -791,11 +1143,15 @@ void ParameterGrp::RemoveInt(const char* Name) node->release(); // trigger observer + _Notify(ParamType::FCInt, Name, nullptr); Notify(Name); } void ParameterGrp::RemoveUnsigned(const char* Name) { + if (!_pGroupNode) + return; + // check if Element in group DOMElement *pcElem = FindElement(_pGroupNode,"FCUInt",Name); // if not return @@ -806,37 +1162,34 @@ void ParameterGrp::RemoveUnsigned(const char* Name) node->release(); // trigger observer + _Notify(ParamType::FCUInt, Name, nullptr); Notify(Name); } void ParameterGrp::RemoveGrp(const char* Name) { + if (!_pGroupNode) + return; + auto it = _GroupMap.find(Name); if (it == _GroupMap.end()) return; - // if this or any of its children is referenced by an observer - // it cannot be deleted -#if 1 - if (!it->second->ShouldRemove()) { - it->second->Clear(); + // If this or any of its children is referenced by an observer we do not + // delete the handle, just in case the group is later added again, or else + // those existing observer won't get any notification. BUT, we DO delete + // the underlying xml elements, so that we don't save the empty group + // later. + it->second->Clear(false); + if (!it->second->_Detached) { + it->second->_Detached = true; + _pGroupNode->removeChild(it->second->_pGroupNode); } - else { -#endif - // check if Element in group - DOMElement *pcElem = FindElement(_pGroupNode,"FCParamGroup",Name); - // if not return - if (!pcElem) - return; - - // remove group handle - _GroupMap.erase(Name); - - DOMNode* node = _pGroupNode->removeChild(pcElem); - node->release(); -#if 1 + if (it->second->ShouldRemove()) { + it->second->_Parent = nullptr; + it->second->_Manager = nullptr; + _GroupMap.erase(it); } -#endif // trigger observer Notify(Name); @@ -844,6 +1197,9 @@ void ParameterGrp::RemoveGrp(const char* Name) bool ParameterGrp::RenameGrp(const char* OldName, const char* NewName) { + if (!_pGroupNode) + return false; + auto it = _GroupMap.find(OldName); if (it == _GroupMap.end()) return false; @@ -861,43 +1217,58 @@ bool ParameterGrp::RenameGrp(const char* OldName, const char* NewName) if (pcElem) pcElem-> setAttribute(XStr("Name").unicodeForm(), XStr(NewName).unicodeForm()); + _Notify(ParamType::FCGroup, NewName, OldName); return true; } -void ParameterGrp::Clear() +void ParameterGrp::Clear(bool notify) { - std::vector vecNodes; + if (!_pGroupNode) + return; + + Base::StateLocker guard(_Clearing); + + // early trigger notification of group removal when all its children + // hierarchies are intact. + _Notify(ParamType::FCGroup, nullptr, nullptr); // checking on references - std::vector removeGrp; - for (auto it = _GroupMap.begin();it!=_GroupMap.end();++it) { - // If a group is referenced by some observer then do not remove it - // but clear it - if (!it->second->ShouldRemove()) { - it->second->Clear(); + for (auto it = _GroupMap.begin();it!=_GroupMap.end();) { + // If a group handle is referenced by some observer, then do not remove + // it but clear it, so that any existing observer can still get + // notification if the group is later on add back. We do remove the + // underlying xml element from its parent so that we won't save this + // empty group. + it->second->Clear(notify); + if (!it->second->_Detached) { + it->second->_Detached = true; + _pGroupNode->removeChild(it->second->_pGroupNode); } + if (!it->second->ShouldRemove()) + ++it; else { - removeGrp.push_back(it->first); + it->second->_Parent = nullptr; + it->second->_Manager = nullptr; + it = _GroupMap.erase(it); } } - // remove group handles - for (const auto& it : removeGrp) { - auto pos = _GroupMap.find(it); - vecNodes.push_back(pos->second->_pGroupNode); - _GroupMap.erase(pos->first); + // Remove the rest of non-group nodes; + std::vector> params; + for (DOMNode *child = _pGroupNode->getFirstChild(), *next = child; child != 0; child = next) { + next = next->getNextSibling(); + ParamType type = TypeValue(StrX(child->getNodeName()).c_str()); + if (type != ParamType::FCInvalid && type != ParamType::FCGroup) + params.emplace_back(type, StrX(child->getAttributes()->getNamedItem( + XStr("Name").unicodeForm())->getNodeValue()).c_str()); + DOMNode *node = _pGroupNode->removeChild(child); + node->release(); } - // searching all non-group nodes - for (DOMNode *child = _pGroupNode->getFirstChild(); child != nullptr; child = child->getNextSibling()) { - if (XMLString::compareString(child->getNodeName(), XStr("FCParamGroup").unicodeForm()) != 0) - vecNodes.push_back(child); - } - - // deleting the nodes - for (auto it = vecNodes.begin(); it != vecNodes.end(); ++it) { - DOMNode *child = _pGroupNode->removeChild(*it); - child->release(); + for (auto &v : params) { + _Notify(v.first, v.second.c_str(), nullptr); + if (notify) + Notify(v.second.c_str()); } // trigger observer @@ -963,26 +1334,14 @@ XERCES_CPP_NAMESPACE_QUALIFIER DOMElement *ParameterGrp::FindNextElement(XERCES_ return nullptr; } -XERCES_CPP_NAMESPACE_QUALIFIER DOMElement *ParameterGrp::FindOrCreateElement(XERCES_CPP_NAMESPACE_QUALIFIER DOMElement *Start, const char* Type, const char* Name) const +XERCES_CPP_NAMESPACE_QUALIFIER DOMElement *ParameterGrp::FindOrCreateElement(XERCES_CPP_NAMESPACE_QUALIFIER DOMElement *Start, const char* Type, const char* Name) { // first try to find it DOMElement *pcElem = FindElement(Start,Type,Name); if (pcElem) return pcElem; - if (XMLString::compareString(Start->getNodeName(), XStr("FCParamGroup").unicodeForm()) != 0 && - XMLString::compareString(Start->getNodeName(), XStr("FCParameters").unicodeForm()) != 0) { - Base::Console().Warning("FindOrCreateElement: %s cannot have the element %s of type %s\n", StrX(Start->getNodeName()).c_str(), Name, Type); - return nullptr; - } - - XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument *pDocument = _pGroupNode->getOwnerDocument(); - - pcElem = pDocument->createElement(XStr(Type).unicodeForm()); - pcElem-> setAttribute(XStr("Name").unicodeForm(), XStr(Name).unicodeForm()); - Start->appendChild(pcElem); - - return pcElem; + return CreateElement(Start,Type,Name); } XERCES_CPP_NAMESPACE_QUALIFIER DOMNode *ParameterGrp::FindAttribute(XERCES_CPP_NAMESPACE_QUALIFIER DOMNode *Node, const char* Name) const @@ -994,6 +1353,33 @@ XERCES_CPP_NAMESPACE_QUALIFIER DOMNode *ParameterGrp::FindAttribute(XERCES_CPP_N return nullptr; } +std::vector > +ParameterGrp::GetParameterNames(const char * sFilter) const +{ + std::vector > res; + if (!_pGroupNode) + return res; + + std::string Name; + + for (DOMNode *clChild = _pGroupNode->getFirstChild(); + clChild != 0; clChild = clChild->getNextSibling()) { + if (clChild->getNodeType() == DOMNode::ELEMENT_NODE) { + StrX type(clChild->getNodeName()); + ParamType Type = TypeValue(type.c_str()); + if (Type != ParamType::FCInvalid && Type != ParamType::FCGroup) { + if (clChild->getAttributes()->getLength() > 0) { + StrX name(clChild->getAttributes()->getNamedItem( + XStr("Name").unicodeForm())->getNodeValue()); + if (!sFilter || strstr(name.c_str(), sFilter)) + res.emplace_back(Type, name.c_str()); + } + } + } + } + return res; +} + void ParameterGrp::NotifyAll() { // get all ints and notify @@ -1022,6 +1408,13 @@ void ParameterGrp::NotifyAll() Notify(It5->first.c_str()); } +void ParameterGrp::_Reset() +{ + _pGroupNode = nullptr; + for (auto &v : _GroupMap) + v.second->_Reset(); +} + //************************************************************************** //************************************************************************** // ParameterSerializer @@ -1063,6 +1456,8 @@ static XercesDOMParser::ValSchemes gValScheme = XercesDOMParser::Val_Au ParameterManager::ParameterManager() : ParameterGrp(), _pDocument(nullptr), paramSerializer(nullptr) { + _Manager = this; + // initialize the XML system Init(); @@ -1126,6 +1521,7 @@ ParameterManager::ParameterManager() */ ParameterManager::~ParameterManager() { + _Reset(); delete _pDocument; delete paramSerializer; } @@ -1175,6 +1571,12 @@ bool ParameterManager::HasSerializer() const return (paramSerializer != nullptr); } +const std::string & ParameterManager::GetSerializeFileName() const +{ + static const std::string _dummy; + return paramSerializer ? paramSerializer->GetFileName() : _dummy; +} + int ParameterManager::LoadDocument() { if (paramSerializer) @@ -1356,7 +1758,8 @@ void ParameterManager::SaveDocument(XMLFormatTarget* pFormatTarget) const if (gUseFilter) { myFilter.reset(new DOMPrintFilter(DOMNodeFilter::SHOW_ELEMENT | DOMNodeFilter::SHOW_ATTRIBUTE | - DOMNodeFilter::SHOW_DOCUMENT_TYPE + DOMNodeFilter::SHOW_DOCUMENT_TYPE | + DOMNodeFilter::SHOW_TEXT )); theSerializer->setFilter(myFilter.get()); } @@ -1536,6 +1939,13 @@ DOMPrintFilter::FilterAction DOMPrintFilter::acceptNode(const DOMNode* node) con break; } case DOMNode::TEXT_NODE: { + // Filter out text element if it is under a group node. Note text xml + // element is plain text in between tags, and we do not store any text + // there. + auto parent = node->getParentNode(); + if (parent && XMLString::compareString( + parent->getNodeName(), XStr("FCParamGroup").unicodeForm()) == 0) + return DOMNodeFilter::FILTER_REJECT; return DOMNodeFilter::FILTER_ACCEPT; break; } diff --git a/src/Base/Parameter.h b/src/Base/Parameter.h index 42cede3726..0d138e696b 100644 --- a/src/Base/Parameter.h +++ b/src/Base/Parameter.h @@ -51,6 +51,7 @@ using PyObject = struct _object; #include #include +#include #include #include "Handle.h" @@ -105,6 +106,10 @@ public: void importFrom(const char* FileName); /// insert from a file to this group, overwrite only the similar void insert(const char* FileName); + /// revert to default value by deleting any parameter that has the same value in the given file + void revert(const char* FileName); + /// revert to default value by deleting any parameter that has the same value in the given group + void revert(Base::Reference); //@} /** @name methods for group handling */ @@ -124,7 +129,37 @@ public: /// rename a sub group from this group bool RenameGrp(const char* OldName, const char* NewName); /// clears everything in this group (all types) - void Clear(); + /// @param notify: whether to notify on deleted parameters using the Observer interface. + void Clear(bool notify = false); + //@} + + /** @name methods for generic attribute handling */ + //@{ + enum class ParamType { + FCInvalid = 0, + FCText = 1, + FCBool = 2, + FCInt = 3, + FCUInt = 4, + FCFloat = 5, + FCGroup = 6, + }; + static const char *TypeName(ParamType type); + static ParamType TypeValue(const char *); + void SetAttribute(ParamType Type, const char *Name, const char *Value); + void RemoveAttribute(ParamType Type, const char *Name); + const char *GetAttribute(ParamType Type, + const char *Name, + std::string &Value, + const char *Default) const; + std::vector> + GetAttributeMap(ParamType Type, const char * sFilter = NULL) const; + /** Return the type and name of all parameters with optional filter + * @param sFilter only strings which name includes sFilter are put in the vector + * @return std::vector of pair(type, name) + */ + std::vector> + GetParameterNames(const char * sFilter = NULL) const; //@} /** @name methods for bool handling */ @@ -201,6 +236,8 @@ public: //@{ /// set a string value void SetASCII(const char* Name, const char *sValue); + /// set a string value + void SetASCII(const char* Name, const std::string &sValue) { SetASCII(Name, sValue.c_str()); } /// read a string values std::string GetASCII(const char* Name, const char * pPreset=nullptr) const; /// remove a string value from this group @@ -222,19 +259,33 @@ public: return _cName.c_str(); } + /// return the full path of this group + std::string GetPath() const; + void GetPath(std::string &) const; + /** Notifies all observers for all entries except of sub-groups. */ void NotifyAll(); + ParameterGrp *Parent() const {return _Parent;} + ParameterManager *Manager() const {return _Manager;} + protected: /// constructor is protected (handle concept) - ParameterGrp(XERCES_CPP_NAMESPACE_QUALIFIER DOMElement *GroupNode=nullptr,const char* sName=nullptr); + ParameterGrp(XERCES_CPP_NAMESPACE_QUALIFIER DOMElement *GroupNode=nullptr, + const char* sName=nullptr, + ParameterGrp *Parent=nullptr); /// destructor is protected (handle concept) ~ParameterGrp() override; /// helper function for GetGroup Base::Reference _GetGroup(const char* Name); bool ShouldRemove() const; + void _Reset(); + + void _SetAttribute(ParamType Type, const char *Name, const char *Value); + void _Notify(ParamType Type, const char *Name, const char *Value); + XERCES_CPP_NAMESPACE_QUALIFIER DOMElement *FindNextElement(XERCES_CPP_NAMESPACE_QUALIFIER DOMNode *Prev, const char* Type) const; /** Find an element specified by Type and Name @@ -250,7 +301,9 @@ protected: * element of Type and with the attribute Name=Name. On success it returns * the pointer to that element, otherwise it creates the element and returns the pointer. */ - XERCES_CPP_NAMESPACE_QUALIFIER DOMElement *FindOrCreateElement(XERCES_CPP_NAMESPACE_QUALIFIER DOMElement *Start, const char* Type, const char* Name) const; + XERCES_CPP_NAMESPACE_QUALIFIER DOMElement *FindOrCreateElement(XERCES_CPP_NAMESPACE_QUALIFIER DOMElement *Start, const char* Type, const char* Name); + + XERCES_CPP_NAMESPACE_QUALIFIER DOMElement *CreateElement(XERCES_CPP_NAMESPACE_QUALIFIER DOMElement *Start, const char* Type, const char* Name); /** Find an attribute specified by Name */ @@ -262,7 +315,15 @@ protected: std::string _cName; /// map of already exported groups std::map > _GroupMap; - + ParameterGrp * _Parent = nullptr; + ParameterManager *_Manager = nullptr; + /// Means this group xml element has not been added to its parent yet. + bool _Detached = false; + /** Indicate this group is currently being cleared + * + * This is used to prevent anynew value/sub-group to be added in observer + */ + bool _Clearing = false; }; /** The parameter serializer class @@ -281,6 +342,7 @@ public: virtual void SaveDocument(const ParameterManager&); virtual int LoadDocument(ParameterManager&); virtual bool LoadOrCreateDocument(ParameterManager&); + const std::string &GetFileName() const {return filename;} protected: std::string filename; @@ -299,6 +361,33 @@ public: static void Init(); static void Terminate(); + /** Signal on parameter changes + * + * The signal is triggered on adding, removing, renaming or modifying on + * all individual parameters and group. The signature of the signal is + * \code + * void (ParameterGrp *param, ParamType type, const char *name, const char *value) + * \endcode + * where 'param' is the parameter group causing the change, 'type' is the + * type of the parameter, 'name' is the name of the parameter, and 'value' + * is the current value. + * + * The possible values of 'type' are, 'FCBool', 'FCInt', 'FCUint', + * 'FCFloat', 'FCText', and 'FCParamGroup'. The notification is triggered + * when value is changed, in which case 'value' contains the new value in + * text form, or, when the parameter is removed, in which case 'value' is + * empty. + * + * For 'FCParamGroup' type, the observer will be notified in the following events. + * - Group creation: both 'name' and 'value' contain the name of the new group + * - Group removal: both 'name' and 'value' are empty + * - Group rename: 'name' is the new name, and 'value' is the old name + */ + boost::signals2::signal signalParamChanged; + int LoadDocument(const char* sFileName); int LoadDocument(const XERCES_CPP_NAMESPACE_QUALIFIER InputSource&); bool LoadOrCreateDocument(const char* sFileName); @@ -313,6 +402,8 @@ public: void SetSerializer(ParameterSerializer*); /// Returns true if a serializer is set, otherwise false is returned. bool HasSerializer() const; + /// Returns the filename of the serialize. + const std::string & GetSerializeFileName() const; /// Loads an XML document by calling the serializer's load method. int LoadDocument(); /// Loads or creates an XML document by calling the serializer's load method. diff --git a/src/Base/ParameterPy.cpp b/src/Base/ParameterPy.cpp index c5a50b5020..8a0a3dcb58 100644 --- a/src/Base/ParameterPy.cpp +++ b/src/Base/ParameterPy.cpp @@ -52,10 +52,15 @@ public: { inst = obj; } + ParameterGrpObserver(const Py::Object& obj, const Py::Object &callable, ParameterGrp *target) + : callable(callable), _target(target), inst(obj) + { + } ~ParameterGrpObserver() override { Base::PyGILStateLocker lock; inst = Py::None(); + callable = Py::None(); } void OnChange(ParameterGrp::SubjectType &rCaller,ParameterGrp::MessageType Reason) override { @@ -81,6 +86,11 @@ public: return this->inst.is(obj); } +public: + Py::Object callable; + boost::signals2::scoped_connection conn; + ParameterGrp *_target = nullptr; // no reference counted, do not access + private: Py::Object inst; }; @@ -103,10 +113,14 @@ public: Py::Object remGroup(const Py::Tuple&); Py::Object hasGroup(const Py::Tuple&); + Py::Object getManager(const Py::Tuple&); + Py::Object getParent(const Py::Tuple&); + Py::Object isEmpty(const Py::Tuple&); Py::Object clear(const Py::Tuple&); Py::Object attach(const Py::Tuple&); + Py::Object attachManager(const Py::Tuple& args); Py::Object detach(const Py::Tuple&); Py::Object notify(const Py::Tuple&); Py::Object notifyAll(const Py::Tuple&); @@ -165,10 +179,31 @@ void ParameterGrpPy::init_type() add_varargs_method("RemGroup",&ParameterGrpPy::remGroup,"RemGroup(str)"); add_varargs_method("HasGroup",&ParameterGrpPy::hasGroup,"HasGroup(str)"); + add_varargs_method("Manager",&ParameterGrpPy::getManager,"Manager()"); + add_varargs_method("Parent",&ParameterGrpPy::getParent,"Parent()"); + add_varargs_method("IsEmpty",&ParameterGrpPy::isEmpty,"IsEmpty()"); add_varargs_method("Clear",&ParameterGrpPy::clear,"Clear()"); add_varargs_method("Attach",&ParameterGrpPy::attach,"Attach()"); + add_varargs_method("AttachManager",&ParameterGrpPy::attachManager, + "AttachManager(observer) -- attach parameter manager for notification\n\n" + "This method attaches a user defined observer to the manager (i.e. the root)\n" + "of the current parameter group to receive notification of all its parameters\n" + "and those from its sub-groups\n\n" + "The method expects the observer to have a callable attribute as shown below\n" + " slotParamChanged(param, tp, name, value)\n" + "where 'param' is the parameter group causing the change, 'tp' is the type of\n" + "the parameter, 'name' is the name of the parameter, and 'value' is the current\n" + "value.\n\n" + "The possible value of type are, 'FCBool', 'FCInt', 'FCUint', 'FCFloat', 'FCText',\n" + "and 'FCParamGroup'. The notification is triggered when value is changed, in which\n" + "case 'value' contains the new value in text form, or, when the parameter is removed,\n" + "in which case 'value' is empty.\n\n" + "For 'FCParamGroup' type, the observer will be notified in the following events.\n" + "* Group creation: both 'name' and 'value' contain the name of the new group\n" + "* Group removal: both 'name' and 'value' are empty\n" + "* Group rename: 'name' is the new name, and 'value' is the old name"); add_varargs_method("Detach",&ParameterGrpPy::detach,"Detach()"); add_varargs_method("Notify",&ParameterGrpPy::notify,"Notify()"); add_varargs_method("NotifyAll",&ParameterGrpPy::notifyAll,"NotifyAll()"); @@ -214,7 +249,8 @@ ParameterGrpPy::~ParameterGrpPy() { for (ParameterGrpObserverList::iterator it = _observers.begin(); it != _observers.end(); ++it) { ParameterGrpObserver* obs = *it; - _cParamGrp->Detach(obs); + if (!obs->_target) + _cParamGrp->Detach(obs); delete obs; } } @@ -281,6 +317,40 @@ Py::Object ParameterGrpPy::getGroup(const Py::Tuple& args) } } +Py::Object ParameterGrpPy::getManager(const Py::Tuple& args) +{ + if (!PyArg_ParseTuple(args.ptr(), "")) + throw Py::Exception(); + + // get the Handle of the wanted group + Base::Reference handle = _cParamGrp->Manager(); + if (handle.isValid()) { + // create a python wrapper class + ParameterGrpPy *pcParamGrp = new ParameterGrpPy(handle); + // increment the ref count + return Py::asObject(pcParamGrp); + } + else + return Py::Object(); +} + +Py::Object ParameterGrpPy::getParent(const Py::Tuple& args) +{ + if (!PyArg_ParseTuple(args.ptr(), "")) + throw Py::Exception(); + + // get the Handle of the wanted group + Base::Reference handle = _cParamGrp->Parent(); + if (handle.isValid()) { + // create a python wrapper class + ParameterGrpPy *pcParamGrp = new ParameterGrpPy(handle); + // increment the ref count + return Py::asObject(pcParamGrp); + } + else + return Py::Object(); +} + Py::Object ParameterGrpPy::getGroupName(const Py::Tuple& args) { if (!PyArg_ParseTuple(args.ptr(), "")) @@ -593,6 +663,56 @@ Py::Object ParameterGrpPy::attach(const Py::Tuple& args) return Py::None(); } +Py::Object ParameterGrpPy::attachManager(const Py::Tuple& args) +{ + PyObject* obj; + if (!PyArg_ParseTuple(args.ptr(), "O", &obj)) + throw Py::Exception(); + + if (!_cParamGrp->Manager()) + throw Py::RuntimeError("Parameter has no manager"); + + Py::Object o(obj); + if (!o.hasAttr(std::string("slotParamChanged"))) + throw Py::TypeError("Object has no slotParamChanged attribute"); + + Py::Object attr(o.getAttr("slotParamChanged")); + if (!attr.isCallable()) + throw Py::TypeError("Object has no slotParamChanged callable attribute"); + + for (ParameterGrpObserverList::iterator it = _observers.begin(); it != _observers.end(); ++it) { + if ((*it)->isEqual(o)) { + throw Py::RuntimeError("Object is already attached."); + } + } + + ParameterGrpObserver* obs = new ParameterGrpObserver(o, attr, _cParamGrp); + obs->conn = _cParamGrp->Manager()->signalParamChanged.connect( + [obs](ParameterGrp *Param, ParameterGrp::ParamType Type, const char *Name, const char *Value) { + if (!Param) return; + for (auto p = Param; p; p = p->Parent()) { + if (p == obs->_target) { + Base::PyGILStateLocker lock; + Py::TupleN args( + Py::asObject(new ParameterGrpPy(Param)), + Py::String(ParameterGrp::TypeName(Type)), + Py::String(Name ? Name : ""), + Py::String(Value ? Value : "")); + try { + Py::Callable(obs->callable).apply(args); + } catch (Py::Exception &) { + Base::PyException e; + e.ReportException(); + } + break; + } + } + }); + + _observers.push_back(obs); + return Py::None(); +} + Py::Object ParameterGrpPy::detach(const Py::Tuple& args) { PyObject* obj; diff --git a/src/Gui/DlgParameterImp.cpp b/src/Gui/DlgParameterImp.cpp index fc702d5454..3b4457497b 100644 --- a/src/Gui/DlgParameterImp.cpp +++ b/src/Gui/DlgParameterImp.cpp @@ -92,7 +92,7 @@ DlgParameterImp::DlgParameterImp( QWidget* parent, Qt::WindowFlags fl ) #endif ParameterManager* sys = App::GetApplication().GetParameterSet("System parameter"); - const std::map& rcList = App::GetApplication().GetParameterSetList(); + const auto& rcList = App::GetApplication().GetParameterSetList(); for (const auto & it : rcList) { if (it.second != sys) // for now ignore system parameters because they are nowhere used ui->parameterSet->addItem(tr(it.first.c_str()), QVariant(QByteArray(it.first.c_str())));