diff --git a/data/examples/FemCalculixCantilever2D.FCStd b/data/examples/FemCalculixCantilever2D.FCStd index 7029b54be6..22bc6f4ef5 100644 Binary files a/data/examples/FemCalculixCantilever2D.FCStd and b/data/examples/FemCalculixCantilever2D.FCStd differ diff --git a/data/examples/FemCalculixCantilever3D.FCStd b/data/examples/FemCalculixCantilever3D.FCStd index f001970390..6a2509b01f 100644 Binary files a/data/examples/FemCalculixCantilever3D.FCStd and b/data/examples/FemCalculixCantilever3D.FCStd differ diff --git a/src/App/Application.cpp b/src/App/Application.cpp index 7754554d9e..2aa62f41dc 100644 --- a/src/App/Application.cpp +++ b/src/App/Application.cpp @@ -147,6 +147,34 @@ using namespace Base; using namespace App; using namespace std; +/** Observer that watches relabeled objects and make sure that the labels inside + * a document are unique. + * @note In the FreeCAD design it is explicitly allowed to have duplicate labels + * (i.e. the user visible text e.g. in the tree view) while the internal names + * are always guaranteed to be unique. + */ +class ObjectLabelObserver +{ +public: + /// The one and only instance. + static ObjectLabelObserver* instance(); + /// Destructs the sole instance. + static void destruct (); + + /** Checks the new label of the object and relabel it if needed + * to make it unique document-wide + */ + void slotRelabelObject(const App::DocumentObject&, const App::Property&); + +private: + static ObjectLabelObserver* _singleton; + + ObjectLabelObserver(); + ~ObjectLabelObserver(); + const App::DocumentObject* current; + ParameterGrp::handle _hPGrp; +}; + //========================================================================== // Application @@ -160,7 +188,6 @@ Base::ConsoleObserverFile *Application::_pConsoleObserverFile =0; AppExport std::map Application::mConfig; BaseExport extern PyObject* Base::BaseExceptionFreeCADError; - //************************************************************************** // Construction and destruction @@ -1346,6 +1373,8 @@ void Application::initApplication(void) Console().Log("Run App init script\n"); Interpreter().runString(Base::ScriptFactory().ProduceScript("CMakeVariables")); Interpreter().runString(Base::ScriptFactory().ProduceScript("FreeCADInit")); + + ObjectLabelObserver::instance(); } std::list Application::getCmdLineFiles() @@ -2261,3 +2290,80 @@ std::string Application::FindHomePath(const char* sCall) #else # error "std::string Application::FindHomePath(const char*) not implemented" #endif + +ObjectLabelObserver* ObjectLabelObserver::_singleton = 0; + +ObjectLabelObserver* ObjectLabelObserver::instance() +{ + if (!_singleton) + _singleton = new ObjectLabelObserver; + return _singleton; +} + +void ObjectLabelObserver::destruct () +{ + delete _singleton; + _singleton = 0; +} + +void ObjectLabelObserver::slotRelabelObject(const App::DocumentObject& obj, const App::Property& prop) +{ + // observe only the Label property + if (&prop == &obj.Label) { + // have we processed this (or another?) object right now? + if (current) { + return; + } + + std::string label = obj.Label.getValue(); + App::Document* doc = obj.getDocument(); + if (doc && !_hPGrp->GetBool("DuplicateLabels")) { + std::vector objectLabels; + std::vector::const_iterator it; + std::vector objs = doc->getObjects(); + bool match = false; + + for (it = objs.begin();it != objs.end();++it) { + if (*it == &obj) + continue; // don't compare object with itself + std::string objLabel = (*it)->Label.getValue(); + if (!match && objLabel == label) + match = true; + objectLabels.push_back(objLabel); + } + + // make sure that there is a name conflict otherwise we don't have to do anything + if (match && !label.empty()) { + // remove number from end to avoid lengthy names + size_t lastpos = label.length()-1; + while (label[lastpos] >= 48 && label[lastpos] <= 57) { + // if 'lastpos' becomes 0 then all characters are digits. In this case we use + // the complete label again + if (lastpos == 0) { + lastpos = label.length()-1; + break; + } + lastpos--; + } + + label = label.substr(0, lastpos+1); + label = Base::Tools::getUniqueName(label, objectLabels, 3); + this->current = &obj; + const_cast(obj).Label.setValue(label); + this->current = 0; + } + } + } +} + +ObjectLabelObserver::ObjectLabelObserver() : current(0) +{ + App::GetApplication().signalChangedObject.connect(boost::bind + (&ObjectLabelObserver::slotRelabelObject, this, _1, _2)); + _hPGrp = App::GetApplication().GetUserParameter().GetGroup("BaseApp"); + _hPGrp = _hPGrp->GetGroup("Preferences")->GetGroup("Document"); +} + +ObjectLabelObserver::~ObjectLabelObserver() +{ +} diff --git a/src/App/Document.cpp b/src/App/Document.cpp index 23547b101d..77be9f9c55 100644 --- a/src/App/Document.cpp +++ b/src/App/Document.cpp @@ -615,6 +615,7 @@ void Document::exportGraphviz(std::ostream& out) const if (i_in_deg_pair.first == in_edges.end()) { removeEdges(in_edges, out_edges, i_out_deg_pair, [&](Edge e) { return target(e, DepList); }); changed = true; + i_out_deg_pair = out_edges.equal_range(*uvi); } // Remove in edges of nodes that don't have a single edge out diff --git a/src/App/DocumentObjectExtension.cpp b/src/App/DocumentObjectExtension.cpp index 526bda7764..9c8c5a34f7 100644 --- a/src/App/DocumentObjectExtension.cpp +++ b/src/App/DocumentObjectExtension.cpp @@ -37,7 +37,7 @@ EXTENSION_PROPERTY_SOURCE(App::DocumentObjectExtension, App::Extension) DocumentObjectExtension::DocumentObjectExtension() { - initExtension(App::DocumentObjectExtension::getExtensionClassTypeId()); + initExtensionType(App::DocumentObjectExtension::getExtensionClassTypeId()); } DocumentObjectExtension::~DocumentObjectExtension() diff --git a/src/App/Extension.cpp b/src/App/Extension.cpp index 35637df6d3..519982bc5b 100644 --- a/src/App/Extension.cpp +++ b/src/App/Extension.cpp @@ -71,7 +71,7 @@ Extension::~Extension() } } -void Extension::initExtension(Base::Type type) { +void Extension::initExtensionType(Base::Type type) { m_extensionType = type; if(m_extensionType.isBad()) diff --git a/src/App/Extension.h b/src/App/Extension.h index 1dfc8dab80..4b1137ed3b 100644 --- a/src/App/Extension.h +++ b/src/App/Extension.h @@ -215,7 +215,7 @@ public: Extension(); virtual ~Extension(); - void initExtension(App::ExtensionContainer* obj); + virtual void initExtension(App::ExtensionContainer* obj); App::ExtensionContainer* getExtendedContainer() {return m_base;} const App::ExtensionContainer* getExtendedContainer() const {return m_base;} @@ -272,7 +272,7 @@ protected: friend class App::ExtensionContainer; protected: - void initExtension(Base::Type type); + void initExtensionType(Base::Type type); bool m_isPythonExtension = false; Py::Object ExtensionPythonObject; @@ -309,7 +309,7 @@ public: ExtensionPythonT() { ExtensionT::m_isPythonExtension = true; - ExtensionT::initExtension(ExtensionPythonT::getExtensionClassTypeId()); + ExtensionT::initExtensionType(ExtensionPythonT::getExtensionClassTypeId()); EXTENSION_ADD_PROPERTY(ExtensionProxy,(Py::Object())); } diff --git a/src/App/ExtensionContainer.cpp b/src/App/ExtensionContainer.cpp index 1b5ef4f49d..6fd435415d 100644 --- a/src/App/ExtensionContainer.cpp +++ b/src/App/ExtensionContainer.cpp @@ -35,11 +35,11 @@ using namespace App; -TYPESYSTEM_SOURCE(App::ExtensionContainer, App::PropertyContainer); +TYPESYSTEM_SOURCE(App::ExtensionContainer, App::PropertyContainer) ExtensionContainer::ExtensionContainer() { -}; +} ExtensionContainer::~ExtensionContainer() { @@ -48,7 +48,7 @@ ExtensionContainer::~ExtensionContainer() { if(entry.second->isPythonExtension()) delete entry.second; } -}; +} void ExtensionContainer::registerExtension(Base::Type extension, Extension* ext) { @@ -361,7 +361,7 @@ void ExtensionContainer::restoreExtensions(Base::XMLReader& reader) { for (int i=0 ;iisDerivedFrom(App::GeoFeature::getClassTypeId())) + throw Base::Exception("GeoFeatureGroupExtension can only be applied to GeoFeatures"); + + App::GroupExtension::initExtension(obj); +} + +PropertyPlacement& GeoFeatureGroupExtension::placement() { + + if(!getExtendedContainer()) + throw Base::Exception("GeoFeatureGroupExtension was not applied to GeoFeature"); + + return static_cast(getExtendedContainer())->Placement; +} + + void GeoFeatureGroupExtension::transformPlacement(const Base::Placement &transform) { // NOTE: Keep in sync with APP::GeoFeature - Base::Placement plm = this->Placement.getValue(); + Base::Placement plm = this->placement().getValue(); plm = transform * plm; - this->Placement.setValue(plm); + this->placement().setValue(plm); } std::vector GeoFeatureGroupExtension::getGeoSubObjects () const { @@ -128,9 +143,9 @@ bool GeoFeatureGroupExtension::geoHasObject (const DocumentObject* obj) const { DocumentObject* GeoFeatureGroupExtension::getGroupOfObject(const DocumentObject* obj, bool indirect) { const Document* doc = obj->getDocument(); - std::vector grps = doc->getObjectsOfType(GeoFeatureGroupExtension::getExtensionClassTypeId()); + std::vector grps = doc->getObjectsWithExtension(GeoFeatureGroupExtension::getExtensionClassTypeId()); for (std::vector::const_iterator it = grps.begin(); it != grps.end(); ++it) { - GeoFeatureGroupExtension* grp = (GeoFeatureGroupExtension*)(*it); + GeoFeatureGroupExtension* grp = (*it)->getExtensionByType(); if ( indirect ) { if (grp->geoHasObject(obj)) { return dynamic_cast(grp); diff --git a/src/App/GeoFeatureGroupExtension.h b/src/App/GeoFeatureGroupExtension.h index 31f22eb3d7..901b34bcfd 100644 --- a/src/App/GeoFeatureGroupExtension.h +++ b/src/App/GeoFeatureGroupExtension.h @@ -41,7 +41,9 @@ class AppExport GeoFeatureGroupExtension : public App::GroupExtension EXTENSION_PROPERTY_HEADER(App::GeoFeatureGroupExtension); public: - PropertyPlacement Placement; + PropertyPlacement& placement(); + + virtual void initExtension(ExtensionContainer* obj); /** * @brief transformPlacement applies transform to placement of this shape. @@ -71,7 +73,8 @@ public: /// Returns true if the given DocumentObject is DocumentObjectGroup but not GeoFeatureGroup static bool isNonGeoGroup(const DocumentObject* obj) { - return obj->hasExtension(GroupExtension::getExtensionClassTypeId()); + return obj->hasExtension(GroupExtension::getExtensionClassTypeId()) & + !obj->hasExtension(GeoFeatureGroupExtension::getExtensionClassTypeId()); } }; diff --git a/src/App/GroupExtension.cpp b/src/App/GroupExtension.cpp index a070840b5d..0ed16d8f15 100644 --- a/src/App/GroupExtension.cpp +++ b/src/App/GroupExtension.cpp @@ -38,7 +38,7 @@ EXTENSION_PROPERTY_SOURCE(App::GroupExtension, App::DocumentObjectExtension) GroupExtension::GroupExtension() { - initExtension(GroupExtension::getExtensionClassTypeId()); + initExtensionType(GroupExtension::getExtensionClassTypeId()); EXTENSION_ADD_PROPERTY_TYPE(Group,(0),"Base",(App::PropertyType)(Prop_Output),"List of referenced objects"); } @@ -63,6 +63,11 @@ void GroupExtension::addObject(DocumentObject* obj) if(!allowObject(obj)) return; + //only one group per object + auto *group = App::GroupExtension::getGroupOfObject(obj); + if(group && group != getExtendedObject()) + group->getExtensionByType()->removeObject(obj); + if (!hasObject(obj)) { std::vector grp = Group.getValues(); grp.push_back(obj); @@ -180,9 +185,9 @@ int GroupExtension::countObjectsOfType(const Base::Type& typeId) const DocumentObject* GroupExtension::getGroupOfObject(const DocumentObject* obj) { const Document* doc = obj->getDocument(); - std::vector grps = doc->getObjectsOfType(GroupExtension::getExtensionClassTypeId()); + std::vector grps = doc->getObjectsWithExtension(GroupExtension::getExtensionClassTypeId()); for (std::vector::const_iterator it = grps.begin(); it != grps.end(); ++it) { - GroupExtension* grp = (GroupExtension*)(*it); + GroupExtension* grp = (*it)->getExtensionByType(); if (grp->hasObject(obj)) return *it; } diff --git a/src/App/GroupExtension.h b/src/App/GroupExtension.h index 01fe0fa79f..f6531020f4 100644 --- a/src/App/GroupExtension.h +++ b/src/App/GroupExtension.h @@ -49,20 +49,20 @@ public: /** Adds an object of \a sType with \a pObjectName to the document this group belongs to and * append it to this group as well. */ - DocumentObject *addObject(const char* sType, const char* pObjectName); + virtual DocumentObject *addObject(const char* sType, const char* pObjectName); /* Adds the object \a obj to this group. */ - void addObject(DocumentObject* obj); + virtual void addObject(DocumentObject* obj); /*override this function if you want only special objects */ virtual bool allowObject(DocumentObject* ) {return true;}; /** Removes an object from this group. */ - void removeObject(DocumentObject* obj); + virtual void removeObject(DocumentObject* obj); /** Removes all children objects from this group and the document. */ - void removeObjectsFromDocument(); + virtual void removeObjectsFromDocument(); /** Returns the object of this group with \a Name. If the group doesn't have such an object 0 is returned. * @note This method might return 0 even if the document this group belongs to contains an object with this name. */ diff --git a/src/App/OriginGroupExtension.cpp b/src/App/OriginGroupExtension.cpp index 2f0ac1468c..0235ea6b8c 100644 --- a/src/App/OriginGroupExtension.cpp +++ b/src/App/OriginGroupExtension.cpp @@ -39,7 +39,7 @@ EXTENSION_PROPERTY_SOURCE(App::OriginGroupExtension, App::GeoFeatureGroupExtensi OriginGroupExtension::OriginGroupExtension () { - initExtension(OriginGroupExtension::getExtensionClassTypeId()); + initExtensionType(OriginGroupExtension::getExtensionClassTypeId()); EXTENSION_ADD_PROPERTY_TYPE ( Origin, (0), 0, App::Prop_Hidden, "Origin linked to the group" ); } @@ -129,6 +129,69 @@ void OriginGroupExtension::onExtendedUnsetupObject () { GeoFeatureGroupExtension::onExtendedUnsetupObject (); } +void OriginGroupExtension::relinkToOrigin(App::DocumentObject* obj) +{ + //we get all links and replace the origin objects if needed (subnames need not to change, they + //would stay the same) + std::vector< App::DocumentObject* > result; + std::vector list; + obj->getPropertyList(list); + for(App::Property* prop : list) { + if(prop->getTypeId().isDerivedFrom(App::PropertyLink::getClassTypeId())) { + + auto p = static_cast(prop); + if(!p->getValue() || !p->getValue()->isDerivedFrom(App::OriginFeature::getClassTypeId())) + continue; + + p->setValue(getOrigin()->getOriginFeature(static_cast(p->getValue())->Role.getValue())); + } + else if(prop->getTypeId().isDerivedFrom(App::PropertyLinkList::getClassTypeId())) { + auto p = static_cast(prop); + auto vec = p->getValues(); + std::vector result; + bool changed = false; + for(App::DocumentObject* o : vec) { + if(!o || !o->isDerivedFrom(App::OriginFeature::getClassTypeId())) + result.push_back(o); + else { + result.push_back(getOrigin()->getOriginFeature(static_cast(o)->Role.getValue())); + changed = true; + } + } + if(changed) + static_cast(prop)->setValues(result); + } + else if(prop->getTypeId().isDerivedFrom(App::PropertyLinkSub::getClassTypeId())) { + auto p = static_cast(prop); + if(!p->getValue() || !p->getValue()->isDerivedFrom(App::OriginFeature::getClassTypeId())) + continue; + + p->setValue(getOrigin()->getOriginFeature(static_cast(p->getValue())->Role.getValue())); + } + else if(prop->getTypeId().isDerivedFrom(App::PropertyLinkSubList::getClassTypeId())) { + auto p = static_cast(prop); + auto vec = p->getValues(); + std::vector result; + bool changed = false; + for(App::DocumentObject* o : vec) { + if(!o || !o->isDerivedFrom(App::OriginFeature::getClassTypeId())) + result.push_back(o); + else { + result.push_back(getOrigin()->getOriginFeature(static_cast(o)->Role.getValue())); + changed = true; + } + } + if(changed) + static_cast(prop)->setValues(result); + } + } +} + +void OriginGroupExtension::addObject(DocumentObject* obj) { + relinkToOrigin(obj); + App::GeoFeatureGroupExtension::addObject(obj); +} + // Python feature --------------------------------------------------------- diff --git a/src/App/OriginGroupExtension.h b/src/App/OriginGroupExtension.h index fe5cb89f36..53938ade2a 100644 --- a/src/App/OriginGroupExtension.h +++ b/src/App/OriginGroupExtension.h @@ -62,6 +62,11 @@ public: /// Origin linked to the group PropertyLink Origin; + + //changes all links of obj to a origin to point to this groupes origin + void relinkToOrigin(App::DocumentObject* obj); + + virtual void addObject(DocumentObject* obj); protected: /// Checks integrity of the Origin diff --git a/src/App/Part.cpp b/src/App/Part.cpp index 96303bad40..8030c0212d 100644 --- a/src/App/Part.cpp +++ b/src/App/Part.cpp @@ -35,7 +35,7 @@ using namespace App; -PROPERTY_SOURCE_WITH_EXTENSIONS(App::Part, App::DocumentObject) +PROPERTY_SOURCE_WITH_EXTENSIONS(App::Part, App::GeoFeature) //=========================================================================== diff --git a/src/App/Part.h b/src/App/Part.h index d34388fe48..089c0d6888 100644 --- a/src/App/Part.h +++ b/src/App/Part.h @@ -35,7 +35,7 @@ namespace App /** Base class of all geometric document objects. */ -class AppExport Part : public App::DocumentObject, public App::OriginGroupExtension +class AppExport Part : public App::GeoFeature, public App::OriginGroupExtension { PROPERTY_HEADER_WITH_EXTENSIONS(App::Part); diff --git a/src/Build/CMakeLists.txt b/src/Build/CMakeLists.txt index af47278204..924f4859e1 100644 --- a/src/Build/CMakeLists.txt +++ b/src/Build/CMakeLists.txt @@ -3,6 +3,9 @@ set(PACKAGE_WCREF "$WCREV$") set(PACKAGE_WCDATE "$WCDATE$") set(PACKAGE_WCURL "$WCURL$") +# If the sources don't include a Version.h then create one +if (NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/Version.h) + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/Version.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/Version.h.in ) @@ -19,3 +22,4 @@ if (NOT EXISTS ${CMAKE_CURRENT_BINARY_DIR}/Version.h) ${CMAKE_CURRENT_BINARY_DIR}/Version.h ) endif (NOT EXISTS ${CMAKE_CURRENT_BINARY_DIR}/Version.h) +endif (NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/Version.h) diff --git a/src/Doc/BuildWebDoc.cfg.in b/src/Doc/BuildWebDoc.cfg.in index 257643eea1..dcec68aa6d 100644 --- a/src/Doc/BuildWebDoc.cfg.in +++ b/src/Doc/BuildWebDoc.cfg.in @@ -500,7 +500,7 @@ SHOW_FILES = NO # This will remove the Namespaces entry from the Quick Index # and from the Folder Tree View (if specified). The default is YES. -SHOW_NAMESPACES = NO +SHOW_NAMESPACES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from diff --git a/src/Doc/mainpage.dox b/src/Doc/mainpage.dox index 971b3e396f..172d02e94f 100644 --- a/src/Doc/mainpage.dox +++ b/src/Doc/mainpage.dox @@ -26,7 +26,7 @@ \mainpage FreeCAD source documentation - This is the source documentation of FreeCAD. + This is the source documentation of FreeCAD. It is automatically generated from the source code, and describes the different components of the FreeCAD source code, for both the parts written in C++ and Python. diff --git a/src/Doc/templates/customdoxygen.css b/src/Doc/templates/customdoxygen.css index 53a9a9eb9f..753938007b 100644 --- a/src/Doc/templates/customdoxygen.css +++ b/src/Doc/templates/customdoxygen.css @@ -1,3 +1,19 @@ +@font-face { + font-family: 'Fira Sans'; + src: url('/fonts/FiraSans-Regular.eot'); + src: local('☺'), url('/fonts/FiraSans-Regular.woff') format('woff'), url('/fonts/FiraSans-Regular.ttf') format('truetype'), url('/fonts/FiraSans-Regular.svg') format('svg'); + font-weight: 400; + font-style: normal; +} + +@font-face { + font-family: 'Roboto'; + src: url('/fonts/Roboto.eot'); + src: local('☺'), url('/fonts/Roboto.woff') format('woff'), url('/fonts/Roboto.ttf') format('truetype'), url('/fonts/Roboto.svg') format('svg'); + font-weight: 400; + font-style: normal; +} + h1, .h1, h2, .h2, h3, .h3{ font-weight: 200 !important; } @@ -389,3 +405,6 @@ pre.fragment { .memdoc p { text-align: left; } +body, table, div, p, dl { + font: 400 16px/22px Fira Sans,sans-serif; +} diff --git a/src/Doc/templates/header.html b/src/Doc/templates/header.html index 9d41180a0c..f0b9b181fa 100644 --- a/src/Doc/templates/header.html +++ b/src/Doc/templates/header.html @@ -43,10 +43,18 @@