App: Apply clang format (part 1)

This commit is contained in:
wmayer
2024-11-15 17:43:49 +01:00
committed by wwmayer
parent 463cc08f3f
commit 6f5259af26
124 changed files with 6733 additions and 4886 deletions

View File

@@ -45,7 +45,7 @@ PROPERTY_SOURCE(App::AnnotationLabel, App::DocumentObject)
AnnotationLabel::AnnotationLabel()
{
ADD_PROPERTY_TYPE(LabelText, (""), "Label",Prop_Output, "Text label of the annotation");
ADD_PROPERTY_TYPE(LabelText, (""), "Label", Prop_Output, "Text label of the annotation");
ADD_PROPERTY_TYPE(BasePosition, (Base::Vector3d()), "Label", Prop_Output, "Base position");
ADD_PROPERTY_TYPE(TextPosition, (Base::Vector3d()), "Label", Prop_Output, "Text position");
}

View File

@@ -32,7 +32,7 @@
namespace App
{
class AppExport Annotation : public DocumentObject
class AppExport Annotation: public DocumentObject
{
PROPERTY_HEADER_WITH_OVERRIDE(App::Annotation);
@@ -45,12 +45,13 @@ public:
App::PropertyVector Position;
/// returns the type name of the ViewProvider
const char* getViewProviderName() const override {
const char* getViewProviderName() const override
{
return "Gui::ViewProviderAnnotation";
}
};
class AppExport AnnotationLabel : public DocumentObject
class AppExport AnnotationLabel: public DocumentObject
{
PROPERTY_HEADER_WITH_OVERRIDE(App::AnnotationLabel);
@@ -64,12 +65,13 @@ public:
App::PropertyVector TextPosition;
/// returns the type name of the ViewProvider
const char* getViewProviderName() const override {
const char* getViewProviderName() const override
{
return "Gui::ViewProviderAnnotationLabel";
}
};
} //namespace App
} // namespace App
#endif // APP_ANNOTATION_H
#endif // APP_ANNOTATION_H

File diff suppressed because it is too large Load Diff

View File

@@ -37,12 +37,11 @@ using namespace App;
static int _TransactionLock;
static int _TransactionClosed;
AutoTransaction::AutoTransaction(const char *name, bool tmpName) {
auto &app = GetApplication();
if(name && app._activeTransactionGuard>=0) {
if(!app.getActiveTransaction()
|| (!tmpName && app._activeTransactionTmpName))
{
AutoTransaction::AutoTransaction(const char* name, bool tmpName)
{
auto& app = GetApplication();
if (name && app._activeTransactionGuard >= 0) {
if (!app.getActiveTransaction() || (!tmpName && app._activeTransactionTmpName)) {
FC_LOG("auto transaction '" << name << "', " << tmpName);
tid = app.setActiveTransaction(name);
app._activeTransactionTmpName = tmpName;
@@ -52,166 +51,206 @@ AutoTransaction::AutoTransaction(const char *name, bool tmpName) {
// and any stack below. This is to support user setting active transaction
// before having any existing AutoTransaction on stack, or 'persist'
// transaction that can out live AutoTransaction.
if(app._activeTransactionGuard<0)
if (app._activeTransactionGuard < 0) {
--app._activeTransactionGuard;
else if(tid || app._activeTransactionGuard>0)
}
else if (tid || app._activeTransactionGuard > 0) {
++app._activeTransactionGuard;
else if(app.getActiveTransaction()) {
}
else if (app.getActiveTransaction()) {
FC_LOG("auto transaction disabled because of '" << app._activeTransactionName << "'");
--app._activeTransactionGuard;
} else
}
else {
++app._activeTransactionGuard;
}
FC_TRACE("construct auto Transaction " << app._activeTransactionGuard);
}
AutoTransaction::~AutoTransaction() {
auto &app = GetApplication();
AutoTransaction::~AutoTransaction()
{
auto& app = GetApplication();
FC_TRACE("before destruct auto Transaction " << app._activeTransactionGuard);
if(app._activeTransactionGuard<0)
if (app._activeTransactionGuard < 0) {
++app._activeTransactionGuard;
else if(!app._activeTransactionGuard) {
}
else if (!app._activeTransactionGuard) {
#ifdef FC_DEBUG
FC_ERR("Transaction guard error");
#endif
} else if(--app._activeTransactionGuard == 0) {
}
else if (--app._activeTransactionGuard == 0) {
try {
// We don't call close() here, because close() only closes
// transaction that we opened during construction time. However,
// when _activeTransactionGuard reaches zero here, we are supposed
// to close any transaction opened.
app.closeActiveTransaction();
} catch(Base::Exception &e) {
}
catch (Base::Exception& e) {
e.ReportException();
} catch(...)
{}
}
catch (...) {
}
}
FC_TRACE("destruct auto Transaction " << app._activeTransactionGuard);
}
void AutoTransaction::close(bool abort) {
if(tid || abort) {
GetApplication().closeActiveTransaction(abort,abort?0:tid);
void AutoTransaction::close(bool abort)
{
if (tid || abort) {
GetApplication().closeActiveTransaction(abort, abort ? 0 : tid);
tid = 0;
}
}
void AutoTransaction::setEnable(bool enable) {
auto &app = GetApplication();
if(!app._activeTransactionGuard)
void AutoTransaction::setEnable(bool enable)
{
auto& app = GetApplication();
if (!app._activeTransactionGuard) {
return;
if((enable && app._activeTransactionGuard>0)
|| (!enable && app._activeTransactionGuard<0))
}
if ((enable && app._activeTransactionGuard > 0)
|| (!enable && app._activeTransactionGuard < 0)) {
return;
}
app._activeTransactionGuard = -app._activeTransactionGuard;
FC_TRACE("toggle auto Transaction " << app._activeTransactionGuard);
if(!enable && app._activeTransactionTmpName) {
if (!enable && app._activeTransactionTmpName) {
bool close = true;
for(auto &v : app.DocMap) {
if(v.second->hasPendingTransaction()) {
for (auto& v : app.DocMap) {
if (v.second->hasPendingTransaction()) {
close = false;
break;
}
}
if(close)
if (close) {
app.closeActiveTransaction();
}
}
}
int Application::setActiveTransaction(const char *name, bool persist) {
if(!name || !name[0])
int Application::setActiveTransaction(const char* name, bool persist)
{
if (!name || !name[0]) {
name = "Command";
}
if(_activeTransactionGuard>0 && getActiveTransaction()) {
if(_activeTransactionTmpName) {
if (_activeTransactionGuard > 0 && getActiveTransaction()) {
if (_activeTransactionTmpName) {
FC_LOG("transaction rename to '" << name << "'");
for(auto &v : DocMap)
v.second->renameTransaction(name,_activeTransactionID);
} else {
if(persist)
for (auto& v : DocMap) {
v.second->renameTransaction(name, _activeTransactionID);
}
}
else {
if (persist) {
AutoTransaction::setEnable(false);
}
return 0;
}
} else if (_TransactionLock) {
if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG))
}
else if (_TransactionLock) {
if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) {
FC_WARN("Transaction locked, ignore new transaction '" << name << "'");
}
return 0;
} else {
}
else {
FC_LOG("set active transaction '" << name << "'");
_activeTransactionID = 0;
for(auto &v : DocMap)
for (auto& v : DocMap) {
v.second->_commitTransaction();
}
_activeTransactionID = Transaction::getNewID();
}
_activeTransactionTmpName = false;
_activeTransactionName = name;
if(persist)
if (persist) {
AutoTransaction::setEnable(false);
}
return _activeTransactionID;
}
const char *Application::getActiveTransaction(int *id) const {
const char* Application::getActiveTransaction(int* id) const
{
int tid = 0;
if(Transaction::getLastID() == _activeTransactionID)
if (Transaction::getLastID() == _activeTransactionID) {
tid = _activeTransactionID;
if (id)
}
if (id) {
*id = tid;
}
return tid ? _activeTransactionName.c_str() : nullptr;
}
void Application::closeActiveTransaction(bool abort, int id) {
if(!id) id = _activeTransactionID;
if(!id)
void Application::closeActiveTransaction(bool abort, int id)
{
if (!id) {
id = _activeTransactionID;
}
if (!id) {
return;
}
if(_activeTransactionGuard>0 && !abort) {
if (_activeTransactionGuard > 0 && !abort) {
FC_LOG("ignore close transaction");
return;
}
if(_TransactionLock) {
if(_TransactionClosed >= 0)
_TransactionLock = abort?-1:1;
FC_LOG("pending " << (abort?"abort":"close") << " transaction");
if (_TransactionLock) {
if (_TransactionClosed >= 0) {
_TransactionLock = abort ? -1 : 1;
}
FC_LOG("pending " << (abort ? "abort" : "close") << " transaction");
return;
}
FC_LOG("close transaction '" << _activeTransactionName << "' " << abort);
_activeTransactionID = 0;
TransactionSignaller signaller(abort,false);
for(auto &v : DocMap) {
if(v.second->getTransactionID(true) != id)
TransactionSignaller signaller(abort, false);
for (auto& v : DocMap) {
if (v.second->getTransactionID(true) != id) {
continue;
if(abort)
}
if (abort) {
v.second->_abortTransaction();
else
}
else {
v.second->_commitTransaction();
}
}
}
////////////////////////////////////////////////////////////////////////
TransactionLocker::TransactionLocker(bool lock)
:active(lock)
: active(lock)
{
if(lock)
if (lock) {
++_TransactionLock;
}
}
TransactionLocker::~TransactionLocker()
{
if(active) {
if (active) {
try {
activate(false);
return;
} catch (Base::Exception &e) {
}
catch (Base::Exception& e) {
e.ReportException();
} catch (Py::Exception &) {
}
catch (Py::Exception&) {
Base::PyException e;
e.ReportException();
} catch (std::exception &e) {
}
catch (std::exception& e) {
FC_ERR(e.what());
} catch (...) {
}
catch (...) {
}
FC_ERR("Exception when unlocking transaction");
}
@@ -219,26 +258,28 @@ TransactionLocker::~TransactionLocker()
void TransactionLocker::activate(bool enable)
{
if(active == enable)
if (active == enable) {
return;
}
active = enable;
if(active) {
if (active) {
++_TransactionLock;
return;
}
if(--_TransactionLock != 0)
if (--_TransactionLock != 0) {
return;
}
if(_TransactionClosed) {
bool abort = (_TransactionClosed<0);
if (_TransactionClosed) {
bool abort = (_TransactionClosed < 0);
_TransactionClosed = 0;
GetApplication().closeActiveTransaction(abort);
}
}
bool TransactionLocker::isLocked() {
bool TransactionLocker::isLocked()
{
return _TransactionLock > 0;
}

View File

@@ -26,15 +26,17 @@
#include <cstddef>
#include <FCGlobal.h>
namespace App {
namespace App
{
class Application;
/// Helper class to manager transaction (i.e. undo/redo)
class AppExport AutoTransaction {
class AppExport AutoTransaction
{
public:
/// Private new operator to prevent heap allocation
void* operator new (std::size_t) = delete;
void* operator new(std::size_t) = delete;
public:
/** Constructor
@@ -51,7 +53,7 @@ public:
* current active transaction until it reaches zero. It does not have any
* effect on aborting transaction, though.
*/
AutoTransaction(const char *name=nullptr, bool tmpName=false);
AutoTransaction(const char* name = nullptr, bool tmpName = false);
/** Destructor
*
@@ -67,7 +69,7 @@ public:
* transaction, if the current transaction ID matches the one created inside
* the constructor. For aborting, it will abort any current transaction
*/
void close(bool abort=false);
void close(bool abort = false);
/** Enable/Disable any AutoTransaction instance in the current stack
*
@@ -90,13 +92,13 @@ private:
* The helper class is used to protect some critical transaction from being
* closed prematurely, e.g. when deleting some object.
*/
class AppExport TransactionLocker {
class AppExport TransactionLocker
{
public:
/** Constructor
* @param lock: whether to activate the lock
*/
TransactionLocker(bool lock=true);
TransactionLocker(bool lock = true);
/** Destructor
* Unlock the transaction is this locker is active
@@ -114,7 +116,10 @@ public:
void activate(bool enable);
/// Check if the locker is active
bool isActive() const {return active;}
bool isActive() const
{
return active;
}
/// Check if transaction is being locked
static bool isLocked();
@@ -123,12 +128,12 @@ public:
public:
/// Private new operator to prevent heap allocation
void* operator new (std::size_t) = delete;
void* operator new(std::size_t) = delete;
private:
bool active;
};
} // namespace App
} // namespace App
#endif // APP_AUTOTRANSACTION_H
#endif // APP_AUTOTRANSACTION_H

View File

@@ -63,10 +63,12 @@ Branding::Branding()
bool Branding::readFile(const QString& fn)
{
QFile file(fn);
if (!file.open(QFile::ReadOnly))
if (!file.open(QFile::ReadOnly)) {
return false;
if (!evaluateXML(&file, domDocument))
}
if (!evaluateXML(&file, domDocument)) {
return false;
}
file.close();
return true;
}
@@ -81,22 +83,22 @@ Branding::XmlConfig Branding::getUserDefines() const
while (!child.isNull()) {
std::string name = child.localName().toLatin1().constData();
std::string value = child.text().toUtf8().constData();
if (std::find(filter.begin(), filter.end(), name) != filter.end())
if (std::find(filter.begin(), filter.end(), name) != filter.end()) {
cfg[name] = value;
}
child = child.nextSiblingElement();
}
}
return cfg;
}
bool Branding::evaluateXML(QIODevice *device, QDomDocument& xmlDocument)
bool Branding::evaluateXML(QIODevice* device, QDomDocument& xmlDocument)
{
QString errorStr;
int errorLine;
int errorColumn;
if (!xmlDocument.setContent(device, true, &errorStr, &errorLine,
&errorColumn)) {
if (!xmlDocument.setContent(device, true, &errorStr, &errorLine, &errorColumn)) {
return false;
}
@@ -106,8 +108,9 @@ bool Branding::evaluateXML(QIODevice *device, QDomDocument& xmlDocument)
}
else if (root.hasAttribute(QLatin1String("version"))) {
QString attr = root.attribute(QLatin1String("version"));
if (attr != QLatin1String("1.0"))
if (attr != QLatin1String("1.0")) {
return false;
}
}
return true;

View File

@@ -33,7 +33,8 @@
class QIODevice;
namespace App {
namespace App
{
class Branding
{
@@ -46,10 +47,10 @@ public:
private:
QVector<std::string> filter;
bool evaluateXML(QIODevice *device, QDomDocument& xmlDocument);
bool evaluateXML(QIODevice* device, QDomDocument& xmlDocument);
QDomDocument domDocument;
};
}
} // namespace App
#endif // APP_BRANDING_H
#endif // APP_BRANDING_H

View File

@@ -31,7 +31,7 @@ using namespace App;
namespace
{
static std::list<std::function<void()>> cleanup_funcs; // NOLINT
static std::list<std::function<void()>> cleanup_funcs; // NOLINT
}
void CleanupProcess::registerCleanup(const std::function<void()>& func)

View File

@@ -52,6 +52,6 @@ public:
static void callCleanup();
};
}
} // namespace App
#endif // APP_CLEANUPPROCESS_H

View File

@@ -23,7 +23,7 @@
#include "PreCompiled.h"
#ifndef _PreComp_
# include <memory>
#include <memory>
#endif
#include "ComplexGeoData.h"
@@ -50,10 +50,11 @@ std::string ComplexGeoDataPy::representation() const
return {"<ComplexGeoData object>"};
}
PyObject* ComplexGeoDataPy::getElementTypes(PyObject *args)
PyObject* ComplexGeoDataPy::getElementTypes(PyObject* args)
{
if (!PyArg_ParseTuple(args, ""))
if (!PyArg_ParseTuple(args, "")) {
return nullptr;
}
std::vector<const char*> types = getComplexGeoDataPtr()->getElementTypes();
Py::List list;
@@ -63,11 +64,12 @@ PyObject* ComplexGeoDataPy::getElementTypes(PyObject *args)
return Py::new_reference_to(list);
}
PyObject* ComplexGeoDataPy::countSubElements(PyObject *args)
PyObject* ComplexGeoDataPy::countSubElements(PyObject* args)
{
char *type;
if (!PyArg_ParseTuple(args, "s", &type))
char* type;
if (!PyArg_ParseTuple(args, "s", &type)) {
return nullptr;
}
try {
unsigned long count = getComplexGeoDataPtr()->countSubElements(type);
@@ -79,12 +81,13 @@ PyObject* ComplexGeoDataPy::countSubElements(PyObject *args)
}
}
PyObject* ComplexGeoDataPy::getFacesFromSubElement(PyObject *args)
PyObject* ComplexGeoDataPy::getFacesFromSubElement(PyObject* args)
{
char *type;
char* type;
unsigned long index;
if (!PyArg_ParseTuple(args, "sk", &type, &index))
if (!PyArg_ParseTuple(args, "sk", &type, &index)) {
return nullptr;
}
std::vector<Base::Vector3d> points;
std::vector<Base::Vector3d> normals;
@@ -100,27 +103,29 @@ PyObject* ComplexGeoDataPy::getFacesFromSubElement(PyObject *args)
Py::Tuple tuple(2);
Py::List vertex;
for (const auto & it : points)
for (const auto& it : points) {
vertex.append(Py::asObject(new Base::VectorPy(it)));
}
tuple.setItem(0, vertex);
Py::List facet;
for (const auto & it : facets) {
for (const auto& it : facets) {
Py::Tuple f(3);
f.setItem(0,Py::Int(int(it.I1)));
f.setItem(1,Py::Int(int(it.I2)));
f.setItem(2,Py::Int(int(it.I3)));
f.setItem(0, Py::Int(int(it.I1)));
f.setItem(1, Py::Int(int(it.I2)));
f.setItem(2, Py::Int(int(it.I3)));
facet.append(f);
}
tuple.setItem(1, facet);
return Py::new_reference_to(tuple);
}
PyObject* ComplexGeoDataPy::getLinesFromSubElement(PyObject *args)
PyObject* ComplexGeoDataPy::getLinesFromSubElement(PyObject* args)
{
char *type;
char* type;
int index;
if (!PyArg_ParseTuple(args, "si", &type, &index))
if (!PyArg_ParseTuple(args, "si", &type, &index)) {
return nullptr;
}
std::vector<Base::Vector3d> points;
std::vector<Data::ComplexGeoData::Line> lines;
@@ -135,25 +140,27 @@ PyObject* ComplexGeoDataPy::getLinesFromSubElement(PyObject *args)
Py::Tuple tuple(2);
Py::List vertex;
for (const auto & it : points)
for (const auto& it : points) {
vertex.append(Py::asObject(new Base::VectorPy(it)));
}
tuple.setItem(0, vertex);
Py::List line;
for (const auto & it : lines) {
for (const auto& it : lines) {
Py::Tuple l(2);
l.setItem(0,Py::Int((int)it.I1));
l.setItem(1,Py::Int((int)it.I2));
l.setItem(0, Py::Int((int)it.I1));
l.setItem(1, Py::Int((int)it.I2));
line.append(l);
}
tuple.setItem(1, line);
return Py::new_reference_to(tuple);
}
PyObject* ComplexGeoDataPy::getPoints(PyObject *args)
PyObject* ComplexGeoDataPy::getPoints(PyObject* args)
{
double accuracy = 0.05;
if (!PyArg_ParseTuple(args, "d", &accuracy))
if (!PyArg_ParseTuple(args, "d", &accuracy)) {
return nullptr;
}
std::vector<Base::Vector3d> points;
std::vector<Base::Vector3d> normals;
@@ -167,24 +174,25 @@ PyObject* ComplexGeoDataPy::getPoints(PyObject *args)
Py::Tuple tuple(2);
Py::List vertex;
for (const auto & it : points) {
for (const auto& it : points) {
vertex.append(Py::asObject(new Base::VectorPy(it)));
}
tuple.setItem(0, vertex);
Py::List normal;
for (const auto & it : normals) {
for (const auto& it : normals) {
normal.append(Py::asObject(new Base::VectorPy(it)));
}
tuple.setItem(1, normal);
return Py::new_reference_to(tuple);
}
PyObject* ComplexGeoDataPy::getLines(PyObject *args)
PyObject* ComplexGeoDataPy::getLines(PyObject* args)
{
double accuracy = 0.05;
if (!PyArg_ParseTuple(args, "d", &accuracy))
if (!PyArg_ParseTuple(args, "d", &accuracy)) {
return nullptr;
}
std::vector<Base::Vector3d> points;
std::vector<Data::ComplexGeoData::Line> lines;
@@ -198,25 +206,27 @@ PyObject* ComplexGeoDataPy::getLines(PyObject *args)
Py::Tuple tuple(2);
Py::List vertex;
for (const auto & it : points)
for (const auto& it : points) {
vertex.append(Py::asObject(new Base::VectorPy(it)));
}
tuple.setItem(0, vertex);
Py::List line;
for (const auto & it : lines) {
for (const auto& it : lines) {
Py::Tuple l(2);
l.setItem(0,Py::Int((int)it.I1));
l.setItem(1,Py::Int((int)it.I2));
l.setItem(0, Py::Int((int)it.I1));
l.setItem(1, Py::Int((int)it.I2));
line.append(l);
}
tuple.setItem(1, line);
return Py::new_reference_to(tuple);
}
PyObject* ComplexGeoDataPy::getFaces(PyObject *args)
PyObject* ComplexGeoDataPy::getFaces(PyObject* args)
{
double accuracy = 0.05;
if (!PyArg_ParseTuple(args, "d", &accuracy))
if (!PyArg_ParseTuple(args, "d", &accuracy)) {
return nullptr;
}
std::vector<Base::Vector3d> points;
std::vector<Data::ComplexGeoData::Facet> facets;
@@ -230,26 +240,28 @@ PyObject* ComplexGeoDataPy::getFaces(PyObject *args)
Py::Tuple tuple(2);
Py::List vertex;
for (const auto & it : points)
for (const auto& it : points) {
vertex.append(Py::asObject(new Base::VectorPy(it)));
}
tuple.setItem(0, vertex);
Py::List facet;
for (const auto & it : facets) {
for (const auto& it : facets) {
Py::Tuple f(3);
f.setItem(0,Py::Int((int)it.I1));
f.setItem(1,Py::Int((int)it.I2));
f.setItem(2,Py::Int((int)it.I3));
f.setItem(0, Py::Int((int)it.I1));
f.setItem(1, Py::Int((int)it.I2));
f.setItem(2, Py::Int((int)it.I3));
facet.append(f);
}
tuple.setItem(1, facet);
return Py::new_reference_to(tuple);
}
PyObject* ComplexGeoDataPy::applyTranslation(PyObject *args)
PyObject* ComplexGeoDataPy::applyTranslation(PyObject* args)
{
PyObject *obj;
if (!PyArg_ParseTuple(args, "O!", &(Base::VectorPy::Type),&obj))
PyObject* obj;
if (!PyArg_ParseTuple(args, "O!", &(Base::VectorPy::Type), &obj)) {
return nullptr;
}
try {
Base::Vector3d move = static_cast<Base::VectorPy*>(obj)->value();
@@ -262,11 +274,12 @@ PyObject* ComplexGeoDataPy::applyTranslation(PyObject *args)
}
}
PyObject* ComplexGeoDataPy::applyRotation(PyObject *args)
PyObject* ComplexGeoDataPy::applyRotation(PyObject* args)
{
PyObject *obj;
if (!PyArg_ParseTuple(args, "O!", &(Base::RotationPy::Type),&obj))
PyObject* obj;
if (!PyArg_ParseTuple(args, "O!", &(Base::RotationPy::Type), &obj)) {
return nullptr;
}
try {
Base::Rotation rot = static_cast<Base::RotationPy*>(obj)->value();
@@ -279,11 +292,12 @@ PyObject* ComplexGeoDataPy::applyRotation(PyObject *args)
}
}
PyObject* ComplexGeoDataPy::transformGeometry(PyObject *args)
PyObject* ComplexGeoDataPy::transformGeometry(PyObject* args)
{
PyObject *obj;
if (!PyArg_ParseTuple(args, "O!", &(Base::MatrixPy::Type),&obj))
PyObject* obj;
if (!PyArg_ParseTuple(args, "O!", &(Base::MatrixPy::Type), &obj)) {
return nullptr;
}
try {
Base::Matrix4D mat = static_cast<Base::MatrixPy*>(obj)->value();
@@ -377,17 +391,18 @@ PyObject* ComplexGeoDataPy::setElementName(PyObject* args, PyObject* kwds)
PyObject* pySid = Py_None;
PyObject* overwrite = Py_False;
const std::array<const char *,7> kwlist = {"element", "name", "postfix", "overwrite", "sid", "tag", nullptr};
const std::array<const char*, 7> kwlist =
{"element", "name", "postfix", "overwrite", "sid", "tag", nullptr};
if (!Wrapped_ParseTupleAndKeywords(args,
kwds,
"s|sssOOi",
kwlist,
&element,
&name,
&postfix,
&overwrite,
&pySid,
&tag)) {
kwds,
"s|sssOOi",
kwlist,
&element,
&name,
&postfix,
&overwrite,
&pySid,
&tag)) {
return NULL;
}
ElementIDRefs sids;
@@ -529,8 +544,9 @@ Py::Object ComplexGeoDataPy::getBoundBox() const
Py::Object ComplexGeoDataPy::getCenterOfGravity() const
{
Base::Vector3d center;
if (getComplexGeoDataPtr()->getCenterOfGravity(center))
if (getComplexGeoDataPtr()->getCenterOfGravity(center)) {
return Py::Vector(center);
}
throw Py::RuntimeError("Cannot get center of gravity");
}

View File

@@ -39,55 +39,60 @@ DocumentObjectExtension::DocumentObjectExtension()
DocumentObjectExtension::~DocumentObjectExtension() = default;
short int DocumentObjectExtension::extensionMustExecute() {
short int DocumentObjectExtension::extensionMustExecute()
{
return 0;
}
App::DocumentObjectExecReturn* DocumentObjectExtension::extensionExecute() {
App::DocumentObjectExecReturn* DocumentObjectExtension::extensionExecute()
{
return App::DocumentObject::StdReturn;
}
void DocumentObjectExtension::onExtendedSettingDocument() {
void DocumentObjectExtension::onExtendedSettingDocument()
{}
}
void DocumentObjectExtension::onExtendedDocumentRestored()
{}
void DocumentObjectExtension::onExtendedDocumentRestored() {
void DocumentObjectExtension::onExtendedSetupObject()
{}
}
void DocumentObjectExtension::onExtendedUnsetupObject()
{}
void DocumentObjectExtension::onExtendedSetupObject() {
PyObject* DocumentObjectExtension::getExtensionPyObject()
{
}
void DocumentObjectExtension::onExtendedUnsetupObject() {
}
PyObject* DocumentObjectExtension::getExtensionPyObject() {
if (ExtensionPythonObject.is(Py::_None())){
if (ExtensionPythonObject.is(Py::_None())) {
// ref counter is set to 1
ExtensionPythonObject = Py::Object(new DocumentObjectExtensionPy(this),true);
ExtensionPythonObject = Py::Object(new DocumentObjectExtensionPy(this), true);
}
return Py::new_reference_to(ExtensionPythonObject);
}
const DocumentObject* DocumentObjectExtension::getExtendedObject() const {
const DocumentObject* DocumentObjectExtension::getExtendedObject() const
{
assert(getExtendedContainer()->isDerivedFrom(DocumentObject::getClassTypeId()));
return static_cast<const DocumentObject*>(getExtendedContainer());
}
DocumentObject* DocumentObjectExtension::getExtendedObject() {
DocumentObject* DocumentObjectExtension::getExtendedObject()
{
assert(getExtendedContainer()->isDerivedFrom(DocumentObject::getClassTypeId()));
return static_cast<DocumentObject*>(getExtendedContainer());
}
bool DocumentObjectExtension::extensionGetSubObject(DocumentObject *&,
const char *, PyObject **, Base::Matrix4D *, bool, int) const
bool DocumentObjectExtension::extensionGetSubObject(DocumentObject*&,
const char*,
PyObject**,
Base::Matrix4D*,
bool,
int) const
{
return false;
}
@@ -97,8 +102,11 @@ bool DocumentObjectExtension::extensionGetSubObjects(std::vector<std::string>&,
return false;
}
bool DocumentObjectExtension::extensionGetLinkedObject(
DocumentObject *&, bool, Base::Matrix4D *, bool, int) const
bool DocumentObjectExtension::extensionGetLinkedObject(DocumentObject*&,
bool,
Base::Matrix4D*,
bool,
int) const
{
return false;
}

View File

@@ -26,10 +26,12 @@
#include "Extension.h"
namespace Base {
namespace Base
{
class Matrix4D;
}
namespace App {
namespace App
{
class DocumentObject;
class DocumentObjectExecReturn;
@@ -37,24 +39,23 @@ class DocumentObjectExecReturn;
* @brief Extension with special document object calls
*
*/
class AppExport DocumentObjectExtension : public App::Extension
class AppExport DocumentObjectExtension: public App::Extension
{
//The cass does not have properties itself, but it is important to provide the property access
//functions. see cpp file for details
// The cass does not have properties itself, but it is important to provide the property access
// functions. see cpp file for details
EXTENSION_PROPERTY_HEADER_WITH_OVERRIDE(App::DocumentObjectExtension);
public:
DocumentObjectExtension();
~DocumentObjectExtension() override;
DocumentObjectExtension ();
~DocumentObjectExtension () override;
App::DocumentObject* getExtendedObject();
App::DocumentObject* getExtendedObject();
const App::DocumentObject* getExtendedObject() const;
//override if execution is necessary
// override if execution is necessary
virtual short extensionMustExecute();
virtual App::DocumentObjectExecReturn *extensionExecute();
virtual App::DocumentObjectExecReturn* extensionExecute();
/// get called after setting the document
@@ -70,36 +71,55 @@ public:
/// returns the type name of the ViewProviderExtension which is automatically attached
/// to the viewprovider object when it is initiated
virtual const char* getViewProviderExtensionName() const {return "";}
virtual const char* getViewProviderExtensionName() const
{
return "";
}
/** Get the sub object by name
* @sa DocumentObject::getSubObject()
*
* @return Return turn if handled, the sub object is returned in \c ret
*/
virtual bool extensionGetSubObject(DocumentObject *&ret, const char *subname,
PyObject **pyObj, Base::Matrix4D *mat, bool transform, int depth) const;
virtual bool extensionGetSubObject(DocumentObject*& ret,
const char* subname,
PyObject** pyObj,
Base::Matrix4D* mat,
bool transform,
int depth) const;
/** Get name references of all sub objects
* @sa DocumentObject::getSubObjects()
*
* @return Return turn if handled, the sub object is returned in \c ret
*/
virtual bool extensionGetSubObjects(std::vector<std::string> &ret, int reason) const;
virtual bool extensionGetSubObjects(std::vector<std::string>& ret, int reason) const;
/** Get the linked object
* @sa DocumentObject::getLinkedObject()
*
* @return Return turn if handled, the linked object is returned in \c ret
*/
virtual bool extensionGetLinkedObject(DocumentObject *&ret, bool recursive,
Base::Matrix4D *mat, bool transform, int depth) const;
virtual bool extensionGetLinkedObject(DocumentObject*& ret,
bool recursive,
Base::Matrix4D* mat,
bool transform,
int depth) const;
virtual int extensionSetElementVisible(const char *, bool) {return -1;}
virtual int extensionIsElementVisible(const char *) {return -1;}
virtual bool extensionHasChildElement() const {return false;}
virtual int extensionSetElementVisible(const char*, bool)
{
return -1;
}
virtual int extensionIsElementVisible(const char*)
{
return -1;
}
virtual bool extensionHasChildElement() const
{
return false;
}
};
} //App
} // namespace App
#endif // APP_DOCUMENTOBJECTEXTENSION_H
#endif // APP_DOCUMENTOBJECTEXTENSION_H

View File

@@ -1,13 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<GenerateModel xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="generateMetaModel_Module.xsd">
<PythonExport
Father="ExtensionPy"
Name="DocumentObjectExtensionPy"
TwinPointer="DocumentObjectExtension"
Twin="DocumentObjectExtension"
Include="App/DocumentObjectExtension.h"
Namespace="App"
FatherInclude="App/ExtensionPy.h"
<PythonExport
Father="ExtensionPy"
Name="DocumentObjectExtensionPy"
TwinPointer="DocumentObjectExtension"
Twin="DocumentObjectExtension"
Include="App/DocumentObjectExtension.h"
Namespace="App"
FatherInclude="App/ExtensionPy.h"
FatherNamespace="App">
<Documentation>
<Author Licence="LGPL" Name="Stefan Troeger" EMail="stefantroeger@gmx.net" />

View File

@@ -35,12 +35,12 @@ std::string DocumentObjectExtensionPy::representation() const
return {"<document object extension>"};
}
PyObject *DocumentObjectExtensionPy::getCustomAttributes(const char* /*attr*/) const
PyObject* DocumentObjectExtensionPy::getCustomAttributes(const char* /*attr*/) const
{
return nullptr;
}
int DocumentObjectExtensionPy::setCustomAttributes(const char* /*attr*/, PyObject * /*obj*/)
int DocumentObjectExtensionPy::setCustomAttributes(const char* /*attr*/, PyObject* /*obj*/)
{
return 0;
}

View File

@@ -32,8 +32,11 @@ PROPERTY_SOURCE(App::DocumentObjectFileIncluded, App::DocumentObject)
DocumentObjectFileIncluded::DocumentObjectFileIncluded()
{
ADD_PROPERTY_TYPE(File,(nullptr),"",(App::PropertyType)(Prop_None),"File to include into Project File");
ADD_PROPERTY_TYPE(File,
(nullptr),
"",
(App::PropertyType)(Prop_None),
"File to include into Project File");
}
DocumentObjectFileIncluded::~DocumentObjectFileIncluded() = default;

View File

@@ -31,7 +31,7 @@
namespace App
{
class AppExport DocumentObjectFileIncluded : public DocumentObject
class AppExport DocumentObjectFileIncluded: public DocumentObject
{
PROPERTY_HEADER_WITH_OVERRIDE(App::DocumentObjectFileIncluded);
@@ -42,16 +42,16 @@ public:
/// returns the type name of the ViewProvider
const char* getViewProviderName() const override {
const char* getViewProviderName() const override
{
return "Gui::ViewProviderDocumentObject";
}
/// Properties
PropertyFileIncluded File;
};
} //namespace App
} // namespace App
#endif // APP_DOCUMENTOBJECTFILEINCLUDED_H
#endif // APP_DOCUMENTOBJECTFILEINCLUDED_H

View File

@@ -31,19 +31,22 @@ using namespace App;
PROPERTY_SOURCE_WITH_EXTENSIONS(App::DocumentObjectGroup, App::DocumentObject)
DocumentObjectGroup::DocumentObjectGroup(): DocumentObject(), GroupExtension() {
DocumentObjectGroup::DocumentObjectGroup()
: DocumentObject()
, GroupExtension()
{
GroupExtension::initExtension(this);
_GroupTouched.setStatus(App::Property::Output,true);
_GroupTouched.setStatus(App::Property::Output, true);
}
DocumentObjectGroup::~DocumentObjectGroup() = default;
PyObject *DocumentObjectGroup::getPyObject()
PyObject* DocumentObjectGroup::getPyObject()
{
if (PythonObject.is(Py::_None())){
if (PythonObject.is(Py::_None())) {
// ref counter is set to 1
PythonObject = Py::Object(new DocumentObjectGroupPy(this),true);
PythonObject = Py::Object(new DocumentObjectGroupPy(this), true);
}
return Py::new_reference_to(PythonObject);
}
@@ -51,17 +54,22 @@ PyObject *DocumentObjectGroup::getPyObject()
// Python feature ---------------------------------------------------------
namespace App {
namespace App
{
/// @cond DOXERR
PROPERTY_SOURCE_TEMPLATE(App::DocumentObjectGroupPython, App::DocumentObjectGroup)
template<> const char* App::DocumentObjectGroupPython::getViewProviderName() const {
template<>
const char* App::DocumentObjectGroupPython::getViewProviderName() const
{
return "Gui::ViewProviderDocumentObjectGroupPython";
}
template<> PyObject* App::DocumentObjectGroupPython::getPyObject() {
template<>
PyObject* App::DocumentObjectGroupPython::getPyObject()
{
if (PythonObject.is(Py::_None())) {
// ref counter is set to 1
PythonObject = Py::Object(new FeaturePythonPyT<App::DocumentObjectGroupPy>(this),true);
PythonObject = Py::Object(new FeaturePythonPyT<App::DocumentObjectGroupPy>(this), true);
}
return Py::new_reference_to(PythonObject);
}
@@ -69,4 +77,4 @@ template<> PyObject* App::DocumentObjectGroupPython::getPyObject() {
// explicit template instantiation
template class AppExport FeaturePythonT<App::DocumentObjectGroup>;
}
} // namespace App

View File

@@ -32,7 +32,8 @@
namespace App
{
class AppExport DocumentObjectGroup : public DocumentObject, public GroupExtension {
class AppExport DocumentObjectGroup: public DocumentObject, public GroupExtension
{
PROPERTY_HEADER_WITH_EXTENSIONS(App::DocumentObjectGroup);
@@ -42,17 +43,18 @@ public:
~DocumentObjectGroup() override;
/// returns the type name of the ViewProvider
const char* getViewProviderName() const override {
const char* getViewProviderName() const override
{
return "Gui::ViewProviderDocumentObjectGroup";
}
PyObject *getPyObject() override;
PyObject* getPyObject() override;
};
using DocumentObjectGroupPython = App::FeaturePythonT<DocumentObjectGroup>;
} //namespace App
} // namespace App
#endif // APP_DOCUMENTOBJECTGROUP_H
#endif // APP_DOCUMENTOBJECTGROUP_H

View File

@@ -1,13 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<GenerateModel xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="generateMetaModel_Module.xsd">
<PythonExport
Father="DocumentObjectPy"
Name="DocumentObjectGroupPy"
Twin="DocumentObjectGroup"
TwinPointer="DocumentObjectGroup"
Include="App/DocumentObjectGroup.h"
Namespace="App"
FatherInclude="App/DocumentObjectPy.h"
<PythonExport
Father="DocumentObjectPy"
Name="DocumentObjectGroupPy"
Twin="DocumentObjectGroup"
TwinPointer="DocumentObjectGroup"
Include="App/DocumentObjectGroup.h"
Namespace="App"
FatherInclude="App/DocumentObjectPy.h"
FatherNamespace="App">
<Documentation>
<Author Licence="LGPL" Name="Werner Mayer" EMail="wmayer@users.sourceforge.net" />

View File

@@ -37,7 +37,7 @@ std::string DocumentObjectGroupPy::representation() const
return {"<group object>"};
}
PyObject *DocumentObjectGroupPy::getCustomAttributes(const char* /*attr*/) const
PyObject* DocumentObjectGroupPy::getCustomAttributes(const char* /*attr*/) const
{
return nullptr;
}
@@ -46,4 +46,3 @@ int DocumentObjectGroupPy::setCustomAttributes(const char* /*attr*/, PyObject* /
{
return 0;
}

View File

@@ -1,13 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<GenerateModel xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="generateMetaModel_Module.xsd">
<PythonExport
Father="ExtensionContainerPy"
Name="DocumentObjectPy"
Twin="DocumentObject"
TwinPointer="DocumentObject"
Include="App/DocumentObject.h"
Namespace="App"
FatherInclude="App/ExtensionContainerPy.h"
<PythonExport
Father="ExtensionContainerPy"
Name="DocumentObjectPy"
Twin="DocumentObject"
TwinPointer="DocumentObject"
Include="App/DocumentObject.h"
Namespace="App"
FatherInclude="App/ExtensionContainerPy.h"
FatherNamespace="App">
<Documentation>
<Author Licence="LGPL" Name="Juergen Riegel" EMail="FreeCAD@juergen-riegel.net" />
@@ -95,7 +95,7 @@ referencing subobject.
PyObject: return a python binding object for the (sub)object referenced in
each 'subname' The actual type of 'PyObject' is implementation dependent.
For Part::Feature compatible objects, this will be of type TopoShapePy and
pre-transformed by accumulated transformation matrix along the object path.
pre-transformed by accumulated transformation matrix along the object path.
DocObject: return the document object referenced in subname, if 'matrix' is
None. Or, return a tuple (object, matrix) for each 'subname' and 'matrix' is
@@ -168,15 +168,15 @@ Return -1 if element visibility is not supported or element not found, 0 if invi
</Methode>
<Methode Name="getParentGroup">
<Documentation>
<UserDocu>Returns the group the object is in or None if it is not part of a group.
Note that an object can only be in a single group, hence only a single return
<UserDocu>Returns the group the object is in or None if it is not part of a group.
Note that an object can only be in a single group, hence only a single return
value.</UserDocu>
</Documentation>
</Methode>
<Methode Name="getParentGeoFeatureGroup">
<Documentation>
<UserDocu>Returns the GeoFeatureGroup, and hence the local coordinate system, the object
is in or None if it is not part of a group. Note that an object can only be
<UserDocu>Returns the GeoFeatureGroup, and hence the local coordinate system, the object
is in or None if it is not part of a group. Note that an object can only be
in a single group, hence only a single return value.</UserDocu>
</Documentation>
</Methode>

View File

@@ -76,7 +76,7 @@ Py::Object DocumentObjectPy::getDocument() const
}
}
PyObject* DocumentObjectPy::isAttachedToDocument(PyObject *args)
PyObject* DocumentObjectPy::isAttachedToDocument(PyObject* args)
{
if (!PyArg_ParseTuple(args, "")) {
return nullptr;
@@ -87,27 +87,52 @@ PyObject* DocumentObjectPy::isAttachedToDocument(PyObject *args)
return Py::new_reference_to(Py::Boolean(ok));
}
PyObject* DocumentObjectPy::addProperty(PyObject *args, PyObject *kwd)
PyObject* DocumentObjectPy::addProperty(PyObject* args, PyObject* kwd)
{
char *sType,*sName=nullptr,*sGroup=nullptr,*sDoc=nullptr;
short attr=0;
char *sType, *sName = nullptr, *sGroup = nullptr, *sDoc = nullptr;
short attr = 0;
std::string sDocStr;
PyObject *ro = Py_False, *hd = Py_False;
PyObject* enumVals = nullptr;
const std::array<const char *, 9> kwlist {"type","name","group","doc","attr","read_only","hidden","enum_vals",nullptr};
if (!Base::Wrapped_ParseTupleAndKeywords(
args, kwd, "ss|sethO!O!O", kwlist, &sType, &sName, &sGroup, "utf-8",
&sDoc, &attr, &PyBool_Type, &ro, &PyBool_Type, &hd, &enumVals))
const std::array<const char*, 9> kwlist {"type",
"name",
"group",
"doc",
"attr",
"read_only",
"hidden",
"enum_vals",
nullptr};
if (!Base::Wrapped_ParseTupleAndKeywords(args,
kwd,
"ss|sethO!O!O",
kwlist,
&sType,
&sName,
&sGroup,
"utf-8",
&sDoc,
&attr,
&PyBool_Type,
&ro,
&PyBool_Type,
&hd,
&enumVals)) {
return nullptr;
}
if (sDoc) {
sDocStr = sDoc;
PyMem_Free(sDoc);
}
Property *prop = getDocumentObjectPtr()->
addDynamicProperty(sType,sName,sGroup,sDocStr.c_str(),attr,
Base::asBoolean(ro), Base::asBoolean(hd));
Property* prop = getDocumentObjectPtr()->addDynamicProperty(sType,
sName,
sGroup,
sDocStr.c_str(),
attr,
Base::asBoolean(ro),
Base::asBoolean(hd));
// enum support
auto* propEnum = dynamic_cast<App::PropertyEnumeration*>(prop);
@@ -118,26 +143,28 @@ PyObject* DocumentObjectPy::addProperty(PyObject *args, PyObject *kwd)
return Py::new_reference_to(this);
}
PyObject* DocumentObjectPy::removeProperty(PyObject *args)
PyObject* DocumentObjectPy::removeProperty(PyObject* args)
{
char *sName;
if (!PyArg_ParseTuple(args, "s", &sName))
char* sName;
if (!PyArg_ParseTuple(args, "s", &sName)) {
return nullptr;
}
bool ok = getDocumentObjectPtr()->removeDynamicProperty(sName);
return Py_BuildValue("O", (ok ? Py_True : Py_False));
}
PyObject* DocumentObjectPy::supportedProperties(PyObject *args)
PyObject* DocumentObjectPy::supportedProperties(PyObject* args)
{
if (!PyArg_ParseTuple(args, ""))
if (!PyArg_ParseTuple(args, "")) {
return nullptr;
}
std::vector<Base::Type> ary;
Base::Type::getAllDerivedFrom(App::Property::getClassTypeId(), ary);
Py::List res;
for (auto & it : ary) {
Base::BaseClass *data = static_cast<Base::BaseClass*>(it.createInstance());
for (auto& it : ary) {
Base::BaseClass* data = static_cast<Base::BaseClass*>(it.createInstance());
if (data) {
delete data;
res.append(Py::String(it.getName()));
@@ -146,19 +173,21 @@ PyObject* DocumentObjectPy::supportedProperties(PyObject *args)
return Py::new_reference_to(res);
}
PyObject* DocumentObjectPy::touch(PyObject * args)
PyObject* DocumentObjectPy::touch(PyObject* args)
{
char *propName = nullptr;
if (!PyArg_ParseTuple(args, "|s",&propName))
char* propName = nullptr;
if (!PyArg_ParseTuple(args, "|s", &propName)) {
return nullptr;
if(propName) {
if(!propName[0]) {
}
if (propName) {
if (!propName[0]) {
getDocumentObjectPtr()->touch(true);
Py_Return;
}
auto prop = getDocumentObjectPtr()->getPropertyByName(propName);
if(!prop)
if (!prop) {
throw Py::RuntimeError("Property not found");
}
prop->touch();
Py_Return;
}
@@ -167,18 +196,20 @@ PyObject* DocumentObjectPy::touch(PyObject * args)
Py_Return;
}
PyObject* DocumentObjectPy::purgeTouched(PyObject * args)
PyObject* DocumentObjectPy::purgeTouched(PyObject* args)
{
if (!PyArg_ParseTuple(args, ""))
if (!PyArg_ParseTuple(args, "")) {
return nullptr;
}
getDocumentObjectPtr()->purgeTouched();
Py_Return;
}
PyObject* DocumentObjectPy::enforceRecompute(PyObject * args)
PyObject* DocumentObjectPy::enforceRecompute(PyObject* args)
{
if (!PyArg_ParseTuple(args, ""))
if (!PyArg_ParseTuple(args, "")) {
return nullptr;
}
getDocumentObjectPtr()->enforceRecompute();
Py_Return;
}
@@ -207,13 +238,13 @@ Py::List DocumentObjectPy::getState() const
uptodate = false;
list.append(Py::String("Restore"));
}
if (object->testStatus(App::Expand)){
if (object->testStatus(App::Expand)) {
list.append(Py::String("Expanded"));
}
if (object->testStatus(App::PartialObject)){
if (object->testStatus(App::PartialObject)) {
list.append(Py::String("Partial"));
}
if (object->testStatus(App::ObjImporting)){
if (object->testStatus(App::ObjImporting)) {
list.append(Py::String("Importing"));
}
if (uptodate) {
@@ -225,7 +256,7 @@ Py::List DocumentObjectPy::getState() const
Py::Object DocumentObjectPy::getViewObject() const
{
try {
PyObject *dict = PySys_GetObject("modules");
PyObject* dict = PySys_GetObject("modules");
if (!dict) {
return Py::None();
}
@@ -237,12 +268,13 @@ Py::Object DocumentObjectPy::getViewObject() const
}
// double-check that the module doesn't have a null pointer
Py::Module module(PyImport_ImportModule("FreeCADGui"),true);
Py::Module module(PyImport_ImportModule("FreeCADGui"), true);
if (module.isNull() || !module.hasAttr("getDocument")) {
// in v0.14+, the GUI module can be loaded in console mode (but doesn't have all its document methods)
// in v0.14+, the GUI module can be loaded in console mode (but doesn't have all its
// document methods)
return Py::None();
}
if(!getDocumentObjectPtr()->getDocument()) {
if (!getDocumentObjectPtr()->getDocument()) {
throw Py::RuntimeError("Object has no document");
}
const char* internalName = getDocumentObjectPtr()->getNameInDocument();
@@ -266,7 +298,7 @@ Py::Object DocumentObjectPy::getViewObject() const
return Py::None();
}
// FreeCADGui is loaded, so there must be wrong something else
throw; // re-throw
throw; // re-throw
}
}
@@ -275,8 +307,9 @@ Py::List DocumentObjectPy::getInList() const
Py::List ret;
std::vector<DocumentObject*> list = getDocumentObjectPtr()->getInList();
for (auto It : list)
for (auto It : list) {
ret.append(Py::Object(It->getPyObject(), true));
}
return ret;
}
@@ -287,8 +320,9 @@ Py::List DocumentObjectPy::getInListRecursive() const
try {
std::vector<DocumentObject*> list = getDocumentObjectPtr()->getInListRecursive();
for (auto It : list)
for (auto It : list) {
ret.append(Py::Object(It->getPyObject(), true));
}
}
catch (const Base::Exception& e) {
throw Py::IndexError(e.what());
@@ -301,8 +335,9 @@ Py::List DocumentObjectPy::getOutList() const
Py::List ret;
std::vector<DocumentObject*> list = getDocumentObjectPtr()->getOutList();
for (auto It : list)
for (auto It : list) {
ret.append(Py::Object(It->getPyObject(), true));
}
return ret;
}
@@ -314,8 +349,9 @@ Py::List DocumentObjectPy::getOutListRecursive() const
std::vector<DocumentObject*> list = getDocumentObjectPtr()->getOutListRecursive();
// create the python list for the output
for (auto It : list)
for (auto It : list) {
ret.append(Py::Object(It->getPyObject(), true));
}
}
catch (const Base::Exception& e) {
throw Py::IndexError(e.what());
@@ -324,14 +360,15 @@ Py::List DocumentObjectPy::getOutListRecursive() const
return ret;
}
PyObject* DocumentObjectPy::setExpression(PyObject * args)
PyObject* DocumentObjectPy::setExpression(PyObject* args)
{
char * path = nullptr;
PyObject * expr;
char * comment = nullptr;
char* path = nullptr;
PyObject* expr;
char* comment = nullptr;
if (!PyArg_ParseTuple(args, "sO|s", &path, &expr, &comment))
if (!PyArg_ParseTuple(args, "sO|s", &path, &expr, &comment)) {
return nullptr;
}
App::ObjectIdentifier p(ObjectIdentifier::parse(getDocumentObjectPtr(), path));
@@ -340,10 +377,11 @@ PyObject* DocumentObjectPy::setExpression(PyObject * args)
Py_Return;
}
else if (PyUnicode_Check(expr)) {
const char * exprStr = PyUnicode_AsUTF8(expr);
const char* exprStr = PyUnicode_AsUTF8(expr);
std::shared_ptr<Expression> shared_expr(Expression::parse(getDocumentObjectPtr(), exprStr));
if (shared_expr && comment)
if (shared_expr && comment) {
shared_expr->comment = comment;
}
getDocumentObjectPtr()->setExpression(p, shared_expr);
Py_Return;
@@ -352,22 +390,24 @@ PyObject* DocumentObjectPy::setExpression(PyObject * args)
throw Py::TypeError("String or None expected.");
}
PyObject* DocumentObjectPy::clearExpression(PyObject * args)
PyObject* DocumentObjectPy::clearExpression(PyObject* args)
{
char * path = nullptr;
if (!PyArg_ParseTuple(args, "s", &path))
char* path = nullptr;
if (!PyArg_ParseTuple(args, "s", &path)) {
return nullptr;
}
App::ObjectIdentifier p(ObjectIdentifier::parse(getDocumentObjectPtr(), path));
getDocumentObjectPtr()->clearExpression(p);
Py_Return;
}
PyObject* DocumentObjectPy::evalExpression(PyObject *self, PyObject * args)
PyObject* DocumentObjectPy::evalExpression(PyObject* self, PyObject* args)
{
const char *expr;
if (!PyArg_ParseTuple(args, "s", &expr))
const char* expr;
if (!PyArg_ParseTuple(args, "s", &expr)) {
return nullptr;
}
// HINT:
// The standard behaviour of Python for class methods is to always pass the class
@@ -385,19 +425,23 @@ PyObject* DocumentObjectPy::evalExpression(PyObject *self, PyObject * args)
obj = static_cast<DocumentObjectPy*>(self)->getDocumentObjectPtr();
}
PY_TRY {
PY_TRY
{
std::shared_ptr<Expression> shared_expr(Expression::parse(obj, expr));
if (shared_expr)
if (shared_expr) {
return Py::new_reference_to(shared_expr->getPyValue());
}
Py_Return;
} PY_CATCH
}
PY_CATCH
}
PyObject* DocumentObjectPy::recompute(PyObject *args)
PyObject* DocumentObjectPy::recompute(PyObject* args)
{
PyObject *recursive = Py_False;
if (!PyArg_ParseTuple(args, "|O!", &PyBool_Type, &recursive))
PyObject* recursive = Py_False;
if (!PyArg_ParseTuple(args, "|O!", &PyBool_Type, &recursive)) {
return nullptr;
}
try {
bool ok = getDocumentObjectPtr()->recomputeFeature(Base::asBoolean(recursive));
@@ -408,10 +452,11 @@ PyObject* DocumentObjectPy::recompute(PyObject *args)
}
}
PyObject* DocumentObjectPy::isValid(PyObject *args)
PyObject* DocumentObjectPy::isValid(PyObject* args)
{
if (!PyArg_ParseTuple(args, ""))
if (!PyArg_ParseTuple(args, "")) {
return nullptr;
}
try {
bool ok = getDocumentObjectPtr()->isValid();
@@ -422,10 +467,11 @@ PyObject* DocumentObjectPy::isValid(PyObject *args)
}
}
PyObject* DocumentObjectPy::getStatusString(PyObject *args)
PyObject* DocumentObjectPy::getStatusString(PyObject* args)
{
if (!PyArg_ParseTuple(args, ""))
if (!PyArg_ParseTuple(args, "")) {
return nullptr;
}
try {
Py::String text(getDocumentObjectPtr()->getStatusString());
@@ -436,9 +482,10 @@ PyObject* DocumentObjectPy::getStatusString(PyObject *args)
}
}
PyObject* DocumentObjectPy::getSubObject(PyObject *args, PyObject *keywds)
PyObject* DocumentObjectPy::getSubObject(PyObject* args, PyObject* keywds)
{
enum class ReturnType {
enum class ReturnType
{
PyObject = 0,
DocObject = 1,
DocAndPyObject = 2,
@@ -448,19 +495,33 @@ PyObject* DocumentObjectPy::getSubObject(PyObject *args, PyObject *keywds)
LinkAndMatrix = 6
};
PyObject *obj;
PyObject* obj;
short retType = 0;
PyObject *pyMat = nullptr;
PyObject *doTransform = Py_True;
PyObject* pyMat = nullptr;
PyObject* doTransform = Py_True;
short depth = 0;
static const std::array<const char *, 6> kwlist {"subname", "retType", "matrix", "transform", "depth", nullptr};
if (!Base::Wrapped_ParseTupleAndKeywords(args, keywds, "O|hO!O!h", kwlist, &obj, &retType, &Base::MatrixPy::Type,
&pyMat, &PyBool_Type, &doTransform, &depth)) {
static const std::array<const char*, 6> kwlist {"subname",
"retType",
"matrix",
"transform",
"depth",
nullptr};
if (!Base::Wrapped_ParseTupleAndKeywords(args,
keywds,
"O|hO!O!h",
kwlist,
&obj,
&retType,
&Base::MatrixPy::Type,
&pyMat,
&PyBool_Type,
&doTransform,
&depth)) {
return nullptr;
}
if (retType < 0 || static_cast<size_t> (retType) > kwlist.size()) {
if (retType < 0 || static_cast<size_t>(retType) > kwlist.size()) {
PyErr_SetString(PyExc_ValueError, "invalid retType, can only be integer 0~6");
return nullptr;
}
@@ -493,12 +554,15 @@ PyObject* DocumentObjectPy::getSubObject(PyObject *args, PyObject *keywds)
bool transform = Base::asBoolean(doTransform);
struct SubInfo {
App::DocumentObject *sobj{nullptr};
struct SubInfo
{
App::DocumentObject* sobj {nullptr};
Py::Object obj;
Py::Object pyObj;
Base::Matrix4D mat;
explicit SubInfo(const Base::Matrix4D &mat) : mat(mat){}
explicit SubInfo(const Base::Matrix4D& mat)
: mat(mat)
{}
};
Base::Matrix4D mat;
@@ -506,50 +570,66 @@ PyObject* DocumentObjectPy::getSubObject(PyObject *args, PyObject *keywds)
mat = *static_cast<Base::MatrixPy*>(pyMat)->getMatrixPtr();
}
PY_TRY {
PY_TRY
{
std::vector<SubInfo> ret;
for(const auto &sub : subs) {
for (const auto& sub : subs) {
ret.emplace_back(mat);
auto &info = ret.back();
PyObject *pyObj = nullptr;
auto& info = ret.back();
PyObject* pyObj = nullptr;
info.sobj = getDocumentObjectPtr()->getSubObject(sub.c_str(),
retEnum != ReturnType::PyObject &&
retEnum != ReturnType::DocAndPyObject ? nullptr : &pyObj,
&info.mat, transform, depth);
if (pyObj)
info.sobj = getDocumentObjectPtr()->getSubObject(
sub.c_str(),
retEnum != ReturnType::PyObject && retEnum != ReturnType::DocAndPyObject ? nullptr
: &pyObj,
&info.mat,
transform,
depth);
if (pyObj) {
info.pyObj = Py::asObject(pyObj);
if (info.sobj)
}
if (info.sobj) {
info.obj = Py::asObject(info.sobj->getPyObject());
}
}
if (ret.empty())
if (ret.empty()) {
Py_Return;
}
auto getReturnValue = [retEnum, pyMat](SubInfo& ret) -> Py::Object {
if (retEnum == ReturnType::PyObject)
if (retEnum == ReturnType::PyObject) {
return ret.pyObj;
else if (retEnum == ReturnType::DocObject && !pyMat)
}
else if (retEnum == ReturnType::DocObject && !pyMat) {
return ret.obj;
else if (!ret.sobj)
}
else if (!ret.sobj) {
return Py::None();
else if (retEnum == ReturnType::Placement)
}
else if (retEnum == ReturnType::Placement) {
return Py::Placement(Base::Placement(ret.mat));
else if (retEnum == ReturnType::Matrix)
}
else if (retEnum == ReturnType::Matrix) {
return Py::Matrix(ret.mat);
else if (retEnum == ReturnType::LinkAndPlacement || retEnum == ReturnType::LinkAndMatrix) {
}
else if (retEnum == ReturnType::LinkAndPlacement
|| retEnum == ReturnType::LinkAndMatrix) {
ret.sobj->getLinkedObject(true, &ret.mat, false);
if (retEnum == ReturnType::LinkAndPlacement)
if (retEnum == ReturnType::LinkAndPlacement) {
return Py::Placement(Base::Placement(ret.mat));
else
}
else {
return Py::Matrix(ret.mat);
}
}
else {
Py::Tuple rret(retEnum == ReturnType::DocObject ? 2 : 3);
rret.setItem(0, ret.obj);
rret.setItem(1, Py::asObject(new Base::MatrixPy(ret.mat)));
if (retEnum != ReturnType::DocObject)
if (retEnum != ReturnType::DocObject) {
rret.setItem(2, ret.pyObj);
}
return rret;
}
};
@@ -559,7 +639,7 @@ PyObject* DocumentObjectPy::getSubObject(PyObject *args, PyObject *keywds)
}
Py::Tuple tuple(ret.size());
for(size_t i=0; i<ret.size(); ++i) {
for (size_t i = 0; i < ret.size(); ++i) {
tuple.setItem(i, getReturnValue(ret[i]));
}
return Py::new_reference_to(tuple);
@@ -567,62 +647,90 @@ PyObject* DocumentObjectPy::getSubObject(PyObject *args, PyObject *keywds)
PY_CATCH
}
PyObject* DocumentObjectPy::getSubObjectList(PyObject *args) {
const char *subname;
if (!PyArg_ParseTuple(args, "s", &subname))
return nullptr;
Py::List res;
PY_TRY {
for(auto o : getDocumentObjectPtr()->getSubObjectList(subname))
res.append(Py::asObject(o->getPyObject()));
return Py::new_reference_to(res);
}PY_CATCH
}
PyObject* DocumentObjectPy::getSubObjects(PyObject *args) {
int reason = 0;
if (!PyArg_ParseTuple(args, "|i", &reason))
return nullptr;
PY_TRY {
auto names = getDocumentObjectPtr()->getSubObjects(reason);
Py::Tuple pyObjs(names.size());
for(size_t i=0;i<names.size();++i)
pyObjs.setItem(i,Py::String(names[i]));
return Py::new_reference_to(pyObjs);
}PY_CATCH;
}
PyObject* DocumentObjectPy::getLinkedObject(PyObject *args, PyObject *keywds)
PyObject* DocumentObjectPy::getSubObjectList(PyObject* args)
{
PyObject *recursive = Py_True;
PyObject *pyMat = Py_None;
PyObject *transform = Py_True;
short depth = 0;
static const std::array<const char *, 5> kwlist {"recursive","matrix","transform","depth", nullptr};
if (!Base::Wrapped_ParseTupleAndKeywords(args, keywds, "|O!OO!h", kwlist,
&PyBool_Type, &recursive, &pyMat, &PyBool_Type, &transform, &depth)) {
const char* subname;
if (!PyArg_ParseTuple(args, "s", &subname)) {
return nullptr;
}
Py::List res;
PY_TRY
{
for (auto o : getDocumentObjectPtr()->getSubObjectList(subname)) {
res.append(Py::asObject(o->getPyObject()));
}
return Py::new_reference_to(res);
}
PY_CATCH
}
PyObject* DocumentObjectPy::getSubObjects(PyObject* args)
{
int reason = 0;
if (!PyArg_ParseTuple(args, "|i", &reason)) {
return nullptr;
}
PY_TRY {
Base::PyTypeCheck(&pyMat, &Base::MatrixPy::Type, "expect argument 'matrix' to be of type Base.Matrix");
PY_TRY
{
auto names = getDocumentObjectPtr()->getSubObjects(reason);
Py::Tuple pyObjs(names.size());
for (size_t i = 0; i < names.size(); ++i) {
pyObjs.setItem(i, Py::String(names[i]));
}
return Py::new_reference_to(pyObjs);
}
PY_CATCH;
}
PyObject* DocumentObjectPy::getLinkedObject(PyObject* args, PyObject* keywds)
{
PyObject* recursive = Py_True;
PyObject* pyMat = Py_None;
PyObject* transform = Py_True;
short depth = 0;
static const std::array<const char*, 5> kwlist {"recursive",
"matrix",
"transform",
"depth",
nullptr};
if (!Base::Wrapped_ParseTupleAndKeywords(args,
keywds,
"|O!OO!h",
kwlist,
&PyBool_Type,
&recursive,
&pyMat,
&PyBool_Type,
&transform,
&depth)) {
return nullptr;
}
PY_TRY
{
Base::PyTypeCheck(&pyMat,
&Base::MatrixPy::Type,
"expect argument 'matrix' to be of type Base.Matrix");
Base::Matrix4D _mat;
Base::Matrix4D *mat = nullptr;
Base::Matrix4D* mat = nullptr;
if (pyMat) {
_mat = *static_cast<Base::MatrixPy*>(pyMat)->getMatrixPtr();
mat = &_mat;
}
auto linked = getDocumentObjectPtr()->getLinkedObject(
Base::asBoolean(recursive), mat, Base::asBoolean(transform), depth);
if (!linked)
auto linked = getDocumentObjectPtr()->getLinkedObject(Base::asBoolean(recursive),
mat,
Base::asBoolean(transform),
depth);
if (!linked) {
linked = getDocumentObjectPtr();
auto pyObj = Py::Object(linked->getPyObject(),true);
}
auto pyObj = Py::Object(linked->getPyObject(), true);
if (mat) {
Py::Tuple ret(2);
ret.setItem(0,pyObj);
ret.setItem(1,Py::asObject(new Base::MatrixPy(*mat)));
ret.setItem(0, pyObj);
ret.setItem(1, Py::asObject(new Base::MatrixPy(*mat)));
return Py::new_reference_to(ret);
}
@@ -631,44 +739,56 @@ PyObject* DocumentObjectPy::getLinkedObject(PyObject *args, PyObject *keywds)
PY_CATCH;
}
PyObject* DocumentObjectPy::isElementVisible(PyObject *args)
PyObject* DocumentObjectPy::isElementVisible(PyObject* args)
{
char *element = nullptr;
if (!PyArg_ParseTuple(args, "s", &element))
char* element = nullptr;
if (!PyArg_ParseTuple(args, "s", &element)) {
return nullptr;
PY_TRY {
}
PY_TRY
{
return Py_BuildValue("h", getDocumentObjectPtr()->isElementVisible(element));
} PY_CATCH;
}
PY_CATCH;
}
PyObject* DocumentObjectPy::setElementVisible(PyObject *args)
PyObject* DocumentObjectPy::setElementVisible(PyObject* args)
{
char *element = nullptr;
PyObject *visible = Py_True;
if (!PyArg_ParseTuple(args, "s|O!", &element, &PyBool_Type, &visible))
char* element = nullptr;
PyObject* visible = Py_True;
if (!PyArg_ParseTuple(args, "s|O!", &element, &PyBool_Type, &visible)) {
return nullptr;
PY_TRY {
return Py_BuildValue("h", getDocumentObjectPtr()->setElementVisible(element, Base::asBoolean(visible)));
} PY_CATCH;
}
PY_TRY
{
return Py_BuildValue(
"h",
getDocumentObjectPtr()->setElementVisible(element, Base::asBoolean(visible)));
}
PY_CATCH;
}
PyObject* DocumentObjectPy::hasChildElement(PyObject *args)
PyObject* DocumentObjectPy::hasChildElement(PyObject* args)
{
if (!PyArg_ParseTuple(args, ""))
if (!PyArg_ParseTuple(args, "")) {
return nullptr;
PY_TRY {
return Py_BuildValue("O", getDocumentObjectPtr()->hasChildElement()?Py_True:Py_False);
} PY_CATCH;
}
PY_TRY
{
return Py_BuildValue("O", getDocumentObjectPtr()->hasChildElement() ? Py_True : Py_False);
}
PY_CATCH;
}
PyObject* DocumentObjectPy::getParentGroup(PyObject *args)
PyObject* DocumentObjectPy::getParentGroup(PyObject* args)
{
if (!PyArg_ParseTuple(args, ""))
if (!PyArg_ParseTuple(args, "")) {
return nullptr;
}
try {
auto grp = GroupExtension::getGroupOfObject(getDocumentObjectPtr());
if(!grp) {
if (!grp) {
Py_INCREF(Py_None);
return Py_None;
}
@@ -679,14 +799,15 @@ PyObject* DocumentObjectPy::getParentGroup(PyObject *args)
}
}
PyObject* DocumentObjectPy::getParentGeoFeatureGroup(PyObject *args)
PyObject* DocumentObjectPy::getParentGeoFeatureGroup(PyObject* args)
{
if (!PyArg_ParseTuple(args, ""))
if (!PyArg_ParseTuple(args, "")) {
return nullptr;
}
try {
auto grp = GeoFeatureGroupExtension::getGroupOfObject(getDocumentObjectPtr());
if(!grp) {
if (!grp) {
Py_INCREF(Py_None);
return Py_None;
}
@@ -697,14 +818,15 @@ PyObject* DocumentObjectPy::getParentGeoFeatureGroup(PyObject *args)
}
}
PyObject* DocumentObjectPy::getParent(PyObject *args)
PyObject* DocumentObjectPy::getParent(PyObject* args)
{
if (!PyArg_ParseTuple(args, ""))
if (!PyArg_ParseTuple(args, "")) {
return nullptr;
}
try {
auto grp = getDocumentObjectPtr()->getFirstParent();
if(!grp) {
if (!grp) {
Py_INCREF(Py_None);
return Py_None;
}
@@ -725,15 +847,15 @@ Py::Boolean DocumentObjectPy::getMustExecute() const
}
}
PyObject* DocumentObjectPy::getPathsByOutList(PyObject *args)
PyObject* DocumentObjectPy::getPathsByOutList(PyObject* args)
{
PyObject* o;
if (!PyArg_ParseTuple(args, "O!", &DocumentObjectPy::Type, &o))
if (!PyArg_ParseTuple(args, "O!", &DocumentObjectPy::Type, &o)) {
return nullptr;
}
try {
DocumentObject* target = static_cast<DocumentObjectPy*>
(o)->getDocumentObjectPtr();
DocumentObject* target = static_cast<DocumentObjectPy*>(o)->getDocumentObjectPtr();
auto array = getDocumentObjectPtr()->getPathsByOutList(target);
Py::List list;
for (const auto& it : array) {
@@ -766,100 +888,120 @@ PyObject* DocumentObjectPy::getElementMapVersion(PyObject* args)
Py::String(getDocumentObjectPtr()->getElementMapVersion(prop, Base::asBoolean(restored))));
}
PyObject *DocumentObjectPy::getCustomAttributes(const char* ) const
PyObject* DocumentObjectPy::getCustomAttributes(const char*) const
{
return nullptr;
return nullptr;
}
//remove
int DocumentObjectPy::setCustomAttributes(const char* , PyObject *)
// remove
int DocumentObjectPy::setCustomAttributes(const char*, PyObject*)
{
return 0;
}
Py::Int DocumentObjectPy::getID() const {
Py::Int DocumentObjectPy::getID() const
{
return Py::Int(getDocumentObjectPtr()->getID());
}
Py::Boolean DocumentObjectPy::getRemoving() const {
Py::Boolean DocumentObjectPy::getRemoving() const
{
return {getDocumentObjectPtr()->testStatus(ObjectStatus::Remove)};
}
PyObject *DocumentObjectPy::resolve(PyObject *args)
PyObject* DocumentObjectPy::resolve(PyObject* args)
{
const char *subname;
if (!PyArg_ParseTuple(args, "s",&subname))
const char* subname;
if (!PyArg_ParseTuple(args, "s", &subname)) {
return nullptr;
}
PY_TRY {
PY_TRY
{
std::string elementName;
const char *subElement = nullptr;
App::DocumentObject *parent = nullptr;
auto obj = getDocumentObjectPtr()->resolve(subname,&parent,&elementName,&subElement);
const char* subElement = nullptr;
App::DocumentObject* parent = nullptr;
auto obj = getDocumentObjectPtr()->resolve(subname, &parent, &elementName, &subElement);
Py::Tuple ret(4);
ret.setItem(0,obj?Py::Object(obj->getPyObject(),true):Py::None());
ret.setItem(1,parent?Py::Object(parent->getPyObject(),true):Py::None());
ret.setItem(2,Py::String(elementName.c_str()));
ret.setItem(3,Py::String(subElement?subElement:""));
ret.setItem(0, obj ? Py::Object(obj->getPyObject(), true) : Py::None());
ret.setItem(1, parent ? Py::Object(parent->getPyObject(), true) : Py::None());
ret.setItem(2, Py::String(elementName.c_str()));
ret.setItem(3, Py::String(subElement ? subElement : ""));
return Py::new_reference_to(ret);
} PY_CATCH;
}
PY_CATCH;
Py_Return;
}
PyObject *DocumentObjectPy::resolveSubElement(PyObject *args)
PyObject* DocumentObjectPy::resolveSubElement(PyObject* args)
{
const char *subname;
PyObject *append = Py_False;
const char* subname;
PyObject* append = Py_False;
int type = 0;
if (!PyArg_ParseTuple(args, "s|O!i",&subname,&PyBool_Type,&append,&type))
if (!PyArg_ParseTuple(args, "s|O!i", &subname, &PyBool_Type, &append, &type)) {
return nullptr;
}
PY_TRY {
PY_TRY
{
ElementNamePair elementName;
auto obj = GeoFeature::resolveElement(getDocumentObjectPtr(), subname,elementName,
Base::asBoolean(append), static_cast<GeoFeature::ElementNameType>(type));
auto obj = GeoFeature::resolveElement(getDocumentObjectPtr(),
subname,
elementName,
Base::asBoolean(append),
static_cast<GeoFeature::ElementNameType>(type));
Py::Tuple ret(3);
ret.setItem(0,obj?Py::Object(obj->getPyObject(),true):Py::None());
ret.setItem(1,Py::String(elementName.newName));
ret.setItem(2,Py::String(elementName.oldName));
ret.setItem(0, obj ? Py::Object(obj->getPyObject(), true) : Py::None());
ret.setItem(1, Py::String(elementName.newName));
ret.setItem(2, Py::String(elementName.oldName));
return Py::new_reference_to(ret);
} PY_CATCH;
}
PY_CATCH;
Py_Return;
}
Py::List DocumentObjectPy::getParents() const {
Py::List DocumentObjectPy::getParents() const
{
Py::List ret;
for(auto &v : getDocumentObjectPtr()->getParents())
ret.append(Py::TupleN(Py::Object(v.first->getPyObject(),true),Py::String(v.second)));
for (auto& v : getDocumentObjectPtr()->getParents()) {
ret.append(Py::TupleN(Py::Object(v.first->getPyObject(), true), Py::String(v.second)));
}
return ret;
}
PyObject *DocumentObjectPy::adjustRelativeLinks(PyObject *args) {
PyObject *pyobj;
PyObject *recursive = Py_True;
if (!PyArg_ParseTuple(args, "O!|O",&DocumentObjectPy::Type,&pyobj,&recursive))
PyObject* DocumentObjectPy::adjustRelativeLinks(PyObject* args)
{
PyObject* pyobj;
PyObject* recursive = Py_True;
if (!PyArg_ParseTuple(args, "O!|O", &DocumentObjectPy::Type, &pyobj, &recursive)) {
return nullptr;
PY_TRY {
}
PY_TRY
{
auto obj = static_cast<DocumentObjectPy*>(pyobj)->getDocumentObjectPtr();
auto inList = obj->getInListEx(true);
inList.insert(obj);
std::set<App::DocumentObject *> visited;
return Py::new_reference_to(Py::Boolean(
getDocumentObjectPtr()->adjustRelativeLinks(inList,
Base::asBoolean(recursive) ? &visited : nullptr)));
}PY_CATCH
std::set<App::DocumentObject*> visited;
return Py::new_reference_to(Py::Boolean(getDocumentObjectPtr()->adjustRelativeLinks(
inList,
Base::asBoolean(recursive) ? &visited : nullptr)));
}
PY_CATCH
}
Py::String DocumentObjectPy::getOldLabel() const {
Py::String DocumentObjectPy::getOldLabel() const
{
return {getDocumentObjectPtr()->getOldLabel()};
}
Py::Boolean DocumentObjectPy::getNoTouch() const {
Py::Boolean DocumentObjectPy::getNoTouch() const
{
return {getDocumentObjectPtr()->testStatus(ObjectStatus::NoTouch)};
}
void DocumentObjectPy::setNoTouch(Py::Boolean value) {
getDocumentObjectPtr()->setStatus(ObjectStatus::NoTouch,value.isTrue());
void DocumentObjectPy::setNoTouch(Py::Boolean value)
{
getDocumentObjectPtr()->setStatus(ObjectStatus::NoTouch, value.isTrue());
}

View File

@@ -57,8 +57,9 @@ DocumentT::~DocumentT() = default;
void DocumentT::operator=(const DocumentT& doc)
{
if (this == &doc)
if (this == &doc) {
return;
}
document = doc.document;
}
@@ -77,7 +78,7 @@ Document* DocumentT::getDocument() const
return GetApplication().getDocument(document.c_str());
}
const std::string &DocumentT::getDocumentName() const
const std::string& DocumentT::getDocumentName() const
{
return document;
}
@@ -93,12 +94,12 @@ std::string DocumentT::getDocumentPython() const
DocumentObjectT::DocumentObjectT() = default;
DocumentObjectT::DocumentObjectT(const DocumentObjectT &other)
DocumentObjectT::DocumentObjectT(const DocumentObjectT& other)
{
*this = other;
}
DocumentObjectT::DocumentObjectT(DocumentObjectT &&other)
DocumentObjectT::DocumentObjectT(DocumentObjectT&& other)
{
*this = std::move(other);
}
@@ -115,25 +116,29 @@ DocumentObjectT::DocumentObjectT(const Property* prop)
DocumentObjectT::DocumentObjectT(const Document* doc, const std::string& objName)
{
if (doc && doc->getName())
if (doc && doc->getName()) {
document = doc->getName();
}
object = objName;
}
DocumentObjectT::DocumentObjectT(const char *docName, const char *objName)
DocumentObjectT::DocumentObjectT(const char* docName, const char* objName)
{
if(docName)
if (docName) {
document = docName;
if(objName)
}
if (objName) {
object = objName;
}
}
DocumentObjectT::~DocumentObjectT() = default;
DocumentObjectT &DocumentObjectT::operator=(const DocumentObjectT& obj)
DocumentObjectT& DocumentObjectT::operator=(const DocumentObjectT& obj)
{
if (this == &obj)
if (this == &obj) {
return *this;
}
object = obj.object;
label = obj.label;
document = obj.document;
@@ -141,10 +146,11 @@ DocumentObjectT &DocumentObjectT::operator=(const DocumentObjectT& obj)
return *this;
}
DocumentObjectT &DocumentObjectT::operator=(DocumentObjectT&& obj)
DocumentObjectT& DocumentObjectT::operator=(DocumentObjectT&& obj)
{
if (this == &obj)
if (this == &obj) {
return *this;
}
object = std::move(obj.object);
label = std::move(obj.label);
document = std::move(obj.document);
@@ -154,12 +160,13 @@ DocumentObjectT &DocumentObjectT::operator=(DocumentObjectT&& obj)
void DocumentObjectT::operator=(const DocumentObject* obj)
{
if(!obj || !obj->isAttachedToDocument()) {
if (!obj || !obj->isAttachedToDocument()) {
object.clear();
label.clear();
document.clear();
property.clear();
} else {
}
else {
object = obj->getNameInDocument();
label = obj->Label.getValue();
document = obj->getDocument()->getName();
@@ -167,16 +174,16 @@ void DocumentObjectT::operator=(const DocumentObject* obj)
}
}
void DocumentObjectT::operator=(const Property *prop) {
if(!prop || !prop->hasName()
|| !prop->getContainer()
|| !prop->getContainer()->isDerivedFrom(App::DocumentObject::getClassTypeId()))
{
void DocumentObjectT::operator=(const Property* prop)
{
if (!prop || !prop->hasName() || !prop->getContainer()
|| !prop->getContainer()->isDerivedFrom(App::DocumentObject::getClassTypeId())) {
object.clear();
label.clear();
document.clear();
property.clear();
} else {
}
else {
auto obj = static_cast<App::DocumentObject*>(prop->getContainer());
object = obj->getNameInDocument();
label = obj->Label.getValue();
@@ -185,10 +192,9 @@ void DocumentObjectT::operator=(const Property *prop) {
}
}
bool DocumentObjectT::operator==(const DocumentObjectT &other) const {
return document == other.document
&& object == other.object
&& label == other.label
bool DocumentObjectT::operator==(const DocumentObjectT& other) const
{
return document == other.document && object == other.object && label == other.label
&& property == other.property;
}
@@ -219,12 +225,12 @@ DocumentObject* DocumentObjectT::getObject() const
return obj;
}
const std::string &DocumentObjectT::getObjectName() const
const std::string& DocumentObjectT::getObjectName() const
{
return object;
}
const std::string &DocumentObjectT::getObjectLabel() const
const std::string& DocumentObjectT::getObjectLabel() const
{
return label;
}
@@ -236,7 +242,8 @@ std::string DocumentObjectT::getObjectPython() const
return str.str();
}
const std::string &DocumentObjectT::getPropertyName() const {
const std::string& DocumentObjectT::getPropertyName() const
{
return property;
}
@@ -244,15 +251,18 @@ std::string DocumentObjectT::getPropertyPython() const
{
std::stringstream str;
str << getObjectPython();
if (!property.empty())
if (!property.empty()) {
str << '.' << property;
}
return str.str();
}
Property *DocumentObjectT::getProperty() const {
Property* DocumentObjectT::getProperty() const
{
auto obj = getObject();
if(obj)
if (obj) {
return obj->getPropertyByName(property.c_str());
}
return nullptr;
}
@@ -260,86 +270,95 @@ Property *DocumentObjectT::getProperty() const {
SubObjectT::SubObjectT() = default;
SubObjectT::SubObjectT(const SubObjectT &) = default;
SubObjectT::SubObjectT(const SubObjectT&) = default;
SubObjectT::SubObjectT(SubObjectT &&other)
:DocumentObjectT(std::move(other)), subname(std::move(other.subname))
SubObjectT::SubObjectT(SubObjectT&& other)
: DocumentObjectT(std::move(other))
, subname(std::move(other.subname))
{}
SubObjectT::SubObjectT(const DocumentObject* obj, const char* s)
: DocumentObjectT(obj)
, subname(s ? s : "")
{}
SubObjectT::SubObjectT(const DocumentObject* obj)
: DocumentObjectT(obj)
{}
SubObjectT::SubObjectT(const DocumentObjectT& obj, const char* s)
: DocumentObjectT(obj)
, subname(s ? s : "")
{}
SubObjectT::SubObjectT(const char* docName, const char* objName, const char* s)
: DocumentObjectT(docName, objName)
, subname(s ? s : "")
{}
bool SubObjectT::operator<(const SubObjectT& other) const
{
}
SubObjectT::SubObjectT(const DocumentObject *obj, const char *s)
:DocumentObjectT(obj),subname(s?s:"")
{
}
SubObjectT::SubObjectT(const DocumentObject *obj)
:DocumentObjectT(obj)
{
}
SubObjectT::SubObjectT(const DocumentObjectT& obj, const char *s)
:DocumentObjectT(obj),subname(s?s:"")
{
}
SubObjectT::SubObjectT(const char *docName, const char *objName, const char *s)
:DocumentObjectT(docName,objName), subname(s?s:"")
{
}
bool SubObjectT::operator<(const SubObjectT &other) const {
if(getDocumentName() < other.getDocumentName())
if (getDocumentName() < other.getDocumentName()) {
return true;
if(getDocumentName() > other.getDocumentName())
}
if (getDocumentName() > other.getDocumentName()) {
return false;
if(getObjectName() < other.getObjectName())
}
if (getObjectName() < other.getObjectName()) {
return true;
if(getObjectName() > other.getObjectName())
}
if (getObjectName() > other.getObjectName()) {
return false;
if(getSubName() < other.getSubName())
}
if (getSubName() < other.getSubName()) {
return true;
if(getSubName() > other.getSubName())
}
if (getSubName() > other.getSubName()) {
return false;
}
return getPropertyName() < other.getPropertyName();
}
SubObjectT &SubObjectT::operator=(const SubObjectT& other)
SubObjectT& SubObjectT::operator=(const SubObjectT& other)
{
if (this == &other)
if (this == &other) {
return *this;
}
static_cast<DocumentObjectT&>(*this) = other;
subname = other.subname;
return *this;
}
SubObjectT &SubObjectT::operator=(SubObjectT &&other)
SubObjectT& SubObjectT::operator=(SubObjectT&& other)
{
if (this == &other)
if (this == &other) {
return *this;
}
static_cast<DocumentObjectT&>(*this) = std::move(other);
subname = std::move(other.subname);
return *this;
}
SubObjectT &SubObjectT::operator=(const DocumentObjectT &other)
SubObjectT& SubObjectT::operator=(const DocumentObjectT& other)
{
if (this == &other)
if (this == &other) {
return *this;
}
static_cast<DocumentObjectT&>(*this) = other;
subname.clear();
return *this;
}
SubObjectT &SubObjectT::operator=(const DocumentObject *other)
SubObjectT& SubObjectT::operator=(const DocumentObject* other)
{
static_cast<DocumentObjectT&>(*this) = other;
subname.clear();
return *this;
}
bool SubObjectT::operator==(const SubObjectT &other) const {
return static_cast<const DocumentObjectT&>(*this) == other
&& subname == other.subname;
bool SubObjectT::operator==(const SubObjectT& other) const
{
return static_cast<const DocumentObjectT&>(*this) == other && subname == other.subname;
}
namespace
@@ -430,19 +449,23 @@ SubObjectT App::SubObjectT::normalized(NormalizeOptions options) const
return res;
}
void SubObjectT::setSubName(const char *s) {
subname = s?s:"";
void SubObjectT::setSubName(const char* s)
{
subname = s ? s : "";
}
const std::string &SubObjectT::getSubName() const {
const std::string& SubObjectT::getSubName() const
{
return subname;
}
std::string SubObjectT::getSubNameNoElement() const {
std::string SubObjectT::getSubNameNoElement() const
{
return Data::noElementName(subname.c_str());
}
const char *SubObjectT::getElementName() const {
const char* SubObjectT::getElementName() const
{
return Data::findElementName(subname.c_str());
}
@@ -457,90 +480,107 @@ bool SubObjectT::hasSubElement() const
return element && (element[0] != '\0');
}
std::string SubObjectT::getNewElementName() const {
std::string SubObjectT::getNewElementName() const
{
ElementNamePair element;
auto obj = getObject();
if(!obj)
if (!obj) {
return {};
GeoFeature::resolveElement(obj,subname.c_str(),element);
}
GeoFeature::resolveElement(obj, subname.c_str(), element);
return std::move(element.newName);
}
std::string SubObjectT::getOldElementName(int *index) const {
std::string SubObjectT::getOldElementName(int* index) const
{
ElementNamePair element;
auto obj = getObject();
if(!obj)
if (!obj) {
return {};
GeoFeature::resolveElement(obj,subname.c_str(),element);
if(!index)
}
GeoFeature::resolveElement(obj, subname.c_str(), element);
if (!index) {
return std::move(element.oldName);
}
std::size_t pos = element.oldName.find_first_of("0123456789");
if(pos == std::string::npos)
if (pos == std::string::npos) {
*index = -1;
}
else {
*index = std::atoi(element.oldName.c_str()+pos);
*index = std::atoi(element.oldName.c_str() + pos);
element.oldName.resize(pos);
}
return std::move(element.oldName);
}
App::DocumentObject *SubObjectT::getSubObject() const {
App::DocumentObject* SubObjectT::getSubObject() const
{
auto obj = getObject();
if(obj)
if (obj) {
return obj->getSubObject(subname.c_str());
}
return nullptr;
}
std::string SubObjectT::getSubObjectPython(bool force) const {
if(!force && subname.empty())
std::string SubObjectT::getSubObjectPython(bool force) const
{
if (!force && subname.empty()) {
return getObjectPython();
}
std::stringstream str;
str << "(" << getObjectPython() << ",u'"
<< Base::Tools::escapedUnicodeFromUtf8(subname.c_str()) << "')";
str << "(" << getObjectPython() << ",u'" << Base::Tools::escapedUnicodeFromUtf8(subname.c_str())
<< "')";
return str.str();
}
std::vector<App::DocumentObject*> SubObjectT::getSubObjectList() const {
std::vector<App::DocumentObject*> SubObjectT::getSubObjectList() const
{
auto obj = getObject();
if(obj)
if (obj) {
return obj->getSubObjectList(subname.c_str());
}
return {};
}
std::string SubObjectT::getObjectFullName(const char *docName) const
std::string SubObjectT::getObjectFullName(const char* docName) const
{
std::ostringstream ss;
if (!docName || getDocumentName() != docName) {
ss << getDocumentName();
if (auto doc = getDocument()) {
if (doc->Label.getStrValue() != getDocumentName())
if (doc->Label.getStrValue() != getDocumentName()) {
ss << "(" << doc->Label.getValue() << ")";
}
}
ss << "#";
}
ss << getObjectName();
if (!getObjectLabel().empty() && getObjectLabel() != getObjectName())
if (!getObjectLabel().empty() && getObjectLabel() != getObjectName()) {
ss << " (" << getObjectLabel() << ")";
}
return ss.str();
}
std::string SubObjectT::getSubObjectFullName(const char *docName) const
std::string SubObjectT::getSubObjectFullName(const char* docName) const
{
if (subname.empty())
if (subname.empty()) {
return getObjectFullName(docName);
}
std::ostringstream ss;
if (!docName || getDocumentName() != docName) {
ss << getDocumentName();
if (auto doc = getDocument()) {
if (doc->Label.getStrValue() != getDocumentName())
if (doc->Label.getStrValue() != getDocumentName()) {
ss << "(" << doc->Label.getValue() << ")";
}
}
ss << "#";
}
ss << getObjectName() << "." << subname;
auto sobj = getSubObject();
if (sobj && sobj->Label.getStrValue() != sobj->getNameInDocument())
if (sobj && sobj->Label.getStrValue() != sobj->getNameInDocument()) {
ss << " (" << sobj->Label.getValue() << ")";
}
return ss.str();
}
@@ -548,10 +588,9 @@ std::string SubObjectT::getSubObjectFullName(const char *docName) const
PropertyLinkT::PropertyLinkT()
: toPython("None")
{
}
{}
PropertyLinkT::PropertyLinkT(DocumentObject *obj)
PropertyLinkT::PropertyLinkT(DocumentObject* obj)
: PropertyLinkT()
{
if (obj) {
@@ -563,15 +602,16 @@ PropertyLinkT::PropertyLinkT(DocumentObject *obj)
}
}
PropertyLinkT::PropertyLinkT(DocumentObject *obj, const std::vector<std::string>& subNames)
PropertyLinkT::PropertyLinkT(DocumentObject* obj, const std::vector<std::string>& subNames)
: PropertyLinkT()
{
if (obj) {
std::ostringstream str;
DocumentObjectT objT(obj);
str << "(" << objT.getObjectPython() << ",[";
for(const auto& it : subNames)
for (const auto& it : subNames) {
str << "'" << it << "',";
}
str << "])";
toPython = str.str();
@@ -585,8 +625,9 @@ PropertyLinkT::PropertyLinkT(const std::vector<DocumentObject*>& objs)
std::stringstream str;
str << "[";
for (std::size_t i = 0; i < objs.size(); i++) {
if (i > 0)
if (i > 0) {
str << ", ";
}
App::DocumentObject* obj = objs[i];
if (obj) {
@@ -602,17 +643,20 @@ PropertyLinkT::PropertyLinkT(const std::vector<DocumentObject*>& objs)
}
}
PropertyLinkT::PropertyLinkT(const std::vector<DocumentObject*>& objs, const std::vector<std::string>& subNames)
PropertyLinkT::PropertyLinkT(const std::vector<DocumentObject*>& objs,
const std::vector<std::string>& subNames)
: PropertyLinkT()
{
if (!objs.empty() && objs.size() == subNames.size()) {
std::stringstream str;
str << "[";
for (std::size_t i = 0; i < subNames.size(); i++) {
if (i>0)
if (i > 0) {
str << ",(";
else
}
else {
str << "(";
}
App::DocumentObject* obj = objs[i];
if (obj) {
@@ -639,22 +683,28 @@ std::string PropertyLinkT::getPropertyPython() const
// -----------------------------------------------------------------------------
class DocumentWeakPtrT::Private {
class DocumentWeakPtrT::Private
{
public:
explicit Private(App::Document* doc) : _document(doc) {
explicit Private(App::Document* doc)
: _document(doc)
{
if (doc) {
//NOLINTBEGIN
connectApplicationDeletedDocument = App::GetApplication().signalDeleteDocument.connect(std::bind
(&Private::deletedDocument, this, sp::_1));
//NOLINTEND
// NOLINTBEGIN
connectApplicationDeletedDocument = App::GetApplication().signalDeleteDocument.connect(
std::bind(&Private::deletedDocument, this, sp::_1));
// NOLINTEND
}
}
void deletedDocument(const App::Document& doc) {
if (_document == &doc)
void deletedDocument(const App::Document& doc)
{
if (_document == &doc) {
reset();
}
}
void reset() {
void reset()
{
connectApplicationDeletedDocument.disconnect();
_document = nullptr;
}
@@ -665,9 +715,8 @@ public:
};
DocumentWeakPtrT::DocumentWeakPtrT(App::Document* doc) noexcept
: d(new Private(doc))
{
}
: d(new Private(doc))
{}
DocumentWeakPtrT::~DocumentWeakPtrT() = default;
@@ -693,56 +742,65 @@ App::Document* DocumentWeakPtrT::operator->() const noexcept
// -----------------------------------------------------------------------------
class DocumentObjectWeakPtrT::Private {
class DocumentObjectWeakPtrT::Private
{
public:
explicit Private(App::DocumentObject* obj) : object(obj) {
explicit Private(App::DocumentObject* obj)
: object(obj)
{
set(obj);
}
void deletedDocument(const App::Document& doc) {
void deletedDocument(const App::Document& doc)
{
// When deleting document then there is no way to undo it
if (object && object->getDocument() == &doc) {
reset();
}
}
void createdObject(const App::DocumentObject& obj) noexcept {
void createdObject(const App::DocumentObject& obj) noexcept
{
// When undoing the removal
if (object == &obj) {
indocument = true;
}
}
void deletedObject(const App::DocumentObject& obj) noexcept {
void deletedObject(const App::DocumentObject& obj) noexcept
{
if (object == &obj) {
indocument = false;
}
}
void reset() {
void reset()
{
connectApplicationDeletedDocument.disconnect();
connectDocumentCreatedObject.disconnect();
connectDocumentDeletedObject.disconnect();
object = nullptr;
indocument = false;
}
void set(App::DocumentObject* obj) {
void set(App::DocumentObject* obj)
{
object = obj;
if (obj) {
//NOLINTBEGIN
// NOLINTBEGIN
indocument = true;
connectApplicationDeletedDocument = App::GetApplication().signalDeleteDocument.connect(std::bind
(&Private::deletedDocument, this, sp::_1));
connectApplicationDeletedDocument = App::GetApplication().signalDeleteDocument.connect(
std::bind(&Private::deletedDocument, this, sp::_1));
App::Document* doc = obj->getDocument();
connectDocumentCreatedObject = doc->signalNewObject.connect(std::bind
(&Private::createdObject, this, sp::_1));
connectDocumentDeletedObject = doc->signalDeletedObject.connect(std::bind
(&Private::deletedObject, this, sp::_1));
//NOLINTEND
connectDocumentCreatedObject =
doc->signalNewObject.connect(std::bind(&Private::createdObject, this, sp::_1));
connectDocumentDeletedObject =
doc->signalDeletedObject.connect(std::bind(&Private::deletedObject, this, sp::_1));
// NOLINTEND
}
}
App::DocumentObject* get() const noexcept {
App::DocumentObject* get() const noexcept
{
return indocument ? object : nullptr;
}
App::DocumentObject* object;
bool indocument{false};
bool indocument {false};
using Connection = boost::signals2::scoped_connection;
Connection connectApplicationDeletedDocument;
Connection connectDocumentCreatedObject;
@@ -750,9 +808,8 @@ public:
};
DocumentObjectWeakPtrT::DocumentObjectWeakPtrT(App::DocumentObject* obj)
: d(new Private(obj))
{
}
: d(new Private(obj))
{}
DocumentObjectWeakPtrT::~DocumentObjectWeakPtrT() = default;
@@ -771,7 +828,7 @@ bool DocumentObjectWeakPtrT::expired() const noexcept
return !d->indocument;
}
DocumentObjectWeakPtrT& DocumentObjectWeakPtrT::operator= (App::DocumentObject* p)
DocumentObjectWeakPtrT& DocumentObjectWeakPtrT::operator=(App::DocumentObject* p)
{
d->reset();
d->set(p);
@@ -788,31 +845,33 @@ App::DocumentObject* DocumentObjectWeakPtrT::operator->() const noexcept
return d->get();
}
bool DocumentObjectWeakPtrT::operator== (const DocumentObjectWeakPtrT& p) const noexcept
bool DocumentObjectWeakPtrT::operator==(const DocumentObjectWeakPtrT& p) const noexcept
{
return d->get() == p.d->get();
}
bool DocumentObjectWeakPtrT::operator!= (const DocumentObjectWeakPtrT& p) const noexcept
bool DocumentObjectWeakPtrT::operator!=(const DocumentObjectWeakPtrT& p) const noexcept
{
return d->get() != p.d->get();
}
// -----------------------------------------------------------------------------
DocumentObserver::DocumentObserver() : _document(nullptr)
DocumentObserver::DocumentObserver()
: _document(nullptr)
{
//NOLINTBEGIN
this->connectApplicationCreatedDocument = App::GetApplication().signalNewDocument.connect(std::bind
(&DocumentObserver::slotCreatedDocument, this, sp::_1));
this->connectApplicationDeletedDocument = App::GetApplication().signalDeleteDocument.connect(std::bind
(&DocumentObserver::slotDeletedDocument, this, sp::_1));
this->connectApplicationActivateDocument = App::GetApplication().signalActiveDocument.connect(std::bind
(&DocumentObserver::slotActivateDocument, this, sp::_1));
//NOLINTEND
// NOLINTBEGIN
this->connectApplicationCreatedDocument = App::GetApplication().signalNewDocument.connect(
std::bind(&DocumentObserver::slotCreatedDocument, this, sp::_1));
this->connectApplicationDeletedDocument = App::GetApplication().signalDeleteDocument.connect(
std::bind(&DocumentObserver::slotDeletedDocument, this, sp::_1));
this->connectApplicationActivateDocument = App::GetApplication().signalActiveDocument.connect(
std::bind(&DocumentObserver::slotActivateDocument, this, sp::_1));
// NOLINTEND
}
DocumentObserver::DocumentObserver(Document* doc) : DocumentObserver()
DocumentObserver::DocumentObserver(Document* doc)
: DocumentObserver()
{
// Connect to application and given document
attachDocument(doc);
@@ -838,18 +897,18 @@ void DocumentObserver::attachDocument(Document* doc)
detachDocument();
_document = doc;
//NOLINTBEGIN
this->connectDocumentCreatedObject = _document->signalNewObject.connect(std::bind
(&DocumentObserver::slotCreatedObject, this, sp::_1));
this->connectDocumentDeletedObject = _document->signalDeletedObject.connect(std::bind
(&DocumentObserver::slotDeletedObject, this, sp::_1));
this->connectDocumentChangedObject = _document->signalChangedObject.connect(std::bind
(&DocumentObserver::slotChangedObject, this, sp::_1, sp::_2));
this->connectDocumentRecomputedObject = _document->signalRecomputedObject.connect(std::bind
(&DocumentObserver::slotRecomputedObject, this, sp::_1));
this->connectDocumentRecomputed = _document->signalRecomputed.connect(std::bind
(&DocumentObserver::slotRecomputedDocument, this, sp::_1));
//NOLINTEND
// NOLINTBEGIN
this->connectDocumentCreatedObject = _document->signalNewObject.connect(
std::bind(&DocumentObserver::slotCreatedObject, this, sp::_1));
this->connectDocumentDeletedObject = _document->signalDeletedObject.connect(
std::bind(&DocumentObserver::slotDeletedObject, this, sp::_1));
this->connectDocumentChangedObject = _document->signalChangedObject.connect(
std::bind(&DocumentObserver::slotChangedObject, this, sp::_1, sp::_2));
this->connectDocumentRecomputedObject = _document->signalRecomputedObject.connect(
std::bind(&DocumentObserver::slotRecomputedObject, this, sp::_1));
this->connectDocumentRecomputed = _document->signalRecomputed.connect(
std::bind(&DocumentObserver::slotRecomputedDocument, this, sp::_1));
// NOLINTEND
}
}
@@ -866,36 +925,29 @@ void DocumentObserver::detachDocument()
}
void DocumentObserver::slotCreatedDocument(const App::Document& /*Doc*/)
{
}
{}
void DocumentObserver::slotDeletedDocument(const App::Document& /*Doc*/)
{
}
{}
void DocumentObserver::slotActivateDocument(const App::Document& /*Doc*/)
{
}
{}
void DocumentObserver::slotCreatedObject(const App::DocumentObject& /*Obj*/)
{
}
{}
void DocumentObserver::slotDeletedObject(const App::DocumentObject& /*Obj*/)
{
}
{}
void DocumentObserver::slotChangedObject(const App::DocumentObject& /*Obj*/, const App::Property& /*Prop*/)
{
}
void DocumentObserver::slotChangedObject(const App::DocumentObject& /*Obj*/,
const App::Property& /*Prop*/)
{}
void DocumentObserver::slotRecomputedObject(const DocumentObject& /*Obj*/)
{
}
{}
void DocumentObserver::slotRecomputedDocument(const Document& /*doc*/)
{
}
{}
// -----------------------------------------------------------------------------
@@ -925,8 +977,7 @@ void DocumentObjectObserver::removeFromObservation(App::DocumentObject* obj)
}
void DocumentObjectObserver::slotCreatedDocument(const App::Document&)
{
}
{}
void DocumentObjectObserver::slotDeletedDocument(const App::Document& Doc)
{
@@ -938,24 +989,22 @@ void DocumentObjectObserver::slotDeletedDocument(const App::Document& Doc)
}
void DocumentObjectObserver::slotCreatedObject(const App::DocumentObject&)
{
}
{}
void DocumentObjectObserver::slotDeletedObject(const App::DocumentObject& Obj)
{
std::set<App::DocumentObject*>::iterator it = _objects.find
(const_cast<App::DocumentObject*>(&Obj));
if (it != _objects.end())
std::set<App::DocumentObject*>::iterator it =
_objects.find(const_cast<App::DocumentObject*>(&Obj));
if (it != _objects.end()) {
_objects.erase(it);
if (_objects.empty())
}
if (_objects.empty()) {
cancelObservation();
}
}
void DocumentObjectObserver::slotChangedObject(const App::DocumentObject&,
const App::Property&)
{
}
void DocumentObjectObserver::slotChangedObject(const App::DocumentObject&, const App::Property&)
{}
void DocumentObjectObserver::cancelObservation()
{
}
{}

View File

@@ -51,7 +51,7 @@ public:
/*! Constructor */
DocumentT();
/*! Constructor */
DocumentT(Document*); // explicit bombs
DocumentT(Document*); // explicit bombs
/*! Constructor */
explicit DocumentT(const std::string&);
/*! Constructor */
@@ -65,18 +65,20 @@ public:
/*! Assignment operator */
void operator=(const std::string&);
bool operator==(const DocumentT &other) const {
bool operator==(const DocumentT& other) const
{
return document == other.document;
}
bool operator<(const DocumentT &other) const {
bool operator<(const DocumentT& other) const
{
return document < other.document;
}
/*! Get a pointer to the document or 0 if it doesn't exist any more. */
Document* getDocument() const;
/*! Get the name of the document. */
const std::string &getDocumentName() const;
const std::string& getDocumentName() const;
/*! Get the document as Python command. */
std::string getDocumentPython() const;
@@ -85,9 +87,9 @@ private:
};
/**
* The DocumentObjectT class is a helper class to store the names of a document object and its document.
* This can be useful when you cannot rely on that the document or the object still exists when you have to
* access it.
* The DocumentObjectT class is a helper class to store the names of a document object and its
* document. This can be useful when you cannot rely on that the document or the object still exists
* when you have to access it.
*
* @author Werner Mayer
*/
@@ -97,23 +99,23 @@ public:
/*! Constructor */
DocumentObjectT();
/*! Constructor */
DocumentObjectT(const DocumentObjectT &);
DocumentObjectT(const DocumentObjectT&);
/*! Constructor */
DocumentObjectT(DocumentObjectT &&);
DocumentObjectT(DocumentObjectT&&);
/*! Constructor */
explicit DocumentObjectT(const DocumentObject*);
/*! Constructor */
DocumentObjectT(const Document*, const std::string& objName);
/*! Constructor */
DocumentObjectT(const char *docName, const char *objName);
DocumentObjectT(const char* docName, const char* objName);
/*! Constructor */
explicit DocumentObjectT(const Property*);
/*! Destructor */
~DocumentObjectT();
/*! Assignment operator */
DocumentObjectT &operator=(const DocumentObjectT&);
DocumentObjectT& operator=(const DocumentObjectT&);
/*! Assignment operator */
DocumentObjectT &operator=(DocumentObjectT &&);
DocumentObjectT& operator=(DocumentObjectT&&);
/*! Assignment operator */
void operator=(const DocumentObject*);
/*! Assignment operator */
@@ -124,7 +126,7 @@ public:
/*! Get a pointer to the document or 0 if it doesn't exist any more. */
Document* getDocument() const;
/*! Get the name of the document. */
const std::string &getDocumentName() const;
const std::string& getDocumentName() const;
/*! Get the document as Python command. */
std::string getDocumentPython() const;
/*! Get a pointer to the document object or 0 if it doesn't exist any more. */
@@ -132,16 +134,17 @@ public:
/*! Get a pointer to the property or 0 if it doesn't exist any more. */
Property* getProperty() const;
/*! Get the name of the document object. */
const std::string &getObjectName() const;
const std::string& getObjectName() const;
/*! Get the label of the document object. */
const std::string &getObjectLabel() const;
const std::string& getObjectLabel() const;
/*! Get the name of the property. */
const std::string &getPropertyName() const;
const std::string& getPropertyName() const;
/*! Get the document object as Python command. */
std::string getObjectPython() const;
/*! Get the property as Python command. */
std::string getPropertyPython() const;
/*! Get a pointer to the document or 0 if it doesn't exist any more or the type doesn't match. */
/*! Get a pointer to the document or 0 if it doesn't exist any more or the type doesn't match.
*/
template<typename T>
inline T* getObjectAs() const
{
@@ -160,72 +163,73 @@ private:
std::string property;
};
class AppExport SubObjectT : public DocumentObjectT
class AppExport SubObjectT: public DocumentObjectT
{
public:
/*! Constructor */
SubObjectT();
/*! Constructor */
SubObjectT(const SubObjectT &);
SubObjectT(const SubObjectT&);
/*! Constructor */
SubObjectT(SubObjectT &&);
SubObjectT(SubObjectT&&);
/*! Constructor */
SubObjectT(const DocumentObjectT & obj, const char *subname);
SubObjectT(const DocumentObjectT& obj, const char* subname);
/*! Constructor */
SubObjectT(const DocumentObject*, const char *subname);
SubObjectT(const DocumentObject*, const char* subname);
/*! Constructor */
SubObjectT(const DocumentObject*);// explicit bombs
SubObjectT(const DocumentObject*); // explicit bombs
/*! Constructor */
SubObjectT(const char *docName, const char *objName, const char *subname);
SubObjectT(const char* docName, const char* objName, const char* subname);
/*! Assignment operator */
SubObjectT &operator=(const SubObjectT&);
SubObjectT& operator=(const SubObjectT&);
/*! Assignment operator */
SubObjectT &operator=(SubObjectT &&);
SubObjectT& operator=(SubObjectT&&);
/*! Assignment operator */
SubObjectT &operator=(const DocumentObjectT&);
SubObjectT& operator=(const DocumentObjectT&);
/*! Assignment operator */
SubObjectT &operator=(const App::DocumentObject*);
SubObjectT& operator=(const App::DocumentObject*);
/*! Equality operator */
bool operator==(const SubObjectT&) const;
/// Set the subname path to the sub-object
void setSubName(const char *subname);
void setSubName(const char* subname);
/// Set the subname path to the sub-object
void setSubName(const std::string &subname) {
void setSubName(const std::string& subname)
{
setSubName(subname.c_str());
}
/// Return the subname path
const std::string &getSubName() const;
const std::string& getSubName() const;
/** Return docname#objname (label)
* @param docName: optional document name. The document prefix will only be printed
* if it is different then the given 'doc'.
*/
std::string getObjectFullName(const char *docName=nullptr) const;
std::string getObjectFullName(const char* docName = nullptr) const;
/** Return docname#objname.subname (label)
* @param doc: optional document name. The document prefix will only be printed
* if it is different then the given 'doc'.
*/
std::string getSubObjectFullName(const char *docName=nullptr) const;
std::string getSubObjectFullName(const char* docName = nullptr) const;
/// Return the subname path without sub-element
std::string getSubNameNoElement() const;
/// Return the sub-element (Face, Edge, etc) of the subname path
const char *getElementName() const;
const char* getElementName() const;
/// Check if there is any sub object reference
bool hasSubObject() const;
@@ -239,17 +243,17 @@ public:
/** Return the old style sub-element name
* @param index: if given, then return the element type, and extract the index
*/
std::string getOldElementName(int *index=nullptr) const;
std::string getOldElementName(int* index = nullptr) const;
/// Return the sub-object
DocumentObject *getSubObject() const;
DocumentObject* getSubObject() const;
/// Return all objects along the subname path
std::vector<DocumentObject *> getSubObjectList() const;
std::vector<DocumentObject*> getSubObjectList() const;
bool operator<(const SubObjectT &other) const;
bool operator<(const SubObjectT& other) const;
std::string getSubObjectPython(bool force=true) const;
std::string getSubObjectPython(bool force = true) const;
/// Options used by normalize()
enum class NormalizeOption : uint8_t
@@ -299,16 +303,17 @@ public:
PropertyLinkT();
/*! Constructor */
explicit PropertyLinkT(DocumentObject *obj);
explicit PropertyLinkT(DocumentObject* obj);
/*! Constructor */
PropertyLinkT(DocumentObject *obj, const std::vector<std::string>& subNames);
PropertyLinkT(DocumentObject* obj, const std::vector<std::string>& subNames);
/*! Constructor */
explicit PropertyLinkT(const std::vector<DocumentObject*>& objs);
/*! Constructor */
PropertyLinkT(const std::vector<DocumentObject*>& objs, const std::vector<std::string>& subNames);
PropertyLinkT(const std::vector<DocumentObject*>& objs,
const std::vector<std::string>& subNames);
/*! Get the property as Python command. */
std::string getPropertyPython() const;
@@ -379,7 +384,7 @@ public:
* \brief operator =
* Assignment operator
*/
DocumentObjectWeakPtrT& operator= (App::DocumentObject* p);
DocumentObjectWeakPtrT& operator=(App::DocumentObject* p);
/*!
* \brief operator *
* \return pointer to the document object
@@ -394,12 +399,12 @@ public:
* \brief operator ==
* \return true if both objects are equal, false otherwise
*/
bool operator== (const DocumentObjectWeakPtrT& p) const noexcept;
bool operator==(const DocumentObjectWeakPtrT& p) const noexcept;
/*!
* \brief operator !=
* \return true if both objects are inequal, false otherwise
*/
bool operator!= (const DocumentObjectWeakPtrT& p) const noexcept;
bool operator!=(const DocumentObjectWeakPtrT& p) const noexcept;
/*! Get a pointer to the object or 0 if it doesn't exist any more or the type doesn't match. */
template<typename T>
inline T* get() const noexcept
@@ -423,33 +428,37 @@ private:
/**
* @brief The WeakPtrT class
*/
template <class T>
template<class T>
class WeakPtrT
{
public:
explicit WeakPtrT(T* t) : ptr(t) {
}
explicit WeakPtrT(T* t)
: ptr(t)
{}
~WeakPtrT() = default;
/*!
* \brief reset
* Releases the reference to the managed object. After the call *this manages no object.
*/
void reset() {
void reset()
{
ptr.reset();
}
/*!
* \brief expired
* \return true if the managed object has already been deleted, false otherwise.
*/
bool expired() const {
bool expired() const
{
return ptr.expired();
}
/*!
* \brief operator =
* Assignment operator
*/
WeakPtrT<T>& operator= (T* p) {
WeakPtrT<T>& operator=(T* p)
{
ptr = p;
return *this;
}
@@ -457,28 +466,32 @@ public:
* \brief operator ->
* \return pointer to the document object
*/
T* operator*() const {
T* operator*() const
{
return ptr.get<T>();
}
/*!
* \brief operator ->
* \return pointer to the document object
*/
T* operator->() const {
T* operator->() const
{
return ptr.get<T>();
}
/*!
* \brief operator ==
* \return true if both objects are equal, false otherwise
*/
bool operator== (const WeakPtrT<T>& p) const {
bool operator==(const WeakPtrT<T>& p) const
{
return ptr == p.ptr;
}
/*!
* \brief operator !=
* \return true if both objects are inequal, false otherwise
*/
bool operator!= (const WeakPtrT<T>& p) const {
bool operator!=(const WeakPtrT<T>& p) const
{
return ptr != p.ptr;
}
/*! Get a pointer to the object or 0 if it doesn't exist any more. */
@@ -561,7 +574,7 @@ private:
*
* @author Werner Mayer
*/
class AppExport DocumentObjectObserver : public DocumentObserver
class AppExport DocumentObjectObserver: public DocumentObserver
{
public:
@@ -587,18 +600,18 @@ private:
void slotDeletedObject(const App::DocumentObject& Obj) override;
/** The property of an observed object has changed */
void slotChangedObject(const App::DocumentObject& Obj, const App::Property& Prop) override;
/** This method gets called when all observed objects are deleted or the whole document is deleted.
* This method can be re-implemented to perform an extra step like closing a dialog that observes
* a document.
*/
/** This method gets called when all observed objects are deleted or the whole document is
* deleted. This method can be re-implemented to perform an extra step like closing a dialog
* that observes a document.
*/
virtual void cancelObservation();
private:
std::set<App::DocumentObject*> _objects;
};
} //namespace App
} // namespace App
ENABLE_BITMASK_OPERATORS(App::SubObjectT::NormalizeOption)
#endif // APP_DOCUMENTOBSERVER_H
#endif // APP_DOCUMENTOBSERVER_H

View File

@@ -46,9 +46,10 @@ void DocumentObserverPython::addObserver(const Py::Object& obj)
void DocumentObserverPython::removeObserver(const Py::Object& obj)
{
DocumentObserverPython* obs=nullptr;
for (std::vector<DocumentObserverPython*>::iterator it =
_instances.begin(); it != _instances.end(); ++it) {
DocumentObserverPython* obs = nullptr;
for (std::vector<DocumentObserverPython*>::iterator it = _instances.begin();
it != _instances.end();
++it) {
if ((*it)->inst == obj) {
obs = *it;
_instances.erase(it);
@@ -59,33 +60,34 @@ void DocumentObserverPython::removeObserver(const Py::Object& obj)
delete obs;
}
DocumentObserverPython::DocumentObserverPython(const Py::Object& obj) : inst(obj)
DocumentObserverPython::DocumentObserverPython(const Py::Object& obj)
: inst(obj)
{
#define FC_PY_ELEMENT_ARG0(_name1, _name2) do {\
FC_PY_GetCallable(obj.ptr(), "slot" #_name1, py##_name1.py);\
if (!py##_name1.py.isNone())\
py##_name1.slot = App::GetApplication().signal##_name2.connect(\
std::bind(&DocumentObserverPython::slot##_name1, this));\
}\
while(0);
#define FC_PY_ELEMENT_ARG0(_name1, _name2) \
do { \
FC_PY_GetCallable(obj.ptr(), "slot" #_name1, py##_name1.py); \
if (!py##_name1.py.isNone()) \
py##_name1.slot = App::GetApplication().signal##_name2.connect( \
std::bind(&DocumentObserverPython::slot##_name1, this)); \
} while (0);
#define FC_PY_ELEMENT_ARG1(_name1, _name2) do {\
FC_PY_GetCallable(obj.ptr(), "slot" #_name1, py##_name1.py);\
if (!py##_name1.py.isNone())\
py##_name1.slot = App::GetApplication().signal##_name2.connect(\
std::bind(&DocumentObserverPython::slot##_name1, this, sp::_1));\
}\
while(0);
#define FC_PY_ELEMENT_ARG1(_name1, _name2) \
do { \
FC_PY_GetCallable(obj.ptr(), "slot" #_name1, py##_name1.py); \
if (!py##_name1.py.isNone()) \
py##_name1.slot = App::GetApplication().signal##_name2.connect( \
std::bind(&DocumentObserverPython::slot##_name1, this, sp::_1)); \
} while (0);
//NOLINTBEGIN
#define FC_PY_ELEMENT_ARG2(_name1, _name2) do {\
FC_PY_GetCallable(obj.ptr(), "slot" #_name1, py##_name1.py);\
if (!py##_name1.py.isNone())\
py##_name1.slot = App::GetApplication().signal##_name2.connect(\
std::bind(&DocumentObserverPython::slot##_name1, this, sp::_1, sp::_2));\
}\
while(0);
// NOLINTBEGIN
#define FC_PY_ELEMENT_ARG2(_name1, _name2) \
do { \
FC_PY_GetCallable(obj.ptr(), "slot" #_name1, py##_name1.py); \
if (!py##_name1.py.isNone()) \
py##_name1.slot = App::GetApplication().signal##_name2.connect( \
std::bind(&DocumentObserverPython::slot##_name1, this, sp::_1, sp::_2)); \
} while (0);
FC_PY_ELEMENT_ARG1(CreatedDocument, NewDocument)
FC_PY_ELEMENT_ARG1(DeletedDocument, DeleteDocument)
@@ -116,7 +118,7 @@ DocumentObserverPython::DocumentObserverPython(const Py::Object& obj) : inst(obj
FC_PY_ELEMENT_ARG2(ChangePropertyEditor, ChangePropertyEditor)
FC_PY_ELEMENT_ARG2(BeforeAddingDynamicExtension, BeforeAddingDynamicExtension)
FC_PY_ELEMENT_ARG2(AddedDynamicExtension, AddedDynamicExtension)
//NOLINTEND
// NOLINTEND
}
DocumentObserverPython::~DocumentObserverPython() = default;
@@ -127,10 +129,10 @@ void DocumentObserverPython::slotCreatedDocument(const App::Document& Doc)
try {
Py::Tuple args(1);
args.setItem(0, Py::asObject(const_cast<App::Document&>(Doc).getPyObject()));
Base::pyCall(pyCreatedDocument.ptr(),args.ptr());
Base::pyCall(pyCreatedDocument.ptr(), args.ptr());
}
catch (Py::Exception&) {
Base::PyException e; // extract the Python error text
Base::PyException e; // extract the Python error text
e.ReportException();
}
}
@@ -141,10 +143,10 @@ void DocumentObserverPython::slotDeletedDocument(const App::Document& Doc)
try {
Py::Tuple args(1);
args.setItem(0, Py::asObject(const_cast<App::Document&>(Doc).getPyObject()));
Base::pyCall(pyDeletedDocument.ptr(),args.ptr());
Base::pyCall(pyDeletedDocument.ptr(), args.ptr());
}
catch (Py::Exception&) {
Base::PyException e; // extract the Python error text
Base::PyException e; // extract the Python error text
e.ReportException();
}
}
@@ -155,10 +157,10 @@ void DocumentObserverPython::slotRelabelDocument(const App::Document& Doc)
try {
Py::Tuple args(1);
args.setItem(0, Py::asObject(const_cast<App::Document&>(Doc).getPyObject()));
Base::pyCall(pyRelabelDocument.ptr(),args.ptr());
Base::pyCall(pyRelabelDocument.ptr(), args.ptr());
}
catch (Py::Exception&) {
Base::PyException e; // extract the Python error text
Base::PyException e; // extract the Python error text
e.ReportException();
}
}
@@ -169,10 +171,10 @@ void DocumentObserverPython::slotActivateDocument(const App::Document& Doc)
try {
Py::Tuple args(1);
args.setItem(0, Py::asObject(const_cast<App::Document&>(Doc).getPyObject()));
Base::pyCall(pyActivateDocument.ptr(),args.ptr());
Base::pyCall(pyActivateDocument.ptr(), args.ptr());
}
catch (Py::Exception&) {
Base::PyException e; // extract the Python error text
Base::PyException e; // extract the Python error text
e.ReportException();
}
}
@@ -183,10 +185,10 @@ void DocumentObserverPython::slotUndoDocument(const App::Document& Doc)
try {
Py::Tuple args(1);
args.setItem(0, Py::asObject(const_cast<App::Document&>(Doc).getPyObject()));
Base::pyCall(pyUndoDocument.ptr(),args.ptr());
Base::pyCall(pyUndoDocument.ptr(), args.ptr());
}
catch (Py::Exception&) {
Base::PyException e; // extract the Python error text
Base::PyException e; // extract the Python error text
e.ReportException();
}
}
@@ -198,10 +200,10 @@ void DocumentObserverPython::slotRedoDocument(const App::Document& Doc)
try {
Py::Tuple args(1);
args.setItem(0, Py::asObject(const_cast<App::Document&>(Doc).getPyObject()));
Base::pyCall(pyRedoDocument.ptr(),args.ptr());
Base::pyCall(pyRedoDocument.ptr(), args.ptr());
}
catch (Py::Exception&) {
Base::PyException e; // extract the Python error text
Base::PyException e; // extract the Python error text
e.ReportException();
}
}
@@ -213,7 +215,7 @@ void DocumentObserverPython::slotUndo()
Base::pyCall(pyUndo.ptr());
}
catch (Py::Exception&) {
Base::PyException e; // extract the Python error text
Base::PyException e; // extract the Python error text
e.ReportException();
}
}
@@ -225,7 +227,7 @@ void DocumentObserverPython::slotRedo()
Base::pyCall(pyRedo.ptr());
}
catch (Py::Exception&) {
Base::PyException e; // extract the Python error text
Base::PyException e; // extract the Python error text
e.ReportException();
}
}
@@ -236,10 +238,10 @@ void DocumentObserverPython::slotBeforeCloseTransaction(bool abort)
try {
Py::Tuple args(1);
args.setItem(0, Py::Boolean(abort));
Base::pyCall(pyBeforeCloseTransaction.ptr(),args.ptr());
Base::pyCall(pyBeforeCloseTransaction.ptr(), args.ptr());
}
catch (Py::Exception&) {
Base::PyException e; // extract the Python error text
Base::PyException e; // extract the Python error text
e.ReportException();
}
}
@@ -250,15 +252,16 @@ void DocumentObserverPython::slotCloseTransaction(bool abort)
try {
Py::Tuple args(1);
args.setItem(0, Py::Boolean(abort));
Base::pyCall(pyCloseTransaction.ptr(),args.ptr());
Base::pyCall(pyCloseTransaction.ptr(), args.ptr());
}
catch (Py::Exception&) {
Base::PyException e; // extract the Python error text
Base::PyException e; // extract the Python error text
e.ReportException();
}
}
void DocumentObserverPython::slotBeforeChangeDocument(const App::Document& Doc, const App::Property& Prop)
void DocumentObserverPython::slotBeforeChangeDocument(const App::Document& Doc,
const App::Property& Prop)
{
Base::PyGILStateLocker lock;
try {
@@ -269,16 +272,17 @@ void DocumentObserverPython::slotBeforeChangeDocument(const App::Document& Doc,
const char* prop_name = Doc.getPropertyName(&Prop);
if (prop_name) {
args.setItem(1, Py::String(prop_name));
Base::pyCall(pyBeforeChangeDocument.ptr(),args.ptr());
Base::pyCall(pyBeforeChangeDocument.ptr(), args.ptr());
}
}
catch (Py::Exception&) {
Base::PyException e; // extract the Python error text
Base::PyException e; // extract the Python error text
e.ReportException();
}
}
void DocumentObserverPython::slotChangedDocument(const App::Document& Doc, const App::Property& Prop)
void DocumentObserverPython::slotChangedDocument(const App::Document& Doc,
const App::Property& Prop)
{
Base::PyGILStateLocker lock;
try {
@@ -289,11 +293,11 @@ void DocumentObserverPython::slotChangedDocument(const App::Document& Doc, const
const char* prop_name = Doc.getPropertyName(&Prop);
if (prop_name) {
args.setItem(1, Py::String(prop_name));
Base::pyCall(pyChangedDocument.ptr(),args.ptr());
Base::pyCall(pyChangedDocument.ptr(), args.ptr());
}
}
catch (Py::Exception&) {
Base::PyException e; // extract the Python error text
Base::PyException e; // extract the Python error text
e.ReportException();
}
}
@@ -304,10 +308,10 @@ void DocumentObserverPython::slotCreatedObject(const App::DocumentObject& Obj)
try {
Py::Tuple args(1);
args.setItem(0, Py::asObject(const_cast<App::DocumentObject&>(Obj).getPyObject()));
Base::pyCall(pyCreatedObject.ptr(),args.ptr());
Base::pyCall(pyCreatedObject.ptr(), args.ptr());
}
catch (Py::Exception&) {
Base::PyException e; // extract the Python error text
Base::PyException e; // extract the Python error text
e.ReportException();
}
}
@@ -318,16 +322,16 @@ void DocumentObserverPython::slotDeletedObject(const App::DocumentObject& Obj)
try {
Py::Tuple args(1);
args.setItem(0, Py::asObject(const_cast<App::DocumentObject&>(Obj).getPyObject()));
Base::pyCall(pyDeletedObject.ptr(),args.ptr());
Base::pyCall(pyDeletedObject.ptr(), args.ptr());
}
catch (Py::Exception&) {
Base::PyException e; // extract the Python error text
Base::PyException e; // extract the Python error text
e.ReportException();
}
}
void DocumentObserverPython::slotBeforeChangeObject(const App::DocumentObject& Obj,
const App::Property& Prop)
const App::Property& Prop)
{
Base::PyGILStateLocker lock;
try {
@@ -338,11 +342,11 @@ void DocumentObserverPython::slotBeforeChangeObject(const App::DocumentObject& O
const char* prop_name = Obj.getPropertyName(&Prop);
if (prop_name) {
args.setItem(1, Py::String(prop_name));
Base::pyCall(pyBeforeChangeObject.ptr(),args.ptr());
Base::pyCall(pyBeforeChangeObject.ptr(), args.ptr());
}
}
catch (Py::Exception&) {
Base::PyException e; // extract the Python error text
Base::PyException e; // extract the Python error text
e.ReportException();
}
}
@@ -359,11 +363,11 @@ void DocumentObserverPython::slotChangedObject(const App::DocumentObject& Obj,
const char* prop_name = Obj.getPropertyName(&Prop);
if (prop_name) {
args.setItem(1, Py::String(prop_name));
Base::pyCall(pyChangedObject.ptr(),args.ptr());
Base::pyCall(pyChangedObject.ptr(), args.ptr());
}
}
catch (Py::Exception&) {
Base::PyException e; // extract the Python error text
Base::PyException e; // extract the Python error text
e.ReportException();
}
}
@@ -374,10 +378,10 @@ void DocumentObserverPython::slotRecomputedObject(const App::DocumentObject& Obj
try {
Py::Tuple args(1);
args.setItem(0, Py::asObject(const_cast<App::DocumentObject&>(Obj).getPyObject()));
Base::pyCall(pyRecomputedObject.ptr(),args.ptr());
Base::pyCall(pyRecomputedObject.ptr(), args.ptr());
}
catch (Py::Exception&) {
Base::PyException e; // extract the Python error text
Base::PyException e; // extract the Python error text
e.ReportException();
}
}
@@ -388,10 +392,10 @@ void DocumentObserverPython::slotRecomputedDocument(const App::Document& doc)
try {
Py::Tuple args(1);
args.setItem(0, Py::asObject(const_cast<App::Document&>(doc).getPyObject()));
Base::pyCall(pyRecomputedDocument.ptr(),args.ptr());
Base::pyCall(pyRecomputedDocument.ptr(), args.ptr());
}
catch (Py::Exception&) {
Base::PyException e; // extract the Python error text
Base::PyException e; // extract the Python error text
e.ReportException();
}
}
@@ -402,10 +406,10 @@ void DocumentObserverPython::slotBeforeRecomputeDocument(const App::Document& do
try {
Py::Tuple args(1);
args.setItem(0, Py::asObject(const_cast<App::Document&>(doc).getPyObject()));
Base::pyCall(pyBeforeRecomputeDocument.ptr(),args.ptr());
Base::pyCall(pyBeforeRecomputeDocument.ptr(), args.ptr());
}
catch (Py::Exception&) {
Base::PyException e; // extract the Python error text
Base::PyException e; // extract the Python error text
e.ReportException();
}
}
@@ -417,10 +421,10 @@ void DocumentObserverPython::slotOpenTransaction(const App::Document& doc, std::
Py::Tuple args(2);
args.setItem(0, Py::asObject(const_cast<App::Document&>(doc).getPyObject()));
args.setItem(1, Py::String(str));
Base::pyCall(pyOpenTransaction.ptr(),args.ptr());
Base::pyCall(pyOpenTransaction.ptr(), args.ptr());
}
catch (Py::Exception&) {
Base::PyException e; // extract the Python error text
Base::PyException e; // extract the Python error text
e.ReportException();
}
}
@@ -431,10 +435,10 @@ void DocumentObserverPython::slotCommitTransaction(const App::Document& doc)
try {
Py::Tuple args(1);
args.setItem(0, Py::asObject(const_cast<App::Document&>(doc).getPyObject()));
Base::pyCall(pyCommitTransaction.ptr(),args.ptr());
Base::pyCall(pyCommitTransaction.ptr(), args.ptr());
}
catch (Py::Exception&) {
Base::PyException e; // extract the Python error text
Base::PyException e; // extract the Python error text
e.ReportException();
}
}
@@ -445,10 +449,10 @@ void DocumentObserverPython::slotAbortTransaction(const App::Document& doc)
try {
Py::Tuple args(1);
args.setItem(0, Py::asObject(const_cast<App::Document&>(doc).getPyObject()));
Base::pyCall(pyAbortTransaction.ptr(),args.ptr());
Base::pyCall(pyAbortTransaction.ptr(), args.ptr());
}
catch (Py::Exception&) {
Base::PyException e; // extract the Python error text
Base::PyException e; // extract the Python error text
e.ReportException();
}
}
@@ -465,11 +469,11 @@ void DocumentObserverPython::slotAppendDynamicProperty(const App::Property& Prop
const char* prop_name = container->getPropertyName(&Prop);
if (prop_name) {
args.setItem(1, Py::String(prop_name));
Base::pyCall(pyAppendDynamicProperty.ptr(),args.ptr());
Base::pyCall(pyAppendDynamicProperty.ptr(), args.ptr());
}
}
catch (Py::Exception&) {
Base::PyException e; // extract the Python error text
Base::PyException e; // extract the Python error text
e.ReportException();
}
}
@@ -486,16 +490,17 @@ void DocumentObserverPython::slotRemoveDynamicProperty(const App::Property& Prop
const char* prop_name = container->getPropertyName(&Prop);
if (prop_name) {
args.setItem(1, Py::String(prop_name));
Base::pyCall(pyRemoveDynamicProperty.ptr(),args.ptr());
Base::pyCall(pyRemoveDynamicProperty.ptr(), args.ptr());
}
}
catch (Py::Exception&) {
Base::PyException e; // extract the Python error text
Base::PyException e; // extract the Python error text
e.ReportException();
}
}
void DocumentObserverPython::slotChangePropertyEditor(const App::Document &, const App::Property& Prop)
void DocumentObserverPython::slotChangePropertyEditor(const App::Document&,
const App::Property& Prop)
{
Base::PyGILStateLocker lock;
try {
@@ -507,72 +512,76 @@ void DocumentObserverPython::slotChangePropertyEditor(const App::Document &, con
const char* prop_name = container->getPropertyName(&Prop);
if (prop_name) {
args.setItem(1, Py::String(prop_name));
Base::pyCall(pyChangePropertyEditor.ptr(),args.ptr());
Base::pyCall(pyChangePropertyEditor.ptr(), args.ptr());
}
}
catch (Py::Exception&) {
Base::PyException e; // extract the Python error text
Base::PyException e; // extract the Python error text
e.ReportException();
}
}
void DocumentObserverPython::slotStartSaveDocument(const App::Document& doc, const std::string& file)
void DocumentObserverPython::slotStartSaveDocument(const App::Document& doc,
const std::string& file)
{
Base::PyGILStateLocker lock;
try {
Py::Tuple args(2);
args.setItem(0, Py::asObject(const_cast<App::Document&>(doc).getPyObject()));
args.setItem(1, Py::String(file));
Base::pyCall(pyStartSaveDocument.ptr(),args.ptr());
Base::pyCall(pyStartSaveDocument.ptr(), args.ptr());
}
catch (Py::Exception&) {
Base::PyException e; // extract the Python error text
Base::PyException e; // extract the Python error text
e.ReportException();
}
}
void DocumentObserverPython::slotFinishSaveDocument(const App::Document& doc, const std::string& file)
void DocumentObserverPython::slotFinishSaveDocument(const App::Document& doc,
const std::string& file)
{
Base::PyGILStateLocker lock;
try {
Py::Tuple args(2);
args.setItem(0, Py::asObject(const_cast<App::Document&>(doc).getPyObject()));
args.setItem(1, Py::String(file));
Base::pyCall(pyFinishSaveDocument.ptr(),args.ptr());
Base::pyCall(pyFinishSaveDocument.ptr(), args.ptr());
}
catch (Py::Exception&) {
Base::PyException e; // extract the Python error text
Base::PyException e; // extract the Python error text
e.ReportException();
}
}
void DocumentObserverPython::slotBeforeAddingDynamicExtension(const App::ExtensionContainer& extcont, std::string extension)
void DocumentObserverPython::slotBeforeAddingDynamicExtension(
const App::ExtensionContainer& extcont,
std::string extension)
{
Base::PyGILStateLocker lock;
try {
Py::Tuple args(2);
args.setItem(0, Py::asObject(const_cast<App::ExtensionContainer&>(extcont).getPyObject()));
args.setItem(1, Py::String(extension));
Base::pyCall(pyBeforeAddingDynamicExtension.ptr(),args.ptr());
Base::pyCall(pyBeforeAddingDynamicExtension.ptr(), args.ptr());
}
catch (Py::Exception&) {
Base::PyException e; // extract the Python error text
Base::PyException e; // extract the Python error text
e.ReportException();
}
}
void DocumentObserverPython::slotAddedDynamicExtension(const App::ExtensionContainer& extcont, std::string extension)
void DocumentObserverPython::slotAddedDynamicExtension(const App::ExtensionContainer& extcont,
std::string extension)
{
Base::PyGILStateLocker lock;
try {
Py::Tuple args(2);
args.setItem(0, Py::asObject(const_cast<App::ExtensionContainer&>(extcont).getPyObject()));
args.setItem(1, Py::String(extension));
Base::pyCall(pyAddedDynamicExtension.ptr(),args.ptr());
Base::pyCall(pyAddedDynamicExtension.ptr(), args.ptr());
}
catch (Py::Exception&) {
Base::PyException e; // extract the Python error text
Base::PyException e; // extract the Python error text
e.ReportException();
}
}

View File

@@ -106,7 +106,7 @@ private:
/** Called when an object gets a dynamic property removed*/
void slotRemoveDynamicProperty(const App::Property& Prop);
/** Called when an object property gets a new editor relevant status like hidden or read only*/
void slotChangePropertyEditor(const App::Document &Doc, const App::Property& Prop);
void slotChangePropertyEditor(const App::Document& Doc, const App::Property& Prop);
/** Called when a document is about to be saved*/
void slotStartSaveDocument(const App::Document&, const std::string&);
/** Called when an document has been saved*/
@@ -121,12 +121,14 @@ private:
Py::Object inst;
static std::vector<DocumentObserverPython*> _instances;
using Connection = struct PythonObject {
boost::signals2::scoped_connection slot;
Py::Object py;
PyObject* ptr() {
return py.ptr();
}
using Connection = struct PythonObject
{
boost::signals2::scoped_connection slot;
Py::Object py;
PyObject* ptr()
{
return py.ptr();
}
};
Connection pyCreatedDocument;
@@ -160,6 +162,6 @@ private:
Connection pyAddedDynamicExtension;
};
} //namespace App
} // namespace App
#endif // APP_DOCUMENTOBSERVERPYTHON_H
#endif // APP_DOCUMENTOBSERVERPYTHON_H

File diff suppressed because it is too large Load Diff

View File

@@ -33,7 +33,7 @@
#include "PropertyContainer.h"
FC_LOG_LEVEL_INIT("Property",true,true)
FC_LOG_LEVEL_INIT("Property", true, true)
using namespace App;
@@ -46,65 +46,75 @@ DynamicProperty::~DynamicProperty()
clear();
}
void DynamicProperty::clear() {
auto &index = props.get<0>();
for(auto &v : index)
void DynamicProperty::clear()
{
auto& index = props.get<0>();
for (auto& v : index) {
delete v.property;
}
index.clear();
}
void DynamicProperty::getPropertyList(std::vector<Property*> &List) const
void DynamicProperty::getPropertyList(std::vector<Property*>& List) const
{
for (auto &v : props.get<0>())
for (auto& v : props.get<0>()) {
List.push_back(v.property);
}
}
void DynamicProperty::getPropertyNamedList(std::vector<std::pair<const char*, Property*> > &List) const
void DynamicProperty::getPropertyNamedList(
std::vector<std::pair<const char*, Property*>>& List) const
{
for (auto &v : props.get<0>())
List.emplace_back(v.getName(),v.property);
for (auto& v : props.get<0>()) {
List.emplace_back(v.getName(), v.property);
}
}
void DynamicProperty::getPropertyMap(std::map<std::string,Property*> &Map) const
void DynamicProperty::getPropertyMap(std::map<std::string, Property*>& Map) const
{
for (auto &v : props.get<0>())
for (auto& v : props.get<0>()) {
Map[v.name] = v.property;
}
}
Property *DynamicProperty::getDynamicPropertyByName(const char* name) const
Property* DynamicProperty::getDynamicPropertyByName(const char* name) const
{
auto &index = props.get<0>();
auto& index = props.get<0>();
auto it = index.find(name);
if (it != index.end())
if (it != index.end()) {
return it->property;
}
return nullptr;
}
std::vector<std::string> DynamicProperty::getDynamicPropertyNames() const
{
std::vector<std::string> names;
auto &index = props.get<0>();
auto& index = props.get<0>();
names.reserve(index.size());
for(auto &v : index)
for (auto& v : index) {
names.push_back(v.name);
}
return names;
}
short DynamicProperty::getPropertyType(const Property* prop) const
{
return prop?prop->getType():0;
return prop ? prop->getType() : 0;
}
short DynamicProperty::getPropertyType(const char *name) const
short DynamicProperty::getPropertyType(const char* name) const
{
auto &index = props.get<0>();
auto& index = props.get<0>();
auto it = index.find(name);
if (it != index.end()) {
short attr = it->attr;
if (it->hidden)
if (it->hidden) {
attr |= Prop_Hidden;
if (it->readonly)
}
if (it->readonly) {
attr |= Prop_ReadOnly;
}
return attr;
}
return 0;
@@ -112,92 +122,113 @@ short DynamicProperty::getPropertyType(const char *name) const
const char* DynamicProperty::getPropertyGroup(const Property* prop) const
{
auto &index = props.get<1>();
auto& index = props.get<1>();
auto it = index.find(const_cast<Property*>(prop));
if(it!=index.end())
if (it != index.end()) {
return it->group.c_str();
}
return nullptr;
}
const char* DynamicProperty::getPropertyGroup(const char *name) const
const char* DynamicProperty::getPropertyGroup(const char* name) const
{
auto &index = props.get<0>();
auto& index = props.get<0>();
auto it = index.find(name);
if (it != index.end())
if (it != index.end()) {
return it->group.c_str();
}
return nullptr;
}
const char* DynamicProperty::getPropertyDocumentation(const Property* prop) const
{
auto &index = props.get<1>();
auto& index = props.get<1>();
auto it = index.find(const_cast<Property*>(prop));
if(it!=index.end())
if (it != index.end()) {
return it->doc.c_str();
}
return nullptr;
}
const char* DynamicProperty::getPropertyDocumentation(const char *name) const
const char* DynamicProperty::getPropertyDocumentation(const char* name) const
{
auto &index = props.get<0>();
auto& index = props.get<0>();
auto it = index.find(name);
if (it != index.end())
if (it != index.end()) {
return it->doc.c_str();
}
return nullptr;
}
Property* DynamicProperty::addDynamicProperty(PropertyContainer &pc, const char* type,
const char* name, const char* group, const char* doc, short attr, bool ro, bool hidden)
Property* DynamicProperty::addDynamicProperty(PropertyContainer& pc,
const char* type,
const char* name,
const char* group,
const char* doc,
short attr,
bool ro,
bool hidden)
{
if(!type)
if (!type) {
type = "<null>";
}
std::string _name;
static ParameterGrp::handle hGrp = GetApplication().GetParameterGroupByPath(
"User parameter:BaseApp/Preferences/Document");
if(hGrp->GetBool("AutoNameDynamicProperty",false)) {
if(!name || !name[0])
static ParameterGrp::handle hGrp =
GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Document");
if (hGrp->GetBool("AutoNameDynamicProperty", false)) {
if (!name || !name[0]) {
name = type;
_name = getUniquePropertyName(pc,name);
if(_name != name) {
FC_WARN(pc.getFullName() << " rename dynamic property from '"
<< name << "' to '" << _name << "'");
}
_name = getUniquePropertyName(pc, name);
if (_name != name) {
FC_WARN(pc.getFullName()
<< " rename dynamic property from '" << name << "' to '" << _name << "'");
}
name = _name.c_str();
} else if(!name)
name = "<null>"; // setting a bad name to trigger exception
}
else if (!name) {
name = "<null>"; // setting a bad name to trigger exception
}
auto prop = pc.getPropertyByName(name);
if(prop && prop->getContainer()==&pc)
FC_THROWM(Base::NameError, "Property " << pc.getFullName() << '.' << name << " already exists");
if (prop && prop->getContainer() == &pc) {
FC_THROWM(Base::NameError,
"Property " << pc.getFullName() << '.' << name << " already exists");
}
if(Base::Tools::getIdentifier(name) != name)
if (Base::Tools::getIdentifier(name) != name) {
FC_THROWM(Base::NameError, "Invalid property name '" << name << "'");
}
Base::Type propType = Base::Type::getTypeIfDerivedFrom(type, App::Property::getClassTypeId(), true);
Base::Type propType =
Base::Type::getTypeIfDerivedFrom(type, App::Property::getClassTypeId(), true);
if (propType.isBad()) {
FC_THROWM(Base::TypeError, "Invalid type "
<< type << " for property " << pc.getFullName() << '.' << name);
FC_THROWM(Base::TypeError,
"Invalid type " << type << " for property " << pc.getFullName() << '.' << name);
}
void* propInstance = propType.createInstance();
if (!propInstance) {
FC_THROWM(Base::RuntimeError, "Failed to create property "
<< pc.getFullName() << '.' << name << " of type " << type);
FC_THROWM(Base::RuntimeError,
"Failed to create property " << pc.getFullName() << '.' << name << " of type "
<< type);
}
Property* pcProperty = static_cast<Property*>(propInstance);
auto res = props.get<0>().emplace(pcProperty,name, nullptr, group, doc, attr, ro, hidden);
auto res = props.get<0>().emplace(pcProperty, name, nullptr, group, doc, attr, ro, hidden);
pcProperty->setContainer(&pc);
pcProperty->myName = res.first->name.c_str();
if(ro)
if (ro) {
attr |= Prop_ReadOnly;
if(hidden)
}
if (hidden) {
attr |= Prop_Hidden;
}
pcProperty->syncType(attr);
pcProperty->StatusBits.set((size_t)Property::PropDynamic);
@@ -207,21 +238,29 @@ Property* DynamicProperty::addDynamicProperty(PropertyContainer &pc, const char*
return pcProperty;
}
bool DynamicProperty::addProperty(Property *prop)
bool DynamicProperty::addProperty(Property* prop)
{
if(!prop || !prop->hasName())
if (!prop || !prop->hasName()) {
return false;
auto &index = props.get<0>();
if(index.count(prop->getName()))
}
auto& index = props.get<0>();
if (index.count(prop->getName())) {
return false;
index.emplace(prop,std::string(),prop->getName(),
prop->getGroup(),prop->getDocumentation(),prop->getType(),false,false);
}
index.emplace(prop,
std::string(),
prop->getName(),
prop->getGroup(),
prop->getDocumentation(),
prop->getType(),
false,
false);
return true;
}
bool DynamicProperty::removeProperty(const Property *prop)
bool DynamicProperty::removeProperty(const Property* prop)
{
auto &index = props.get<1>();
auto& index = props.get<1>();
auto it = index.find(const_cast<Property*>(prop));
if (it != index.end()) {
index.erase(it);
@@ -232,14 +271,16 @@ bool DynamicProperty::removeProperty(const Property *prop)
bool DynamicProperty::removeDynamicProperty(const char* name)
{
auto &index = props.get<0>();
auto& index = props.get<0>();
auto it = index.find(name);
if (it != index.end()) {
if(it->property->testStatus(Property::LockDynamic))
if (it->property->testStatus(Property::LockDynamic)) {
throw Base::RuntimeError("property is locked");
else if(!it->property->testStatus(Property::PropDynamic))
}
else if (!it->property->testStatus(Property::PropDynamic)) {
throw Base::RuntimeError("property is not dynamic");
Property *prop = it->property;
}
Property* prop = it->property;
GetApplication().signalRemoveDynamicProperty(*prop);
// Handle possible recursive calls of removeDynamicProperty
@@ -255,12 +296,12 @@ bool DynamicProperty::removeDynamicProperty(const char* name)
return false;
}
std::string DynamicProperty::getUniquePropertyName(PropertyContainer &pc, const char *Name) const
std::string DynamicProperty::getUniquePropertyName(PropertyContainer& pc, const char* Name) const
{
std::string CleanName = Base::Tools::getIdentifier(Name);
// name in use?
std::map<std::string,Property*> objectProps;
std::map<std::string, Property*> objectProps;
pc.getPropertyMap(objectProps);
auto pos = objectProps.find(CleanName);
@@ -271,38 +312,42 @@ std::string DynamicProperty::getUniquePropertyName(PropertyContainer &pc, const
else {
std::vector<std::string> names;
names.reserve(objectProps.size());
for (pos = objectProps.begin();pos != objectProps.end();++pos) {
for (pos = objectProps.begin(); pos != objectProps.end(); ++pos) {
names.push_back(pos->first);
}
return Base::Tools::getUniqueName(CleanName, names);
}
}
void DynamicProperty::save(const Property *prop, Base::Writer &writer) const
void DynamicProperty::save(const Property* prop, Base::Writer& writer) const
{
auto &index = props.get<1>();
auto& index = props.get<1>();
auto it = index.find(const_cast<Property*>(prop));
if(it != index.end()) {
auto &data = *it;
if (it != index.end()) {
auto& data = *it;
writer.Stream() << "\" group=\"" << Base::Persistence::encodeAttribute(data.group)
<< "\" doc=\"" << Base::Persistence::encodeAttribute(data.doc)
<< "\" attr=\"" << data.attr << "\" ro=\"" << data.readonly
<< "\" hide=\"" << data.hidden;
<< "\" attr=\"" << data.attr << "\" ro=\"" << data.readonly << "\" hide=\""
<< data.hidden;
}
}
Property *DynamicProperty::restore(PropertyContainer &pc,
const char *PropName, const char *TypeName, Base::XMLReader &reader)
Property* DynamicProperty::restore(PropertyContainer& pc,
const char* PropName,
const char* TypeName,
Base::XMLReader& reader)
{
if (!reader.hasAttribute("group"))
if (!reader.hasAttribute("group")) {
return nullptr;
}
short attribute = 0;
bool readonly = false, hidden = false;
const char *group=nullptr, *doc=nullptr, *attr=nullptr, *ro=nullptr, *hide=nullptr;
const char *group = nullptr, *doc = nullptr, *attr = nullptr, *ro = nullptr, *hide = nullptr;
group = reader.getAttribute("group");
if (reader.hasAttribute("doc"))
if (reader.hasAttribute("doc")) {
doc = reader.getAttribute("doc");
}
if (reader.hasAttribute("attr")) {
attr = reader.getAttribute("attr");
if (attr) {
@@ -312,42 +357,54 @@ Property *DynamicProperty::restore(PropertyContainer &pc,
}
if (reader.hasAttribute("ro")) {
ro = reader.getAttribute("ro");
if (ro) readonly = (ro[0]-48) != 0;
if (ro) {
readonly = (ro[0] - 48) != 0;
}
}
if (reader.hasAttribute("hide")) {
hide = reader.getAttribute("hide");
if (hide) hidden = (hide[0]-48) != 0;
if (hide) {
hidden = (hide[0] - 48) != 0;
}
}
return addDynamicProperty(pc,TypeName, PropName, group, doc, attribute, readonly, hidden);
return addDynamicProperty(pc, TypeName, PropName, group, doc, attribute, readonly, hidden);
}
DynamicProperty::PropData DynamicProperty::getDynamicPropertyData(const Property *prop) const
DynamicProperty::PropData DynamicProperty::getDynamicPropertyData(const Property* prop) const
{
auto &index = props.get<1>();
auto& index = props.get<1>();
auto it = index.find(const_cast<Property*>(prop));
if(it != index.end())
if (it != index.end()) {
return *it;
}
return {};
}
bool DynamicProperty::changeDynamicProperty(const Property *prop, const char *group, const char *doc) {
auto &index = props.get<1>();
bool DynamicProperty::changeDynamicProperty(const Property* prop,
const char* group,
const char* doc)
{
auto& index = props.get<1>();
auto it = index.find(const_cast<Property*>(prop));
if (it == index.end())
if (it == index.end()) {
return false;
if(group)
}
if (group) {
it->group = group;
if(doc)
}
if (doc) {
it->doc = doc;
}
return true;
}
const char *DynamicProperty::getPropertyName(const Property *prop) const
const char* DynamicProperty::getPropertyName(const Property* prop) const
{
auto &index = props.get<1>();
auto& index = props.get<1>();
auto it = index.find(const_cast<Property*>(prop));
if(it != index.end())
if (it != index.end()) {
return it->getName();
}
return nullptr;
}

View File

@@ -35,11 +35,12 @@
#include <boost/multi_index/mem_fun.hpp>
namespace Base {
namespace Base
{
class Writer;
class XMLReader;
class XMLWriter;
}
} // namespace Base
namespace App
{
@@ -48,15 +49,24 @@ class PropertyContainer;
namespace bmi = boost::multi_index;
struct CStringHasher {
inline std::size_t operator()(const char *s) const {
if(!s) return 0;
return boost::hash_range(s,s+std::strlen(s));
struct CStringHasher
{
inline std::size_t operator()(const char* s) const
{
if (!s) {
return 0;
}
return boost::hash_range(s, s + std::strlen(s));
}
inline bool operator()(const char *a, const char *b) const {
if(!a) return !b;
if(!b) return false;
return std::strcmp(a,b)==0;
inline bool operator()(const char* a, const char* b) const
{
if (!a) {
return !b;
}
if (!b) {
return false;
}
return std::strcmp(a, b) == 0;
}
};
@@ -73,13 +83,13 @@ public:
/** @name Access properties */
//@{
/// Get all properties of the class (including parent)
void getPropertyList(std::vector<Property*> &List) const;
void getPropertyList(std::vector<Property*>& List) const;
/// get all properties with their names
void getPropertyNamedList(std::vector<std::pair<const char*,Property*> > &List) const;
void getPropertyNamedList(std::vector<std::pair<const char*, Property*>>& List) const;
/// Get all properties of the class (including parent)
void getPropertyMap(std::map<std::string,Property*> &Map) const;
void getPropertyMap(std::map<std::string, Property*>& Map) const;
/// Find a dynamic property by its name
Property *getDynamicPropertyByName(const char* name) const;
Property* getDynamicPropertyByName(const char* name) const;
/*!
Add a dynamic property of the type @a type and with the name @a name.
@a Group gives the grouping name which appears in the property editor and
@@ -97,22 +107,28 @@ public:
addDynamicProperty(..., ..., "Base","blah", Prop_None, true, true);
@endcode
*/
Property* addDynamicProperty(PropertyContainer &pc, const char* type, const char* name=nullptr, const char* group=nullptr,
const char* doc=nullptr, short attr=0, bool ro=false, bool hidden=false);
Property* addDynamicProperty(PropertyContainer& pc,
const char* type,
const char* name = nullptr,
const char* group = nullptr,
const char* doc = nullptr,
short attr = 0,
bool ro = false,
bool hidden = false);
/** Add a pre-existing property
*
* The property is not treated as dynamic, and will not trigger signal.
*
* @return Return false if there is a property exist with the same name.
*/
bool addProperty(Property *prop);
bool addProperty(Property* prop);
/*!
Removes a dynamic property by name. Returns true if the property is part of the container, otherwise
false is returned.
Removes a dynamic property by name. Returns true if the property is part of the container,
otherwise false is returned.
*/
bool removeDynamicProperty(const char* name);
/// Remove pre-existing property, which will not be deleted.
bool removeProperty(const Property *prop);
bool removeProperty(const Property* prop);
/// Get a list of all dynamic properties.
std::vector<std::string> getDynamicPropertyNames() const;
/// Get the name of a property
@@ -124,72 +140,86 @@ public:
/// Get the attributes of a property
short getPropertyType(const Property* prop) const;
/// Get the attributes of a named property
short getPropertyType(const char *name) const;
short getPropertyType(const char* name) const;
/// Get the group name of a property
const char* getPropertyGroup(const Property* prop) const;
/// Get the group name of a named property
const char* getPropertyGroup(const char *name) const;
const char* getPropertyGroup(const char* name) const;
/// Get the documentation of a property
const char* getPropertyDocumentation(const Property* prop) const;
/// Get the documentation of a named property
const char* getPropertyDocumentation(const char *name) const;
const char* getPropertyDocumentation(const char* name) const;
//@}
/// Remove all properties
void clear();
/// Get property count
size_t size() const { return props.size(); }
size_t size() const
{
return props.size();
}
void save(const Property *prop, Base::Writer &writer) const;
void save(const Property* prop, Base::Writer& writer) const;
Property *restore(PropertyContainer &pc,
const char *PropName, const char *TypeName, Base::XMLReader &reader);
Property* restore(PropertyContainer& pc,
const char* PropName,
const char* TypeName,
Base::XMLReader& reader);
struct PropData {
struct PropData
{
Property* property;
std::string name;
const char *pName;
const char* pName;
mutable std::string group;
mutable std::string doc;
short attr;
bool readonly;
bool hidden;
PropData(Property *prop=nullptr, std::string &&n=std::string(), const char *pn=nullptr,
const char *g=nullptr, const char *d=nullptr, short a=0, bool ro=false, bool h=false)
:property(prop),name(std::move(n)),pName(pn)
,group(g?g:""),doc(d?d:""),attr(a),readonly(ro),hidden(h)
PropData(Property* prop = nullptr,
std::string&& n = std::string(),
const char* pn = nullptr,
const char* g = nullptr,
const char* d = nullptr,
short a = 0,
bool ro = false,
bool h = false)
: property(prop)
, name(std::move(n))
, pName(pn)
, group(g ? g : "")
, doc(d ? d : "")
, attr(a)
, readonly(ro)
, hidden(h)
{}
const char *getName() const {
return pName?pName:name.c_str();
const char* getName() const
{
return pName ? pName : name.c_str();
}
};
PropData getDynamicPropertyData(const Property* prop) const;
bool changeDynamicProperty(const Property *prop, const char *group, const char *doc);
bool changeDynamicProperty(const Property* prop, const char* group, const char* doc);
private:
std::string getUniquePropertyName(PropertyContainer &pc, const char *Name) const;
std::string getUniquePropertyName(PropertyContainer& pc, const char* Name) const;
private:
bmi::multi_index_container<
PropData,
bmi::indexed_by<
bmi::hashed_unique<
bmi::const_mem_fun<PropData, const char*, &PropData::getName>,
CStringHasher,
CStringHasher
>,
bmi::hashed_unique<
bmi::member<PropData, Property*, &PropData::property>
>
>
> props;
bmi::hashed_unique<bmi::const_mem_fun<PropData, const char*, &PropData::getName>,
CStringHasher,
CStringHasher>,
bmi::hashed_unique<bmi::member<PropData, Property*, &PropData::property>>>>
props;
};
} // namespace App
} // namespace App
#endif // APP_DYNAMICPROPERTY_H
#endif // APP_DYNAMICPROPERTY_H

View File

@@ -18,7 +18,7 @@
#include <boost/algorithm/string/split.hpp>
FC_LOG_LEVEL_INIT("ElementMap", true, 2);// NOLINT
FC_LOG_LEVEL_INIT("ElementMap", true, 2); // NOLINT
namespace Data
{
@@ -104,7 +104,8 @@ void ElementMap::beforeSave(const ::App::StringHasherRef& hasherRef) const
}
}
void ElementMap::save(std::ostream& stream, int index,
void ElementMap::save(std::ostream& stream,
int index,
const std::map<const ElementMap*, int>& childMapSet,
const std::map<QByteArray, int>& postfixMap) const
{
@@ -121,7 +122,7 @@ void ElementMap::save(std::ostream& stream, int index,
if (child.elementMap) {
auto it = childMapSet.find(child.elementMap.get());
if (it == childMapSet.end() || it->second == 0) {
FC_ERR("Invalid child element map");// NOLINT
FC_ERR("Invalid child element map"); // NOLINT
}
else {
mapIndex = it->second;
@@ -240,7 +241,7 @@ ElementMapPtr ElementMap::restore(::App::StringHasherRef hasherRef, std::istream
int count = 0;
std::string tmp;
if (!(stream >> id >> tmp >> count) || tmp != "PostfixCount") {
FC_THROWM(Base::RuntimeError, msg);// NOLINT
FC_THROWM(Base::RuntimeError, msg); // NOLINT
}
auto& map = _idToElementMap[id];
@@ -258,7 +259,7 @@ ElementMapPtr ElementMap::restore(::App::StringHasherRef hasherRef, std::istream
std::vector<ElementMapPtr> childMaps;
count = 0;
if (!(stream >> tmp >> count) || tmp != "MapCount" || count == 0) {
FC_THROWM(Base::RuntimeError, msg);// NOLINT
FC_THROWM(Base::RuntimeError, msg); // NOLINT
}
childMaps.reserve(count - 1);
for (int i = 0; i < count - 1; ++i) {
@@ -269,7 +270,8 @@ ElementMapPtr ElementMap::restore(::App::StringHasherRef hasherRef, std::istream
return restore(hasherRef, stream, childMaps, postfixes);
}
ElementMapPtr ElementMap::restore(::App::StringHasherRef hasherRef, std::istream& stream,
ElementMapPtr ElementMap::restore(::App::StringHasherRef hasherRef,
std::istream& stream,
std::vector<ElementMapPtr>& childMaps,
const std::vector<std::string>& postfixes)
{
@@ -281,14 +283,14 @@ ElementMapPtr ElementMap::restore(::App::StringHasherRef hasherRef, std::istream
int typeCount = 0;
unsigned id = 0;
if (!(stream >> tmp >> index >> id >> typeCount) || tmp != "ElementMap") {
FC_THROWM(Base::RuntimeError, msg);// NOLINT
FC_THROWM(Base::RuntimeError, msg); // NOLINT
}
auto& map = _idToElementMap[id];
if (map) {
while (tmp != "EndMap") {
if (!std::getline(stream, tmp)) {
FC_THROWM(Base::RuntimeError, "unexpected end of child element map");// NOLINT
FC_THROWM(Base::RuntimeError, "unexpected end of child element map"); // NOLINT
}
}
return map;
@@ -303,12 +305,12 @@ ElementMapPtr ElementMap::restore(::App::StringHasherRef hasherRef, std::istream
for (int i = 0; i < typeCount; ++i) {
int outerCount = 0;
if (!(stream >> tmp)) {
FC_THROWM(Base::RuntimeError, "missing element type");// NOLINT
FC_THROWM(Base::RuntimeError, "missing element type"); // NOLINT
}
IndexedName idx(tmp.c_str(), 1);
if (!(stream >> tmp >> outerCount) || tmp != "ChildCount") {
FC_THROWM(Base::RuntimeError, "missing element child count");// NOLINT
FC_THROWM(Base::RuntimeError, "missing element child count"); // NOLINT
}
auto& indices = this->indexedNames[idx.getType()];
@@ -319,16 +321,16 @@ ElementMapPtr ElementMap::restore(::App::StringHasherRef hasherRef, std::istream
long tag = 0;
int mapIndex = 0;
if (!(stream >> cIndex >> offset >> count >> tag >> mapIndex >> tmp)) {
FC_THROWM(Base::RuntimeError, "Invalid element child");// NOLINT
FC_THROWM(Base::RuntimeError, "Invalid element child"); // NOLINT
}
if (cIndex < 0) {
FC_THROWM(Base::RuntimeError, "Invalid element child index");// NOLINT
FC_THROWM(Base::RuntimeError, "Invalid element child index"); // NOLINT
}
if (offset < 0) {
FC_THROWM(Base::RuntimeError, "Invalid element child offset");// NOLINT
FC_THROWM(Base::RuntimeError, "Invalid element child offset"); // NOLINT
}
if (mapIndex >= index || mapIndex < 0 || mapIndex > (int)childMaps.size()) {
FC_THROWM(Base::RuntimeError, "Invalid element child map index");// NOLINT
FC_THROWM(Base::RuntimeError, "Invalid element child map index"); // NOLINT
}
auto& child = indices.children[cIndex + offset + count];
child.indexedName = IndexedName::fromConst(idx.getType(), cIndex);
@@ -346,7 +348,7 @@ ElementMapPtr ElementMap::restore(::App::StringHasherRef hasherRef, std::istream
this->childElementSize += child.count;
if (!(stream >> tmp)) {
FC_THROWM(Base::RuntimeError, "Invalid element child string id");// NOLINT
FC_THROWM(Base::RuntimeError, "Invalid element child string id"); // NOLINT
}
tokens.clear();
@@ -371,7 +373,7 @@ ElementMapPtr ElementMap::restore(::App::StringHasherRef hasherRef, std::istream
}
if (!(stream >> tmp >> outerCount) || tmp != "NameCount") {
FC_THROWM(Base::RuntimeError, "missing element name outerCount");// NOLINT
FC_THROWM(Base::RuntimeError, "missing element name outerCount"); // NOLINT
}
boost::io::ios_flags_saver ifs(stream);
@@ -384,7 +386,7 @@ ElementMapPtr ElementMap::restore(::App::StringHasherRef hasherRef, std::istream
int innerCount = 0;
while (true) {
if (!(stream >> tmp)) {
FC_THROWM(Base::RuntimeError, "Failed to read element name");// NOLINT
FC_THROWM(Base::RuntimeError, "Failed to read element name"); // NOLINT
}
if (tmp == "0") {
break;
@@ -396,7 +398,7 @@ ElementMapPtr ElementMap::restore(::App::StringHasherRef hasherRef, std::istream
tokens.clear();
boost::split(tokens, tmp, boost::is_any_of("."));
if (tokens.size() < 2) {
FC_THROWM(Base::RuntimeError, "Invalid element entry");// NOLINT
FC_THROWM(Base::RuntimeError, "Invalid element entry"); // NOLINT
}
int offset = 1;
@@ -406,12 +408,12 @@ ElementMapPtr ElementMap::restore(::App::StringHasherRef hasherRef, std::istream
switch (tokens[0][0]) {
case ':': {
if (tokens.size() < 3) {
FC_THROWM(Base::RuntimeError, "Invalid element entry");// NOLINT
FC_THROWM(Base::RuntimeError, "Invalid element entry"); // NOLINT
}
++offset;
long elementNameIndex = strtol(tokens[0].c_str() + 1, nullptr, hexBase);
if (elementNameIndex <= 0 || elementNameIndex > (int)postfixes.size()) {
FC_THROWM(Base::RuntimeError, "Invalid element name index");// NOLINT
FC_THROWM(Base::RuntimeError, "Invalid element name index"); // NOLINT
}
long elementIndex = strtol(tokens[1].c_str(), nullptr, hexBase);
ref->name = MappedName(
@@ -427,7 +429,7 @@ ElementMapPtr ElementMap::restore(::App::StringHasherRef hasherRef, std::istream
ref->name = MappedName(tokens[0].c_str() + 1);
break;
default:
FC_THROWM(Base::RuntimeError, "Invalid element name marker");// NOLINT
FC_THROWM(Base::RuntimeError, "Invalid element name marker"); // NOLINT
}
if (tokens[offset] != "0") {
@@ -473,31 +475,34 @@ ElementMapPtr ElementMap::restore(::App::StringHasherRef hasherRef, std::istream
}
}
if (hasherWarn) {
FC_WARN(hasherWarn);// NOLINT
FC_WARN(hasherWarn); // NOLINT
}
if (hasherIDWarn) {
FC_WARN(hasherIDWarn);// NOLINT
FC_WARN(hasherIDWarn); // NOLINT
}
if (postfixWarn) {
FC_WARN(postfixWarn);// NOLINT
FC_WARN(postfixWarn); // NOLINT
}
if (childSIDWarn) {
FC_WARN(childSIDWarn);// NOLINT
FC_WARN(childSIDWarn); // NOLINT
}
if (!(stream >> tmp) || tmp != "EndMap") {
FC_THROWM(Base::RuntimeError, "unexpected end of child element map");// NOLINT
FC_THROWM(Base::RuntimeError, "unexpected end of child element map"); // NOLINT
}
return shared_from_this();
}
MappedName ElementMap::addName(MappedName& name, const IndexedName& idx, const ElementIDRefs& sids,
bool overwrite, IndexedName* existing)
MappedName ElementMap::addName(MappedName& name,
const IndexedName& idx,
const ElementIDRefs& sids,
bool overwrite,
IndexedName* existing)
{
if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) {
if (name.find("#") >= 0 && name.findTagInElementName() < 0) {
FC_ERR("missing tag postfix " << name);// NOLINT
FC_ERR("missing tag postfix " << name); // NOLINT
}
}
while (true) {
@@ -505,14 +510,14 @@ MappedName ElementMap::addName(MappedName& name, const IndexedName& idx, const E
erase(idx);
}
auto ret = mappedNames.insert(std::make_pair(name, idx));
if (ret.second) { // element just inserted did not exist yet in the map
ret.first->first.compact();// FIXME see MappedName.cpp
if (ret.second) { // element just inserted did not exist yet in the map
ret.first->first.compact(); // FIXME see MappedName.cpp
mappedRef(idx).append(ret.first->first, sids);
FC_TRACE(idx << " -> " << name);// NOLINT
FC_TRACE(idx << " -> " << name); // NOLINT
return ret.first->first;
}
if (ret.first->second == idx) {
FC_TRACE("duplicate " << idx << " -> " << name);// NOLINT
FC_TRACE("duplicate " << idx << " -> " << name); // NOLINT
return ret.first->first;
}
if (!overwrite) {
@@ -526,7 +531,8 @@ MappedName ElementMap::addName(MappedName& name, const IndexedName& idx, const E
};
}
void ElementMap::addPostfix(const QByteArray& postfix, std::map<QByteArray, int>& postfixMap,
void ElementMap::addPostfix(const QByteArray& postfix,
std::map<QByteArray, int>& postfixMap,
std::vector<QByteArray>& postfixes)
{
if (postfix.isEmpty()) {
@@ -539,8 +545,11 @@ void ElementMap::addPostfix(const QByteArray& postfix, std::map<QByteArray, int>
}
}
MappedName ElementMap::setElementName(const IndexedName& element, const MappedName& name,
long masterTag, const ElementIDRefs* sid, bool overwrite)
MappedName ElementMap::setElementName(const IndexedName& element,
const MappedName& name,
long masterTag,
const ElementIDRefs* sid,
bool overwrite)
{
if (!element) {
throw Base::ValueError("Invalid input");
@@ -553,13 +562,13 @@ MappedName ElementMap::setElementName(const IndexedName& element, const MappedNa
for (int i = 0, count = name.size(); i < count; ++i) {
char check = name[i];
if (check == '.' || (std::isspace((int)check) != 0)) {
FC_THROWM(Base::RuntimeError, "Illegal character in mapped name: " << name);// NOLINT
FC_THROWM(Base::RuntimeError, "Illegal character in mapped name: " << name); // NOLINT
}
}
for (const char* readChar = element.getType(); *readChar != 0; ++readChar) {
char check = *readChar;
if (check == '.' || (std::isspace((int)check) != 0)) {
FC_THROWM(Base::RuntimeError,// NOLINT
FC_THROWM(Base::RuntimeError, // NOLINT
"Illegal character in element name: " << element);
}
}
@@ -586,7 +595,7 @@ MappedName ElementMap::setElementName(const IndexedName& element, const MappedNa
}
const int maxAttempts {100};
if (++i == maxAttempts) {
FC_ERR("unresolved duplicate element mapping '"// NOLINT
FC_ERR("unresolved duplicate element mapping '" // NOLINT
<< name << ' ' << element << '/' << existing);
return name;
}
@@ -602,9 +611,14 @@ MappedName ElementMap::setElementName(const IndexedName& element, const MappedNa
}
// try to hash element name while preserving the source tag
void ElementMap::encodeElementName(char element_type, MappedName& name, std::ostringstream& ss,
ElementIDRefs* sids, long masterTag, const char* postfix,
long tag, bool forceTag) const
void ElementMap::encodeElementName(char element_type,
MappedName& name,
std::ostringstream& ss,
ElementIDRefs* sids,
long masterTag,
const char* postfix,
long tag,
bool forceTag) const
{
if (postfix && (postfix[0] != 0)) {
if (!boost::starts_with(postfix, ELEMENT_MAP_PREFIX)) {
@@ -715,26 +729,30 @@ MappedName ElementMap::dehashElementName(const MappedName& name) const
auto sid = this->hasher->getID(id);
if (!sid) {
if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_TRACE)) {
FC_WARN("failed to find hash id " << id);// NOLINT
FC_WARN("failed to find hash id " << id); // NOLINT
}
else {
FC_LOG("failed to find hash id " << id);// NOLINT
FC_LOG("failed to find hash id " << id); // NOLINT
}
return name;
}
if (sid.isHashed()) {
FC_LOG("cannot de-hash id " << id);// NOLINT
FC_LOG("cannot de-hash id " << id); // NOLINT
return name;
}
MappedName ret(sid);
// sid.toString());// FIXME .toString() was missing in original function. is this correct?
FC_TRACE("de-hash " << name << " -> " << ret);// NOLINT
// sid.toString());// FIXME .toString() was missing in original function. is this
// correct?
FC_TRACE("de-hash " << name << " -> " << ret); // NOLINT
return ret;
}
MappedName ElementMap::renameDuplicateElement(int index, const IndexedName& element,
const IndexedName& element2, const MappedName& name,
ElementIDRefs& sids, long masterTag) const
MappedName ElementMap::renameDuplicateElement(int index,
const IndexedName& element,
const IndexedName& element2,
const MappedName& name,
ElementIDRefs& sids,
long masterTag) const
{
int idx {0};
#ifdef FC_DEBUG
@@ -751,7 +769,7 @@ MappedName ElementMap::renameDuplicateElement(int index, const IndexedName& elem
MappedName renamed(name);
encodeElementName(element.getType()[0], renamed, ss, &sids, masterTag);
if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) {
FC_WARN("duplicate element mapping '"// NOLINT
FC_WARN("duplicate element mapping '" // NOLINT
<< name << " -> " << renamed << ' ' << element << '/' << element2);
}
return renamed;
@@ -1008,13 +1026,20 @@ void ElementMap::hashChildMaps(long masterTag)
.findTagInElementName(&tag, &len, nullptr, nullptr, false, false);
// TODO: What is this 10?
if (pos > 10) {
MappedName postfix = hashElementName(
MappedName::fromRawData(child.postfix.constData(), pos), child.sids);
MappedName postfix =
hashElementName(MappedName::fromRawData(child.postfix.constData(), pos),
child.sids);
ss.str("");
ss << MAPPED_CHILD_ELEMENTS_PREFIX << postfix;
MappedName tmp;
encodeElementName(
child.indexedName[0], tmp, ss, nullptr, masterTag, nullptr, child.tag, true);
encodeElementName(child.indexedName[0],
tmp,
ss,
nullptr,
masterTag,
nullptr,
child.tag,
true);
this->childElements.remove(child.postfix);
child.postfix = tmp.toBytes();
this->childElements[child.postfix].childMap = &child;
@@ -1152,7 +1177,7 @@ void ElementMap::addChildElements(long masterTag, const std::vector<MappedChildE
for (auto& child : expansion.empty() ? children : expansion) {
if (!child.indexedName || (child.count == 0)) {
if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) {
FC_ERR("invalid mapped child element");// NOLINT
FC_ERR("invalid mapped child element"); // NOLINT
}
continue;
}
@@ -1195,15 +1220,20 @@ void ElementMap::addChildElements(long masterTag, const std::vector<MappedChildE
if (!name) {
if ((child.tag == 0) || child.tag == masterTag) {
if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) {
FC_WARN("unmapped element");// NOLINT
FC_WARN("unmapped element"); // NOLINT
}
continue;
}
name = MappedName(childIdx);
}
ss.str("");
encodeElementName(
idx[0], name, ss, &sids, masterTag, child.postfix.constData(), child.tag);
encodeElementName(idx[0],
name,
ss,
&sids,
masterTag,
child.postfix.constData(),
child.tag);
setElementName(idx, name, masterTag, &sids);
}
continue;
@@ -1231,19 +1261,20 @@ void ElementMap::addChildElements(long masterTag, const std::vector<MappedChildE
entry = &childElements[tmp.toBytes()];
if (entry->childMap) {
FC_ERR("duplicate mapped child element");// NOLINT
FC_ERR("duplicate mapped child element"); // NOLINT
continue;
}
}
auto& indices = this->indexedNames[child.indexedName.getType()];
auto res = indices.children.emplace(
child.indexedName.getIndex() + child.offset + child.count, child);
auto res =
indices.children.emplace(child.indexedName.getIndex() + child.offset + child.count,
child);
if (!res.second) {
if (!entry->childMap) {
this->childElements.remove(tmp.toBytes());
}
FC_ERR("duplicate mapped child element");// NOLINT
FC_ERR("duplicate mapped child element"); // NOLINT
continue;
}
@@ -1293,7 +1324,9 @@ std::vector<MappedElement> ElementMap::getAll() const
return ret;
}
long ElementMap::getElementHistory(const MappedName& name, long masterTag, MappedName* original,
long ElementMap::getElementHistory(const MappedName& name,
long masterTag,
MappedName* original,
std::vector<MappedName>* history) const
{
long tag = 0;
@@ -1321,7 +1354,7 @@ long ElementMap::getElementHistory(const MappedName& name, long masterTag, Mappe
while (true) {
if ((len == 0) || len > pos) {
FC_WARN("invalid name length " << name);// NOLINT
FC_WARN("invalid name length " << name); // NOLINT
return 0;
}
bool deHashed = false;
@@ -1440,4 +1473,4 @@ void ElementMap::traceElement(const MappedName& name, long masterTag, TraceCallb
}
}// Namespace Data
} // Namespace Data

View File

@@ -46,17 +46,17 @@ class ElementMap;
using ElementMapPtr = std::shared_ptr<ElementMap>;
/** Element trace callback
*
* The callback has the following call signature
* (const std::string &name, size_t offset, long encodedTag, long tag) -> bool
*
* @param name: the current element name.
* @param offset: the offset skipping the encoded element name for the next iteration.
* @param encodedTag: the tag encoded inside the current element, which is usually the tag
* of the previous step in the shape history.
* @param tag: the tag of the current shape element.
*
* @sa traceElement()
*
* The callback has the following call signature
* (const std::string &name, size_t offset, long encodedTag, long tag) -> bool
*
* @param name: the current element name.
* @param offset: the offset skipping the encoded element name for the next iteration.
* @param encodedTag: the tag encoded inside the current element, which is usually the tag
* of the previous step in the shape history.
* @param tag: the tag of the current shape element.
*
* @sa traceElement()
*/
typedef std::function<bool(const MappedName&, int, long, long)> TraceCallback;
@@ -68,13 +68,14 @@ typedef std::function<bool(const MappedName&, int, long, long)> TraceCallback;
* possibly a recursive elementmap
* `mappedNames` maps a MappedName to a specific IndexedName.
*/
class AppExport ElementMap: public std::enable_shared_from_this<ElementMap> //TODO can remove shared_from_this?
class AppExport ElementMap
: public std::enable_shared_from_this<ElementMap> // TODO can remove shared_from_this?
{
public:
/** Default constructor: hooks internal functions to \c signalSaveDocument and
* \c signalStartRestoreDocument. This is related to the save and restore process
* of the map.
*/
*/
ElementMap();
/** Ensures that naming is properly assigned. It then marks as "used" all the StringID
@@ -82,14 +83,14 @@ public:
* as a parameter. Finally do this recursively for all childEelementMaps as well.
*
* @param hasherRef where all the StringID needed to build the map are stored.
*/
*/
// FIXME this should be made part of \c save, to achieve symmetry with the restore method
void beforeSave(const ::App::StringHasherRef& hasherRef) const;
/** Serialize this map. Calls \c collectChildMaps to get \c childMapSet and
* \c postfixMap, then calls the other (private) save function with those parameters.
* @param stream: serialized stream
*/
*/
void save(std::ostream& stream) const;
/** Deserialize and restore this map. This function restores \c childMaps and
@@ -97,7 +98,7 @@ public:
* parameters.
* @param hasherRef: where all the StringIDs are stored
* @param stream: stream to deserialize
*/
*/
ElementMapPtr restore(::App::StringHasherRef hasherRef, std::istream& stream);
@@ -196,9 +197,10 @@ public:
std::vector<MappedElement> getAll() const;
long getElementHistory(const MappedName & name,
long getElementHistory(const MappedName& name,
long masterTag,
MappedName *original=nullptr, std::vector<MappedName> *history=nullptr) const;
MappedName* original = nullptr,
std::vector<MappedName>* history = nullptr) const;
/** Iterate through the history of the give element name with a given callback
*
@@ -214,8 +216,10 @@ private:
* @param stream: serialized stream
* @param childMapSet: where all child element maps are stored
* @param postfixMap. where all postfixes are stored
*/
void save(std::ostream& stream, int index, const std::map<const ElementMap*, int>& childMapSet,
*/
void save(std::ostream& stream,
int index,
const std::map<const ElementMap*, int>& childMapSet,
const std::map<QByteArray, int>& postfixMap) const;
/** Deserialize and restore this map.
@@ -223,8 +227,9 @@ private:
* @param stream: stream to deserialize
* @param childMaps: where all child element maps are stored
* @param postfixes. where all postfixes are stored
*/
ElementMapPtr restore(::App::StringHasherRef hasherRef, std::istream& stream,
*/
ElementMapPtr restore(::App::StringHasherRef hasherRef,
std::istream& stream,
std::vector<ElementMapPtr>& childMaps,
const std::vector<std::string>& postfixes);
@@ -237,20 +242,27 @@ private:
* associated with another indexedName, set \c existing to that indexedname
* @return the name just added, or an empty name if it wasn't added.
*/
MappedName addName(MappedName& name, const IndexedName& idx, const ElementIDRefs& sids,
bool overwrite, IndexedName* existing);
MappedName addName(MappedName& name,
const IndexedName& idx,
const ElementIDRefs& sids,
bool overwrite,
IndexedName* existing);
/** Utility function that adds \c postfix to \c postfixMap, and to \c postfixes
* if it was not present in the map.
*/
static void addPostfix(const QByteArray& postfix, std::map<QByteArray, int>& postfixMap,
*/
static void addPostfix(const QByteArray& postfix,
std::map<QByteArray, int>& postfixMap,
std::vector<QByteArray>& postfixes);
/* Note: the original proc passed `ComplexGeoData& master` for getting the `Tag`,
* now it just passes `long masterTag`.*/
MappedName renameDuplicateElement(int index, const IndexedName& element,
const IndexedName& element2, const MappedName& name,
ElementIDRefs& sids, long masterTag) const;
MappedName renameDuplicateElement(int index,
const IndexedName& element,
const IndexedName& element2,
const MappedName& name,
ElementIDRefs& sids,
long masterTag) const;
/** Convenience method to hash the main element name
*
@@ -263,7 +275,7 @@ private:
/// Reverse hashElementName()
MappedName dehashElementName(const MappedName& name) const;
//FIXME duplicate code? as in copy/paste
// FIXME duplicate code? as in copy/paste
const MappedNameRef* findMappedRef(const IndexedName& idx) const;
MappedNameRef* findMappedRef(const IndexedName& idx);
@@ -313,6 +325,6 @@ public:
};
}// namespace Data
} // namespace Data
#endif// DATA_ELEMENTMAP_H
#endif // DATA_ELEMENTMAP_H

View File

@@ -4,96 +4,119 @@
#include <boost/algorithm/string/predicate.hpp>
const char *Data::isMappedElement(const char *name) {
if(name && boost::starts_with(name, ELEMENT_MAP_PREFIX))
const char* Data::isMappedElement(const char* name)
{
if (name && boost::starts_with(name, ELEMENT_MAP_PREFIX)) {
return name + ELEMENT_MAP_PREFIX_SIZE;
}
return nullptr;
}
std::string Data::newElementName(const char *name) {
if(!name)
std::string Data::newElementName(const char* name)
{
if (!name) {
return {};
const char *dot = strrchr(name,'.');
if(!dot || dot==name)
}
const char* dot = strrchr(name, '.');
if (!dot || dot == name) {
return name;
const char *c = dot-1;
for(;c!=name;--c) {
if(*c == '.') {
}
const char* c = dot - 1;
for (; c != name; --c) {
if (*c == '.') {
++c;
break;
}
}
if(isMappedElement(c))
return std::string(name,dot-name);
if (isMappedElement(c)) {
return std::string(name, dot - name);
}
return name;
}
std::string Data::oldElementName(const char *name) {
if(!name)
std::string Data::oldElementName(const char* name)
{
if (!name) {
return {};
const char *dot = strrchr(name,'.');
if(!dot || dot==name)
}
const char* dot = strrchr(name, '.');
if (!dot || dot == name) {
return name;
const char *c = dot-1;
for(;c!=name;--c) {
if(*c == '.') {
}
const char* c = dot - 1;
for (; c != name; --c) {
if (*c == '.') {
++c;
break;
}
}
if(isMappedElement(c))
return std::string(name,c-name)+(dot+1);
if (isMappedElement(c)) {
return std::string(name, c - name) + (dot + 1);
}
return name;
}
std::string Data::noElementName(const char *name) {
if(!name)
std::string Data::noElementName(const char* name)
{
if (!name) {
return {};
}
auto element = findElementName(name);
if(element)
return std::string(name,element-name);
if (element) {
return std::string(name, element - name);
}
return name;
}
const char *Data::findElementName(const char *subname) {
const char* Data::findElementName(const char* subname)
{
// skip leading dots
while(subname && subname[0] == '.')
while (subname && subname[0] == '.') {
++subname;
if(!subname || !subname[0] || isMappedElement(subname))
}
if (!subname || !subname[0] || isMappedElement(subname)) {
return subname;
const char *dot = strrchr(subname,'.');
if(!dot)
}
const char* dot = strrchr(subname, '.');
if (!dot) {
return subname;
const char *element = dot+1;
if(dot==subname || isMappedElement(element))
}
const char* element = dot + 1;
if (dot == subname || isMappedElement(element)) {
return element;
for(--dot;dot!=subname;--dot) {
if(*dot == '.') {
}
for (--dot; dot != subname; --dot) {
if (*dot == '.') {
++dot;
break;
}
}
if(isMappedElement(dot))
if (isMappedElement(dot)) {
return dot;
}
return element;
}
bool Data::hasMissingElement(const char *subname) {
if(!subname)
bool Data::hasMissingElement(const char* subname)
{
if (!subname) {
return false;
auto dot = strrchr(subname,'.');
if(dot)
subname = dot+1;
}
auto dot = strrchr(subname, '.');
if (dot) {
subname = dot + 1;
}
return boost::starts_with(subname, MISSING_PREFIX);
}
const char *Data::hasMappedElementName(const char *subname) {
const char* Data::hasMappedElementName(const char* subname)
{
return isMappedElement(findElementName(subname));
}
const std::string Data::indexSuffix(int index, const char *label)
const std::string Data::indexSuffix(int index, const char* label)
{
if ( index < 2 ) { // Don't add a suffix for item #1, begin appending at 2
if (index < 2) { // Don't add a suffix for item #1, begin appending at 2
return {""};
}
std::string name(label);

View File

@@ -18,10 +18,10 @@ struct ElementNamePair
ElementNamePair() = default;
ElementNamePair(std::string newNameStr, std::string oldNameStr) :
newName(std::move(newNameStr)), oldName(std::move(oldNameStr))
{
}
ElementNamePair(std::string newNameStr, std::string oldNameStr)
: newName(std::move(newNameStr))
, oldName(std::move(oldNameStr))
{}
bool operator==(const ElementNamePair& other) const
{
@@ -34,7 +34,7 @@ struct ElementNamePair
}
};
}
} // namespace App
// clang-format off
namespace Data
@@ -101,4 +101,4 @@ AppExport const std::string indexSuffix(int index, const char *label=ELEMENT_MAP
} // namespace Data
// clang-format on
#endif // ELEMENT_NAMING_UTILS_H
#endif // ELEMENT_NAMING_UTILS_H

View File

@@ -22,8 +22,8 @@
#include "PreCompiled.h"
#ifndef _PreComp_
# include <cassert>
# include <cstring>
#include <cassert>
#include <cstring>
#endif
#include <Base/Exception.h>
@@ -32,17 +32,23 @@
using namespace App;
namespace {
struct StringCopy : public Enumeration::Object {
explicit StringCopy(const char* str) : d(str) {
}
const char* data() const override {
namespace
{
struct StringCopy: public Enumeration::Object
{
explicit StringCopy(const char* str)
: d(str)
{}
const char* data() const override
{
return d.data();
}
bool isEqual(const char* str) const override {
bool isEqual(const char* str) const override
{
return d == str;
}
bool isCustom() const override {
bool isCustom() const override
{
return true;
}
@@ -50,43 +56,47 @@ private:
std::string d;
};
struct StringView : public Enumeration::Object {
explicit StringView(const char* str) : d(str) {
}
const char* data() const override {
struct StringView: public Enumeration::Object
{
explicit StringView(const char* str)
: d(str)
{}
const char* data() const override
{
return d.data();
}
bool isEqual(const char* str) const override {
bool isEqual(const char* str) const override
{
return d == str;
}
bool isCustom() const override {
bool isCustom() const override
{
return false;
}
private:
std::string_view d;
};
}
} // namespace
Enumeration::Enumeration()
: _index(0)
{
}
{}
Enumeration::Enumeration(const Enumeration &other)
Enumeration::Enumeration(const Enumeration& other)
{
enumArray = other.enumArray;
_index = other._index;
}
Enumeration::Enumeration(const char *valStr)
Enumeration::Enumeration(const char* valStr)
: _index(0)
{
enumArray.push_back(std::make_shared<StringCopy>(valStr));
setValue(valStr);
}
Enumeration::Enumeration(const char **list, const char *valStr)
Enumeration::Enumeration(const char** list, const char* valStr)
: _index(0)
{
while (list && *list) {
@@ -101,14 +111,15 @@ Enumeration::~Enumeration()
enumArray.clear();
}
void Enumeration::setEnums(const char **plEnums)
void Enumeration::setEnums(const char** plEnums)
{
std::string oldValue;
bool preserve = (isValid() && plEnums != nullptr);
if (preserve) {
const char* str = getCStr();
if (str)
if (str) {
oldValue = str;
}
}
enumArray.clear();
@@ -118,14 +129,15 @@ void Enumeration::setEnums(const char **plEnums)
}
// set _index
if (_index < 0)
if (_index < 0) {
_index = 0;
}
if (preserve) {
setValue(oldValue);
}
}
void Enumeration::setEnums(const std::vector<std::string> &values)
void Enumeration::setEnums(const std::vector<std::string>& values)
{
if (values.empty()) {
setEnums(nullptr);
@@ -136,24 +148,26 @@ void Enumeration::setEnums(const std::vector<std::string> &values)
bool preserve = isValid();
if (preserve) {
const char* str = getCStr();
if (str)
if (str) {
oldValue = str;
}
}
enumArray.clear();
for (const auto & it : values) {
for (const auto& it : values) {
enumArray.push_back(std::make_shared<StringCopy>(it.c_str()));
}
// set _index
if (_index < 0)
if (_index < 0) {
_index = 0;
}
if (preserve) {
setValue(oldValue);
}
}
void Enumeration::setValue(const char *value)
void Enumeration::setValue(const char* value)
{
_index = 0;
for (std::size_t i = 0; i < enumArray.size(); i++) {
@@ -168,41 +182,45 @@ void Enumeration::setValue(long value, bool checkRange)
{
if (value >= 0 && value < countItems()) {
_index = value;
} else {
}
else {
if (checkRange) {
throw Base::ValueError("Out of range");
} else {
}
else {
_index = value;
}
}
}
bool Enumeration::isValue(const char *value) const
bool Enumeration::isValue(const char* value) const
{
int i = getInt();
if (i == -1) {
return false;
} else {
}
else {
return enumArray[i]->isEqual(value);
}
}
bool Enumeration::contains(const char *value) const
bool Enumeration::contains(const char* value) const
{
if (!isValid()) {
return false;
}
for (const auto& it : enumArray) {
if (it->isEqual(value))
if (it->isEqual(value)) {
return true;
}
}
return false;
}
const char * Enumeration::getCStr() const
const char* Enumeration::getCStr() const
{
if (!isValid() || _index < 0 || _index >= countItems()) {
return nullptr;
@@ -223,8 +241,9 @@ int Enumeration::getInt() const
std::vector<std::string> Enumeration::getEnumVector() const
{
std::vector<std::string> list;
for (const auto& it : enumArray)
for (const auto& it : enumArray) {
list.emplace_back(it->data());
}
return list;
}
@@ -241,24 +260,27 @@ bool Enumeration::isValid() const
int Enumeration::maxValue() const
{
int num = -1;
if (!enumArray.empty())
if (!enumArray.empty()) {
num = static_cast<int>(enumArray.size()) - 1;
}
return num;
}
bool Enumeration::isCustom() const
{
for (const auto& it : enumArray) {
if (it->isCustom())
if (it->isCustom()) {
return true;
}
}
return false;
}
Enumeration & Enumeration::operator=(const Enumeration &other)
Enumeration& Enumeration::operator=(const Enumeration& other)
{
if (this == &other)
if (this == &other) {
return *this;
}
enumArray = other.enumArray;
_index = other._index;
@@ -266,23 +288,26 @@ Enumeration & Enumeration::operator=(const Enumeration &other)
return *this;
}
bool Enumeration::operator==(const Enumeration &other) const
bool Enumeration::operator==(const Enumeration& other) const
{
if (_index != other._index || enumArray.size() != other.enumArray.size()) {
return false;
}
for (size_t i = 0; i < enumArray.size(); ++i) {
if (enumArray[i]->data() == other.enumArray[i]->data())
if (enumArray[i]->data() == other.enumArray[i]->data()) {
continue;
if (!enumArray[i]->data() || !other.enumArray[i]->data())
}
if (!enumArray[i]->data() || !other.enumArray[i]->data()) {
return false;
if (!enumArray[i]->isEqual(other.enumArray[i]->data()))
}
if (!enumArray[i]->isEqual(other.enumArray[i]->data())) {
return false;
}
}
return true;
}
bool Enumeration::operator==(const char *other) const
bool Enumeration::operator==(const char* other) const
{
if (!getCStr()) {
return false;

View File

@@ -31,162 +31,166 @@
namespace App
{
/// A bidirectional string-integer mapping
/*!
* This is mainly intended for two purposes: working around the difficulty
* in C++ of sharing enumerations between different source files,
* namespaces, etc. and as the data type stored by App::PropertyEnumeration
*
* Internally, Enumeration maintains
* -# Either a const pointer to an array of C-style strings, or a vector
* of C++ std::strings
* -# An integer index into that array/vector representing the string
* representing the instance's value.
*
* If built with FC_DEBUG defined, some boundaries of passed in pointers
* will be checked. Otherwise, the caller has the responsibility of
* checking the limits of given indices.
*
* \todo Implement lazy copy
*/
class AppExport Enumeration
/// A bidirectional string-integer mapping
/*!
* This is mainly intended for two purposes: working around the difficulty
* in C++ of sharing enumerations between different source files,
* namespaces, etc. and as the data type stored by App::PropertyEnumeration
*
* Internally, Enumeration maintains
* -# Either a const pointer to an array of C-style strings, or a vector
* of C++ std::strings
* -# An integer index into that array/vector representing the string
* representing the instance's value.
*
* If built with FC_DEBUG defined, some boundaries of passed in pointers
* will be checked. Otherwise, the caller has the responsibility of
* checking the limits of given indices.
*
* \todo Implement lazy copy
*/
class AppExport Enumeration
{
public:
class Object
{
public:
class Object {
public:
virtual ~Object() = default;
virtual const char* data() const = 0;
virtual bool isEqual(const char*) const = 0;
virtual bool isCustom() const = 0;
};
virtual ~Object() = default;
virtual const char* data() const = 0;
virtual bool isEqual(const char*) const = 0;
virtual bool isCustom() const = 0;
};
public:
/// Constructs an empty Enumeration object
Enumeration();
public:
/// Constructs an empty Enumeration object
Enumeration();
/// Standard copy constructor
Enumeration(const Enumeration& other);
/// Standard copy constructor
Enumeration(const Enumeration& other);
/// Constructs an Enumeration with a single element
explicit Enumeration(const char *valStr);
/// Constructs an Enumeration with a single element
explicit Enumeration(const char* valStr);
/// Constructs an Enumeration using val within list
Enumeration(const char **list, const char *valStr);
/// Constructs an Enumeration using val within list
Enumeration(const char** list, const char* valStr);
/// Standard destructor
~Enumeration();
/// Standard destructor
~Enumeration();
/** Sets the enumeration string list
* The list is a NULL terminated array of pointers to const
* char* strings.
* \code
* const char enums[] = {"Black","White","Other",NULL}
* \endcode
*
* If Enumeration was already valid, will attempt to preserve
* the string-representation value of the Enumeration
*
* Enumeration does not take ownership of the passed object
*/
void setEnums(const char **plEnums);
/** Sets the enumeration string list
* The list is a NULL terminated array of pointers to const
* char* strings.
* \code
* const char enums[] = {"Black","White","Other",NULL}
* \endcode
*
* If Enumeration was already valid, will attempt to preserve
* the string-representation value of the Enumeration
*
* Enumeration does not take ownership of the passed object
*/
void setEnums(const char** plEnums);
/// Set all enum values as vector of strings
/*!
* This method causes the Enumeration to dynamically allocate
* it's own array of C Strings, which will be deleted by the
* destructor or subsequent calls to setEnums(). So, it is
* important to make sure the Enumeration stays in scope as
* long as values returned by getCStr are in use.
*
* If Enumeration was already valid, will attempt to preserve
* the string-representation value of the Enumeration
*/
void setEnums(const std::vector<std::string> &values);
/// Set all enum values as vector of strings
/*!
* This method causes the Enumeration to dynamically allocate
* it's own array of C Strings, which will be deleted by the
* destructor or subsequent calls to setEnums(). So, it is
* important to make sure the Enumeration stays in scope as
* long as values returned by getCStr are in use.
*
* If Enumeration was already valid, will attempt to preserve
* the string-representation value of the Enumeration
*/
void setEnums(const std::vector<std::string>& values);
/// Set the enum using a C string
void setValue(const char *value);
/// Set the enum using a C string
void setValue(const char* value);
/// Overload of setValue(const char *value)
void setValue(const std::string &value) {setValue(value.c_str());}
/// Overload of setValue(const char *value)
void setValue(const std::string& value)
{
setValue(value.c_str());
}
/// Set the enum using a long
/*!
* if checkRange is set to true, throws Base::ValueError when
* values are set out of range
*
* Checks for boundaries via assert()
*/
void setValue(long value, bool checkRange = false);
/// Set the enum using a long
/*!
* if checkRange is set to true, throws Base::ValueError when
* values are set out of range
*
* Checks for boundaries via assert()
*/
void setValue(long value, bool checkRange = false);
/// Checks if the property is set to a certain string value
bool isValue(const char *value) const;
/// Checks if the property is set to a certain string value
bool isValue(const char* value) const;
/// Checks if a string is included in the enumeration
bool contains(const char *value) const;
/// Checks if a string is included in the enumeration
bool contains(const char* value) const;
/// Return the value as C string
/*!
* Returns NULL if the enumeration is invalid.
*/
const char * getCStr() const;
/// Return the value as C string
/*!
* Returns NULL if the enumeration is invalid.
*/
const char* getCStr() const;
/// Return value as integer
/*!
* Returns -1 if the Enumeration isn't valid
*/
int getInt() const;
/// Return value as integer
/*!
* Returns -1 if the Enumeration isn't valid
*/
int getInt() const;
/// get all possible enum values as vector of strings
std::vector<std::string> getEnumVector() const;
/// get all possible enum values as vector of strings
std::vector<std::string> getEnumVector() const;
/// returns true if the enum list is non-empty, false otherwise
bool hasEnums() const;
/// returns true if the enum list is non-empty, false otherwise
bool hasEnums() const;
/// Returns true if the instance is in a usable state
bool isValid() const;
/// Returns true if the instance is in a usable state
bool isValid() const;
/// Returns the highest usable integer value for this enum
/*!
* Returns -1 if the enumeration is not valid according to isValid()
*/
int maxValue() const;
/// Returns the highest usable integer value for this enum
/*!
* Returns -1 if the enumeration is not valid according to isValid()
*/
int maxValue() const;
/// Returns true if any of the items is a user-defined string
bool isCustom() const;
/// Returns true if any of the items is a user-defined string
bool isCustom() const;
/// Assignment operator
Enumeration & operator=(const Enumeration &other);
/// Assignment operator
Enumeration& operator=(const Enumeration& other);
/// true iff our string representation matches other's
/*!
* Returns false if either Enumeration is not valid.
*/
bool operator==(const Enumeration &other) const;
/// true iff our string representation matches other's
/*!
* Returns false if either Enumeration is not valid.
*/
bool operator==(const Enumeration& other) const;
/// true iff our string representation matches other
/*!
* Returns false if Enumeration is not valid.
*/
bool operator==(const char *other) const;
protected:
/// true iff our string representation matches other
/*!
* Returns false if Enumeration is not valid.
*/
bool operator==(const char* other) const;
/// Number of items
int countItems() const;
protected:
/// Number of items
int countItems() const;
private:
/// Handle to C Strings of possible enumeration values
using ObjectPtr = std::shared_ptr<Object>;
std::vector<ObjectPtr> enumArray;
private:
/// Handle to C Strings of possible enumeration values
using ObjectPtr = std::shared_ptr<Object>;
std::vector<ObjectPtr> enumArray;
/// Integer value of the enumeration
/*!
* This serves as an index into enumArray to get the string
* representation.
*/
int _index;
/// Integer value of the enumeration
/*!
* This serves as an index into enumArray to get the string
* representation.
*/
int _index;
friend class PropertyEnumeration;
}; // class Enumeration
} // namespace App
friend class PropertyEnumeration;
}; // class Enumeration
} // namespace App
#endif // #ifndef BASE_ENUMERATION_H
#endif // #ifndef BASE_ENUMERATION_H

View File

@@ -24,7 +24,7 @@
#include "PreCompiled.h"
#ifndef _PreComp_
# include <cassert>
#include <cassert>
#endif
#include <Base/PyObjectBase.h>
@@ -39,167 +39,201 @@
* PropertyData in the extension chain, there is no parent property data.
*/
EXTENSION_TYPESYSTEM_SOURCE_P(App::Extension)
const App::PropertyData * App::Extension::extensionGetPropertyDataPtr(){return &propertyData;}
const App::PropertyData & App::Extension::extensionGetPropertyData() const{return propertyData;}
const App::PropertyData* App::Extension::extensionGetPropertyDataPtr()
{
return &propertyData;
}
const App::PropertyData& App::Extension::extensionGetPropertyData() const
{
return propertyData;
}
App::PropertyData App::Extension::propertyData;
void App::Extension::init(){
void App::Extension::init()
{
assert(Extension::classTypeId == Base::Type::badType() && "don't init() twice!");
/* Set up entry in the type system. */
Extension::classTypeId = Base::Type::createType(Base::Type::badType(), "App::Extension",
Extension::create);
Extension::classTypeId =
Base::Type::createType(Base::Type::badType(), "App::Extension", Extension::create);
}
using namespace App;
Extension::~Extension()
{
if (!ExtensionPythonObject.is(Py::_None())){
if (!ExtensionPythonObject.is(Py::_None())) {
// Remark: The API of Py::Object has been changed to set whether the wrapper owns the passed
// Python object or not. In the constructor we forced the wrapper to own the object so we need
// not to dec'ref the Python object any more.
// But we must still invalidate the Python object because it need not to be
// destructed right now because the interpreter can own several references to it.
// Python object or not. In the constructor we forced the wrapper to own the object so we
// need not to dec'ref the Python object any more. But we must still invalidate the Python
// object because it need not to be destructed right now because the interpreter can own
// several references to it.
Base::PyObjectBase* obj = static_cast<Base::PyObjectBase*>(ExtensionPythonObject.ptr());
// Call before decrementing the reference counter, otherwise a heap error can occur
obj->setInvalid();
}
}
void Extension::initExtensionType(Base::Type type) {
void Extension::initExtensionType(Base::Type type)
{
m_extensionType = type;
if (m_extensionType.isBad())
if (m_extensionType.isBad()) {
throw Base::RuntimeError("Extension: Extension type not set");
}
}
void Extension::initExtension(ExtensionContainer* obj) {
if (m_extensionType.isBad())
void Extension::initExtension(ExtensionContainer* obj)
{
if (m_extensionType.isBad()) {
throw Base::RuntimeError("Extension: Extension type not set");
}
//all properties are initialised without PropertyContainer father. Now that we know it we can
//finally finish the property initialisation
// all properties are initialised without PropertyContainer father. Now that we know it we can
// finally finish the property initialisation
std::vector<Property*> list;
extensionGetPropertyData().getPropertyList(this, list);
for(Property* prop : list)
for (Property* prop : list) {
prop->setContainer(obj);
}
m_base = obj;
m_base->registerExtension( m_extensionType, this );
m_base->registerExtension(m_extensionType, this);
}
PyObject* Extension::getExtensionPyObject() {
PyObject* Extension::getExtensionPyObject()
{
if (ExtensionPythonObject.is(Py::_None())){
if (ExtensionPythonObject.is(Py::_None())) {
// ref counter is set to 1
auto grp = new ExtensionPy(this);
ExtensionPythonObject = Py::Object(grp,true);
ExtensionPythonObject = Py::Object(grp, true);
}
return Py::new_reference_to(ExtensionPythonObject);
}
std::string Extension::name() const {
std::string Extension::name() const
{
if (m_extensionType.isBad())
if (m_extensionType.isBad()) {
throw Base::RuntimeError("Extension::name: Extension type not set");
}
std::string temp(m_extensionType.getName());
std::string::size_type pos = temp.find_last_of(':');
if (pos != std::string::npos)
return temp.substr(pos+1);
if (pos != std::string::npos) {
return temp.substr(pos + 1);
}
return {};
}
Property* Extension::extensionGetPropertyByName(const char* name) const {
Property* Extension::extensionGetPropertyByName(const char* name) const
{
return extensionGetPropertyData().getPropertyByName(this, name);
}
short int Extension::extensionGetPropertyType(const Property* prop) const {
short int Extension::extensionGetPropertyType(const Property* prop) const
{
return extensionGetPropertyData().getType(this, prop);
}
short int Extension::extensionGetPropertyType(const char* name) const {
short int Extension::extensionGetPropertyType(const char* name) const
{
return extensionGetPropertyData().getType(this, name);
}
const char* Extension::extensionGetPropertyName(const Property* prop) const {
const char* Extension::extensionGetPropertyName(const Property* prop) const
{
return extensionGetPropertyData().getName(this,prop);
return extensionGetPropertyData().getName(this, prop);
}
const char* Extension::extensionGetPropertyGroup(const Property* prop) const {
const char* Extension::extensionGetPropertyGroup(const Property* prop) const
{
return extensionGetPropertyData().getGroup(this,prop);
return extensionGetPropertyData().getGroup(this, prop);
}
const char* Extension::extensionGetPropertyGroup(const char* name) const {
const char* Extension::extensionGetPropertyGroup(const char* name) const
{
return extensionGetPropertyData().getGroup(this,name);
return extensionGetPropertyData().getGroup(this, name);
}
const char* Extension::extensionGetPropertyDocumentation(const Property* prop) const {
const char* Extension::extensionGetPropertyDocumentation(const Property* prop) const
{
return extensionGetPropertyData().getDocumentation(this, prop);
}
const char* Extension::extensionGetPropertyDocumentation(const char* name) const {
const char* Extension::extensionGetPropertyDocumentation(const char* name) const
{
return extensionGetPropertyData().getDocumentation(this, name);
}
void Extension::extensionGetPropertyList(std::vector< Property* >& List) const {
void Extension::extensionGetPropertyList(std::vector<Property*>& List) const
{
extensionGetPropertyData().getPropertyList(this, List);
}
void Extension::extensionGetPropertyMap(std::map< std::string, Property* >& Map) const {
void Extension::extensionGetPropertyMap(std::map<std::string, Property*>& Map) const
{
extensionGetPropertyData().getPropertyMap(this, Map);
}
void Extension::initExtensionSubclass(Base::Type& toInit, const char* ClassName, const char* ParentName,
Base::Type::instantiationMethod method) {
void Extension::initExtensionSubclass(Base::Type& toInit,
const char* ClassName,
const char* ParentName,
Base::Type::instantiationMethod method)
{
// don't init twice!
assert(toInit == Base::Type::badType());
// get the parent class
Base::Type parentType(Base::Type::fromName(ParentName));
// forgot init parent!
assert(parentType != Base::Type::badType() );
assert(parentType != Base::Type::badType());
// create the new type
toInit = Base::Type::createType(parentType, ClassName, method);
}
bool Extension::extensionHandleChangedPropertyName(Base::XMLReader &reader, const char * TypeName, const char *PropName)
bool Extension::extensionHandleChangedPropertyName(Base::XMLReader& reader,
const char* TypeName,
const char* PropName)
{
(void) reader;
(void) TypeName;
(void) PropName;
(void)reader;
(void)TypeName;
(void)PropName;
return false;
};
bool Extension::extensionHandleChangedPropertyType(Base::XMLReader &reader, const char * TypeName, Property * prop)
bool Extension::extensionHandleChangedPropertyType(Base::XMLReader& reader,
const char* TypeName,
Property* prop)
{
(void) reader;
(void) TypeName;
(void) prop;
(void)reader;
(void)TypeName;
(void)prop;
return false;
};
namespace App {
namespace App
{
EXTENSION_PROPERTY_SOURCE_TEMPLATE(App::ExtensionPython, App::ExtensionPython::Inherited)
// explicit template instantiation
template class AppExport ExtensionPythonT<Extension>;
}
} // namespace App

View File

@@ -38,23 +38,28 @@ TYPESYSTEM_SOURCE(App::ExtensionContainer, App::PropertyContainer)
ExtensionContainer::ExtensionContainer() = default;
ExtensionContainer::~ExtensionContainer() {
ExtensionContainer::~ExtensionContainer()
{
//we need to delete all dynamically added extensions
for(const auto& entry : _extensions) {
if(entry.second->isPythonExtension())
// we need to delete all dynamically added extensions
for (const auto& entry : _extensions) {
if (entry.second->isPythonExtension()) {
delete entry.second;
}
}
}
void ExtensionContainer::registerExtension(Base::Type extension, Extension* ext) {
if(ext->getExtendedContainer() != this)
throw Base::ValueError("ExtensionContainer::registerExtension: Extension has not this as base object");
void ExtensionContainer::registerExtension(Base::Type extension, Extension* ext)
{
if (ext->getExtendedContainer() != this) {
throw Base::ValueError(
"ExtensionContainer::registerExtension: Extension has not this as base object");
}
//no duplicate extensions (including base classes)
if(hasExtension(extension)) {
for(const auto& entry : _extensions) {
if(entry.first == extension || entry.first.isDerivedFrom(extension)) {
// no duplicate extensions (including base classes)
if (hasExtension(extension)) {
for (const auto& entry : _extensions) {
if (entry.first == extension || entry.first.isDerivedFrom(extension)) {
_extensions.erase(entry.first);
break;
}
@@ -64,275 +69,325 @@ void ExtensionContainer::registerExtension(Base::Type extension, Extension* ext)
_extensions[extension] = ext;
}
bool ExtensionContainer::hasExtension(Base::Type t, bool derived) const {
bool ExtensionContainer::hasExtension(Base::Type t, bool derived) const
{
//check for the exact type
// check for the exact type
bool found = _extensions.find(t) != _extensions.end();
if(!found && derived) {
//and for types derived from it, as they can be cast to the extension
for(const auto& entry : _extensions) {
if(entry.first.isDerivedFrom(t))
if (!found && derived) {
// and for types derived from it, as they can be cast to the extension
for (const auto& entry : _extensions) {
if (entry.first.isDerivedFrom(t)) {
return true;
}
}
return false;
}
return found;
}
bool ExtensionContainer::hasExtension(const std::string& name) const {
bool ExtensionContainer::hasExtension(const std::string& name) const
{
//and for types derived from it, as they can be cast to the extension
for(const auto& entry : _extensions) {
if(entry.second->name() == name)
// and for types derived from it, as they can be cast to the extension
for (const auto& entry : _extensions) {
if (entry.second->name() == name) {
return true;
}
}
return false;
}
Extension* ExtensionContainer::getExtension(Base::Type t, bool derived, bool no_except) const {
Extension* ExtensionContainer::getExtension(Base::Type t, bool derived, bool no_except) const
{
auto result = _extensions.find(t);
if((result == _extensions.end()) && derived) {
//we need to check for derived types
for(const auto& entry : _extensions) {
if(entry.first.isDerivedFrom(t))
if ((result == _extensions.end()) && derived) {
// we need to check for derived types
for (const auto& entry : _extensions) {
if (entry.first.isDerivedFrom(t)) {
return entry.second;
}
}
if(no_except)
if (no_except) {
return nullptr;
//if we arrive here we don't have anything matching
throw Base::TypeError("ExtensionContainer::getExtension: No extension of given type available");
}
// if we arrive here we don't have anything matching
throw Base::TypeError(
"ExtensionContainer::getExtension: No extension of given type available");
}
else if (result != _extensions.end()) {
return result->second;
}
else {
if(no_except)
if (no_except) {
return nullptr;
//if we arrive here we don't have anything matching
throw Base::TypeError("ExtensionContainer::getExtension: No extension of given type available");
}
// if we arrive here we don't have anything matching
throw Base::TypeError(
"ExtensionContainer::getExtension: No extension of given type available");
}
}
bool ExtensionContainer::hasExtensions() const {
bool ExtensionContainer::hasExtensions() const
{
return !_extensions.empty();
}
Extension* ExtensionContainer::getExtension(const std::string& name) const {
Extension* ExtensionContainer::getExtension(const std::string& name) const
{
//and for types derived from it, as they can be cast to the extension
for(const auto& entry : _extensions) {
if(entry.second->name() == name)
// and for types derived from it, as they can be cast to the extension
for (const auto& entry : _extensions) {
if (entry.second->name() == name) {
return entry.second;
}
}
return nullptr;
}
std::vector< Extension* > ExtensionContainer::getExtensionsDerivedFrom(Base::Type type) const {
std::vector<Extension*> ExtensionContainer::getExtensionsDerivedFrom(Base::Type type) const
{
std::vector<Extension*> vec;
//and for types derived from it, as they can be cast to the extension
for(const auto& entry : _extensions) {
if(entry.first.isDerivedFrom(type))
// and for types derived from it, as they can be cast to the extension
for (const auto& entry : _extensions) {
if (entry.first.isDerivedFrom(type)) {
vec.push_back(entry.second);
}
}
return vec;
}
void ExtensionContainer::getPropertyList(std::vector< Property* >& List) const {
void ExtensionContainer::getPropertyList(std::vector<Property*>& List) const
{
App::PropertyContainer::getPropertyList(List);
for(const auto& entry : _extensions)
for (const auto& entry : _extensions) {
entry.second->extensionGetPropertyList(List);
}
}
void ExtensionContainer::getPropertyMap(std::map< std::string, Property* >& Map) const {
void ExtensionContainer::getPropertyMap(std::map<std::string, Property*>& Map) const
{
App::PropertyContainer::getPropertyMap(Map);
for(const auto& entry : _extensions)
for (const auto& entry : _extensions) {
entry.second->extensionGetPropertyMap(Map);
}
}
Property* ExtensionContainer::getPropertyByName(const char* name) const {
Property* ExtensionContainer::getPropertyByName(const char* name) const
{
auto prop = App::PropertyContainer::getPropertyByName(name);
if(prop)
if (prop) {
return prop;
}
for(const auto& entry : _extensions) {
for (const auto& entry : _extensions) {
auto prop = entry.second->extensionGetPropertyByName(name);
if(prop)
if (prop) {
return prop;
}
}
return nullptr;
}
short int ExtensionContainer::getPropertyType(const Property* prop) const {
short int ExtensionContainer::getPropertyType(const Property* prop) const
{
short int res = App::PropertyContainer::getPropertyType(prop);
if(res != 0)
if (res != 0) {
return res;
}
for(const auto& entry : _extensions) {
for (const auto& entry : _extensions) {
res = entry.second->extensionGetPropertyType(prop);
if(res != 0)
if (res != 0) {
return res;
}
}
return 0;
}
short int ExtensionContainer::getPropertyType(const char* name) const {
short int ExtensionContainer::getPropertyType(const char* name) const
{
short int res = App::PropertyContainer::getPropertyType(name);
if(res != 0)
if (res != 0) {
return res;
}
for(const auto& entry : _extensions) {
for (const auto& entry : _extensions) {
res = entry.second->extensionGetPropertyType(name);
if(res != 0)
if (res != 0) {
return res;
}
}
return 0;
}
const char* ExtensionContainer::getPropertyName(const Property* prop) const {
const char* ExtensionContainer::getPropertyName(const Property* prop) const
{
const char* res = App::PropertyContainer::getPropertyName(prop);
if (res)
if (res) {
return res;
}
for (const auto& entry : _extensions) {
res = entry.second->extensionGetPropertyName(prop);
if (res)
if (res) {
return res;
}
}
return nullptr;
}
const char* ExtensionContainer::getPropertyGroup(const Property* prop) const {
const char* ExtensionContainer::getPropertyGroup(const Property* prop) const
{
const char* res = App::PropertyContainer::getPropertyGroup(prop);
if (res)
if (res) {
return res;
}
for (const auto& entry : _extensions) {
res = entry.second->extensionGetPropertyGroup(prop);
if (res)
if (res) {
return res;
}
}
return nullptr;
}
const char* ExtensionContainer::getPropertyGroup(const char* name) const {
const char* ExtensionContainer::getPropertyGroup(const char* name) const
{
const char* res = App::PropertyContainer::getPropertyGroup(name);
if (res)
if (res) {
return res;
}
for (const auto& entry : _extensions) {
res = entry.second->extensionGetPropertyGroup(name);
if (res)
if (res) {
return res;
}
}
return nullptr;
}
const char* ExtensionContainer::getPropertyDocumentation(const Property* prop) const {
const char* ExtensionContainer::getPropertyDocumentation(const Property* prop) const
{
const char* res = App::PropertyContainer::getPropertyDocumentation(prop);
if (res)
if (res) {
return res;
}
for (const auto& entry : _extensions) {
res = entry.second->extensionGetPropertyDocumentation(prop);
if (res)
if (res) {
return res;
}
}
return nullptr;
}
const char* ExtensionContainer::getPropertyDocumentation(const char* name) const {
const char* ExtensionContainer::getPropertyDocumentation(const char* name) const
{
const char* res = App::PropertyContainer::getPropertyDocumentation(name);
if (res)
if (res) {
return res;
}
for(const auto& entry : _extensions) {
for (const auto& entry : _extensions) {
res = entry.second->extensionGetPropertyDocumentation(name);
if (res)
if (res) {
return res;
}
}
return nullptr;
}
void ExtensionContainer::onChanged(const Property* prop) {
void ExtensionContainer::onChanged(const Property* prop)
{
//inform all extensions about changed property. This includes all properties from the
//extended object (this) as well as all extension properties
for(const auto& entry : _extensions)
// inform all extensions about changed property. This includes all properties from the
// extended object (this) as well as all extension properties
for (const auto& entry : _extensions) {
entry.second->extensionOnChanged(prop);
}
App::PropertyContainer::onChanged(prop);
}
void ExtensionContainer::Save(Base::Writer& writer) const {
void ExtensionContainer::Save(Base::Writer& writer) const
{
//Note: save extensions must be called first to ensure that the extension element is always the
// very first inside the object element. This is needed since extension element works together with
// an object attribute, and if another element would be read first the object attributes would be
// cleared.
// Note: save extensions must be called first to ensure that the extension element is always the
// very first inside the object element. This is needed since extension element works
// together with an object attribute, and if another element would be read first the
// object attributes would be cleared.
saveExtensions(writer);
App::PropertyContainer::Save(writer);
}
void ExtensionContainer::Restore(Base::XMLReader& reader) {
void ExtensionContainer::Restore(Base::XMLReader& reader)
{
//restore dynamic extensions.
//Note 1: The extension element must be read first, before all other object elements. That is
// needed as the element works together with an object element attribute, which would be
// cleared if another attribute is read first
//Note 2: This must happen before the py object of this container is used, as only in the
// pyobject constructor the extension methods are added to the container.
// restore dynamic extensions.
// Note 1: The extension element must be read first, before all other object elements. That is
// needed as the element works together with an object element attribute, which would be
// cleared if another attribute is read first
// Note 2: This must happen before the py object of this container is used, as only in the
// pyobject constructor the extension methods are added to the container.
restoreExtensions(reader);
App::PropertyContainer::Restore(reader);
}
void ExtensionContainer::saveExtensions(Base::Writer& writer) const {
void ExtensionContainer::saveExtensions(Base::Writer& writer) const
{
//we don't save anything if there are no dynamic extensions
if(!hasExtensions())
// we don't save anything if there are no dynamic extensions
if (!hasExtensions()) {
return;
}
//save dynamic extensions
writer.incInd(); // indentation for 'Extensions'
writer.Stream() << writer.ind() << "<Extensions Count=\"" << _extensions.size() << "\">" << std::endl;
for(const auto& entry : _extensions) {
// save dynamic extensions
writer.incInd(); // indentation for 'Extensions'
writer.Stream() << writer.ind() << "<Extensions Count=\"" << _extensions.size() << "\">"
<< std::endl;
for (const auto& entry : _extensions) {
auto ext = entry.second;
writer.incInd(); // indentation for 'Extension name'
writer.incInd(); // indentation for 'Extension name'
writer.Stream() << writer.ind() << "<Extension"
<< " type=\"" << ext->getExtensionTypeId().getName() <<"\""
<< " name=\"" << ext->name() << "\">" << std::endl;
writer.incInd(); // indentation for the actual Extension
<< " type=\"" << ext->getExtensionTypeId().getName() << "\""
<< " name=\"" << ext->name() << "\">" << std::endl;
writer.incInd(); // indentation for the actual Extension
try {
// We must make sure to handle all exceptions accordingly so that
// the project file doesn't get invalidated. In the error case this
// means to proceed instead of aborting the write operation.
ext->extensionSave(writer);
}
catch (const Base::Exception &e) {
catch (const Base::Exception& e) {
Base::Console().Error("%s\n", e.what());
}
catch (const std::exception &e) {
catch (const std::exception& e) {
Base::Console().Error("%s\n", e.what());
}
catch (const char* e) {
@@ -340,47 +395,51 @@ void ExtensionContainer::saveExtensions(Base::Writer& writer) const {
}
#ifndef FC_DEBUG
catch (...) {
Base::Console().Error("ExtensionContainer::Save: Unknown C++ exception thrown. Try to continue...\n");
Base::Console().Error(
"ExtensionContainer::Save: Unknown C++ exception thrown. Try to continue...\n");
}
#endif
writer.decInd(); // indentation for the actual extension
writer.decInd(); // indentation for the actual extension
writer.Stream() << writer.ind() << "</Extension>" << std::endl;
writer.decInd(); // indentation for 'Extension name'
writer.decInd(); // indentation for 'Extension name'
}
writer.Stream() << writer.ind() << "</Extensions>" << std::endl;
writer.decInd();
}
void ExtensionContainer::restoreExtensions(Base::XMLReader& reader) {
void ExtensionContainer::restoreExtensions(Base::XMLReader& reader)
{
//Dynamic extensions are optional (also because they are introduced late into the document format)
//and hence it is possible that the element does not exist. As we cannot check for the existence of
//an element a object attribute is set if extensions are available. Here we check that
//attribute, and only if it exists the extensions element will be available.
if(!reader.hasAttribute("Extensions"))
// Dynamic extensions are optional (also because they are introduced late into the document
// format) and hence it is possible that the element does not exist. As we cannot check for the
// existence of an element a object attribute is set if extensions are available. Here we check
// that attribute, and only if it exists the extensions element will be available.
if (!reader.hasAttribute("Extensions")) {
return;
}
reader.readElement("Extensions");
int Cnt = reader.getAttributeAsInteger("Count");
for (int i=0 ;i<Cnt ;i++) {
for (int i = 0; i < Cnt; i++) {
reader.readElement("Extension");
const char* Type = reader.getAttribute("type");
const char* Name = reader.getAttribute("name");
try {
App::Extension* ext = getExtension(Name);
if(!ext) {
//get the extension type asked for
Base::Type extension = Base::Type::fromName(Type);
if (extension.isBad() || !extension.isDerivedFrom(App::Extension::getExtensionClassTypeId())) {
if (!ext) {
// get the extension type asked for
Base::Type extension = Base::Type::fromName(Type);
if (extension.isBad()
|| !extension.isDerivedFrom(App::Extension::getExtensionClassTypeId())) {
std::stringstream str;
str << "No extension found of type '" << Type << "'" << std::ends;
throw Base::TypeError(str.str());
}
//register the extension
// register the extension
ext = static_cast<App::Extension*>(extension.createInstance());
//check if this really is a python extension!
// check if this really is a python extension!
if (!ext->isPythonExtension()) {
delete ext;
std::stringstream str;
@@ -390,16 +449,17 @@ void ExtensionContainer::restoreExtensions(Base::XMLReader& reader) {
ext->initExtension(this);
}
if (ext && strcmp(ext->getExtensionTypeId().getName(), Type) == 0)
if (ext && strcmp(ext->getExtensionTypeId().getName(), Type) == 0) {
ext->extensionRestore(reader);
}
}
catch (const Base::XMLParseException&) {
throw; // re-throw
throw; // re-throw
}
catch (const Base::Exception &e) {
catch (const Base::Exception& e) {
Base::Console().Error("%s\n", e.what());
}
catch (const std::exception &e) {
catch (const std::exception& e) {
Base::Console().Error("%s\n", e.what());
}
catch (const char* e) {
@@ -416,29 +476,35 @@ void ExtensionContainer::restoreExtensions(Base::XMLReader& reader) {
reader.readEndElement("Extensions");
}
void ExtensionContainer::handleChangedPropertyName(Base::XMLReader &reader, const char * TypeName, const char *PropName)
void ExtensionContainer::handleChangedPropertyName(Base::XMLReader& reader,
const char* TypeName,
const char* PropName)
{
//inform all extensions about changed property name. This includes all properties from the
//extended object (this) as well as all extension properties
for(const auto& entry : _extensions) {
// inform all extensions about changed property name. This includes all properties from the
// extended object (this) as well as all extension properties
for (const auto& entry : _extensions) {
bool handled = entry.second->extensionHandleChangedPropertyName(reader, TypeName, PropName);
if(handled)
return; // one property change needs only be handled once
if (handled) {
return; // one property change needs only be handled once
}
}
PropertyContainer::handleChangedPropertyName(reader, TypeName, PropName);
}
void ExtensionContainer::handleChangedPropertyType(Base::XMLReader &reader, const char * TypeName, Property * prop)
void ExtensionContainer::handleChangedPropertyType(Base::XMLReader& reader,
const char* TypeName,
Property* prop)
{
//inform all extensions about changed property type. This includes all properties from the
//extended object (this) as well as all extension properties
for(const auto& entry : _extensions) {
// inform all extensions about changed property type. This includes all properties from the
// extended object (this) as well as all extension properties
for (const auto& entry : _extensions) {
bool handled = entry.second->extensionHandleChangedPropertyType(reader, TypeName, prop);
if(handled)
return; // one property change needs only be handled once
if (handled) {
return; // one property change needs only be handled once
}
}
PropertyContainer::handleChangedPropertyType(reader, TypeName, prop);

View File

@@ -27,7 +27,8 @@
#include "PropertyContainer.h"
namespace App {
namespace App
{
class Extension;
/**
@@ -49,14 +50,14 @@ class Extension;
* - Extensions can be added from c++ and python, even from both together
*
* The interoperability with python is highly important, as in FreeCAD all functionality should be
* as easily accessible from python as from c++. To ensure this, and as already noted, extensions can
* be added to a object from python. However, this means that it is not clear from the c++ object type
* if an extension was added or not. If added from c++ it becomes clear in the type due to the use of
* multiple inheritance. If added from python it is a runtime extension and not visible from type.
* Hence querying existing extensions of an object and accessing its methods works not by type
* casting but by the interface provided in ExtensionContainer. The default workflow is to query if
* an extension exists and then get the extension object. No matter if added from python or c++ this
* interface works always the same.
* as easily accessible from python as from c++. To ensure this, and as already noted, extensions
* can be added to a object from python. However, this means that it is not clear from the c++
* object type if an extension was added or not. If added from c++ it becomes clear in the type due
* to the use of multiple inheritance. If added from python it is a runtime extension and not
* visible from type. Hence querying existing extensions of an object and accessing its methods
* works not by type casting but by the interface provided in ExtensionContainer. The default
* workflow is to query if an extension exists and then get the extension object. No matter if added
* from python or c++ this interface works always the same.
* @code
* if (object->hasExtension(GroupExtension::getClassTypeId())) {
* App::GroupExtension* group = object->getExtensionByType<GroupExtension>();
@@ -77,8 +78,8 @@ class Extension;
*
* Here is a working example:
* @code
* class AppExport Part : public App::DocumentObject, public App::FirstExtension, public App::SecondExtension {
* PROPERTY_HEADER_WITH_EXTENSIONS(App::Part);
* class AppExport Part : public App::DocumentObject, public App::FirstExtension, public
* App::SecondExtension { PROPERTY_HEADER_WITH_EXTENSIONS(App::Part);
* };
* PROPERTY_SOURCE_WITH_EXTENSIONS(App::Part, App::DocumentObject)
* Part::Part(void) {
@@ -88,9 +89,9 @@ class Extension;
* @endcode
*
* From python adding an extension is easier, it must be simply registered to a document object
* at object initialisation like done with properties. Note that the special python extension objects
* need to be added, not the c++ objects. Normally the only difference in name is the additional
* "Python" at the end of the extension name.
* at object initialisation like done with properties. Note that the special python extension
* objects need to be added, not the c++ objects. Normally the only difference in name is the
* additional "Python" at the end of the extension name.
* @code{.py}
* class Test():
* __init(self)__:
@@ -99,86 +100,98 @@ class Extension;
* @endcode
*
* Extensions can provide methods that should be overridden by the extended object for customisation
* of the extension behaviour. In c++ this is as simple as overriding the provided virtual functions.
* In python a class method must be provided which has the same name as the method to override. This
* method must not necessarily be in the object that is extended, it must be in the object which is
* provided to the "registerExtension" call as second argument. This second argument is used as a
* proxy and enqueired if the method to override exists in this proxy before calling it.
* of the extension behaviour. In c++ this is as simple as overriding the provided virtual
* functions. In python a class method must be provided which has the same name as the method to
* override. This method must not necessarily be in the object that is extended, it must be in the
* object which is provided to the "registerExtension" call as second argument. This second argument
* is used as a proxy and enqueired if the method to override exists in this proxy before calling
* it.
*
* For information on howto create extension see the documentation of Extension
*/
class AppExport ExtensionContainer : public App::PropertyContainer
class AppExport ExtensionContainer: public App::PropertyContainer
{
TYPESYSTEM_HEADER_WITH_OVERRIDE();
public:
using ExtensionIterator = std::map<Base::Type, App::Extension*>::iterator;
ExtensionContainer();
~ExtensionContainer() override;
void registerExtension(Base::Type extension, App::Extension* ext);
//returns first of type (or derived from if set to true) and throws otherwise
bool hasExtension(Base::Type, bool derived=true) const;
//this version does not check derived classes
// returns first of type (or derived from if set to true) and throws otherwise
bool hasExtension(Base::Type, bool derived = true) const;
// this version does not check derived classes
bool hasExtension(const std::string& name) const;
bool hasExtensions() const;
App::Extension* getExtension(Base::Type, bool derived = true, bool no_except=false) const;
//this version does not check derived classes
App::Extension* getExtension(Base::Type, bool derived = true, bool no_except = false) const;
// this version does not check derived classes
App::Extension* getExtension(const std::string& name) const;
// this version checks for derived types and doesn't throw
template<typename ExtensionT>
ExtensionT* getExtension() const {
return static_cast<ExtensionT*>(getExtension(ExtensionT::getExtensionClassTypeId(), true, true));
ExtensionT* getExtension() const
{
return static_cast<ExtensionT*>(
getExtension(ExtensionT::getExtensionClassTypeId(), true, true));
}
//returns first of type (or derived from) and throws otherwise
// returns first of type (or derived from) and throws otherwise
template<typename ExtensionT>
ExtensionT* getExtensionByType(bool no_except=false, bool derived=true) const {
return static_cast<ExtensionT*>(getExtension(ExtensionT::getExtensionClassTypeId(),derived,no_except));
ExtensionT* getExtensionByType(bool no_except = false, bool derived = true) const
{
return static_cast<ExtensionT*>(
getExtension(ExtensionT::getExtensionClassTypeId(), derived, no_except));
}
//get all extensions which have the given base class
// get all extensions which have the given base class
std::vector<Extension*> getExtensionsDerivedFrom(Base::Type type) const;
template<typename ExtensionT>
std::vector<ExtensionT*> getExtensionsDerivedFromType() const {
std::vector<ExtensionT*> getExtensionsDerivedFromType() const
{
std::vector<ExtensionT*> typevec;
for(const auto& entry : _extensions) {
if(entry.first.isDerivedFrom(ExtensionT::getExtensionClassTypeId()))
for (const auto& entry : _extensions) {
if (entry.first.isDerivedFrom(ExtensionT::getExtensionClassTypeId())) {
typevec.push_back(static_cast<ExtensionT*>(entry.second));
}
}
return typevec;
}
ExtensionIterator extensionBegin() {return _extensions.begin();}
ExtensionIterator extensionEnd() {return _extensions.end();}
ExtensionIterator extensionBegin()
{
return _extensions.begin();
}
ExtensionIterator extensionEnd()
{
return _extensions.end();
}
/** @name Access properties */
//@{
/// find a property by its name
Property *getPropertyByName(const char* name) const override;
Property* getPropertyByName(const char* name) const override;
/// get the name of a property
const char* getPropertyName(const Property* prop) const override;
/// get all properties of the class (including properties of the parent)
void getPropertyMap(std::map<std::string,Property*> &Map) const override;
void getPropertyMap(std::map<std::string, Property*>& Map) const override;
/// get all properties of the class (including properties of the parent)
void getPropertyList(std::vector<Property*> &List) const override;
void getPropertyList(std::vector<Property*>& List) const override;
/// get the Type of a Property
short getPropertyType(const Property* prop) const override;
/// get the Type of a named Property
short getPropertyType(const char *name) const override;
short getPropertyType(const char* name) const override;
/// get the Group of a Property
const char* getPropertyGroup(const Property* prop) const override;
/// get the Group of a named Property
const char* getPropertyGroup(const char *name) const override;
const char* getPropertyGroup(const char* name) const override;
/// get the Group of a Property
const char* getPropertyDocumentation(const Property* prop) const override;
/// get the Group of a named Property
const char* getPropertyDocumentation(const char *name) const override;
const char* getPropertyDocumentation(const char* name) const override;
//@}
void onChanged(const Property*) override;
@@ -186,43 +199,50 @@ public:
void Save(Base::Writer& writer) const override;
void Restore(Base::XMLReader& reader) override;
//those methods save/restore the dynamic extensions without handling properties, which is something
//done by the default Save/Restore methods.
// those methods save/restore the dynamic extensions without handling properties, which is
// something done by the default Save/Restore methods.
void saveExtensions(Base::Writer& writer) const;
void restoreExtensions(Base::XMLReader& reader);
/** Extends the rules for handling property name changed, so that extensions are given an opportunity to handle it.
* If an extension handles a change, neither the rest of the extensions, nor the container itself get to handle it.
/** Extends the rules for handling property name changed, so that extensions are given an
* opportunity to handle it. If an extension handles a change, neither the rest of the
* extensions, nor the container itself get to handle it.
*
* Extensions get their extensionHandleChangedPropertyName() called.
*
* If no extension handles the request, then the containers handleChangedPropertyName() is called.
* If no extension handles the request, then the containers handleChangedPropertyName() is
* called.
*/
void handleChangedPropertyName(Base::XMLReader &reader, const char * TypeName, const char *PropName) override;
/** Extends the rules for handling property type changed, so that extensions are given an opportunity to handle it.
* If an extension handles a change, neither the rest of the extensions, nor the container itself get to handle it.
void handleChangedPropertyName(Base::XMLReader& reader,
const char* TypeName,
const char* PropName) override;
/** Extends the rules for handling property type changed, so that extensions are given an
* opportunity to handle it. If an extension handles a change, neither the rest of the
* extensions, nor the container itself get to handle it.
*
* Extensions get their extensionHandleChangedPropertyType() called.
*
* If no extension handles the request, then the containers handleChangedPropertyType() is called.
* If no extension handles the request, then the containers handleChangedPropertyType() is
* called.
*/
void handleChangedPropertyType(Base::XMLReader &reader, const char * TypeName, Property * prop) override;
void handleChangedPropertyType(Base::XMLReader& reader,
const char* TypeName,
Property* prop) override;
private:
//stored extensions
// stored extensions
std::map<Base::Type, App::Extension*> _extensions;
};
#define PROPERTY_HEADER_WITH_EXTENSIONS(_class_) \
PROPERTY_HEADER_WITH_OVERRIDE(_class)
#define PROPERTY_HEADER_WITH_EXTENSIONS(_class_) PROPERTY_HEADER_WITH_OVERRIDE(_class)
/// We make sure that the PropertyData of the container is not connected to the one of the extension
#define PROPERTY_SOURCE_WITH_EXTENSIONS(_class_, _parentclass_) \
#define PROPERTY_SOURCE_WITH_EXTENSIONS(_class_, _parentclass_) \
PROPERTY_SOURCE(_class_, _parentclass_)
#define PROPERTY_SOURCE_ABSTRACT_WITH_EXTENSIONS(_class_, _parentclass_) \
#define PROPERTY_SOURCE_ABSTRACT_WITH_EXTENSIONS(_class_, _parentclass_) \
PROPERTY_SOURCE_ABSTRACT(_class_, _parentclass_)
} //App
} // namespace App
#endif // APP_EXTENSIONCONTAINER_H
#endif // APP_EXTENSIONCONTAINER_H

View File

@@ -1,13 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<GenerateModel xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="generateMetaModel_Module.xsd">
<PythonExport
Father="PropertyContainerPy"
Name="ExtensionContainerPy"
TwinPointer="ExtensionContainer"
Twin="ExtensionContainer"
Include="App/ExtensionContainer.h"
Namespace="App"
FatherInclude="App/PropertyContainerPy.h"
<PythonExport
Father="PropertyContainerPy"
Name="ExtensionContainerPy"
TwinPointer="ExtensionContainer"
Twin="ExtensionContainer"
Include="App/ExtensionContainer.h"
Namespace="App"
FatherInclude="App/PropertyContainerPy.h"
FatherNamespace="App"
Initialization="true"
Constructor = "true">

View File

@@ -24,7 +24,7 @@
#include "PreCompiled.h"
#ifndef _PreComp_
# include <sstream>
#include <sstream>
#endif
#include "Application.h"
@@ -41,22 +41,24 @@ std::string ExtensionContainerPy::representation() const
return {"<extension>"};
}
int ExtensionContainerPy::initialization() {
int ExtensionContainerPy::initialization()
{
if (!this->ob_type->tp_dict) {
if (PyType_Ready(this->ob_type) < 0)
if (PyType_Ready(this->ob_type) < 0) {
return 0;
}
}
ExtensionContainer::ExtensionIterator it = this->getExtensionContainerPtr()->extensionBegin();
for(; it != this->getExtensionContainerPtr()->extensionEnd(); ++it) {
for (; it != this->getExtensionContainerPtr()->extensionEnd(); ++it) {
// The PyTypeObject is shared by all instances of this type and therefore
// we have to add new methods only once.
PyObject* obj = (*it).second->getExtensionPyObject();
PyMethodDef* meth = obj->ob_type->tp_methods;
PyTypeObject *type = this->ob_type;
PyObject *dict = type->tp_dict;
PyTypeObject* type = this->ob_type;
PyObject* dict = type->tp_dict;
// make sure to do the initialization only once
if (meth->ml_name) {
@@ -67,12 +69,14 @@ int ExtensionContainerPy::initialization() {
// to an instance
Py_INCREF(dict);
while (meth->ml_name) {
PyObject *func;
PyObject* func;
func = PyCFunction_New(meth, 0);
if (!func)
if (!func) {
break;
if (PyDict_SetItemString(dict, meth->ml_name, func) < 0)
}
if (PyDict_SetItemString(dict, meth->ml_name, func) < 0) {
break;
}
Py_DECREF(func);
++meth;
}
@@ -86,20 +90,22 @@ int ExtensionContainerPy::initialization() {
return 1;
}
int ExtensionContainerPy::finalization() {
/*
//we need to delete all added python extensions, as we are the owner!
ExtensionContainer::ExtensionIterator it = this->getExtensionContainerPtr()->extensionBegin();
for(; it != this->getExtensionContainerPtr()->extensionEnd(); ++it) {
if((*it).second->isPythonExtension())
delete (*it).second;
}*/
int ExtensionContainerPy::finalization()
{
/*
//we need to delete all added python extensions, as we are the owner!
ExtensionContainer::ExtensionIterator it =
this->getExtensionContainerPtr()->extensionBegin(); for(; it !=
this->getExtensionContainerPtr()->extensionEnd(); ++it) {
if((*it).second->isPythonExtension())
delete (*it).second;
}*/
return 1;
}
PyObject* ExtensionContainerPy::PyMake(struct _typeobject *, PyObject *, PyObject *) // Python wrapper
PyObject* ExtensionContainerPy::PyMake(struct _typeobject*, PyObject*, PyObject*) // Python wrapper
{
// create a new instance of @self.export.Name@ and the Twin object
// create a new instance of @self.export.Name@ and the Twin object
return nullptr;
}
@@ -109,7 +115,7 @@ int ExtensionContainerPy::PyInit(PyObject* /*args*/, PyObject* /*kwd*/)
return 0;
}
PyObject *ExtensionContainerPy::getCustomAttributes(const char* attr) const
PyObject* ExtensionContainerPy::getCustomAttributes(const char* attr) const
{
if (Base::streq(attr, "__dict__")) {
PyObject* dict = PyDict_New();
@@ -119,12 +125,13 @@ PyObject *ExtensionContainerPy::getCustomAttributes(const char* attr) const
Py_DECREF(props);
}
ExtensionContainer::ExtensionIterator it = this->getExtensionContainerPtr()->extensionBegin();
ExtensionContainer::ExtensionIterator it =
this->getExtensionContainerPtr()->extensionBegin();
for (; it != this->getExtensionContainerPtr()->extensionEnd(); ++it) {
// The PyTypeObject is shared by all instances of this type and therefore
// we have to add new methods only once.
PyObject* obj = (*it).second->getExtensionPyObject();
PyTypeObject *tp = Py_TYPE(obj);
PyTypeObject* tp = Py_TYPE(obj);
if (tp && tp->tp_dict) {
Py_XINCREF(tp->tp_dict);
PyDict_Merge(dict, tp->tp_dict, 0);
@@ -139,13 +146,13 @@ PyObject *ExtensionContainerPy::getCustomAttributes(const char* attr) const
// Py_FindMethod is successful then a PyCFunction_New instance is returned
// with the PyObject pointer of the extension to make sure the method will
// be called for the correct instance.
PyObject *func = nullptr;
PyObject* func = nullptr;
ExtensionContainer::ExtensionIterator it = this->getExtensionContainerPtr()->extensionBegin();
for (; it != this->getExtensionContainerPtr()->extensionEnd(); ++it) {
// The PyTypeObject is shared by all instances of this type and therefore
// we have to add new methods only once.
PyObject* obj = (*it).second->getExtensionPyObject();
PyObject *nameobj = PyUnicode_FromString(attr);
PyObject* nameobj = PyUnicode_FromString(attr);
func = PyObject_GenericGetAttr(obj, nameobj);
Py_DECREF(nameobj);
Py_DECREF(obj);
@@ -153,33 +160,36 @@ PyObject *ExtensionContainerPy::getCustomAttributes(const char* attr) const
PyCFunctionObject* cfunc = reinterpret_cast<PyCFunctionObject*>(func);
// OK, that's what we wanted
if (cfunc->m_self == obj)
if (cfunc->m_self == obj) {
break;
}
// otherwise cleanup the result again
Py_DECREF(func);
func = nullptr;
}
PyErr_Clear(); // clear the error set inside Py_FindMethod
PyErr_Clear(); // clear the error set inside Py_FindMethod
}
return func;
}
int ExtensionContainerPy::setCustomAttributes(const char* /*attr*/, PyObject * /*obj*/)
int ExtensionContainerPy::setCustomAttributes(const char* /*attr*/, PyObject* /*obj*/)
{
return 0;
}
PyObject* ExtensionContainerPy::hasExtension(PyObject *args) {
PyObject* ExtensionContainerPy::hasExtension(PyObject* args)
{
char *type;
PyObject *deriv = Py_True;
if (!PyArg_ParseTuple(args, "s|O!", &type, &PyBool_Type, &deriv))
char* type;
PyObject* deriv = Py_True;
if (!PyArg_ParseTuple(args, "s|O!", &type, &PyBool_Type, &deriv)) {
return nullptr;
}
//get the extension type asked for
// get the extension type asked for
bool derived = Base::asBoolean(deriv);
Base::Type extension = Base::Type::fromName(type);
Base::Type extension = Base::Type::fromName(type);
if (extension.isBad() || !extension.isDerivedFrom(App::Extension::getExtensionClassTypeId())) {
std::stringstream str;
str << "No extension found of type '" << type << "'" << std::ends;
@@ -194,30 +204,34 @@ PyObject* ExtensionContainerPy::hasExtension(PyObject *args) {
return PyBool_FromLong(val ? 1 : 0);
}
PyObject* ExtensionContainerPy::addExtension(PyObject *args) {
PyObject* ExtensionContainerPy::addExtension(PyObject* args)
{
char *typeId;
char* typeId;
PyObject* proxy = nullptr;
if (!PyArg_ParseTuple(args, "s|O", &typeId, &proxy))
if (!PyArg_ParseTuple(args, "s|O", &typeId, &proxy)) {
return nullptr;
}
if (proxy) {
PyErr_SetString(PyExc_DeprecationWarning, "Second argument is deprecated. It is ignored and will be removed in future versions. "
"The default Python feature proxy is used for extension method overrides.");
PyErr_SetString(
PyExc_DeprecationWarning,
"Second argument is deprecated. It is ignored and will be removed in future versions. "
"The default Python feature proxy is used for extension method overrides.");
PyErr_Print();
}
//get the extension type asked for
Base::Type extension = Base::Type::fromName(typeId);
// get the extension type asked for
Base::Type extension = Base::Type::fromName(typeId);
if (extension.isBad() || !extension.isDerivedFrom(App::Extension::getExtensionClassTypeId())) {
std::stringstream str;
str << "No extension found of type '" << typeId << "'" << std::ends;
throw Py::TypeError(str.str());
}
//register the extension
// register the extension
App::Extension* ext = static_cast<App::Extension*>(extension.createInstance());
//check if this really is a python extension!
// check if this really is a python extension!
if (!ext->isPythonExtension()) {
delete ext;
std::stringstream str;
@@ -232,8 +246,8 @@ PyObject* ExtensionContainerPy::addExtension(PyObject *args) {
// we have to add new methods only once.
PyObject* obj = ext->getExtensionPyObject();
PyMethodDef* meth = obj->ob_type->tp_methods;
PyTypeObject *type = this->ob_type;
PyObject *dict = type->tp_dict;
PyTypeObject* type = this->ob_type;
PyObject* dict = type->tp_dict;
// make sure to do the initialization only once
if (meth->ml_name) {
@@ -244,12 +258,14 @@ PyObject* ExtensionContainerPy::addExtension(PyObject *args) {
// to an instance
Py_INCREF(dict);
while (meth->ml_name) {
PyObject *func;
PyObject* func;
func = PyCFunction_New(meth, 0);
if (!func)
if (!func) {
break;
if (PyDict_SetItemString(dict, meth->ml_name, func) < 0)
}
if (PyDict_SetItemString(dict, meth->ml_name, func) < 0) {
break;
}
Py_DECREF(func);
++meth;
}
@@ -259,8 +275,8 @@ PyObject* ExtensionContainerPy::addExtension(PyObject *args) {
}
Py_DECREF(obj);
//throw the appropriate event
// throw the appropriate event
GetApplication().signalAddedDynamicExtension(*getExtensionContainerPtr(), typeId);
Py_Return;

View File

@@ -1,13 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<GenerateModel xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="generateMetaModel_Module.xsd">
<PythonExport
Father="PyObjectBase"
Name="ExtensionPy"
TwinPointer="Extension"
Twin="Extension"
Include="App/Extension.h"
Namespace="App"
FatherInclude="Base/PyObjectBase.h"
<PythonExport
Father="PyObjectBase"
Name="ExtensionPy"
TwinPointer="Extension"
Twin="Extension"
Include="App/Extension.h"
Namespace="App"
FatherInclude="Base/PyObjectBase.h"
FatherNamespace="Base">
<Documentation>
<Author Licence="LGPL" Name="Stefan Troeger" EMail="stefantroeger@gmx.net" />

View File

@@ -37,12 +37,12 @@ std::string ExtensionPy::representation() const
return {"<extension>"};
}
PyObject *ExtensionPy::getCustomAttributes(const char* /*attr*/) const
PyObject* ExtensionPy::getCustomAttributes(const char* /*attr*/) const
{
return nullptr;
}
int ExtensionPy::setCustomAttributes(const char* /*attr*/, PyObject * /*obj*/)
int ExtensionPy::setCustomAttributes(const char* /*attr*/, PyObject* /*obj*/)
{
return 0;
}

View File

@@ -28,21 +28,23 @@
#include "Extension.h"
#include "PropertyPythonObject.h"
namespace App {
namespace App
{
/**
* Generic Python extension class which allows every extension derived
* class to behave as a Python extension -- simply by subclassing.
*/
template <class ExtensionT>
class ExtensionPythonT : public ExtensionT //NOLINT
template<class ExtensionT>
class ExtensionPythonT: public ExtensionT // NOLINT
{
EXTENSION_PROPERTY_HEADER_WITH_OVERRIDE(App::ExtensionPythonT<ExtensionT>);
public:
using Inherited = ExtensionT;
ExtensionPythonT() {
ExtensionPythonT()
{
ExtensionT::m_isPythonExtension = true;
ExtensionT::initExtensionType(ExtensionPythonT::getExtensionClassTypeId());
}
@@ -50,73 +52,72 @@ public:
ExtensionPythonT(const ExtensionPythonT&) = delete;
ExtensionPythonT(ExtensionPythonT&&) = delete;
ExtensionPythonT& operator= (const ExtensionPythonT&) = delete;
ExtensionPythonT& operator= (ExtensionPythonT&&) = delete;
ExtensionPythonT& operator=(const ExtensionPythonT&) = delete;
ExtensionPythonT& operator=(ExtensionPythonT&&) = delete;
};
using ExtensionPython = ExtensionPythonT<App::Extension>;
// Helper macros to define python extensions
#define EXTENSION_PROXY_FIRST(function) \
Base::PyGILStateLocker lock;\
Py::Object result;\
try {\
Property* proxy = this->getExtendedContainer()->getPropertyByName("Proxy");\
if (proxy && proxy->is<PropertyPythonObject>()) {\
Py::Object feature = static_cast<PropertyPythonObject*>(proxy)->getValue();\
if (feature.hasAttr(std::string("function"))) {\
if (feature.hasAttr("__object__")) {\
#define EXTENSION_PROXY_FIRST(function) \
Base::PyGILStateLocker lock; \
Py::Object result; \
try { \
Property* proxy = this->getExtendedContainer()->getPropertyByName("Proxy"); \
if (proxy && proxy->is<PropertyPythonObject>()) { \
Py::Object feature = static_cast<PropertyPythonObject*>(proxy)->getValue(); \
if (feature.hasAttr(std::string("function"))) { \
if (feature.hasAttr("__object__")) { \
Py::Callable method(feature.getAttr(std::string("function")));
#define EXTENSION_PROXY_SECOND(function) \
result = method.apply(args); \
} \
else \
{ \
Py::Callable method(feature.getAttr(std::string("function")));
#define EXTENSION_PROXY_SECOND(function)\
result = method.apply(args);\
}\
else {\
Py::Callable method(feature.getAttr(std::string("function")));
#define EXTENSION_PROXY_THIRD()\
result = method.apply(args);\
}\
}\
}\
}\
catch (Py::Exception&) {\
Base::PyException e;\
e.ReportException();\
#define EXTENSION_PROXY_THIRD() \
result = method.apply(args); \
} \
} \
} \
} \
catch (Py::Exception&) \
{ \
Base::PyException e; \
e.ReportException(); \
}
#define EXTENSION_PROXY_NOARG(function)\
EXTENSION_PROXY_FIRST(function) \
Py::Tuple args;\
EXTENSION_PROXY_SECOND(function) \
Py::Tuple args(1);\
args.setItem(0, Py::Object(this->getExtensionPyObject(), true));\
#define EXTENSION_PROXY_NOARG(function) \
EXTENSION_PROXY_FIRST(function) \
Py::Tuple args; \
EXTENSION_PROXY_SECOND(function) \
Py::Tuple args(1); \
args.setItem(0, Py::Object(this->getExtensionPyObject(), true)); \
EXTENSION_PROXY_THIRD()
#define EXTENSION_PROXY_ONEARG(function, arg)\
EXTENSION_PROXY_FIRST(function) \
Py::Tuple args;\
args.setItem(0, arg); \
EXTENSION_PROXY_SECOND(function) \
Py::Tuple args(2);\
args.setItem(0, Py::Object(this->getExtensionPyObject(), true));\
args.setItem(1, arg); \
#define EXTENSION_PROXY_ONEARG(function, arg) \
EXTENSION_PROXY_FIRST(function) \
Py::Tuple args; \
args.setItem(0, arg); \
EXTENSION_PROXY_SECOND(function) \
Py::Tuple args(2); \
args.setItem(0, Py::Object(this->getExtensionPyObject(), true)); \
args.setItem(1, arg); \
EXTENSION_PROXY_THIRD()
#define EXTENSION_PYTHON_OVERRIDE_VOID_NOARGS(function)\
virtual void function() override {\
EXTENSION_PROXY_NOARGS(function)\
#define EXTENSION_PYTHON_OVERRIDE_VOID_NOARGS(function) \
virtual void function() override { EXTENSION_PROXY_NOARGS(function) };
#define EXTENSION_PYTHON_OVERRIDE_OBJECT_NOARGS(function) \
virtual PyObject* function() override \
{ \
EXTENSION_PROXY_NOARGS(function) \
return res.ptr(); \
};
#define EXTENSION_PYTHON_OVERRIDE_OBJECT_NOARGS(function)\
virtual PyObject* function() override {\
EXTENSION_PROXY_NOARGS(function)\
return res.ptr();\
};
} // namespace App
} //App
#endif // APP_EXTENSIONPYTHON_H
#endif // APP_EXTENSIONPYTHON_H

View File

@@ -21,7 +21,6 @@
***************************************************************************/
#ifndef APP_FEATURECUSTOM_H
#define APP_FEATURECUSTOM_H
@@ -41,8 +40,8 @@ class Property;
* it has no support for in Python written feature classes.
* @author Werner Mayer
*/
template <class FeatureT>
class FeatureCustomT : public FeatureT //NOLINT
template<class FeatureT>
class FeatureCustomT: public FeatureT // NOLINT
{
PROPERTY_HEADER_WITH_OVERRIDE(App::FeatureCustomT<FeatureT>);
@@ -53,46 +52,55 @@ public:
/** @name methods override DocumentObject */
//@{
short mustExecute() const override {
short mustExecute() const override
{
return FeatureT::mustExecute();
}
/// recalculate the Feature
DocumentObjectExecReturn *execute() override {
DocumentObjectExecReturn* execute() override
{
return FeatureT::execute();
}
/// returns the type name of the ViewProvider
const char* getViewProviderName() const override {
const char* getViewProviderName() const override
{
return FeatureT::getViewProviderName();
}
PyObject *getPyObject() override {
PyObject* getPyObject() override
{
return FeatureT::getPyObject();
}
void setPyObject(PyObject *obj) override {
void setPyObject(PyObject* obj) override
{
FeatureT::setPyObject(obj);
}
protected:
void onBeforeChange(const Property* prop) override {
void onBeforeChange(const Property* prop) override
{
FeatureT::onBeforeChange(prop);
}
void onChanged(const Property* prop) override {
void onChanged(const Property* prop) override
{
FeatureT::onChanged(prop);
}
void onDocumentRestored() override {
void onDocumentRestored() override
{
FeatureT::onDocumentRestored();
}
void onSettingDocument() override {
void onSettingDocument() override
{
FeatureT::onSettingDocument();
}
public:
FeatureCustomT(const FeatureCustomT&) = delete;
FeatureCustomT(FeatureCustomT&&) = delete;
FeatureCustomT& operator= (const FeatureCustomT&) = delete;
FeatureCustomT& operator= (FeatureCustomT&&) = delete;
FeatureCustomT& operator=(const FeatureCustomT&) = delete;
FeatureCustomT& operator=(FeatureCustomT&&) = delete;
};
} //namespace App
} // namespace App
#endif // APP_FEATURECUSTOM_H
#endif // APP_FEATURECUSTOM_H

View File

@@ -23,7 +23,7 @@
#include "PreCompiled.h"
#ifndef _PreComp_
# include <sstream>
#include <sstream>
#endif
#include <App/DocumentObjectPy.h>
@@ -39,8 +39,7 @@ using namespace App;
FeaturePythonImp::FeaturePythonImp(App::DocumentObject* o)
: object(o)
{
}
{}
FeaturePythonImp::~FeaturePythonImp()
{
@@ -56,7 +55,8 @@ FeaturePythonImp::~FeaturePythonImp()
}
}
void FeaturePythonImp::init(PyObject *pyobj) {
void FeaturePythonImp::init(PyObject* pyobj)
{
Base::PyGILStateLocker lock;
has__object__ = !!PyObject_HasAttrString(pyobj, "__object__");
@@ -66,11 +66,11 @@ void FeaturePythonImp::init(PyObject *pyobj) {
FC_PY_FEATURE_PYTHON
}
#define FC_PY_CALL_CHECK(_name) _FC_PY_CALL_CHECK(_name,return(false))
#define FC_PY_CALL_CHECK(_name) _FC_PY_CALL_CHECK(_name, return (false))
/*!
Calls the execute() method of the Python feature class. If the Python feature class doesn't have an execute()
method or if it returns False this method also return false and true otherwise.
Calls the execute() method of the Python feature class. If the Python feature class doesn't have an
execute() method or if it returns False this method also return false and true otherwise.
*/
bool FeaturePythonImp::execute()
{
@@ -79,16 +79,18 @@ bool FeaturePythonImp::execute()
try {
if (has__object__) {
Py::Object res = Base::pyCall(py_execute.ptr());
if (res.isBoolean() && !res.isTrue())
if (res.isBoolean() && !res.isTrue()) {
return false;
}
return true;
}
else {
Py::Tuple args(1);
args.setItem(0, Py::Object(object->getPyObject(), true));
Py::Object res = Base::pyCall(py_execute.ptr(),args.ptr());
if (res.isBoolean() && !res.isTrue())
Py::Object res = Base::pyCall(py_execute.ptr(), args.ptr());
if (res.isBoolean() && !res.isTrue()) {
return false;
}
return true;
}
}
@@ -97,7 +99,7 @@ bool FeaturePythonImp::execute()
PyErr_Clear();
return false;
}
Base::PyException::ThrowException(); // extract the Python error text
Base::PyException::ThrowException(); // extract the Python error text
}
return false;
@@ -115,12 +117,12 @@ bool FeaturePythonImp::mustExecute() const
else {
Py::Tuple args(1);
args.setItem(0, Py::Object(object->getPyObject(), true));
Py::Object res(Base::pyCall(py_mustExecute.ptr(),args.ptr()));
Py::Object res(Base::pyCall(py_mustExecute.ptr(), args.ptr()));
return res.isTrue();
}
}
catch (Py::Exception&) {
Base::PyException e; // extract the Python error text
Base::PyException e; // extract the Python error text
e.ReportException();
}
return false;
@@ -129,54 +131,58 @@ bool FeaturePythonImp::mustExecute() const
void FeaturePythonImp::onBeforeChange(const Property* prop)
{
if (py_onBeforeChange.isNone())
if (py_onBeforeChange.isNone()) {
return;
}
// Run the execute method of the proxy object.
Base::PyGILStateLocker lock;
try {
const char *prop_name = object->getPropertyName(prop);
if (!prop_name)
const char* prop_name = object->getPropertyName(prop);
if (!prop_name) {
return;
}
if (has__object__) {
Py::Tuple args(1);
args.setItem(0, Py::String(prop_name));
Base::pyCall(py_onBeforeChange.ptr(),args.ptr());
Base::pyCall(py_onBeforeChange.ptr(), args.ptr());
}
else {
Py::Tuple args(2);
args.setItem(0, Py::Object(object->getPyObject(), true));
args.setItem(1, Py::String(prop_name));
Base::pyCall(py_onBeforeChange.ptr(),args.ptr());
Base::pyCall(py_onBeforeChange.ptr(), args.ptr());
}
}
catch (Py::Exception&) {
Base::PyException e; // extract the Python error text
Base::PyException e; // extract the Python error text
e.ReportException();
}
}
bool FeaturePythonImp::onBeforeChangeLabel(std::string &newLabel)
bool FeaturePythonImp::onBeforeChangeLabel(std::string& newLabel)
{
if(py_onBeforeChangeLabel.isNone())
if (py_onBeforeChangeLabel.isNone()) {
return false;
}
// Run the execute method of the proxy object.
Base::PyGILStateLocker lock;
try {
Py::Tuple args(2);
args.setItem(0, Py::Object(object->getPyObject(), true));
args.setItem(1,Py::String(newLabel));
Py::Object ret(Base::pyCall(py_onBeforeChangeLabel.ptr(),args.ptr()));
if(!ret.isNone()) {
if(!ret.isString())
args.setItem(1, Py::String(newLabel));
Py::Object ret(Base::pyCall(py_onBeforeChangeLabel.ptr(), args.ptr()));
if (!ret.isNone()) {
if (!ret.isString()) {
throw Py::TypeError("onBeforeChangeLabel expects to return a string");
}
newLabel = ret.as_string();
return true;
}
}
catch (Py::Exception&) {
Base::PyException e; // extract the Python error text
Base::PyException e; // extract the Python error text
e.ReportException();
}
return false;
@@ -184,35 +190,37 @@ bool FeaturePythonImp::onBeforeChangeLabel(std::string &newLabel)
void FeaturePythonImp::onChanged(const Property* prop)
{
if (py_onChanged.isNone())
if (py_onChanged.isNone()) {
return;
}
// Run the execute method of the proxy object.
Base::PyGILStateLocker lock;
try {
const char *prop_name = object->getPropertyName(prop);
if (!prop_name)
const char* prop_name = object->getPropertyName(prop);
if (!prop_name) {
return;
}
if (has__object__) {
Py::Tuple args(1);
args.setItem(0, Py::String(prop_name));
Base::pyCall(py_onChanged.ptr(),args.ptr());
Base::pyCall(py_onChanged.ptr(), args.ptr());
}
else {
Py::Tuple args(2);
args.setItem(0, Py::Object(object->getPyObject(), true));
args.setItem(1, Py::String(prop_name));
Base::pyCall(py_onChanged.ptr(),args.ptr());
Base::pyCall(py_onChanged.ptr(), args.ptr());
}
}
catch (Py::Exception&) {
Base::PyException e; // extract the Python error text
Base::PyException e; // extract the Python error text
e.ReportException();
}
}
void FeaturePythonImp::onDocumentRestored()
{
_FC_PY_CALL_CHECK(onDocumentRestored,return);
_FC_PY_CALL_CHECK(onDocumentRestored, return);
// Run the execute method of the proxy object.
Base::PyGILStateLocker lock;
@@ -223,11 +231,11 @@ void FeaturePythonImp::onDocumentRestored()
else {
Py::Tuple args(1);
args.setItem(0, Py::Object(object->getPyObject(), true));
Base::pyCall(py_onDocumentRestored.ptr(),args.ptr());
Base::pyCall(py_onDocumentRestored.ptr(), args.ptr());
}
}
catch (Py::Exception&) {
Base::PyException e; // extract the Python error text
Base::PyException e; // extract the Python error text
e.ReportException();
}
}
@@ -249,57 +257,71 @@ void FeaturePythonImp::unsetupObject()
}
}
catch (Py::Exception&) {
Base::PyException e; // extract the Python error text
Base::PyException e; // extract the Python error text
e.ReportException();
}
}
bool FeaturePythonImp::getSubObject(DocumentObject *&ret, const char *subname,
PyObject **pyObj, Base::Matrix4D *_mat, bool transform, int depth) const
bool FeaturePythonImp::getSubObject(DocumentObject*& ret,
const char* subname,
PyObject** pyObj,
Base::Matrix4D* _mat,
bool transform,
int depth) const
{
FC_PY_CALL_CHECK(getSubObject);
Base::PyGILStateLocker lock;
try {
Py::Tuple args(6);
args.setItem(0, Py::Object(object->getPyObject(), true));
if(!subname) subname = "";
args.setItem(1,Py::String(subname));
args.setItem(2,Py::Int(pyObj?2:1));
Base::MatrixPy *pyMat = new Base::MatrixPy(new Base::Matrix4D);
if(_mat) *pyMat->getMatrixPtr() = *_mat;
args.setItem(3,Py::asObject(pyMat));
args.setItem(4,Py::Boolean(transform));
args.setItem(5,Py::Int(depth));
if (!subname) {
subname = "";
}
args.setItem(1, Py::String(subname));
args.setItem(2, Py::Int(pyObj ? 2 : 1));
Base::MatrixPy* pyMat = new Base::MatrixPy(new Base::Matrix4D);
if (_mat) {
*pyMat->getMatrixPtr() = *_mat;
}
args.setItem(3, Py::asObject(pyMat));
args.setItem(4, Py::Boolean(transform));
args.setItem(5, Py::Int(depth));
Py::Object res(Base::pyCall(py_getSubObject.ptr(),args.ptr()));
if(res.isNone()) {
Py::Object res(Base::pyCall(py_getSubObject.ptr(), args.ptr()));
if (res.isNone()) {
ret = nullptr;
return true;
}
if(!res.isTrue())
if (!res.isTrue()) {
return false;
if(!res.isSequence())
}
if (!res.isSequence()) {
throw Py::TypeError("getSubObject expects return type of tuple");
}
Py::Sequence seq(res);
if(seq.length() < 2 ||
(!seq.getItem(0).isNone() &&
!PyObject_TypeCheck(seq.getItem(0).ptr(),&DocumentObjectPy::Type)) ||
!PyObject_TypeCheck(seq.getItem(1).ptr(),&Base::MatrixPy::Type))
{
if (seq.length() < 2
|| (!seq.getItem(0).isNone()
&& !PyObject_TypeCheck(seq.getItem(0).ptr(), &DocumentObjectPy::Type))
|| !PyObject_TypeCheck(seq.getItem(1).ptr(), &Base::MatrixPy::Type)) {
throw Py::TypeError("getSubObject expects return type of (obj,matrix,pyobj)");
}
if(_mat)
if (_mat) {
*_mat = *static_cast<Base::MatrixPy*>(seq.getItem(1).ptr())->getMatrixPtr();
if(pyObj) {
if(seq.length()>2)
*pyObj = Py::new_reference_to(seq.getItem(2));
else
*pyObj = Py::new_reference_to(Py::None());
}
if(seq.getItem(0).isNone())
if (pyObj) {
if (seq.length() > 2) {
*pyObj = Py::new_reference_to(seq.getItem(2));
}
else {
*pyObj = Py::new_reference_to(Py::None());
}
}
if (seq.getItem(0).isNone()) {
ret = nullptr;
else
}
else {
ret = static_cast<DocumentObjectPy*>(seq.getItem(0).ptr())->getDocumentObjectPtr();
}
return true;
}
catch (Py::Exception&) {
@@ -307,30 +329,34 @@ bool FeaturePythonImp::getSubObject(DocumentObject *&ret, const char *subname,
PyErr_Clear();
return false;
}
Base::PyException e; // extract the Python error text
Base::PyException e; // extract the Python error text
e.ReportException();
ret = nullptr;
return true;
}
}
bool FeaturePythonImp::getSubObjects(std::vector<std::string> &ret, int reason) const {
bool FeaturePythonImp::getSubObjects(std::vector<std::string>& ret, int reason) const
{
FC_PY_CALL_CHECK(getSubObjects);
Base::PyGILStateLocker lock;
try {
Py::Tuple args(2);
args.setItem(0, Py::Object(object->getPyObject(), true));
args.setItem(1, Py::Int(reason));
Py::Object res(Base::pyCall(py_getSubObjects.ptr(),args.ptr()));
if(!res.isTrue())
Py::Object res(Base::pyCall(py_getSubObjects.ptr(), args.ptr()));
if (!res.isTrue()) {
return true;
if(!res.isSequence())
}
if (!res.isSequence()) {
throw Py::TypeError("getSubObjects expects return type of tuple");
}
Py::Sequence seq(res);
for(Py_ssize_t i=0;i<seq.length();++i) {
for (Py_ssize_t i = 0; i < seq.length(); ++i) {
Py::Object name(seq[i].ptr());
if(!name.isString())
if (!name.isString()) {
throw Py::TypeError("getSubObjects expects string in returned sequence");
}
ret.push_back(name.as_string());
}
return true;
@@ -340,48 +366,56 @@ bool FeaturePythonImp::getSubObjects(std::vector<std::string> &ret, int reason)
PyErr_Clear();
return false;
}
Base::PyException e; // extract the Python error text
Base::PyException e; // extract the Python error text
e.ReportException();
return true;
}
}
bool FeaturePythonImp::getLinkedObject(DocumentObject *&ret, bool recurse,
Base::Matrix4D *_mat, bool transform, int depth) const
bool FeaturePythonImp::getLinkedObject(DocumentObject*& ret,
bool recurse,
Base::Matrix4D* _mat,
bool transform,
int depth) const
{
FC_PY_CALL_CHECK(getLinkedObject);
Base::PyGILStateLocker lock;
try {
Py::Tuple args(5);
args.setItem(0, Py::Object(object->getPyObject(), true));
args.setItem(1,Py::Boolean(recurse));
Base::MatrixPy *pyMat = new Base::MatrixPy(new Base::Matrix4D);
if(_mat) *pyMat->getMatrixPtr() = *_mat;
args.setItem(2,Py::asObject(pyMat));
args.setItem(3,Py::Boolean(transform));
args.setItem(4,Py::Int(depth));
args.setItem(1, Py::Boolean(recurse));
Base::MatrixPy* pyMat = new Base::MatrixPy(new Base::Matrix4D);
if (_mat) {
*pyMat->getMatrixPtr() = *_mat;
}
args.setItem(2, Py::asObject(pyMat));
args.setItem(3, Py::Boolean(transform));
args.setItem(4, Py::Int(depth));
Py::Object res(Base::pyCall(py_getLinkedObject.ptr(),args.ptr()));
if(!res.isTrue()) {
Py::Object res(Base::pyCall(py_getLinkedObject.ptr(), args.ptr()));
if (!res.isTrue()) {
ret = object;
return true;
}
if(!res.isSequence())
throw Py::TypeError("getLinkedObject expects return type of (object,matrix)");
Py::Sequence seq(res);
if(seq.length() != 2 ||
(!seq.getItem(0).isNone() &&
!PyObject_TypeCheck(seq.getItem(0).ptr(),&DocumentObjectPy::Type)) ||
!PyObject_TypeCheck(seq.getItem(1).ptr(),&Base::MatrixPy::Type))
{
if (!res.isSequence()) {
throw Py::TypeError("getLinkedObject expects return type of (object,matrix)");
}
if(_mat)
Py::Sequence seq(res);
if (seq.length() != 2
|| (!seq.getItem(0).isNone()
&& !PyObject_TypeCheck(seq.getItem(0).ptr(), &DocumentObjectPy::Type))
|| !PyObject_TypeCheck(seq.getItem(1).ptr(), &Base::MatrixPy::Type)) {
throw Py::TypeError("getLinkedObject expects return type of (object,matrix)");
}
if (_mat) {
*_mat = *static_cast<Base::MatrixPy*>(seq.getItem(1).ptr())->getMatrixPtr();
if(seq.getItem(0).isNone())
}
if (seq.getItem(0).isNone()) {
ret = object;
else
}
else {
ret = static_cast<DocumentObjectPy*>(seq.getItem(0).ptr())->getDocumentObjectPtr();
}
return true;
}
catch (Py::Exception&) {
@@ -389,28 +423,27 @@ bool FeaturePythonImp::getLinkedObject(DocumentObject *&ret, bool recurse,
PyErr_Clear();
return false;
}
Base::PyException e; // extract the Python error text
Base::PyException e; // extract the Python error text
e.ReportException();
ret = nullptr;
return true;
}
}
PyObject *FeaturePythonImp::getPyObject()
PyObject* FeaturePythonImp::getPyObject()
{
// ref counter is set to 1
return new FeaturePythonPyT<DocumentObjectPy>(object);
}
FeaturePythonImp::ValueT
FeaturePythonImp::hasChildElement() const
FeaturePythonImp::ValueT FeaturePythonImp::hasChildElement() const
{
_FC_PY_CALL_CHECK(hasChildElement,return(NotImplemented));
_FC_PY_CALL_CHECK(hasChildElement, return (NotImplemented));
Base::PyGILStateLocker lock;
try {
Py::Tuple args(1);
args.setItem(0, Py::Object(object->getPyObject(), true));
Py::Boolean ok(Base::pyCall(py_hasChildElement.ptr(),args.ptr()));
Py::Boolean ok(Base::pyCall(py_hasChildElement.ptr(), args.ptr()));
return static_cast<bool>(ok) ? Accepted : Rejected;
}
catch (Py::Exception&) {
@@ -419,48 +452,50 @@ FeaturePythonImp::hasChildElement() const
return NotImplemented;
}
Base::PyException e; // extract the Python error text
Base::PyException e; // extract the Python error text
e.ReportException();
return Rejected;
}
}
int FeaturePythonImp::isElementVisible(const char *element) const {
_FC_PY_CALL_CHECK(isElementVisible,return(-2));
int FeaturePythonImp::isElementVisible(const char* element) const
{
_FC_PY_CALL_CHECK(isElementVisible, return (-2));
Base::PyGILStateLocker lock;
try {
Py::Tuple args(2);
args.setItem(0, Py::Object(object->getPyObject(), true));
args.setItem(1,Py::String(element?element:""));
return Py::Int(Base::pyCall(py_isElementVisible.ptr(),args.ptr()));
args.setItem(1, Py::String(element ? element : ""));
return Py::Int(Base::pyCall(py_isElementVisible.ptr(), args.ptr()));
}
catch (Py::Exception&) {
if (PyErr_ExceptionMatches(PyExc_NotImplementedError)) {
PyErr_Clear();
return -2;
}
Base::PyException e; // extract the Python error text
Base::PyException e; // extract the Python error text
e.ReportException();
return -1;
}
}
int FeaturePythonImp::setElementVisible(const char *element, bool visible) {
_FC_PY_CALL_CHECK(setElementVisible,return(-2));
int FeaturePythonImp::setElementVisible(const char* element, bool visible)
{
_FC_PY_CALL_CHECK(setElementVisible, return (-2));
Base::PyGILStateLocker lock;
try {
Py::Tuple args(3);
args.setItem(0, Py::Object(object->getPyObject(), true));
args.setItem(1,Py::String(element?element:""));
args.setItem(2,Py::Boolean(visible));
return Py::Int(Base::pyCall(py_setElementVisible.ptr(),args.ptr()));
args.setItem(1, Py::String(element ? element : ""));
args.setItem(2, Py::Boolean(visible));
return Py::Int(Base::pyCall(py_setElementVisible.ptr(), args.ptr()));
}
catch (Py::Exception&) {
if (PyErr_ExceptionMatches(PyExc_NotImplementedError)) {
PyErr_Clear();
return -2;
}
Base::PyException e; // extract the Python error text
Base::PyException e; // extract the Python error text
e.ReportException();
return -1;
}
@@ -468,30 +503,29 @@ int FeaturePythonImp::setElementVisible(const char *element, bool visible) {
std::string FeaturePythonImp::getViewProviderName()
{
_FC_PY_CALL_CHECK(getViewProviderName,return(std::string()));
_FC_PY_CALL_CHECK(getViewProviderName, return (std::string()));
Base::PyGILStateLocker lock;
try {
Py::TupleN args(Py::Object(object->getPyObject(), true));
Py::String ret(Base::pyCall(py_getViewProviderName.ptr(),args.ptr()));
Py::String ret(Base::pyCall(py_getViewProviderName.ptr(), args.ptr()));
return ret.as_string();
}
catch (Py::Exception&) {
Base::PyException e; // extract the Python error text
Base::PyException e; // extract the Python error text
e.ReportException();
}
return {};
}
FeaturePythonImp::ValueT
FeaturePythonImp::canLinkProperties() const
FeaturePythonImp::ValueT FeaturePythonImp::canLinkProperties() const
{
_FC_PY_CALL_CHECK(canLinkProperties,return(NotImplemented));
_FC_PY_CALL_CHECK(canLinkProperties, return (NotImplemented));
Base::PyGILStateLocker lock;
try {
Py::Tuple args(1);
args.setItem(0, Py::Object(object->getPyObject(), true));
Py::Boolean ok(Base::pyCall(py_canLinkProperties.ptr(),args.ptr()));
Py::Boolean ok(Base::pyCall(py_canLinkProperties.ptr(), args.ptr()));
return ok ? Accepted : Rejected;
}
catch (Py::Exception&) {
@@ -499,21 +533,20 @@ FeaturePythonImp::canLinkProperties() const
PyErr_Clear();
return NotImplemented;
}
Base::PyException e; // extract the Python error text
Base::PyException e; // extract the Python error text
e.ReportException();
return Rejected;
}
}
FeaturePythonImp::ValueT
FeaturePythonImp::allowDuplicateLabel() const
FeaturePythonImp::ValueT FeaturePythonImp::allowDuplicateLabel() const
{
_FC_PY_CALL_CHECK(allowDuplicateLabel,return(NotImplemented));
_FC_PY_CALL_CHECK(allowDuplicateLabel, return (NotImplemented));
Base::PyGILStateLocker lock;
try {
Py::Tuple args(1);
args.setItem(0, Py::Object(object->getPyObject(), true));
Py::Boolean ok(Base::pyCall(py_allowDuplicateLabel.ptr(),args.ptr()));
Py::Boolean ok(Base::pyCall(py_allowDuplicateLabel.ptr(), args.ptr()));
return ok ? Accepted : Rejected;
}
catch (Py::Exception&) {
@@ -522,19 +555,20 @@ FeaturePythonImp::allowDuplicateLabel() const
return NotImplemented;
}
Base::PyException e; // extract the Python error text
Base::PyException e; // extract the Python error text
e.ReportException();
return Rejected;
}
}
int FeaturePythonImp::canLoadPartial() const {
_FC_PY_CALL_CHECK(canLoadPartial,return(-1));
int FeaturePythonImp::canLoadPartial() const
{
_FC_PY_CALL_CHECK(canLoadPartial, return (-1));
Base::PyGILStateLocker lock;
try {
Py::Tuple args(1);
args.setItem(0, Py::Object(object->getPyObject(), true));
Py::Int ret(Base::pyCall(py_canLoadPartial.ptr(),args.ptr()));
Py::Int ret(Base::pyCall(py_canLoadPartial.ptr(), args.ptr()));
return ret;
}
catch (Py::Exception&) {
@@ -542,28 +576,28 @@ int FeaturePythonImp::canLoadPartial() const {
PyErr_Clear();
return -1;
}
Base::PyException e; // extract the Python error text
Base::PyException e; // extract the Python error text
e.ReportException();
return 0;
}
}
FeaturePythonImp::ValueT
FeaturePythonImp::redirectSubName(std::ostringstream &ss,
App::DocumentObject *topParent,
App::DocumentObject *child) const
FeaturePythonImp::ValueT FeaturePythonImp::redirectSubName(std::ostringstream& ss,
App::DocumentObject* topParent,
App::DocumentObject* child) const
{
_FC_PY_CALL_CHECK(redirectSubName,return(NotImplemented));
_FC_PY_CALL_CHECK(redirectSubName, return (NotImplemented));
Base::PyGILStateLocker lock;
try {
Py::Tuple args(4);
args.setItem(0, Py::Object(object->getPyObject(), true));
args.setItem(1,Py::String(ss.str()));
args.setItem(2,topParent?Py::Object(topParent->getPyObject(),true):Py::Object());
args.setItem(3,child?Py::Object(child->getPyObject(),true):Py::Object());
Py::Object ret(Base::pyCall(py_redirectSubName.ptr(),args.ptr()));
if (ret.isNone())
args.setItem(1, Py::String(ss.str()));
args.setItem(2, topParent ? Py::Object(topParent->getPyObject(), true) : Py::Object());
args.setItem(3, child ? Py::Object(child->getPyObject(), true) : Py::Object());
Py::Object ret(Base::pyCall(py_redirectSubName.ptr(), args.ptr()));
if (ret.isNone()) {
return Rejected;
}
ss.str("");
ss << ret.as_string();
return Accepted;
@@ -574,20 +608,20 @@ FeaturePythonImp::redirectSubName(std::ostringstream &ss,
return NotImplemented;
}
Base::PyException e; // extract the Python error text
Base::PyException e; // extract the Python error text
e.ReportException();
return Rejected;
}
}
bool FeaturePythonImp::editProperty(const char *name)
bool FeaturePythonImp::editProperty(const char* name)
{
_FC_PY_CALL_CHECK(editProperty,return false);
_FC_PY_CALL_CHECK(editProperty, return false);
Base::PyGILStateLocker lock;
try {
Py::Tuple args(1);
args.setItem(0, Py::String(name));
Py::Object ret(Base::pyCall(py_editProperty.ptr(),args.ptr()));
Py::Object ret(Base::pyCall(py_editProperty.ptr(), args.ptr()));
return ret.isTrue();
}
catch (Py::Exception&) {
@@ -596,7 +630,7 @@ bool FeaturePythonImp::editProperty(const char *name)
return false;
}
Base::PyException e; // extract the Python error text
Base::PyException e; // extract the Python error text
e.ReportException();
}
return false;
@@ -604,29 +638,37 @@ bool FeaturePythonImp::editProperty(const char *name)
// ---------------------------------------------------------
namespace App {
namespace App
{
PROPERTY_SOURCE_TEMPLATE(App::FeaturePython, App::DocumentObject)
template<> const char* App::FeaturePython::getViewProviderName() const {
template<>
const char* App::FeaturePython::getViewProviderName() const
{
return "Gui::ViewProviderFeaturePython";
}
template<> PyObject* App::FeaturePython::getPyObject() {
template<>
PyObject* App::FeaturePython::getPyObject()
{
if (PythonObject.is(Py::_None())) {
// ref counter is set to 1
PythonObject = Py::Object(new FeaturePythonPyT<DocumentObjectPy>(this),true);
PythonObject = Py::Object(new FeaturePythonPyT<DocumentObjectPy>(this), true);
}
return Py::new_reference_to(PythonObject);
}
// explicit template instantiation
template class AppExport FeaturePythonT<DocumentObject>;
}
} // namespace App
// ---------------------------------------------------------
namespace App {
namespace App
{
PROPERTY_SOURCE_TEMPLATE(App::GeometryPython, App::GeoFeature)
template<> const char* App::GeometryPython::getViewProviderName() const {
template<>
const char* App::GeometryPython::getViewProviderName() const
{
return "Gui::ViewProviderGeometryPython";
}
// explicit template instantiation
template class AppExport FeaturePythonT<GeoFeature>;
}
} // namespace App

View File

@@ -21,7 +21,6 @@
***************************************************************************/
#ifndef APP_FEATUREPYTHON_H
#define APP_FEATUREPYTHON_H
@@ -38,10 +37,11 @@ class Property;
class AppExport FeaturePythonImp
{
public:
enum ValueT {
NotImplemented = 0, // not handled
Accepted = 1, // handled and accepted
Rejected = 2 // handled and rejected
enum ValueT
{
NotImplemented = 0, // not handled
Accepted = 1, // handled and accepted
Rejected = 2 // handled and rejected
};
explicit FeaturePythonImp(App::DocumentObject*);
@@ -50,93 +50,95 @@ public:
bool execute();
bool mustExecute() const;
void onBeforeChange(const Property* prop);
bool onBeforeChangeLabel(std::string &newLabel);
bool onBeforeChangeLabel(std::string& newLabel);
void onChanged(const Property* prop);
void onDocumentRestored();
void unsetupObject();
std::string getViewProviderName();
PyObject *getPyObject();
PyObject* getPyObject();
bool getSubObject(App::DocumentObject *&ret, const char *subname, PyObject **pyObj,
Base::Matrix4D *mat, bool transform, int depth) const;
bool getSubObject(App::DocumentObject*& ret,
const char* subname,
PyObject** pyObj,
Base::Matrix4D* mat,
bool transform,
int depth) const;
bool getSubObjects(std::vector<std::string> &ret, int reason) const;
bool getSubObjects(std::vector<std::string>& ret, int reason) const;
bool getLinkedObject(App::DocumentObject *&ret, bool recurse,
Base::Matrix4D *mat, bool transform, int depth) const;
bool getLinkedObject(App::DocumentObject*& ret,
bool recurse,
Base::Matrix4D* mat,
bool transform,
int depth) const;
ValueT canLinkProperties() const;
ValueT allowDuplicateLabel() const;
ValueT redirectSubName(std::ostringstream &ss,
App::DocumentObject *topParent,
App::DocumentObject *child) const;
ValueT redirectSubName(std::ostringstream& ss,
App::DocumentObject* topParent,
App::DocumentObject* child) const;
int canLoadPartial() const;
/// return true to activate tree view group object handling
ValueT hasChildElement() const;
/// Get sub-element visibility
int isElementVisible(const char *) const;
int isElementVisible(const char*) const;
/// Set sub-element visibility
int setElementVisible(const char *, bool);
int setElementVisible(const char*, bool);
bool editProperty(const char *propName);
bool editProperty(const char* propName);
private:
App::DocumentObject* object;
bool has__object__{false};
bool has__object__ {false};
#define FC_PY_FEATURE_PYTHON \
FC_PY_ELEMENT(execute)\
FC_PY_ELEMENT(mustExecute)\
FC_PY_ELEMENT(onBeforeChange)\
FC_PY_ELEMENT(onBeforeChangeLabel)\
FC_PY_ELEMENT(onChanged)\
FC_PY_ELEMENT(onDocumentRestored)\
FC_PY_ELEMENT(unsetupObject)\
FC_PY_ELEMENT(getViewProviderName)\
FC_PY_ELEMENT(getSubObject)\
FC_PY_ELEMENT(getSubObjects)\
FC_PY_ELEMENT(getLinkedObject)\
FC_PY_ELEMENT(canLinkProperties)\
FC_PY_ELEMENT(allowDuplicateLabel)\
FC_PY_ELEMENT(redirectSubName)\
FC_PY_ELEMENT(canLoadPartial)\
FC_PY_ELEMENT(hasChildElement)\
FC_PY_ELEMENT(isElementVisible)\
FC_PY_ELEMENT(setElementVisible)\
FC_PY_ELEMENT(editProperty)\
#define FC_PY_FEATURE_PYTHON \
FC_PY_ELEMENT(execute) \
FC_PY_ELEMENT(mustExecute) \
FC_PY_ELEMENT(onBeforeChange) \
FC_PY_ELEMENT(onBeforeChangeLabel) \
FC_PY_ELEMENT(onChanged) \
FC_PY_ELEMENT(onDocumentRestored) \
FC_PY_ELEMENT(unsetupObject) \
FC_PY_ELEMENT(getViewProviderName) \
FC_PY_ELEMENT(getSubObject) \
FC_PY_ELEMENT(getSubObjects) \
FC_PY_ELEMENT(getLinkedObject) \
FC_PY_ELEMENT(canLinkProperties) \
FC_PY_ELEMENT(allowDuplicateLabel) \
FC_PY_ELEMENT(redirectSubName) \
FC_PY_ELEMENT(canLoadPartial) \
FC_PY_ELEMENT(hasChildElement) \
FC_PY_ELEMENT(isElementVisible) \
FC_PY_ELEMENT(setElementVisible) \
FC_PY_ELEMENT(editProperty)
#define FC_PY_ELEMENT_DEFINE(_name) \
Py::Object py_##_name;
#define FC_PY_ELEMENT_DEFINE(_name) Py::Object py_##_name;
#define FC_PY_ELEMENT_INIT(_name) \
FC_PY_GetCallable(pyobj,#_name,py_##_name);\
if(!py_##_name.isNone()) {\
PyObject *pyRecursive = PyObject_GetAttrString(pyobj, \
"__allow_recursive_" #_name);\
if(!pyRecursive) {\
PyErr_Clear();\
_Flags.set(FlagAllowRecursive_##_name, false);\
}else{\
_Flags.set(FlagAllowRecursive_##_name, PyObject_IsTrue(pyRecursive));\
Py_DECREF(pyRecursive);\
}\
#define FC_PY_ELEMENT_INIT(_name) \
FC_PY_GetCallable(pyobj, #_name, py_##_name); \
if (!py_##_name.isNone()) { \
PyObject* pyRecursive = PyObject_GetAttrString(pyobj, "__allow_recursive_" #_name); \
if (!pyRecursive) { \
PyErr_Clear(); \
_Flags.set(FlagAllowRecursive_##_name, false); \
} \
else { \
_Flags.set(FlagAllowRecursive_##_name, PyObject_IsTrue(pyRecursive)); \
Py_DECREF(pyRecursive); \
} \
}
#define FC_PY_ELEMENT_FLAG(_name) \
FlagCalling_##_name,\
FlagAllowRecursive_##_name,
#define FC_PY_ELEMENT_FLAG(_name) FlagCalling_##_name, FlagAllowRecursive_##_name,
#define _FC_PY_CALL_CHECK(_name,_ret) \
if((!_Flags.test(FlagAllowRecursive_##_name) \
&& _Flags.test(FlagCalling_##_name)) \
|| py_##_name.isNone()) \
{\
_ret;\
}\
#define _FC_PY_CALL_CHECK(_name, _ret) \
if ((!_Flags.test(FlagAllowRecursive_##_name) && _Flags.test(FlagCalling_##_name)) \
|| py_##_name.isNone()) { \
_ret; \
} \
Base::BitsetLocker<Flags> guard(_Flags, FlagCalling_##_name);
#undef FC_PY_ELEMENT
@@ -147,16 +149,16 @@ private:
#undef FC_PY_ELEMENT
#define FC_PY_ELEMENT(_name) FC_PY_ELEMENT_FLAG(_name)
enum Flag {
FC_PY_FEATURE_PYTHON
FlagMax,
enum Flag
{
FC_PY_FEATURE_PYTHON FlagMax,
};
using Flags = std::bitset<FlagMax>;
mutable Flags _Flags;
public:
void init(PyObject *pyobj);
void init(PyObject* pyobj);
};
/**
@@ -164,185 +166,226 @@ public:
* derived class as Python feature -- simply by subclassing.
* @author Werner Mayer
*/
template <class FeatureT>
class FeaturePythonT : public FeatureT
template<class FeatureT>
class FeaturePythonT: public FeatureT
{
PROPERTY_HEADER_WITH_OVERRIDE(App::FeaturePythonT<FeatureT>);
public:
FeaturePythonT() {
ADD_PROPERTY(Proxy,(Py::Object()));
FeaturePythonT()
{
ADD_PROPERTY(Proxy, (Py::Object()));
// cannot move this to the initializer list to avoid warning
imp = new FeaturePythonImp(this);
}
~FeaturePythonT() override {
~FeaturePythonT() override
{
delete imp;
}
/** @name methods override DocumentObject */
//@{
short mustExecute() const override {
if (this->isTouched())
short mustExecute() const override
{
if (this->isTouched()) {
return 1;
}
auto ret = FeatureT::mustExecute();
if(ret) return ret;
return imp->mustExecute()?1:0;
if (ret) {
return ret;
}
return imp->mustExecute() ? 1 : 0;
}
/// recalculate the Feature
DocumentObjectExecReturn *execute() override {
DocumentObjectExecReturn* execute() override
{
try {
bool handled = imp->execute();
if (!handled)
if (!handled) {
return FeatureT::execute();
}
}
catch (const Base::Exception& e) {
return new App::DocumentObjectExecReturn(e.what());
}
return DocumentObject::StdReturn;
}
const char* getViewProviderNameOverride() const override {
const char* getViewProviderNameOverride() const override
{
viewProviderName = imp->getViewProviderName();
if(!viewProviderName.empty())
if (!viewProviderName.empty()) {
return viewProviderName.c_str();
}
return FeatureT::getViewProviderNameOverride();
}
/// returns the type name of the ViewProvider
const char* getViewProviderName() const override {
const char* getViewProviderName() const override
{
return FeatureT::getViewProviderName();
}
App::DocumentObject *getSubObject(const char *subname, PyObject **pyObj,
Base::Matrix4D *mat, bool transform, int depth) const override
App::DocumentObject* getSubObject(const char* subname,
PyObject** pyObj,
Base::Matrix4D* mat,
bool transform,
int depth) const override
{
App::DocumentObject *ret = nullptr;
if(imp->getSubObject(ret,subname,pyObj,mat,transform,depth))
App::DocumentObject* ret = nullptr;
if (imp->getSubObject(ret, subname, pyObj, mat, transform, depth)) {
return ret;
return FeatureT::getSubObject(subname,pyObj,mat,transform,depth);
}
return FeatureT::getSubObject(subname, pyObj, mat, transform, depth);
}
std::vector<std::string> getSubObjects(int reason=0) const override {
std::vector<std::string> getSubObjects(int reason = 0) const override
{
std::vector<std::string> ret;
if(imp->getSubObjects(ret,reason))
if (imp->getSubObjects(ret, reason)) {
return ret;
}
return FeatureT::getSubObjects(reason);
}
App::DocumentObject *getLinkedObject(bool recurse,
Base::Matrix4D *mat, bool transform, int depth) const override
App::DocumentObject*
getLinkedObject(bool recurse, Base::Matrix4D* mat, bool transform, int depth) const override
{
App::DocumentObject *ret = nullptr;
if(imp->getLinkedObject(ret,recurse,mat,transform,depth))
App::DocumentObject* ret = nullptr;
if (imp->getLinkedObject(ret, recurse, mat, transform, depth)) {
return ret;
return FeatureT::getLinkedObject(recurse,mat,transform,depth);
}
return FeatureT::getLinkedObject(recurse, mat, transform, depth);
}
/// return true to activate tree view group object handling
bool hasChildElement() const override {
bool hasChildElement() const override
{
switch (imp->hasChildElement()) {
case FeaturePythonImp::Accepted:
return true;
case FeaturePythonImp::Rejected:
return false;
default:
return FeatureT::hasChildElement();
case FeaturePythonImp::Accepted:
return true;
case FeaturePythonImp::Rejected:
return false;
default:
return FeatureT::hasChildElement();
}
}
/// Get sub-element visibility
int isElementVisible(const char *element) const override {
int isElementVisible(const char* element) const override
{
int ret = imp->isElementVisible(element);
if(ret == -2)
if (ret == -2) {
return FeatureT::isElementVisible(element);
}
return ret;
}
/// Set sub-element visibility
int setElementVisible(const char *element, bool visible) override {
int ret = imp->setElementVisible(element,visible);
if(ret == -2)
return FeatureT::setElementVisible(element,visible);
int setElementVisible(const char* element, bool visible) override
{
int ret = imp->setElementVisible(element, visible);
if (ret == -2) {
return FeatureT::setElementVisible(element, visible);
}
return ret;
}
bool canLinkProperties() const override {
switch (imp->canLinkProperties()) {
case FeaturePythonImp::Accepted:
return true;
case FeaturePythonImp::Rejected:
return false;
default:
return FeatureT::canLinkProperties();
}
}
bool allowDuplicateLabel() const override {
switch (imp->allowDuplicateLabel()) {
case FeaturePythonImp::Accepted:
return true;
case FeaturePythonImp::Rejected:
return false;
default:
return FeatureT::allowDuplicateLabel();
}
}
bool redirectSubName(std::ostringstream &ss,
App::DocumentObject *topParent, App::DocumentObject *child) const override
bool canLinkProperties() const override
{
switch (imp->redirectSubName(ss,topParent,child)) {
case FeaturePythonImp::Accepted:
return true;
case FeaturePythonImp::Rejected:
return false;
default:
return FeatureT::redirectSubName(ss, topParent, child);
switch (imp->canLinkProperties()) {
case FeaturePythonImp::Accepted:
return true;
case FeaturePythonImp::Rejected:
return false;
default:
return FeatureT::canLinkProperties();
}
}
int canLoadPartial() const override {
bool allowDuplicateLabel() const override
{
switch (imp->allowDuplicateLabel()) {
case FeaturePythonImp::Accepted:
return true;
case FeaturePythonImp::Rejected:
return false;
default:
return FeatureT::allowDuplicateLabel();
}
}
bool redirectSubName(std::ostringstream& ss,
App::DocumentObject* topParent,
App::DocumentObject* child) const override
{
switch (imp->redirectSubName(ss, topParent, child)) {
case FeaturePythonImp::Accepted:
return true;
case FeaturePythonImp::Rejected:
return false;
default:
return FeatureT::redirectSubName(ss, topParent, child);
}
}
int canLoadPartial() const override
{
int ret = imp->canLoadPartial();
if(ret>=0)
if (ret >= 0) {
return ret;
}
return FeatureT::canLoadPartial();
}
void editProperty(const char *propName) override {
if (!imp->editProperty(propName))
void editProperty(const char* propName) override
{
if (!imp->editProperty(propName)) {
FeatureT::editProperty(propName);
}
}
PyObject *getPyObject() override {
PyObject* getPyObject() override
{
if (FeatureT::PythonObject.is(Py::_None())) {
// ref counter is set to 1
FeatureT::PythonObject = Py::Object(imp->getPyObject(),true);
FeatureT::PythonObject = Py::Object(imp->getPyObject(), true);
}
return Py::new_reference_to(FeatureT::PythonObject);
}
void setPyObject(PyObject *obj) override {
if (obj)
void setPyObject(PyObject* obj) override
{
if (obj) {
FeatureT::PythonObject = obj;
else
}
else {
FeatureT::PythonObject = Py::None();
}
}
protected:
void onBeforeChange(const Property* prop) override {
void onBeforeChange(const Property* prop) override
{
FeatureT::onBeforeChange(prop);
imp->onBeforeChange(prop);
}
void onBeforeChangeLabel(std::string &newLabel) override{
if(!imp->onBeforeChangeLabel(newLabel))
void onBeforeChangeLabel(std::string& newLabel) override
{
if (!imp->onBeforeChangeLabel(newLabel)) {
FeatureT::onBeforeChangeLabel(newLabel);
}
}
void onChanged(const Property* prop) override {
if(prop == &Proxy)
void onChanged(const Property* prop) override
{
if (prop == &Proxy) {
imp->init(Proxy.getValue().ptr());
}
imp->onChanged(prop);
FeatureT::onChanged(prop);
}
void onDocumentRestored() override {
void onDocumentRestored() override
{
imp->onDocumentRestored();
FeatureT::onDocumentRestored();
}
void unsetupObject() override {
void unsetupObject() override
{
imp->unsetupObject();
FeatureT::unsetupObject();
}
@@ -350,8 +393,8 @@ protected:
public:
FeaturePythonT(const FeaturePythonT&) = delete;
FeaturePythonT(FeaturePythonT&&) = delete;
FeaturePythonT& operator= (const FeaturePythonT&) = delete;
FeaturePythonT& operator= (FeaturePythonT&&) = delete;
FeaturePythonT& operator=(const FeaturePythonT&) = delete;
FeaturePythonT& operator=(FeaturePythonT&&) = delete;
private:
FeaturePythonImp* imp;
@@ -360,9 +403,9 @@ private:
};
// Special Feature-Python classes
using FeaturePython = FeaturePythonT<DocumentObject>;
using GeometryPython = FeaturePythonT<GeoFeature >;
using FeaturePython = FeaturePythonT<DocumentObject>;
using GeometryPython = FeaturePythonT<GeoFeature>;
} //namespace App
} // namespace App
#endif // APP_FEATUREPYTHON_H
#endif // APP_FEATUREPYTHON_H

View File

@@ -1,25 +1,25 @@
#***************************************************************************
#* Copyright (c) 2002 Jürgen Riegel <juergen.riegel@web.de> *
#* *
#* This file is part of the FreeCAD CAx development system. *
#* *
#* This program is free software; you can redistribute it and/or modify *
#* it under the terms of the GNU Lesser General Public License (LGPL) *
#* as published by the Free Software Foundation; either version 2 of *
#* the License, or (at your option) any later version. *
#* for detail see the LICENCE text file. *
#* *
#* FreeCAD 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 Lesser General Public License for more details. *
#* *
#* You should have received a copy of the GNU Library General Public *
#* License along with FreeCAD; if not, write to the Free Software *
#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
#* USA *
#* *
#***************************************************************************/
# ***************************************************************************
# * Copyright (c) 2002 Jürgen Riegel <juergen.riegel@web.de> *
# * *
# * This file is part of the FreeCAD CAx development system. *
# * *
# * This program is free software; you can redistribute it and/or modify *
# * it under the terms of the GNU Lesser General Public License (LGPL) *
# * as published by the Free Software Foundation; either version 2 of *
# * the License, or (at your option) any later version. *
# * for detail see the LICENCE text file. *
# * *
# * FreeCAD 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 Lesser General Public License for more details. *
# * *
# * You should have received a copy of the GNU Library General Public *
# * License along with FreeCAD; if not, write to the Free Software *
# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
# * USA *
# * *
# ***************************************************************************/
# FreeCAD test module
#
@@ -27,8 +27,7 @@
# (if existing) the test function of the modules
Log ("FreeCAD test running...\n\n")
Log("FreeCAD test running...\n\n")
import sys
@@ -39,6 +38,6 @@ testCase = FreeCAD.ConfigGet("TestCase")
testResult = TestApp.TestText(testCase)
Log ("FreeCAD test done\n")
Log("FreeCAD test done\n")
sys.exit(0 if testResult.wasSuccessful() else 1)

View File

@@ -52,24 +52,28 @@ GeoFeatureGroupExtension::GeoFeatureGroupExtension()
GeoFeatureGroupExtension::~GeoFeatureGroupExtension() = default;
void GeoFeatureGroupExtension::initExtension(ExtensionContainer* obj) {
void GeoFeatureGroupExtension::initExtension(ExtensionContainer* obj)
{
if(!obj->isDerivedFrom(App::GeoFeature::getClassTypeId()))
if (!obj->isDerivedFrom(App::GeoFeature::getClassTypeId())) {
throw Base::RuntimeError("GeoFeatureGroupExtension can only be applied to GeoFeatures");
}
App::GroupExtension::initExtension(obj);
}
PropertyPlacement& GeoFeatureGroupExtension::placement() {
PropertyPlacement& GeoFeatureGroupExtension::placement()
{
if(!getExtendedContainer())
if (!getExtendedContainer()) {
throw Base::RuntimeError("GeoFeatureGroupExtension was not applied to GeoFeature");
}
return static_cast<App::GeoFeature*>(getExtendedContainer())->Placement;
}
void GeoFeatureGroupExtension::transformPlacement(const Base::Placement &transform)
void GeoFeatureGroupExtension::transformPlacement(const Base::Placement& transform)
{
// NOTE: Keep in sync with APP::GeoFeature
Base::Placement plm = this->placement().getValue();
@@ -79,25 +83,29 @@ void GeoFeatureGroupExtension::transformPlacement(const Base::Placement &transfo
DocumentObject* GeoFeatureGroupExtension::getGroupOfObject(const DocumentObject* obj)
{
if(!obj)
if (!obj) {
return nullptr;
}
//we will find origins, but not origin features
if(obj->isDerivedFrom(App::OriginFeature::getClassTypeId()))
// we will find origins, but not origin features
if (obj->isDerivedFrom(App::OriginFeature::getClassTypeId())) {
return OriginGroupExtension::getGroupOfObject(obj);
}
//compared to GroupExtension we do return here all GeoFeatureGroups including all extensions derived from it
//like OriginGroup. That is needed as we use this function to get all local coordinate systems. Also there
//is no reason to distinguish between GeoFeatuerGroups, there is only between group/geofeaturegroup
// compared to GroupExtension we do return here all GeoFeatureGroups including all extensions
// derived from it like OriginGroup. That is needed as we use this function to get all local
// coordinate systems. Also there is no reason to distinguish between GeoFeatuerGroups, there is
// only between group/geofeaturegroup
auto list = obj->getInList();
for (auto inObj : list) {
//There is a chance that a derived geofeaturegroup links with a local link and hence is not
//the parent group even though it links to the object. We use hasObject as one and only truth
//if it has the object within the group
// There is a chance that a derived geofeaturegroup links with a local link and hence is not
// the parent group even though it links to the object. We use hasObject as one and only
// truth if it has the object within the group
auto group = inObj->getExtensionByType<GeoFeatureGroupExtension>(true);
if(group && group->hasObject(obj))
if (group && group->hasObject(obj)) {
return inObj;
}
}
return nullptr;
@@ -115,8 +123,9 @@ Base::Placement GeoFeatureGroupExtension::globalGroupPlacement()
}
Base::Placement GeoFeatureGroupExtension::recursiveGroupPlacement(GeoFeatureGroupExtension* group,
std::unordered_set<GeoFeatureGroupExtension*>& history)
Base::Placement GeoFeatureGroupExtension::recursiveGroupPlacement(
GeoFeatureGroupExtension* group,
std::unordered_set<GeoFeatureGroupExtension*>& history)
{
history.insert(this);
@@ -135,25 +144,29 @@ Base::Placement GeoFeatureGroupExtension::recursiveGroupPlacement(GeoFeatureGrou
return group->placement().getValue();
}
std::vector<DocumentObject*> GeoFeatureGroupExtension::addObjects(std::vector<App::DocumentObject*> objects) {
std::vector<DocumentObject*>
GeoFeatureGroupExtension::addObjects(std::vector<App::DocumentObject*> objects)
{
std::vector<DocumentObject*> grp = Group.getValues();
std::vector<DocumentObject*> ret;
for(auto object : objects) {
for (auto object : objects) {
if(!allowObject(object))
if (!allowObject(object)) {
continue;
}
//cross CoordinateSystem links are not allowed, so we need to move the whole link group
// cross CoordinateSystem links are not allowed, so we need to move the whole link group
std::vector<App::DocumentObject*> links = getCSRelevantLinks(object);
links.push_back(object);
for( auto obj : links) {
//only one geofeaturegroup per object.
auto *group = App::GeoFeatureGroupExtension::getGroupOfObject(obj);
if(group && group != getExtendedObject())
for (auto obj : links) {
// only one geofeaturegroup per object.
auto* group = App::GeoFeatureGroupExtension::getGroupOfObject(obj);
if (group && group != getExtendedObject()) {
group->getExtensionByType<App::GroupExtension>()->removeObject(obj);
}
if (!hasObject(obj)) {
grp.push_back(obj);
@@ -166,61 +179,68 @@ std::vector<DocumentObject*> GeoFeatureGroupExtension::addObjects(std::vector<Ap
return ret;
}
std::vector<DocumentObject*> GeoFeatureGroupExtension::removeObjects(std::vector<App::DocumentObject*> objects) {
std::vector<DocumentObject*>
GeoFeatureGroupExtension::removeObjects(std::vector<App::DocumentObject*> objects)
{
std::vector<DocumentObject*> removed;
std::vector<DocumentObject*> grp = Group.getValues();
for(auto object : objects) {
//cross CoordinateSystem links are not allowed, so we need to remove the whole link group
std::vector< DocumentObject* > links = getCSRelevantLinks(object);
for (auto object : objects) {
// cross CoordinateSystem links are not allowed, so we need to remove the whole link group
std::vector<DocumentObject*> links = getCSRelevantLinks(object);
links.push_back(object);
//remove all links out of group
for(auto link : links) {
// remove all links out of group
for (auto link : links) {
auto end = std::remove(grp.begin(), grp.end(), link);
if(end != grp.end()) {
if (end != grp.end()) {
grp.erase(end, grp.end());
removed.push_back(link);
}
}
}
if(!removed.empty())
if (!removed.empty()) {
Group.setValues(grp);
}
return removed;
}
void GeoFeatureGroupExtension::extensionOnChanged(const Property* p) {
void GeoFeatureGroupExtension::extensionOnChanged(const Property* p)
{
//objects are only allowed in a single GeoFeatureGroup
if(p == &Group && !Group.testStatus(Property::User3)) {
// objects are only allowed in a single GeoFeatureGroup
if (p == &Group && !Group.testStatus(Property::User3)) {
if((!getExtendedObject()->isRestoring()
|| getExtendedObject()->getDocument()->testStatus(Document::Importing))
if ((!getExtendedObject()->isRestoring()
|| getExtendedObject()->getDocument()->testStatus(Document::Importing))
&& !getExtendedObject()->getDocument()->isPerformingTransaction()) {
bool error = false;
auto corrected = Group.getValues();
for(auto obj : Group.getValues()) {
for (auto obj : Group.getValues()) {
//we have already set the obj into the group, so in a case of multiple groups getGroupOfObject
//would return anyone of it and hence it is possible that we miss an error. We need a custom check
// we have already set the obj into the group, so in a case of multiple groups
// getGroupOfObject would return anyone of it and hence it is possible that we miss
// an error. We need a custom check
auto list = obj->getInList();
for (auto in : list) {
if(in == getExtendedObject())
if (in == getExtendedObject()) {
continue;
}
auto parent = in->getExtensionByType<GeoFeatureGroupExtension>(true);
if(parent && parent->hasObject(obj)) {
if (parent && parent->hasObject(obj)) {
error = true;
corrected.erase(std::remove(corrected.begin(), corrected.end(), obj), corrected.end());
corrected.erase(std::remove(corrected.begin(), corrected.end(), obj),
corrected.end());
}
}
}
//if an error was found we need to correct the values and inform the user
if(error) {
// if an error was found we need to correct the values and inform the user
if (error) {
Base::ObjectStatusLocker<Property::Status, Property> guard(Property::User3, &Group);
Group.setValues(corrected);
throw Base::RuntimeError("Object can only be in a single GeoFeatureGroup");
@@ -232,99 +252,119 @@ void GeoFeatureGroupExtension::extensionOnChanged(const Property* p) {
}
std::vector< DocumentObject* > GeoFeatureGroupExtension::getScopedObjectsFromLinks(const DocumentObject* obj, LinkScope scope) {
std::vector<DocumentObject*>
GeoFeatureGroupExtension::getScopedObjectsFromLinks(const DocumentObject* obj, LinkScope scope)
{
if(!obj)
if (!obj) {
return {};
}
//we get all linked objects. We can't use outList() as this includes the links from expressions
std::vector< App::DocumentObject* > result;
// we get all linked objects. We can't use outList() as this includes the links from expressions
std::vector<App::DocumentObject*> result;
std::vector<App::Property*> list;
obj->getPropertyList(list);
for(App::Property* prop : list) {
for (App::Property* prop : list) {
auto vec = getScopedObjectsFromLink(prop, scope);
result.insert(result.end(), vec.begin(), vec.end());
}
//clear all null objects and duplicates
// clear all null objects and duplicates
std::sort(result.begin(), result.end());
result.erase(std::unique(result.begin(), result.end()), result.end());
return result;
}
std::vector< DocumentObject* > GeoFeatureGroupExtension::getScopedObjectsFromLink(App::Property* prop, LinkScope scope) {
std::vector<DocumentObject*> GeoFeatureGroupExtension::getScopedObjectsFromLink(App::Property* prop,
LinkScope scope)
{
if(!prop)
if (!prop) {
return {};
}
std::vector< App::DocumentObject* > result;
std::vector<App::DocumentObject*> result;
auto link = Base::freecad_dynamic_cast<PropertyLinkBase>(prop);
if(link && link->getScope()==scope)
if (link && link->getScope() == scope) {
link->getLinks(result);
}
return result;
}
void GeoFeatureGroupExtension::getCSOutList(const App::DocumentObject* obj,
std::vector< DocumentObject* >& vec) {
std::vector<DocumentObject*>& vec)
{
if(!obj)
if (!obj) {
return;
}
//we get all relevant linked objects. We can't use outList() as this includes the links from expressions,
//also we only want links with scope Local
// we get all relevant linked objects. We can't use outList() as this includes the links from
// expressions, also we only want links with scope Local
auto result = getScopedObjectsFromLinks(obj, LinkScope::Local);
//we remove all links to origin features and origins, they belong to a CS too and can't be moved
result.erase(std::remove_if(result.begin(), result.end(), [](App::DocumentObject* obj)->bool {
return (obj->isDerivedFrom(App::OriginFeature::getClassTypeId()) ||
obj->isDerivedFrom(App::Origin::getClassTypeId()));
}), result.end());
// we remove all links to origin features and origins, they belong to a CS too and can't be
// moved
result.erase(std::remove_if(result.begin(),
result.end(),
[](App::DocumentObject* obj) -> bool {
return (obj->isDerivedFrom(App::OriginFeature::getClassTypeId())
|| obj->isDerivedFrom(App::Origin::getClassTypeId()));
}),
result.end());
vec.insert(vec.end(), result.begin(), result.end());
//post process the vector
// post process the vector
std::sort(vec.begin(), vec.end());
vec.erase(std::unique(vec.begin(), vec.end()), vec.end());
}
void GeoFeatureGroupExtension::getCSInList(const DocumentObject* obj,
std::vector< DocumentObject* >& vec) {
std::vector<DocumentObject*>& vec)
{
if(!obj)
if (!obj) {
return;
//search the inlist for objects that have non-expression links to us
for(App::DocumentObject* parent : obj->getInList()) {
//not interested in other groups (and here we mean all groups, normal ones and geofeaturegroup)
if(parent->hasExtension(App::GroupExtension::getExtensionClassTypeId()))
continue;
//check if the link is real Local scope one or if it is a expression one (could also be both, so it is not
//enough to check the expressions)
auto res = getScopedObjectsFromLinks(parent, LinkScope::Local);
if(std::find(res.begin(), res.end(), obj) != res.end())
vec.push_back(parent);
}
//clear all duplicates
// search the inlist for objects that have non-expression links to us
for (App::DocumentObject* parent : obj->getInList()) {
// not interested in other groups (and here we mean all groups, normal ones and
// geofeaturegroup)
if (parent->hasExtension(App::GroupExtension::getExtensionClassTypeId())) {
continue;
}
// check if the link is real Local scope one or if it is a expression one (could also be
// both, so it is not enough to check the expressions)
auto res = getScopedObjectsFromLinks(parent, LinkScope::Local);
if (std::find(res.begin(), res.end(), obj) != res.end()) {
vec.push_back(parent);
}
}
// clear all duplicates
std::sort(vec.begin(), vec.end());
vec.erase(std::unique(vec.begin(), vec.end()), vec.end());
}
std::vector< DocumentObject* > GeoFeatureGroupExtension::getCSRelevantLinks(const DocumentObject* obj) {
std::vector<DocumentObject*> GeoFeatureGroupExtension::getCSRelevantLinks(const DocumentObject* obj)
{
if(!obj)
if (!obj) {
return {};
}
//get all out links
// get all out links
std::vector<DocumentObject*> vec;
recursiveCSRelevantLinks(obj, vec);
//post process the list after we added many things
// post process the list after we added many things
std::sort(vec.begin(), vec.end());
vec.erase(std::unique(vec.begin(), vec.end()), vec.end());
vec.erase(std::remove(vec.begin(), vec.end(), obj), vec.end());
@@ -333,53 +373,63 @@ std::vector< DocumentObject* > GeoFeatureGroupExtension::getCSRelevantLinks(cons
}
void GeoFeatureGroupExtension::recursiveCSRelevantLinks(const DocumentObject* obj,
std::vector< DocumentObject* >& vec) {
std::vector<DocumentObject*>& vec)
{
if(!obj)
if (!obj) {
return;
}
std::vector< DocumentObject* > links;
std::vector<DocumentObject*> links;
getCSOutList(obj, links);
getCSInList(obj, links);
//go on traversing the graph in all directions!
for(auto o : links) {
if(!o || o == obj || std::find(vec.begin(), vec.end(), o) != vec.end())
// go on traversing the graph in all directions!
for (auto o : links) {
if (!o || o == obj || std::find(vec.begin(), vec.end(), o) != vec.end()) {
continue;
}
vec.push_back(o);
recursiveCSRelevantLinks(o, vec);
}
}
bool GeoFeatureGroupExtension::extensionGetSubObject(DocumentObject *&ret, const char *subname,
PyObject **pyObj, Base::Matrix4D *mat, bool transform, int depth) const
bool GeoFeatureGroupExtension::extensionGetSubObject(DocumentObject*& ret,
const char* subname,
PyObject** pyObj,
Base::Matrix4D* mat,
bool transform,
int depth) const
{
ret = nullptr;
const char *dot;
if(!subname || *subname==0) {
const char* dot;
if (!subname || *subname == 0) {
auto obj = dynamic_cast<const DocumentObject*>(getExtendedContainer());
ret = const_cast<DocumentObject*>(obj);
if(mat && transform)
if (mat && transform) {
*mat *= const_cast<GeoFeatureGroupExtension*>(this)->placement().getValue().toMatrix();
}else if((dot=strchr(subname,'.'))) {
if(subname[0]!='$')
ret = Group.findUsingMap(std::string(subname,dot));
else{
std::string name = std::string(subname+1,dot);
for(auto child : Group.getValues()) {
if(name == child->Label.getStrValue()){
}
}
else if ((dot = strchr(subname, '.'))) {
if (subname[0] != '$') {
ret = Group.findUsingMap(std::string(subname, dot));
}
else {
std::string name = std::string(subname + 1, dot);
for (auto child : Group.getValues()) {
if (name == child->Label.getStrValue()) {
ret = child;
break;
}
}
}
if(ret) {
if(dot) ++dot;
if(dot && *dot
&& !ret->hasExtension(App::LinkBaseExtension::getExtensionClassTypeId())
&& !ret->hasExtension(App::GeoFeatureGroupExtension::getExtensionClassTypeId()))
{
if (ret) {
if (dot) {
++dot;
}
if (dot && *dot && !ret->hasExtension(App::LinkBaseExtension::getExtensionClassTypeId())
&& !ret->hasExtension(App::GeoFeatureGroupExtension::getExtensionClassTypeId())) {
// Consider this
// Body
// | -- Pad
@@ -391,33 +441,37 @@ bool GeoFeatureGroupExtension::extensionGetSubObject(DocumentObject *&ret, const
// getSubObject(Pad,"Sketch.") as this will transform Sketch
// using Pad's placement.
//
const char *next = strchr(dot,'.');
if(next) {
App::DocumentObject *nret=nullptr;
extensionGetSubObject(nret,dot,pyObj,mat,transform,depth+1);
if(nret) {
const char* next = strchr(dot, '.');
if (next) {
App::DocumentObject* nret = nullptr;
extensionGetSubObject(nret, dot, pyObj, mat, transform, depth + 1);
if (nret) {
ret = nret;
return true;
}
}
}
if(mat && transform)
*mat *= const_cast<GeoFeatureGroupExtension*>(this)->placement().getValue().toMatrix();
ret = ret->getSubObject(dot?dot:"",pyObj,mat,true,depth+1);
if (mat && transform) {
*mat *=
const_cast<GeoFeatureGroupExtension*>(this)->placement().getValue().toMatrix();
}
ret = ret->getSubObject(dot ? dot : "", pyObj, mat, true, depth + 1);
}
}
return true;
}
bool GeoFeatureGroupExtension::areLinksValid(const DocumentObject* obj) {
bool GeoFeatureGroupExtension::areLinksValid(const DocumentObject* obj)
{
if(!obj)
if (!obj) {
return true;
}
std::vector<App::Property*> list;
obj->getPropertyList(list);
for(App::Property* prop : list) {
if(!isLinkValid(prop)) {
for (App::Property* prop : list) {
if (!isLinkValid(prop)) {
return false;
}
}
@@ -425,65 +479,79 @@ bool GeoFeatureGroupExtension::areLinksValid(const DocumentObject* obj) {
return true;
}
bool GeoFeatureGroupExtension::isLinkValid(App::Property* prop) {
bool GeoFeatureGroupExtension::isLinkValid(App::Property* prop)
{
if(!prop)
if (!prop) {
return true;
}
//get the object that holds the property
if(!prop->getContainer()->isDerivedFrom(App::DocumentObject::getClassTypeId()))
return true; //this link comes not from a document object, scopes are meaningless
// get the object that holds the property
if (!prop->getContainer()->isDerivedFrom(App::DocumentObject::getClassTypeId())) {
return true; // this link comes not from a document object, scopes are meaningless
}
auto obj = static_cast<App::DocumentObject*>(prop->getContainer());
//no cross CS link for local links.
// no cross CS link for local links.
auto result = getScopedObjectsFromLink(prop, LinkScope::Local);
auto group = getGroupOfObject(obj);
for(auto link : result) {
if(getGroupOfObject(link) != group)
for (auto link : result) {
if (getGroupOfObject(link) != group) {
return false;
}
}
//for links with scope SubGroup we need to check if all features are part of subgroups
if(obj->hasExtension(App::GeoFeatureGroupExtension::getExtensionClassTypeId())) {
// for links with scope SubGroup we need to check if all features are part of subgroups
if (obj->hasExtension(App::GeoFeatureGroupExtension::getExtensionClassTypeId())) {
result = getScopedObjectsFromLink(prop, LinkScope::Child);
auto groupExt = obj->getExtensionByType<App::GeoFeatureGroupExtension>();
for(auto link : result) {
if(!groupExt->hasObject(link, true))
for (auto link : result) {
if (!groupExt->hasObject(link, true)) {
return false;
}
}
}
return true;
}
void GeoFeatureGroupExtension::getInvalidLinkObjects(const DocumentObject* obj, std::vector< DocumentObject* >& vec) {
void GeoFeatureGroupExtension::getInvalidLinkObjects(const DocumentObject* obj,
std::vector<DocumentObject*>& vec)
{
if(!obj)
if (!obj) {
return;
//no cross CS link for local links.
auto result = getScopedObjectsFromLinks(obj, LinkScope::Local);
auto group = obj->hasExtension(App::GeoFeatureGroupExtension::getExtensionClassTypeId()) ? obj : getGroupOfObject(obj);
for(auto link : result) {
if(getGroupOfObject(link) != group)
vec.push_back(link);
}
//for links with scope SubGroup we need to check if all features are part of subgroups
if(group) {
// no cross CS link for local links.
auto result = getScopedObjectsFromLinks(obj, LinkScope::Local);
auto group = obj->hasExtension(App::GeoFeatureGroupExtension::getExtensionClassTypeId())
? obj
: getGroupOfObject(obj);
for (auto link : result) {
if (getGroupOfObject(link) != group) {
vec.push_back(link);
}
}
// for links with scope SubGroup we need to check if all features are part of subgroups
if (group) {
result = getScopedObjectsFromLinks(obj, LinkScope::Child);
auto groupExt = group->getExtensionByType<App::GeoFeatureGroupExtension>();
for(auto link : result) {
if(!groupExt->hasObject(link, true))
for (auto link : result) {
if (!groupExt->hasObject(link, true)) {
vec.push_back(link);
}
}
}
}
bool GeoFeatureGroupExtension::extensionGetSubObjects(std::vector<std::string> &ret, int) const {
for(auto obj : Group.getValues()) {
if(obj && obj->isAttachedToDocument() && !obj->testStatus(ObjectStatus::GeoExcluded))
ret.push_back(std::string(obj->getNameInDocument())+'.');
bool GeoFeatureGroupExtension::extensionGetSubObjects(std::vector<std::string>& ret, int) const
{
for (auto obj : Group.getValues()) {
if (obj && obj->isAttachedToDocument() && !obj->testStatus(ObjectStatus::GeoExcluded)) {
ret.push_back(std::string(obj->getNameInDocument()) + '.');
}
}
return true;
}
@@ -491,9 +559,11 @@ bool GeoFeatureGroupExtension::extensionGetSubObjects(std::vector<std::string> &
// Python feature ---------------------------------------------------------
namespace App {
EXTENSION_PROPERTY_SOURCE_TEMPLATE(App::GeoFeatureGroupExtensionPython, App::GeoFeatureGroupExtension)
namespace App
{
EXTENSION_PROPERTY_SOURCE_TEMPLATE(App::GeoFeatureGroupExtensionPython,
App::GeoFeatureGroupExtension)
// explicit template instantiation
template class AppExport ExtensionPythonT<GroupExtensionPythonT<GeoFeatureGroupExtension>>;
}
} // namespace App

View File

@@ -35,21 +35,23 @@ namespace App
{
/**
* @brief The base class for placeable group of DocumentObjects. It represents a local coordnate system
* @brief The base class for placeable group of DocumentObjects. It represents a local coordnate
* system
*
* This class is the FreeCAD way of representing local coordinate systems. It groups its children beneath
* it and transforms them all with the GeoFeatureGroup placement. A few important properties:
* - Every child that belongs to the CS must be in the Group property. Even if a sketch is part of a pad,
* it must be in the Group property of the same GeoFeatureGroup as pad. This also holds for normal
* GroupExtensions. They can be added to a GeoFeatureGroup, but all objects that the group holds must
* also be added to the GeoFeatureGroup
* This class is the FreeCAD way of representing local coordinate systems. It groups its children
* beneath it and transforms them all with the GeoFeatureGroup placement. A few important
* properties:
* - Every child that belongs to the CS must be in the Group property. Even if a sketch is part of a
* pad, it must be in the Group property of the same GeoFeatureGroup as pad. This also holds for
* normal GroupExtensions. They can be added to a GeoFeatureGroup, but all objects that the group
* holds must also be added to the GeoFeatureGroup
* - Objects can be only in a single GeoFeatureGroup. It is not allowed to have a document object in
* multiple GeoFeatureGroups
* - PropertyLinks between different GeoFeatureGroups are forbidden. There are special link properties
* that allow such cross-CS links.
* - PropertyLinks between different GeoFeatureGroups are forbidden. There are special link
* properties that allow such cross-CS links.
* - Expressions can cross GeoFeatureGroup borders
*/
class AppExport GeoFeatureGroupExtension : public App::GroupExtension
class AppExport GeoFeatureGroupExtension: public App::GroupExtension
{
using inherited = App::GroupExtension;
EXTENSION_PROPERTY_HEADER_WITH_OVERRIDE(App::GeoFeatureGroupExtension);
@@ -65,7 +67,7 @@ public:
* features.
* @param transform (input).
*/
virtual void transformPlacement(const Base::Placement &transform);
virtual void transformPlacement(const Base::Placement& transform);
/// Constructor
GeoFeatureGroupExtension();
@@ -84,65 +86,77 @@ public:
* @brief Calculates the global placement of this group
*
* The returned placement describes the transformation from the global reference coordinate
* system to the local coordinate system of this geo feature group. If this group has a no parent
* GeoFeatureGroup the returned placement is the one of this group. For multiple stacked
* system to the local coordinate system of this geo feature group. If this group has a no
* parent GeoFeatureGroup the returned placement is the one of this group. For multiple stacked
* GeoFeatureGroups the returned Placement is the combination of all parent placements including
* the one of this group.
* @return Base::Placement The transformation from global reference system to the groups local system
* @return Base::Placement The transformation from global reference system to the groups local
* system
*/
Base::Placement globalGroupPlacement();
/// Returns true if the given DocumentObject is DocumentObjectGroup but not GeoFeatureGroup
static bool isNonGeoGroup(const DocumentObject* obj) {
return obj->hasExtension(GroupExtension::getExtensionClassTypeId()) &&
!obj->hasExtension(GeoFeatureGroupExtension::getExtensionClassTypeId());
static bool isNonGeoGroup(const DocumentObject* obj)
{
return obj->hasExtension(GroupExtension::getExtensionClassTypeId())
&& !obj->hasExtension(GeoFeatureGroupExtension::getExtensionClassTypeId());
}
bool extensionGetSubObject(DocumentObject *&ret, const char *subname, PyObject **pyObj,
Base::Matrix4D *mat, bool transform, int depth) const override;
bool extensionGetSubObject(DocumentObject*& ret,
const char* subname,
PyObject** pyObj,
Base::Matrix4D* mat,
bool transform,
int depth) const override;
bool extensionGetSubObjects(std::vector<std::string> &ret, int reason) const override;
bool extensionGetSubObjects(std::vector<std::string>& ret, int reason) const override;
std::vector< DocumentObject* > addObjects(std::vector< DocumentObject* > obj) override;
std::vector< DocumentObject* > removeObjects(std::vector< DocumentObject* > obj) override;
std::vector<DocumentObject*> addObjects(std::vector<DocumentObject*> obj) override;
std::vector<DocumentObject*> removeObjects(std::vector<DocumentObject*> obj) override;
/// Collects all links that are relevant for the coordinate system, meaning all recursive links to
/// obj and from obj excluding expressions and stopping the recursion at other geofeaturegroups.
/// The result is the combination of CSOutList and CSInList.
/// Collects all links that are relevant for the coordinate system, meaning all recursive links
/// to obj and from obj excluding expressions and stopping the recursion at other
/// geofeaturegroups. The result is the combination of CSOutList and CSInList.
static std::vector<App::DocumentObject*> getCSRelevantLinks(const App::DocumentObject* obj);
/// Checks if the links of the given object comply with all GeoFeatureGroup requirements, that means
/// if normal links are only within the parent GeoFeatureGroup.
/// Checks if the links of the given object comply with all GeoFeatureGroup requirements, that
/// means if normal links are only within the parent GeoFeatureGroup.
static bool areLinksValid(const App::DocumentObject* obj);
/// Checks if the given link complies with all GeoFeatureGroup requirements, that means
/// if normal links are only within the parent GeoFeatureGroup.
static bool isLinkValid(App::Property* link);
//Returns all objects that are wrongly linked from this object, meaning which are out of scope of the
//links of obj
static void getInvalidLinkObjects(const App::DocumentObject* obj, std::vector<App::DocumentObject*>& vec);
// Returns all objects that are wrongly linked from this object, meaning which are out of scope
// of the links of obj
static void getInvalidLinkObjects(const App::DocumentObject* obj,
std::vector<App::DocumentObject*>& vec);
private:
Base::Placement recursiveGroupPlacement(GeoFeatureGroupExtension* group, std::unordered_set<GeoFeatureGroupExtension*>& history);
static std::vector<App::DocumentObject*> getScopedObjectsFromLinks(const App::DocumentObject*, LinkScope scope = LinkScope::Local);
static std::vector<App::DocumentObject*> getScopedObjectsFromLink(App::Property*, LinkScope scope = LinkScope::Local);
Base::Placement recursiveGroupPlacement(GeoFeatureGroupExtension* group,
std::unordered_set<GeoFeatureGroupExtension*>& history);
static std::vector<App::DocumentObject*>
getScopedObjectsFromLinks(const App::DocumentObject*, LinkScope scope = LinkScope::Local);
static std::vector<App::DocumentObject*>
getScopedObjectsFromLink(App::Property*, LinkScope scope = LinkScope::Local);
/// Collects GeoFeatureGroup relevant objects that are linked from the given one. That means all linked objects
/// except GeoFeatureGroups. Expressions links are ignored. Only local scope links are considered. There is no
/// recursion. An exception is thrown when there are dependency loops.
static void getCSOutList(const App::DocumentObject* obj, std::vector<App::DocumentObject*>& vec);
/// Collects GeoFeatureGroup relevant objects that are linked from the given one. That means all
/// linked objects except GeoFeatureGroups. Expressions links are ignored. Only local scope
/// links are considered. There is no recursion. An exception is thrown when there are
/// dependency loops.
static void getCSOutList(const App::DocumentObject* obj,
std::vector<App::DocumentObject*>& vec);
/// Collects GeoFeatureGroup relevant objects that link to the given one. That means all objects
/// except GeoFeatureGroups. Expression links are ignored. Only local scope links are relevant, and
/// there is no recursion. An exception is thrown when there are dependency loops.
/// except GeoFeatureGroups. Expression links are ignored. Only local scope links are relevant,
/// and there is no recursion. An exception is thrown when there are dependency loops.
static void getCSInList(const App::DocumentObject* obj, std::vector<App::DocumentObject*>& vec);
static void recursiveCSRelevantLinks(const App::DocumentObject* obj,
std::vector<App::DocumentObject*>& vec);
};
using GeoFeatureGroupExtensionPython = ExtensionPythonT<GroupExtensionPythonT<GeoFeatureGroupExtension>>;
using GeoFeatureGroupExtensionPython =
ExtensionPythonT<GroupExtensionPythonT<GeoFeatureGroupExtension>>;
} //namespace App
} // namespace App
#endif // APP_GeoFeatureGroup_H
#endif // APP_GeoFeatureGroup_H

View File

@@ -36,7 +36,7 @@ std::string GeoFeatureGroupExtensionPy::representation() const
return {"<GeoFeatureGroup object>"};
}
PyObject *GeoFeatureGroupExtensionPy::getCustomAttributes(const char* /*attr*/) const
PyObject* GeoFeatureGroupExtensionPy::getCustomAttributes(const char* /*attr*/) const
{
return nullptr;
}
@@ -45,5 +45,3 @@ int GeoFeatureGroupExtensionPy::setCustomAttributes(const char* /*attr*/, PyObje
{
return 0;
}

View File

@@ -37,16 +37,18 @@ std::string GeoFeaturePy::representation() const
return {"<GeoFeature object>"};
}
PyObject* GeoFeaturePy::getPaths(PyObject * /*args*/)
PyObject* GeoFeaturePy::getPaths(PyObject* /*args*/)
{
PyErr_SetString(PyExc_NotImplementedError, "Not yet implemented");
return nullptr;
}
PyObject* GeoFeaturePy::getGlobalPlacement(PyObject * args) {
PyObject* GeoFeaturePy::getGlobalPlacement(PyObject* args)
{
if (!PyArg_ParseTuple(args, ""))
if (!PyArg_ParseTuple(args, "")) {
return nullptr;
}
try {
Base::Placement p = static_cast<GeoFeature*>(getDocumentObjectPtr())->globalPlacement();
@@ -57,7 +59,8 @@ PyObject* GeoFeaturePy::getGlobalPlacement(PyObject * args) {
}
}
PyObject* GeoFeaturePy::getGlobalPlacementOf(PyObject * args) {
PyObject* GeoFeaturePy::getGlobalPlacementOf(PyObject* args)
{
PyObject* pyTargetObj {nullptr};
PyObject* pyRootObj {nullptr};
@@ -78,10 +81,11 @@ PyObject* GeoFeaturePy::getGlobalPlacementOf(PyObject * args) {
}
}
PyObject* GeoFeaturePy::getPropertyNameOfGeometry(PyObject * args)
PyObject* GeoFeaturePy::getPropertyNameOfGeometry(PyObject* args)
{
if (!PyArg_ParseTuple(args, ""))
if (!PyArg_ParseTuple(args, "")) {
return nullptr;
}
GeoFeature* object = this->getGeoFeaturePtr();
const PropertyComplexGeoData* prop = object->getPropertyOfGeometry();
@@ -92,10 +96,11 @@ PyObject* GeoFeaturePy::getPropertyNameOfGeometry(PyObject * args)
return Py::new_reference_to(Py::None());
}
PyObject* GeoFeaturePy::getPropertyOfGeometry(PyObject * args)
PyObject* GeoFeaturePy::getPropertyOfGeometry(PyObject* args)
{
if (!PyArg_ParseTuple(args, ""))
if (!PyArg_ParseTuple(args, "")) {
return nullptr;
}
GeoFeature* object = this->getGeoFeaturePtr();
const PropertyComplexGeoData* prop = object->getPropertyOfGeometry();
@@ -105,7 +110,7 @@ PyObject* GeoFeaturePy::getPropertyOfGeometry(PyObject * args)
return Py::new_reference_to(Py::None());
}
PyObject *GeoFeaturePy::getCustomAttributes(const char* /*attr*/) const
PyObject* GeoFeaturePy::getCustomAttributes(const char* /*attr*/) const
{
return nullptr;
}
@@ -115,7 +120,8 @@ int GeoFeaturePy::setCustomAttributes(const char* /*attr*/, PyObject* /*obj*/)
return 0;
}
Py::String GeoFeaturePy::getElementMapVersion() const {
return Py::String(getGeoFeaturePtr()->getElementMapVersion(
getGeoFeaturePtr()->getPropertyOfGeometry()));
Py::String GeoFeaturePy::getElementMapVersion() const
{
return Py::String(
getGeoFeaturePtr()->getElementMapVersion(getGeoFeaturePtr()->getPropertyOfGeometry()));
}

View File

@@ -38,21 +38,22 @@
using namespace App;
using namespace boost;
void Document::writeDependencyGraphViz(std::ostream &out)
void Document::writeDependencyGraphViz(std::ostream& out)
{
// // caching vertex to DocObject
//std::map<Vertex,DocumentObject*> VertexMap;
//for(std::map<DocumentObject*,Vertex>::const_iterator It1= _DepConMap.begin();It1 != _DepConMap.end(); ++It1)
// std::map<Vertex,DocumentObject*> VertexMap;
// for(std::map<DocumentObject*,Vertex>::const_iterator It1= _DepConMap.begin();It1 !=
// _DepConMap.end(); ++It1)
// VertexMap[It1->second] = It1->first;
out << "digraph G {" << std::endl;
out << "\tordering=out;" << std::endl;
out << "\tnode [shape = box];" << std::endl;
for (const auto &It : d->objectMap) {
for (const auto& It : d->objectMap) {
out << "\t" << It.first << ";" << std::endl;
std::vector<DocumentObject*> OutList = It.second->getOutList();
for (const auto &It2 : OutList) {
for (const auto& It2 : OutList) {
if (It2) {
out << "\t" << It.first << "->" << It2->getNameInDocument() << ";" << std::endl;
}
@@ -75,14 +76,19 @@ void Document::exportGraphviz(std::ostream& out) const
{
/* Type defs for a graph with graphviz attributes */
using GraphvizAttributes = std::map<std::string, std::string>;
using Graph = boost::subgraph< adjacency_list<vecS, vecS, directedS,
property<vertex_attribute_t, GraphvizAttributes>,
property<edge_index_t, int, property<edge_attribute_t, GraphvizAttributes> >,
property<graph_name_t, std::string,
property<graph_graph_attribute_t, GraphvizAttributes,
property<graph_vertex_attribute_t, GraphvizAttributes,
property<graph_edge_attribute_t, GraphvizAttributes>
> > > > >;
using Graph = boost::subgraph<adjacency_list<
vecS,
vecS,
directedS,
property<vertex_attribute_t, GraphvizAttributes>,
property<edge_index_t, int, property<edge_attribute_t, GraphvizAttributes>>,
property<graph_name_t,
std::string,
property<graph_graph_attribute_t,
GraphvizAttributes,
property<graph_vertex_attribute_t,
GraphvizAttributes,
property<graph_edge_attribute_t, GraphvizAttributes>>>>>>;
/**
* @brief The GraphCreator class
@@ -91,18 +97,25 @@ void Document::exportGraphviz(std::ostream& out) const
*
*/
class GraphCreator {
class GraphCreator
{
public:
explicit GraphCreator(struct DocumentP* _d) : d(_d), seed(std::random_device()()), distribution(0,255) {
explicit GraphCreator(struct DocumentP* _d)
: d(_d)
, seed(std::random_device()())
, distribution(0, 255)
{
build();
}
const Graph & getGraph() const { return DepList; }
const Graph& getGraph() const
{
return DepList;
}
private:
void build() {
void build()
{
// Set attribute(s) for main graph
get_property(DepList, graph_graph_attribute)["compound"] = "true";
@@ -119,7 +132,8 @@ void Document::exportGraphviz(std::ostream& out) const
* @return A string
*/
std::string getId(const DocumentObject * docObj) {
std::string getId(const DocumentObject* docObj)
{
std::string id;
if (docObj->isAttachedToDocument()) {
auto doc = docObj->getDocument();
@@ -136,25 +150,32 @@ void Document::exportGraphviz(std::ostream& out) const
* @return A string
*/
std::string getId(const ObjectIdentifier & path) {
DocumentObject * docObj = path.getDocumentObject();
if (!docObj)
std::string getId(const ObjectIdentifier& path)
{
DocumentObject* docObj = path.getDocumentObject();
if (!docObj) {
return {};
}
return std::string((docObj)->getDocument()->getName()) + "#" + docObj->getNameInDocument() + "." + path.getPropertyName() + path.getSubPathStr();
return std::string((docObj)->getDocument()->getName()) + "#"
+ docObj->getNameInDocument() + "." + path.getPropertyName() + path.getSubPathStr();
}
std::string getClusterName(const DocumentObject * docObj) const {
std::string getClusterName(const DocumentObject* docObj) const
{
return std::string("cluster") + docObj->getNameInDocument();
}
void setGraphLabel(Graph& g, const DocumentObject* obj) const {
void setGraphLabel(Graph& g, const DocumentObject* obj) const
{
std::string name(obj->getNameInDocument());
std::string label(obj->Label.getValue());
if (name == label)
if (name == label) {
get_property(g, graph_graph_attribute)["label"] = name;
else
}
else {
get_property(g, graph_graph_attribute)["label"] = name + "&#92;n(" + label + ")";
}
}
/**
@@ -162,7 +183,8 @@ void Document::exportGraphviz(std::ostream& out) const
* @param obj DocumentObject
*/
void setGraphAttributes(const DocumentObject * obj) {
void setGraphAttributes(const DocumentObject* obj)
{
assert(GraphList.find(obj) != GraphList.end());
get_property(*GraphList[obj], graph_name) = getClusterName(obj);
@@ -179,7 +201,8 @@ void Document::exportGraphviz(std::ostream& out) const
* @param name Name of node
*/
void setPropertyVertexAttributes(Graph & g, Vertex vertex, const std::string & name) {
void setPropertyVertexAttributes(Graph& g, Vertex vertex, const std::string& name)
{
get(vertex_attribute, g)[vertex]["label"] = name;
get(vertex_attribute, g)[vertex]["shape"] = "box";
get(vertex_attribute, g)[vertex]["style"] = "dashed";
@@ -187,13 +210,15 @@ void Document::exportGraphviz(std::ostream& out) const
}
/**
* @brief addExpressionSubgraphIfNeeded Add a subgraph to the main graph if it is needed, i.e. there are defined at least one
* expression in the document object, or other objects are referencing properties in it.
* @brief addExpressionSubgraphIfNeeded Add a subgraph to the main graph if it is needed,
* i.e. there are defined at least one expression in the document object, or other objects
* are referencing properties in it.
* @param obj DocumentObject to assess.
* @param CSSubgraphs Boolean if the GeoFeatureGroups are created as subgraphs
*/
void addExpressionSubgraphIfNeeded(DocumentObject * obj, bool CSsubgraphs) {
void addExpressionSubgraphIfNeeded(DocumentObject* obj, bool CSsubgraphs)
{
auto expressions = obj->ExpressionEngine.getExpressions();
@@ -205,8 +230,9 @@ void Document::exportGraphviz(std::ostream& out) const
auto group = GeoFeatureGroupExtension::getGroupOfObject(obj);
if (group) {
auto it = GraphList.find(group);
if (it != GraphList.end())
if (it != GraphList.end()) {
graph = it->second;
}
}
}
@@ -216,16 +242,18 @@ void Document::exportGraphviz(std::ostream& out) const
setGraphAttributes(obj);
}
// Create subgraphs for all document objects that it depends on; it will depend on some property there
for (const auto &expr : expressions) {
// Create subgraphs for all document objects that it depends on; it will depend on
// some property there
for (const auto& expr : expressions) {
std::map<ObjectIdentifier, bool> deps;
expr.second->getIdentifiers(deps);
for (const auto &dep : deps) {
if (dep.second)
for (const auto& dep : deps) {
if (dep.second) {
continue;
DocumentObject * o = dep.first.getDocumentObject();
}
DocumentObject* o = dep.first.getDocumentObject();
// Doesn't exist already?
if (o && !GraphList[o]) {
@@ -249,38 +277,48 @@ void Document::exportGraphviz(std::ostream& out) const
}
/**
* @brief add Add @docObj to the graph, including all expressions (and dependencies) it includes.
* @brief add Add @docObj to the graph, including all expressions (and dependencies) it
* includes.
* @param docObj The document object to add.
* @param name Name of node.
*/
void add(DocumentObject *docObj, const std::string &name, const std::string &label, bool CSSubgraphs)
void add(DocumentObject* docObj,
const std::string& name,
const std::string& label,
bool CSSubgraphs)
{
//don't add objects twice
if (std::find(objects.begin(), objects.end(), docObj) != objects.end())
// don't add objects twice
if (std::find(objects.begin(), objects.end(), docObj) != objects.end()) {
return;
}
//find the correct graph to add the vertex to. Check first expression graphs, afterwards
//the parent CS and origin graphs
Graph *sgraph = GraphList[docObj];
// find the correct graph to add the vertex to. Check first expression graphs,
// afterwards the parent CS and origin graphs
Graph* sgraph = GraphList[docObj];
if (CSSubgraphs) {
if (!sgraph) {
auto group = GeoFeatureGroupExtension::getGroupOfObject(docObj);
if (group) {
if (docObj->isDerivedFrom(App::OriginFeature::getClassTypeId()))
sgraph = GraphList[group->getExtensionByType<OriginGroupExtension>()->Origin.getValue()];
else
if (docObj->isDerivedFrom(App::OriginFeature::getClassTypeId())) {
sgraph = GraphList[group->getExtensionByType<OriginGroupExtension>()
->Origin.getValue()];
}
else {
sgraph = GraphList[group];
}
}
}
if (!sgraph) {
if (docObj->isDerivedFrom(OriginFeature::getClassTypeId()))
sgraph = GraphList[static_cast<OriginFeature *>(docObj)->getOrigin()];
if (docObj->isDerivedFrom(OriginFeature::getClassTypeId())) {
sgraph = GraphList[static_cast<OriginFeature*>(docObj)->getOrigin()];
}
}
}
if (!sgraph)
if (!sgraph) {
sgraph = &DepList;
}
// Keep a list of all added document objects.
objects.insert(docObj);
@@ -294,21 +332,25 @@ void Document::exportGraphviz(std::ostream& out) const
get(vertex_attribute, *sgraph)[LocalVertexList[getId(docObj)]]["style"] = "filled";
get(vertex_attribute, *sgraph)[LocalVertexList[getId(docObj)]]["shape"] = "Mrecord";
// Set node label
if (name == label)
if (name == label) {
get(vertex_attribute, *sgraph)[LocalVertexList[getId(docObj)]]["label"] = name;
else
get(vertex_attribute, *sgraph)[LocalVertexList[getId(docObj)]]["label"] = name + "&#92;n(" + label + ")";
}
else {
get(vertex_attribute, *sgraph)[LocalVertexList[getId(docObj)]]["label"] =
name + "&#92;n(" + label + ")";
}
}
else {
get(vertex_attribute, *sgraph)[LocalVertexList[getId(docObj)]]["style"] = "invis";
get(vertex_attribute, *sgraph)[LocalVertexList[getId(docObj)]]["fixedsize"] = "true";
get(vertex_attribute, *sgraph)[LocalVertexList[getId(docObj)]]["fixedsize"] =
"true";
get(vertex_attribute, *sgraph)[LocalVertexList[getId(docObj)]]["width"] = "0";
get(vertex_attribute, *sgraph)[LocalVertexList[getId(docObj)]]["height"] = "0";
}
// Add expressions and its dependencies
auto expressions{docObj->ExpressionEngine.getExpressions()};
for (const auto &expr : expressions) {
auto expressions {docObj->ExpressionEngine.getExpressions()};
for (const auto& expr : expressions) {
auto found = std::as_const(GlobalVertexList).find(getId(expr.first));
if (found == GlobalVertexList.end()) {
int vid = LocalVertexList[getId(expr.first)] = add_vertex(*sgraph);
@@ -318,63 +360,70 @@ void Document::exportGraphviz(std::ostream& out) const
}
// Add all dependencies
for (const auto &expression : expressions) {
for (const auto& expression : expressions) {
// Get dependencies
std::map<ObjectIdentifier, bool> deps;
expression.second->getIdentifiers(deps);
// Create subgraphs for all documentobjects that it depends on; it will depend on some property there
for (const auto &dep : deps) {
// Create subgraphs for all documentobjects that it depends on; it will depend on
// some property there
for (const auto& dep : deps) {
if (dep.second) {
continue;
}
DocumentObject *depObjDoc = dep.first.getDocumentObject();
DocumentObject* depObjDoc = dep.first.getDocumentObject();
auto found = GlobalVertexList.find(getId(dep.first));
if (found == GlobalVertexList.end()) {
Graph *depSgraph = GraphList[depObjDoc] ? GraphList[depObjDoc] : &DepList;
Graph* depSgraph = GraphList[depObjDoc] ? GraphList[depObjDoc] : &DepList;
LocalVertexList[getId(dep.first)] = add_vertex(*depSgraph);
GlobalVertexList[getId(dep.first)] = vertex_no++;
setPropertyVertexAttributes(*depSgraph, LocalVertexList[getId(dep.first)], dep.first.getPropertyName() + dep.first.getSubPathStr());
setPropertyVertexAttributes(*depSgraph,
LocalVertexList[getId(dep.first)],
dep.first.getPropertyName()
+ dep.first.getSubPathStr());
}
}
}
}
void recursiveCSSubgraphs(DocumentObject* cs, DocumentObject* parent) {
void recursiveCSSubgraphs(DocumentObject* cs, DocumentObject* parent)
{
auto graph = parent ? GraphList[parent] : &DepList;
// check if the value for the key 'parent' is null
if (!graph)
if (!graph) {
return;
}
auto& sub = graph->create_subgraph();
GraphList[cs] = &sub;
get_property(sub, graph_name) = getClusterName(cs);
//build random color string
// build random color string
std::stringstream stream;
stream << "#" << std::setfill('0') << std::setw(2)<< std::hex << distribution(seed)
<< std::setfill('0') << std::setw(2)<< std::hex << distribution(seed)
<< std::setfill('0') << std::setw(2)<< std::hex << distribution(seed) << 80;
stream << "#" << std::setfill('0') << std::setw(2) << std::hex << distribution(seed)
<< std::setfill('0') << std::setw(2) << std::hex << distribution(seed)
<< std::setfill('0') << std::setw(2) << std::hex << distribution(seed) << 80;
std::string result(stream.str());
get_property(sub, graph_graph_attribute)["bgcolor"] = result;
get_property(sub, graph_graph_attribute)["style"] = "rounded,filled";
setGraphLabel(sub, cs);
for(auto obj : cs->getOutList()) {
for (auto obj : cs->getOutList()) {
if (obj->hasExtension(GeoFeatureGroupExtension::getExtensionClassTypeId())) {
// in case of dependencies loops check if obj is already part of the
// map to avoid infinite recursions
auto it = GraphList.find(obj);
if (it == GraphList.end())
if (it == GraphList.end()) {
recursiveCSSubgraphs(obj, cs);
}
}
}
//setup the origin if available
if(cs->hasExtension(App::OriginGroupExtension::getExtensionClassTypeId())) {
// setup the origin if available
if (cs->hasExtension(App::OriginGroupExtension::getExtensionClassTypeId())) {
auto origin = cs->getExtensionByType<OriginGroupExtension>()->Origin.getValue();
if (!origin) {
std::cerr << "Origin feature not found" << std::endl;
@@ -388,67 +437,80 @@ void Document::exportGraphviz(std::ostream& out) const
}
}
void addSubgraphs() {
void addSubgraphs()
{
ParameterGrp::handle depGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/DependencyGraph");
ParameterGrp::handle depGrp = App::GetApplication().GetParameterGroupByPath(
"User parameter:BaseApp/Preferences/DependencyGraph");
bool CSSubgraphs = depGrp->GetBool("GeoFeatureSubgraphs", true);
if(CSSubgraphs) {
//first build up the coordinate system subgraphs
if (CSSubgraphs) {
// first build up the coordinate system subgraphs
for (auto objectIt : d->objectArray) {
// ignore groups inside other groups, these will be processed in one of the next recursive calls.
// App::Origin now has the GeoFeatureGroupExtension but it should not move its
// group symbol outside its parent
if (!objectIt->isDerivedFrom(Origin::getClassTypeId()) &&
objectIt->hasExtension(GeoFeatureGroupExtension::getExtensionClassTypeId()) &&
GeoFeatureGroupExtension::getGroupOfObject(objectIt) == nullptr)
{
// ignore groups inside other groups, these will be processed in one of the next
// recursive calls. App::Origin now has the GeoFeatureGroupExtension but it
// should not move its group symbol outside its parent
if (!objectIt->isDerivedFrom(Origin::getClassTypeId())
&& objectIt->hasExtension(
GeoFeatureGroupExtension::getExtensionClassTypeId())
&& GeoFeatureGroupExtension::getGroupOfObject(objectIt) == nullptr) {
recursiveCSSubgraphs(objectIt, nullptr);
}
}
}
// Internal document objects
for (const auto & It : d->objectMap)
for (const auto& It : d->objectMap) {
addExpressionSubgraphIfNeeded(It.second, CSSubgraphs);
}
// Add external document objects
for (const auto & it : d->objectMap) {
for (const auto& it : d->objectMap) {
std::vector<DocumentObject*> OutList = it.second->getOutList();
for (auto obj : OutList) {
if (obj) {
std::map<std::string,Vertex>::const_iterator item = GlobalVertexList.find(getId(obj));
std::map<std::string, Vertex>::const_iterator item =
GlobalVertexList.find(getId(obj));
if (item == GlobalVertexList.end())
if (item == GlobalVertexList.end()) {
addExpressionSubgraphIfNeeded(obj, CSSubgraphs);
}
}
}
}
}
// Filling up the adjacency List
void buildAdjacencyList() {
void buildAdjacencyList()
{
ParameterGrp::handle depGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/DependencyGraph");
ParameterGrp::handle depGrp = App::GetApplication().GetParameterGroupByPath(
"User parameter:BaseApp/Preferences/DependencyGraph");
bool CSSubgraphs = depGrp->GetBool("GeoFeatureSubgraphs", true);
// Add internal document objects
for (const auto & It : d->objectMap)
add(It.second, It.second->getNameInDocument(), It.second->Label.getValue(), CSSubgraphs);
for (const auto& It : d->objectMap) {
add(It.second,
It.second->getNameInDocument(),
It.second->Label.getValue(),
CSSubgraphs);
}
// Add external document objects
for (const auto & It : d->objectMap) {
for (const auto& It : d->objectMap) {
std::vector<DocumentObject*> OutList = It.second->getOutList();
for (auto obj : OutList) {
if (obj) {
std::map<std::string,Vertex>::const_iterator item = GlobalVertexList.find(getId(obj));
std::map<std::string, Vertex>::const_iterator item =
GlobalVertexList.find(getId(obj));
if (item == GlobalVertexList.end()) {
if (obj->isAttachedToDocument()) {
add(obj,
std::string(obj->getDocument()->getName()) + "#" + obj->getNameInDocument(),
std::string(obj->getDocument()->getName()) + "#" + obj->Label.getValue(),
std::string(obj->getDocument()->getName()) + "#"
+ obj->getNameInDocument(),
std::string(obj->getDocument()->getName()) + "#"
+ obj->Label.getValue(),
CSSubgraphs);
}
}
@@ -457,31 +519,37 @@ void Document::exportGraphviz(std::ostream& out) const
}
}
void addEdges() {
void addEdges()
{
// Get edge properties for main graph
const boost::property_map<Graph, boost::edge_attribute_t>::type& edgeAttrMap = boost::get(boost::edge_attribute, DepList);
const boost::property_map<Graph, boost::edge_attribute_t>::type& edgeAttrMap =
boost::get(boost::edge_attribute, DepList);
// Track edges between document objects connected by expression dependencies
std::set<std::pair<const DocumentObject*, const DocumentObject*> > existingEdges;
std::set<std::pair<const DocumentObject*, const DocumentObject*>> existingEdges;
// Add edges between properties
for (const auto &docObj : objects) {
for (const auto& docObj : objects) {
// Add expressions and its dependencies
auto expressions = docObj->ExpressionEngine.getExpressions();
for (const auto &expr : expressions) {
for (const auto& expr : expressions) {
std::map<ObjectIdentifier, bool> deps;
expr.second->getIdentifiers(deps);
// Create subgraphs for all documentobjects that it depends on; it will depend on some property there
for (const auto &dep : deps) {
if (dep.second)
// Create subgraphs for all documentobjects that it depends on; it will depend
// on some property there
for (const auto& dep : deps) {
if (dep.second) {
continue;
DocumentObject * depObjDoc = dep.first.getDocumentObject();
}
DocumentObject* depObjDoc = dep.first.getDocumentObject();
Edge edge;
bool inserted;
tie(edge, inserted) = add_edge(GlobalVertexList[getId(expr.first)], GlobalVertexList[getId(dep.first)], DepList);
tie(edge, inserted) = add_edge(GlobalVertexList[getId(expr.first)],
GlobalVertexList[getId(dep.first)],
DepList);
// Add this edge to the set of all expression generated edges
existingEdges.insert(std::make_pair(docObj, depObjDoc));
@@ -493,54 +561,69 @@ void Document::exportGraphviz(std::ostream& out) const
}
}
ParameterGrp::handle depGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/DependencyGraph");
ParameterGrp::handle depGrp = App::GetApplication().GetParameterGroupByPath(
"User parameter:BaseApp/Preferences/DependencyGraph");
bool omitGeoFeatureGroups = depGrp->GetBool("GeoFeatureSubgraphs", true);
// Add edges between document objects
for (const auto & It : d->objectMap) {
for (const auto& It : d->objectMap) {
if(omitGeoFeatureGroups && It.second->isDerivedFrom(Origin::getClassTypeId())) {
if (omitGeoFeatureGroups && It.second->isDerivedFrom(Origin::getClassTypeId())) {
continue;
}
std::map<DocumentObject*, int> dups;
std::vector<DocumentObject*> OutList = It.second->getOutList();
const DocumentObject * docObj = It.second;
const bool docObj_is_group = docObj->hasExtension(GeoFeatureGroupExtension::getExtensionClassTypeId());
const DocumentObject* docObj = It.second;
const bool docObj_is_group =
docObj->hasExtension(GeoFeatureGroupExtension::getExtensionClassTypeId());
for (auto obj : OutList) {
if (obj) {
if(omitGeoFeatureGroups && docObj_is_group && GeoFeatureGroupExtension::getGroupOfObject(obj) == docObj) {
if (omitGeoFeatureGroups && docObj_is_group
&& GeoFeatureGroupExtension::getGroupOfObject(obj) == docObj) {
continue;
}
// Count duplicate edges
bool inserted = edge(GlobalVertexList[getId(docObj)], GlobalVertexList[getId(obj)], DepList).second;
bool inserted = edge(GlobalVertexList[getId(docObj)],
GlobalVertexList[getId(obj)],
DepList)
.second;
if (inserted) {
dups[obj]++;
continue;
}
// Skip edge if an expression edge already exists
if (existingEdges.find(std::make_pair(docObj, obj)) != existingEdges.end())
if (existingEdges.find(std::make_pair(docObj, obj))
!= existingEdges.end()) {
continue;
}
// Add edge
Edge edge;
tie(edge, inserted) = add_edge(GlobalVertexList[getId(docObj)], GlobalVertexList[getId(obj)], DepList);
tie(edge, inserted) = add_edge(GlobalVertexList[getId(docObj)],
GlobalVertexList[getId(obj)],
DepList);
// Set properties to make arrows go between subgraphs if needed
if (GraphList[docObj])
if (GraphList[docObj]) {
edgeAttrMap[edge]["ltail"] = getClusterName(docObj);
if (GraphList[obj])
}
if (GraphList[obj]) {
edgeAttrMap[edge]["lhead"] = getClusterName(obj);
}
}
}
// Set labels for duplicate edges
for (const auto & dup : dups) {
Edge e(edge(GlobalVertexList[getId(It.second)], GlobalVertexList[getId(dup.first)], DepList).first);
for (const auto& dup : dups) {
Edge e(edge(GlobalVertexList[getId(It.second)],
GlobalVertexList[getId(dup.first)],
DepList)
.first);
std::stringstream s;
s << " " << (dup.second + 1) << "x";
edgeAttrMap[e]["label"] = s.str();
@@ -550,10 +633,11 @@ void Document::exportGraphviz(std::ostream& out) const
using EdgeMap = std::unordered_multimap<Vertex, Edge>;
void removeEdges(EdgeMap & in_edges,
EdgeMap & out_edges,
std::pair<EdgeMap::iterator, EdgeMap::iterator > i_pair,
std::function<Vertex (const Edge&)> select_vertex) {
void removeEdges(EdgeMap& in_edges,
EdgeMap& out_edges,
std::pair<EdgeMap::iterator, EdgeMap::iterator> i_pair,
std::function<Vertex(const Edge&)> select_vertex)
{
auto i = i_pair.first;
while (i != i_pair.second) {
@@ -562,10 +646,12 @@ void Document::exportGraphviz(std::ostream& out) const
auto in_i = in_i_pair.first;
while (in_i != in_i_pair.second) {
if (in_i->second == i->second)
if (in_i->second == i->second) {
in_i = in_edges.erase(in_i);
else
}
else {
++in_i;
}
}
// Remove node from out_edges
@@ -574,12 +660,13 @@ void Document::exportGraphviz(std::ostream& out) const
}
#if defined(__clang__)
#elif defined (__GNUC__)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
#elif defined(__GNUC__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
#endif
void markCycles() {
void markCycles()
{
bool changed = true;
std::unordered_set<Vertex> in_use;
EdgeMap in_edges;
@@ -588,8 +675,9 @@ void Document::exportGraphviz(std::ostream& out) const
// Add all vertices to the in_use set
graph_traits<Graph>::vertex_iterator vi, vi_end;
tie(vi, vi_end) = vertices(DepList);
for (; vi != vi_end; ++vi)
for (; vi != vi_end; ++vi) {
in_use.insert(*vi);
}
// Add all edges to the in_edges and out_edges multimaps
graph_traits<Graph>::edge_iterator ei, ei_end;
@@ -616,21 +704,26 @@ void Document::exportGraphviz(std::ostream& out) const
auto i_in_deg_pair = in_edges.equal_range(*uvi);
auto i_out_deg_pair = out_edges.equal_range(*uvi);
if (i_in_deg_pair.first == in_edges.end() && i_out_deg_pair.first == out_edges.end()) {
if (i_in_deg_pair.first == in_edges.end()
&& i_out_deg_pair.first == out_edges.end()) {
uvi = in_use.erase(uvi);
continue;
}
// Remove out edges of nodes that don't have a single edge in
if (i_in_deg_pair.first == in_edges.end()) {
removeEdges(in_edges, out_edges, i_out_deg_pair, [&](Edge e) { return target(e, DepList); });
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
if (i_out_deg_pair.first == out_edges.end()) {
removeEdges(out_edges, in_edges, i_in_deg_pair, [&](Edge e) { return source(e, DepList); });
removeEdges(out_edges, in_edges, i_in_deg_pair, [&](Edge e) {
return source(e, DepList);
});
changed = true;
}
@@ -639,41 +732,48 @@ void Document::exportGraphviz(std::ostream& out) const
}
// Update colors in graph
const boost::property_map<Graph, boost::edge_attribute_t>::type& edgeAttrMap = boost::get(boost::edge_attribute, DepList);
for (auto ei : out_edges)
const boost::property_map<Graph, boost::edge_attribute_t>::type& edgeAttrMap =
boost::get(boost::edge_attribute, DepList);
for (auto ei : out_edges) {
edgeAttrMap[ei.second]["color"] = "red";
}
}
#if defined(__clang__)
#elif defined (__GNUC__)
# pragma GCC diagnostic pop
#elif defined(__GNUC__)
#pragma GCC diagnostic pop
#endif
void markOutOfScopeLinks() {
const boost::property_map<Graph, boost::edge_attribute_t>::type& edgeAttrMap = boost::get(boost::edge_attribute, DepList);
void markOutOfScopeLinks()
{
const boost::property_map<Graph, boost::edge_attribute_t>::type& edgeAttrMap =
boost::get(boost::edge_attribute, DepList);
for( auto obj : objects) {
for (auto obj : objects) {
std::vector<App::DocumentObject*> invalids;
GeoFeatureGroupExtension::getInvalidLinkObjects(obj, invalids);
//isLinkValid returns true for non-link properties
for(auto linkedObj : invalids) {
// isLinkValid returns true for non-link properties
for (auto linkedObj : invalids) {
auto res = edge(GlobalVertexList[getId(obj)], GlobalVertexList[getId(linkedObj)], DepList);
if(res.second)
auto res = edge(GlobalVertexList[getId(obj)],
GlobalVertexList[getId(linkedObj)],
DepList);
if (res.second) {
edgeAttrMap[res.first]["color"] = "orange";
}
}
}
}
const struct DocumentP* d;
Graph DepList;
int vertex_no{0};
int vertex_no {0};
std::map<std::string, Vertex> LocalVertexList;
std::map<std::string, Vertex> GlobalVertexList;
std::set<const DocumentObject*> objects;
std::map<const DocumentObject*, Graph*> GraphList;
//random color generation
// random color generation
std::mt19937 seed;
std::uniform_int_distribution<int> distribution;
};

View File

@@ -35,21 +35,29 @@ namespace sp = std::placeholders;
EXTENSION_PROPERTY_SOURCE(App::GroupExtension, App::DocumentObjectExtension)
namespace App {
namespace App
{
EXTENSION_PROPERTY_SOURCE_TEMPLATE(App::GroupExtensionPython, App::GroupExtension)
// explicit template instantiation
template class AppExport ExtensionPythonT<GroupExtensionPythonT<GroupExtension>>;
}
} // namespace App
GroupExtension::GroupExtension()
{
initExtensionType(GroupExtension::getExtensionClassTypeId());
EXTENSION_ADD_PROPERTY_TYPE(Group,(nullptr),"Base",(App::PropertyType)(Prop_None),"List of referenced objects");
EXTENSION_ADD_PROPERTY_TYPE(_GroupTouched, (false), "Base",
PropertyType(Prop_Hidden|Prop_Transient),0);
EXTENSION_ADD_PROPERTY_TYPE(Group,
(nullptr),
"Base",
(App::PropertyType)(Prop_None),
"List of referenced objects");
EXTENSION_ADD_PROPERTY_TYPE(_GroupTouched,
(false),
"Base",
PropertyType(Prop_Hidden | Prop_Transient),
0);
}
GroupExtension::~GroupExtension() = default;
@@ -57,7 +65,7 @@ GroupExtension::~GroupExtension() = default;
DocumentObject* GroupExtension::addObject(const char* sType, const char* pObjectName)
{
DocumentObject* obj = getExtendedObject()->getDocument()->addObject(sType, pObjectName);
if(!allowObject(obj)) {
if (!allowObject(obj)) {
getExtendedObject()->getDocument()->removeObject(obj->getNameInDocument());
return nullptr;
}
@@ -71,47 +79,55 @@ std::vector<DocumentObject*> GroupExtension::addObject(DocumentObject* obj)
return addObjects(vec);
}
std::vector< DocumentObject* > GroupExtension::addObjects(std::vector< DocumentObject* > objs) {
std::vector<DocumentObject*> GroupExtension::addObjects(std::vector<DocumentObject*> objs)
{
std::vector<DocumentObject*> added;
std::vector<DocumentObject*> grp = Group.getValues();
for(auto obj : objs) {
if(!allowObject(obj))
for (auto obj : objs) {
if (!allowObject(obj)) {
continue;
if (hasObject(obj))
}
if (hasObject(obj)) {
continue;
//only one group per object. Note that it is allowed to be in a group and geofeaturegroup. However,
//getGroupOfObject() returns only normal groups, no GeoFeatureGroups. Hence this works.
auto *group = App::GroupExtension::getGroupOfObject(obj);
if(group && group != getExtendedObject())
}
// only one group per object. Note that it is allowed to be in a group and geofeaturegroup.
// However, getGroupOfObject() returns only normal groups, no GeoFeatureGroups. Hence this
// works.
auto* group = App::GroupExtension::getGroupOfObject(obj);
if (group && group != getExtendedObject()) {
group->getExtensionByType<App::GroupExtension>()->removeObject(obj);
//if we are in a geofeaturegroup we need to ensure the object is too
}
// if we are in a geofeaturegroup we need to ensure the object is too
auto geogrp = GeoFeatureGroupExtension::getGroupOfObject(getExtendedObject());
auto objgrp = GeoFeatureGroupExtension::getGroupOfObject(obj);
if( geogrp != objgrp ) {
//what to do depends on if we are in geofeature group or not
if(geogrp)
if (geogrp != objgrp) {
// what to do depends on if we are in geofeature group or not
if (geogrp) {
geogrp->getExtensionByType<GeoFeatureGroupExtension>()->addObject(obj);
else
}
else {
objgrp->getExtensionByType<GeoFeatureGroupExtension>()->removeObject(obj);
}
}
grp.push_back(obj);
added.push_back(obj);
}
Group.setValues(grp);
return added;
}
std::vector< DocumentObject* > GroupExtension::setObjects(std::vector< DocumentObject* > obj) {
std::vector<DocumentObject*> GroupExtension::setObjects(std::vector<DocumentObject*> obj)
{
Group.setValues(std::vector< DocumentObject* > ());
Group.setValues(std::vector<DocumentObject*>());
return addObjects(obj);
}
@@ -121,26 +137,27 @@ std::vector<DocumentObject*> GroupExtension::removeObject(DocumentObject* obj)
return removeObjects(vec);
}
std::vector< DocumentObject* > GroupExtension::removeObjects(std::vector< DocumentObject* > objs) {
std::vector<DocumentObject*> GroupExtension::removeObjects(std::vector<DocumentObject*> objs)
{
const std::vector<DocumentObject*> & grp = Group.getValues();
const std::vector<DocumentObject*>& grp = Group.getValues();
std::vector<DocumentObject*> newGrp = grp;
std::vector<DocumentObject*> removed;
std::vector<DocumentObject*>::iterator end = newGrp.end();
for(auto obj : objs) {
auto res = std::remove(newGrp.begin(), end, obj);
if(res != end) {
end = res;
removed.push_back(obj);
}
for (auto obj : objs) {
auto res = std::remove(newGrp.begin(), end, obj);
if (res != end) {
end = res;
removed.push_back(obj);
}
}
newGrp.erase(end, newGrp.end());
if (grp.size() != newGrp.size()) {
Group.setValues (newGrp);
Group.setValues(newGrp);
}
return removed;
}
@@ -150,7 +167,7 @@ void GroupExtension::removeObjectsFromDocument()
// Remove the objects step by step because it can happen
// that an object is part of several groups and thus a
// double destruction could be possible
const std::vector<DocumentObject*> & grp = Group.getValues();
const std::vector<DocumentObject*>& grp = Group.getValues();
removeObjectFromDocument(grp.front());
}
}
@@ -158,12 +175,14 @@ void GroupExtension::removeObjectsFromDocument()
void GroupExtension::removeObjectFromDocument(DocumentObject* obj)
{
// check that object is not invalid
if (!obj || !obj->isAttachedToDocument())
if (!obj || !obj->isAttachedToDocument()) {
return;
}
// remove all children
if (obj->hasExtension(GroupExtension::getExtensionClassTypeId())) {
GroupExtension *grp = static_cast<GroupExtension*>(obj->getExtension(GroupExtension::getExtensionClassTypeId()));
GroupExtension* grp = static_cast<GroupExtension*>(
obj->getExtension(GroupExtension::getExtensionClassTypeId()));
// recursive call to remove all subgroups
grp->removeObjectsFromDocument();
@@ -172,11 +191,12 @@ void GroupExtension::removeObjectFromDocument(DocumentObject* obj)
getExtendedObject()->getDocument()->removeObject(obj->getNameInDocument());
}
DocumentObject *GroupExtension::getObject(const char *Name) const
DocumentObject* GroupExtension::getObject(const char* Name) const
{
DocumentObject* obj = getExtendedObject()->getDocument()->getObject(Name);
if (obj && hasObject(obj))
if (obj && hasObject(obj)) {
return obj;
}
return nullptr;
}
@@ -190,22 +210,24 @@ bool GroupExtension::hasObject(const DocumentObject* obj, bool recursive) const
const std::vector<DocumentObject*>& grp = Group.getValues();
for (auto child : grp) {
if (!child)
if (!child) {
continue;
}
if (child == obj) {
return true;
}
else if (child == getExtendedObject()) {
throw Base::RuntimeError("Cyclic dependencies detected: Search cannot be performed");
throw Base::RuntimeError(
"Cyclic dependencies detected: Search cannot be performed");
}
else if ( recursive && child->hasExtension(GroupExtension::getExtensionClassTypeId()) ) {
App::GroupExtension *subGroup = static_cast<App::GroupExtension *> (
child->getExtension(GroupExtension::getExtensionClassTypeId()));
else if (recursive && child->hasExtension(GroupExtension::getExtensionClassTypeId())) {
App::GroupExtension* subGroup = static_cast<App::GroupExtension*>(
child->getExtension(GroupExtension::getExtensionClassTypeId()));
std::vector<const GroupExtension*> history;
history.push_back(this);
if (subGroup->recursiveHasObject (obj, subGroup, history)) {
if (subGroup->recursiveHasObject(obj, subGroup, history)) {
return true;
}
}
@@ -219,31 +241,36 @@ bool GroupExtension::hasObject(const DocumentObject* obj, bool recursive) const
}
}
bool GroupExtension::recursiveHasObject(const DocumentObject* obj, const GroupExtension* group,
std::vector< const GroupExtension* > history) const {
bool GroupExtension::recursiveHasObject(const DocumentObject* obj,
const GroupExtension* group,
std::vector<const GroupExtension*> history) const
{
//the purpose is to prevent infinite recursion when groups form a cyclic graph. To do this
//we store every group we processed on the current leave of the tree, and if we reach an
//already processed group we know that it not really is a tree but a cycle.
// the purpose is to prevent infinite recursion when groups form a cyclic graph. To do this
// we store every group we processed on the current leave of the tree, and if we reach an
// already processed group we know that it not really is a tree but a cycle.
history.push_back(this);
//we use hasObject with out recursion to allow override in derived classes
if(group->hasObject(obj, false))
// we use hasObject with out recursion to allow override in derived classes
if (group->hasObject(obj, false)) {
return true;
}
//we checked for the searched object already with hasObject and did not find it, now we need to
//do the same for all subgroups
// we checked for the searched object already with hasObject and did not find it, now we need to
// do the same for all subgroups
for (auto child : group->Group.getValues()) {
if(!child)
if (!child) {
continue;
}
if ( child->hasExtension(GroupExtension::getExtensionClassTypeId()) ) {
if (child->hasExtension(GroupExtension::getExtensionClassTypeId())) {
auto ext = child->getExtensionByType<GroupExtension>();
if (std::find(history.begin(), history.end(), ext) != history.end()) {
throw Base::RuntimeError("Cyclic dependencies detected: Search cannot be performed");
throw Base::RuntimeError(
"Cyclic dependencies detected: Search cannot be performed");
}
if (recursiveHasObject(obj, ext, history)) {
@@ -259,7 +286,7 @@ bool GroupExtension::isChildOf(const GroupExtension* group, bool recursive) cons
return group->hasObject(getExtendedObject(), recursive);
}
const std::vector<DocumentObject*> &GroupExtension::getObjects() const
const std::vector<DocumentObject*>& GroupExtension::getObjects() const
{
return Group.getValues();
}
@@ -269,8 +296,9 @@ std::vector<DocumentObject*> GroupExtension::getObjectsOfType(const Base::Type&
std::vector<DocumentObject*> type;
const std::vector<DocumentObject*>& grp = Group.getValues();
for (auto it : grp) {
if (it->getTypeId().isDerivedFrom(typeId))
if (it->getTypeId().isDerivedFrom(typeId)) {
type.push_back(it);
}
}
return type;
@@ -278,11 +306,12 @@ std::vector<DocumentObject*> GroupExtension::getObjectsOfType(const Base::Type&
int GroupExtension::countObjectsOfType(const Base::Type& typeId) const
{
int type=0;
int type = 0;
const std::vector<DocumentObject*>& grp = Group.getValues();
for (auto it : grp) {
if ( it->getTypeId().isDerivedFrom(typeId))
if (it->getTypeId().isDerivedFrom(typeId)) {
type++;
}
}
return type;
@@ -290,57 +319,63 @@ int GroupExtension::countObjectsOfType(const Base::Type& typeId) const
DocumentObject* GroupExtension::getGroupOfObject(const DocumentObject* obj)
{
//note that we return here only Groups, but nothing derived from it, e.g. no GeoFeatureGroups.
//That is important as there are clear differences between groups/geofeature groups (e.g. an object
//can be in only one group, and only one geofeaturegroup, however, it can be in both at the same time)
// note that we return here only Groups, but nothing derived from it, e.g. no GeoFeatureGroups.
// That is important as there are clear differences between groups/geofeature groups (e.g. an
// object can be in only one group, and only one geofeaturegroup, however, it can be in both at
// the same time)
for (auto o : obj->getInList()) {
if (o->hasExtension(App::GroupExtension::getExtensionClassTypeId(), false))
if (o->hasExtension(App::GroupExtension::getExtensionClassTypeId(), false)) {
return o;
if (o->hasExtension(App::GroupExtensionPython::getExtensionClassTypeId(), false))
}
if (o->hasExtension(App::GroupExtensionPython::getExtensionClassTypeId(), false)) {
return o;
}
}
return nullptr;
}
PyObject* GroupExtension::getExtensionPyObject() {
PyObject* GroupExtension::getExtensionPyObject()
{
if (ExtensionPythonObject.is(Py::_None())){
if (ExtensionPythonObject.is(Py::_None())) {
// ref counter is set to 1
auto grp = new GroupExtensionPy(this);
ExtensionPythonObject = Py::Object(grp,true);
ExtensionPythonObject = Py::Object(grp, true);
}
return Py::new_reference_to(ExtensionPythonObject);
}
void GroupExtension::extensionOnChanged(const Property* p) {
void GroupExtension::extensionOnChanged(const Property* p)
{
// objects are only allowed in a single group. Note that this check must only be done for normal
// groups, not any derived classes
if ((this->getExtensionTypeId() == GroupExtension::getExtensionClassTypeId()) && p == &Group
&& !Group.testStatus(Property::User3)) {
if (!getExtendedObject()->isRestoring()
&& !getExtendedObject()->getDocument()->isPerformingTransaction()) {
//objects are only allowed in a single group. Note that this check must only be done for normal
//groups, not any derived classes
if((this->getExtensionTypeId() == GroupExtension::getExtensionClassTypeId())
&& p == &Group && !Group.testStatus(Property::User3))
{
if(!getExtendedObject()->isRestoring() &&
!getExtendedObject()->getDocument()->isPerformingTransaction()) {
bool error = false;
auto corrected = Group.getValues();
for(auto obj : Group.getValues()) {
for (auto obj : Group.getValues()) {
//we have already set the obj into the group, so in a case of multiple groups getGroupOfObject
//would return anyone of it and hence it is possible that we miss an error. We need a custom check
// we have already set the obj into the group, so in a case of multiple groups
// getGroupOfObject would return anyone of it and hence it is possible that we miss
// an error. We need a custom check
auto list = obj->getInList();
for (auto in : list) {
if(in->hasExtension(App::GroupExtension::getExtensionClassTypeId(), false) &&
in != getExtendedObject()) {
if (in->hasExtension(App::GroupExtension::getExtensionClassTypeId(), false)
&& in != getExtendedObject()) {
error = true;
corrected.erase(std::remove(corrected.begin(), corrected.end(), obj), corrected.end());
corrected.erase(std::remove(corrected.begin(), corrected.end(), obj),
corrected.end());
}
}
}
//if an error was found we need to correct the values and inform the user
if(error) {
// if an error was found we need to correct the values and inform the user
if (error) {
Base::ObjectStatusLocker<Property::Status, Property> guard(Property::User3, &Group);
Group.setValues(corrected);
throw Base::RuntimeError("Object can only be in a single Group");
@@ -348,14 +383,14 @@ void GroupExtension::extensionOnChanged(const Property* p) {
}
}
if(p == &Group) {
if (p == &Group) {
_Conns.clear();
for(auto obj : Group.getValue()) {
if(obj && obj->isAttachedToDocument()) {
//NOLINTBEGIN
_Conns[obj] = obj->signalChanged.connect(std::bind(
&GroupExtension::slotChildChanged,this,sp::_1, sp::_2));
//NOLINTEND
for (auto obj : Group.getValue()) {
if (obj && obj->isAttachedToDocument()) {
// NOLINTBEGIN
_Conns[obj] = obj->signalChanged.connect(
std::bind(&GroupExtension::slotChildChanged, this, sp::_1, sp::_2));
// NOLINTEND
}
}
}
@@ -363,71 +398,87 @@ void GroupExtension::extensionOnChanged(const Property* p) {
App::Extension::extensionOnChanged(p);
}
void GroupExtension::slotChildChanged(const DocumentObject &obj, const Property &prop) {
if(&prop == &obj.Visibility)
void GroupExtension::slotChildChanged(const DocumentObject& obj, const Property& prop)
{
if (&prop == &obj.Visibility) {
_GroupTouched.touch();
}
}
bool GroupExtension::extensionGetSubObject(DocumentObject *&ret, const char *subname,
PyObject **pyObj, Base::Matrix4D *mat, bool /*transform*/, int depth) const
bool GroupExtension::extensionGetSubObject(DocumentObject*& ret,
const char* subname,
PyObject** pyObj,
Base::Matrix4D* mat,
bool /*transform*/,
int depth) const
{
const char *dot;
if(!subname || *subname==0) {
const char* dot;
if (!subname || *subname == 0) {
auto obj = Base::freecad_dynamic_cast<const DocumentObject>(getExtendedContainer());
ret = const_cast<DocumentObject*>(obj);
return true;
}
dot=strchr(subname,'.');
if(!dot)
dot = strchr(subname, '.');
if (!dot) {
return false;
if(subname[0]!='$')
ret = Group.findUsingMap(std::string(subname,dot));
else{
std::string name = std::string(subname+1,dot);
for(auto child : Group.getValues()) {
if(name == child->Label.getStrValue()){
}
if (subname[0] != '$') {
ret = Group.findUsingMap(std::string(subname, dot));
}
else {
std::string name = std::string(subname + 1, dot);
for (auto child : Group.getValues()) {
if (name == child->Label.getStrValue()) {
ret = child;
break;
}
}
}
if(!ret)
if (!ret) {
return false;
return ret->getSubObject(dot+1,pyObj,mat,true,depth+1);
}
return ret->getSubObject(dot + 1, pyObj, mat, true, depth + 1);
}
bool GroupExtension::extensionGetSubObjects(std::vector<std::string> &ret, int) const {
for(auto obj : Group.getValues()) {
if(obj && obj->isAttachedToDocument())
ret.push_back(std::string(obj->getNameInDocument())+'.');
bool GroupExtension::extensionGetSubObjects(std::vector<std::string>& ret, int) const
{
for (auto obj : Group.getValues()) {
if (obj && obj->isAttachedToDocument()) {
ret.push_back(std::string(obj->getNameInDocument()) + '.');
}
}
return true;
}
App::DocumentObjectExecReturn *GroupExtension::extensionExecute() {
App::DocumentObjectExecReturn* GroupExtension::extensionExecute()
{
// This touch property is for propagating changes to upper group
_GroupTouched.touch();
return inherited::extensionExecute();
}
std::vector<App::DocumentObject*> GroupExtension::getAllChildren() const {
std::vector<App::DocumentObject*> GroupExtension::getAllChildren() const
{
std::vector<DocumentObject*> res;
std::set<DocumentObject*> rset;
getAllChildren(res,rset);
getAllChildren(res, rset);
return res;
}
void GroupExtension::getAllChildren(std::vector<App::DocumentObject*> &res,
std::set<App::DocumentObject*> &rset) const
void GroupExtension::getAllChildren(std::vector<App::DocumentObject*>& res,
std::set<App::DocumentObject*>& rset) const
{
for(auto obj : Group.getValues()) {
if(!obj || !obj->isAttachedToDocument())
for (auto obj : Group.getValues()) {
if (!obj || !obj->isAttachedToDocument()) {
continue;
if(!rset.insert(obj).second)
}
if (!rset.insert(obj).second) {
continue;
}
res.push_back(obj);
auto ext = obj->getExtensionByType<GroupExtension>(true,false);
if(ext)
ext->getAllChildren(res,rset);
auto ext = obj->getExtensionByType<GroupExtension>(true, false);
if (ext) {
ext->getAllChildren(res, rset);
}
}
}

View File

@@ -35,7 +35,7 @@ namespace App
class DocumentObjectGroup;
class GroupExtensionPy;
class AppExport GroupExtension : public DocumentObjectExtension
class AppExport GroupExtension: public DocumentObjectExtension
{
EXTENSION_PROPERTY_HEADER_WITH_OVERRIDE(App::GroupExtension);
using inherited = DocumentObjectExtension;
@@ -50,22 +50,25 @@ 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.
*/
virtual 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. Returns all objects that have been added.
*/
virtual std::vector<DocumentObject*> addObject(DocumentObject* obj);
/* Adds the objects \a objs to this group. Returns all objects that have been added.
*/
virtual std::vector<DocumentObject*> addObjects(std::vector<DocumentObject*> obj);
/* Sets the objects in this group. Everything contained already will be removed first
*/
virtual std::vector< DocumentObject* > setObjects(std::vector< DocumentObject* > obj);
virtual std::vector<DocumentObject*> setObjects(std::vector<DocumentObject*> obj);
/*override this function if you want only special objects
*/
virtual bool allowObject(DocumentObject* ) {return true;}
virtual bool allowObject(DocumentObject*)
{
return true;
}
/** Removes an object from this group. Returns all objects that have been removed.
*/
virtual std::vector<DocumentObject*> removeObject(DocumentObject* obj);
@@ -75,16 +78,19 @@ public:
/** Removes all children objects from this group and the document.
*/
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.
/** 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.
*/
DocumentObject *getObject(const char* Name) const;
DocumentObject* getObject(const char* Name) const;
/**
* Checks whether the object \a obj is part of this group.
* @param obj the object to check for.
* @param recursive if true check also if the obj is child of some sub group (default is false).
* @param recursive if true check also if the obj is child of some sub group (default is
* false).
*/
virtual bool hasObject(const DocumentObject* obj, bool recursive=false) const;
virtual bool hasObject(const DocumentObject* obj, bool recursive = false) const;
/**
* Checks whether this group object is a child (or sub-child if enabled)
* of the given group object.
@@ -92,7 +98,7 @@ public:
bool isChildOf(const GroupExtension* group, bool recursive = true) const;
/** Returns a list of all objects this group does have.
*/
const std::vector<DocumentObject*> &getObjects() const;
const std::vector<DocumentObject*>& getObjects() const;
/** Returns a list of all objects of \a typeId this group does have.
*/
std::vector<DocumentObject*> getObjectsOfType(const Base::Type& typeId) const;
@@ -100,36 +106,44 @@ public:
*/
int countObjectsOfType(const Base::Type& typeId) const;
/** Returns the object group of the document which the given object \a obj is part of.
* In case this object is not part of a group 0 is returned.
* @note This only returns objects that are normal groups, not any special derived type
* like GeoFeatureGroups or OriginGroups. To retrieve those please use their appropriate functions
* In case this object is not part of a group 0 is returned.
* @note This only returns objects that are normal groups, not any special derived type
* like GeoFeatureGroups or OriginGroups. To retrieve those please use their appropriate
* functions
*/
static DocumentObject* getGroupOfObject(const DocumentObject* obj);
//@}
PyObject* getExtensionPyObject() override;
void extensionOnChanged(const Property* p) override;
bool extensionGetSubObject(DocumentObject *&ret, const char *subname,
PyObject **pyObj, Base::Matrix4D *mat, bool transform, int depth) const override;
bool extensionGetSubObject(DocumentObject*& ret,
const char* subname,
PyObject** pyObj,
Base::Matrix4D* mat,
bool transform,
int depth) const override;
bool extensionGetSubObjects(std::vector<std::string> &ret, int reason) const override;
bool extensionGetSubObjects(std::vector<std::string>& ret, int reason) const override;
App::DocumentObjectExecReturn *extensionExecute() override;
App::DocumentObjectExecReturn* extensionExecute() override;
std::vector<DocumentObject*> getAllChildren() const;
void getAllChildren(std::vector<DocumentObject*> &, std::set<DocumentObject*> &) const;
void getAllChildren(std::vector<DocumentObject*>&, std::set<DocumentObject*>&) const;
/// Properties
PropertyLinkList Group;
PropertyBool _GroupTouched;
private:
void removeObjectFromDocument(DocumentObject*);
// This function stores the already searched objects to prevent infinite recursion in case of a cyclic group graph
// It throws an exception of type Base::RuntimeError if a cyclic dependency is detected.
bool recursiveHasObject(const DocumentObject* obj, const GroupExtension* group, std::vector<const GroupExtension*> history) const;
// This function stores the already searched objects to prevent infinite recursion in case of a
// cyclic group graph It throws an exception of type Base::RuntimeError if a cyclic dependency
// is detected.
bool recursiveHasObject(const DocumentObject* obj,
const GroupExtension* group,
std::vector<const GroupExtension*> history) const;
// for tracking children visibility
void slotChildChanged(const App::DocumentObject&, const App::Property&);
@@ -138,32 +152,35 @@ private:
template<typename ExtensionT>
class GroupExtensionPythonT : public ExtensionT {
class GroupExtensionPythonT: public ExtensionT
{
public:
GroupExtensionPythonT() = default;
~GroupExtensionPythonT() override = default;
//override the documentobjectextension functions to make them available in python
bool allowObject(DocumentObject* obj) override {
// override the documentobjectextension functions to make them available in python
bool allowObject(DocumentObject* obj) override
{
Base::PyGILStateLocker locker;
Py::Object pyobj = Py::asObject(obj->getPyObject());
EXTENSION_PROXY_ONEARG(allowObject, pyobj);
if(result.isNone())
if (result.isNone()) {
return ExtensionT::allowObject(obj);
if(result.isBoolean())
}
if (result.isBoolean()) {
return result.isTrue();
}
return false;
};
};
using GroupExtensionPython = ExtensionPythonT<GroupExtensionPythonT<GroupExtension>>;
} //namespace App
} // namespace App
#endif // APP_GROUPEXTENSION_H
#endif // APP_GROUPEXTENSION_H

View File

@@ -1,13 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<GenerateModel xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="generateMetaModel_Module.xsd">
<PythonExport
Father="DocumentObjectExtensionPy"
Name="GroupExtensionPy"
Twin="GroupExtension"
TwinPointer="GroupExtension"
Include="App/DocumentObjectGroup.h"
Namespace="App"
FatherInclude="App/DocumentObjectExtensionPy.h"
<PythonExport
Father="DocumentObjectExtensionPy"
Name="GroupExtensionPy"
Twin="GroupExtension"
TwinPointer="GroupExtension"
Include="App/DocumentObjectGroup.h"
Namespace="App"
FatherInclude="App/DocumentObjectExtensionPy.h"
FatherNamespace="App">
<Documentation>
<Author Licence="LGPL" Name="Werner Mayer" EMail="wmayer@users.sourceforge.net" />

View File

@@ -39,14 +39,15 @@ std::string GroupExtensionPy::representation() const
return {"<group extension object>"};
}
PyObject* GroupExtensionPy::newObject(PyObject *args)
PyObject* GroupExtensionPy::newObject(PyObject* args)
{
char *sType,*sName=nullptr;
if (!PyArg_ParseTuple(args, "s|s", &sType,&sName))
char *sType, *sName = nullptr;
if (!PyArg_ParseTuple(args, "s|s", &sType, &sName)) {
return nullptr;
}
DocumentObject *object = getGroupExtensionPtr()->addObject(sType, sName);
if ( object ) {
DocumentObject* object = getGroupExtensionPtr()->addObject(sType, sName);
if (object) {
return object->getPyObject();
}
else {
@@ -55,20 +56,24 @@ PyObject* GroupExtensionPy::newObject(PyObject *args)
}
}
PyObject* GroupExtensionPy::addObject(PyObject *args)
PyObject* GroupExtensionPy::addObject(PyObject* args)
{
PyObject *object;
if (!PyArg_ParseTuple(args, "O!", &(DocumentObjectPy::Type), &object))
PyObject* object;
if (!PyArg_ParseTuple(args, "O!", &(DocumentObjectPy::Type), &object)) {
return nullptr;
}
DocumentObjectPy* docObj = static_cast<DocumentObjectPy*>(object);
if (!docObj->getDocumentObjectPtr() || !docObj->getDocumentObjectPtr()->isAttachedToDocument()) {
if (!docObj->getDocumentObjectPtr()
|| !docObj->getDocumentObjectPtr()->isAttachedToDocument()) {
PyErr_SetString(Base::PyExc_FC_GeneralError, "Cannot add an invalid object");
return nullptr;
}
if (docObj->getDocumentObjectPtr()->getDocument() != getGroupExtensionPtr()->getExtendedObject()->getDocument()) {
PyErr_SetString(Base::PyExc_FC_GeneralError, "Cannot add an object from another document to this group");
if (docObj->getDocumentObjectPtr()->getDocument()
!= getGroupExtensionPtr()->getExtendedObject()->getDocument()) {
PyErr_SetString(Base::PyExc_FC_GeneralError,
"Cannot add an object from another document to this group");
return nullptr;
}
if (docObj->getDocumentObjectPtr() == this->getGroupExtensionPtr()->getExtendedObject()) {
@@ -76,110 +81,123 @@ PyObject* GroupExtensionPy::addObject(PyObject *args)
return nullptr;
}
if (docObj->getDocumentObjectPtr()->hasExtension(GroupExtension::getExtensionClassTypeId())) {
App::GroupExtension* docGrp = docObj->getDocumentObjectPtr()->getExtensionByType<GroupExtension>();
App::GroupExtension* docGrp =
docObj->getDocumentObjectPtr()->getExtensionByType<GroupExtension>();
if (docGrp->hasObject(getGroupExtensionPtr()->getExtendedObject())) {
PyErr_SetString(Base::PyExc_FC_GeneralError, "Cannot add a group object to a child group");
PyErr_SetString(Base::PyExc_FC_GeneralError,
"Cannot add a group object to a child group");
return nullptr;
}
}
GroupExtension* grp = getGroupExtensionPtr();
auto vec = grp->addObject(docObj->getDocumentObjectPtr());
auto vec = grp->addObject(docObj->getDocumentObjectPtr());
Py::List list;
for (App::DocumentObject* obj : vec)
for (App::DocumentObject* obj : vec) {
list.append(Py::asObject(obj->getPyObject()));
}
return Py::new_reference_to(list);
}
PyObject* GroupExtensionPy::addObjects(PyObject *args) {
PyObject *object;
if (!PyArg_ParseTuple(args, "O", &object))
return nullptr;
if (PyTuple_Check(object) || PyList_Check(object)) {
Py::Sequence list(object);
Py::Sequence::size_type size = list.size();
std::vector<DocumentObject*> values;
values.resize(size);
for (Py::Sequence::size_type i = 0; i < size; i++) {
Py::Object item = list[i];
if (!PyObject_TypeCheck(*item, &(DocumentObjectPy::Type))) {
std::string error = std::string("type in list must be 'DocumentObject', not ");
error += (*item)->ob_type->tp_name;
throw Base::TypeError(error);
}
values[i] = static_cast<DocumentObjectPy*>(*item)->getDocumentObjectPtr();
}
GroupExtension* grp = getGroupExtensionPtr();
auto vec = grp->addObjects(values);
Py::List result;
for (App::DocumentObject* obj : vec)
result.append(Py::asObject(obj->getPyObject()));
return Py::new_reference_to(result);
}
std::string error = std::string("type must be list of 'DocumentObject', not ");
error += object->ob_type->tp_name;
throw Base::TypeError(error);
}
PyObject* GroupExtensionPy::setObjects(PyObject *args) {
PyObject *object;
if (!PyArg_ParseTuple(args, "O", &object))
return nullptr;
if (PyTuple_Check(object) || PyList_Check(object)) {
Py::Sequence list(object);
Py::Sequence::size_type size = list.size();
std::vector<DocumentObject*> values;
values.resize(size);
for (Py::Sequence::size_type i = 0; i < size; i++) {
Py::Object item = list[i];
if (!PyObject_TypeCheck(*item, &(DocumentObjectPy::Type))) {
std::string error = std::string("type in list must be 'DocumentObject', not ");
error += (*item)->ob_type->tp_name;
throw Base::TypeError(error);
}
values[i] = static_cast<DocumentObjectPy*>(*item)->getDocumentObjectPtr();
}
GroupExtension* grp = getGroupExtensionPtr();
auto vec = grp->setObjects(values);
Py::List result;
for (App::DocumentObject* obj : vec)
result.append(Py::asObject(obj->getPyObject()));
return Py::new_reference_to(result);
}
std::string error = std::string("type must be list of 'DocumentObject', not ");
error += object->ob_type->tp_name;
throw Base::TypeError(error);
}
PyObject* GroupExtensionPy::removeObject(PyObject *args)
PyObject* GroupExtensionPy::addObjects(PyObject* args)
{
PyObject *object;
if (!PyArg_ParseTuple(args, "O!", &(DocumentObjectPy::Type), &object))
PyObject* object;
if (!PyArg_ParseTuple(args, "O", &object)) {
return nullptr;
}
if (PyTuple_Check(object) || PyList_Check(object)) {
Py::Sequence list(object);
Py::Sequence::size_type size = list.size();
std::vector<DocumentObject*> values;
values.resize(size);
for (Py::Sequence::size_type i = 0; i < size; i++) {
Py::Object item = list[i];
if (!PyObject_TypeCheck(*item, &(DocumentObjectPy::Type))) {
std::string error = std::string("type in list must be 'DocumentObject', not ");
error += (*item)->ob_type->tp_name;
throw Base::TypeError(error);
}
values[i] = static_cast<DocumentObjectPy*>(*item)->getDocumentObjectPtr();
}
GroupExtension* grp = getGroupExtensionPtr();
auto vec = grp->addObjects(values);
Py::List result;
for (App::DocumentObject* obj : vec) {
result.append(Py::asObject(obj->getPyObject()));
}
return Py::new_reference_to(result);
}
std::string error = std::string("type must be list of 'DocumentObject', not ");
error += object->ob_type->tp_name;
throw Base::TypeError(error);
}
PyObject* GroupExtensionPy::setObjects(PyObject* args)
{
PyObject* object;
if (!PyArg_ParseTuple(args, "O", &object)) {
return nullptr;
}
if (PyTuple_Check(object) || PyList_Check(object)) {
Py::Sequence list(object);
Py::Sequence::size_type size = list.size();
std::vector<DocumentObject*> values;
values.resize(size);
for (Py::Sequence::size_type i = 0; i < size; i++) {
Py::Object item = list[i];
if (!PyObject_TypeCheck(*item, &(DocumentObjectPy::Type))) {
std::string error = std::string("type in list must be 'DocumentObject', not ");
error += (*item)->ob_type->tp_name;
throw Base::TypeError(error);
}
values[i] = static_cast<DocumentObjectPy*>(*item)->getDocumentObjectPtr();
}
GroupExtension* grp = getGroupExtensionPtr();
auto vec = grp->setObjects(values);
Py::List result;
for (App::DocumentObject* obj : vec) {
result.append(Py::asObject(obj->getPyObject()));
}
return Py::new_reference_to(result);
}
std::string error = std::string("type must be list of 'DocumentObject', not ");
error += object->ob_type->tp_name;
throw Base::TypeError(error);
}
PyObject* GroupExtensionPy::removeObject(PyObject* args)
{
PyObject* object;
if (!PyArg_ParseTuple(args, "O!", &(DocumentObjectPy::Type), &object)) {
return nullptr;
}
DocumentObjectPy* docObj = static_cast<DocumentObjectPy*>(object);
if (!docObj->getDocumentObjectPtr() || !docObj->getDocumentObjectPtr()->isAttachedToDocument()) {
if (!docObj->getDocumentObjectPtr()
|| !docObj->getDocumentObjectPtr()->isAttachedToDocument()) {
PyErr_SetString(Base::PyExc_FC_GeneralError, "Cannot remove an invalid object");
return nullptr;
}
if (docObj->getDocumentObjectPtr()->getDocument() != getGroupExtensionPtr()->getExtendedObject()->getDocument()) {
PyErr_SetString(Base::PyExc_FC_GeneralError, "Cannot remove an object from another document from this group");
if (docObj->getDocumentObjectPtr()->getDocument()
!= getGroupExtensionPtr()->getExtendedObject()->getDocument()) {
PyErr_SetString(Base::PyExc_FC_GeneralError,
"Cannot remove an object from another document from this group");
return nullptr;
}
@@ -187,18 +205,21 @@ PyObject* GroupExtensionPy::removeObject(PyObject *args)
auto vec = grp->removeObject(docObj->getDocumentObjectPtr());
Py::List list;
for (App::DocumentObject* obj : vec)
for (App::DocumentObject* obj : vec) {
list.append(Py::asObject(obj->getPyObject()));
}
return Py::new_reference_to(list);
}
PyObject* GroupExtensionPy::removeObjects(PyObject *args) {
PyObject* GroupExtensionPy::removeObjects(PyObject* args)
{
PyObject *object;
if (!PyArg_ParseTuple(args, "O", &object))
PyObject* object;
if (!PyArg_ParseTuple(args, "O", &object)) {
return nullptr;
}
if (PyTuple_Check(object) || PyList_Check(object)) {
Py::Sequence list(object);
Py::Sequence::size_type size = list.size();
@@ -217,10 +238,11 @@ PyObject* GroupExtensionPy::removeObjects(PyObject *args) {
}
GroupExtension* grp = getGroupExtensionPtr();
auto vec = grp->removeObjects(values);
auto vec = grp->removeObjects(values);
Py::List result;
for (App::DocumentObject* obj : vec)
for (App::DocumentObject* obj : vec) {
result.append(Py::asObject(obj->getPyObject()));
}
return Py::new_reference_to(result);
}
@@ -230,44 +252,56 @@ PyObject* GroupExtensionPy::removeObjects(PyObject *args) {
throw Base::TypeError(error);
}
PyObject* GroupExtensionPy::removeObjectsFromDocument(PyObject *args)
PyObject* GroupExtensionPy::removeObjectsFromDocument(PyObject* args)
{
if (!PyArg_ParseTuple(args, ""))
if (!PyArg_ParseTuple(args, "")) {
return nullptr;
}
getGroupExtensionPtr()->removeObjectsFromDocument();
Py_Return;
}
PyObject* GroupExtensionPy::getObject(PyObject *args)
PyObject* GroupExtensionPy::getObject(PyObject* args)
{
char* pcName;
if (!PyArg_ParseTuple(args, "s", &pcName))
if (!PyArg_ParseTuple(args, "s", &pcName)) {
return nullptr;
}
DocumentObject* obj = getGroupExtensionPtr()->getObject(pcName);
if ( obj ) {
if (obj) {
return obj->getPyObject();
} else {
}
else {
Py_Return;
}
}
PyObject* GroupExtensionPy::hasObject(PyObject *args)
PyObject* GroupExtensionPy::hasObject(PyObject* args)
{
PyObject *object;
PyObject *recursivePy = Py_False;
if (!PyArg_ParseTuple(args, "O!|O!", &(DocumentObjectPy::Type), &object, &PyBool_Type, &recursivePy))
PyObject* object;
PyObject* recursivePy = Py_False;
if (!PyArg_ParseTuple(args,
"O!|O!",
&(DocumentObjectPy::Type),
&object,
&PyBool_Type,
&recursivePy)) {
return nullptr;
}
DocumentObjectPy* docObj = static_cast<DocumentObjectPy*>(object);
bool recursive = Base::asBoolean(recursivePy);
if (!docObj->getDocumentObjectPtr() || !docObj->getDocumentObjectPtr()->isAttachedToDocument()) {
if (!docObj->getDocumentObjectPtr()
|| !docObj->getDocumentObjectPtr()->isAttachedToDocument()) {
PyErr_SetString(Base::PyExc_FC_GeneralError, "Cannot check an invalid object");
return nullptr;
}
if (docObj->getDocumentObjectPtr()->getDocument() != getGroupExtensionPtr()->getExtendedObject()->getDocument()) {
PyErr_SetString(Base::PyExc_FC_GeneralError, "Cannot check an object from another document with this group");
if (docObj->getDocumentObjectPtr()->getDocument()
!= getGroupExtensionPtr()->getExtendedObject()->getDocument()) {
PyErr_SetString(Base::PyExc_FC_GeneralError,
"Cannot check an object from another document with this group");
return nullptr;
}
@@ -275,7 +309,7 @@ PyObject* GroupExtensionPy::hasObject(PyObject *args)
return PyBool_FromLong(v ? 1 : 0);
}
PyObject *GroupExtensionPy::getCustomAttributes(const char* /*attr*/) const
PyObject* GroupExtensionPy::getCustomAttributes(const char* /*attr*/) const
{
return nullptr;
}

View File

@@ -32,9 +32,9 @@ PROPERTY_SOURCE(Image::ImagePlane, App::GeoFeature)
ImagePlane::ImagePlane()
{
ADD_PROPERTY_TYPE( ImageFile,(nullptr) , "ImagePlane",App::Prop_None,"File of the image");
ADD_PROPERTY_TYPE( XSize, (100), "ImagePlane",App::Prop_None,"Size of a pixel in X");
ADD_PROPERTY_TYPE( YSize, (100), "ImagePlane",App::Prop_None,"Size of a pixel in Y");
ADD_PROPERTY_TYPE(ImageFile, (nullptr), "ImagePlane", App::Prop_None, "File of the image");
ADD_PROPERTY_TYPE(XSize, (100), "ImagePlane", App::Prop_None, "Size of a pixel in X");
ADD_PROPERTY_TYPE(YSize, (100), "ImagePlane", App::Prop_None, "Size of a pixel in Y");
}
int ImagePlane::getXSizeInPixel()

View File

@@ -30,7 +30,7 @@
namespace Image
{
class AppExport ImagePlane : public App::GeoFeature
class AppExport ImagePlane: public App::GeoFeature
{
PROPERTY_HEADER_WITH_OVERRIDE(Image::ImagePlane);
@@ -40,24 +40,25 @@ public:
~ImagePlane() override = default;
App::PropertyFileIncluded ImageFile;
App::PropertyLength XSize;
App::PropertyLength YSize;
App::PropertyLength XSize;
App::PropertyLength YSize;
int getXSizeInPixel();
int getYSizeInPixel();
void setXSizeInPixel(int);
void setYSizeInPixel(int);
double XPixelsPerMeter{1000.0};
double YPixelsPerMeter{1000.0};
double XPixelsPerMeter {1000.0};
double YPixelsPerMeter {1000.0};
/// returns the type name of the ViewProvider
const char* getViewProviderName() const override {
const char* getViewProviderName() const override
{
return "Gui::ViewProviderImagePlane";
}
};
} //namespace Image
} // namespace Image
#endif // App_ImagePlane_H
#endif // App_ImagePlane_H

View File

@@ -26,8 +26,8 @@
#include "PreCompiled.h"
#ifndef _PreComp_
# include <cstdlib>
# include <unordered_set>
#include <cstdlib>
#include <unordered_set>
#endif
#include "IndexedName.h"
@@ -37,17 +37,17 @@ using namespace Data;
/// Check whether the input character is an underscore or an ASCII letter a-Z or A-Z
inline bool isInvalidChar(char test)
{
return test != '_' && (test < 'a' || test > 'z' ) && (test < 'A' || test > 'Z');
return test != '_' && (test < 'a' || test > 'z') && (test < 'A' || test > 'Z');
}
/// Get the integer suffix of name. Returns a tuple of (suffix, suffixPosition). Calling code
/// should check to ensure that suffixPosition is not equal to nameLength (in which case there was no
/// suffix).
/// should check to ensure that suffixPosition is not equal to nameLength (in which case there was
/// no suffix).
///
/// \param name The name to check
/// \param nameLength The length of the string in name
/// \returns An integer pair of the suffix itself and the position of that suffix in name
std::pair<int,int> getIntegerSuffix(const char *name, int nameLength)
std::pair<int, int> getIntegerSuffix(const char* name, int nameLength)
{
int suffixPosition {nameLength - 1};
@@ -68,11 +68,10 @@ std::pair<int,int> getIntegerSuffix(const char *name, int nameLength)
return std::make_pair(suffix, suffixPosition);
}
void IndexedName::set(
const char* name,
int length,
const std::vector<const char*>& allowedNames,
bool allowOthers)
void IndexedName::set(const char* name,
int length,
const std::vector<const char*>& allowedNames,
bool allowOthers)
{
// Storage for names that we weren't given external storage for
static std::unordered_set<ByteArray, ByteArrayHasher> NameSet;
@@ -90,22 +89,22 @@ void IndexedName::set(
// underscore. If any other character appears, reject the entire string.
// When we support C++20 we can use std::span<> to eliminate the clang-tidy warning
// NOLINTNEXTLINE cppcoreguidelines-pro-bounds-pointer-arithmetic
if (std::any_of(name, name+suffixPosition, isInvalidChar)) {
if (std::any_of(name, name + suffixPosition, isInvalidChar)) {
this->type = "";
return;
}
// If a list of allowedNames was provided, see if our set name matches one of those allowedNames: if it
// does, reference that memory location and return.
for (const auto *typeName : allowedNames) {
// If a list of allowedNames was provided, see if our set name matches one of those
// allowedNames: if it does, reference that memory location and return.
for (const auto* typeName : allowedNames) {
if (std::strncmp(name, typeName, suffixPosition) == 0) {
this->type = typeName;
return;
}
}
// If the type was NOT in the list of allowedNames, but the caller has set the allowOthers flag to
// true, then add the new type to the static NameSet (if it is not already there).
// If the type was NOT in the list of allowedNames, but the caller has set the allowOthers flag
// to true, then add the new type to the static NameSet (if it is not already there).
if (allowOthers) {
auto res = NameSet.insert(ByteArray(QByteArray::fromRawData(name, suffixPosition)));
if (res.second /*The insert succeeded (the type was new)*/) {

View File

@@ -40,21 +40,21 @@
namespace Data
{
/// The IndexedName class provides a very memory-efficient data structure to hold a name and an index
/// value, and to perform various comparisons and validations of those values. The name must only
/// consist of upper- and lower-case ASCII characters and the underscore ('_') character. The index
/// must be a positive integer. The string representation of this IndexedName is the name followed by
/// the index, with no spaces between: an IndexedName may be constructed from this string. For
/// example "EDGE1" or "FACE345" might be the names of elements that use an IndexedName. If there is
/// then an "EDGE2", only a pointer to the original stored name "EDGE" is retained.
/// The IndexedName class provides a very memory-efficient data structure to hold a name and an
/// index value, and to perform various comparisons and validations of those values. The name must
/// only consist of upper- and lower-case ASCII characters and the underscore ('_') character. The
/// index must be a positive integer. The string representation of this IndexedName is the name
/// followed by the index, with no spaces between: an IndexedName may be constructed from this
/// string. For example "EDGE1" or "FACE345" might be the names of elements that use an IndexedName.
/// If there is then an "EDGE2", only a pointer to the original stored name "EDGE" is retained.
///
/// The memory efficiency of the class comes from re-using the same character storage for names that
/// match, while retaining their differing indices. This is achieved by either using user-provided
/// const char * names (provided as a list of typeNames and presumed to never be deallocated), or by
/// maintaining an internal list of names that have been used before, and can be re-used later.
class AppExport IndexedName {
class AppExport IndexedName
{
public:
/// Construct from a name and an optional index. If the name contains an index it is read, but
/// is used as the index *only* if _index parameter is unset. If the _index parameter is given
/// it overrides any trailing integer in the name. Index must be positive, and name must contain
@@ -64,7 +64,7 @@ public:
/// \param name The new name - ASCII letters and underscores only, with optional integer suffix.
/// This memory will be copied into a new internal storage location and need not be persistent.
/// \param _index The new index - if provided, it overrides any suffix provided by name
explicit IndexedName(const char *name = nullptr, int _index = 0)
explicit IndexedName(const char* name = nullptr, int _index = 0)
: index(0)
{
assert(_index >= 0);
@@ -93,9 +93,11 @@ public:
/// \param allowOthers Whether a name not in allowedTypeNames is permitted. If true (the
/// default) then a name not in allowedTypeNames is added to a static internal storage vector
/// so that it can be re-used later without additional memory allocation.
IndexedName(const char *name,
const std::vector<const char*> & allowedTypeNames,
bool allowOthers=true) : type(""), index(0)
IndexedName(const char* name,
const std::vector<const char*>& allowedTypeNames,
bool allowOthers = true)
: type("")
, index(0)
{
set(name, -1, allowedTypeNames, allowOthers);
}
@@ -105,7 +107,9 @@ public:
/// is made.
///
/// \param data The QByteArray to copy the data from
explicit IndexedName(const QByteArray & data) : type(""), index(0)
explicit IndexedName(const QByteArray& data)
: type("")
, index(0)
{
set(data.constData(), data.size());
}
@@ -117,8 +121,9 @@ public:
/// \param name The name of the object. This memory is NOT copied and must be persistent.
/// \param index A positive, non-zero integer
/// \return An IndexedName with the given name and index, re-using the existing memory for name
static IndexedName fromConst(const char *name, int index) {
assert (index >= 0);
static IndexedName fromConst(const char* name, int index)
{
assert(index >= 0);
IndexedName res;
res.type = name;
res.index = index;
@@ -130,7 +135,7 @@ public:
///
/// \param buffer A (possibly non-empty) string buffer to append the name to.
/// \return A const char pointer to the name we appended to the buffer.
const char * appendToStringBuffer(std::string & buffer) const
const char* appendToStringBuffer(std::string& buffer) const
{
// Note! buffer is not cleared on purpose.
std::size_t offset = buffer.size();
@@ -153,7 +158,7 @@ public:
/// An indexedName is represented as the simple concatenation of the name and its index, e.g.
/// "EDGE1" or "FACE42".
friend std::ostream & operator<<(std::ostream & stream, const IndexedName & indexedName)
friend std::ostream& operator<<(std::ostream& stream, const IndexedName& indexedName)
{
stream << indexedName.type;
if (indexedName.index > 0) {
@@ -163,15 +168,14 @@ public:
}
/// True only if both the name and index compare exactly equal.
bool operator==(const IndexedName & other) const
bool operator==(const IndexedName& other) const
{
return this->index == other.index
&& (this->type == other.type
|| std::strcmp(this->type, other.type)==0);
&& (this->type == other.type || std::strcmp(this->type, other.type) == 0);
}
/// Increments the index by the given offset. Does not affect the text part of the name.
IndexedName & operator+=(int offset)
IndexedName& operator+=(int offset)
{
this->index += offset;
assert(this->index >= 0);
@@ -179,7 +183,7 @@ public:
}
/// Pre-increment operator: increases the index of this element by one.
IndexedName & operator++()
IndexedName& operator++()
{
++this->index;
return *this;
@@ -187,7 +191,7 @@ public:
/// Pre-decrement operator: decreases the index of this element by one. Must not make the index
/// negative (only checked when compiled in debug mode).
IndexedName & operator--()
IndexedName& operator--()
{
--this->index;
assert(this->index >= 0);
@@ -195,13 +199,13 @@ public:
}
/// True if either the name or the index compare not equal.
bool operator!=(const IndexedName & other) const
bool operator!=(const IndexedName& other) const
{
return !(this->operator==(other));
}
/// Equivalent to C++20's operator <=>
int compare(const IndexedName & other) const
int compare(const IndexedName& other) const
{
int res = std::strcmp(this->type, other.type);
if (res != 0) {
@@ -218,7 +222,7 @@ public:
/// Provided to enable sorting operations: the comparison is first lexicographical for the text
/// element of the names, then numerical for the indices.
bool operator<(const IndexedName & other) const
bool operator<(const IndexedName& other) const
{
return compare(other) < 0;
}
@@ -235,25 +239,41 @@ public:
}
/// Get a pointer to text part of the name - does NOT make a copy, returns direct memory access
const char * getType() const { return this->type; }
const char* getType() const
{
return this->type;
}
/// Get the numerical part of the name
int getIndex() const { return this->index; }
int getIndex() const
{
return this->index;
}
/// Set the numerical part of the name (note that there is no equivalent function to allow
/// changing the text part of the name, which is immutable once created).
///
/// \param input The new index. Must be a positive non-zero integer
void setIndex(int input) { assert(input>=0); this->index = input; }
void setIndex(int input)
{
assert(input >= 0);
this->index = input;
}
/// A name is considered "null" if its text component is an empty string.
// When we support C++20 we can use std::span<> to eliminate the clang-tidy warning
// NOLINTNEXTLINE cppcoreguidelines-pro-bounds-pointer-arithmetic
bool isNull() const { return this->type[0] == '\0'; }
bool isNull() const
{
return this->type[0] == '\0';
}
/// Boolean conversion provides the opposite of isNull(), yielding true when the text part of
/// the name is NOT the empty string.
explicit operator bool() const { return !isNull(); }
explicit operator bool() const
{
return !isNull();
}
protected:
/// Apply the IndexedName rules and either store the characters of a new type or a reference to
@@ -268,13 +288,13 @@ protected:
/// \param allowOthers If true (the default), then if name is not in allowedNames it is allowed,
/// and it is added to internal storage (making a copy of the name if this is its first
/// occurrence).
void set(const char *name,
void set(const char* name,
int length = -1,
const std::vector<const char *> & allowedNames = {},
const std::vector<const char*>& allowedNames = {},
bool allowOthers = true);
private:
const char * type;
const char* type;
int index;
};
@@ -285,13 +305,13 @@ private:
struct ByteArray
{
explicit ByteArray(QByteArray other)
:bytes(std::move(other))
: bytes(std::move(other))
{}
ByteArray(const ByteArray& other) = default;
ByteArray(ByteArray&& other) noexcept
:bytes(std::move(other.bytes))
: bytes(std::move(other.bytes))
{}
~ByteArray() = default;
@@ -304,17 +324,19 @@ struct ByteArray
bytes = copy;
}
bool operator==(const ByteArray& other) const {
bool operator==(const ByteArray& other) const
{
return bytes == other.bytes;
}
ByteArray &operator=(const ByteArray & other) {
ByteArray& operator=(const ByteArray& other)
{
bytes.clear();
bytes.append(other.bytes.constData(), other.bytes.size());
return *this;
}
ByteArray &operator= (ByteArray&& other) noexcept
ByteArray& operator=(ByteArray&& other) noexcept
{
bytes = std::move(other.bytes);
return *this;
@@ -337,6 +359,6 @@ struct ByteArrayHasher
}
};
}
} // namespace Data
#endif // APP_INDEXEDNAME_H
#endif // APP_INDEXEDNAME_H

View File

@@ -32,10 +32,10 @@ using namespace App;
PROPERTY_SOURCE(App::InventorObject, App::GeoFeature)
InventorObject::InventorObject()
InventorObject::InventorObject()
{
ADD_PROPERTY_TYPE(Buffer,(""),"",Prop_None,"String buffer with a scene graph");
ADD_PROPERTY_TYPE(FileName,(""),"",Prop_None,"Path to an Inventor file");
ADD_PROPERTY_TYPE(Buffer, (""), "", Prop_None, "String buffer with a scene graph");
ADD_PROPERTY_TYPE(FileName, (""), "", Prop_None, "Path to an Inventor file");
}
InventorObject::~InventorObject() = default;
@@ -45,11 +45,11 @@ short InventorObject::mustExecute() const
return 0;
}
PyObject *InventorObject::getPyObject()
PyObject* InventorObject::getPyObject()
{
if (PythonObject.is(Py::_None())){
if (PythonObject.is(Py::_None())) {
// ref counter is set to 1
PythonObject = Py::Object(new DocumentObjectPy(this),true);
PythonObject = Py::Object(new DocumentObjectPy(this), true);
}
return Py::new_reference_to(PythonObject);
return Py::new_reference_to(PythonObject);
}

View File

@@ -31,7 +31,7 @@
namespace App
{
class AppExport InventorObject : public GeoFeature
class AppExport InventorObject: public GeoFeature
{
PROPERTY_HEADER_WITH_OVERRIDE(App::InventorObject);
@@ -41,20 +41,22 @@ public:
~InventorObject() override;
/// returns the type name of the ViewProvider
const char* getViewProviderName() const override {
const char* getViewProviderName() const override
{
return "Gui::ViewProviderInventorObject";
}
DocumentObjectExecReturn *execute() override {
DocumentObjectExecReturn* execute() override
{
return DocumentObject::StdReturn;
}
short mustExecute() const override;
PyObject *getPyObject() override;
PyObject* getPyObject() override;
PropertyString Buffer;
PropertyString FileName;
};
} //namespace App
} // namespace App
#endif // APP_INVENTOROBJECT_H
#endif // APP_INVENTOROBJECT_H

View File

@@ -75,6 +75,6 @@ int constexpr findLicense(const char* identifier)
}
return -1;
}
}// namespace App
} // namespace App
#endif// APP_LICENSE_H
#endif // APP_LICENSE_H

View File

@@ -24,8 +24,8 @@
#include "PreCompiled.h"
#ifndef _PreComp_
# include <cstdlib>
# include <unordered_set>
#include <cstdlib>
#include <unordered_set>
#endif
#include "DocumentObject.h"
@@ -164,9 +164,12 @@ bool ElementNameComparator::operator()(const MappedName& leftName,
return leftName.size() < rightName.size();
}
HistoryItem::HistoryItem(App::DocumentObject *obj, const Data::MappedName &name)
:obj(obj),tag(0),element(name)
HistoryItem::HistoryItem(App::DocumentObject* obj, const Data::MappedName& name)
: obj(obj)
, tag(0)
, element(name)
{
if(obj)
if (obj) {
tag = obj->getID();
}
}

View File

@@ -46,13 +46,13 @@ struct AppExport MappedElement
MappedElement() = default;
MappedElement(const IndexedName& idx, MappedName n)
: index(idx),
name(std::move(n))
: index(idx)
, name(std::move(n))
{}
MappedElement(MappedName n, const IndexedName& idx)
: index(idx),
name(std::move(n))
: index(idx)
, name(std::move(n))
{}
~MappedElement() = default;
@@ -60,8 +60,8 @@ struct AppExport MappedElement
MappedElement(const MappedElement& other) = default;
MappedElement(MappedElement&& other) noexcept
: index(other.index),
name(std::move(other.name))
: index(other.index)
, name(std::move(other.name))
{}
MappedElement& operator=(MappedElement&& other) noexcept
@@ -99,16 +99,18 @@ struct AppExport MappedElement
}
};
struct AppExport HistoryItem {
App::DocumentObject *obj;
struct AppExport HistoryItem
{
App::DocumentObject* obj;
long tag;
Data::MappedName element;
Data::IndexedName index;
std::vector<Data::MappedName> intermediates;
HistoryItem(App::DocumentObject *obj, const Data::MappedName &name);
HistoryItem(App::DocumentObject* obj, const Data::MappedName& name);
};
struct AppExport ElementNameComparator {
struct AppExport ElementNameComparator
{
/** Comparison function to make topo name more stable
*
* The sorting decomposes the name into either of the following two forms
@@ -121,10 +123,10 @@ struct AppExport ElementNameComparator {
* The reason for this is to prevent names with bigger digits (which usually means
* they come later in history) from coming earlier when sorting.
*/
bool operator()(const MappedName & leftName, const MappedName & rightName) const;
bool operator()(const MappedName& leftName, const MappedName& rightName) const;
};
}// namespace Data
} // namespace Data
#endif// APP_MAPPED_ELEMENT_H
#endif // APP_MAPPED_ELEMENT_H

View File

@@ -23,7 +23,7 @@
#include "PreCompiled.h"
#ifndef _PreComp_
# include <unordered_set>
#include <unordered_set>
#endif
#include "MappedName.h"
@@ -34,13 +34,15 @@
#include <boost/iostreams/stream.hpp>
FC_LOG_LEVEL_INIT("MappedName", true, 2);// NOLINT
FC_LOG_LEVEL_INIT("MappedName", true, 2); // NOLINT
namespace Data {
namespace Data
{
void MappedName::compact() const
{
auto self = const_cast<MappedName*>(this); //FIXME this is a workaround for a single call in ElementMap::addName()
auto self = const_cast<MappedName*>(
this); // FIXME this is a workaround for a single call in ElementMap::addName()
if (this->raw) {
self->data = QByteArray(self->data.constData(), self->data.size());
@@ -49,8 +51,12 @@ void MappedName::compact() const
}
int MappedName::findTagInElementName(long* tagOut, int* lenOut, std::string* postfixOut,
char* typeOut, bool negative, bool recursive) const
int MappedName::findTagInElementName(long* tagOut,
int* lenOut,
std::string* postfixOut,
char* typeOut,
bool negative,
bool recursive) const
{
bool hex = true;
int pos = this->rfind(POSTFIX_TAG);
@@ -61,7 +67,7 @@ int MappedName::findTagInElementName(long* tagOut, int* lenOut, std::string* pos
// |
// pos
if(pos < 0) {
if (pos < 0) {
pos = this->rfind(POSTFIX_DECIMAL_TAG);
if (pos < 0) {
return -1;
@@ -77,7 +83,7 @@ int MappedName::findTagInElementName(long* tagOut, int* lenOut, std::string* pos
char eof = 0;
int size {0};
const char * nameAsChars = this->toConstString(offset, size);
const char* nameAsChars = this->toConstString(offset, size);
// check if the number followed by the tagPosfix is negative
bool isNegative = (nameAsChars[0] == '-');
@@ -89,7 +95,8 @@ int MappedName::findTagInElementName(long* tagOut, int* lenOut, std::string* pos
if (!hex) {
// no hex is an older version of the encoding scheme
iss >> _tag >> sep;
} else {
}
else {
// The purpose of tagOut postfixOut is to encode one model operation. The
// 'tagOut' field is used to record the own object ID of that model shape,
// and the 'lenOut' field indicates the length of the operation codes
@@ -148,13 +155,13 @@ int MappedName::findTagInElementName(long* tagOut, int* lenOut, std::string* pos
}
if (hex) {
if (pos-_len < 0) {
if (pos - _len < 0) {
return -1;
}
if ((_len != 0) && recursive && (tagOut || lenOut)) {
// in case of recursive tagOut postfixOut (used by hierarchy element
// map), look for any embedded tagOut postfixOut
int next = MappedName::fromRawData(*this, pos-_len, _len).rfind(POSTFIX_TAG);
int next = MappedName::fromRawData(*this, pos - _len, _len).rfind(POSTFIX_TAG);
if (next >= 0) {
next += pos - _len;
// #94;:G0;XTR;:H19:8,F;:H1a,F;BND:-1:0;:H1b:10,F
@@ -174,7 +181,7 @@ int MappedName::findTagInElementName(long* tagOut, int* lenOut, std::string* pos
.find(ELEMENT_MAP_PREFIX);
}
if (end >= 0) {
end += next+1;
end += next + 1;
// #94;:G0;XTR;:H19:8,F;:H1a,F;BND:-1:0;:H1b:10,F
// ^
// |
@@ -183,7 +190,8 @@ int MappedName::findTagInElementName(long* tagOut, int* lenOut, std::string* pos
// #94;:G0;XTR;:H19:8,F;:H1a,F;BND:-1:0;:H1b:10,F
// | |
// -- lenOut --
} else {
}
else {
_len = 0;
}
}
@@ -196,28 +204,28 @@ int MappedName::findTagInElementName(long* tagOut, int* lenOut, std::string* pos
// ----------- lenOut -----------
_len = pos - _len;
}
if(typeOut) {
if (typeOut) {
*typeOut = tp;
}
if(tagOut) {
if (tagOut) {
if (_tag == 0 && recursive) {
return MappedName(*this, 0, _len)
.findTagInElementName(tagOut, lenOut, postfixOut, typeOut, negative);
}
if(_tag>0 || negative) {
if (_tag > 0 || negative) {
*tagOut = _tag;
}
else {
*tagOut = -_tag;
}
}
if(lenOut) {
if (lenOut) {
*lenOut = _len;
}
if(postfixOut) {
if (postfixOut) {
*postfixOut = this->toString(pos);
}
return pos;
}
}
} // namespace Data

View File

@@ -415,7 +415,7 @@ public:
}
if (startPosition < other.data.size())// if starting inside data
if (startPosition < other.data.size()) // if starting inside data
{
int count = size;
// make sure count doesn't exceed data size and end up in postfix
@@ -436,7 +436,7 @@ public:
startPosition = 0;
size -= count;
}
else// else starting inside postfix
else // else starting inside postfix
{
startPosition -= other.data.size();
}
@@ -879,7 +879,8 @@ public:
return false;
}
return startsWith(
QByteArray::fromRawData(searchTarget, static_cast<int>(qstrlen(searchTarget))), offset);
QByteArray::fromRawData(searchTarget, static_cast<int>(qstrlen(searchTarget))),
offset);
}
/// Returns true if this MappedName starts with the search target. If there is a postfix, only
@@ -902,15 +903,18 @@ public:
/// \param lenOut: optional pointer to receive the length field after the tagOut field.
/// This gives the length of the previous hashed element name starting
/// from the beginning of the give element name.
/// \param postfixOut: optional pointer to receive the postfixOut starting at the found tagOut field.
/// \param typeOut: optional pointer to receive the element typeOut character
/// \param negative: return negative tagOut as it is. If disabled, then always return positive tagOut.
/// \param postfixOut: optional pointer to receive the postfixOut starting at the found tagOut
/// field. \param typeOut: optional pointer to receive the element typeOut character \param
/// negative: return negative tagOut as it is. If disabled, then always return positive tagOut.
/// Negative tagOut is sometimes used for element disambiguation.
/// \param recursive: recursively find the last non-zero tagOut
///
/// \return Return the end position of the tagOut field, or return -1 if not found.
int findTagInElementName(long* tagOut = nullptr, int* lenOut = nullptr, std::string* postfixOut = nullptr,
char* typeOut = nullptr, bool negative = false,
int findTagInElementName(long* tagOut = nullptr,
int* lenOut = nullptr,
std::string* postfixOut = nullptr,
char* typeOut = nullptr,
bool negative = false,
bool recursive = true) const;
/// Get a hash for this MappedName
@@ -1043,7 +1047,7 @@ struct MappedNameRef
// NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic)
}// namespace Data
} // namespace Data
#endif// APP_MAPPED_NAME_H
#endif // APP_MAPPED_NAME_H

View File

@@ -33,20 +33,22 @@ PROPERTY_SOURCE(App::MaterialObject, App::DocumentObject)
MaterialObject::MaterialObject()
{
ADD_PROPERTY_TYPE(Material,(),"Material",Prop_None,"Material key/value map");
ADD_PROPERTY_TYPE(Material, (), "Material", Prop_None, "Material key/value map");
}
// Python feature ---------------------------------------------------------
namespace App {
namespace App
{
/// @cond DOXERR
PROPERTY_SOURCE_TEMPLATE(App::MaterialObjectPython, App::MaterialObject)
template<> const char* App::MaterialObjectPython::getViewProviderName() const {
template<>
const char* App::MaterialObjectPython::getViewProviderName() const
{
return "Gui::ViewProviderMaterialObjectPython";
}
/// @endcond
// explicit template instantiation
template class AppExport FeaturePythonT<App::MaterialObject>;
}
} // namespace App

View File

@@ -31,7 +31,7 @@
namespace App
{
class AppExport MaterialObject : public DocumentObject
class AppExport MaterialObject: public DocumentObject
{
PROPERTY_HEADER_WITH_OVERRIDE(App::MaterialObject);
@@ -43,16 +43,16 @@ public:
/// returns the type name of the ViewProvider
const char* getViewProviderName() const override {
const char* getViewProviderName() const override
{
return "Gui::ViewProviderMaterialObject";
}
};
using MaterialObjectPython = App::FeaturePythonT<MaterialObject>;
} //namespace App
} // namespace App
#endif // APP_MaterialObject_H
#endif // APP_MaterialObject_H

View File

@@ -28,179 +28,208 @@
#include "MeasureManager.h"
namespace App {
namespace App
{
std::vector<MeasureHandler> MeasureManager::_mMeasureHandlers;
std::vector<MeasureType*> MeasureManager::_mMeasureTypes;
std::vector<MeasureHandler> MeasureManager::_mMeasureHandlers;
std::vector<MeasureType*> MeasureManager::_mMeasureTypes;
MeasureManager::MeasureManager()
{
// Constructor implementation
MeasureManager::MeasureManager()
{
// Constructor implementation
}
void MeasureManager::addMeasureHandler(const char* module, MeasureTypeMethod typeCb)
{
_mMeasureHandlers.emplace_back(MeasureHandler {module, typeCb});
}
bool MeasureManager::hasMeasureHandler(const char* module)
{
for (MeasureHandler& handler : _mMeasureHandlers) {
if (strcmp(handler.module.c_str(), module) == 0) {
return true;
}
}
return false;
}
MeasureHandler MeasureManager::getMeasureHandler(const char* module)
{
for (MeasureHandler handler : _mMeasureHandlers) {
if (!strcmp(handler.module.c_str(), module)) {
return handler;
}
}
MeasureHandler empty;
return empty;
}
void MeasureManager::addMeasureHandler(const char* module, MeasureTypeMethod typeCb) {
_mMeasureHandlers.emplace_back(MeasureHandler{module, typeCb});
MeasureHandler MeasureManager::getMeasureHandler(const App::MeasureSelectionItem& selectionItem)
{
auto objT = selectionItem.object;
// Resolve App::Link
App::DocumentObject* sub = objT.getSubObject();
if (sub->isDerivedFrom<App::Link>()) {
auto link = static_cast<App::Link*>(sub);
sub = link->getLinkedObject(true);
}
bool MeasureManager::hasMeasureHandler(const char* module) {
for(MeasureHandler& handler : _mMeasureHandlers) {
if (strcmp(handler.module.c_str(), module) == 0) {
return true;
const char* className = sub->getTypeId().getName();
std::string mod = Base::Type::getModuleName(className);
return getMeasureHandler(mod.c_str());
}
MeasureElementType
MeasureManager::getMeasureElementType(const App::MeasureSelectionItem& selectionItem)
{
auto handler = getMeasureHandler(selectionItem);
if (handler.module.empty()) {
return App::MeasureElementType::INVALID;
}
auto objT = selectionItem.object;
return handler.typeCb(objT.getObject(), objT.getSubName().c_str());
}
void MeasureManager::addMeasureType(MeasureType* measureType)
{
_mMeasureTypes.push_back(measureType);
}
void MeasureManager::addMeasureType(std::string id,
std::string label,
std::string measureObj,
MeasureValidateMethod validatorCb,
MeasurePrioritizeMethod prioritizeCb)
{
MeasureType* mType =
new MeasureType {id, label, measureObj, validatorCb, prioritizeCb, false, nullptr};
_mMeasureTypes.push_back(mType);
}
void MeasureManager::addMeasureType(const char* id,
const char* label,
const char* measureObj,
MeasureValidateMethod validatorCb,
MeasurePrioritizeMethod prioritizeCb)
{
addMeasureType(std::string(id),
std::string(label),
std::string(measureObj),
validatorCb,
prioritizeCb);
}
const std::vector<MeasureType*> MeasureManager::getMeasureTypes()
{
return _mMeasureTypes;
}
Py::Tuple MeasureManager::getSelectionPy(const App::MeasureSelection& selection)
{
// Convert selection to python list
Py::Tuple selectionPy(selection.size());
int i = 0;
for (auto it : selection) {
Py::Dict sel;
sel.setItem("object", Py::asObject(it.object.getObject()->getPyObject()));
sel.setItem("subName", Py::String(it.object.getSubName()));
sel.setItem("pickedPoint", Py::asObject(new Base::VectorPy(it.pickedPoint)));
selectionPy.setItem(i, sel);
i++;
}
return selectionPy;
}
std::vector<MeasureType*> MeasureManager::getValidMeasureTypes(App::MeasureSelection selection,
std::string mode)
{
Base::PyGILStateLocker lock;
// Convert selection to python list
Py::Tuple selectionPy = getSelectionPy(selection);
// Store valid measure types
std::vector<MeasureType*> validTypes;
std::pair<int, MeasureType>();
// Loop through measure types and check if they work with given selection
for (App::MeasureType* mType : getMeasureTypes()) {
if (mode != "" && mType->label != mode) {
continue;
}
if (mType->isPython) {
// Parse Python measure types
auto measurePyClass = Py::Object(mType->pythonClass);
Py::Tuple args(1);
args.setItem(0, selectionPy);
Py::Object isValid;
try {
isValid = measurePyClass.callMemberFunction(std::string("isValidSelection"), args);
}
catch (const Py::Exception&) {
Base::PyException e;
e.ReportException();
isValid = Py::False();
}
if (isValid.as_bool()) {
// Check priority
Py::Object isPriority;
try {
isPriority = measurePyClass.callMemberFunction("isPrioritySelection", args);
}
catch (const Py::Exception&) {
Base::PyException e;
e.ReportException();
isPriority = Py::False();
}
if (isPriority.as_bool()) {
validTypes.insert(validTypes.begin(), mType);
}
else {
validTypes.push_back(mType);
}
}
}
return false;
}
else {
// Parse c++ measure types
MeasureHandler MeasureManager::getMeasureHandler(const char* module) {
for(MeasureHandler handler : _mMeasureHandlers) {
if (!strcmp(handler.module.c_str(), module)) {
return handler;
}
}
MeasureHandler empty;
return empty;
}
MeasureHandler MeasureManager::getMeasureHandler(const App::MeasureSelectionItem& selectionItem) {
auto objT = selectionItem.object;
// Resolve App::Link
App::DocumentObject* sub = objT.getSubObject();
if (sub->isDerivedFrom<App::Link>()) {
auto link = static_cast<App::Link*>(sub);
sub = link->getLinkedObject(true);
}
const char* className = sub->getTypeId().getName();
std::string mod = Base::Type::getModuleName(className);
return getMeasureHandler(mod.c_str());
}
MeasureElementType MeasureManager::getMeasureElementType(const App::MeasureSelectionItem& selectionItem) {
auto handler = getMeasureHandler(selectionItem);
if (handler.module.empty()) {
return App::MeasureElementType::INVALID;
}
auto objT = selectionItem.object;
return handler.typeCb(objT.getObject(), objT.getSubName().c_str());
}
void MeasureManager::addMeasureType(MeasureType* measureType) {
_mMeasureTypes.push_back(measureType);
}
void MeasureManager::addMeasureType(std::string id, std::string label, std::string measureObj, MeasureValidateMethod validatorCb, MeasurePrioritizeMethod prioritizeCb) {
MeasureType* mType = new MeasureType{id, label, measureObj, validatorCb, prioritizeCb, false, nullptr};
_mMeasureTypes.push_back(mType);
}
void MeasureManager::addMeasureType(const char* id, const char* label, const char* measureObj, MeasureValidateMethod validatorCb, MeasurePrioritizeMethod prioritizeCb) {
addMeasureType(std::string(id), std::string(label), std::string(measureObj), validatorCb, prioritizeCb);
}
const std::vector<MeasureType*> MeasureManager::getMeasureTypes() {
return _mMeasureTypes;
}
Py::Tuple MeasureManager::getSelectionPy(const App::MeasureSelection& selection) {
// Convert selection to python list
Py::Tuple selectionPy(selection.size());
int i = 0;
for (auto it : selection) {
Py::Dict sel;
sel.setItem("object", Py::asObject(it.object.getObject()->getPyObject()));
sel.setItem("subName", Py::String(it.object.getSubName()));
sel.setItem("pickedPoint", Py::asObject(new Base::VectorPy(it.pickedPoint)));
selectionPy.setItem(i, sel);
i++;
}
return selectionPy;
}
std::vector<MeasureType*> MeasureManager::getValidMeasureTypes(App::MeasureSelection selection, std::string mode) {
Base::PyGILStateLocker lock;
// Convert selection to python list
Py::Tuple selectionPy = getSelectionPy(selection);
// Store valid measure types
std::vector<MeasureType*> validTypes;
std::pair<int, MeasureType>();
// Loop through measure types and check if they work with given selection
for (App::MeasureType* mType : getMeasureTypes()){
if (mode != "" && mType->label != mode) {
if (mType->validatorCb && !mType->validatorCb(selection)) {
continue;
}
if (mType->isPython) {
// Parse Python measure types
auto measurePyClass = Py::Object(mType->pythonClass);
Py::Tuple args(1);
args.setItem(0, selectionPy);
Py::Object isValid;
try {
isValid = measurePyClass.callMemberFunction(std::string("isValidSelection"), args);
} catch (const Py::Exception&) {
Base::PyException e;
e.ReportException();
isValid = Py::False();
}
if (isValid.as_bool()) {
// Check priority
Py::Object isPriority;
try {
isPriority = measurePyClass.callMemberFunction("isPrioritySelection", args);
} catch (const Py::Exception&) {
Base::PyException e;
e.ReportException();
isPriority = Py::False();
}
if (isPriority.as_bool()) {
validTypes.insert(validTypes.begin(), mType);
} else {
validTypes.push_back(mType);
}
}
} else {
// Parse c++ measure types
if (mType->validatorCb && !mType->validatorCb(selection)) {
continue;
}
// Check if the measurement type prioritizes the given selection
if (mType->prioritizeCb && mType->prioritizeCb(selection)) {
validTypes.insert(validTypes.begin(), mType);
} else {
validTypes.push_back(mType);
}
// Check if the measurement type prioritizes the given selection
if (mType->prioritizeCb && mType->prioritizeCb(selection)) {
validTypes.insert(validTypes.begin(), mType);
}
else {
validTypes.push_back(mType);
}
}
return validTypes;
}
return validTypes;
}
} // namespace App
} // namespace App

View File

@@ -36,24 +36,27 @@
#include <FCGlobal.h>
namespace App {
namespace App
{
// Add your class methods and member variables here
enum class MeasureElementType {
enum class MeasureElementType
{
INVALID,
POINT,
LINE,
LINESEGMENT,
CIRCLE,
ARC,
CURVE, // Has a length but no radius or axis
CURVE, // Has a length but no radius or axis
PLANE,
CYLINDER,
Volume,
};
struct MeasureSelectionItem {
struct MeasureSelectionItem
{
App::SubObjectT object;
Base::Vector3d pickedPoint;
};
@@ -62,9 +65,10 @@ struct MeasureSelectionItem {
using MeasureSelection = std::vector<MeasureSelectionItem>;
using MeasureValidateMethod = std::function<bool(const MeasureSelection&)>;
using MeasurePrioritizeMethod = std::function<bool(const MeasureSelection&)>;
using MeasureTypeMethod = std::function<MeasureElementType (App::DocumentObject*, const char*)>;
using MeasureTypeMethod = std::function<MeasureElementType(App::DocumentObject*, const char*)>;
struct MeasureType {
struct MeasureType
{
std::string identifier;
std::string label;
std::string measureObject;
@@ -72,21 +76,24 @@ struct MeasureType {
// Checks if the measurement works with a given selection
MeasureValidateMethod validatorCb;
// Allows to prioritize this over other measurement types when the measurement type is picked implicitly from the selection.
// Gets called only when validatorCb returned true for the given selection
// Allows to prioritize this over other measurement types when the measurement type is picked
// implicitly from the selection. Gets called only when validatorCb returned true for the given
// selection
MeasurePrioritizeMethod prioritizeCb;
bool isPython;
PyObject* pythonClass;
};
struct MeasureHandler {
struct MeasureHandler
{
std::string module;
MeasureTypeMethod typeCb;
};
class AppExport MeasureManager {
class AppExport MeasureManager
{
public:
MeasureManager();
@@ -96,11 +103,20 @@ public:
static MeasureHandler getMeasureHandler(const App::MeasureSelectionItem& selectionItem);
static MeasureElementType getMeasureElementType(const App::MeasureSelectionItem& selectionItem);
static void addMeasureType(MeasureType* measureType);
static void addMeasureType(std::string id, std::string label, std::string measureObj, MeasureValidateMethod validatorCb, MeasurePrioritizeMethod prioritizeCb);
static void addMeasureType(const char* id, const char* label, const char* measureObj, MeasureValidateMethod validatorCb, MeasurePrioritizeMethod prioritizeCb);
static void addMeasureType(std::string id,
std::string label,
std::string measureObj,
MeasureValidateMethod validatorCb,
MeasurePrioritizeMethod prioritizeCb);
static void addMeasureType(const char* id,
const char* label,
const char* measureObj,
MeasureValidateMethod validatorCb,
MeasurePrioritizeMethod prioritizeCb);
static const std::vector<MeasureType*> getMeasureTypes();
static Py::Tuple getSelectionPy(const App::MeasureSelection& selection);
static std::vector<MeasureType*> getValidMeasureTypes(App::MeasureSelection selection, std::string mode);
static std::vector<MeasureType*> getValidMeasureTypes(App::MeasureSelection selection,
std::string mode);
private:
@@ -109,6 +125,6 @@ private:
};
} // namespace App
} // namespace App
#endif // MEASUREMANAGER_H
#endif // MEASUREMANAGER_H

View File

@@ -47,17 +47,17 @@ int MeasureManagerPy::setCustomAttributes(const char* /*attr*/, PyObject* /*obj*
}
PyObject* MeasureManagerPy::addMeasureType(PyObject *args)
PyObject* MeasureManagerPy::addMeasureType(PyObject* args)
{
PyObject *pyobj = Py_None;
PyObject* pyobj = Py_None;
char *id, *label;
if (!PyArg_ParseTuple(args, "ssO", &id, &label, &pyobj))
if (!PyArg_ParseTuple(args, "ssO", &id, &label, &pyobj)) {
return nullptr;
}
MeasureManager::addMeasureType(
new App::MeasureType{id, label, "", nullptr, nullptr, true, pyobj}
);
new App::MeasureType {id, label, "", nullptr, nullptr, true, pyobj});
Py_Return;
}
@@ -66,7 +66,7 @@ PyObject* MeasureManagerPy::addMeasureType(PyObject *args)
PyObject* MeasureManagerPy::getMeasureTypes()
{
Py::List types;
for (auto & it : MeasureManager::getMeasureTypes()) {
for (auto& it : MeasureManager::getMeasureTypes()) {
Py::Tuple type(3);
type.setItem(0, Py::String(it->identifier));
type.setItem(1, Py::String(it->label));

View File

@@ -22,7 +22,7 @@
#include "PreCompiled.h"
#ifndef _PreComp_
# include <stack>
#include <stack>
#endif
#include <QCoreApplication>
@@ -36,13 +36,17 @@
using namespace App;
namespace sp = std::placeholders;
namespace App {
namespace App
{
class XMLMergeReader : public Base::XMLReader
class XMLMergeReader: public Base::XMLReader
{
public:
XMLMergeReader(std::map<std::string, std::string>& name, const char* FileName, std::istream& str)
: Base::XMLReader(FileName, str), nameMap(name)
XMLMergeReader(std::map<std::string, std::string>& name,
const char* FileName,
std::istream& str)
: Base::XMLReader(FileName, str)
, nameMap(name)
{}
void addName(const char* s1, const char* s2) override
@@ -52,34 +56,35 @@ public:
const char* getName(const char* name) const override
{
std::map<std::string, std::string>::const_iterator it = nameMap.find(name);
if (it != nameMap.end())
if (it != nameMap.end()) {
return it->second.c_str();
else
}
else {
return name;
}
}
bool doNameMapping() const override
{
return true;
}
protected:
private:
std::map<std::string, std::string>& nameMap;
using PropertyTag = std::pair<std::string, std::string>;
std::stack<PropertyTag> propertyStack;
};
}
} // namespace App
MergeDocuments::MergeDocuments(App::Document* doc)
: appdoc(doc)
{
//NOLINTBEGIN
connectExport = doc->signalExportObjects.connect
(std::bind(&MergeDocuments::exportObject, this, sp::_1, sp::_2));
connectImport = doc->signalImportObjects.connect
(std::bind(&MergeDocuments::importObject, this, sp::_1, sp::_2));
//NOLINTEND
// NOLINTBEGIN
connectExport = doc->signalExportObjects.connect(
std::bind(&MergeDocuments::exportObject, this, sp::_1, sp::_2));
connectImport = doc->signalImportObjects.connect(
std::bind(&MergeDocuments::importObject, this, sp::_1, sp::_2));
// NOLINTEND
QCoreApplication* app = QCoreApplication::instance();
if (app && app->inherits("QApplication")) {
@@ -93,17 +98,16 @@ MergeDocuments::~MergeDocuments()
connectImport.disconnect();
}
unsigned int MergeDocuments::getMemSize () const
unsigned int MergeDocuments::getMemSize() const
{
return 0;
}
std::vector<App::DocumentObject*>
MergeDocuments::importObjects(std::istream& input)
std::vector<App::DocumentObject*> MergeDocuments::importObjects(std::istream& input)
{
this->nameMap.clear();
this->stream = new zipios::ZipInputStream(input);
XMLMergeReader reader(this->nameMap,"<memory>", *stream);
XMLMergeReader reader(this->nameMap, "<memory>", *stream);
reader.setVerbose(isVerbose());
std::vector<App::DocumentObject*> objs = appdoc->importObjects(reader);
@@ -113,20 +117,20 @@ MergeDocuments::importObjects(std::istream& input)
return objs;
}
void MergeDocuments::importObject(const std::vector<App::DocumentObject*>& o, Base::XMLReader & r)
void MergeDocuments::importObject(const std::vector<App::DocumentObject*>& o, Base::XMLReader& r)
{
objects = o;
Restore(r);
r.readFiles(*this->stream);
}
void MergeDocuments::exportObject(const std::vector<App::DocumentObject*>& o, Base::Writer & w)
void MergeDocuments::exportObject(const std::vector<App::DocumentObject*>& o, Base::Writer& w)
{
objects = o;
Save(w);
}
void MergeDocuments::Save (Base::Writer & w) const
void MergeDocuments::Save(Base::Writer& w) const
{
// Save view provider stuff
if (guiup) {
@@ -134,7 +138,7 @@ void MergeDocuments::Save (Base::Writer & w) const
}
}
void MergeDocuments::Restore(Base::XMLReader &r)
void MergeDocuments::Restore(Base::XMLReader& r)
{
// Restore view provider stuff
if (guiup) {
@@ -142,13 +146,13 @@ void MergeDocuments::Restore(Base::XMLReader &r)
}
}
void MergeDocuments::SaveDocFile (Base::Writer & w) const
void MergeDocuments::SaveDocFile(Base::Writer& w) const
{
// Save view provider stuff
appdoc->signalExportViewObjects(this->objects, w);
}
void MergeDocuments::RestoreDocFile(Base::Reader & r)
void MergeDocuments::RestoreDocFile(Base::Reader& r)
{
// Restore view provider stuff
appdoc->signalImportViewObjects(this->objects, r, this->nameMap);

View File

@@ -27,36 +27,47 @@
#include <Base/Persistence.h>
#include <boost/signals2.hpp>
namespace zipios {
namespace zipios
{
class ZipInputStream;
}
namespace App {
namespace App
{
class Document;
class DocumentObject;
class AppExport MergeDocuments : public Base::Persistence
class AppExport MergeDocuments: public Base::Persistence
{
public:
explicit MergeDocuments(App::Document* doc);
~MergeDocuments() override;
bool isVerbose() const { return verbose; }
void setVerbose(bool on) { verbose = on; }
unsigned int getMemSize () const override;
bool isVerbose() const
{
return verbose;
}
void setVerbose(bool on)
{
verbose = on;
}
unsigned int getMemSize() const override;
std::vector<App::DocumentObject*> importObjects(std::istream&);
void importObject(const std::vector<App::DocumentObject*>& o, Base::XMLReader & r);
void exportObject(const std::vector<App::DocumentObject*>& o, Base::Writer & w);
void Save (Base::Writer & w) const override;
void Restore(Base::XMLReader &r) override;
void SaveDocFile (Base::Writer & w) const override;
void RestoreDocFile(Base::Reader & r) override;
void importObject(const std::vector<App::DocumentObject*>& o, Base::XMLReader& r);
void exportObject(const std::vector<App::DocumentObject*>& o, Base::Writer& w);
void Save(Base::Writer& w) const override;
void Restore(Base::XMLReader& r) override;
void SaveDocFile(Base::Writer& w) const override;
void RestoreDocFile(Base::Reader& r) override;
const std::map<std::string, std::string> &getNameMap() const {return nameMap;}
const std::map<std::string, std::string>& getNameMap() const
{
return nameMap;
}
private:
bool guiup{false};
bool verbose{true};
zipios::ZipInputStream* stream{nullptr};
App::Document* appdoc{nullptr};
bool guiup {false};
bool verbose {true};
zipios::ZipInputStream* stream {nullptr};
App::Document* appdoc {nullptr};
std::vector<App::DocumentObject*> objects;
std::map<std::string, std::string> nameMap;
using Connection = boost::signals2::connection;
@@ -64,6 +75,6 @@ private:
Connection connectImport;
};
} // namespace App
} // namespace App
#endif // APP_MERGEDOCUMENTS_H
#endif // APP_MERGEDOCUMENTS_H

View File

@@ -1,31 +1,31 @@
/**************************************************************************
* *
* Copyright (c) 2021-2023 FreeCAD Project Association *
* *
* This file is part of FreeCAD. *
* *
* FreeCAD is free software: you can redistribute it and/or modify it *
* under the terms of the GNU Lesser General Public License as *
* published by the Free Software Foundation, either version 2.1 of the *
* License, or (at your option) any later version. *
* *
* FreeCAD 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 *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with FreeCAD. If not, see *
* <https://www.gnu.org/licenses/>. *
* *
***************************************************************************/
* *
* Copyright (c) 2021-2023 FreeCAD Project Association *
* *
* This file is part of FreeCAD. *
* *
* FreeCAD is free software: you can redistribute it and/or modify it *
* under the terms of the GNU Lesser General Public License as *
* published by the Free Software Foundation, either version 2.1 of the *
* License, or (at your option) any later version. *
* *
* FreeCAD 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 *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with FreeCAD. If not, see *
* <https://www.gnu.org/licenses/>. *
* *
***************************************************************************/
#include "PreCompiled.h"
#ifndef _PreComp_
# include <boost/core/ignore_unused.hpp>
# include <memory>
# include <sstream>
#include <boost/core/ignore_unused.hpp>
#include <memory>
#include <sstream>
#endif
#include <xercesc/framework/LocalFileFormatTarget.hpp>
@@ -93,7 +93,7 @@ class XMLErrorHandler: public HandlerBase
throw Base::XMLBaseException(message.str());
}
};
}// namespace MetadataInternal
} // namespace MetadataInternal
Metadata::Metadata(const fs::path& metadataFile)
: _dom(nullptr)
@@ -130,10 +130,9 @@ Metadata::Metadata(const DOMNode* domNode, int format)
App::Metadata::Metadata(const std::string& rawData)
: _dom(nullptr)
{
MemBufInputSource buffer(
reinterpret_cast<const XMLByte*>(rawData.c_str()),
rawData.size(),
"raw data (in memory)");
MemBufInputSource buffer(reinterpret_cast<const XMLByte*>(rawData.c_str()),
rawData.size(),
"raw data (in memory)");
loadFromInputSource(buffer);
}
@@ -294,7 +293,7 @@ XERCES_CPP_NAMESPACE::DOMElement* Metadata::dom() const
void Metadata::setName(const std::string& name)
{
std::string invalidCharacters = "/\\?%*:|\"<>";// Should cover all OSes
std::string invalidCharacters = "/\\?%*:|\"<>"; // Should cover all OSes
if (_name.find_first_of(invalidCharacters) != std::string::npos) {
throw Base::RuntimeError("Name cannot contain any of: " + invalidCharacters);
}
@@ -534,7 +533,8 @@ void Metadata::clearFile()
}
DOMElement* appendSimpleXMLNode(DOMElement* baseNode, const std::string& nodeName,
DOMElement* appendSimpleXMLNode(DOMElement* baseNode,
const std::string& nodeName,
const std::string& nodeContents)
{
// For convenience (and brevity of final output) don't create nodes that don't have contents
@@ -665,8 +665,9 @@ bool Metadata::satisfies(const Meta::Dependency& dep)
if (dep.package != _name) {
return false;
}
// The "condition" attribute allows an expression to enable or disable this dependency check: it must contain a valid
// FreeCAD Expression. If it evaluates to false, this dependency is bypassed (e.g. this function returns false).
// The "condition" attribute allows an expression to enable or disable this dependency check: it
// must contain a valid FreeCAD Expression. If it evaluates to false, this dependency is
// bypassed (e.g. this function returns false).
if (!dep.condition.empty()) {
auto injectedString = dep.condition;
std::map<std::string, std::string> replacements;
@@ -938,11 +939,11 @@ void Metadata::parseVersion1(const DOMNode* startNode)
_icon = fs::path(StrXUTF8(element->getTextContent()).str);
}
else if (tagString == "content") {
parseContentNodeVersion1(element);// Recursive call
parseContentNodeVersion1(element); // Recursive call
}
else {
// If none of this node's nodeChildren have nodeChildren of their own, it is a simple element and we
// can handle it as a GenericMetadata object
// If none of this node's nodeChildren have nodeChildren of their own, it is a simple
// element and we can handle it as a GenericMetadata object
auto nodeChildren = element->getChildNodes();
bool hasGrandchildren = false;
for (XMLSize_t j = 0; j < nodeChildren->getLength() && !hasGrandchildren; ++j) {
@@ -970,15 +971,15 @@ void Metadata::parseContentNodeVersion1(const DOMElement* contentNode)
}
Meta::Contact::Contact(std::string name, std::string email)
: name(std::move(name)),
email(std::move(email))
: name(std::move(name))
, email(std::move(email))
{
// This has to be provided manually since we have another constructor
}
Meta::Contact::Contact(const XERCES_CPP_NAMESPACE::DOMElement* elem)
{
if (!elem){
if (!elem) {
return;
}
auto emailAttribute = elem->getAttribute(XUTF8Str("email").unicodeForm());
@@ -992,15 +993,15 @@ bool App::Meta::Contact::operator==(const Contact& rhs) const
}
Meta::License::License(std::string name, fs::path file)
: name(std::move(name)),
file(std::move(file))
: name(std::move(name))
, file(std::move(file))
{
// This has to be provided manually since we have another constructor
}
Meta::License::License(const XERCES_CPP_NAMESPACE::DOMElement* elem)
{
if (!elem){
if (!elem) {
return;
}
auto fileAttribute = elem->getAttribute(XUTF8Str("file").unicodeForm());
@@ -1016,13 +1017,13 @@ bool App::Meta::License::operator==(const License& rhs) const
}
App::Meta::Url::Url()
: location(""),
type(App::Meta::UrlType::website)
: location("")
, type(App::Meta::UrlType::website)
{}
Meta::Url::Url(std::string location, UrlType type)
: location(std::move(location)),
type(type)
: location(std::move(location))
, type(type)
{
// This has to be provided manually since we have another constructor
}
@@ -1070,14 +1071,14 @@ bool App::Meta::Url::operator==(const Url& rhs) const
}
App::Meta::Dependency::Dependency()
: optional(false),
dependencyType(App::Meta::DependencyType::automatic)
: optional(false)
, dependencyType(App::Meta::DependencyType::automatic)
{}
App::Meta::Dependency::Dependency(std::string pkg)
: package(std::move(pkg)),
optional(false),
dependencyType(App::Meta::DependencyType::automatic)
: package(std::move(pkg))
, optional(false)
, dependencyType(App::Meta::DependencyType::automatic)
{}
Meta::Dependency::Dependency(const XERCES_CPP_NAMESPACE::DOMElement* elem)
@@ -1090,7 +1091,7 @@ Meta::Dependency::Dependency(const XERCES_CPP_NAMESPACE::DOMElement* elem)
condition = StrXUTF8(elem->getAttribute(XUTF8Str("condition").unicodeForm())).str;
std::string opt_string = StrXUTF8(elem->getAttribute(XUTF8Str("optional").unicodeForm())).str;
if (opt_string == "true"
|| opt_string == "True") {// Support Python capitalization in this one case...
|| opt_string == "True") { // Support Python capitalization in this one case...
optional = true;
}
else {
@@ -1128,10 +1129,10 @@ bool App::Meta::Dependency::operator==(const Dependency& rhs) const
Meta::Version::Version() = default;
Meta::Version::Version(int major, int minor, int patch, std::string suffix)
: major(major),
minor(minor),
patch(patch),
suffix(std::move(suffix))
: major(major)
, minor(minor)
, patch(patch)
, suffix(std::move(suffix))
{}
Meta::Version::Version(const std::string& versionString)
@@ -1207,8 +1208,8 @@ Meta::GenericMetadata::GenericMetadata(const XERCES_CPP_NAMESPACE::DOMElement* e
contents = StrXUTF8(elem->getTextContent()).str;
for (XMLSize_t i = 0; i < elem->getAttributes()->getLength(); ++i) {
auto attr = elem->getAttributes()->item(i);
attributes.insert(
std::make_pair(StrXUTF8(attr->getNodeName()).str, StrXUTF8(attr->getTextContent()).str));
attributes.insert(std::make_pair(StrXUTF8(attr->getNodeName()).str,
StrXUTF8(attr->getTextContent()).str));
}
}

View File

@@ -1,24 +1,24 @@
/**************************************************************************
* *
* Copyright (c) 2021-2023 FreeCAD Project Association *
* *
* This file is part of FreeCAD. *
* *
* FreeCAD is free software: you can redistribute it and/or modify it *
* under the terms of the GNU Lesser General Public License as *
* published by the Free Software Foundation, either version 2.1 of the *
* License, or (at your option) any later version. *
* *
* FreeCAD 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 *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with FreeCAD. If not, see *
* <https://www.gnu.org/licenses/>. *
* *
***************************************************************************/
* *
* Copyright (c) 2021-2023 FreeCAD Project Association *
* *
* This file is part of FreeCAD. *
* *
* FreeCAD is free software: you can redistribute it and/or modify it *
* under the terms of the GNU Lesser General Public License as *
* published by the Free Software Foundation, either version 2.1 of the *
* License, or (at your option) any later version. *
* *
* FreeCAD 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 *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with FreeCAD. If not, see *
* <https://www.gnu.org/licenses/>. *
* *
***************************************************************************/
#ifndef BASE_METADATAREADER_H
#define BASE_METADATAREADER_H
@@ -43,32 +43,36 @@ namespace Meta
{
/**
* \struct Contact
* \brief A person or company representing a point of contact for the package (either author or maintainer).
*/
struct AppExport Contact {
* \struct Contact
* \brief A person or company representing a point of contact for the package (either author or
* maintainer).
*/
struct AppExport Contact
{
Contact() = default;
Contact(std::string name, std::string email);
Contact(std::string name, std::string email);
explicit Contact(const XERCES_CPP_NAMESPACE::DOMElement* elem);
std::string name; //< Contact name - required
std::string email;//< Contact email - may be optional
std::string name; //< Contact name - required
std::string email; //< Contact email - may be optional
bool operator==(const Contact& rhs) const;
};
/**
* \struct License
* \brief A license that covers some or all of this package.
*
* Many licenses also require the inclusion of the complete license text, specified in this struct
* using the "file" member.
*/
struct AppExport License {
* \struct License
* \brief A license that covers some or all of this package.
*
* Many licenses also require the inclusion of the complete license text, specified in this struct
* using the "file" member.
*/
struct AppExport License
{
License() = default;
License(std::string name, boost::filesystem::path file);
explicit License(const XERCES_CPP_NAMESPACE::DOMElement* elem);
std::string name;//< Short name of license, e.g. "LGPL2", "MIT", "Mozilla Public License", etc.
std::string
name; //< Short name of license, e.g. "LGPL2", "MIT", "Mozilla Public License", etc.
boost::filesystem::path
file;//< Optional path to the license file, relative to the XML file's location
file; //< Optional path to the license file, relative to the XML file's location
bool operator==(const License& rhs) const;
};
@@ -83,32 +87,35 @@ enum class UrlType
};
/**
* \struct Url
* \brief A URL, including type information (e.g. website, repository, or bugtracker, in package.xml)
*/
struct AppExport Url {
* \struct Url
* \brief A URL, including type information (e.g. website, repository, or bugtracker, in
* package.xml)
*/
struct AppExport Url
{
Url();
Url(std::string location, UrlType type);
explicit Url(const XERCES_CPP_NAMESPACE::DOMElement* elem);
std::string location;//< The actual URL, including protocol
UrlType type; //< What kind of URL this is
std::string branch; //< If it's a repository, which branch to use
std::string location; //< The actual URL, including protocol
UrlType type; //< What kind of URL this is
std::string branch; //< If it's a repository, which branch to use
bool operator==(const Url& rhs) const;
};
/**
* \struct Version
* A semantic version structure providing comparison operators and conversion to and from std::string
*/
struct AppExport Version {
* \struct Version
* A semantic version structure providing comparison operators and conversion to and from
* std::string
*/
struct AppExport Version
{
Version();
explicit Version(int major, int minor = 0, int patch = 0,
std::string suffix = std::string());
explicit Version(int major, int minor = 0, int patch = 0, std::string suffix = std::string());
explicit Version(const std::string& semanticString);
int major{};
int minor{};
int patch{};
int major {};
int minor {};
int patch {};
std::string suffix;
std::string str() const;
@@ -122,9 +129,9 @@ struct AppExport Version {
};
/**
* \enum DependencyType
* The type of dependency.
*/
* \enum DependencyType
* The type of dependency.
*/
enum class DependencyType
{
automatic,
@@ -134,81 +141,83 @@ enum class DependencyType
};
/**
* \struct Dependency
* \brief Another package that this package depends on, conflicts with, or replaces
*/
struct AppExport Dependency {
* \struct Dependency
* \brief Another package that this package depends on, conflicts with, or replaces
*/
struct AppExport Dependency
{
Dependency();
explicit Dependency(std::string pkg);
explicit Dependency(std::string pkg);
explicit Dependency(const XERCES_CPP_NAMESPACE::DOMElement* elem);
std::string
package;//< Required: must exactly match the contents of the "name" element in the referenced package's package.xml file.
std::string
version_lt;//< Optional: The dependency to the package is restricted to versions less than the stated version number.
std::string
version_lte;//< Optional: The dependency to the package is restricted to versions less or equal than the stated version number.
std::string
version_eq;//< Optional: The dependency to the package is restricted to a version equal than the stated version number.
std::string
version_gte;//< Optional: The dependency to the package is restricted to versions greater or equal than the stated version number.
std::string
version_gt;//< Optional: The dependency to the package is restricted to versions greater than the stated version number.
std::string condition; //< Optional: Conditional expression as documented in REP149.
bool optional; //< Optional: Whether this dependency is considered "optional"
DependencyType dependencyType;//< Optional: defaults to "automatic"
std::string package; //< Required: must exactly match the contents of the "name" element in the
//referenced package's package.xml file.
std::string version_lt; //< Optional: The dependency to the package is restricted to versions
//less than the stated version number.
std::string version_lte; //< Optional: The dependency to the package is restricted to versions
//less or equal than the stated version number.
std::string version_eq; //< Optional: The dependency to the package is restricted to a version
//equal than the stated version number.
std::string version_gte; //< Optional: The dependency to the package is restricted to versions
//greater or equal than the stated version number.
std::string version_gt; //< Optional: The dependency to the package is restricted to versions
//greater than the stated version number.
std::string condition; //< Optional: Conditional expression as documented in REP149.
bool optional; //< Optional: Whether this dependency is considered "optional"
DependencyType dependencyType; //< Optional: defaults to "automatic"
bool operator==(const Dependency& rhs) const;
};
/**
* \struct GenericMetadata
* A structure to hold unrecognized single-level metadata.
*
* Most unrecognized metadata is simple: when parsing the XML, if the parser finds a tag it
* does not recognize, and that tag has no children, it is parsed into this data structure
* for convenient access by client code.
*/
struct AppExport GenericMetadata {
* \struct GenericMetadata
* A structure to hold unrecognized single-level metadata.
*
* Most unrecognized metadata is simple: when parsing the XML, if the parser finds a tag it
* does not recognize, and that tag has no children, it is parsed into this data structure
* for convenient access by client code.
*/
struct AppExport GenericMetadata
{
GenericMetadata() = default;
explicit GenericMetadata(const XERCES_CPP_NAMESPACE::DOMElement* elem);
explicit GenericMetadata(std::string contents);
std::string contents; //< The contents of the tag
std::map<std::string, std::string> attributes;//< The XML attributes of the tag
explicit GenericMetadata(std::string contents);
std::string contents; //< The contents of the tag
std::map<std::string, std::string> attributes; //< The XML attributes of the tag
};
}// namespace Meta
} // namespace Meta
/**
* \class Metadata
* \brief Reads data from a metadata file.
*
* The metadata format is based on https://ros.org/reps/rep-0149.html, modified for FreeCAD
* use. Full format documentation is available at the FreeCAD Wiki:
* https://wiki.freecad.org/Package_Metadata
*/
* \class Metadata
* \brief Reads data from a metadata file.
*
* The metadata format is based on https://ros.org/reps/rep-0149.html, modified for FreeCAD
* use. Full format documentation is available at the FreeCAD Wiki:
* https://wiki.freecad.org/Package_Metadata
*/
class AppExport Metadata
{
public:
Metadata();
/**
* Read the data from a file on disk
*
* This constructor takes a path to an XML file and loads the XML from that file as
* metadata.
*/
* Read the data from a file on disk
*
* This constructor takes a path to an XML file and loads the XML from that file as
* metadata.
*/
explicit Metadata(const boost::filesystem::path& metadataFile);
/**
* Construct a Metadata object from a DOM node.
*
* This node may have any tag name: it is only accessed via its children, which are
* expected to follow the standard Metadata format for the contents of the <package> element.
*/
* Construct a Metadata object from a DOM node.
*
* This node may have any tag name: it is only accessed via its children, which are
* expected to follow the standard Metadata format for the contents of the <package> element.
*/
Metadata(const XERCES_CPP_NAMESPACE::DOMNode* domNode, int format);
/**
* Treat the incoming rawData as metadata to be parsed.
*/
* Treat the incoming rawData as metadata to be parsed.
*/
explicit Metadata(const std::string& rawData);
~Metadata();
@@ -218,69 +227,69 @@ public:
// Recognized Metadata
//////////////////////////////////////////////////////////////
std::string name() const; //< A short name for this package, often used as a menu entry.
std::string type() const; //< The type for this package.
Meta::Version version() const;//< Version string in semantic triplet format, e.g. "1.2.3".
std::string name() const; //< A short name for this package, often used as a menu entry.
std::string type() const; //< The type for this package.
Meta::Version version() const; //< Version string in semantic triplet format, e.g. "1.2.3".
std::string date()
const;//< Date string -- currently arbitrary (when C++20 is well-supported we can revisit)
std::string description() const;//< Text-only description of the package. No markup.
const; //< Date string -- currently arbitrary (when C++20 is well-supported we can revisit)
std::string description() const; //< Text-only description of the package. No markup.
std::vector<Meta::Contact>
maintainer() const;//< Must be at least one, and must specify an email address.
maintainer() const; //< Must be at least one, and must specify an email address.
std::vector<Meta::License>
license() const;//< Must be at least one, and most licenses require including a license file.
std::vector<Meta::Url> url()
const;//< Any number of URLs may be specified, but at least one repository URL must be included at the package level.
license() const; //< Must be at least one, and most licenses require including a license file.
std::vector<Meta::Url> url() const; //< Any number of URLs may be specified, but at least one
//repository URL must be included at the package level.
std::vector<Meta::Contact>
author() const;//< Any number of authors may be specified, and email addresses are optional.
author() const; //< Any number of authors may be specified, and email addresses are optional.
std::vector<Meta::Dependency>
depend() const;//< Zero or more packages this package requires prior to use.
depend() const; //< Zero or more packages this package requires prior to use.
std::vector<Meta::Dependency>
conflict() const;//< Zero of more packages this package conflicts with.
conflict() const; //< Zero of more packages this package conflicts with.
std::vector<Meta::Dependency>
replace() const;//< Zero or more packages this package is intended to replace.
std::vector<std::string> tag() const;//< Zero or more text tags related to this package.
boost::filesystem::path icon() const;//< Path to an icon file.
replace() const; //< Zero or more packages this package is intended to replace.
std::vector<std::string> tag() const; //< Zero or more text tags related to this package.
boost::filesystem::path icon() const; //< Path to an icon file.
std::string
classname() const;//< Recognized for convenience -- generally only used by Workbenches.
classname() const; //< Recognized for convenience -- generally only used by Workbenches.
boost::filesystem::path
subdirectory() const;//< Optional, override the default subdirectory name for this item.
subdirectory() const; //< Optional, override the default subdirectory name for this item.
std::vector<boost::filesystem::path>
file() const;//< Arbitrary files associated with this package or content item.
Meta::Version freecadmin() const;//< The minimum FreeCAD version.
Meta::Version freecadmax() const;//< The maximum FreeCAD version.
Meta::Version pythonmin() const; //< The minimum Python version.
file() const; //< Arbitrary files associated with this package or content item.
Meta::Version freecadmin() const; //< The minimum FreeCAD version.
Meta::Version freecadmax() const; //< The maximum FreeCAD version.
Meta::Version pythonmin() const; //< The minimum Python version.
/**
* Access the metadata for the content elements of this package
*
* In addition to the overall package metadata, this class reads in metadata contained in a
* <content> element. Each entry in the content element is an element representing some
* type of package content (e.g. add-on, macro, theme, etc.). This class places no restriction
* on the types, it is up to client code to place requirements on the metadata included
* here.
*
* For example, themes might be specified:
* <content>
* <theme>
* <name>High Contrast</name>
* </theme>
* </content>
*/
* Access the metadata for the content elements of this package
*
* In addition to the overall package metadata, this class reads in metadata contained in a
* <content> element. Each entry in the content element is an element representing some
* type of package content (e.g. add-on, macro, theme, etc.). This class places no restriction
* on the types, it is up to client code to place requirements on the metadata included
* here.
*
* For example, themes might be specified:
* <content>
* <theme>
* <name>High Contrast</name>
* </theme>
* </content>
*/
std::multimap<std::string, Metadata> content() const;
/**
* Convenience accessor for unrecognized simple metadata.
*
* If the XML parser encounters tags that it does not recognize, and those tags have
* no children, a GenericMetadata object is created. Those objects can be accessed using
* operator[], which returns a (potentially empty) vector containing all instances of the
* given tag. It cannot be used to *create* a new tag, however. See addGenericMetadata().
*/
* Convenience accessor for unrecognized simple metadata.
*
* If the XML parser encounters tags that it does not recognize, and those tags have
* no children, a GenericMetadata object is created. Those objects can be accessed using
* operator[], which returns a (potentially empty) vector containing all instances of the
* given tag. It cannot be used to *create* a new tag, however. See addGenericMetadata().
*/
std::vector<Meta::GenericMetadata> operator[](const std::string& tag) const;
/**
* Directly access the DOM tree to support unrecognized multi-level metadata
*/
* Directly access the DOM tree to support unrecognized multi-level metadata
*/
XERCES_CPP_NAMESPACE::DOMElement* dom() const;
@@ -333,19 +342,19 @@ public:
void clearFile();
/**
* Write the metadata to an XML file
*/
* Write the metadata to an XML file
*/
void write(const boost::filesystem::path& file) const;
/**
* Determine whether this package satisfies the given dependency
*/
* Determine whether this package satisfies the given dependency
*/
bool satisfies(const Meta::Dependency&);
/**
* Determine whether the current metadata specifies support for the currently-running version of FreeCAD.
* Does not interrogate content items, which must be queried individually.
*/
* Determine whether the current metadata specifies support for the currently-running version of
* FreeCAD. Does not interrogate content items, which must be queried individually.
*/
bool supportsCurrentFreeCAD() const;
private:
@@ -384,6 +393,6 @@ private:
void appendToElement(XERCES_CPP_NAMESPACE::DOMElement* root) const;
};
}// namespace App
} // namespace App
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -24,7 +24,7 @@
#include "PreCompiled.h"
#ifndef _PreComp_
# include <string>
#include <string>
#endif
#include <App/Document.h>
@@ -36,7 +36,7 @@
#ifndef M_PI
# define M_PI 3.14159265358979323846
#define M_PI 3.14159265358979323846
#endif
using namespace App;
@@ -44,100 +44,116 @@ using namespace App;
PROPERTY_SOURCE(App::Origin, App::DocumentObject)
Origin::Origin() : extension(this) {
ADD_PROPERTY_TYPE ( OriginFeatures, (nullptr), 0, App::Prop_Hidden,
"Axis and baseplanes controlled by the origin" );
Origin::Origin()
: extension(this)
{
ADD_PROPERTY_TYPE(OriginFeatures,
(nullptr),
0,
App::Prop_Hidden,
"Axis and baseplanes controlled by the origin");
setStatus(App::NoAutoExpand,true);
setStatus(App::NoAutoExpand, true);
extension.initExtension(this);
}
Origin::~Origin() = default;
App::OriginFeature *Origin::getOriginFeature( const char *role) const {
const auto & features = OriginFeatures.getValues ();
auto featIt = std::find_if (features.begin(), features.end(),
[role] (App::DocumentObject *obj) {
return obj->isDerivedFrom ( App::OriginFeature::getClassTypeId () ) &&
strcmp (static_cast<App::OriginFeature *>(obj)->Role.getValue(), role) == 0;
} );
App::OriginFeature* Origin::getOriginFeature(const char* role) const
{
const auto& features = OriginFeatures.getValues();
auto featIt = std::find_if(features.begin(), features.end(), [role](App::DocumentObject* obj) {
return obj->isDerivedFrom(App::OriginFeature::getClassTypeId())
&& strcmp(static_cast<App::OriginFeature*>(obj)->Role.getValue(), role) == 0;
});
if (featIt != features.end()) {
return static_cast<App::OriginFeature *>(*featIt);
} else {
return static_cast<App::OriginFeature*>(*featIt);
}
else {
std::stringstream err;
err << "Origin \"" << getFullName () << "\" doesn't contain feature with role \""
<< role << '"';
throw Base::RuntimeError ( err.str().c_str () );
err << "Origin \"" << getFullName() << "\" doesn't contain feature with role \"" << role
<< '"';
throw Base::RuntimeError(err.str().c_str());
}
}
App::Line *Origin::getAxis( const char *role ) const {
App::OriginFeature *feat = getOriginFeature (role);
if ( feat->isDerivedFrom(App::Line::getClassTypeId () ) ) {
return static_cast<App::Line *> (feat);
} else {
App::Line* Origin::getAxis(const char* role) const
{
App::OriginFeature* feat = getOriginFeature(role);
if (feat->isDerivedFrom(App::Line::getClassTypeId())) {
return static_cast<App::Line*>(feat);
}
else {
std::stringstream err;
err << "Origin \"" << getFullName () << "\" contains bad Axis object for role \""
<< role << '"';
throw Base::RuntimeError ( err.str().c_str () );
err << "Origin \"" << getFullName() << "\" contains bad Axis object for role \"" << role
<< '"';
throw Base::RuntimeError(err.str().c_str());
}
}
App::Plane *Origin::getPlane( const char *role ) const {
App::OriginFeature *feat = getOriginFeature (role);
if ( feat->isDerivedFrom(App::Plane::getClassTypeId () ) ) {
return static_cast<App::Plane *> (feat);
} else {
App::Plane* Origin::getPlane(const char* role) const
{
App::OriginFeature* feat = getOriginFeature(role);
if (feat->isDerivedFrom(App::Plane::getClassTypeId())) {
return static_cast<App::Plane*>(feat);
}
else {
std::stringstream err;
err << "Origin \"" << getFullName () << "\" contains bad Plane object for role \""
<< role << '"';
throw Base::RuntimeError ( err.str().c_str () );
err << "Origin \"" << getFullName() << "\" contains bad Plane object for role \"" << role
<< '"';
throw Base::RuntimeError(err.str().c_str());
}
}
bool Origin::hasObject (const DocumentObject *obj) const {
const auto & features = OriginFeatures.getValues ();
return std::find (features.begin(), features.end(), obj) != features.end ();
bool Origin::hasObject(const DocumentObject* obj) const
{
const auto& features = OriginFeatures.getValues();
return std::find(features.begin(), features.end(), obj) != features.end();
}
short Origin::mustExecute() const {
if (OriginFeatures.isTouched ()) {
short Origin::mustExecute() const
{
if (OriginFeatures.isTouched()) {
return 1;
} else {
}
else {
return DocumentObject::mustExecute();
}
}
App::DocumentObjectExecReturn *Origin::execute() {
try { // try to find all base axis and planes in the origin
for (const char* role: AxisRoles) {
App::Line *axis = getAxis (role);
App::DocumentObjectExecReturn* Origin::execute()
{
try { // try to find all base axis and planes in the origin
for (const char* role : AxisRoles) {
App::Line* axis = getAxis(role);
assert(axis);
(void)axis;
}
for (const char* role: PlaneRoles) {
App::Plane *plane = getPlane (role);
for (const char* role : PlaneRoles) {
App::Plane* plane = getPlane(role);
assert(plane);
(void)plane;
}
} catch (const Base::Exception &ex) {
setError ();
return new App::DocumentObjectExecReturn ( ex.what () );
}
catch (const Base::Exception& ex) {
setError();
return new App::DocumentObjectExecReturn(ex.what());
}
return DocumentObject::execute ();
return DocumentObject::execute();
}
void Origin::setupObject () {
const static struct {
void Origin::setupObject()
{
const static struct
{
const Base::Type type;
const char *role;
const QString label;
const char* role;
const QString label;
Base::Rotation rot;
}
setupData [] = {
} setupData[] = {
// clang-format off
{App::Line::getClassTypeId(), AxisRoles[0], tr("X-axis"), Base::Rotation()},
{App::Line::getClassTypeId(), AxisRoles[1], tr("Y-axis"), Base::Rotation(Base::Vector3d(1,1,1), M_PI*2/3)},
@@ -148,39 +164,40 @@ void Origin::setupObject () {
// clang-format on
};
App::Document *doc = getDocument ();
App::Document* doc = getDocument();
std::vector<App::DocumentObject *> links;
for (auto data: setupData) {
std::string objName = doc->getUniqueObjectName ( data.role );
App::DocumentObject *featureObj = doc->addObject ( data.type.getName(), objName.c_str () );
std::vector<App::DocumentObject*> links;
for (auto data : setupData) {
std::string objName = doc->getUniqueObjectName(data.role);
App::DocumentObject* featureObj = doc->addObject(data.type.getName(), objName.c_str());
assert ( featureObj && featureObj->isDerivedFrom ( App::OriginFeature::getClassTypeId () ) );
assert(featureObj && featureObj->isDerivedFrom(App::OriginFeature::getClassTypeId()));
QByteArray byteArray = data.label.toUtf8();
QByteArray byteArray = data.label.toUtf8();
featureObj->Label.setValue(byteArray.constData());
App::OriginFeature *feature = static_cast <App::OriginFeature *> ( featureObj );
feature->Placement.setValue ( Base::Placement ( Base::Vector3d (), data.rot ) );
feature->Role.setValue ( data.role );
App::OriginFeature* feature = static_cast<App::OriginFeature*>(featureObj);
feature->Placement.setValue(Base::Placement(Base::Vector3d(), data.rot));
feature->Role.setValue(data.role);
links.push_back (feature);
links.push_back(feature);
}
OriginFeatures.setValues (links);
OriginFeatures.setValues(links);
}
void Origin::unsetupObject () {
const auto &objsLnk = OriginFeatures.getValues ();
void Origin::unsetupObject()
{
const auto& objsLnk = OriginFeatures.getValues();
// Copy to set to assert we won't call methode more then one time for each object
std::set<App::DocumentObject *> objs (objsLnk.begin(), objsLnk.end());
std::set<App::DocumentObject*> objs(objsLnk.begin(), objsLnk.end());
// Remove all controlled objects
for (auto obj: objs ) {
for (auto obj : objs) {
// Check that previous deletes wasn't inderectly removed one of our objects
const auto &objsLnk = OriginFeatures.getValues ();
if ( std::find(objsLnk.begin(), objsLnk.end(), obj) != objsLnk.end()) {
if ( ! obj->isRemoving() ) {
obj->getDocument()->removeObject (obj->getNameInDocument());
const auto& objsLnk = OriginFeatures.getValues();
if (std::find(objsLnk.begin(), objsLnk.end(), obj) != objsLnk.end()) {
if (!obj->isRemoving()) {
obj->getDocument()->removeObject(obj->getNameInDocument());
}
}
}
@@ -194,19 +211,25 @@ Origin::OriginExtension::OriginExtension(Origin* obj)
Group.setStatus(Property::Transient, true);
}
void Origin::OriginExtension::initExtension(ExtensionContainer* obj) {
void Origin::OriginExtension::initExtension(ExtensionContainer* obj)
{
App::GroupExtension::initExtension(obj);
}
bool Origin::OriginExtension::extensionGetSubObject(DocumentObject *&ret, const char *subname,
PyObject **pyobj, Base::Matrix4D *mat, bool, int depth) const {
bool Origin::OriginExtension::extensionGetSubObject(DocumentObject*& ret,
const char* subname,
PyObject** pyobj,
Base::Matrix4D* mat,
bool,
int depth) const
{
if (!subname || subname[0] == '\0') {
return false;
}
// mapping of object name to role name
std::string name(subname);
for (int i=0; i<3; i++) {
for (int i = 0; i < 3; i++) {
if (name.rfind(Origin::AxisRoles[i], 0) == 0) {
name = Origin::AxisRoles[i];
break;
@@ -219,14 +242,17 @@ bool Origin::OriginExtension::extensionGetSubObject(DocumentObject *&ret, const
try {
ret = obj->getOriginFeature(name.c_str());
if (!ret)
if (!ret) {
return false;
const char *dot = strchr(subname, '.');
if (dot)
subname = dot+1;
else
}
const char* dot = strchr(subname, '.');
if (dot) {
subname = dot + 1;
}
else {
subname = "";
ret = ret->getSubObject(subname, pyobj, mat, true, depth+1);
}
ret = ret->getSubObject(subname, pyobj, mat, true, depth + 1);
return true;
}
catch (const Base::Exception& e) {

View File

@@ -35,7 +35,7 @@ namespace App
/** Base class of all geometric document objects.
*/
class AppExport Origin : public App::DocumentObject
class AppExport Origin: public App::DocumentObject
{
PROPERTY_HEADER_WITH_OVERRIDE(App::Origin);
Q_DECLARE_TR_FUNCTIONS(App::Origin)
@@ -46,7 +46,8 @@ public:
~Origin() override;
/// returns the type name of the ViewProvider
const char* getViewProviderName() const override {
const char* getViewProviderName() const override
{
return "Gui::ViewProviderOrigin";
}
@@ -56,58 +57,67 @@ public:
*/
///@{
// returns X axis
App::Line *getX () const {
return getAxis (AxisRoles[0]);
App::Line* getX() const
{
return getAxis(AxisRoles[0]);
}
// returns Y axis
App::Line *getY () const {
return getAxis (AxisRoles[1]);
App::Line* getY() const
{
return getAxis(AxisRoles[1]);
}
// returns Z axis
App::Line *getZ () const {
return getAxis (AxisRoles[2]);
App::Line* getZ() const
{
return getAxis(AxisRoles[2]);
}
// returns XY plane
App::Plane *getXY () const {
return getPlane (PlaneRoles[0]);
App::Plane* getXY() const
{
return getPlane(PlaneRoles[0]);
}
// returns XZ plane
App::Plane *getXZ () const {
return getPlane (PlaneRoles[1]);
App::Plane* getXZ() const
{
return getPlane(PlaneRoles[1]);
}
// returns YZ plane
App::Plane *getYZ () const {
return getPlane (PlaneRoles[2]);
App::Plane* getYZ() const
{
return getPlane(PlaneRoles[2]);
}
/// Returns all axis objects to iterate on them
std::vector<App::Line *> axes() const {
return { getX(), getY(), getZ() };
std::vector<App::Line*> axes() const
{
return {getX(), getY(), getZ()};
}
/// Returns all base planes objects to iterate on them
std::vector<App::Plane *> planes() const {
return { getXY(), getXZ(), getYZ() };
std::vector<App::Plane*> planes() const
{
return {getXY(), getXZ(), getYZ()};
}
/// Returns all controlled objects (both planes and axis) to iterate on them
std::vector<App::OriginFeature *> baseObjects() const {
return { getX(), getY(), getZ(), getXY(), getXZ(), getYZ() };
std::vector<App::OriginFeature*> baseObjects() const
{
return {getX(), getY(), getZ(), getXY(), getXZ(), getYZ()};
}
/// Returns an axis by it's name
App::OriginFeature *getOriginFeature( const char* role ) const;
App::OriginFeature* getOriginFeature(const char* role) const;
/// Returns an axis by it's name
App::Line *getAxis( const char* role ) const;
App::Line* getAxis(const char* role) const;
/// Returns an axis by it's name
App::Plane *getPlane( const char* role ) const;
App::Plane* getPlane(const char* role) const;
///@}
/// Returns true if the given object is part of the origin
bool hasObject (const DocumentObject *obj) const;
bool hasObject(const DocumentObject* obj) const;
/// Returns the default bounding box of the origin (use this if you confused what should be s )
// TODO Delete me if not really needed (2015-09-01, Fat-Zer)
@@ -126,27 +136,33 @@ public:
protected:
/// Checks integrity of the Origin
App::DocumentObjectExecReturn *execute() override;
App::DocumentObjectExecReturn* execute() override;
/// Creates all corresponding Axes and Planes objects for the origin if they aren't linked yet
void setupObject () override;
void setupObject() override;
/// Removes all planes and axis if they are still linked to the document
void unsetupObject () override;
void unsetupObject() override;
private:
struct SetupData;
void setupOriginFeature (App::PropertyLink &featProp, const SetupData &data);
void setupOriginFeature(App::PropertyLink& featProp, const SetupData& data);
class OriginExtension : public GeoFeatureGroupExtension {
class OriginExtension: public GeoFeatureGroupExtension
{
Origin* obj;
public:
explicit OriginExtension(Origin* obj);
void initExtension(ExtensionContainer* obj) override;
bool extensionGetSubObject(DocumentObject *&ret, const char *subname,
PyObject **, Base::Matrix4D *, bool, int) const override;
bool extensionGetSubObject(DocumentObject*& ret,
const char* subname,
PyObject**,
Base::Matrix4D*,
bool,
int) const override;
};
OriginExtension extension;
};
} //namespace App
} // namespace App
#endif // APP_Origin_H
#endif // APP_Origin_H

View File

@@ -35,7 +35,7 @@ PROPERTY_SOURCE(App::Line, App::OriginFeature)
OriginFeature::OriginFeature()
{
ADD_PROPERTY_TYPE ( Role, (""), 0, App::Prop_ReadOnly, "Role of the feature in the Origin" ) ;
ADD_PROPERTY_TYPE(Role, (""), 0, App::Prop_ReadOnly, "Role of the feature in the Origin");
// Set placement to read-only
Placement.setStatus(Property::Hidden, true);
@@ -43,18 +43,20 @@ OriginFeature::OriginFeature()
OriginFeature::~OriginFeature() = default;
Origin * OriginFeature::getOrigin () {
App::Document *doc = getDocument();
auto origins = doc->getObjectsOfType ( App::Origin::getClassTypeId() );
Origin* OriginFeature::getOrigin()
{
App::Document* doc = getDocument();
auto origins = doc->getObjectsOfType(App::Origin::getClassTypeId());
auto originIt= std::find_if (origins.begin(), origins.end(), [this] (DocumentObject *origin) {
assert ( origin->isDerivedFrom ( App::Origin::getClassTypeId() ) );
return static_cast<App::Origin *> (origin)->hasObject (this);
} );
auto originIt = std::find_if(origins.begin(), origins.end(), [this](DocumentObject* origin) {
assert(origin->isDerivedFrom(App::Origin::getClassTypeId()));
return static_cast<App::Origin*>(origin)->hasObject(this);
});
if (originIt == origins.end()) {
return nullptr;
} else {
assert ( (*originIt)->isDerivedFrom ( App::Origin::getClassTypeId() ) );
return static_cast<App::Origin *> (*originIt);
}
else {
assert((*originIt)->isDerivedFrom(App::Origin::getClassTypeId()));
return static_cast<App::Origin*>(*originIt);
}
}

View File

@@ -36,6 +36,7 @@ class Origin;
class AppExport OriginFeature: public App::GeoFeature
{
PROPERTY_HEADER_WITH_OVERRIDE(App::OriginFeature);
public:
/// additional information about the feature usage (e.g. "BasePlane-XY" or "Axis-X" in a Origin)
PropertyString Role;
@@ -45,25 +46,31 @@ public:
~OriginFeature() override;
/// Finds the origin object this plane belongs to
App::Origin *getOrigin ();
App::Origin* getOrigin();
};
class AppExport Plane: public App::OriginFeature {
class AppExport Plane: public App::OriginFeature
{
PROPERTY_HEADER_WITH_OVERRIDE(App::OriginFeature);
public:
const char* getViewProviderName() const override {
const char* getViewProviderName() const override
{
return "Gui::ViewProviderPlane";
}
};
class AppExport Line: public App::OriginFeature {
class AppExport Line: public App::OriginFeature
{
PROPERTY_HEADER_WITH_OVERRIDE(App::OriginFeature);
public:
const char* getViewProviderName() const override {
const char* getViewProviderName() const override
{
return "Gui::ViewProviderLine";
}
};
} //namespace App
} // namespace App
#endif /* end of include guard: ORIGINFEATURE_H */

View File

@@ -38,139 +38,172 @@ using namespace App;
EXTENSION_PROPERTY_SOURCE(App::OriginGroupExtension, App::GeoFeatureGroupExtension)
OriginGroupExtension::OriginGroupExtension () {
OriginGroupExtension::OriginGroupExtension()
{
initExtensionType(OriginGroupExtension::getExtensionClassTypeId());
EXTENSION_ADD_PROPERTY_TYPE ( Origin, (nullptr), 0, App::Prop_Hidden, "Origin linked to the group" );
EXTENSION_ADD_PROPERTY_TYPE(Origin,
(nullptr),
0,
App::Prop_Hidden,
"Origin linked to the group");
Origin.setScope(LinkScope::Child);
}
OriginGroupExtension::~OriginGroupExtension () = default;
OriginGroupExtension::~OriginGroupExtension() = default;
App::Origin *OriginGroupExtension::getOrigin () const {
App::DocumentObject *originObj = Origin.getValue ();
App::Origin* OriginGroupExtension::getOrigin() const
{
App::DocumentObject* originObj = Origin.getValue();
if ( !originObj ) {
if (!originObj) {
std::stringstream err;
err << "Can't find Origin for \"" << getExtendedObject()->getFullName () << "\"";
throw Base::RuntimeError ( err.str().c_str () );
} else if (! originObj->isDerivedFrom ( App::Origin::getClassTypeId() ) ) {
err << "Can't find Origin for \"" << getExtendedObject()->getFullName() << "\"";
throw Base::RuntimeError(err.str().c_str());
}
else if (!originObj->isDerivedFrom(App::Origin::getClassTypeId())) {
std::stringstream err;
err << "Bad object \"" << originObj->getFullName () << "\"(" << originObj->getTypeId().getName()
<< ") linked to the Origin of \"" << getExtendedObject()->getFullName () << "\"";
throw Base::RuntimeError ( err.str().c_str () );
} else {
return static_cast<App::Origin *> ( originObj );
err << "Bad object \"" << originObj->getFullName() << "\"("
<< originObj->getTypeId().getName() << ") linked to the Origin of \""
<< getExtendedObject()->getFullName() << "\"";
throw Base::RuntimeError(err.str().c_str());
}
else {
return static_cast<App::Origin*>(originObj);
}
}
bool OriginGroupExtension::extensionGetSubObject(DocumentObject *&ret, const char *subname,
PyObject **pyObj, Base::Matrix4D *mat, bool transform, int depth) const
bool OriginGroupExtension::extensionGetSubObject(DocumentObject*& ret,
const char* subname,
PyObject** pyObj,
Base::Matrix4D* mat,
bool transform,
int depth) const
{
App::DocumentObject *originObj = Origin.getValue ();
const char *dot;
if(originObj && originObj->isAttachedToDocument() &&
subname && (dot=strchr(subname,'.')))
{
App::DocumentObject* originObj = Origin.getValue();
const char* dot;
if (originObj && originObj->isAttachedToDocument() && subname && (dot = strchr(subname, '.'))) {
bool found;
if(subname[0] == '$')
found = std::string(subname+1,dot)==originObj->Label.getValue();
else
found = std::string(subname,dot)==originObj->getNameInDocument();
if(found) {
if(mat && transform)
if (subname[0] == '$') {
found = std::string(subname + 1, dot) == originObj->Label.getValue();
}
else {
found = std::string(subname, dot) == originObj->getNameInDocument();
}
if (found) {
if (mat && transform) {
*mat *= const_cast<OriginGroupExtension*>(this)->placement().getValue().toMatrix();
ret = originObj->getSubObject(dot+1,pyObj,mat,true,depth+1);
}
ret = originObj->getSubObject(dot + 1, pyObj, mat, true, depth + 1);
return true;
}
}
return GeoFeatureGroupExtension::extensionGetSubObject(ret,subname,pyObj,mat,transform,depth);
return GeoFeatureGroupExtension::extensionGetSubObject(ret,
subname,
pyObj,
mat,
transform,
depth);
}
App::DocumentObject *OriginGroupExtension::getGroupOfObject (const DocumentObject* obj) {
App::DocumentObject* OriginGroupExtension::getGroupOfObject(const DocumentObject* obj)
{
if(!obj)
if (!obj) {
return nullptr;
}
bool isOriginFeature = obj->isDerivedFrom(App::OriginFeature::getClassTypeId());
auto list = obj->getInList();
for (auto o : list) {
if(o->hasExtension(App::OriginGroupExtension::getExtensionClassTypeId()))
if (o->hasExtension(App::OriginGroupExtension::getExtensionClassTypeId())) {
return o;
}
else if (isOriginFeature && o->isDerivedFrom(App::Origin::getClassTypeId())) {
auto result = getGroupOfObject(o);
if(result)
if (result) {
return result;
}
}
}
return nullptr;
}
short OriginGroupExtension::extensionMustExecute() {
if (Origin.isTouched ()) {
short OriginGroupExtension::extensionMustExecute()
{
if (Origin.isTouched()) {
return 1;
} else {
}
else {
return GeoFeatureGroupExtension::extensionMustExecute();
}
}
App::DocumentObjectExecReturn *OriginGroupExtension::extensionExecute() {
try { // try to find all base axis and planes in the origin
getOrigin ();
} catch (const Base::Exception &ex) {
//getExtendedObject()->setError ();
return new App::DocumentObjectExecReturn ( ex.what () );
App::DocumentObjectExecReturn* OriginGroupExtension::extensionExecute()
{
try { // try to find all base axis and planes in the origin
getOrigin();
}
catch (const Base::Exception& ex) {
// getExtendedObject()->setError ();
return new App::DocumentObjectExecReturn(ex.what());
}
return GeoFeatureGroupExtension::extensionExecute ();
return GeoFeatureGroupExtension::extensionExecute();
}
App::DocumentObject *OriginGroupExtension::getLocalizedOrigin(App::Document *doc) {
App::DocumentObject *originObject = doc->addObject ( "App::Origin", "Origin" );
App::DocumentObject* OriginGroupExtension::getLocalizedOrigin(App::Document* doc)
{
App::DocumentObject* originObject = doc->addObject("App::Origin", "Origin");
QByteArray byteArray = tr("Origin").toUtf8();
originObject->Label.setValue(byteArray.constData());
return originObject;
}
void OriginGroupExtension::onExtendedSetupObject () {
App::Document *doc = getExtendedObject()->getDocument ();
void OriginGroupExtension::onExtendedSetupObject()
{
App::Document* doc = getExtendedObject()->getDocument();
App::DocumentObject *originObj = getLocalizedOrigin(doc);
App::DocumentObject* originObj = getLocalizedOrigin(doc);
assert ( originObj && originObj->isDerivedFrom ( App::Origin::getClassTypeId () ) );
Origin.setValue (originObj);
assert(originObj && originObj->isDerivedFrom(App::Origin::getClassTypeId()));
Origin.setValue(originObj);
GeoFeatureGroupExtension::onExtendedSetupObject ();
GeoFeatureGroupExtension::onExtendedSetupObject();
}
void OriginGroupExtension::onExtendedUnsetupObject () {
App::DocumentObject *origin = Origin.getValue ();
if (origin && !origin->isRemoving ()) {
origin->getDocument ()->removeObject (origin->getNameInDocument());
void OriginGroupExtension::onExtendedUnsetupObject()
{
App::DocumentObject* origin = Origin.getValue();
if (origin && !origin->isRemoving()) {
origin->getDocument()->removeObject(origin->getNameInDocument());
}
GeoFeatureGroupExtension::onExtendedUnsetupObject ();
GeoFeatureGroupExtension::onExtendedUnsetupObject();
}
void OriginGroupExtension::extensionOnChanged(const Property* p) {
if(p == &Origin) {
App::DocumentObject *owner = getExtendedObject();
App::DocumentObject *origin = Origin.getValue();
void OriginGroupExtension::extensionOnChanged(const Property* p)
{
if (p == &Origin) {
App::DocumentObject* owner = getExtendedObject();
App::DocumentObject* origin = Origin.getValue();
// Document::Importing indicates the object is being imported (i.e.
// copied). So check the Origin ownership here to prevent copy without
// dependency
if (origin && owner && owner->getDocument()
&& owner->getDocument()->testStatus(Document::Importing)) {
&& owner->getDocument()->testStatus(Document::Importing)) {
for (auto o : origin->getInList()) {
if(o != owner && o->hasExtension(App::OriginGroupExtension::getExtensionClassTypeId())) {
App::Document *document = owner->getDocument();
// Temporarily reset 'Restoring' status to allow document to auto label new objects
Base::ObjectStatusLocker<Document::Status, Document> guard(
Document::Restoring, document, false);
if (o != owner
&& o->hasExtension(App::OriginGroupExtension::getExtensionClassTypeId())) {
App::Document* document = owner->getDocument();
// Temporarily reset 'Restoring' status to allow document to auto label new
// objects
Base::ObjectStatusLocker<Document::Status, Document> guard(Document::Restoring,
document,
false);
Origin.setValue(getLocalizedOrigin(document));
FC_WARN("Reset origin in " << owner->getFullName());
return;
@@ -183,83 +216,99 @@ void OriginGroupExtension::extensionOnChanged(const Property* p) {
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;
// 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<App::Property*> list;
obj->getPropertyList(list);
for(App::Property* prop : list) {
if(prop->isDerivedFrom<App::PropertyLink>()) {
for (App::Property* prop : list) {
if (prop->isDerivedFrom<App::PropertyLink>()) {
auto p = static_cast<App::PropertyLink*>(prop);
if(!p->getValue() || !p->getValue()->isDerivedFrom(App::OriginFeature::getClassTypeId()))
if (!p->getValue()
|| !p->getValue()->isDerivedFrom(App::OriginFeature::getClassTypeId())) {
continue;
}
p->setValue(getOrigin()->getOriginFeature(static_cast<OriginFeature*>(p->getValue())->Role.getValue()));
p->setValue(getOrigin()->getOriginFeature(
static_cast<OriginFeature*>(p->getValue())->Role.getValue()));
}
else if(prop->isDerivedFrom<App::PropertyLinkList>()) {
else if (prop->isDerivedFrom<App::PropertyLinkList>()) {
auto p = static_cast<App::PropertyLinkList*>(prop);
auto vec = p->getValues();
std::vector<App::DocumentObject*> result;
bool changed = false;
for(App::DocumentObject* o : vec) {
if(!o || !o->isDerivedFrom(App::OriginFeature::getClassTypeId()))
for (App::DocumentObject* o : vec) {
if (!o || !o->isDerivedFrom(App::OriginFeature::getClassTypeId())) {
result.push_back(o);
}
else {
result.push_back(getOrigin()->getOriginFeature(static_cast<OriginFeature*>(o)->Role.getValue()));
result.push_back(getOrigin()->getOriginFeature(
static_cast<OriginFeature*>(o)->Role.getValue()));
changed = true;
}
}
if(changed)
if (changed) {
static_cast<App::PropertyLinkList*>(prop)->setValues(result);
}
}
else if(prop->isDerivedFrom<App::PropertyLinkSub>()) {
else if (prop->isDerivedFrom<App::PropertyLinkSub>()) {
auto p = static_cast<App::PropertyLinkSub*>(prop);
if(!p->getValue() || !p->getValue()->isDerivedFrom(App::OriginFeature::getClassTypeId()))
if (!p->getValue()
|| !p->getValue()->isDerivedFrom(App::OriginFeature::getClassTypeId())) {
continue;
}
std::vector<std::string> subValues = p->getSubValues();
p->setValue(getOrigin()->getOriginFeature(static_cast<OriginFeature*>(p->getValue())->Role.getValue()), subValues);
p->setValue(getOrigin()->getOriginFeature(
static_cast<OriginFeature*>(p->getValue())->Role.getValue()),
subValues);
}
else if(prop->isDerivedFrom<App::PropertyLinkSubList>()) {
else if (prop->isDerivedFrom<App::PropertyLinkSubList>()) {
auto p = static_cast<App::PropertyLinkSubList*>(prop);
auto vec = p->getSubListValues();
bool changed = false;
for(auto &v : vec) {
if(v.first && v.first->isDerivedFrom(App::OriginFeature::getClassTypeId())) {
v.first = getOrigin()->getOriginFeature(static_cast<OriginFeature*>(v.first)->Role.getValue());
for (auto& v : vec) {
if (v.first && v.first->isDerivedFrom(App::OriginFeature::getClassTypeId())) {
v.first = getOrigin()->getOriginFeature(
static_cast<OriginFeature*>(v.first)->Role.getValue());
changed = true;
}
}
if(changed)
if (changed) {
p->setSubListValues(vec);
}
}
}
}
std::vector< DocumentObject* > OriginGroupExtension::addObjects(std::vector<DocumentObject*> objs) {
std::vector<DocumentObject*> OriginGroupExtension::addObjects(std::vector<DocumentObject*> objs)
{
for(auto obj : objs)
for (auto obj : objs) {
relinkToOrigin(obj);
}
return App::GeoFeatureGroupExtension::addObjects(objs);
}
bool OriginGroupExtension::hasObject(const DocumentObject* obj, bool recursive) const {
bool OriginGroupExtension::hasObject(const DocumentObject* obj, bool recursive) const
{
if(Origin.getValue() && (obj == getOrigin() || getOrigin()->hasObject(obj)))
if (Origin.getValue() && (obj == getOrigin() || getOrigin()->hasObject(obj))) {
return true;
}
return App::GroupExtension::hasObject(obj, recursive);
}
// Python feature ---------------------------------------------------------
namespace App {
namespace App
{
EXTENSION_PROPERTY_SOURCE_TEMPLATE(App::OriginGroupExtensionPython, App::OriginGroupExtension)
// explicit template instantiation
template class AppExport ExtensionPythonT<GroupExtensionPythonT<OriginGroupExtension>>;
}
} // namespace App

View File

@@ -26,26 +26,28 @@
#include "GeoFeatureGroupExtension.h"
#include "QCoreApplication"
namespace App {
namespace App
{
class Origin;
/**
* Represents an abstract placeable group of objects with an associated Origin
*/
class AppExport OriginGroupExtension : public App::GeoFeatureGroupExtension
class AppExport OriginGroupExtension: public App::GeoFeatureGroupExtension
{
EXTENSION_PROPERTY_HEADER_WITH_OVERRIDE(App::OriginGroupExtension);
Q_DECLARE_TR_FUNCTIONS(App::OriginGroupExtension)
public:
OriginGroupExtension ();
~OriginGroupExtension () override;
OriginGroupExtension();
~OriginGroupExtension() override;
/// Returns the origin link or throws an exception
App::Origin *getOrigin () const;
App::Origin* getOrigin() const;
/// returns the type name of the ViewProvider
virtual const char* getViewProviderName () const {
virtual const char* getViewProviderName() const
{
return "Gui::ViewProviderOriginGroup";
}
@@ -54,10 +56,10 @@ public:
* In case this object is not part of any geoFeatureGroup, 0 is returned.
* @param obj the object to search for
*/
static DocumentObject* getGroupOfObject (const DocumentObject* obj);
static DocumentObject* getGroupOfObject(const DocumentObject* obj);
/// Returns true on changing OriginFeature set
short extensionMustExecute () override;
short extensionMustExecute() override;
/// Origin linked to the group
PropertyLink Origin;
@@ -68,26 +70,30 @@ public:
std::vector<DocumentObject*> addObjects(std::vector<DocumentObject*> obj) override;
bool hasObject(const DocumentObject* obj, bool recursive = false) const override;
bool extensionGetSubObject(DocumentObject *&ret, const char *subname, PyObject **pyObj,
Base::Matrix4D *mat, bool transform, int depth) const override;
bool extensionGetSubObject(DocumentObject*& ret,
const char* subname,
PyObject** pyObj,
Base::Matrix4D* mat,
bool transform,
int depth) const override;
void extensionOnChanged(const Property* p) override;
protected:
/// Checks integrity of the Origin
App::DocumentObjectExecReturn *extensionExecute () override;
App::DocumentObjectExecReturn* extensionExecute() override;
/// Creates the corresponding Origin object
void onExtendedSetupObject () override;
void onExtendedSetupObject() override;
/// Removes all planes and axis if they are still linked to the document
void onExtendedUnsetupObject () override;
void onExtendedUnsetupObject() override;
private:
/// Creates a localized Origin object
App::DocumentObject *getLocalizedOrigin(App::Document *doc);
App::DocumentObject* getLocalizedOrigin(App::Document* doc);
};
using OriginGroupExtensionPython = ExtensionPythonT<GroupExtensionPythonT<OriginGroupExtension>>;
} /* App */
} // namespace App
#endif /* end of include guard: ORIGINGROUP_H_QHTU73IF */

View File

@@ -16,4 +16,3 @@
</PythonExport>
</GenerateModel>

View File

@@ -35,7 +35,7 @@ std::string OriginGroupExtensionPy::representation() const
return {"<OriginGroup object>"};
}
PyObject *OriginGroupExtensionPy::getCustomAttributes(const char* /*attr*/) const
PyObject* OriginGroupExtensionPy::getCustomAttributes(const char* /*attr*/) const
{
return nullptr;
}
@@ -44,5 +44,3 @@ int OriginGroupExtensionPy::setCustomAttributes(const char* /*attr*/, PyObject*
{
return 0;
}

View File

@@ -42,7 +42,7 @@ PROPERTY_SOURCE_WITH_EXTENSIONS(App::Part, App::GeoFeature)
Part::Part()
{
ADD_PROPERTY(Type,(""));
ADD_PROPERTY(Type, (""));
ADD_PROPERTY_TYPE(Material, (nullptr), 0, App::Prop_None, "The Material for this Part");
ADD_PROPERTY_TYPE(Meta, (), 0, App::Prop_None, "Map with additional meta information");
@@ -62,22 +62,25 @@ Part::Part()
Part::~Part() = default;
static App::Part *_getPartOfObject(const DocumentObject *obj,
std::set<const DocumentObject*> *objset)
static App::Part* _getPartOfObject(const DocumentObject* obj,
std::set<const DocumentObject*>* objset)
{
// as a Part is a geofeaturegroup it must directly link to all
// objects it contains, even if they are in additional groups etc.
// But we still must call 'hasObject()' to exclude link brought in by
// expressions.
for (auto inObj : obj->getInList()) {
if (objset && !objset->insert(inObj).second)
if (objset && !objset->insert(inObj).second) {
continue;
}
auto group = inObj->getExtensionByType<GeoFeatureGroupExtension>(true);
if(group && group->hasObject(obj)) {
if(inObj->isDerivedFrom(App::Part::getClassTypeId()))
if (group && group->hasObject(obj)) {
if (inObj->isDerivedFrom(App::Part::getClassTypeId())) {
return static_cast<App::Part*>(inObj);
else if (objset)
}
else if (objset) {
return _getPartOfObject(inObj, objset);
}
// Only one parent geofeature group per object, so break
break;
}
@@ -86,35 +89,41 @@ static App::Part *_getPartOfObject(const DocumentObject *obj,
return nullptr;
}
App::Part *Part::getPartOfObject (const DocumentObject* obj, bool recursive) {
if (!recursive)
App::Part* Part::getPartOfObject(const DocumentObject* obj, bool recursive)
{
if (!recursive) {
return _getPartOfObject(obj, nullptr);
std::set<const DocumentObject *> objset;
}
std::set<const DocumentObject*> objset;
objset.insert(obj);
return _getPartOfObject(obj, &objset);
}
PyObject *Part::getPyObject()
PyObject* Part::getPyObject()
{
if (PythonObject.is(Py::_None())){
if (PythonObject.is(Py::_None())) {
// ref counter is set to 1
PythonObject = Py::Object(new PartPy(this),true);
PythonObject = Py::Object(new PartPy(this), true);
}
return Py::new_reference_to(PythonObject);
}
void Part::handleChangedPropertyType(Base::XMLReader &reader, const char *TypeName, App::Property *prop)
void Part::handleChangedPropertyType(Base::XMLReader& reader,
const char* TypeName,
App::Property* prop)
{
// Migrate Material from App::PropertyMap to App::PropertyLink
if (!strcmp(TypeName, "App::PropertyMap")) {
App::PropertyMap oldvalue;
oldvalue.Restore(reader);
if (oldvalue.getSize()) {
auto oldprop = static_cast<App::PropertyMap*>(addDynamicProperty("App::PropertyMap", "Material_old", "Base"));
auto oldprop = static_cast<App::PropertyMap*>(
addDynamicProperty("App::PropertyMap", "Material_old", "Base"));
oldprop->setValues(oldvalue.getValues());
}
} else {
}
else {
App::GeoFeature::handleChangedPropertyType(reader, TypeName, prop);
}
}
@@ -124,21 +133,21 @@ void Part::handleChangedPropertyType(Base::XMLReader &reader, const char *TypeNa
// Not quite sure yet making Part derivable in Python is good Idea!
// JR 2014
//namespace App {
// namespace App {
///// @cond DOXERR
//PROPERTY_SOURCE_TEMPLATE(App::PartPython, App::Part)
//template<> const char* App::PartPython::getViewProviderName(void) const {
// return "Gui::ViewProviderPartPython";
//}
//template<> PyObject* App::PartPython::getPyObject(void) {
// if (PythonObject.is(Py::_None())) {
// // ref counter is set to 1
// PythonObject = Py::Object(new FeaturePythonPyT<App::PartPy>(this),true);
// }
// return Py::new_reference_to(PythonObject);
//}
// PROPERTY_SOURCE_TEMPLATE(App::PartPython, App::Part)
// template<> const char* App::PartPython::getViewProviderName(void) const {
// return "Gui::ViewProviderPartPython";
// }
// template<> PyObject* App::PartPython::getPyObject(void) {
// if (PythonObject.is(Py::_None())) {
// // ref counter is set to 1
// PythonObject = Py::Object(new FeaturePythonPyT<App::PartPy>(this),true);
// }
// return Py::new_reference_to(PythonObject);
// }
///// @endcond
//
//// explicit template instantiation
//template class AppExport FeaturePythonT<App::Part>;
//}
// template class AppExport FeaturePythonT<App::Part>;
// }

View File

@@ -35,7 +35,7 @@ namespace App
/** Base class of all geometric document objects.
*/
class AppExport Part : public App::GeoFeature, public App::OriginGroupExtension
class AppExport Part: public App::GeoFeature, public App::OriginGroupExtension
{
PROPERTY_HEADER_WITH_EXTENSIONS(App::Part);
@@ -44,26 +44,26 @@ public:
PropertyString Type;
/** @name base properties of all Assembly Items
* These properties correspond mostly to the meta information
* in the App::Document class
*/
* These properties correspond mostly to the meta information
* in the App::Document class
*/
//@{
/// Id e.g. Part number
App::PropertyString Id;
App::PropertyString Id;
/// unique identifier of the Item
App::PropertyUUID Uid;
App::PropertyUUID Uid;
/// material descriptions
App::PropertyLink Material;
App::PropertyLink Material;
/// Meta descriptions
App::PropertyMap Meta;
App::PropertyMap Meta;
/** License string
* Holds the short license string for the Item, e.g. CC-BY
* for the Creative Commons license suit.
*/
App::PropertyString License;
* Holds the short license string for the Item, e.g. CC-BY
* for the Creative Commons license suit.
*/
App::PropertyString License;
/// License description/contract URL
App::PropertyString LicenseURL;
App::PropertyString LicenseURL;
//@}
/** @name Visual properties */
@@ -80,12 +80,15 @@ public:
~Part() override;
/// returns the type name of the ViewProvider
const char* getViewProviderName() const override {
const char* getViewProviderName() const override
{
return "Gui::ViewProviderPart";
}
void handleChangedPropertyType(Base::XMLReader &reader, const char *TypeName, App::Property *prop) override;
void handleChangedPropertyType(Base::XMLReader& reader,
const char* TypeName,
App::Property* prop) override;
/**
* Returns the part which contains this object.
@@ -93,14 +96,14 @@ public:
* @param obj the object to search for
* @param recursive: whether to recursively find any grand parent Part container
*/
static App::Part* getPartOfObject (const DocumentObject* obj, bool recursive=true);
static App::Part* getPartOfObject(const DocumentObject* obj, bool recursive = true);
PyObject *getPyObject() override;
PyObject* getPyObject() override;
};
//using PartPython = App::FeaturePythonT<Part>;
// using PartPython = App::FeaturePythonT<Part>;
} //namespace App
} // namespace App
#endif // APP_Part_H
#endif // APP_Part_H

View File

@@ -35,7 +35,7 @@ std::string PartPy::representation() const
return {"<Part object>"};
}
PyObject *PartPy::getCustomAttributes(const char* /*attr*/) const
PyObject* PartPy::getCustomAttributes(const char* /*attr*/) const
{
return nullptr;
}
@@ -44,4 +44,3 @@ int PartPy::setCustomAttributes(const char* /*attr*/, PyObject* /*obj*/)
{
return 0;
}

View File

@@ -28,7 +28,6 @@
using namespace App;
Path::Path(const std::vector<Base::Persistence *> &PathVector)
: _PathVector(PathVector)
{
}
Path::Path(const std::vector<Base::Persistence*>& PathVector)
: _PathVector(PathVector)
{}

View File

@@ -36,21 +36,22 @@ namespace App
class AppExport Path
{
protected:
std::vector<Base::Persistence *> _PathVector;
std::vector<Base::Persistence*> _PathVector;
public:
/// Constructor
Path() = default;
explicit Path(const std::vector<Base::Persistence *> & PathVector);
explicit Path(const std::vector<Base::Persistence*>& PathVector);
virtual ~Path() = default;
const std::vector<Base::Persistence *> & getVector() const {
const std::vector<Base::Persistence*>& getVector() const
{
return _PathVector;
}
};
} //namespace App
} // namespace App
#endif // APP_Path_H
#endif // APP_Path_H

View File

@@ -42,16 +42,14 @@ Placement::Placement() = default;
Placement::~Placement() = default;
// Python feature ---------------------------------------------------------
namespace App {
namespace App
{
PROPERTY_SOURCE_TEMPLATE(App::PlacementPython, App::Placement)
template<> const char* App::PlacementPython::getViewProviderName() const {
return "Gui::ViewProviderPlacementPython";
template<>
const char* App::PlacementPython::getViewProviderName() const
{
return "Gui::ViewProviderPlacementPython";
}
template class AppExport FeaturePythonT<App::Placement>;
}
} // namespace App

View File

@@ -37,21 +37,20 @@ class AppExport Placement: public App::GeoFeature
PROPERTY_HEADER_WITH_OVERRIDE(App::Placement);
public:
/// Constructor
Placement();
~Placement() override;
/// returns the type name of the ViewProvider
const char* getViewProviderName() const override {
return "Gui::ViewProviderPlacement";
}
/// Constructor
Placement();
~Placement() override;
/// returns the type name of the ViewProvider
const char* getViewProviderName() const override
{
return "Gui::ViewProviderPlacement";
}
};
using PlacementPython = App::FeaturePythonT<App::Placement>;
} //namespace App
} // namespace App
#endif

View File

@@ -22,4 +22,3 @@
#include "PreCompiled.h"

View File

@@ -27,12 +27,13 @@
// point at which warnings of overly long specifiers disabled
#ifdef _MSC_VER
#pragma warning( disable : 4251 )
#pragma warning( disable : 4273 )
#pragma warning( disable : 4275 )
#pragma warning( disable : 4482 ) // nonstandard extension used: enum 'App::ObjectStatus' used in qualified name
#pragma warning( disable : 4503 )
#pragma warning( disable : 4786 ) // specifier longer then 255 chars
#pragma warning(disable : 4251)
#pragma warning(disable : 4273)
#pragma warning(disable : 4275)
#pragma warning(disable : 4482) // nonstandard extension used: enum 'App::ObjectStatus' used in
// qualified name
#pragma warning(disable : 4503)
#pragma warning(disable : 4786) // specifier longer then 255 chars
#endif
#ifdef FC_OS_WIN32
@@ -52,15 +53,15 @@
#include <cfloat>
#ifdef FC_OS_WIN32
# include <crtdbg.h>
# include <direct.h>
# include <windows.h>
#include <crtdbg.h>
#include <direct.h>
#include <windows.h>
#endif
#if defined(FC_OS_LINUX) || defined(FC_OS_MACOSX) || defined(FC_OS_BSD)
# include <pwd.h>
# include <unistd.h>
# include <sys/types.h>
#include <pwd.h>
#include <unistd.h>
#include <sys/types.h>
#endif
// Streams
@@ -100,6 +101,6 @@
#include <boost/filesystem/operations.hpp>
#include <boost/filesystem/path.hpp>
#endif //_PreComp_
#endif //_PreComp_
#endif // APP_PRECOMPILED_H
#endif // APP_PRECOMPILED_H

View File

@@ -1,25 +1,25 @@
/***************************************************************************
* Copyright (c) 2002 Jürgen Riegel <juergen.riegel@web.de> *
* *
* This file is part of the FreeCAD CAx development system. *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Library General Public License (LGPL) *
* as published by the Free Software Foundation; either version 2 of *
* the License, or (at your option) any later version. *
* for detail see the LICENCE text file. *
* *
* FreeCAD 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 Library General Public License for more details. *
* *
* You should have received a copy of the GNU Library General Public *
* License along with FreeCAD; if not, write to the Free Software *
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* USA *
* *
***************************************************************************/
* Copyright (c) 2002 Jürgen Riegel <juergen.riegel@web.de> *
* *
* This file is part of the FreeCAD CAx development system. *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Library General Public License (LGPL) *
* as published by the Free Software Foundation; either version 2 of *
* the License, or (at your option) any later version. *
* for detail see the LICENCE text file. *
* *
* FreeCAD 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 Library General Public License for more details. *
* *
* You should have received a copy of the GNU Library General Public *
* License along with FreeCAD; if not, write to the Free Software *
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* USA *
* *
***************************************************************************/
#ifndef PROGRAMOPTIONSUTILITIES_H
#define PROGRAMOPTIONSUTILITIES_H
@@ -28,12 +28,13 @@
#include <array>
#include <string>
namespace App::Util{
namespace App::Util
{
std::pair<std::string, std::string> customSyntax(std::string_view strIn)
{
if(strIn.size() < 2) {
return{};
if (strIn.size() < 2) {
return {};
}
char leadChr {strIn[0]};
@@ -53,7 +54,7 @@ std::pair<std::string, std::string> customSyntax(std::string_view strIn)
}
#endif
if(rest == "widgetcount"){
if (rest == "widgetcount") {
return {rest, ""};
}
@@ -73,11 +74,11 @@ std::pair<std::string, std::string> customSyntax(std::string_view strIn)
"title",
"visual"};
if(std::find(knowns.begin(), knowns.end(), rest) != knowns.end()) {
if (std::find(knowns.begin(), knowns.end(), rest) != knowns.end()) {
return {rest, "null"};
}
return {};
}
} // namespace
#endif// PROGRAMOPTIONSUTILITIES_H
} // namespace App::Util
#endif // PROGRAMOPTIONSUTILITIES_H

View File

@@ -64,12 +64,13 @@ XERCES_CPP_NAMESPACE_USE
#endif
using namespace App;
namespace {
namespace
{
class DocumentMetadata
{
public:
explicit DocumentMetadata(XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument* xmlDocument)
: xmlDocument{xmlDocument}
: xmlDocument {xmlDocument}
{}
ProjectFile::Metadata getMetadata() const
@@ -103,11 +104,13 @@ public:
private:
void readProgramVersion()
{
if (DOMNodeList* nodes = xmlDocument->getElementsByTagName(XStr("Document").unicodeForm())) {
if (DOMNodeList* nodes =
xmlDocument->getElementsByTagName(XStr("Document").unicodeForm())) {
for (XMLSize_t i = 0; i < nodes->getLength(); i++) {
DOMNode* node = nodes->item(i);
if (node->getNodeType() == DOMNode::ELEMENT_NODE) {
DOMNode* nameAttr = node->getAttributes()->getNamedItem(XStr("ProgramVersion").unicodeForm());
DOMNode* nameAttr =
node->getAttributes()->getNamedItem(XStr("ProgramVersion").unicodeForm());
if (nameAttr) {
std::string value = StrX(nameAttr->getNodeValue()).c_str();
metadata.programVersion = value;
@@ -163,12 +166,14 @@ private:
static std::string readValue(DOMNode* node)
{
if (node->getNodeType() == DOMNode::ELEMENT_NODE) {
if (DOMElement* child = static_cast<DOMElement*>(node)->getFirstElementChild()) { // NOLINT
if (DOMNode* nameAttr = child->getAttributes()->getNamedItem(XStr("value").unicodeForm())) {
std::string value = StrX(nameAttr->getNodeValue()).c_str();
return value;
}
}
if (DOMElement* child =
static_cast<DOMElement*>(node)->getFirstElementChild()) { // NOLINT
if (DOMNode* nameAttr =
child->getAttributes()->getNamedItem(XStr("value").unicodeForm())) {
std::string value = StrX(nameAttr->getNodeValue()).c_str();
return value;
}
}
}
return {};
@@ -178,7 +183,7 @@ private:
XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument* xmlDocument;
ProjectFile::Metadata metadata;
};
}
} // namespace
ProjectFile::ProjectFile()
: xmlDocument(nullptr)
@@ -259,8 +264,8 @@ std::list<ProjectFile::Object> ProjectFile::getObjects() const
for (XMLSize_t i = 0; i < nodes->getLength(); i++) {
DOMNode* node = nodes->item(i);
if (node->getNodeType() == DOMNode::ELEMENT_NODE) {
DOMNodeList* objectList =
static_cast<DOMElement*>(node)->getElementsByTagName(XStr("Object").unicodeForm()); // NOLINT
DOMNodeList* objectList = static_cast<DOMElement*>(node)->getElementsByTagName(
XStr("Object").unicodeForm()); // NOLINT
for (XMLSize_t j = 0; j < objectList->getLength(); j++) {
DOMNode* objectNode = objectList->item(j);
DOMNode* typeAttr =
@@ -291,8 +296,8 @@ std::list<std::string> ProjectFile::getObjectsOfType(const Base::Type& typeId) c
for (XMLSize_t i = 0; i < nodes->getLength(); i++) {
DOMNode* node = nodes->item(i);
if (node->getNodeType() == DOMNode::ELEMENT_NODE) {
DOMNodeList* objectList =
static_cast<DOMElement*>(node)->getElementsByTagName(XStr("Object").unicodeForm()); // NOLINT
DOMNodeList* objectList = static_cast<DOMElement*>(node)->getElementsByTagName(
XStr("Object").unicodeForm()); // NOLINT
for (XMLSize_t j = 0; j < objectList->getLength(); j++) {
DOMNode* objectNode = objectList->item(j);
DOMNode* typeAttr =
@@ -311,9 +316,7 @@ std::list<std::string> ProjectFile::getObjectsOfType(const Base::Type& typeId) c
return names;
}
bool ProjectFile::restoreObject(const std::string& name,
App::PropertyContainer* obj,
bool verbose)
bool ProjectFile::restoreObject(const std::string& name, App::PropertyContainer* obj, bool verbose)
{
Base::FileInfo fi(stdFile);
Base::ifstream file(fi, std::ios::in | std::ios::binary);
@@ -367,8 +370,8 @@ Base::Type ProjectFile::getTypeId(const std::string& name) const
for (XMLSize_t i = 0; i < nodes->getLength(); i++) {
DOMNode* node = nodes->item(i);
if (node->getNodeType() == DOMNode::ELEMENT_NODE) {
DOMNodeList* objectList =
static_cast<DOMElement*>(node)->getElementsByTagName(XStr("Object").unicodeForm()); // NOLINT
DOMNodeList* objectList = static_cast<DOMElement*>(node)->getElementsByTagName(
XStr("Object").unicodeForm()); // NOLINT
for (XMLSize_t j = 0; j < objectList->getLength(); j++) {
DOMNode* objectNode = objectList->item(j);
DOMNode* typeAttr =
@@ -388,8 +391,7 @@ Base::Type ProjectFile::getTypeId(const std::string& name) const
return Base::Type::badType();
}
std::list<ProjectFile::PropertyFile>
ProjectFile::getPropertyFiles(const std::string& name) const
std::list<ProjectFile::PropertyFile> ProjectFile::getPropertyFiles(const std::string& name) const
{
// <ObjectData Count="1">
// <Object name="Mesh">
@@ -409,8 +411,8 @@ ProjectFile::getPropertyFiles(const std::string& name) const
for (XMLSize_t i = 0; i < nodes->getLength(); i++) {
DOMNode* node = nodes->item(i);
if (node->getNodeType() == DOMNode::ELEMENT_NODE) {
DOMNodeList* objectList =
static_cast<DOMElement*>(node)->getElementsByTagName(XStr("Object").unicodeForm()); // NOLINT
DOMNodeList* objectList = static_cast<DOMElement*>(node)->getElementsByTagName(
XStr("Object").unicodeForm()); // NOLINT
for (XMLSize_t j = 0; j < objectList->getLength(); j++) {
DOMNode* objectNode = objectList->item(j);
DOMNode* nameAttr =
@@ -488,8 +490,8 @@ std::list<std::string> ProjectFile::getInputFiles(const std::string& name) const
for (XMLSize_t i = 0; i < nodes->getLength(); i++) {
DOMNode* node = nodes->item(i);
if (node->getNodeType() == DOMNode::ELEMENT_NODE) {
DOMNodeList* objectList =
static_cast<DOMElement*>(node)->getElementsByTagName(XStr("Object").unicodeForm()); // NOLINT
DOMNodeList* objectList = static_cast<DOMElement*>(node)->getElementsByTagName(
XStr("Object").unicodeForm()); // NOLINT
for (XMLSize_t j = 0; j < objectList->getLength(); j++) {
DOMNode* objectNode = objectList->item(j);
DOMNode* nameAttr =
@@ -646,8 +648,7 @@ std::string ProjectFile::replaceInputFiles(const std::map<std::string, std::istr
return fn;
}
std::string
ProjectFile::replacePropertyFiles(const std::map<std::string, App::Property*>& props)
std::string ProjectFile::replacePropertyFiles(const std::map<std::string, App::Property*>& props)
{
// create a new zip file with the name '<zipfile>.<uuid>'
std::string uuid = Base::Uuid::createUuid();

View File

@@ -35,7 +35,12 @@
#ifndef XERCES_CPP_NAMESPACE_BEGIN
#define XERCES_CPP_NAMESPACE_QUALIFIER
using namespace XERCES_CPP_NAMESPACE;
namespace XERCES_CPP_NAMESPACE { class DOMNode; class DOMElement; class DOMDocument; }
namespace XERCES_CPP_NAMESPACE
{
class DOMNode;
class DOMElement;
class DOMDocument;
} // namespace XERCES_CPP_NAMESPACE
#else
XERCES_CPP_NAMESPACE_BEGIN
class DOMDocument;
@@ -210,6 +215,6 @@ private:
};
} // namespace APP
} // namespace App
#endif // APP_PROJECTFILE_H

View File

@@ -40,7 +40,8 @@ using namespace App;
const int App::CellAddress::MAX_ROWS = 16384;
const int App::CellAddress::MAX_COLUMNS = 26 * 26 + 26;
namespace App {
namespace App
{
// From a given cell address the '$' must be at within the first
// few characters
bool maybeAbsolute(std::string_view address)
@@ -50,9 +51,9 @@ bool maybeAbsolute(std::string_view address)
address = address.substr(0, MAX_COLUMNS_LETTERS + 1);
return address.find("$") != std::string_view::npos;
}
}
} // namespace App
Range::Range(const char * range, bool normalize)
Range::Range(const char* range, bool normalize)
{
std::string from;
std::string to;
@@ -97,7 +98,7 @@ Range::Range(int _row_begin, int _col_begin, int _row_end, int _col_end, bool no
col_curr = col_begin;
}
Range::Range(const CellAddress &from, const CellAddress &to, bool normalize)
Range::Range(const CellAddress& from, const CellAddress& to, bool normalize)
: row_begin(from.row())
, col_begin(from.col())
, row_end(to.row())
@@ -128,8 +129,9 @@ bool Range::next()
return true;
}
if (col_curr < col_end) {
if (row_curr == row_end + 1)
if (row_curr == row_end + 1) {
return false;
}
row_curr = row_begin;
++col_curr;
return true;
@@ -138,14 +140,14 @@ bool Range::next()
}
/**
* @brief Decode a row specification into a 0-based integer.
*
* @param rowstr Row specified as a string, with "1" being the first row.
*
* @returns The row.
*/
* @brief Decode a row specification into a 0-based integer.
*
* @param rowstr Row specified as a string, with "1" being the first row.
*
* @returns The row.
*/
int App::decodeRow(const std::string &rowstr, bool silent)
int App::decodeRow(const std::string& rowstr, bool silent)
{
int row = validRow(rowstr);
@@ -159,10 +161,11 @@ int App::decodeRow(const std::string &rowstr, bool silent)
/**
* Assumes well-formed input. A through ZZZ. 0-based output
*/
int columnStringToNum(const std::string &colstr) {
int columnStringToNum(const std::string& colstr)
{
double out {0};
int pos {0};
for (auto chr = colstr.crbegin(); chr != colstr.crend(); chr++){
for (auto chr = colstr.crbegin(); chr != colstr.crend(); chr++) {
out += (*chr - 'A' + 1) * std::pow(26, pos++);
}
@@ -170,15 +173,15 @@ int columnStringToNum(const std::string &colstr) {
}
/**
* @brief Decode a column name string into a 0-based integer.
*
* @param colstr input string.
*
* @returns The column.
*
*/
* @brief Decode a column name string into a 0-based integer.
*
* @param colstr input string.
*
* @returns The column.
*
*/
int App::decodeColumn( const std::string &colstr, bool silent )
int App::decodeColumn(const std::string& colstr, bool silent)
{
if (validColumn(colstr)) {
return columnStringToNum(colstr);
@@ -192,19 +195,19 @@ int App::decodeColumn( const std::string &colstr, bool silent )
}
/**
* @brief Determine whether a row specification is valid or not.
*
* @param rowstr Row specified as a string, with "1" being the first row.
*
* @returns 0 or positive on success, -1 on error.
*/
* @brief Determine whether a row specification is valid or not.
*
* @param rowstr Row specified as a string, with "1" being the first row.
*
* @returns 0 or positive on success, -1 on error.
*/
int App::validRow(const std::string &rowstr)
int App::validRow(const std::string& rowstr)
{
char * end;
char* end;
int i = strtol(rowstr.c_str(), &end, 10);
if (i <=0 || i > CellAddress::MAX_ROWS || *end) {
if (i <= 0 || i > CellAddress::MAX_ROWS || *end) {
return -1;
}
@@ -212,30 +215,30 @@ int App::validRow(const std::string &rowstr)
}
/**
* @brief Determine if a string is a valid column specification.
*
* @param colstr input string.
*
* @returns true if valid, false if not.
*
*/
* @brief Determine if a string is a valid column specification.
*
* @param colstr input string.
*
* @returns true if valid, false if not.
*
*/
bool App::validColumn( const std::string &colstr )
bool App::validColumn(const std::string& colstr)
{
return boost::regex_match(colstr, boost::regex("[A-Z]{1,3}"));
}
/**
* @brief Convert a string address into integer \a row and \a column.
* row and col are 0-based.
*
* This function will throw an exception if the specified \a address is invalid.
*
* @param strAddress Address to parse.
*
*/
* @brief Convert a string address into integer \a row and \a column.
* row and col are 0-based.
*
* This function will throw an exception if the specified \a address is invalid.
*
* @param strAddress Address to parse.
*
*/
App::CellAddress App::stringToAddress(const char * strAddress, bool silent)
App::CellAddress App::stringToAddress(const char* strAddress, bool silent)
{
assert(strAddress);
@@ -243,24 +246,24 @@ App::CellAddress App::stringToAddress(const char * strAddress, bool silent)
boost::cmatch cm;
if (boost::regex_match(strAddress, cm, e)) {
bool absCol = (cm[1].first[0]=='$');
std::string r,c;
bool absCol = (cm[1].first[0] == '$');
std::string r, c;
if (absCol) {
c = std::string(cm[1].first+1,cm[1].second);
c = std::string(cm[1].first + 1, cm[1].second);
}
else {
c = std::string(cm[1].first,cm[1].second);
c = std::string(cm[1].first, cm[1].second);
}
bool absRow = (cm[2].first[0]=='$');
bool absRow = (cm[2].first[0] == '$');
if (absRow) {
r = std::string(cm[2].first+1,cm[2].second);
r = std::string(cm[2].first + 1, cm[2].second);
}
else {
r = std::string(cm[2].first,cm[2].second);
r = std::string(cm[2].first, cm[2].second);
}
return CellAddress(decodeRow(r,silent), decodeColumn(c,silent), absRow, absCol);
return CellAddress(decodeRow(r, silent), decodeColumn(c, silent), absRow, absCol);
}
else if (silent) {
return CellAddress();
@@ -270,10 +273,10 @@ App::CellAddress App::stringToAddress(const char * strAddress, bool silent)
}
/**
* @brief Convert given \a cell address into its string representation.
*
* @returns Address given as a string.
*/
* @brief Convert given \a cell address into its string representation.
*
* @returns Address given as a string.
*/
std::string App::CellAddress::toString(Cell cell) const
{
@@ -311,7 +314,8 @@ std::string App::CellAddress::toString(Cell cell) const
* If the passed string is a valid and absolute cell address it will be assigned to this instance.
* \return True if it's an absolute cell address and false otherwise
*/
bool App::CellAddress::parseAbsoluteAddress(const char *address) {
bool App::CellAddress::parseAbsoluteAddress(const char* address)
{
if (maybeAbsolute(address)) {
CellAddress addr = stringToAddress(address, true);
if (addr.isValid()) {
@@ -321,4 +325,3 @@ bool App::CellAddress::parseAbsoluteAddress(const char *address) {
}
return false;
}

View File

@@ -29,19 +29,22 @@
#include <FCGlobal.h>
#endif
namespace App {
namespace App
{
struct CellAddress;
AppExport CellAddress stringToAddress(const char *strAddress, bool silent=false);
AppExport int decodeColumn(const std::string &colstr, bool silent=false);
AppExport int decodeRow(const std::string &rowstr, bool silent=false);
AppExport bool validColumn(const std::string &colstr);
AppExport int validRow(const std::string &rowstr);
AppExport CellAddress stringToAddress(const char* strAddress, bool silent = false);
AppExport int decodeColumn(const std::string& colstr, bool silent = false);
AppExport int decodeRow(const std::string& rowstr, bool silent = false);
AppExport bool validColumn(const std::string& colstr);
AppExport int validRow(const std::string& rowstr);
struct AppExport CellAddress {
struct AppExport CellAddress
{
// See call of ENABLE_BITMASK_OPERATORS
enum class Cell {
enum class Cell
{
Absolute = 1,
ShowRow = 2,
ShowColumn = 4,
@@ -49,41 +52,79 @@ struct AppExport CellAddress {
ShowFull = Absolute | ShowRow | ShowColumn
};
explicit CellAddress(int row = -1, int col = -1, bool absRow=false, bool absCol=false)
: _row(row), _col(col), _absRow(absRow), _absCol(absCol)
{ }
explicit CellAddress(int row = -1, int col = -1, bool absRow = false, bool absCol = false)
: _row(row)
, _col(col)
, _absRow(absRow)
, _absCol(absCol)
{}
explicit CellAddress(const char * address) {
explicit CellAddress(const char* address)
{
*this = stringToAddress(address);
}
explicit CellAddress(const std::string & address) {
explicit CellAddress(const std::string& address)
{
*this = stringToAddress(address.c_str());
}
bool parseAbsoluteAddress(const char *txt);
bool parseAbsoluteAddress(const char* txt);
inline int row() const { return _row; }
inline int row() const
{
return _row;
}
inline int col() const { return _col; }
inline int col() const
{
return _col;
}
void setRow(int r, bool clip=false) { _row = (clip && r>=MAX_ROWS) ? MAX_ROWS-1 : r; }
void setRow(int r, bool clip = false)
{
_row = (clip && r >= MAX_ROWS) ? MAX_ROWS - 1 : r;
}
void setCol(int c, bool clip=false) { _col = (clip && c>=MAX_COLUMNS) ? MAX_COLUMNS-1 : c; }
void setCol(int c, bool clip = false)
{
_col = (clip && c >= MAX_COLUMNS) ? MAX_COLUMNS - 1 : c;
}
inline bool operator<(const CellAddress & other) const { return asInt() < other.asInt(); }
inline bool operator<(const CellAddress& other) const
{
return asInt() < other.asInt();
}
inline bool operator>(const CellAddress & other) const { return asInt() > other.asInt(); }
inline bool operator>(const CellAddress& other) const
{
return asInt() > other.asInt();
}
inline bool operator==(const CellAddress & other) const { return asInt() == other.asInt(); }
inline bool operator==(const CellAddress& other) const
{
return asInt() == other.asInt();
}
inline bool operator!=(const CellAddress & other) const { return asInt() != other.asInt(); }
inline bool operator!=(const CellAddress& other) const
{
return asInt() != other.asInt();
}
inline bool isValid() { return (row() >=0 && row() < MAX_ROWS && col() >= 0 && col() < MAX_COLUMNS); }
inline bool isValid()
{
return (row() >= 0 && row() < MAX_ROWS && col() >= 0 && col() < MAX_COLUMNS);
}
inline bool isAbsoluteRow() const { return _absRow; }
inline bool isAbsoluteRow() const
{
return _absRow;
}
inline bool isAbsoluteCol() const { return _absCol; }
inline bool isAbsoluteCol() const
{
return _absCol;
}
std::string toString(Cell = Cell::ShowFull) const;
@@ -94,8 +135,10 @@ struct AppExport CellAddress {
static const int MAX_COLUMNS;
protected:
inline unsigned int asInt() const { return ((_row << 16) | _col); }
inline unsigned int asInt() const
{
return ((_row << 16) | _col);
}
short _row;
short _col;
@@ -115,13 +158,14 @@ protected:
*
*/
class AppExport Range {
class AppExport Range
{
public:
explicit Range(const char *range, bool normalize=false);
explicit Range(const char* range, bool normalize = false);
Range(int _row_begin, int _col_begin, int _row_end, int _col_end, bool normalize=false);
Range(int _row_begin, int _col_begin, int _row_end, int _col_end, bool normalize = false);
Range(const CellAddress & from, const CellAddress & to, bool normalize=false);
Range(const CellAddress& from, const CellAddress& to, bool normalize = false);
bool next();
@@ -129,49 +173,87 @@ public:
void normalize();
/** Current row */
inline int row() const { return row_curr; }
/** Current column */
inline int column() const { return col_curr; }
/** Row count */
inline int rowCount() const { return row_end - row_begin + 1; }
/** Column count */
inline int colCount() const { return col_end - col_begin + 1; }
/** Position of start of range */
inline CellAddress from() const { return CellAddress(row_begin, col_begin); }
/** Position of end of range */
inline CellAddress to() const { return CellAddress(row_end, col_end); }
/** Start of range as a string */
inline std::string fromCellString() const { return CellAddress(row_begin, col_begin).toString(); }
/** End of range as a string */
inline std::string toCellString() const { return CellAddress(row_end, col_end).toString(); }
/** Current cell as a string */
inline std::string address() const { return CellAddress(row_curr, col_curr).toString(); }
/** The raneg as a string */
inline std::string rangeString() const {
return CellAddress(row_begin, col_begin).toString() + ":" + CellAddress(row_end, col_end).toString();
inline int row() const
{
return row_curr;
}
CellAddress operator*() const { return CellAddress(row_curr, col_curr); }
/** Current column */
inline int column() const
{
return col_curr;
}
inline bool operator<(const Range & other) const {
if(from() < other.from())
/** Row count */
inline int rowCount() const
{
return row_end - row_begin + 1;
}
/** Column count */
inline int colCount() const
{
return col_end - col_begin + 1;
}
/** Position of start of range */
inline CellAddress from() const
{
return CellAddress(row_begin, col_begin);
}
/** Position of end of range */
inline CellAddress to() const
{
return CellAddress(row_end, col_end);
}
/** Start of range as a string */
inline std::string fromCellString() const
{
return CellAddress(row_begin, col_begin).toString();
}
/** End of range as a string */
inline std::string toCellString() const
{
return CellAddress(row_end, col_end).toString();
}
/** Current cell as a string */
inline std::string address() const
{
return CellAddress(row_curr, col_curr).toString();
}
/** The raneg as a string */
inline std::string rangeString() const
{
return CellAddress(row_begin, col_begin).toString() + ":"
+ CellAddress(row_end, col_end).toString();
}
CellAddress operator*() const
{
return CellAddress(row_curr, col_curr);
}
inline bool operator<(const Range& other) const
{
if (from() < other.from()) {
return true;
if(from() > other.from())
}
if (from() > other.from()) {
return false;
}
return to() < other.to();
}
/** Number of elements in range */
inline int size() const { return (row_end - row_begin + 1) * (col_end - col_begin + 1); }
inline int size() const
{
return (row_end - row_begin + 1) * (col_end - col_begin + 1);
}
private:
int row_curr, col_curr;
@@ -179,8 +261,8 @@ private:
int row_end, col_end;
};
}
} // namespace App
ENABLE_BITMASK_OPERATORS(App::CellAddress::Cell)
#endif // RANGE_H
#endif // RANGE_H

View File

@@ -7,7 +7,7 @@
<location filename="../../Link.cpp" line="118"/>
<source>Stores the last user choice of whether to apply CopyOnChange setup to all links
that reference the same configurable object</source>
<translation>Pamti posljednji izbor korisnika o tome treba li primijeniti postavljanje CopyOnChange
<translation>Pamti posljednji izbor korisnika o tome treba li primijeniti postavljanje CopyOnChange
na sve veze koje referenciraju isti konfigurabilni objekt</translation>
</message>
</context>

Some files were not shown because too many files have changed in this diff Show More