3514 lines
96 KiB
C++
3514 lines
96 KiB
C++
/***************************************************************************
|
|
* Copyright (c) 2002 Jürgen Riegel <juergen.riegel@web.de> *
|
|
* *
|
|
* This file is part of the FreeCAD CAx development system. *
|
|
* *
|
|
* This library is free software; you can redistribute it and/or *
|
|
* modify it under the terms of the GNU Library General Public *
|
|
* License as published by the Free Software Foundation; either *
|
|
* version 2 of the License, or (at your option) any later version. *
|
|
* *
|
|
* This library 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 this library; see the file COPYING.LIB. If not, *
|
|
* write to the Free Software Foundation, Inc., 59 Temple Place, *
|
|
* Suite 330, Boston, MA 02111-1307, USA *
|
|
* *
|
|
***************************************************************************/
|
|
|
|
|
|
#include "PreCompiled.h"
|
|
|
|
#include <boost/algorithm/string/predicate.hpp>
|
|
#include <boost/math/special_functions/round.hpp>
|
|
|
|
#include <Base/Console.h>
|
|
#include <Base/Exception.h>
|
|
#include <Base/Interpreter.h>
|
|
#include <Base/Reader.h>
|
|
#include <Base/Writer.h>
|
|
#include <Base/Quantity.h>
|
|
#include <Base/Stream.h>
|
|
#include <Base/Tools.h>
|
|
|
|
#include "PropertyStandard.h"
|
|
#include "Application.h"
|
|
#include "Document.h"
|
|
#include "DocumentObject.h"
|
|
#include "MaterialPy.h"
|
|
#include "ObjectIdentifier.h"
|
|
|
|
|
|
using namespace App;
|
|
using namespace Base;
|
|
using namespace std;
|
|
|
|
|
|
//**************************************************************************
|
|
//**************************************************************************
|
|
// PropertyInteger
|
|
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
|
|
TYPESYSTEM_SOURCE(App::PropertyInteger, App::Property)
|
|
|
|
//**************************************************************************
|
|
// Construction/Destruction
|
|
|
|
|
|
PropertyInteger::PropertyInteger()
|
|
{
|
|
_lValue = 0;
|
|
}
|
|
|
|
|
|
PropertyInteger::~PropertyInteger() = default;
|
|
|
|
//**************************************************************************
|
|
// Base class implementer
|
|
|
|
|
|
void PropertyInteger::setValue(long lValue)
|
|
{
|
|
aboutToSetValue();
|
|
_lValue = lValue;
|
|
hasSetValue();
|
|
}
|
|
|
|
long PropertyInteger::getValue() const
|
|
{
|
|
return _lValue;
|
|
}
|
|
|
|
PyObject* PropertyInteger::getPyObject()
|
|
{
|
|
return Py_BuildValue("l", _lValue);
|
|
}
|
|
|
|
void PropertyInteger::setPyObject(PyObject* value)
|
|
{
|
|
if (PyLong_Check(value)) {
|
|
aboutToSetValue();
|
|
_lValue = PyLong_AsLong(value);
|
|
hasSetValue();
|
|
}
|
|
else {
|
|
std::string error = std::string("type must be int, not ");
|
|
error += value->ob_type->tp_name;
|
|
throw Base::TypeError(error);
|
|
}
|
|
}
|
|
|
|
void PropertyInteger::Save(Base::Writer& writer) const
|
|
{
|
|
writer.Stream() << writer.ind() << "<Integer value=\"" << _lValue << "\"/>" << std::endl;
|
|
}
|
|
|
|
void PropertyInteger::Restore(Base::XMLReader& reader)
|
|
{
|
|
// read my Element
|
|
reader.readElement("Integer");
|
|
// get the value of my Attribute
|
|
setValue(reader.getAttributeAsInteger("value"));
|
|
}
|
|
|
|
Property* PropertyInteger::Copy() const
|
|
{
|
|
PropertyInteger* p = new PropertyInteger();
|
|
p->_lValue = _lValue;
|
|
return p;
|
|
}
|
|
|
|
void PropertyInteger::Paste(const Property& from)
|
|
{
|
|
aboutToSetValue();
|
|
_lValue = dynamic_cast<const PropertyInteger&>(from)._lValue;
|
|
hasSetValue();
|
|
}
|
|
|
|
void PropertyInteger::setPathValue(const ObjectIdentifier& path, const boost::any& value)
|
|
{
|
|
verifyPath(path);
|
|
|
|
if (value.type() == typeid(long)) {
|
|
setValue(boost::any_cast<long>(value));
|
|
}
|
|
else if (value.type() == typeid(int)) {
|
|
setValue(boost::any_cast<int>(value));
|
|
}
|
|
else if (value.type() == typeid(double)) {
|
|
setValue(boost::math::round(boost::any_cast<double>(value)));
|
|
}
|
|
else if (value.type() == typeid(float)) {
|
|
setValue(boost::math::round(boost::any_cast<float>(value)));
|
|
}
|
|
else if (value.type() == typeid(Quantity)) {
|
|
setValue(boost::math::round(boost::any_cast<Quantity>(value).getValue()));
|
|
}
|
|
else {
|
|
throw bad_cast();
|
|
}
|
|
}
|
|
|
|
|
|
//**************************************************************************
|
|
//**************************************************************************
|
|
// PropertyPath
|
|
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
|
|
TYPESYSTEM_SOURCE(App::PropertyPath, App::Property)
|
|
|
|
//**************************************************************************
|
|
// Construction/Destruction
|
|
|
|
PropertyPath::PropertyPath() = default;
|
|
|
|
PropertyPath::~PropertyPath() = default;
|
|
|
|
|
|
//**************************************************************************
|
|
// Base class implementer
|
|
|
|
|
|
//**************************************************************************
|
|
// Setter/getter for the property
|
|
|
|
void PropertyPath::setValue(const boost::filesystem::path& Path)
|
|
{
|
|
aboutToSetValue();
|
|
_cValue = Path;
|
|
hasSetValue();
|
|
}
|
|
|
|
void PropertyPath::setValue(const char* Path)
|
|
{
|
|
aboutToSetValue();
|
|
#if (BOOST_FILESYSTEM_VERSION == 2)
|
|
_cValue = boost::filesystem::path(Path, boost::filesystem::no_check);
|
|
#else
|
|
_cValue = boost::filesystem::path(Path);
|
|
#endif
|
|
hasSetValue();
|
|
}
|
|
|
|
const boost::filesystem::path& PropertyPath::getValue() const
|
|
{
|
|
return _cValue;
|
|
}
|
|
|
|
PyObject* PropertyPath::getPyObject()
|
|
{
|
|
#if (BOOST_FILESYSTEM_VERSION == 2)
|
|
std::string str = _cValue.native_file_string();
|
|
#else
|
|
std::string str = _cValue.string();
|
|
#endif
|
|
|
|
// Returns a new reference, don't increment it!
|
|
PyObject* p = PyUnicode_DecodeUTF8(str.c_str(), str.size(), nullptr);
|
|
if (!p) {
|
|
throw Base::UnicodeError("UTF8 conversion failure at PropertyPath::getPyObject()");
|
|
}
|
|
return p;
|
|
}
|
|
|
|
void PropertyPath::setPyObject(PyObject* value)
|
|
{
|
|
std::string path;
|
|
if (PyUnicode_Check(value)) {
|
|
path = PyUnicode_AsUTF8(value);
|
|
}
|
|
else {
|
|
std::string error = std::string("type must be str or unicode, not ");
|
|
error += value->ob_type->tp_name;
|
|
throw Base::TypeError(error);
|
|
}
|
|
|
|
// assign the path
|
|
setValue(path.c_str());
|
|
}
|
|
|
|
|
|
void PropertyPath::Save(Base::Writer& writer) const
|
|
{
|
|
std::string val = encodeAttribute(_cValue.string());
|
|
writer.Stream() << writer.ind() << "<Path value=\"" << val << "\"/>" << std::endl;
|
|
}
|
|
|
|
void PropertyPath::Restore(Base::XMLReader& reader)
|
|
{
|
|
// read my Element
|
|
reader.readElement("Path");
|
|
// get the value of my Attribute
|
|
setValue(reader.getAttribute("value"));
|
|
}
|
|
|
|
Property* PropertyPath::Copy() const
|
|
{
|
|
PropertyPath* p = new PropertyPath();
|
|
p->_cValue = _cValue;
|
|
return p;
|
|
}
|
|
|
|
void PropertyPath::Paste(const Property& from)
|
|
{
|
|
aboutToSetValue();
|
|
_cValue = dynamic_cast<const PropertyPath&>(from)._cValue;
|
|
hasSetValue();
|
|
}
|
|
|
|
unsigned int PropertyPath::getMemSize() const
|
|
{
|
|
return static_cast<unsigned int>(_cValue.string().size());
|
|
}
|
|
|
|
//**************************************************************************
|
|
//**************************************************************************
|
|
// PropertyEnumeration
|
|
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
|
|
TYPESYSTEM_SOURCE(App::PropertyEnumeration, App::PropertyInteger)
|
|
|
|
//**************************************************************************
|
|
// Construction/Destruction
|
|
|
|
|
|
PropertyEnumeration::PropertyEnumeration()
|
|
{
|
|
_editorTypeName = "Gui::PropertyEditor::PropertyEnumItem";
|
|
}
|
|
|
|
PropertyEnumeration::PropertyEnumeration(const App::Enumeration& e)
|
|
{
|
|
_enum = e;
|
|
}
|
|
|
|
PropertyEnumeration::~PropertyEnumeration() = default;
|
|
|
|
void PropertyEnumeration::setEnums(const char** plEnums)
|
|
{
|
|
// For backward compatibility, if the property container is not attached to
|
|
// any document (i.e. its full name starts with '?'), do not notify, or
|
|
// else existing code may crash.
|
|
bool notify = !boost::starts_with(getFullName(), "?");
|
|
if (notify) {
|
|
aboutToSetValue();
|
|
}
|
|
_enum.setEnums(plEnums);
|
|
if (notify) {
|
|
hasSetValue();
|
|
}
|
|
}
|
|
|
|
void PropertyEnumeration::setEnums(const std::vector<std::string>& Enums)
|
|
{
|
|
setEnumVector(Enums);
|
|
}
|
|
|
|
void PropertyEnumeration::setValue(const char* value)
|
|
{
|
|
aboutToSetValue();
|
|
_enum.setValue(value);
|
|
hasSetValue();
|
|
}
|
|
|
|
void PropertyEnumeration::setValue(long value)
|
|
{
|
|
aboutToSetValue();
|
|
_enum.setValue(value);
|
|
hasSetValue();
|
|
}
|
|
|
|
void PropertyEnumeration::setValue(const Enumeration& source)
|
|
{
|
|
aboutToSetValue();
|
|
_enum = source;
|
|
hasSetValue();
|
|
}
|
|
|
|
long PropertyEnumeration::getValue() const
|
|
{
|
|
return _enum.getInt();
|
|
}
|
|
|
|
bool PropertyEnumeration::isValue(const char* value) const
|
|
{
|
|
return _enum.isValue(value);
|
|
}
|
|
|
|
bool PropertyEnumeration::isPartOf(const char* value) const
|
|
{
|
|
return _enum.contains(value);
|
|
}
|
|
|
|
const char* PropertyEnumeration::getValueAsString() const
|
|
{
|
|
if (!_enum.isValid()) {
|
|
throw Base::RuntimeError("Cannot get value from invalid enumeration");
|
|
}
|
|
return _enum.getCStr();
|
|
}
|
|
|
|
const Enumeration& PropertyEnumeration::getEnum() const
|
|
{
|
|
return _enum;
|
|
}
|
|
|
|
std::vector<std::string> PropertyEnumeration::getEnumVector() const
|
|
{
|
|
return _enum.getEnumVector();
|
|
}
|
|
|
|
void PropertyEnumeration::setEnumVector(const std::vector<std::string>& values)
|
|
{
|
|
// For backward compatibility, if the property container is not attached to
|
|
// any document (i.e. its full name starts with '?'), do not notify, or
|
|
// else existing code may crash.
|
|
bool notify = !boost::starts_with(getFullName(), "?");
|
|
if (notify) {
|
|
aboutToSetValue();
|
|
}
|
|
_enum.setEnums(values);
|
|
if (notify) {
|
|
hasSetValue();
|
|
}
|
|
}
|
|
|
|
bool PropertyEnumeration::hasEnums() const
|
|
{
|
|
return _enum.hasEnums();
|
|
}
|
|
|
|
bool PropertyEnumeration::isValid() const
|
|
{
|
|
return _enum.isValid();
|
|
}
|
|
|
|
void PropertyEnumeration::Save(Base::Writer& writer) const
|
|
{
|
|
writer.Stream() << writer.ind() << "<Integer value=\"" << _enum.getInt() << "\"";
|
|
if (_enum.isCustom()) {
|
|
writer.Stream() << " CustomEnum=\"true\"";
|
|
}
|
|
writer.Stream() << "/>" << std::endl;
|
|
if (_enum.isCustom()) {
|
|
std::vector<std::string> items = getEnumVector();
|
|
writer.Stream() << writer.ind() << "<CustomEnumList count=\"" << items.size() << "\">"
|
|
<< endl;
|
|
writer.incInd();
|
|
for (auto& item : items) {
|
|
std::string val = encodeAttribute(item);
|
|
writer.Stream() << writer.ind() << "<Enum value=\"" << val << "\"/>" << endl;
|
|
}
|
|
writer.decInd();
|
|
writer.Stream() << writer.ind() << "</CustomEnumList>" << endl;
|
|
}
|
|
}
|
|
|
|
void PropertyEnumeration::Restore(Base::XMLReader& reader)
|
|
{
|
|
// read my Element
|
|
reader.readElement("Integer");
|
|
// get the value of my Attribute
|
|
long val = reader.getAttributeAsInteger("value");
|
|
|
|
aboutToSetValue();
|
|
|
|
if (reader.hasAttribute("CustomEnum")) {
|
|
reader.readElement("CustomEnumList");
|
|
int count = reader.getAttributeAsInteger("count");
|
|
std::vector<std::string> values(count);
|
|
|
|
for (int i = 0; i < count; i++) {
|
|
reader.readElement("Enum");
|
|
values[i] = reader.getAttribute("value");
|
|
}
|
|
|
|
reader.readEndElement("CustomEnumList");
|
|
|
|
_enum.setEnums(values);
|
|
}
|
|
|
|
if (val < 0) {
|
|
// If the enum is empty at this stage do not print a warning
|
|
if (_enum.hasEnums()) {
|
|
Base::Console().DeveloperWarning(std::string("PropertyEnumeration"),
|
|
"Enumeration index %d is out of range, ignore it\n",
|
|
val);
|
|
}
|
|
val = getValue();
|
|
}
|
|
|
|
_enum.setValue(val);
|
|
hasSetValue();
|
|
}
|
|
|
|
PyObject* PropertyEnumeration::getPyObject()
|
|
{
|
|
if (!_enum.isValid()) {
|
|
Py_Return;
|
|
}
|
|
|
|
return Py_BuildValue("s", getValueAsString());
|
|
}
|
|
|
|
void PropertyEnumeration::setPyObject(PyObject* value)
|
|
{
|
|
if (PyLong_Check(value)) {
|
|
long val = PyLong_AsLong(value);
|
|
if (_enum.isValid()) {
|
|
aboutToSetValue();
|
|
_enum.setValue(val, true);
|
|
hasSetValue();
|
|
}
|
|
return;
|
|
}
|
|
else if (PyUnicode_Check(value)) {
|
|
std::string str = PyUnicode_AsUTF8(value);
|
|
if (_enum.contains(str.c_str())) {
|
|
aboutToSetValue();
|
|
_enum.setValue(str);
|
|
hasSetValue();
|
|
}
|
|
else {
|
|
FC_THROWM(Base::ValueError,
|
|
"'" << str << "' is not part of the enumeration in " << getFullName());
|
|
}
|
|
return;
|
|
}
|
|
else if (PySequence_Check(value)) {
|
|
|
|
try {
|
|
std::vector<std::string> values;
|
|
|
|
int idx = -1;
|
|
Py::Sequence seq(value);
|
|
|
|
if (seq.size() == 2) {
|
|
Py::Object v(seq[0].ptr());
|
|
if (!v.isString() && v.isSequence()) {
|
|
idx = Py::Int(seq[1].ptr());
|
|
seq = v;
|
|
}
|
|
}
|
|
|
|
values.resize(seq.size());
|
|
|
|
for (int i = 0; i < seq.size(); ++i) {
|
|
values[i] = Py::Object(seq[i].ptr()).as_string();
|
|
}
|
|
|
|
aboutToSetValue();
|
|
_enum.setEnums(values);
|
|
if (idx >= 0) {
|
|
_enum.setValue(idx, true);
|
|
}
|
|
hasSetValue();
|
|
return;
|
|
}
|
|
catch (Py::Exception&) {
|
|
Base::PyException e;
|
|
e.ReportException();
|
|
}
|
|
}
|
|
|
|
FC_THROWM(Base::TypeError,
|
|
"PropertyEnumeration "
|
|
<< getFullName()
|
|
<< " expects type to be int, string, or list(string), or list(list, int)");
|
|
}
|
|
|
|
Property* PropertyEnumeration::Copy() const
|
|
{
|
|
return new PropertyEnumeration(_enum);
|
|
}
|
|
|
|
void PropertyEnumeration::Paste(const Property& from)
|
|
{
|
|
const PropertyEnumeration& prop = dynamic_cast<const PropertyEnumeration&>(from);
|
|
setValue(prop._enum);
|
|
}
|
|
|
|
void PropertyEnumeration::setPathValue(const ObjectIdentifier&, const boost::any& value)
|
|
{
|
|
if (value.type() == typeid(int)) {
|
|
setValue(boost::any_cast<int>(value));
|
|
}
|
|
else if (value.type() == typeid(long)) {
|
|
setValue(boost::any_cast<long>(value));
|
|
}
|
|
else if (value.type() == typeid(double)) {
|
|
setValue(boost::any_cast<double>(value));
|
|
}
|
|
else if (value.type() == typeid(float)) {
|
|
setValue(boost::any_cast<float>(value));
|
|
}
|
|
else if (value.type() == typeid(short)) {
|
|
setValue(boost::any_cast<short>(value));
|
|
}
|
|
else if (value.type() == typeid(std::string)) {
|
|
setValue(boost::any_cast<std::string>(value).c_str());
|
|
}
|
|
else if (value.type() == typeid(char*)) {
|
|
setValue(boost::any_cast<char*>(value));
|
|
}
|
|
else if (value.type() == typeid(const char*)) {
|
|
setValue(boost::any_cast<const char*>(value));
|
|
}
|
|
else {
|
|
Base::PyGILStateLocker lock;
|
|
Py::Object pyValue = pyObjectFromAny(value);
|
|
setPyObject(pyValue.ptr());
|
|
}
|
|
}
|
|
|
|
bool PropertyEnumeration::setPyPathValue(const ObjectIdentifier&, const Py::Object& value)
|
|
{
|
|
setPyObject(value.ptr());
|
|
return true;
|
|
}
|
|
|
|
const boost::any PropertyEnumeration::getPathValue(const ObjectIdentifier& path) const
|
|
{
|
|
std::string p = path.getSubPathStr();
|
|
if (p == ".Enum" || p == ".All") {
|
|
Base::PyGILStateLocker lock;
|
|
Py::Object res;
|
|
getPyPathValue(path, res);
|
|
return pyObjectToAny(res, false);
|
|
}
|
|
else if (p == ".String") {
|
|
auto v = getValueAsString();
|
|
return std::string(v ? v : "");
|
|
}
|
|
else {
|
|
return getValue();
|
|
}
|
|
}
|
|
|
|
bool PropertyEnumeration::getPyPathValue(const ObjectIdentifier& path, Py::Object& r) const
|
|
{
|
|
std::string p = path.getSubPathStr();
|
|
if (p == ".Enum" || p == ".All") {
|
|
Base::PyGILStateLocker lock;
|
|
Py::Tuple res(_enum.maxValue() + 1);
|
|
std::vector<std::string> enums = _enum.getEnumVector();
|
|
PropertyString tmp;
|
|
for (int i = 0; i < int(enums.size()); ++i) {
|
|
tmp.setValue(enums[i]);
|
|
res.setItem(i, Py::asObject(tmp.getPyObject()));
|
|
}
|
|
if (p == ".Enum") {
|
|
r = res;
|
|
}
|
|
else {
|
|
Py::Tuple tuple(2);
|
|
tuple.setItem(0, res);
|
|
tuple.setItem(1, Py::Int(getValue()));
|
|
r = tuple;
|
|
}
|
|
}
|
|
else if (p == ".String") {
|
|
auto v = getValueAsString();
|
|
r = Py::String(v ? v : "");
|
|
}
|
|
else {
|
|
r = Py::Int(getValue());
|
|
}
|
|
return true;
|
|
}
|
|
|
|
//**************************************************************************
|
|
//**************************************************************************
|
|
// PropertyIntegerConstraint
|
|
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
|
|
TYPESYSTEM_SOURCE(App::PropertyIntegerConstraint, App::PropertyInteger)
|
|
|
|
//**************************************************************************
|
|
// Construction/Destruction
|
|
|
|
|
|
PropertyIntegerConstraint::PropertyIntegerConstraint() = default;
|
|
|
|
PropertyIntegerConstraint::~PropertyIntegerConstraint()
|
|
{
|
|
if (_ConstStruct && _ConstStruct->isDeletable()) {
|
|
delete _ConstStruct;
|
|
}
|
|
}
|
|
|
|
void PropertyIntegerConstraint::setConstraints(const Constraints* sConstrain)
|
|
{
|
|
if (_ConstStruct != sConstrain) {
|
|
if (_ConstStruct && _ConstStruct->isDeletable()) {
|
|
delete _ConstStruct;
|
|
}
|
|
}
|
|
|
|
_ConstStruct = sConstrain;
|
|
}
|
|
|
|
const PropertyIntegerConstraint::Constraints* PropertyIntegerConstraint::getConstraints() const
|
|
{
|
|
return _ConstStruct;
|
|
}
|
|
|
|
long PropertyIntegerConstraint::getMinimum() const
|
|
{
|
|
if (_ConstStruct) {
|
|
return _ConstStruct->LowerBound;
|
|
}
|
|
// return the min of int, not long
|
|
return std::numeric_limits<int>::min();
|
|
}
|
|
|
|
long PropertyIntegerConstraint::getMaximum() const
|
|
{
|
|
if (_ConstStruct) {
|
|
return _ConstStruct->UpperBound;
|
|
}
|
|
// return the max of int, not long
|
|
return std::numeric_limits<int>::max();
|
|
}
|
|
|
|
long PropertyIntegerConstraint::getStepSize() const
|
|
{
|
|
if (_ConstStruct) {
|
|
return _ConstStruct->StepSize;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
void PropertyIntegerConstraint::setPyObject(PyObject* value)
|
|
{
|
|
if (PyLong_Check(value)) {
|
|
long temp = PyLong_AsLong(value);
|
|
if (_ConstStruct) {
|
|
if (temp > _ConstStruct->UpperBound) {
|
|
temp = _ConstStruct->UpperBound;
|
|
}
|
|
else if (temp < _ConstStruct->LowerBound) {
|
|
temp = _ConstStruct->LowerBound;
|
|
}
|
|
}
|
|
|
|
aboutToSetValue();
|
|
_lValue = temp;
|
|
hasSetValue();
|
|
}
|
|
else if (PyTuple_Check(value) && PyTuple_Size(value) == 4) {
|
|
long values[4];
|
|
for (int i = 0; i < 4; i++) {
|
|
PyObject* item;
|
|
item = PyTuple_GetItem(value, i);
|
|
if (PyLong_Check(item)) {
|
|
values[i] = PyLong_AsLong(item);
|
|
}
|
|
else {
|
|
throw Base::TypeError("Type in tuple must be int");
|
|
}
|
|
}
|
|
|
|
Constraints* c = new Constraints();
|
|
c->setDeletable(true);
|
|
c->LowerBound = values[1];
|
|
c->UpperBound = values[2];
|
|
c->StepSize = std::max<long>(1, values[3]);
|
|
if (values[0] > c->UpperBound) {
|
|
values[0] = c->UpperBound;
|
|
}
|
|
else if (values[0] < c->LowerBound) {
|
|
values[0] = c->LowerBound;
|
|
}
|
|
setConstraints(c);
|
|
|
|
aboutToSetValue();
|
|
_lValue = values[0];
|
|
hasSetValue();
|
|
}
|
|
else {
|
|
std::string error = std::string("type must be int, not ");
|
|
error += value->ob_type->tp_name;
|
|
throw Base::TypeError(error);
|
|
}
|
|
}
|
|
|
|
//**************************************************************************
|
|
//**************************************************************************
|
|
// PropertyPercent
|
|
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
|
|
TYPESYSTEM_SOURCE(App::PropertyPercent, App::PropertyIntegerConstraint)
|
|
|
|
const PropertyIntegerConstraint::Constraints percent = {0, 100, 1};
|
|
|
|
//**************************************************************************
|
|
// Construction/Destruction
|
|
|
|
|
|
PropertyPercent::PropertyPercent()
|
|
{
|
|
_ConstStruct = &percent;
|
|
}
|
|
|
|
PropertyPercent::~PropertyPercent() = default;
|
|
|
|
//**************************************************************************
|
|
//**************************************************************************
|
|
// PropertyIntegerList
|
|
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
|
|
TYPESYSTEM_SOURCE(App::PropertyIntegerList, App::PropertyLists)
|
|
|
|
//**************************************************************************
|
|
// Construction/Destruction
|
|
|
|
|
|
PropertyIntegerList::PropertyIntegerList() = default;
|
|
|
|
PropertyIntegerList::~PropertyIntegerList() = default;
|
|
|
|
//**************************************************************************
|
|
// Base class implementer
|
|
|
|
PyObject* PropertyIntegerList::getPyObject()
|
|
{
|
|
PyObject* list = PyList_New(getSize());
|
|
for (int i = 0; i < getSize(); i++) {
|
|
PyList_SetItem(list, i, PyLong_FromLong(_lValueList[i]));
|
|
}
|
|
return list;
|
|
}
|
|
|
|
long PropertyIntegerList::getPyValue(PyObject* item) const
|
|
{
|
|
if (PyLong_Check(item)) {
|
|
return PyLong_AsLong(item);
|
|
}
|
|
std::string error = std::string("type in list must be int, not ");
|
|
error += item->ob_type->tp_name;
|
|
throw Base::TypeError(error);
|
|
}
|
|
|
|
void PropertyIntegerList::Save(Base::Writer& writer) const
|
|
{
|
|
writer.Stream() << writer.ind() << "<IntegerList count=\"" << getSize() << "\">" << endl;
|
|
writer.incInd();
|
|
for (int i = 0; i < getSize(); i++) {
|
|
writer.Stream() << writer.ind() << "<I v=\"" << _lValueList[i] << "\"/>" << endl;
|
|
};
|
|
writer.decInd();
|
|
writer.Stream() << writer.ind() << "</IntegerList>" << endl;
|
|
}
|
|
|
|
void PropertyIntegerList::Restore(Base::XMLReader& reader)
|
|
{
|
|
// read my Element
|
|
reader.readElement("IntegerList");
|
|
// get the value of my Attribute
|
|
int count = reader.getAttributeAsInteger("count");
|
|
|
|
std::vector<long> values(count);
|
|
for (int i = 0; i < count; i++) {
|
|
reader.readElement("I");
|
|
values[i] = reader.getAttributeAsInteger("v");
|
|
}
|
|
|
|
reader.readEndElement("IntegerList");
|
|
|
|
// assignment
|
|
setValues(values);
|
|
}
|
|
|
|
Property* PropertyIntegerList::Copy() const
|
|
{
|
|
PropertyIntegerList* p = new PropertyIntegerList();
|
|
p->_lValueList = _lValueList;
|
|
return p;
|
|
}
|
|
|
|
void PropertyIntegerList::Paste(const Property& from)
|
|
{
|
|
setValues(dynamic_cast<const PropertyIntegerList&>(from)._lValueList);
|
|
}
|
|
|
|
unsigned int PropertyIntegerList::getMemSize() const
|
|
{
|
|
return static_cast<unsigned int>(_lValueList.size() * sizeof(long));
|
|
}
|
|
|
|
|
|
//**************************************************************************
|
|
//**************************************************************************
|
|
// PropertyIntegerSet
|
|
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
|
|
TYPESYSTEM_SOURCE(App::PropertyIntegerSet, App::Property)
|
|
|
|
//**************************************************************************
|
|
// Construction/Destruction
|
|
|
|
|
|
PropertyIntegerSet::PropertyIntegerSet() = default;
|
|
|
|
PropertyIntegerSet::~PropertyIntegerSet() = default;
|
|
|
|
|
|
//**************************************************************************
|
|
// Base class implementer
|
|
|
|
void PropertyIntegerSet::setValue(long lValue)
|
|
{
|
|
aboutToSetValue();
|
|
_lValueSet.clear();
|
|
_lValueSet.insert(lValue);
|
|
hasSetValue();
|
|
}
|
|
|
|
void PropertyIntegerSet::setValues(const std::set<long>& values)
|
|
{
|
|
aboutToSetValue();
|
|
_lValueSet = values;
|
|
hasSetValue();
|
|
}
|
|
|
|
PyObject* PropertyIntegerSet::getPyObject()
|
|
{
|
|
PyObject* set = PySet_New(nullptr);
|
|
for (long it : _lValueSet) {
|
|
PySet_Add(set, PyLong_FromLong(it));
|
|
}
|
|
return set;
|
|
}
|
|
|
|
void PropertyIntegerSet::setPyObject(PyObject* value)
|
|
{
|
|
if (PySequence_Check(value)) {
|
|
|
|
Py::Sequence sequence(value);
|
|
Py_ssize_t nSize = sequence.size();
|
|
std::set<long> values;
|
|
|
|
for (Py_ssize_t i = 0; i < nSize; ++i) {
|
|
Py::Object item = sequence.getItem(i);
|
|
if (!PyLong_Check(item.ptr())) {
|
|
std::string error = std::string("type in list must be int, not ");
|
|
error += item.ptr()->ob_type->tp_name;
|
|
throw Base::TypeError(error);
|
|
}
|
|
values.insert(PyLong_AsLong(item.ptr()));
|
|
}
|
|
|
|
setValues(values);
|
|
}
|
|
else if (PyLong_Check(value)) {
|
|
setValue(PyLong_AsLong(value));
|
|
}
|
|
else {
|
|
std::string error = std::string("type must be int or list of int, not ");
|
|
error += value->ob_type->tp_name;
|
|
throw Base::TypeError(error);
|
|
}
|
|
}
|
|
|
|
void PropertyIntegerSet::Save(Base::Writer& writer) const
|
|
{
|
|
writer.Stream() << writer.ind() << "<IntegerSet count=\"" << _lValueSet.size() << "\">" << endl;
|
|
writer.incInd();
|
|
for (long it : _lValueSet) {
|
|
writer.Stream() << writer.ind() << "<I v=\"" << it << "\"/>" << endl;
|
|
};
|
|
writer.decInd();
|
|
writer.Stream() << writer.ind() << "</IntegerSet>" << endl;
|
|
}
|
|
|
|
void PropertyIntegerSet::Restore(Base::XMLReader& reader)
|
|
{
|
|
// read my Element
|
|
reader.readElement("IntegerSet");
|
|
// get the value of my Attribute
|
|
int count = reader.getAttributeAsInteger("count");
|
|
|
|
std::set<long> values;
|
|
for (int i = 0; i < count; i++) {
|
|
reader.readElement("I");
|
|
values.insert(reader.getAttributeAsInteger("v"));
|
|
}
|
|
|
|
reader.readEndElement("IntegerSet");
|
|
|
|
// assignment
|
|
setValues(values);
|
|
}
|
|
|
|
Property* PropertyIntegerSet::Copy() const
|
|
{
|
|
PropertyIntegerSet* p = new PropertyIntegerSet();
|
|
p->_lValueSet = _lValueSet;
|
|
return p;
|
|
}
|
|
|
|
void PropertyIntegerSet::Paste(const Property& from)
|
|
{
|
|
aboutToSetValue();
|
|
_lValueSet = dynamic_cast<const PropertyIntegerSet&>(from)._lValueSet;
|
|
hasSetValue();
|
|
}
|
|
|
|
unsigned int PropertyIntegerSet::getMemSize() const
|
|
{
|
|
return static_cast<unsigned int>(_lValueSet.size() * sizeof(long));
|
|
}
|
|
|
|
|
|
//**************************************************************************
|
|
//**************************************************************************
|
|
// PropertyFloat
|
|
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
|
|
TYPESYSTEM_SOURCE(App::PropertyFloat, App::Property)
|
|
|
|
//**************************************************************************
|
|
// Construction/Destruction
|
|
|
|
|
|
PropertyFloat::PropertyFloat()
|
|
{
|
|
_dValue = 0.0;
|
|
}
|
|
|
|
PropertyFloat::~PropertyFloat() = default;
|
|
|
|
//**************************************************************************
|
|
// Base class implementer
|
|
|
|
void PropertyFloat::setValue(double lValue)
|
|
{
|
|
aboutToSetValue();
|
|
_dValue = lValue;
|
|
hasSetValue();
|
|
}
|
|
|
|
double PropertyFloat::getValue() const
|
|
{
|
|
return _dValue;
|
|
}
|
|
|
|
PyObject* PropertyFloat::getPyObject()
|
|
{
|
|
return Py_BuildValue("d", _dValue);
|
|
}
|
|
|
|
void PropertyFloat::setPyObject(PyObject* value)
|
|
{
|
|
if (PyFloat_Check(value)) {
|
|
aboutToSetValue();
|
|
_dValue = PyFloat_AsDouble(value);
|
|
hasSetValue();
|
|
}
|
|
else if (PyLong_Check(value)) {
|
|
aboutToSetValue();
|
|
_dValue = PyLong_AsLong(value);
|
|
hasSetValue();
|
|
}
|
|
else {
|
|
std::string error = std::string("type must be float or int, not ");
|
|
error += value->ob_type->tp_name;
|
|
throw Base::TypeError(error);
|
|
}
|
|
}
|
|
|
|
void PropertyFloat::Save(Base::Writer& writer) const
|
|
{
|
|
writer.Stream() << writer.ind() << "<Float value=\"" << _dValue << "\"/>" << std::endl;
|
|
}
|
|
|
|
void PropertyFloat::Restore(Base::XMLReader& reader)
|
|
{
|
|
// read my Element
|
|
reader.readElement("Float");
|
|
// get the value of my Attribute
|
|
setValue(reader.getAttributeAsFloat("value"));
|
|
}
|
|
|
|
Property* PropertyFloat::Copy() const
|
|
{
|
|
PropertyFloat* p = new PropertyFloat();
|
|
p->_dValue = _dValue;
|
|
return p;
|
|
}
|
|
|
|
void PropertyFloat::Paste(const Property& from)
|
|
{
|
|
aboutToSetValue();
|
|
_dValue = dynamic_cast<const PropertyFloat&>(from)._dValue;
|
|
hasSetValue();
|
|
}
|
|
|
|
void PropertyFloat::setPathValue(const ObjectIdentifier& path, const boost::any& value)
|
|
{
|
|
verifyPath(path);
|
|
|
|
if (value.type() == typeid(long)) {
|
|
setValue(boost::any_cast<long>(value));
|
|
}
|
|
else if (value.type() == typeid(unsigned long)) {
|
|
setValue(boost::any_cast<unsigned long>(value));
|
|
}
|
|
else if (value.type() == typeid(int)) {
|
|
setValue(boost::any_cast<int>(value));
|
|
}
|
|
else if (value.type() == typeid(double)) {
|
|
setValue(boost::any_cast<double>(value));
|
|
}
|
|
else if (value.type() == typeid(float)) {
|
|
setValue(boost::any_cast<float>(value));
|
|
}
|
|
else if (value.type() == typeid(Quantity)) {
|
|
setValue((boost::any_cast<Quantity>(value)).getValue());
|
|
}
|
|
else {
|
|
throw bad_cast();
|
|
}
|
|
}
|
|
|
|
const boost::any PropertyFloat::getPathValue(const ObjectIdentifier& path) const
|
|
{
|
|
verifyPath(path);
|
|
return _dValue;
|
|
}
|
|
|
|
//**************************************************************************
|
|
//**************************************************************************
|
|
// PropertyFloatConstraint
|
|
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
|
|
TYPESYSTEM_SOURCE(App::PropertyFloatConstraint, App::PropertyFloat)
|
|
|
|
//**************************************************************************
|
|
// Construction/Destruction
|
|
|
|
|
|
PropertyFloatConstraint::PropertyFloatConstraint() = default;
|
|
|
|
PropertyFloatConstraint::~PropertyFloatConstraint()
|
|
{
|
|
if (_ConstStruct && _ConstStruct->isDeletable()) {
|
|
delete _ConstStruct;
|
|
}
|
|
}
|
|
|
|
void PropertyFloatConstraint::setConstraints(const Constraints* sConstrain)
|
|
{
|
|
if (_ConstStruct != sConstrain) {
|
|
if (_ConstStruct && _ConstStruct->isDeletable()) {
|
|
delete _ConstStruct;
|
|
}
|
|
}
|
|
_ConstStruct = sConstrain;
|
|
}
|
|
|
|
const PropertyFloatConstraint::Constraints* PropertyFloatConstraint::getConstraints() const
|
|
{
|
|
return _ConstStruct;
|
|
}
|
|
|
|
double PropertyFloatConstraint::getMinimum() const
|
|
{
|
|
if (_ConstStruct) {
|
|
return _ConstStruct->LowerBound;
|
|
}
|
|
return std::numeric_limits<double>::min();
|
|
}
|
|
|
|
double PropertyFloatConstraint::getMaximum() const
|
|
{
|
|
if (_ConstStruct) {
|
|
return _ConstStruct->UpperBound;
|
|
}
|
|
return std::numeric_limits<double>::max();
|
|
}
|
|
|
|
double PropertyFloatConstraint::getStepSize() const
|
|
{
|
|
if (_ConstStruct) {
|
|
return _ConstStruct->StepSize;
|
|
}
|
|
return 1.0;
|
|
}
|
|
|
|
void PropertyFloatConstraint::setPyObject(PyObject* value)
|
|
{
|
|
if (PyFloat_Check(value)) {
|
|
double temp = PyFloat_AsDouble(value);
|
|
if (_ConstStruct) {
|
|
if (temp > _ConstStruct->UpperBound) {
|
|
temp = _ConstStruct->UpperBound;
|
|
}
|
|
else if (temp < _ConstStruct->LowerBound) {
|
|
temp = _ConstStruct->LowerBound;
|
|
}
|
|
}
|
|
|
|
aboutToSetValue();
|
|
_dValue = temp;
|
|
hasSetValue();
|
|
}
|
|
else if (PyLong_Check(value)) {
|
|
double temp = (double)PyLong_AsLong(value);
|
|
if (_ConstStruct) {
|
|
if (temp > _ConstStruct->UpperBound) {
|
|
temp = _ConstStruct->UpperBound;
|
|
}
|
|
else if (temp < _ConstStruct->LowerBound) {
|
|
temp = _ConstStruct->LowerBound;
|
|
}
|
|
}
|
|
|
|
aboutToSetValue();
|
|
_dValue = temp;
|
|
hasSetValue();
|
|
}
|
|
else if (PyTuple_Check(value) && PyTuple_Size(value) == 4) {
|
|
double values[4];
|
|
for (int i = 0; i < 4; i++) {
|
|
PyObject* item;
|
|
item = PyTuple_GetItem(value, i);
|
|
if (PyFloat_Check(item)) {
|
|
values[i] = PyFloat_AsDouble(item);
|
|
}
|
|
else if (PyLong_Check(item)) {
|
|
values[i] = PyLong_AsLong(item);
|
|
}
|
|
else {
|
|
throw Base::TypeError("Type in tuple must be float or int");
|
|
}
|
|
}
|
|
|
|
double stepSize = values[3];
|
|
// need a value > 0
|
|
if (stepSize < DBL_EPSILON) {
|
|
throw Base::ValueError("Step size must be greater than zero");
|
|
}
|
|
|
|
Constraints* c = new Constraints();
|
|
c->setDeletable(true);
|
|
c->LowerBound = values[1];
|
|
c->UpperBound = values[2];
|
|
c->StepSize = stepSize;
|
|
if (values[0] > c->UpperBound) {
|
|
values[0] = c->UpperBound;
|
|
}
|
|
else if (values[0] < c->LowerBound) {
|
|
values[0] = c->LowerBound;
|
|
}
|
|
setConstraints(c);
|
|
|
|
aboutToSetValue();
|
|
_dValue = values[0];
|
|
hasSetValue();
|
|
}
|
|
else {
|
|
std::string error = std::string("type must be float, not ");
|
|
error += value->ob_type->tp_name;
|
|
throw Base::TypeError(error);
|
|
}
|
|
}
|
|
|
|
//**************************************************************************
|
|
// PropertyPrecision
|
|
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
|
|
TYPESYSTEM_SOURCE(App::PropertyPrecision, App::PropertyFloatConstraint)
|
|
|
|
//**************************************************************************
|
|
// Construction/Destruction
|
|
//
|
|
const PropertyFloatConstraint::Constraints PrecisionStandard = {0.0, DBL_MAX, 0.001};
|
|
|
|
PropertyPrecision::PropertyPrecision()
|
|
{
|
|
setConstraints(&PrecisionStandard);
|
|
}
|
|
|
|
PropertyPrecision::~PropertyPrecision() = default;
|
|
|
|
|
|
//**************************************************************************
|
|
// PropertyFloatList
|
|
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
|
|
TYPESYSTEM_SOURCE(App::PropertyFloatList, App::PropertyLists)
|
|
|
|
//**************************************************************************
|
|
// Construction/Destruction
|
|
|
|
|
|
PropertyFloatList::PropertyFloatList() = default;
|
|
|
|
PropertyFloatList::~PropertyFloatList() = default;
|
|
|
|
//**************************************************************************
|
|
// Base class implementer
|
|
|
|
PyObject* PropertyFloatList::getPyObject()
|
|
{
|
|
PyObject* list = PyList_New(getSize());
|
|
for (int i = 0; i < getSize(); i++) {
|
|
PyList_SetItem(list, i, PyFloat_FromDouble(_lValueList[i]));
|
|
}
|
|
return list;
|
|
}
|
|
|
|
double PropertyFloatList::getPyValue(PyObject* item) const
|
|
{
|
|
if (PyFloat_Check(item)) {
|
|
return PyFloat_AsDouble(item);
|
|
}
|
|
else if (PyLong_Check(item)) {
|
|
return static_cast<double>(PyLong_AsLong(item));
|
|
}
|
|
else {
|
|
std::string error = std::string("type in list must be float, not ");
|
|
error += item->ob_type->tp_name;
|
|
throw Base::TypeError(error);
|
|
}
|
|
}
|
|
|
|
void PropertyFloatList::Save(Base::Writer& writer) const
|
|
{
|
|
if (writer.isForceXML()) {
|
|
writer.Stream() << writer.ind() << "<FloatList count=\"" << getSize() << "\">" << endl;
|
|
writer.incInd();
|
|
for (int i = 0; i < getSize(); i++) {
|
|
writer.Stream() << writer.ind() << "<F v=\"" << _lValueList[i] << "\"/>" << endl;
|
|
};
|
|
writer.decInd();
|
|
writer.Stream() << writer.ind() << "</FloatList>" << endl;
|
|
}
|
|
else {
|
|
writer.Stream() << writer.ind() << "<FloatList file=\""
|
|
<< (getSize() ? writer.addFile(getName(), this) : "") << "\"/>"
|
|
<< std::endl;
|
|
}
|
|
}
|
|
|
|
void PropertyFloatList::Restore(Base::XMLReader& reader)
|
|
{
|
|
reader.readElement("FloatList");
|
|
string file(reader.getAttribute("file"));
|
|
|
|
if (!file.empty()) {
|
|
// initiate a file read
|
|
reader.addFile(file.c_str(), this);
|
|
}
|
|
}
|
|
|
|
void PropertyFloatList::SaveDocFile(Base::Writer& writer) const
|
|
{
|
|
Base::OutputStream str(writer.Stream());
|
|
uint32_t uCt = (uint32_t)getSize();
|
|
str << uCt;
|
|
if (!isSinglePrecision()) {
|
|
for (double it : _lValueList) {
|
|
str << it;
|
|
}
|
|
}
|
|
else {
|
|
for (double it : _lValueList) {
|
|
float v = static_cast<float>(it);
|
|
str << v;
|
|
}
|
|
}
|
|
}
|
|
|
|
void PropertyFloatList::RestoreDocFile(Base::Reader& reader)
|
|
{
|
|
Base::InputStream str(reader);
|
|
uint32_t uCt = 0;
|
|
str >> uCt;
|
|
std::vector<double> values(uCt);
|
|
if (!isSinglePrecision()) {
|
|
for (double& it : values) {
|
|
str >> it;
|
|
}
|
|
}
|
|
else {
|
|
for (double& it : values) {
|
|
float val;
|
|
str >> val;
|
|
it = val;
|
|
}
|
|
}
|
|
setValues(values);
|
|
}
|
|
|
|
Property* PropertyFloatList::Copy() const
|
|
{
|
|
PropertyFloatList* p = new PropertyFloatList();
|
|
p->_lValueList = _lValueList;
|
|
return p;
|
|
}
|
|
|
|
void PropertyFloatList::Paste(const Property& from)
|
|
{
|
|
setValues(dynamic_cast<const PropertyFloatList&>(from)._lValueList);
|
|
}
|
|
|
|
unsigned int PropertyFloatList::getMemSize() const
|
|
{
|
|
return static_cast<unsigned int>(_lValueList.size() * sizeof(double));
|
|
}
|
|
|
|
//**************************************************************************
|
|
//**************************************************************************
|
|
// PropertyString
|
|
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
|
|
TYPESYSTEM_SOURCE(App::PropertyString, App::Property)
|
|
|
|
PropertyString::PropertyString() = default;
|
|
|
|
PropertyString::~PropertyString() = default;
|
|
|
|
void PropertyString::setValue(const char* newLabel)
|
|
{
|
|
if (!newLabel) {
|
|
return;
|
|
}
|
|
|
|
if (_cValue == newLabel) {
|
|
return;
|
|
}
|
|
|
|
std::string _newLabel;
|
|
|
|
std::vector<std::pair<Property*, std::unique_ptr<Property>>> propChanges;
|
|
std::string label;
|
|
auto obj = dynamic_cast<DocumentObject*>(getContainer());
|
|
bool commit = false;
|
|
|
|
if (obj && obj->isAttachedToDocument() && this == &obj->Label
|
|
&& (!obj->getDocument()->testStatus(App::Document::Restoring)
|
|
|| obj->getDocument()->testStatus(App::Document::Importing))
|
|
&& !obj->getDocument()->isPerformingTransaction()) {
|
|
// allow object to control label change
|
|
|
|
static ParameterGrp::handle _hPGrp;
|
|
if (!_hPGrp) {
|
|
_hPGrp = GetApplication().GetUserParameter().GetGroup("BaseApp");
|
|
_hPGrp = _hPGrp->GetGroup("Preferences")->GetGroup("Document");
|
|
}
|
|
App::Document* doc = obj->getDocument();
|
|
if (doc && !_hPGrp->GetBool("DuplicateLabels") && !obj->allowDuplicateLabel()) {
|
|
std::vector<std::string> objectLabels;
|
|
std::vector<App::DocumentObject*>::const_iterator it;
|
|
std::vector<App::DocumentObject*> objs = doc->getObjects();
|
|
bool match = false;
|
|
for (it = objs.begin(); it != objs.end(); ++it) {
|
|
if (*it == obj) {
|
|
continue; // don't compare object with itself
|
|
}
|
|
std::string objLabel = (*it)->Label.getValue();
|
|
if (!match && objLabel == newLabel) {
|
|
match = true;
|
|
}
|
|
objectLabels.push_back(objLabel);
|
|
}
|
|
|
|
// make sure that there is a name conflict otherwise we don't have to do anything
|
|
if (match && *newLabel) {
|
|
label = newLabel;
|
|
// remove number from end to avoid lengthy names
|
|
size_t lastpos = label.length() - 1;
|
|
while (label[lastpos] >= 48 && label[lastpos] <= 57) {
|
|
// if 'lastpos' becomes 0 then all characters are digits. In this case we use
|
|
// the complete label again
|
|
if (lastpos == 0) {
|
|
lastpos = label.length() - 1;
|
|
break;
|
|
}
|
|
lastpos--;
|
|
}
|
|
|
|
bool changed = false;
|
|
label = label.substr(0, lastpos + 1);
|
|
if (label != obj->getNameInDocument()
|
|
&& boost::starts_with(obj->getNameInDocument(), label)) {
|
|
// In case the label has the same base name as object's
|
|
// internal name, use it as the label instead.
|
|
const char* objName = obj->getNameInDocument();
|
|
const char* c = &objName[lastpos + 1];
|
|
for (; *c; ++c) {
|
|
if (*c < 48 || *c > 57) {
|
|
break;
|
|
}
|
|
}
|
|
if (*c == 0
|
|
&& std::find(objectLabels.begin(),
|
|
objectLabels.end(),
|
|
obj->getNameInDocument())
|
|
== objectLabels.end()) {
|
|
label = obj->getNameInDocument();
|
|
changed = true;
|
|
}
|
|
}
|
|
if (!changed) {
|
|
label = Base::Tools::getUniqueName(label, objectLabels, 3);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (label.empty()) {
|
|
label = newLabel;
|
|
}
|
|
obj->onBeforeChangeLabel(label);
|
|
newLabel = label.c_str();
|
|
|
|
if (!obj->getDocument()->testStatus(App::Document::Restoring)) {
|
|
// Only update label reference if we are not restoring. When
|
|
// importing (which also counts as restoring), it is possible the
|
|
// new object changes its label. However, we cannot update label
|
|
// references here, because object restoring is not based on
|
|
// dependency order. It can only be done in afterRestore().
|
|
//
|
|
// See PropertyLinkBase::restoreLabelReference() for more details.
|
|
propChanges = PropertyLinkBase::updateLabelReferences(obj, newLabel);
|
|
}
|
|
|
|
if (!propChanges.empty() && !GetApplication().getActiveTransaction()) {
|
|
commit = true;
|
|
std::ostringstream str;
|
|
str << "Change " << obj->getNameInDocument() << ".Label";
|
|
GetApplication().setActiveTransaction(str.str().c_str());
|
|
}
|
|
}
|
|
|
|
aboutToSetValue();
|
|
_cValue = newLabel;
|
|
hasSetValue();
|
|
|
|
for (auto& change : propChanges) {
|
|
change.first->Paste(*change.second.get());
|
|
}
|
|
|
|
if (commit) {
|
|
GetApplication().closeActiveTransaction();
|
|
}
|
|
}
|
|
|
|
void PropertyString::setValue(const std::string& sString)
|
|
{
|
|
setValue(sString.c_str());
|
|
}
|
|
|
|
const char* PropertyString::getValue() const
|
|
{
|
|
return _cValue.c_str();
|
|
}
|
|
|
|
PyObject* PropertyString::getPyObject()
|
|
{
|
|
PyObject* p = PyUnicode_DecodeUTF8(_cValue.c_str(), _cValue.size(), nullptr);
|
|
if (!p) {
|
|
throw Base::UnicodeError("UTF8 conversion failure at PropertyString::getPyObject()");
|
|
}
|
|
return p;
|
|
}
|
|
|
|
void PropertyString::setPyObject(PyObject* value)
|
|
{
|
|
std::string string;
|
|
if (PyUnicode_Check(value)) {
|
|
string = PyUnicode_AsUTF8(value);
|
|
}
|
|
else {
|
|
std::string error = std::string("type must be str or unicode, not ");
|
|
error += value->ob_type->tp_name;
|
|
throw Base::TypeError(error);
|
|
}
|
|
|
|
// assign the string
|
|
setValue(string);
|
|
}
|
|
|
|
void PropertyString::Save(Base::Writer& writer) const
|
|
{
|
|
std::string val;
|
|
auto obj = dynamic_cast<DocumentObject*>(getContainer());
|
|
writer.Stream() << writer.ind() << "<String ";
|
|
bool exported = false;
|
|
if (obj && obj->isAttachedToDocument() && obj->isExporting() && &obj->Label == this) {
|
|
if (obj->allowDuplicateLabel()) {
|
|
writer.Stream() << "restore=\"1\" ";
|
|
}
|
|
else if (_cValue == obj->getNameInDocument()) {
|
|
writer.Stream() << "restore=\"0\" ";
|
|
val = encodeAttribute(obj->getExportName());
|
|
exported = true;
|
|
}
|
|
}
|
|
if (!exported) {
|
|
val = encodeAttribute(_cValue);
|
|
}
|
|
writer.Stream() << "value=\"" << val << "\"/>" << std::endl;
|
|
}
|
|
|
|
void PropertyString::Restore(Base::XMLReader& reader)
|
|
{
|
|
// read my Element
|
|
reader.readElement("String");
|
|
// get the value of my Attribute
|
|
auto obj = dynamic_cast<DocumentObject*>(getContainer());
|
|
if (obj && &obj->Label == this) {
|
|
if (reader.hasAttribute("restore")) {
|
|
int restore = reader.getAttributeAsInteger("restore");
|
|
if (restore == 1) {
|
|
aboutToSetValue();
|
|
_cValue = reader.getAttribute("value");
|
|
hasSetValue();
|
|
}
|
|
else {
|
|
setValue(reader.getName(reader.getAttribute("value")));
|
|
}
|
|
}
|
|
else {
|
|
setValue(reader.getAttribute("value"));
|
|
}
|
|
}
|
|
else {
|
|
setValue(reader.getAttribute("value"));
|
|
}
|
|
}
|
|
|
|
Property* PropertyString::Copy() const
|
|
{
|
|
PropertyString* p = new PropertyString();
|
|
p->_cValue = _cValue;
|
|
return p;
|
|
}
|
|
|
|
void PropertyString::Paste(const Property& from)
|
|
{
|
|
setValue(dynamic_cast<const PropertyString&>(from)._cValue);
|
|
}
|
|
|
|
unsigned int PropertyString::getMemSize() const
|
|
{
|
|
return static_cast<unsigned int>(_cValue.size());
|
|
}
|
|
|
|
void PropertyString::setPathValue(const ObjectIdentifier& path, const boost::any& value)
|
|
{
|
|
verifyPath(path);
|
|
if (value.type() == typeid(bool)) {
|
|
setValue(boost::any_cast<bool>(value) ? "True" : "False");
|
|
}
|
|
else if (value.type() == typeid(int)) {
|
|
setValue(std::to_string(boost::any_cast<int>(value)));
|
|
}
|
|
else if (value.type() == typeid(long)) {
|
|
setValue(std::to_string(boost::any_cast<long>(value)));
|
|
}
|
|
else if (value.type() == typeid(double)) {
|
|
setValue(std::to_string(App::any_cast<double>(value)));
|
|
}
|
|
else if (value.type() == typeid(float)) {
|
|
setValue(std::to_string(App::any_cast<float>(value)));
|
|
}
|
|
else if (value.type() == typeid(Quantity)) {
|
|
setValue(boost::any_cast<Quantity>(value).getUserString().toUtf8().constData());
|
|
}
|
|
else if (value.type() == typeid(std::string)) {
|
|
setValue(boost::any_cast<const std::string&>(value));
|
|
}
|
|
else {
|
|
Base::PyGILStateLocker lock;
|
|
setValue(pyObjectFromAny(value).as_string());
|
|
}
|
|
}
|
|
|
|
const boost::any PropertyString::getPathValue(const ObjectIdentifier& path) const
|
|
{
|
|
verifyPath(path);
|
|
return _cValue;
|
|
}
|
|
|
|
//**************************************************************************
|
|
//**************************************************************************
|
|
// PropertyUUID
|
|
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
|
|
TYPESYSTEM_SOURCE(App::PropertyUUID, App::Property)
|
|
|
|
PropertyUUID::PropertyUUID() = default;
|
|
|
|
PropertyUUID::~PropertyUUID() = default;
|
|
|
|
void PropertyUUID::setValue(const Base::Uuid& id)
|
|
{
|
|
aboutToSetValue();
|
|
_uuid = id;
|
|
hasSetValue();
|
|
}
|
|
|
|
void PropertyUUID::setValue(const char* sString)
|
|
{
|
|
if (sString) {
|
|
aboutToSetValue();
|
|
_uuid.setValue(sString);
|
|
hasSetValue();
|
|
}
|
|
}
|
|
|
|
void PropertyUUID::setValue(const std::string& sString)
|
|
{
|
|
aboutToSetValue();
|
|
_uuid.setValue(sString);
|
|
hasSetValue();
|
|
}
|
|
|
|
const std::string& PropertyUUID::getValueStr() const
|
|
{
|
|
return _uuid.getValue();
|
|
}
|
|
|
|
const Base::Uuid& PropertyUUID::getValue() const
|
|
{
|
|
return _uuid;
|
|
}
|
|
|
|
PyObject* PropertyUUID::getPyObject()
|
|
{
|
|
PyObject* p = PyUnicode_FromString(_uuid.getValue().c_str());
|
|
return p;
|
|
}
|
|
|
|
void PropertyUUID::setPyObject(PyObject* value)
|
|
{
|
|
std::string string;
|
|
if (PyUnicode_Check(value)) {
|
|
string = PyUnicode_AsUTF8(value);
|
|
}
|
|
else {
|
|
std::string error = std::string("type must be unicode or str, not ");
|
|
error += value->ob_type->tp_name;
|
|
throw Base::TypeError(error);
|
|
}
|
|
|
|
try {
|
|
// assign the string
|
|
Base::Uuid uid;
|
|
uid.setValue(string);
|
|
setValue(uid);
|
|
}
|
|
catch (const std::exception& e) {
|
|
throw Base::RuntimeError(e.what());
|
|
}
|
|
}
|
|
|
|
void PropertyUUID::Save(Base::Writer& writer) const
|
|
{
|
|
writer.Stream() << writer.ind() << "<Uuid value=\"" << _uuid.getValue() << "\"/>" << std::endl;
|
|
}
|
|
|
|
void PropertyUUID::Restore(Base::XMLReader& reader)
|
|
{
|
|
// read my Element
|
|
reader.readElement("Uuid");
|
|
// get the value of my Attribute
|
|
setValue(reader.getAttribute("value"));
|
|
}
|
|
|
|
Property* PropertyUUID::Copy() const
|
|
{
|
|
PropertyUUID* p = new PropertyUUID();
|
|
p->_uuid = _uuid;
|
|
return p;
|
|
}
|
|
|
|
void PropertyUUID::Paste(const Property& from)
|
|
{
|
|
aboutToSetValue();
|
|
_uuid = dynamic_cast<const PropertyUUID&>(from)._uuid;
|
|
hasSetValue();
|
|
}
|
|
|
|
unsigned int PropertyUUID::getMemSize() const
|
|
{
|
|
return static_cast<unsigned int>(sizeof(_uuid));
|
|
}
|
|
|
|
//**************************************************************************
|
|
// PropertyFont
|
|
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
|
|
TYPESYSTEM_SOURCE(App::PropertyFont, App::PropertyString)
|
|
|
|
PropertyFont::PropertyFont() = default;
|
|
|
|
PropertyFont::~PropertyFont() = default;
|
|
|
|
//**************************************************************************
|
|
// PropertyStringList
|
|
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
|
|
TYPESYSTEM_SOURCE(App::PropertyStringList, App::PropertyLists)
|
|
|
|
PropertyStringList::PropertyStringList() = default;
|
|
|
|
PropertyStringList::~PropertyStringList() = default;
|
|
|
|
//**************************************************************************
|
|
// Base class implementer
|
|
|
|
void PropertyStringList::setValues(const std::list<std::string>& lValue)
|
|
{
|
|
std::vector<std::string> vals;
|
|
vals.reserve(lValue.size());
|
|
for (const auto& v : lValue) {
|
|
vals.push_back(v);
|
|
}
|
|
setValues(vals);
|
|
}
|
|
|
|
PyObject* PropertyStringList::getPyObject()
|
|
{
|
|
PyObject* list = PyList_New(getSize());
|
|
|
|
for (int i = 0; i < getSize(); i++) {
|
|
PyObject* item =
|
|
PyUnicode_DecodeUTF8(_lValueList[i].c_str(), _lValueList[i].size(), nullptr);
|
|
if (!item) {
|
|
Py_DECREF(list);
|
|
throw Base::UnicodeError(
|
|
"UTF8 conversion failure at PropertyStringList::getPyObject()");
|
|
}
|
|
PyList_SetItem(list, i, item);
|
|
}
|
|
|
|
return list;
|
|
}
|
|
|
|
std::string PropertyStringList::getPyValue(PyObject* item) const
|
|
{
|
|
std::string ret;
|
|
if (PyUnicode_Check(item)) {
|
|
ret = PyUnicode_AsUTF8(item);
|
|
}
|
|
else if (PyBytes_Check(item)) {
|
|
ret = PyBytes_AsString(item);
|
|
}
|
|
else {
|
|
std::string error = std::string("type in list must be str or unicode, not ");
|
|
error += item->ob_type->tp_name;
|
|
throw Base::TypeError(error);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
unsigned int PropertyStringList::getMemSize() const
|
|
{
|
|
size_t size = 0;
|
|
for (int i = 0; i < getSize(); i++) {
|
|
size += _lValueList[i].size();
|
|
}
|
|
return static_cast<unsigned int>(size);
|
|
}
|
|
|
|
void PropertyStringList::Save(Base::Writer& writer) const
|
|
{
|
|
writer.Stream() << writer.ind() << "<StringList count=\"" << getSize() << "\">" << endl;
|
|
writer.incInd();
|
|
for (int i = 0; i < getSize(); i++) {
|
|
std::string val = encodeAttribute(_lValueList[i]);
|
|
writer.Stream() << writer.ind() << "<String value=\"" << val << "\"/>" << endl;
|
|
}
|
|
writer.decInd();
|
|
writer.Stream() << writer.ind() << "</StringList>" << endl;
|
|
}
|
|
|
|
void PropertyStringList::Restore(Base::XMLReader& reader)
|
|
{
|
|
// read my Element
|
|
reader.readElement("StringList");
|
|
// get the value of my Attribute
|
|
int count = reader.getAttributeAsInteger("count");
|
|
|
|
std::vector<std::string> values(count);
|
|
for (int i = 0; i < count; i++) {
|
|
reader.readElement("String");
|
|
values[i] = reader.getAttribute("value");
|
|
}
|
|
|
|
reader.readEndElement("StringList");
|
|
|
|
// assignment
|
|
setValues(values);
|
|
}
|
|
|
|
Property* PropertyStringList::Copy() const
|
|
{
|
|
PropertyStringList* p = new PropertyStringList();
|
|
p->_lValueList = _lValueList;
|
|
return p;
|
|
}
|
|
|
|
void PropertyStringList::Paste(const Property& from)
|
|
{
|
|
setValues(dynamic_cast<const PropertyStringList&>(from)._lValueList);
|
|
}
|
|
|
|
|
|
//**************************************************************************
|
|
// PropertyMap
|
|
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
|
|
TYPESYSTEM_SOURCE(App::PropertyMap, App::Property)
|
|
|
|
PropertyMap::PropertyMap() = default;
|
|
|
|
PropertyMap::~PropertyMap() = default;
|
|
|
|
//**************************************************************************
|
|
// Base class implementer
|
|
|
|
|
|
int PropertyMap::getSize() const
|
|
{
|
|
return static_cast<int>(_lValueList.size());
|
|
}
|
|
|
|
void PropertyMap::setValue(const std::string& key, const std::string& value)
|
|
{
|
|
aboutToSetValue();
|
|
_lValueList[key] = value;
|
|
hasSetValue();
|
|
}
|
|
|
|
void PropertyMap::setValues(const std::map<std::string, std::string>& map)
|
|
{
|
|
aboutToSetValue();
|
|
_lValueList = map;
|
|
hasSetValue();
|
|
}
|
|
|
|
const std::string& PropertyMap::operator[](const std::string& key) const
|
|
{
|
|
static std::string empty;
|
|
auto it = _lValueList.find(key);
|
|
if (it != _lValueList.end()) {
|
|
return it->second;
|
|
}
|
|
return empty;
|
|
}
|
|
|
|
PyObject* PropertyMap::getPyObject()
|
|
{
|
|
PyObject* dict = PyDict_New();
|
|
|
|
for (auto it = _lValueList.begin(); it != _lValueList.end(); ++it) {
|
|
PyObject* item = PyUnicode_DecodeUTF8(it->second.c_str(), it->second.size(), nullptr);
|
|
if (!item) {
|
|
Py_DECREF(dict);
|
|
throw Base::UnicodeError("UTF8 conversion failure at PropertyMap::getPyObject()");
|
|
}
|
|
PyDict_SetItemString(dict, it->first.c_str(), item);
|
|
Py_DECREF(item);
|
|
}
|
|
|
|
return dict;
|
|
}
|
|
|
|
void PropertyMap::setPyObject(PyObject* value)
|
|
{
|
|
if (PyDict_Check(value)) {
|
|
|
|
std::map<std::string, std::string> values;
|
|
// get key and item list
|
|
PyObject* keyList = PyDict_Keys(value);
|
|
|
|
PyObject* itemList = PyDict_Values(value);
|
|
Py_ssize_t nSize = PyList_Size(keyList);
|
|
|
|
for (Py_ssize_t i = 0; i < nSize; ++i) {
|
|
|
|
// check on the key:
|
|
std::string keyStr;
|
|
PyObject* key = PyList_GetItem(keyList, i);
|
|
if (PyUnicode_Check(key)) {
|
|
keyStr = PyUnicode_AsUTF8(key);
|
|
}
|
|
else {
|
|
std::string error("type of the key need to be unicode or string, not");
|
|
error += key->ob_type->tp_name;
|
|
throw Base::TypeError(error);
|
|
}
|
|
|
|
// check on the item:
|
|
PyObject* item = PyList_GetItem(itemList, i);
|
|
if (PyUnicode_Check(item)) {
|
|
values[keyStr] = PyUnicode_AsUTF8(item);
|
|
}
|
|
else {
|
|
std::string error("type in list must be string or unicode, not ");
|
|
error += item->ob_type->tp_name;
|
|
throw Base::TypeError(error);
|
|
}
|
|
}
|
|
|
|
setValues(values);
|
|
}
|
|
else {
|
|
std::string error("type must be a dict object");
|
|
error += value->ob_type->tp_name;
|
|
throw Base::TypeError(error);
|
|
}
|
|
}
|
|
|
|
unsigned int PropertyMap::getMemSize() const
|
|
{
|
|
size_t size = 0;
|
|
for (const auto& it : _lValueList) {
|
|
size += it.second.size();
|
|
size += it.first.size();
|
|
}
|
|
return size;
|
|
}
|
|
|
|
void PropertyMap::Save(Base::Writer& writer) const
|
|
{
|
|
writer.Stream() << writer.ind() << "<Map count=\"" << getSize() << "\">" << endl;
|
|
writer.incInd();
|
|
for (const auto& it : _lValueList) {
|
|
writer.Stream() << writer.ind() << "<Item key=\"" << encodeAttribute(it.first)
|
|
<< "\" value=\"" << encodeAttribute(it.second) << "\"/>" << endl;
|
|
}
|
|
|
|
writer.decInd();
|
|
writer.Stream() << writer.ind() << "</Map>" << endl;
|
|
}
|
|
|
|
void PropertyMap::Restore(Base::XMLReader& reader)
|
|
{
|
|
// read my Element
|
|
reader.readElement("Map");
|
|
// get the value of my Attribute
|
|
int count = reader.getAttributeAsInteger("count");
|
|
|
|
std::map<std::string, std::string> values;
|
|
for (int i = 0; i < count; i++) {
|
|
reader.readElement("Item");
|
|
values[reader.getAttribute("key")] = reader.getAttribute("value");
|
|
}
|
|
|
|
reader.readEndElement("Map");
|
|
|
|
// assignment
|
|
setValues(values);
|
|
}
|
|
|
|
Property* PropertyMap::Copy() const
|
|
{
|
|
PropertyMap* p = new PropertyMap();
|
|
p->_lValueList = _lValueList;
|
|
return p;
|
|
}
|
|
|
|
void PropertyMap::Paste(const Property& from)
|
|
{
|
|
aboutToSetValue();
|
|
_lValueList = dynamic_cast<const PropertyMap&>(from)._lValueList;
|
|
hasSetValue();
|
|
}
|
|
|
|
|
|
//**************************************************************************
|
|
//**************************************************************************
|
|
// PropertyBool
|
|
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
|
|
TYPESYSTEM_SOURCE(App::PropertyBool, App::Property)
|
|
|
|
//**************************************************************************
|
|
// Construction/Destruction
|
|
|
|
PropertyBool::PropertyBool()
|
|
{
|
|
_lValue = false;
|
|
}
|
|
|
|
PropertyBool::~PropertyBool() = default;
|
|
|
|
//**************************************************************************
|
|
// Setter/getter for the property
|
|
|
|
void PropertyBool::setValue(bool lValue)
|
|
{
|
|
aboutToSetValue();
|
|
_lValue = lValue;
|
|
hasSetValue();
|
|
}
|
|
|
|
bool PropertyBool::getValue() const
|
|
{
|
|
return _lValue;
|
|
}
|
|
|
|
PyObject* PropertyBool::getPyObject()
|
|
{
|
|
return PyBool_FromLong(_lValue ? 1 : 0);
|
|
}
|
|
|
|
void PropertyBool::setPyObject(PyObject* value)
|
|
{
|
|
if (PyBool_Check(value) || PyLong_Check(value)) {
|
|
setValue(Base::asBoolean(value));
|
|
}
|
|
else {
|
|
std::string error = std::string("type must be bool, not ");
|
|
error += value->ob_type->tp_name;
|
|
throw Base::TypeError(error);
|
|
}
|
|
}
|
|
|
|
void PropertyBool::Save(Base::Writer& writer) const
|
|
{
|
|
writer.Stream() << writer.ind() << "<Bool value=\"";
|
|
if (_lValue) {
|
|
writer.Stream() << "true" << "\"/>";
|
|
}
|
|
else {
|
|
writer.Stream() << "false" << "\"/>";
|
|
}
|
|
writer.Stream() << std::endl;
|
|
}
|
|
|
|
void PropertyBool::Restore(Base::XMLReader& reader)
|
|
{
|
|
// read my Element
|
|
reader.readElement("Bool");
|
|
// get the value of my Attribute
|
|
string b = reader.getAttribute("value");
|
|
(b == "true") ? setValue(true) : setValue(false);
|
|
}
|
|
|
|
|
|
Property* PropertyBool::Copy() const
|
|
{
|
|
PropertyBool* p = new PropertyBool();
|
|
p->_lValue = _lValue;
|
|
return p;
|
|
}
|
|
|
|
void PropertyBool::Paste(const Property& from)
|
|
{
|
|
aboutToSetValue();
|
|
_lValue = dynamic_cast<const PropertyBool&>(from)._lValue;
|
|
hasSetValue();
|
|
}
|
|
|
|
void PropertyBool::setPathValue(const ObjectIdentifier& path, const boost::any& value)
|
|
{
|
|
verifyPath(path);
|
|
|
|
if (value.type() == typeid(bool)) {
|
|
setValue(boost::any_cast<bool>(value));
|
|
}
|
|
else if (value.type() == typeid(int)) {
|
|
setValue(boost::any_cast<int>(value) != 0);
|
|
}
|
|
else if (value.type() == typeid(long)) {
|
|
setValue(boost::any_cast<long>(value) != 0);
|
|
}
|
|
else if (value.type() == typeid(double)) {
|
|
setValue(boost::math::round(boost::any_cast<double>(value)));
|
|
}
|
|
else if (value.type() == typeid(float)) {
|
|
setValue(boost::math::round(boost::any_cast<float>(value)));
|
|
}
|
|
else if (value.type() == typeid(Quantity)) {
|
|
setValue(boost::any_cast<Quantity>(value).getValue() != 0);
|
|
}
|
|
else {
|
|
throw bad_cast();
|
|
}
|
|
}
|
|
|
|
const boost::any PropertyBool::getPathValue(const ObjectIdentifier& path) const
|
|
{
|
|
verifyPath(path);
|
|
|
|
return _lValue;
|
|
}
|
|
|
|
//**************************************************************************
|
|
//**************************************************************************
|
|
// PropertyBoolList
|
|
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
|
|
TYPESYSTEM_SOURCE(App::PropertyBoolList, App::PropertyLists)
|
|
|
|
//**************************************************************************
|
|
// Construction/Destruction
|
|
|
|
|
|
PropertyBoolList::PropertyBoolList() = default;
|
|
|
|
PropertyBoolList::~PropertyBoolList() = default;
|
|
|
|
//**************************************************************************
|
|
// Base class implementer
|
|
|
|
PyObject* PropertyBoolList::getPyObject()
|
|
{
|
|
PyObject* tuple = PyTuple_New(getSize());
|
|
for (int i = 0; i < getSize(); i++) {
|
|
bool v = _lValueList[i];
|
|
if (v) {
|
|
PyTuple_SetItem(tuple, i, PyBool_FromLong(1));
|
|
}
|
|
else {
|
|
PyTuple_SetItem(tuple, i, PyBool_FromLong(0));
|
|
}
|
|
}
|
|
return tuple;
|
|
}
|
|
|
|
void PropertyBoolList::setPyObject(PyObject* value)
|
|
{
|
|
// string is also a sequence and must be treated differently
|
|
std::string str;
|
|
if (PyUnicode_Check(value)) {
|
|
str = PyUnicode_AsUTF8(value);
|
|
boost::dynamic_bitset<> values(str);
|
|
setValues(values);
|
|
}
|
|
else {
|
|
inherited::setPyObject(value);
|
|
}
|
|
}
|
|
|
|
bool PropertyBoolList::getPyValue(PyObject* item) const
|
|
{
|
|
if (PyBool_Check(item)) {
|
|
return Base::asBoolean(item);
|
|
}
|
|
else if (PyLong_Check(item)) {
|
|
return (PyLong_AsLong(item) ? true : false);
|
|
}
|
|
else {
|
|
std::string error = std::string("type in list must be bool or int, not ");
|
|
error += item->ob_type->tp_name;
|
|
throw Base::TypeError(error);
|
|
}
|
|
}
|
|
|
|
void PropertyBoolList::Save(Base::Writer& writer) const
|
|
{
|
|
writer.Stream() << writer.ind() << "<BoolList value=\"";
|
|
std::string bitset;
|
|
boost::to_string(_lValueList, bitset);
|
|
writer.Stream() << bitset << "\"/>";
|
|
writer.Stream() << std::endl;
|
|
}
|
|
|
|
void PropertyBoolList::Restore(Base::XMLReader& reader)
|
|
{
|
|
// read my Element
|
|
reader.readElement("BoolList");
|
|
// get the value of my Attribute
|
|
string str = reader.getAttribute("value");
|
|
boost::dynamic_bitset<> bitset(str);
|
|
setValues(bitset);
|
|
}
|
|
|
|
Property* PropertyBoolList::Copy() const
|
|
{
|
|
PropertyBoolList* p = new PropertyBoolList();
|
|
p->_lValueList = _lValueList;
|
|
return p;
|
|
}
|
|
|
|
void PropertyBoolList::Paste(const Property& from)
|
|
{
|
|
setValues(dynamic_cast<const PropertyBoolList&>(from)._lValueList);
|
|
}
|
|
|
|
unsigned int PropertyBoolList::getMemSize() const
|
|
{
|
|
return static_cast<unsigned int>(_lValueList.size());
|
|
}
|
|
|
|
//**************************************************************************
|
|
//**************************************************************************
|
|
// PropertyColor
|
|
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
|
|
TYPESYSTEM_SOURCE(App::PropertyColor, App::Property)
|
|
|
|
//**************************************************************************
|
|
// Construction/Destruction
|
|
|
|
PropertyColor::PropertyColor() = default;
|
|
|
|
PropertyColor::~PropertyColor() = default;
|
|
|
|
//**************************************************************************
|
|
// Base class implementer
|
|
|
|
void PropertyColor::setValue(const Color& col)
|
|
{
|
|
aboutToSetValue();
|
|
_cCol = col;
|
|
hasSetValue();
|
|
}
|
|
|
|
void PropertyColor::setValue(uint32_t rgba)
|
|
{
|
|
aboutToSetValue();
|
|
_cCol.setPackedValue(rgba);
|
|
hasSetValue();
|
|
}
|
|
|
|
void PropertyColor::setValue(float r, float g, float b, float a)
|
|
{
|
|
aboutToSetValue();
|
|
_cCol.set(r, g, b, a);
|
|
hasSetValue();
|
|
}
|
|
|
|
const Color& PropertyColor::getValue() const
|
|
{
|
|
return _cCol;
|
|
}
|
|
|
|
PyObject* PropertyColor::getPyObject()
|
|
{
|
|
PyObject* rgba = PyTuple_New(4);
|
|
PyObject* r = PyFloat_FromDouble(_cCol.r);
|
|
PyObject* g = PyFloat_FromDouble(_cCol.g);
|
|
PyObject* b = PyFloat_FromDouble(_cCol.b);
|
|
PyObject* a = PyFloat_FromDouble(_cCol.a);
|
|
|
|
PyTuple_SetItem(rgba, 0, r);
|
|
PyTuple_SetItem(rgba, 1, g);
|
|
PyTuple_SetItem(rgba, 2, b);
|
|
PyTuple_SetItem(rgba, 3, a);
|
|
|
|
return rgba;
|
|
}
|
|
|
|
void PropertyColor::setPyObject(PyObject* value)
|
|
{
|
|
App::Color cCol;
|
|
if (PyTuple_Check(value) && (PyTuple_Size(value) == 3 || PyTuple_Size(value) == 4)) {
|
|
PyObject* item;
|
|
item = PyTuple_GetItem(value, 0);
|
|
if (PyFloat_Check(item)) {
|
|
cCol.r = (float)PyFloat_AsDouble(item);
|
|
item = PyTuple_GetItem(value, 1);
|
|
if (PyFloat_Check(item)) {
|
|
cCol.g = (float)PyFloat_AsDouble(item);
|
|
}
|
|
else {
|
|
throw Base::TypeError("Type in tuple must be consistent (float)");
|
|
}
|
|
item = PyTuple_GetItem(value, 2);
|
|
if (PyFloat_Check(item)) {
|
|
cCol.b = (float)PyFloat_AsDouble(item);
|
|
}
|
|
else {
|
|
throw Base::TypeError("Type in tuple must be consistent (float)");
|
|
}
|
|
if (PyTuple_Size(value) == 4) {
|
|
item = PyTuple_GetItem(value, 3);
|
|
if (PyFloat_Check(item)) {
|
|
cCol.a = (float)PyFloat_AsDouble(item);
|
|
}
|
|
else {
|
|
throw Base::TypeError("Type in tuple must be consistent (float)");
|
|
}
|
|
}
|
|
}
|
|
else if (PyLong_Check(item)) {
|
|
cCol.r = PyLong_AsLong(item) / 255.0;
|
|
item = PyTuple_GetItem(value, 1);
|
|
if (PyLong_Check(item)) {
|
|
cCol.g = PyLong_AsLong(item) / 255.0;
|
|
}
|
|
else {
|
|
throw Base::TypeError("Type in tuple must be consistent (integer)");
|
|
}
|
|
item = PyTuple_GetItem(value, 2);
|
|
if (PyLong_Check(item)) {
|
|
cCol.b = PyLong_AsLong(item) / 255.0;
|
|
}
|
|
else {
|
|
throw Base::TypeError("Type in tuple must be consistent (integer)");
|
|
}
|
|
if (PyTuple_Size(value) == 4) {
|
|
item = PyTuple_GetItem(value, 3);
|
|
if (PyLong_Check(item)) {
|
|
cCol.a = PyLong_AsLong(item) / 255.0;
|
|
}
|
|
else {
|
|
throw Base::TypeError("Type in tuple must be consistent (integer)");
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
throw Base::TypeError("Type in tuple must be float or integer");
|
|
}
|
|
}
|
|
else if (PyLong_Check(value)) {
|
|
cCol.setPackedValue(PyLong_AsUnsignedLong(value));
|
|
}
|
|
else {
|
|
std::string error =
|
|
std::string("type must be integer or tuple of float or tuple integer, not ");
|
|
error += value->ob_type->tp_name;
|
|
throw Base::TypeError(error);
|
|
}
|
|
|
|
setValue(cCol);
|
|
}
|
|
|
|
void PropertyColor::Save(Base::Writer& writer) const
|
|
{
|
|
writer.Stream() << writer.ind() << "<PropertyColor value=\"" << _cCol.getPackedValue() << "\"/>"
|
|
<< endl;
|
|
}
|
|
|
|
void PropertyColor::Restore(Base::XMLReader& reader)
|
|
{
|
|
// read my Element
|
|
reader.readElement("PropertyColor");
|
|
// get the value of my Attribute
|
|
unsigned long rgba = reader.getAttributeAsUnsigned("value");
|
|
setValue(rgba);
|
|
}
|
|
|
|
Property* PropertyColor::Copy() const
|
|
{
|
|
PropertyColor* p = new PropertyColor();
|
|
p->_cCol = _cCol;
|
|
return p;
|
|
}
|
|
|
|
void PropertyColor::Paste(const Property& from)
|
|
{
|
|
aboutToSetValue();
|
|
_cCol = dynamic_cast<const PropertyColor&>(from)._cCol;
|
|
hasSetValue();
|
|
}
|
|
|
|
//**************************************************************************
|
|
// PropertyColorList
|
|
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
|
|
TYPESYSTEM_SOURCE(App::PropertyColorList, App::PropertyLists)
|
|
|
|
//**************************************************************************
|
|
// Construction/Destruction
|
|
|
|
PropertyColorList::PropertyColorList() = default;
|
|
|
|
PropertyColorList::~PropertyColorList() = default;
|
|
|
|
//**************************************************************************
|
|
// Base class implementer
|
|
|
|
PyObject* PropertyColorList::getPyObject()
|
|
{
|
|
PyObject* list = PyList_New(getSize());
|
|
|
|
for (int i = 0; i < getSize(); i++) {
|
|
PyObject* rgba = PyTuple_New(4);
|
|
PyObject* r = PyFloat_FromDouble(_lValueList[i].r);
|
|
PyObject* g = PyFloat_FromDouble(_lValueList[i].g);
|
|
PyObject* b = PyFloat_FromDouble(_lValueList[i].b);
|
|
PyObject* a = PyFloat_FromDouble(_lValueList[i].a);
|
|
|
|
PyTuple_SetItem(rgba, 0, r);
|
|
PyTuple_SetItem(rgba, 1, g);
|
|
PyTuple_SetItem(rgba, 2, b);
|
|
PyTuple_SetItem(rgba, 3, a);
|
|
|
|
PyList_SetItem(list, i, rgba);
|
|
}
|
|
|
|
return list;
|
|
}
|
|
|
|
Color PropertyColorList::getPyValue(PyObject* item) const
|
|
{
|
|
PropertyColor col;
|
|
col.setPyObject(item);
|
|
return col.getValue();
|
|
}
|
|
|
|
void PropertyColorList::Save(Base::Writer& writer) const
|
|
{
|
|
if (!writer.isForceXML()) {
|
|
writer.Stream() << writer.ind() << "<ColorList file=\""
|
|
<< (getSize() ? writer.addFile(getName(), this) : "") << "\"/>"
|
|
<< std::endl;
|
|
}
|
|
}
|
|
|
|
void PropertyColorList::Restore(Base::XMLReader& reader)
|
|
{
|
|
reader.readElement("ColorList");
|
|
if (reader.hasAttribute("file")) {
|
|
std::string file(reader.getAttribute("file"));
|
|
|
|
if (!file.empty()) {
|
|
// initiate a file read
|
|
reader.addFile(file.c_str(), this);
|
|
}
|
|
}
|
|
}
|
|
|
|
void PropertyColorList::SaveDocFile(Base::Writer& writer) const
|
|
{
|
|
Base::OutputStream str(writer.Stream());
|
|
uint32_t uCt = (uint32_t)getSize();
|
|
str << uCt;
|
|
for (auto it : _lValueList) {
|
|
str << it.getPackedValue();
|
|
}
|
|
}
|
|
|
|
void PropertyColorList::RestoreDocFile(Base::Reader& reader)
|
|
{
|
|
Base::InputStream str(reader);
|
|
uint32_t uCt = 0;
|
|
str >> uCt;
|
|
std::vector<Color> values(uCt);
|
|
uint32_t value; // must be 32 bit long
|
|
for (auto& it : values) {
|
|
str >> value;
|
|
it.setPackedValue(value);
|
|
}
|
|
setValues(values);
|
|
}
|
|
|
|
Property* PropertyColorList::Copy() const
|
|
{
|
|
PropertyColorList* p = new PropertyColorList();
|
|
p->_lValueList = _lValueList;
|
|
return p;
|
|
}
|
|
|
|
void PropertyColorList::Paste(const Property& from)
|
|
{
|
|
setValues(dynamic_cast<const PropertyColorList&>(from)._lValueList);
|
|
}
|
|
|
|
unsigned int PropertyColorList::getMemSize() const
|
|
{
|
|
return static_cast<unsigned int>(_lValueList.size() * sizeof(Color));
|
|
}
|
|
|
|
//**************************************************************************
|
|
//**************************************************************************
|
|
// PropertyMaterial
|
|
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
|
|
TYPESYSTEM_SOURCE(App::PropertyMaterial, App::Property)
|
|
|
|
PropertyMaterial::PropertyMaterial() = default;
|
|
|
|
PropertyMaterial::~PropertyMaterial() = default;
|
|
|
|
void PropertyMaterial::setValue(const Material& mat)
|
|
{
|
|
aboutToSetValue();
|
|
_cMat = mat;
|
|
hasSetValue();
|
|
}
|
|
|
|
void PropertyMaterial::setValue(const Color& col)
|
|
{
|
|
setDiffuseColor(col);
|
|
}
|
|
|
|
void PropertyMaterial::setValue(float r, float g, float b, float a)
|
|
{
|
|
setDiffuseColor(r, g, b, a);
|
|
}
|
|
|
|
void PropertyMaterial::setValue(uint32_t rgba)
|
|
{
|
|
setDiffuseColor(rgba);
|
|
}
|
|
|
|
const Material& PropertyMaterial::getValue() const
|
|
{
|
|
return _cMat;
|
|
}
|
|
|
|
void PropertyMaterial::setAmbientColor(const Color& col)
|
|
{
|
|
aboutToSetValue();
|
|
_cMat.ambientColor = col;
|
|
hasSetValue();
|
|
}
|
|
|
|
void PropertyMaterial::setAmbientColor(float r, float g, float b, float a)
|
|
{
|
|
aboutToSetValue();
|
|
_cMat.ambientColor.set(r, g, b, a);
|
|
hasSetValue();
|
|
}
|
|
|
|
void PropertyMaterial::setAmbientColor(uint32_t rgba)
|
|
{
|
|
aboutToSetValue();
|
|
_cMat.ambientColor.setPackedValue(rgba);
|
|
hasSetValue();
|
|
}
|
|
|
|
void PropertyMaterial::setDiffuseColor(const Color& col)
|
|
{
|
|
aboutToSetValue();
|
|
_cMat.diffuseColor = col;
|
|
hasSetValue();
|
|
}
|
|
|
|
void PropertyMaterial::setDiffuseColor(float r, float g, float b, float a)
|
|
{
|
|
aboutToSetValue();
|
|
_cMat.diffuseColor.set(r, g, b, a);
|
|
hasSetValue();
|
|
}
|
|
|
|
void PropertyMaterial::setDiffuseColor(uint32_t rgba)
|
|
{
|
|
aboutToSetValue();
|
|
_cMat.diffuseColor.setPackedValue(rgba);
|
|
hasSetValue();
|
|
}
|
|
|
|
void PropertyMaterial::setSpecularColor(const Color& col)
|
|
{
|
|
aboutToSetValue();
|
|
_cMat.specularColor = col;
|
|
hasSetValue();
|
|
}
|
|
|
|
void PropertyMaterial::setSpecularColor(float r, float g, float b, float a)
|
|
{
|
|
aboutToSetValue();
|
|
_cMat.specularColor.set(r, g, b, a);
|
|
hasSetValue();
|
|
}
|
|
|
|
void PropertyMaterial::setSpecularColor(uint32_t rgba)
|
|
{
|
|
aboutToSetValue();
|
|
_cMat.specularColor.setPackedValue(rgba);
|
|
hasSetValue();
|
|
}
|
|
|
|
void PropertyMaterial::setEmissiveColor(const Color& col)
|
|
{
|
|
aboutToSetValue();
|
|
_cMat.emissiveColor = col;
|
|
hasSetValue();
|
|
}
|
|
|
|
void PropertyMaterial::setEmissiveColor(float r, float g, float b, float a)
|
|
{
|
|
aboutToSetValue();
|
|
_cMat.emissiveColor.set(r, g, b, a);
|
|
hasSetValue();
|
|
}
|
|
|
|
void PropertyMaterial::setEmissiveColor(uint32_t rgba)
|
|
{
|
|
aboutToSetValue();
|
|
_cMat.emissiveColor.setPackedValue(rgba);
|
|
hasSetValue();
|
|
}
|
|
|
|
void PropertyMaterial::setShininess(float val)
|
|
{
|
|
aboutToSetValue();
|
|
_cMat.shininess = val;
|
|
hasSetValue();
|
|
}
|
|
|
|
void PropertyMaterial::setTransparency(float val)
|
|
{
|
|
aboutToSetValue();
|
|
_cMat.transparency = val;
|
|
hasSetValue();
|
|
}
|
|
|
|
const Color& PropertyMaterial::getAmbientColor() const
|
|
{
|
|
return _cMat.ambientColor;
|
|
}
|
|
|
|
const Color& PropertyMaterial::getDiffuseColor() const
|
|
{
|
|
return _cMat.diffuseColor;
|
|
}
|
|
|
|
const Color& PropertyMaterial::getSpecularColor() const
|
|
{
|
|
return _cMat.specularColor;
|
|
}
|
|
|
|
const Color& PropertyMaterial::getEmissiveColor() const
|
|
{
|
|
return _cMat.emissiveColor;
|
|
}
|
|
|
|
double PropertyMaterial::getShininess() const
|
|
{
|
|
return _cMat.shininess;
|
|
}
|
|
|
|
double PropertyMaterial::getTransparency() const
|
|
{
|
|
return _cMat.transparency;
|
|
}
|
|
|
|
PyObject* PropertyMaterial::getPyObject()
|
|
{
|
|
return new MaterialPy(new Material(_cMat));
|
|
}
|
|
|
|
void PropertyMaterial::setPyObject(PyObject* value)
|
|
{
|
|
if (PyObject_TypeCheck(value, &(MaterialPy::Type))) {
|
|
setValue(*static_cast<MaterialPy*>(value)->getMaterialPtr());
|
|
}
|
|
else {
|
|
setValue(MaterialPy::toColor(value));
|
|
}
|
|
}
|
|
|
|
void PropertyMaterial::Save(Base::Writer& writer) const
|
|
{
|
|
// clang-format off
|
|
writer.Stream() << writer.ind()
|
|
<< "<PropertyMaterial ambientColor=\"" << _cMat.ambientColor.getPackedValue()
|
|
<< "\" diffuseColor=\"" << _cMat.diffuseColor.getPackedValue()
|
|
<< "\" specularColor=\"" << _cMat.specularColor.getPackedValue()
|
|
<< "\" emissiveColor=\"" << _cMat.emissiveColor.getPackedValue()
|
|
<< "\" shininess=\"" << _cMat.shininess
|
|
<< "\" transparency=\"" << _cMat.transparency
|
|
<< "\" image=\"" << _cMat.image
|
|
<< "\" imagePath=\"" << _cMat.imagePath
|
|
<< "\" uuid=\"" << _cMat.uuid
|
|
<< "\"/>" << std::endl;
|
|
// clang-format on
|
|
}
|
|
|
|
void PropertyMaterial::Restore(Base::XMLReader& reader)
|
|
{
|
|
// read my Element
|
|
reader.readElement("PropertyMaterial");
|
|
// get the value of my Attribute
|
|
aboutToSetValue();
|
|
_cMat.ambientColor.setPackedValue(reader.getAttributeAsUnsigned("ambientColor"));
|
|
_cMat.diffuseColor.setPackedValue(reader.getAttributeAsUnsigned("diffuseColor"));
|
|
_cMat.specularColor.setPackedValue(reader.getAttributeAsUnsigned("specularColor"));
|
|
_cMat.emissiveColor.setPackedValue(reader.getAttributeAsUnsigned("emissiveColor"));
|
|
_cMat.shininess = (float)reader.getAttributeAsFloat("shininess");
|
|
_cMat.transparency = (float)reader.getAttributeAsFloat("transparency");
|
|
if (reader.hasAttribute("image")) {
|
|
_cMat.image = reader.getAttribute("image");
|
|
}
|
|
if (reader.hasAttribute("imagePath")) {
|
|
_cMat.imagePath = reader.getAttribute("imagePath");
|
|
}
|
|
if (reader.hasAttribute("uuid")) {
|
|
_cMat.uuid = reader.getAttribute("uuid");
|
|
}
|
|
hasSetValue();
|
|
}
|
|
|
|
const char* PropertyMaterial::getEditorName() const
|
|
{
|
|
if (testStatus(MaterialEdit)) {
|
|
return "Gui::PropertyEditor::PropertyMaterialItem";
|
|
}
|
|
return "";
|
|
}
|
|
|
|
Property* PropertyMaterial::Copy() const
|
|
{
|
|
PropertyMaterial* p = new PropertyMaterial();
|
|
p->_cMat = _cMat;
|
|
return p;
|
|
}
|
|
|
|
void PropertyMaterial::Paste(const Property& from)
|
|
{
|
|
aboutToSetValue();
|
|
_cMat = dynamic_cast<const PropertyMaterial&>(from)._cMat;
|
|
hasSetValue();
|
|
}
|
|
|
|
//**************************************************************************
|
|
// PropertyMaterialList
|
|
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
|
|
TYPESYSTEM_SOURCE(App::PropertyMaterialList, App::PropertyLists)
|
|
|
|
//**************************************************************************
|
|
// Construction/Destruction
|
|
|
|
PropertyMaterialList::PropertyMaterialList()
|
|
{
|
|
setMinimumSizeOne();
|
|
}
|
|
|
|
PropertyMaterialList::~PropertyMaterialList() = default;
|
|
|
|
//**************************************************************************
|
|
// Base class implementer
|
|
|
|
void PropertyMaterialList::setValues(const std::vector<App::Material>& newValues)
|
|
{
|
|
if (!newValues.empty()) {
|
|
PropertyListsT<Material>::setValues(newValues);
|
|
}
|
|
else {
|
|
aboutToSetValue();
|
|
setSize(1);
|
|
hasSetValue();
|
|
}
|
|
}
|
|
|
|
PyObject* PropertyMaterialList::getPyObject()
|
|
{
|
|
Py::Tuple tuple(getSize());
|
|
|
|
for (int i = 0; i < getSize(); i++) {
|
|
tuple.setItem(i, Py::asObject(new MaterialPy(new Material(_lValueList[i]))));
|
|
}
|
|
|
|
return Py::new_reference_to(tuple);
|
|
}
|
|
|
|
void PropertyMaterialList::verifyIndex(int index) const
|
|
{
|
|
int size = getSize();
|
|
if (index < -1 || index > size) {
|
|
throw Base::RuntimeError("index out of bound");
|
|
}
|
|
}
|
|
|
|
void PropertyMaterialList::setMinimumSizeOne()
|
|
{
|
|
int size = getSize();
|
|
if (size < 1) {
|
|
setSize(1);
|
|
}
|
|
}
|
|
|
|
int PropertyMaterialList::resizeByOneIfNeeded(int index)
|
|
{
|
|
int size = getSize();
|
|
if (index == -1 || index == size) {
|
|
index = size;
|
|
setSize(size + 1);
|
|
}
|
|
|
|
return index;
|
|
}
|
|
|
|
void PropertyMaterialList::setValue()
|
|
{
|
|
Material empty;
|
|
setValue(empty);
|
|
}
|
|
|
|
void PropertyMaterialList::setValue(const Material& mat)
|
|
{
|
|
aboutToSetValue();
|
|
setSize(1);
|
|
for (auto& material : _lValueList) {
|
|
material = mat;
|
|
}
|
|
hasSetValue();
|
|
}
|
|
|
|
void PropertyMaterialList::setValue(int index, const Material& mat)
|
|
{
|
|
verifyIndex(index);
|
|
|
|
aboutToSetValue();
|
|
index = resizeByOneIfNeeded(index);
|
|
_lValueList[index] = mat;
|
|
hasSetValue();
|
|
}
|
|
|
|
void PropertyMaterialList::setAmbientColor(const Color& col)
|
|
{
|
|
aboutToSetValue();
|
|
setMinimumSizeOne();
|
|
for (auto& material : _lValueList) {
|
|
material.ambientColor = col;
|
|
}
|
|
hasSetValue();
|
|
}
|
|
|
|
void PropertyMaterialList::setAmbientColor(float r, float g, float b, float a)
|
|
{
|
|
aboutToSetValue();
|
|
setMinimumSizeOne();
|
|
for (auto& material : _lValueList) {
|
|
material.ambientColor.set(r, g, b, a);
|
|
}
|
|
hasSetValue();
|
|
}
|
|
|
|
void PropertyMaterialList::setAmbientColor(uint32_t rgba)
|
|
{
|
|
aboutToSetValue();
|
|
setMinimumSizeOne();
|
|
for (auto& material : _lValueList) {
|
|
material.ambientColor.setPackedValue(rgba);
|
|
}
|
|
hasSetValue();
|
|
}
|
|
|
|
void PropertyMaterialList::setAmbientColor(int index, const Color& col)
|
|
{
|
|
verifyIndex(index);
|
|
|
|
aboutToSetValue();
|
|
index = resizeByOneIfNeeded(index);
|
|
_lValueList[index].ambientColor = col;
|
|
hasSetValue();
|
|
}
|
|
|
|
void PropertyMaterialList::setAmbientColor(int index, float r, float g, float b, float a)
|
|
{
|
|
verifyIndex(index);
|
|
|
|
aboutToSetValue();
|
|
index = resizeByOneIfNeeded(index);
|
|
_lValueList[index].ambientColor.set(r, g, b, a);
|
|
hasSetValue();
|
|
}
|
|
|
|
void PropertyMaterialList::setAmbientColor(int index, uint32_t rgba)
|
|
{
|
|
verifyIndex(index);
|
|
|
|
aboutToSetValue();
|
|
index = resizeByOneIfNeeded(index);
|
|
_lValueList[index].ambientColor.setPackedValue(rgba);
|
|
hasSetValue();
|
|
}
|
|
|
|
void PropertyMaterialList::setDiffuseColor(const Color& col)
|
|
{
|
|
aboutToSetValue();
|
|
setMinimumSizeOne();
|
|
for (auto& material : _lValueList) {
|
|
material.diffuseColor = col;
|
|
}
|
|
hasSetValue();
|
|
}
|
|
|
|
void PropertyMaterialList::setDiffuseColor(float r, float g, float b, float a)
|
|
{
|
|
aboutToSetValue();
|
|
setMinimumSizeOne();
|
|
for (auto& material : _lValueList) {
|
|
material.diffuseColor.set(r, g, b, a);
|
|
}
|
|
hasSetValue();
|
|
}
|
|
|
|
void PropertyMaterialList::setDiffuseColor(uint32_t rgba)
|
|
{
|
|
aboutToSetValue();
|
|
setMinimumSizeOne();
|
|
for (auto& material : _lValueList) {
|
|
material.diffuseColor.setPackedValue(rgba);
|
|
}
|
|
hasSetValue();
|
|
}
|
|
|
|
void PropertyMaterialList::setDiffuseColor(int index, const Color& col)
|
|
{
|
|
verifyIndex(index);
|
|
|
|
aboutToSetValue();
|
|
index = resizeByOneIfNeeded(index);
|
|
_lValueList[index].diffuseColor = col;
|
|
hasSetValue();
|
|
}
|
|
|
|
void PropertyMaterialList::setDiffuseColor(int index, float r, float g, float b, float a)
|
|
{
|
|
verifyIndex(index);
|
|
|
|
aboutToSetValue();
|
|
index = resizeByOneIfNeeded(index);
|
|
_lValueList[index].diffuseColor.set(r, g, b, a);
|
|
hasSetValue();
|
|
}
|
|
|
|
void PropertyMaterialList::setDiffuseColor(int index, uint32_t rgba)
|
|
{
|
|
verifyIndex(index);
|
|
|
|
aboutToSetValue();
|
|
index = resizeByOneIfNeeded(index);
|
|
_lValueList[index].diffuseColor.setPackedValue(rgba);
|
|
hasSetValue();
|
|
}
|
|
|
|
void PropertyMaterialList::setDiffuseColors(const std::vector<App::Color>& colors)
|
|
{
|
|
aboutToSetValue();
|
|
setSize(colors.size(), _lValueList[0]);
|
|
|
|
for (std::size_t i = 0; i < colors.size(); i++) {
|
|
_lValueList[i].diffuseColor = colors[i];
|
|
}
|
|
hasSetValue();
|
|
}
|
|
|
|
void PropertyMaterialList::setSpecularColor(const Color& col)
|
|
{
|
|
aboutToSetValue();
|
|
setMinimumSizeOne();
|
|
for (auto& material : _lValueList) {
|
|
material.specularColor = col;
|
|
}
|
|
hasSetValue();
|
|
}
|
|
|
|
void PropertyMaterialList::setSpecularColor(float r, float g, float b, float a)
|
|
{
|
|
aboutToSetValue();
|
|
setMinimumSizeOne();
|
|
for (auto& material : _lValueList) {
|
|
material.specularColor.set(r, g, b, a);
|
|
}
|
|
hasSetValue();
|
|
}
|
|
|
|
void PropertyMaterialList::setSpecularColor(uint32_t rgba)
|
|
{
|
|
aboutToSetValue();
|
|
setMinimumSizeOne();
|
|
for (auto& material : _lValueList) {
|
|
material.specularColor.setPackedValue(rgba);
|
|
}
|
|
hasSetValue();
|
|
}
|
|
|
|
void PropertyMaterialList::setSpecularColor(int index, const Color& col)
|
|
{
|
|
verifyIndex(index);
|
|
|
|
aboutToSetValue();
|
|
index = resizeByOneIfNeeded(index);
|
|
_lValueList[index].specularColor = col;
|
|
hasSetValue();
|
|
}
|
|
|
|
void PropertyMaterialList::setSpecularColor(int index, float r, float g, float b, float a)
|
|
{
|
|
verifyIndex(index);
|
|
|
|
aboutToSetValue();
|
|
index = resizeByOneIfNeeded(index);
|
|
_lValueList[index].specularColor.set(r, g, b, a);
|
|
hasSetValue();
|
|
}
|
|
|
|
void PropertyMaterialList::setSpecularColor(int index, uint32_t rgba)
|
|
{
|
|
verifyIndex(index);
|
|
|
|
aboutToSetValue();
|
|
index = resizeByOneIfNeeded(index);
|
|
_lValueList[index].specularColor.setPackedValue(rgba);
|
|
hasSetValue();
|
|
}
|
|
|
|
void PropertyMaterialList::setEmissiveColor(const Color& col)
|
|
{
|
|
aboutToSetValue();
|
|
setMinimumSizeOne();
|
|
for (auto& material : _lValueList) {
|
|
material.emissiveColor = col;
|
|
}
|
|
hasSetValue();
|
|
}
|
|
|
|
void PropertyMaterialList::setEmissiveColor(float r, float g, float b, float a)
|
|
{
|
|
aboutToSetValue();
|
|
setMinimumSizeOne();
|
|
for (auto& material : _lValueList) {
|
|
material.emissiveColor.set(r, g, b, a);
|
|
}
|
|
hasSetValue();
|
|
}
|
|
|
|
void PropertyMaterialList::setEmissiveColor(uint32_t rgba)
|
|
{
|
|
aboutToSetValue();
|
|
setMinimumSizeOne();
|
|
for (auto& material : _lValueList) {
|
|
material.emissiveColor.setPackedValue(rgba);
|
|
}
|
|
hasSetValue();
|
|
}
|
|
|
|
void PropertyMaterialList::setEmissiveColor(int index, const Color& col)
|
|
{
|
|
verifyIndex(index);
|
|
|
|
aboutToSetValue();
|
|
index = resizeByOneIfNeeded(index);
|
|
_lValueList[index].emissiveColor = col;
|
|
hasSetValue();
|
|
}
|
|
|
|
void PropertyMaterialList::setEmissiveColor(int index, float r, float g, float b, float a)
|
|
{
|
|
verifyIndex(index);
|
|
|
|
aboutToSetValue();
|
|
index = resizeByOneIfNeeded(index);
|
|
_lValueList[index].emissiveColor.set(r, g, b, a);
|
|
hasSetValue();
|
|
}
|
|
|
|
void PropertyMaterialList::setEmissiveColor(int index, uint32_t rgba)
|
|
{
|
|
verifyIndex(index);
|
|
|
|
aboutToSetValue();
|
|
index = resizeByOneIfNeeded(index);
|
|
_lValueList[index].emissiveColor.setPackedValue(rgba);
|
|
hasSetValue();
|
|
}
|
|
|
|
void PropertyMaterialList::setShininess(float val)
|
|
{
|
|
aboutToSetValue();
|
|
setMinimumSizeOne();
|
|
for (auto& material : _lValueList) {
|
|
material.shininess = val;
|
|
}
|
|
hasSetValue();
|
|
}
|
|
|
|
void PropertyMaterialList::setShininess(int index, float val)
|
|
{
|
|
verifyIndex(index);
|
|
|
|
aboutToSetValue();
|
|
index = resizeByOneIfNeeded(index);
|
|
_lValueList[index].shininess = val;
|
|
hasSetValue();
|
|
}
|
|
|
|
void PropertyMaterialList::setTransparency(float val)
|
|
{
|
|
aboutToSetValue();
|
|
setMinimumSizeOne();
|
|
for (auto& material : _lValueList) {
|
|
material.transparency = val;
|
|
}
|
|
hasSetValue();
|
|
}
|
|
|
|
void PropertyMaterialList::setTransparency(int index, float val)
|
|
{
|
|
verifyIndex(index);
|
|
|
|
aboutToSetValue();
|
|
index = resizeByOneIfNeeded(index);
|
|
_lValueList[index].transparency = val;
|
|
hasSetValue();
|
|
}
|
|
|
|
void PropertyMaterialList::setTransparencies(const std::vector<float>& transparencies)
|
|
{
|
|
aboutToSetValue();
|
|
setSize(transparencies.size(), _lValueList[0]);
|
|
|
|
for (std::size_t i = 0; i < transparencies.size(); i++) {
|
|
_lValueList[i].transparency = transparencies[i];
|
|
}
|
|
hasSetValue();
|
|
}
|
|
|
|
const Color& PropertyMaterialList::getAmbientColor() const
|
|
{
|
|
return _lValueList[0].ambientColor;
|
|
}
|
|
|
|
const Color& PropertyMaterialList::getAmbientColor(int index) const
|
|
{
|
|
return _lValueList[index].ambientColor;
|
|
}
|
|
|
|
const Color& PropertyMaterialList::getDiffuseColor() const
|
|
{
|
|
return _lValueList[0].diffuseColor;
|
|
}
|
|
|
|
const Color& PropertyMaterialList::getDiffuseColor(int index) const
|
|
{
|
|
return _lValueList[index].diffuseColor;
|
|
}
|
|
|
|
std::vector<App::Color> PropertyMaterialList::getDiffuseColors() const
|
|
{
|
|
std::vector<App::Color> list;
|
|
for (auto& material : _lValueList) {
|
|
list.push_back(material.diffuseColor);
|
|
}
|
|
|
|
return list;
|
|
}
|
|
|
|
const Color& PropertyMaterialList::getSpecularColor() const
|
|
{
|
|
return _lValueList[0].specularColor;
|
|
}
|
|
|
|
const Color& PropertyMaterialList::getSpecularColor(int index) const
|
|
{
|
|
return _lValueList[index].specularColor;
|
|
}
|
|
|
|
const Color& PropertyMaterialList::getEmissiveColor() const
|
|
{
|
|
return _lValueList[0].emissiveColor;
|
|
}
|
|
|
|
const Color& PropertyMaterialList::getEmissiveColor(int index) const
|
|
{
|
|
return _lValueList[index].emissiveColor;
|
|
}
|
|
|
|
float PropertyMaterialList::getShininess() const
|
|
{
|
|
return _lValueList[0].shininess;
|
|
}
|
|
|
|
float PropertyMaterialList::getShininess(int index) const
|
|
{
|
|
return _lValueList[index].shininess;
|
|
}
|
|
|
|
float PropertyMaterialList::getTransparency() const
|
|
{
|
|
return _lValueList[0].transparency;
|
|
}
|
|
|
|
float PropertyMaterialList::getTransparency(int index) const
|
|
{
|
|
return _lValueList[index].transparency;
|
|
}
|
|
|
|
std::vector<float> PropertyMaterialList::getTransparencies() const
|
|
{
|
|
std::vector<float> list;
|
|
for (auto& material : _lValueList) {
|
|
list.push_back(material.transparency);
|
|
}
|
|
|
|
return list;
|
|
}
|
|
|
|
Material PropertyMaterialList::getPyValue(PyObject* value) const
|
|
{
|
|
if (PyObject_TypeCheck(value, &(MaterialPy::Type))) {
|
|
return *static_cast<MaterialPy*>(value)->getMaterialPtr();
|
|
}
|
|
else {
|
|
std::string error = std::string("type must be 'Material', not ");
|
|
error += value->ob_type->tp_name;
|
|
throw Base::TypeError(error);
|
|
}
|
|
}
|
|
|
|
void PropertyMaterialList::Save(Base::Writer& writer) const
|
|
{
|
|
if (!writer.isForceXML()) {
|
|
writer.Stream() << writer.ind() << "<MaterialList file=\""
|
|
<< (getSize() ? writer.addFile(getName(), this) : "") << "\""
|
|
<< " version=\"3\"/>" << std::endl;
|
|
}
|
|
}
|
|
|
|
void PropertyMaterialList::Restore(Base::XMLReader& reader)
|
|
{
|
|
reader.readElement("MaterialList");
|
|
if (reader.hasAttribute("file")) {
|
|
std::string file(reader.getAttribute("file"));
|
|
if (reader.hasAttribute("version")) {
|
|
formatVersion = static_cast<Format>(reader.getAttributeAsInteger("version"));
|
|
}
|
|
|
|
if (!file.empty()) {
|
|
// initiate a file read
|
|
reader.addFile(file.c_str(), this);
|
|
}
|
|
}
|
|
}
|
|
|
|
void PropertyMaterialList::SaveDocFile(Base::Writer& writer) const
|
|
{
|
|
Base::OutputStream str(writer.Stream());
|
|
uint32_t uCt = (uint32_t)getSize();
|
|
str << uCt;
|
|
for (const auto& it : _lValueList) {
|
|
str << it.ambientColor.getPackedValue();
|
|
str << it.diffuseColor.getPackedValue();
|
|
str << it.specularColor.getPackedValue();
|
|
str << it.emissiveColor.getPackedValue();
|
|
str << it.shininess;
|
|
str << it.transparency;
|
|
}
|
|
|
|
// Apply the latest changes last for backwards compatibility
|
|
for (const auto& it : _lValueList) {
|
|
writeString(str, it.image);
|
|
writeString(str, it.imagePath);
|
|
writeString(str, it.uuid);
|
|
}
|
|
}
|
|
|
|
void PropertyMaterialList::writeString(Base::OutputStream& str, const std::string& value) const
|
|
{
|
|
uint32_t uCt = (uint32_t)value.size();
|
|
str << uCt;
|
|
str.write(value.c_str(), uCt);
|
|
}
|
|
|
|
void PropertyMaterialList::RestoreDocFile(Base::Reader& reader)
|
|
{
|
|
Base::InputStream str(reader);
|
|
if (formatVersion == Version_2) {
|
|
// V2 is same as V0
|
|
uint32_t count = 0;
|
|
str >> count;
|
|
RestoreDocFileV0(count, reader);
|
|
}
|
|
else if (formatVersion == Version_3) {
|
|
// Default to the latest
|
|
RestoreDocFileV3(reader);
|
|
}
|
|
else {
|
|
int32_t version;
|
|
str >> version;
|
|
if (version < 0) {
|
|
// This was a failed attempt at versioning, but is included
|
|
// to support files created during development. In can be removed
|
|
// in a future release once dev files are migrated.
|
|
uint32_t count = 0;
|
|
str >> count;
|
|
RestoreDocFileV0(count, reader);
|
|
}
|
|
else {
|
|
uint32_t uCt = static_cast<uint32_t>(version);
|
|
RestoreDocFileV0(uCt, reader);
|
|
}
|
|
}
|
|
}
|
|
|
|
void PropertyMaterialList::RestoreDocFileV0(uint32_t count, Base::Reader& reader)
|
|
{
|
|
Base::InputStream str(reader);
|
|
std::vector<Material> values(count);
|
|
uint32_t value {}; // must be 32 bit long
|
|
float valueF {};
|
|
for (auto& it : values) {
|
|
str >> value;
|
|
it.ambientColor.setPackedValue(value);
|
|
str >> value;
|
|
it.diffuseColor.setPackedValue(value);
|
|
str >> value;
|
|
it.specularColor.setPackedValue(value);
|
|
str >> value;
|
|
it.emissiveColor.setPackedValue(value);
|
|
str >> valueF;
|
|
it.shininess = valueF;
|
|
str >> valueF;
|
|
it.transparency = valueF;
|
|
}
|
|
setValues(values);
|
|
}
|
|
|
|
void PropertyMaterialList::RestoreDocFileV3(Base::Reader& reader)
|
|
{
|
|
Base::InputStream str(reader);
|
|
uint32_t count = 0;
|
|
str >> count;
|
|
std::vector<Material> values(count);
|
|
uint32_t value {}; // must be 32 bit long
|
|
float valueF {};
|
|
for (auto& it : values) {
|
|
str >> value;
|
|
it.ambientColor.setPackedValue(value);
|
|
str >> value;
|
|
it.diffuseColor.setPackedValue(value);
|
|
str >> value;
|
|
it.specularColor.setPackedValue(value);
|
|
str >> value;
|
|
it.emissiveColor.setPackedValue(value);
|
|
str >> valueF;
|
|
it.shininess = valueF;
|
|
str >> valueF;
|
|
it.transparency = valueF;
|
|
}
|
|
for (auto& it : values) {
|
|
readString(str, it.image);
|
|
readString(str, it.imagePath);
|
|
readString(str, it.uuid);
|
|
}
|
|
setValues(values);
|
|
}
|
|
|
|
void PropertyMaterialList::readString(Base::InputStream& str, std::string& value)
|
|
{
|
|
uint32_t uCt {};
|
|
str >> uCt;
|
|
|
|
std::vector<char> temp(uCt);
|
|
str.read(temp.data(), uCt);
|
|
value.assign(temp.data(), temp.size());
|
|
}
|
|
|
|
const char* PropertyMaterialList::getEditorName() const
|
|
{
|
|
if (testStatus(NoMaterialListEdit)) {
|
|
return "";
|
|
}
|
|
return "Gui::PropertyEditor::PropertyMaterialListItem";
|
|
}
|
|
|
|
Property* PropertyMaterialList::Copy() const
|
|
{
|
|
PropertyMaterialList* p = new PropertyMaterialList();
|
|
p->_lValueList = _lValueList;
|
|
return p;
|
|
}
|
|
|
|
void PropertyMaterialList::Paste(const Property& from)
|
|
{
|
|
setValues(dynamic_cast<const PropertyMaterialList&>(from)._lValueList);
|
|
}
|
|
|
|
unsigned int PropertyMaterialList::getMemSize() const
|
|
{
|
|
return static_cast<unsigned int>(_lValueList.size() * sizeof(Material));
|
|
}
|
|
|
|
//**************************************************************************
|
|
// PropertyPersistentObject
|
|
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
|
|
TYPESYSTEM_SOURCE(App::PropertyPersistentObject, App::PropertyString)
|
|
|
|
PyObject* PropertyPersistentObject::getPyObject()
|
|
{
|
|
if (_pObject) {
|
|
return _pObject->getPyObject();
|
|
}
|
|
return inherited::getPyObject();
|
|
}
|
|
|
|
void PropertyPersistentObject::Save(Base::Writer& writer) const
|
|
{
|
|
inherited::Save(writer);
|
|
writer.Stream() << writer.ind() << "<PersistentObject>" << std::endl;
|
|
if (_pObject) {
|
|
writer.incInd();
|
|
_pObject->Save(writer);
|
|
writer.decInd();
|
|
}
|
|
writer.Stream() << writer.ind() << "</PersistentObject>" << std::endl;
|
|
}
|
|
|
|
void PropertyPersistentObject::Restore(Base::XMLReader& reader)
|
|
{
|
|
inherited::Restore(reader);
|
|
reader.readElement("PersistentObject");
|
|
if (_pObject) {
|
|
_pObject->Restore(reader);
|
|
}
|
|
reader.readEndElement("PersistentObject");
|
|
}
|
|
|
|
Property* PropertyPersistentObject::Copy() const
|
|
{
|
|
auto* p = new PropertyPersistentObject();
|
|
p->_cValue = _cValue;
|
|
p->_pObject = _pObject;
|
|
return p;
|
|
}
|
|
|
|
void PropertyPersistentObject::Paste(const Property& from)
|
|
{
|
|
const auto& prop = dynamic_cast<const PropertyPersistentObject&>(from);
|
|
if (_cValue != prop._cValue || _pObject != prop._pObject) {
|
|
aboutToSetValue();
|
|
_cValue = prop._cValue;
|
|
_pObject = prop._pObject;
|
|
hasSetValue();
|
|
}
|
|
}
|
|
|
|
unsigned int PropertyPersistentObject::getMemSize() const
|
|
{
|
|
auto size = inherited::getMemSize();
|
|
if (_pObject) {
|
|
size += _pObject->getMemSize();
|
|
}
|
|
return size;
|
|
}
|
|
|
|
void PropertyPersistentObject::setValue(const char* type)
|
|
{
|
|
if (!type) {
|
|
type = "";
|
|
}
|
|
if (type[0]) {
|
|
Base::Type::importModule(type);
|
|
Base::Type t = Base::Type::fromName(type);
|
|
if (t.isBad()) {
|
|
throw Base::TypeError("Invalid type");
|
|
}
|
|
if (!t.isDerivedFrom(Persistence::getClassTypeId())) {
|
|
throw Base::TypeError("Type must be derived from Base::Persistence");
|
|
}
|
|
if (_pObject && _pObject->getTypeId() == t) {
|
|
return;
|
|
}
|
|
}
|
|
aboutToSetValue();
|
|
_pObject.reset();
|
|
_cValue = type;
|
|
if (type[0]) {
|
|
_pObject.reset(static_cast<Base::Persistence*>(Base::Type::createInstanceByName(type)));
|
|
}
|
|
hasSetValue();
|
|
}
|