/*************************************************************************** * Copyright (c) 2004 Werner Mayer * * * * 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" #ifndef _PreComp_ # include # include # include # include # include # include # include # include # include # include # include #endif #include "PropertyItem.h" #include "PropertyView.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace Gui::PropertyEditor; using namespace Gui::Dialog; Gui::PropertyEditor::PropertyItemFactory* Gui::PropertyEditor::PropertyItemFactory::_singleton = nullptr; PropertyItemFactory& PropertyItemFactory::instance() { if (_singleton == nullptr) _singleton = new PropertyItemFactory; return *_singleton; } void PropertyItemFactory::destruct () { delete _singleton; _singleton = nullptr; } PropertyItem* PropertyItemFactory::createPropertyItem (const char* sName) const { PropertyItem* w = static_cast(Produce(sName)); return w; } // ---------------------------------------------------- QVariant PropertyItemAttorney::toString(PropertyItem* item, const QVariant& v) { return item->toString(v); } // ---------------------------------------------------- Q_DECLARE_METATYPE(Py::Object) PROPERTYITEM_SOURCE(Gui::PropertyEditor::PropertyItem) PropertyItem::PropertyItem() : parentItem(nullptr), readonly(false), linked(false), expanded(false) { precision = Base::UnitsApi::getDecimals(); setAutoApply(true); } PropertyItem::~PropertyItem() { qDeleteAll(childItems); } void PropertyItem::initialize() { } void PropertyItem::reset() { qDeleteAll(childItems); childItems.clear(); } void PropertyItem::onChange() { if(hasExpression()) { for(auto child : childItems) { if(child && child->hasExpression()) child->setExpression(std::shared_ptr()); } for(auto item=parentItem;item;item=item->parentItem) { if(item->hasExpression()) item->setExpression(std::shared_ptr()); } } } bool PropertyItem::hasAnyExpression() const { if(ExpressionBinding::hasExpression()) return true; if(parentItem) return parentItem->hasExpression(); return false; } void PropertyItem::setPropertyData(const std::vector& items) { //if we have a single property we can bind it for expression handling if (items.size() == 1) { const App::Property& p = *items.front(); try { // Check for 'DocumentObject' as parent because otherwise 'ObjectIdentifier' raises an exception App::DocumentObject * docObj = Base::freecad_dynamic_cast(p.getContainer()); if (docObj && !docObj->isReadOnly(&p)) { App::ObjectIdentifier id(p); std::vector paths; p.getPaths(paths); //there may be no paths available in this property (for example an empty constraint list) if (id.getProperty() && !paths.empty()) bind(id); } } //it may happen that setting properties is not possible catch (...) { } } propertyItems = items; updateData(); this->initialize(); } void PropertyItem::updateData() { bool ro = true; for (std::vector::const_iterator it = propertyItems.begin(); it != propertyItems.end(); ++it) { App::PropertyContainer* parent = (*it)->getContainer(); if (parent) ro &= (parent->isReadOnly(*it) || (*it)->testStatus(App::Property::ReadOnly)); } this->setReadOnly(ro); } const std::vector& PropertyItem::getPropertyData() const { return propertyItems; } bool PropertyItem::hasProperty(const App::Property* prop) const { std::vector::const_iterator it = std::find(propertyItems.begin(), propertyItems.end(), prop); if (it != propertyItems.end()) return true; else return false; } void PropertyItem::assignProperty(const App::Property*) { } bool PropertyItem::removeProperty(const App::Property* prop) { std::vector::iterator it = std::find(propertyItems.begin(), propertyItems.end(), prop); if (it != propertyItems.end()) { propertyItems.erase(it); } return propertyItems.empty(); } App::Property* PropertyItem::getFirstProperty() { if (propertyItems.empty()) return nullptr; return propertyItems.front(); } const App::Property* PropertyItem::getFirstProperty() const { if (propertyItems.empty()) return nullptr; return propertyItems.front(); } void PropertyItem::setParent(PropertyItem* parent) { parentItem = parent; } PropertyItem *PropertyItem::parent() const { return parentItem; } void PropertyItem::appendChild(PropertyItem *item) { childItems.append(item); } void PropertyItem::insertChild(int index, PropertyItem *child) { childItems.insert(index, child); } /*! * \brief PropertyItem::removeChildren * Deletes the children in the range of [from, to] */ void PropertyItem::removeChildren(int from, int to) { int count = to-from+1; for (int i=0; isetParent(nullptr); return child; } PropertyItem *PropertyItem::child(int row) { return childItems.value(row); } int PropertyItem::childCount() const { return childItems.count(); } int PropertyItem::columnCount() const { return 2; } void PropertyItem::setReadOnly(bool ro) { readonly = ro; for (QList::iterator it = childItems.begin(); it != childItems.end(); ++it) (*it)->setReadOnly(ro); } bool PropertyItem::isReadOnly() const { return readonly; } void PropertyItem::setLinked(bool l) { linked = l; for (QList::iterator it = childItems.begin(); it != childItems.end(); ++it) (*it)->setLinked(l); } bool PropertyItem::isLinked() const { return linked; } void PropertyItem::setExpanded(bool enable) { expanded = enable; } bool PropertyItem::isExpanded() const { return expanded; } bool PropertyItem::testStatus(App::Property::Status pos) const { std::vector::const_iterator it; for (it = propertyItems.begin(); it != propertyItems.end(); ++it) { if ((*it)->testStatus(pos)) return true; } return false; } void PropertyItem::setDecimals(int prec) { precision = prec; } int PropertyItem::decimals() const { return precision; } QVariant PropertyItem::displayName() const { return QVariant(displayText); } QVariant PropertyItem::toolTip(const App::Property* prop) const { QString str = QApplication::translate("App::Property", prop->getDocumentation()); return QVariant(str); } QVariant PropertyItem::decoration(const QVariant&) const { return QVariant(); } QVariant PropertyItem::toString(const QVariant& prop) const { if (prop != QVariant() || propertyItems.size()!=1) return prop; std::ostringstream ss; Base::PyGILStateLocker lock; try { Py::Object pyobj(propertyItems[0]->getPyObject(),true); if(pyobj.isNone()) { ss << ""; } else if(pyobj.isSequence()) { ss << '['; Py::Sequence seq(pyobj); bool first = true; Py_ssize_t i=0; for (i=0; i<2 && i < seq.size(); ++i) { if (first) first = false; else ss << ", "; ss << Py::Object(seq[i]).as_string(); } if (i < seq.size()) ss << "..."; ss << ']'; } else if (pyobj.isMapping()) { ss << '{'; Py::Mapping map(pyobj); bool first = true; auto it = map.begin(); for(int i=0; i<2 && it != map.end(); ++it, ++i) { if (first) first = false; else ss << ", "; const auto &v = *it; ss << Py::Object(v.first).as_string() << ':' << Py::Object(v.second).as_string(); } if (it != map.end()) ss << "..."; ss << '}'; } else ss << pyobj.as_string(); } catch (Py::Exception &) { Base::PyException e; ss.str(""); ss << "ERR: " << e.what(); } catch (Base::Exception &e) { ss.str(""); ss << "ERR: " << e.what(); } catch (std::exception &e) { ss.str(""); ss << "ERR: " << e.what(); } catch (...) { ss.str(""); ss << "ERR!"; } return QVariant(QString::fromUtf8(ss.str().c_str())); } QVariant PropertyItem::value(const App::Property* /*prop*/) const { return QVariant(); } void PropertyItem::setValue(const QVariant& /*value*/) { } QWidget* PropertyItem::createEditor(QWidget* /*parent*/, const QObject* /*receiver*/, const char* /*method*/) const { return nullptr; } void PropertyItem::setEditorData(QWidget * /*editor*/, const QVariant& /*data*/) const { } QVariant PropertyItem::editorData(QWidget * /*editor*/) const { return QVariant(); } QWidget* PropertyItem::createExpressionEditor(QWidget* parent, const QObject* receiver, const char* method) const { if(!isBound()) return nullptr; ExpLineEdit *le = new ExpLineEdit(parent,true); le->setFrame(false); le->setReadOnly(true); QObject::connect(le, SIGNAL(textChanged(const QString&)), receiver, method); le->bind(getPath()); le->setAutoApply(autoApply()); return le; } void PropertyItem::setExpressionEditorData(QWidget *editor, const QVariant& data) const { QLineEdit *le = qobject_cast(editor); if(le) le->setText(data.toString()); } QVariant PropertyItem::expressionEditorData(QWidget *editor) const { QLineEdit *le = qobject_cast(editor); if(le) return QVariant(le->text()); return QVariant(); } PropertyEditorWidget* PropertyItem::createPropertyEditorWidget(QWidget* parent) const { PropertyEditorWidget* editor = new PropertyEditorWidget(parent); connect(editor, &PropertyEditorWidget::buttonClick, this, [this]() { const auto &props = this->getPropertyData(); if (!props.empty() && props[0]->getName() && props[0]->testStatus(App::Property::UserEdit) && props[0]->getContainer()) { props[0]->getContainer()->editProperty(props[0]->getName()); } }); return editor; } QString PropertyItem::propertyName() const { if (propName.isEmpty()) return QLatin1String(QT_TRANSLATE_NOOP("App::Property", "")); return propName; } void PropertyItem::setPropertyName(QString name, QString realName) { if(realName.size()) propName = realName; else propName = name; setObjectName(propName); QString display; bool upper = false; for (int i=0; i::const_iterator it = propertyItems.begin(); it != propertyItems.end(); ++it) { auto prop = *it; App::PropertyContainer* parent = prop->getContainer(); if (!parent || parent->isReadOnly(prop) || prop->testStatus(App::Property::ReadOnly)) continue; if (parent->isDerivedFrom(App::Document::getClassTypeId())) { App::Document* doc = static_cast(parent); ss << "FreeCAD.getDocument('" << doc->getName() << "')."; } else if (parent->isDerivedFrom(App::DocumentObject::getClassTypeId())) { App::DocumentObject* obj = static_cast(parent); App::Document* doc = obj->getDocument(); ss << "FreeCAD.getDocument('" << doc->getName() << "').getObject('" << obj->getNameInDocument() << "')."; } else if (parent->isDerivedFrom(ViewProviderDocumentObject::getClassTypeId())) { App::DocumentObject* obj = static_cast(parent)->getObject(); App::Document* doc = obj->getDocument(); ss << "FreeCADGui.getDocument('" << doc->getName() << "').getObject('" << obj->getNameInDocument() << "')."; } else { continue; } ss << parent->getPropertyPrefix() << prop->getName() << " = " << value.toUtf8().constData() << '\n'; } std::string cmd = ss.str(); if (cmd.empty()) return; try { Gui::Command::runCommand(Gui::Command::App, cmd.c_str()); } catch (Base::PyException &e) { e.ReportException(); Base::Console().Error("Stack Trace: %s\n",e.getStackTrace().c_str()); } catch (Base::Exception &e) { e.ReportException(); } catch (...) { Base::Console().Error("Unknown C++ exception in PropertyItem::setPropertyValue thrown\n"); } } QVariant PropertyItem::data(int column, int role) const { // property name if (column == 0) { if (role == Qt::ForegroundRole && linked) return QVariant::fromValue(QColor(0x20,0xaa,0x20)); if (role == Qt::BackgroundRole || role == Qt::ForegroundRole) { if(PropertyView::showAll() && propertyItems.size() == 1 && propertyItems.front()->testStatus(App::Property::PropDynamic) && !propertyItems.front()->testStatus(App::Property::LockDynamic)) { return role==Qt::BackgroundRole ? QVariant::fromValue(QColor(0xFF,0xFF,0x99)) : QVariant::fromValue(QColor(0,0,0)); } return QVariant(); } if (role == Qt::DisplayRole) { return displayName(); } // no properties set if (propertyItems.empty()) { return QVariant(); } else if (role == Qt::ToolTipRole) { QString type = QString::fromLatin1("Type: %1\nName: %2").arg( QString::fromLatin1(propertyItems[0]->getTypeId().getName()), objectName()); QString doc = PropertyItem::toolTip(propertyItems[0]).toString(); if(doc.isEmpty()) doc = toolTip(propertyItems[0]).toString(); if(doc.size()) return type + QLatin1String("\n\n") + doc; return type; } return QVariant(); } else { // no properties set if (propertyItems.empty()) { PropertyItem* parent = this->parent(); if (!parent || !parent->parent()) { return QVariant(); } if (role == Qt::EditRole) { return parent->property(qPrintable(objectName())); } else if (role == Qt::DecorationRole) { QVariant val = parent->property(qPrintable(objectName())); return decoration(val); } else if (role == Qt::DisplayRole) { QVariant val = parent->property(qPrintable(objectName())); return toString(val); } else if (role == Qt::ForegroundRole) { if (hasExpression()) return QVariant::fromValue(QApplication::palette().color(QPalette::Link)); return QVariant(); } return QVariant(); } if (role == Qt::EditRole) { return value(propertyItems[0]); } else if (role == Qt::DecorationRole) { return decoration(value(propertyItems[0])); } else if (role == Qt::DisplayRole) { return toString(value(propertyItems[0])); } else if (role == Qt::ToolTipRole) { return toolTip(propertyItems[0]); } else if( role == Qt::ForegroundRole) { if (hasExpression()) return QVariant::fromValue(QApplication::palette().color(QPalette::Link)); return QVariant(); } return QVariant(); } } bool PropertyItem::setData (const QVariant& value) { // This is the basic mechanism to set the value to // a property and if no property is set for this item // it delegates it to its parent which sets then the // property or delegates again to its parent... if (propertyItems.empty()) { PropertyItem* parent = this->parent(); if (!parent || !parent->parent() || hasAnyExpression()) return false; parent->setProperty(qPrintable(objectName()),value); return true; } else { setValue(value); return true; } } Qt::ItemFlags PropertyItem::flags(int column) const { Qt::ItemFlags basicFlags = Qt::ItemIsEnabled | Qt::ItemIsSelectable; if (column == 1 && !isReadOnly()) return basicFlags | Qt::ItemIsEditable; else return basicFlags; } int PropertyItem::row() const { if (parentItem) return parentItem->childItems.indexOf(const_cast(this)); return 0; } void PropertyItem::bind(const App::ObjectIdentifier& _path) { Gui::ExpressionBinding::bind(_path); propertyBound(); } void PropertyItem::bind(const App::Property& prop) { Gui::ExpressionBinding::bind(prop); propertyBound(); } QString PropertyItem::expressionAsString() const { if (hasExpression()) { try { std::unique_ptr result(getExpression()->eval()); return QString::fromStdString(result->toString()); } catch (const Base::Exception& e) { e.ReportException(); } } return QString(); } // -------------------------------------------------------------------- PROPERTYITEM_SOURCE(Gui::PropertyEditor::PropertyStringItem) PropertyStringItem::PropertyStringItem() { } QVariant PropertyStringItem::value(const App::Property* prop) const { assert(prop && prop->getTypeId().isDerivedFrom(App::PropertyString::getClassTypeId())); std::string value = static_cast(prop)->getValue(); return QVariant(QString::fromUtf8(value.c_str())); } void PropertyStringItem::setValue(const QVariant& value) { if(!hasExpression()) { if (!value.canConvert(QVariant::String)) return; QString val = value.toString(); val = QString::fromUtf8(Base::Interpreter().strToPython(val.toUtf8()).c_str()); QString data = QString::fromLatin1("\"%1\"").arg(val); setPropertyValue(data); } } QWidget* PropertyStringItem::createEditor(QWidget* parent, const QObject* receiver, const char* method) const { ExpLineEdit *le = new ExpLineEdit(parent); le->setFrame(false); le->setReadOnly(isReadOnly()); QObject::connect(le, SIGNAL(textChanged(const QString&)), receiver, method); if(isBound()) { le->bind(getPath()); le->setAutoApply(autoApply()); } return le; } void PropertyStringItem::setEditorData(QWidget *editor, const QVariant& data) const { QLineEdit *le = qobject_cast(editor); le->setText(data.toString()); } QVariant PropertyStringItem::editorData(QWidget *editor) const { QLineEdit *le = qobject_cast(editor); return QVariant(le->text()); } // -------------------------------------------------------------------- PROPERTYITEM_SOURCE(Gui::PropertyEditor::PropertyFontItem) PropertyFontItem::PropertyFontItem() { } QVariant PropertyFontItem::value(const App::Property* prop) const { assert(prop && prop->getTypeId().isDerivedFrom(App::PropertyFont::getClassTypeId())); std::string value = static_cast(prop)->getValue(); return QVariant(QString::fromUtf8(value.c_str())); } void PropertyFontItem::setValue(const QVariant& value) { if (hasExpression() || !value.canConvert(QVariant::String)) return; QString val = value.toString(); QString data = QString::fromLatin1("\"%1\"").arg(val); setPropertyValue(data); } QWidget* PropertyFontItem::createEditor(QWidget* parent, const QObject* receiver, const char* method) const { QComboBox *cb = new QComboBox(parent); cb->setFrame(false); cb->setDisabled(isReadOnly()); QObject::connect(cb, SIGNAL(activated(const QString&)), receiver, method); return cb; } void PropertyFontItem::setEditorData(QWidget *editor, const QVariant& data) const { QComboBox *cb = qobject_cast(editor); QFontDatabase fdb; QStringList familyNames = fdb.families(QFontDatabase::Any); cb->addItems(familyNames); int index = familyNames.indexOf(data.toString()); cb->setCurrentIndex(index); } QVariant PropertyFontItem::editorData(QWidget *editor) const { QComboBox *cb = qobject_cast(editor); return QVariant(cb->currentText()); } // -------------------------------------------------------------------- PROPERTYITEM_SOURCE(Gui::PropertyEditor::PropertySeparatorItem) QWidget* PropertySeparatorItem::createEditor(QWidget* parent, const QObject* receiver, const char* method) const { Q_UNUSED(parent); Q_UNUSED(receiver); Q_UNUSED(method); return nullptr; } // -------------------------------------------------------------------- PROPERTYITEM_SOURCE(Gui::PropertyEditor::PropertyIntegerItem) PropertyIntegerItem::PropertyIntegerItem() { } QVariant PropertyIntegerItem::value(const App::Property* prop) const { assert(prop && prop->getTypeId().isDerivedFrom(App::PropertyInteger::getClassTypeId())); int value = (int)static_cast(prop)->getValue(); return QVariant(value); } void PropertyIntegerItem::setValue(const QVariant& value) { //if the item has an expression it issues the python code if (!hasExpression()) { if (!value.canConvert(QVariant::Int)) return; int val = value.toInt(); QString data = QString::fromLatin1("%1").arg(val); setPropertyValue(data); } } QWidget* PropertyIntegerItem::createEditor(QWidget* parent, const QObject* receiver, const char* method) const { Gui::IntSpinBox *sb = new Gui::IntSpinBox(parent); sb->setFrame(false); sb->setReadOnly(isReadOnly()); QObject::connect(sb, SIGNAL(valueChanged(int)), receiver, method); if (isBound()) { sb->bind(getPath()); sb->setAutoApply(autoApply()); } return sb; } void PropertyIntegerItem::setEditorData(QWidget *editor, const QVariant& data) const { QSpinBox *sb = qobject_cast(editor); sb->setRange(INT_MIN, INT_MAX); sb->setValue(data.toInt()); } QVariant PropertyIntegerItem::editorData(QWidget *editor) const { QSpinBox *sb = qobject_cast(editor); return QVariant(sb->value()); } QVariant PropertyIntegerItem::toString(const QVariant& v) const { QString string(PropertyItem::toString(v).toString()); if (hasExpression()) string += QString::fromLatin1(" ( %1 )").arg(QString::fromStdString(getExpressionString())); return QVariant(string); } // -------------------------------------------------------------------- PROPERTYITEM_SOURCE(Gui::PropertyEditor::PropertyIntegerConstraintItem) PropertyIntegerConstraintItem::PropertyIntegerConstraintItem() { } QVariant PropertyIntegerConstraintItem::value(const App::Property* prop) const { assert(prop && prop->getTypeId().isDerivedFrom(App::PropertyIntegerConstraint::getClassTypeId())); int value = (int)static_cast(prop)->getValue(); return QVariant(value); } void PropertyIntegerConstraintItem::setValue(const QVariant& value) { //if the item has an expression it issues the python code if (!hasExpression()) { if (!value.canConvert(QVariant::Int)) return; int val = value.toInt(); QString data = QString::fromLatin1("%1").arg(val); setPropertyValue(data); } } QWidget* PropertyIntegerConstraintItem::createEditor(QWidget* parent, const QObject* receiver, const char* method) const { Gui::IntSpinBox *sb = new Gui::IntSpinBox(parent); sb->setFrame(false); sb->setReadOnly(isReadOnly()); QObject::connect(sb, SIGNAL(valueChanged(int)), receiver, method); if (isBound()) { sb->bind(getPath()); sb->setAutoApply(autoApply()); } return sb; } void PropertyIntegerConstraintItem::setEditorData(QWidget *editor, const QVariant& data) const { const App::PropertyIntegerConstraint* prop = static_cast (getFirstProperty()); const App::PropertyIntegerConstraint::Constraints* c = nullptr; if (prop) { c = prop->getConstraints(); } QSpinBox *sb = qobject_cast(editor); if (c) { sb->setMinimum(c->LowerBound); sb->setMaximum(c->UpperBound); sb->setSingleStep(c->StepSize); } else { sb->setMinimum(INT_MIN); sb->setMaximum(INT_MAX); } sb->setValue(data.toInt()); } QVariant PropertyIntegerConstraintItem::editorData(QWidget *editor) const { QSpinBox *sb = qobject_cast(editor); return QVariant(sb->value()); } QVariant PropertyIntegerConstraintItem::toString(const QVariant& v) const { QString string(PropertyItem::toString(v).toString()); if (hasExpression()) string += QString::fromLatin1(" ( %1 )").arg(QString::fromStdString(getExpressionString())); return QVariant(string); } // -------------------------------------------------------------------- PROPERTYITEM_SOURCE(Gui::PropertyEditor::PropertyFloatItem) PropertyFloatItem::PropertyFloatItem() { } QVariant PropertyFloatItem::toString(const QVariant& prop) const { double value = prop.toDouble(); QString data = QLocale().toString(value, 'f', decimals()); if (hasExpression()) data += QString::fromLatin1(" ( %1 )").arg(QString::fromStdString(getExpressionString())); return QVariant(data); } QVariant PropertyFloatItem::value(const App::Property* prop) const { assert(prop && prop->getTypeId().isDerivedFrom(App::PropertyFloat::getClassTypeId())); double value = static_cast(prop)->getValue(); return QVariant(value); } void PropertyFloatItem::setValue(const QVariant& value) { //if the item has an expression it issues the python code if (!hasExpression()) { if (!value.canConvert(QVariant::Double)) return; double val = value.toDouble(); QString data = QString::fromLatin1("%1").arg(val,0,'f',decimals()); setPropertyValue(data); } } QWidget* PropertyFloatItem::createEditor(QWidget* parent, const QObject* receiver, const char* method) const { Gui::DoubleSpinBox *sb = new Gui::DoubleSpinBox(parent); sb->setFrame(false); sb->setDecimals(decimals()); sb->setReadOnly(isReadOnly()); QObject::connect(sb, SIGNAL(valueChanged(double)), receiver, method); if (isBound()) { sb->bind(getPath()); sb->setAutoApply(autoApply()); } return sb; } void PropertyFloatItem::setEditorData(QWidget *editor, const QVariant& data) const { QDoubleSpinBox *sb = qobject_cast(editor); sb->setRange((double)INT_MIN, (double)INT_MAX); sb->setValue(data.toDouble()); } QVariant PropertyFloatItem::editorData(QWidget *editor) const { QDoubleSpinBox *sb = qobject_cast(editor); return QVariant(sb->value()); } // -------------------------------------------------------------------- PROPERTYITEM_SOURCE(Gui::PropertyEditor::PropertyUnitItem) PropertyUnitItem::PropertyUnitItem() { } QVariant PropertyUnitItem::toString(const QVariant& prop) const { const Base::Quantity& unit = prop.value(); QString string = unit.getUserString(); if (hasExpression()) string += QString::fromLatin1(" ( %1 )").arg(QString::fromStdString(getExpressionString())); return QVariant(string); } QVariant PropertyUnitItem::value(const App::Property* prop) const { assert(prop && prop->getTypeId().isDerivedFrom(App::PropertyQuantity::getClassTypeId())); Base::Quantity value = static_cast(prop)->getQuantityValue(); return QVariant::fromValue(value); } void PropertyUnitItem::setValue(const QVariant& value) { //if the item has an expression it handles the python code if (!hasExpression()) { if (!value.canConvert()) return; const Base::Quantity& val = value.value(); Base::QuantityFormat format(Base::QuantityFormat::Fixed, decimals()); QString unit = Base::UnitsApi::toString(val, format); setPropertyValue(unit); } } QWidget* PropertyUnitItem::createEditor(QWidget* parent, const QObject* receiver, const char* method) const { Gui::QuantitySpinBox *infield = new Gui::QuantitySpinBox(parent); infield->setFrame(false); infield->setMinimumHeight(0); infield->setReadOnly(isReadOnly()); //if we are bound to an expression we need to bind it to the input field if (isBound()) { infield->bind(getPath()); infield->setAutoApply(autoApply()); } QObject::connect(infield, SIGNAL(valueChanged(double)), receiver, method); return infield; } void PropertyUnitItem::setEditorData(QWidget *editor, const QVariant& data) const { const Base::Quantity& value = data.value(); Gui::QuantitySpinBox *infield = qobject_cast(editor); infield->setValue(value); infield->selectAll(); } QVariant PropertyUnitItem::editorData(QWidget *editor) const { Gui::QuantitySpinBox *infield = qobject_cast(editor); Base::Quantity value = infield->value(); return QVariant::fromValue(value); } // -------------------------------------------------------------------- PROPERTYITEM_SOURCE(Gui::PropertyEditor::PropertyUnitConstraintItem) PropertyUnitConstraintItem::PropertyUnitConstraintItem() { } void PropertyUnitConstraintItem::setEditorData(QWidget *editor, const QVariant& data) const { const Base::Quantity& value = data.value(); Gui::QuantitySpinBox *infield = qobject_cast(editor); infield->setValue(value); infield->selectAll(); const App::PropertyQuantityConstraint* prop = static_cast (getFirstProperty()); const App::PropertyQuantityConstraint::Constraints* c = nullptr; if (prop) { c = prop->getConstraints(); } if (c) { infield->setMinimum(c->LowerBound); infield->setMaximum(c->UpperBound); infield->setSingleStep(c->StepSize); } else { infield->setMinimum((double)INT_MIN); infield->setMaximum((double)INT_MAX); } } // -------------------------------------------------------------------- PROPERTYITEM_SOURCE(Gui::PropertyEditor::PropertyFloatConstraintItem) PropertyFloatConstraintItem::PropertyFloatConstraintItem() { } QVariant PropertyFloatConstraintItem::toString(const QVariant& prop) const { double value = prop.toDouble(); QString data = QLocale().toString(value, 'f', decimals()); return QVariant(data); } QVariant PropertyFloatConstraintItem::value(const App::Property* prop) const { assert(prop && prop->getTypeId().isDerivedFrom(App::PropertyFloatConstraint::getClassTypeId())); double value = static_cast(prop)->getValue(); return QVariant(value); } void PropertyFloatConstraintItem::setValue(const QVariant& value) { //if the item has an expression it issues the python code if (!hasExpression()) { if (!value.canConvert(QVariant::Double)) return; double val = value.toDouble(); QString data = QString::fromLatin1("%1").arg(val,0,'f',decimals()); setPropertyValue(data); } } QWidget* PropertyFloatConstraintItem::createEditor(QWidget* parent, const QObject* receiver, const char* method) const { Gui::DoubleSpinBox *sb = new Gui::DoubleSpinBox(parent); sb->setDecimals(decimals()); sb->setFrame(false); sb->setReadOnly(isReadOnly()); QObject::connect(sb, SIGNAL(valueChanged(double)), receiver, method); if (isBound()) { sb->bind(getPath()); sb->setAutoApply(autoApply()); } return sb; } void PropertyFloatConstraintItem::setEditorData(QWidget *editor, const QVariant& data) const { const App::PropertyFloatConstraint* prop = static_cast (getFirstProperty()); const App::PropertyFloatConstraint::Constraints* c = nullptr; if (prop) { c = prop->getConstraints(); } QDoubleSpinBox *sb = qobject_cast(editor); if (c) { sb->setMinimum(c->LowerBound); sb->setMaximum(c->UpperBound); sb->setSingleStep(c->StepSize); } else { sb->setMinimum((double)INT_MIN); sb->setMaximum((double)INT_MAX); sb->setSingleStep(0.1); } sb->setValue(data.toDouble()); } QVariant PropertyFloatConstraintItem::editorData(QWidget *editor) const { QDoubleSpinBox *sb = qobject_cast(editor); return QVariant(sb->value()); } // -------------------------------------------------------------------- PROPERTYITEM_SOURCE(Gui::PropertyEditor::PropertyPrecisionItem) PropertyPrecisionItem::PropertyPrecisionItem() { setDecimals(16); } // -------------------------------------------------------------------- PROPERTYITEM_SOURCE(Gui::PropertyEditor::PropertyAngleItem) PropertyAngleItem::PropertyAngleItem() { } void PropertyAngleItem::setEditorData(QWidget *editor, const QVariant& data) const { PropertyUnitConstraintItem::setEditorData(editor, data); } QVariant PropertyAngleItem::toString(const QVariant& prop) const { return PropertyUnitConstraintItem::toString(prop); } // -------------------------------------------------------------------- PROPERTYITEM_SOURCE(Gui::PropertyEditor::PropertyBoolItem) PropertyBoolItem::PropertyBoolItem() { } QVariant PropertyBoolItem::value(const App::Property* prop) const { assert(prop && prop->getTypeId().isDerivedFrom(App::PropertyBool::getClassTypeId())); bool value = ((App::PropertyBool*)prop)->getValue(); return QVariant(value); } void PropertyBoolItem::setValue(const QVariant& value) { if (hasExpression() || !value.canConvert(QVariant::Bool)) return; bool val = value.toBool(); QString data = (val ? QLatin1String("True") : QLatin1String("False")); setPropertyValue(data); } QWidget* PropertyBoolItem::createEditor(QWidget* parent, const QObject* receiver, const char* method) const { QComboBox *cb = new QComboBox(parent); cb->setFrame(false); cb->addItem(QLatin1String("false")); cb->addItem(QLatin1String("true")); cb->setDisabled(isReadOnly()); QObject::connect(cb, SIGNAL(activated(int)), receiver, method); return cb; } void PropertyBoolItem::setEditorData(QWidget *editor, const QVariant& data) const { QComboBox *cb = qobject_cast(editor); cb->setCurrentIndex(cb->findText(data.toString())); } QVariant PropertyBoolItem::editorData(QWidget *editor) const { QComboBox *cb = qobject_cast(editor); return QVariant(cb->currentText()); } // --------------------------------------------------------------- namespace Gui { namespace PropertyEditor { class VectorLineEdit : public Gui::ExpLineEdit { int decimals; public: VectorLineEdit (int decimals, QWidget * parent=nullptr, bool expressionOnly=false) : Gui::ExpLineEdit(parent, expressionOnly) , decimals(decimals) { } bool apply(const std::string &propName) { if (!ExpressionBinding::apply(propName)) { QVariant data = property("coords"); if (data.canConvert()) { const Base::Vector3d& value = data.value(); QString str = QString::fromLatin1("(%1, %2, %3)") .arg(value.x,0,'f',decimals) .arg(value.y,0,'f',decimals) .arg(value.z,0,'f',decimals); Gui::Command::doCommand(Gui::Command::Doc, "%s = %s", propName.c_str(), str.toLatin1().constData()); return true; } } return false; } }; }} // --------------------------------------------------------------- PROPERTYITEM_SOURCE(Gui::PropertyEditor::PropertyVectorItem) PropertyVectorItem::PropertyVectorItem() { m_x = static_cast(PropertyFloatItem::create()); m_x->setParent(this); m_x->setPropertyName(QLatin1String("x")); this->appendChild(m_x); m_y = static_cast(PropertyFloatItem::create()); m_y->setParent(this); m_y->setPropertyName(QLatin1String("y")); this->appendChild(m_y); m_z = static_cast(PropertyFloatItem::create()); m_z->setParent(this); m_z->setPropertyName(QLatin1String("z")); this->appendChild(m_z); } QVariant PropertyVectorItem::toString(const QVariant& prop) const { QLocale loc; const Base::Vector3d& value = prop.value(); QString data = QString::fromLatin1("[%1 %2 %3]") .arg(loc.toString(value.x, 'f', 2), loc.toString(value.y, 'f', 2), loc.toString(value.z, 'f', 2)); if (hasExpression()) data += QString::fromLatin1(" ( %1 )").arg(QString::fromStdString(getExpressionString())); return QVariant(data); } QVariant PropertyVectorItem::value(const App::Property* prop) const { assert(prop && prop->getTypeId().isDerivedFrom(App::PropertyVector::getClassTypeId())); const Base::Vector3d& value = static_cast(prop)->getValue(); return QVariant::fromValue(value); } void PropertyVectorItem::setValue(const QVariant& value) { if (hasExpression() || !value.canConvert()) return; const Base::Vector3d& val = value.value(); QString data = QString::fromLatin1("(%1, %2, %3)") .arg(val.x,0,'f',decimals()) .arg(val.y,0,'f',decimals()) .arg(val.z,0,'f',decimals()); setPropertyValue(data); } QWidget* PropertyVectorItem::createEditor(QWidget* parent, const QObject* /*receiver*/, const char* /*method*/) const { VectorLineEdit *le = new VectorLineEdit(decimals(), parent); le->setFrame(false); le->setReadOnly(true); if (isBound()) { le->bind(getPath()); le->setAutoApply(autoApply()); } return le; } void PropertyVectorItem::setEditorData(QWidget *editor, const QVariant& data) const { QLocale loc; QLineEdit* le = qobject_cast(editor); const Base::Vector3d& value = data.value(); QString text = QString::fromLatin1("[%1 %2 %3]") .arg(loc.toString(value.x, 'f', 2), loc.toString(value.y, 'f', 2), loc.toString(value.z, 'f', 2)); le->setProperty("coords", data); le->setText(text); } QVariant PropertyVectorItem::editorData(QWidget *editor) const { QLineEdit *le = qobject_cast(editor); return QVariant(le->text()); } double PropertyVectorItem::x() const { return data(1,Qt::EditRole).value().x; } void PropertyVectorItem::setX(double x) { setData(QVariant::fromValue(Base::Vector3d(x, y(), z()))); } double PropertyVectorItem::y() const { return data(1,Qt::EditRole).value().y; } void PropertyVectorItem::setY(double y) { setData(QVariant::fromValue(Base::Vector3d(x(), y, z()))); } double PropertyVectorItem::z() const { return data(1,Qt::EditRole).value().z; } void PropertyVectorItem::setZ(double z) { setData(QVariant::fromValue(Base::Vector3d(x(), y(), z))); } void PropertyVectorItem::propertyBound() { m_x->bind(App::ObjectIdentifier(getPath())<bind(App::ObjectIdentifier(getPath())<bind(App::ObjectIdentifier(getPath())<setMargin(0); layout->setSpacing(2); lineEdit = new QLineEdit(this); lineEdit->setReadOnly(true); layout->addWidget(lineEdit); button = new QPushButton(QLatin1String("..."), this); #if defined (Q_OS_MAC) button->setAttribute(Qt::WA_LayoutUsesWidgetRect); // layout size from QMacStyle was not correct #endif layout->addWidget(button); connect(button, SIGNAL(clicked()), this, SIGNAL(buttonClick())); // QAbstractItemView will call selectAll() if a QLineEdit is the focus // proxy. Since the QLineEdit here is read-only and not meant for editing, // do not set it as focus proxy. Otherwise, the text won't even shown for // most stylesheets (which contain a trick to hide the content of a selected // read-only/disabled editor widgets). // // setFocusProxy(lineEdit); } PropertyEditorWidget::~PropertyEditorWidget() { } void PropertyEditorWidget::resizeEvent(QResizeEvent* e) { button->setFixedWidth(e->size().height()); button->setFixedHeight(e->size().height()); } void PropertyEditorWidget::showValue(const QVariant &d) { lineEdit->setText(d.toString()); } QVariant PropertyEditorWidget::value() const { return variant; } void PropertyEditorWidget::setValue(const QVariant& val) { variant = val; showValue(variant); valueChanged(variant); } // --------------------------------------------------------------- VectorListWidget::VectorListWidget(int decimals, QWidget *parent) : PropertyEditorWidget(parent) , decimals(decimals) { connect(button, SIGNAL(clicked()), this, SLOT(buttonClicked())); } void VectorListWidget::buttonClicked() { VectorListEditor dlg(decimals, this); dlg.setValues(value().value>()); QPoint p(0, 0); p = this->mapToGlobal(p); dlg.move(p); if (dlg.exec() == QDialog::Accepted) { QVariant data = QVariant::fromValue>(dlg.getValues()); setValue(data); } } void VectorListWidget::showValue(const QVariant& d) { QLocale loc; QString data; const QList& value = d.value>(); if (value.isEmpty()) { data = QString::fromLatin1("[]"); } else { data = QString::fromLatin1("[%1 %2 %3], ...") .arg(loc.toString(value[0].x, 'f', 2), loc.toString(value[0].y, 'f', 2), loc.toString(value[0].z, 'f', 2)); } lineEdit->setText(data); } // --------------------------------------------------------------- PROPERTYITEM_SOURCE(Gui::PropertyEditor::PropertyVectorListItem) PropertyVectorListItem::PropertyVectorListItem() { } QVariant PropertyVectorListItem::toString(const QVariant& prop) const { QLocale loc; QString data; const QList& value = prop.value>(); if (value.isEmpty()) { data = QString::fromLatin1("[]"); } else { data = QString::fromLatin1("[%1 %2 %3], ...") .arg(loc.toString(value[0].x, 'f', 2), loc.toString(value[0].y, 'f', 2), loc.toString(value[0].z, 'f', 2)); } if (hasExpression()) data += QString::fromLatin1(" ( %1 )").arg(QString::fromStdString(getExpressionString())); return QVariant(data); } QVariant PropertyVectorListItem::value(const App::Property* prop) const { assert(prop && prop->getTypeId().isDerivedFrom(App::PropertyVectorList::getClassTypeId())); const std::vector& value = static_cast(prop)->getValue(); QList list; std::copy(value.begin(), value.end(), std::back_inserter(list)); return QVariant::fromValue>(list); } void PropertyVectorListItem::setValue(const QVariant& value) { if (!value.canConvert>()) return; const QList& val = value.value>(); QString data; QTextStream str(&data); str << "["; for (const auto& it : val) { str << QString::fromLatin1("(%1, %2, %3), ") .arg(it.x,0,'f',decimals()) .arg(it.y,0,'f',decimals()) .arg(it.z,0,'f',decimals()); } str << "]"; setPropertyValue(data); } QWidget* PropertyVectorListItem::createEditor(QWidget* parent, const QObject* receiver, const char* method) const { VectorListWidget *pe = new VectorListWidget(decimals(), parent); QObject::connect(pe, SIGNAL(valueChanged(const QVariant &)), receiver, method); pe->setDisabled(isReadOnly()); return pe; } void PropertyVectorListItem::setEditorData(QWidget *editor, const QVariant& data) const { VectorListWidget *pe = qobject_cast(editor); pe->setValue(data); } QVariant PropertyVectorListItem::editorData(QWidget *editor) const { VectorListWidget *pe = qobject_cast(editor); return pe->value(); } // --------------------------------------------------------------- PROPERTYITEM_SOURCE(Gui::PropertyEditor::PropertyVectorDistanceItem) PropertyVectorDistanceItem::PropertyVectorDistanceItem() { m_x = static_cast(PropertyUnitItem::create()); m_x->setParent(this); m_x->setPropertyName(QLatin1String("x")); this->appendChild(m_x); m_y = static_cast(PropertyUnitItem::create()); m_y->setParent(this); m_y->setPropertyName(QLatin1String("y")); this->appendChild(m_y); m_z = static_cast(PropertyUnitItem::create()); m_z->setParent(this); m_z->setPropertyName(QLatin1String("z")); this->appendChild(m_z); } QVariant PropertyVectorDistanceItem::toString(const QVariant& prop) const { const Base::Vector3d& value = prop.value(); QString data = QString::fromLatin1("[") + Base::Quantity(value.x, Base::Unit::Length).getUserString() + QString::fromLatin1(" ") + Base::Quantity(value.y, Base::Unit::Length).getUserString() + QString::fromLatin1(" ") + Base::Quantity(value.z, Base::Unit::Length).getUserString() + QString::fromLatin1("]"); if (hasExpression()) data += QString::fromLatin1(" ( %1 )").arg(QString::fromStdString(getExpressionString())); return QVariant(data); } QVariant PropertyVectorDistanceItem::value(const App::Property* prop) const { assert(prop && prop->getTypeId().isDerivedFrom(App::PropertyVector::getClassTypeId())); const Base::Vector3d& value = static_cast(prop)->getValue(); return QVariant::fromValue(value); } void PropertyVectorDistanceItem::setValue(const QVariant& variant) { if (hasExpression() || !variant.canConvert()) return; const Base::Vector3d& value = variant.value(); Base::Quantity x = Base::Quantity(value.x, Base::Unit::Length); Base::Quantity y = Base::Quantity(value.y, Base::Unit::Length); Base::Quantity z = Base::Quantity(value.z, Base::Unit::Length); Base::QuantityFormat format(Base::QuantityFormat::Fixed, decimals()); QString data = QString::fromLatin1("(%1, %2, %3)") .arg(Base::UnitsApi::toNumber(x, format)) .arg(Base::UnitsApi::toNumber(y, format)) .arg(Base::UnitsApi::toNumber(z, format)); setPropertyValue(data); } void PropertyVectorDistanceItem::setEditorData(QWidget *editor, const QVariant& data) const { QLineEdit* le = qobject_cast(editor); le->setProperty("coords", data); le->setText(toString(data).toString()); } QWidget* PropertyVectorDistanceItem::createEditor(QWidget* parent, const QObject* /*receiver*/, const char* /*method*/) const { VectorLineEdit *le = new VectorLineEdit(decimals(), parent); le->setFrame(false); le->setReadOnly(true); if (isBound()) { le->bind(getPath()); le->setAutoApply(autoApply()); } return le; } QVariant PropertyVectorDistanceItem::editorData(QWidget *editor) const { QLineEdit *le = qobject_cast(editor); return QVariant(le->text()); } Base::Quantity PropertyVectorDistanceItem::x() const { return Base::Quantity(data(1,Qt::EditRole).value().x, Base::Unit::Length); } void PropertyVectorDistanceItem::setX(Base::Quantity x) { setData(QVariant::fromValue(Base::Vector3d(x.getValue(), y().getValue(), z().getValue()))); } Base::Quantity PropertyVectorDistanceItem::y() const { return Base::Quantity(data(1,Qt::EditRole).value().y, Base::Unit::Length); } void PropertyVectorDistanceItem::setY(Base::Quantity y) { setData(QVariant::fromValue(Base::Vector3d(x().getValue(), y.getValue(), z().getValue()))); } Base::Quantity PropertyVectorDistanceItem::z() const { return Base::Quantity(data(1,Qt::EditRole).value().z, Base::Unit::Length); } void PropertyVectorDistanceItem::setZ(Base::Quantity z) { setData(QVariant::fromValue(Base::Vector3d(x().getValue(), y().getValue(), z.getValue()))); } void PropertyVectorDistanceItem::propertyBound() { if (isBound()) { m_x->bind(App::ObjectIdentifier(getPath())<bind(App::ObjectIdentifier(getPath())<bind(App::ObjectIdentifier(getPath())<(PropertyFloatItem::create()); m_a11->setParent(this); m_a11->setPropertyName(QLatin1String("A11")); m_a11->setDecimals(decimals); this->appendChild(m_a11); m_a12 = static_cast(PropertyFloatItem::create()); m_a12->setParent(this); m_a12->setPropertyName(QLatin1String("A12")); m_a12->setDecimals(decimals); this->appendChild(m_a12); m_a13 = static_cast(PropertyFloatItem::create()); m_a13->setParent(this); m_a13->setPropertyName(QLatin1String("A13")); m_a13->setDecimals(decimals); this->appendChild(m_a13); m_a14 = static_cast(PropertyFloatItem::create()); m_a14->setParent(this); m_a14->setPropertyName(QLatin1String("A14")); m_a14->setDecimals(decimals); this->appendChild(m_a14); m_a21 = static_cast(PropertyFloatItem::create()); m_a21->setParent(this); m_a21->setPropertyName(QLatin1String("A21")); m_a21->setDecimals(decimals); this->appendChild(m_a21); m_a22 = static_cast(PropertyFloatItem::create()); m_a22->setParent(this); m_a22->setPropertyName(QLatin1String("A22")); m_a22->setDecimals(decimals); this->appendChild(m_a22); m_a23 = static_cast(PropertyFloatItem::create()); m_a23->setParent(this); m_a23->setPropertyName(QLatin1String("A23")); m_a23->setDecimals(decimals); this->appendChild(m_a23); m_a24 = static_cast(PropertyFloatItem::create()); m_a24->setParent(this); m_a24->setPropertyName(QLatin1String("A24")); m_a24->setDecimals(decimals); this->appendChild(m_a24); m_a31 = static_cast(PropertyFloatItem::create()); m_a31->setParent(this); m_a31->setPropertyName(QLatin1String("A31")); m_a31->setDecimals(decimals); this->appendChild(m_a31); m_a32 = static_cast(PropertyFloatItem::create()); m_a32->setParent(this); m_a32->setPropertyName(QLatin1String("A32")); m_a32->setDecimals(decimals); this->appendChild(m_a32); m_a33 = static_cast(PropertyFloatItem::create()); m_a33->setParent(this); m_a33->setPropertyName(QLatin1String("A33")); m_a33->setDecimals(decimals); this->appendChild(m_a33); m_a34 = static_cast(PropertyFloatItem::create()); m_a34->setParent(this); m_a34->setPropertyName(QLatin1String("A34")); m_a34->setDecimals(decimals); this->appendChild(m_a34); m_a41 = static_cast(PropertyFloatItem::create()); m_a41->setParent(this); m_a41->setPropertyName(QLatin1String("A41")); m_a41->setDecimals(decimals); this->appendChild(m_a41); m_a42 = static_cast(PropertyFloatItem::create()); m_a42->setParent(this); m_a42->setPropertyName(QLatin1String("A42")); m_a42->setDecimals(decimals); this->appendChild(m_a42); m_a43 = static_cast(PropertyFloatItem::create()); m_a43->setParent(this); m_a43->setPropertyName(QLatin1String("A43")); m_a43->setDecimals(decimals); this->appendChild(m_a43); m_a44 = static_cast(PropertyFloatItem::create()); m_a44->setParent(this); m_a44->setPropertyName(QLatin1String("A44")); m_a44->setDecimals(decimals); this->appendChild(m_a44); } QVariant PropertyMatrixItem::toString(const QVariant& prop) const { QLocale loc; const Base::Matrix4D& value = prop.value(); QString text = QString::fromLatin1("[%1 %2 %3 %4 %5 %6 %7 %8 %9 %10 %11 %12 %13 %14 %15 %16]") .arg(loc.toString(value[0][0], 'f', 2), //(unsigned short usNdx) loc.toString(value[0][1], 'f', 2), loc.toString(value[0][2], 'f', 2), loc.toString(value[0][3], 'f', 2), loc.toString(value[1][0], 'f', 2), loc.toString(value[1][1], 'f', 2), loc.toString(value[1][2], 'f', 2), loc.toString(value[1][3], 'f', 2), loc.toString(value[2][0], 'f', 2)) .arg(loc.toString(value[2][1], 'f', 2), loc.toString(value[2][2], 'f', 2), loc.toString(value[2][3], 'f', 2), loc.toString(value[3][0], 'f', 2), loc.toString(value[3][1], 'f', 2), loc.toString(value[3][2], 'f', 2), loc.toString(value[3][3], 'f', 2)); return QVariant(text); } QVariant PropertyMatrixItem::value(const App::Property* prop) const { assert(prop && prop->getTypeId().isDerivedFrom(App::PropertyMatrix::getClassTypeId())); const Base::Matrix4D& value = static_cast(prop)->getValue(); return QVariant::fromValue(value); } QVariant PropertyMatrixItem::toolTip(const App::Property* prop) const { assert(prop && prop->getTypeId().isDerivedFrom(App::PropertyMatrix::getClassTypeId())); const Base::Matrix4D& value = static_cast(prop)->getValue(); return QVariant(QString::fromStdString(value.analyse())); } void PropertyMatrixItem::setValue(const QVariant& value) { if (hasExpression() || !value.canConvert()) return; const Base::Matrix4D& val = value.value(); const int decimals=16; QString data = QString::fromLatin1("FreeCAD.Matrix(%1, %2, %3, %4, %5, %6, %7, %8, %9, %10, %11, %12, %13, %14, %15, %16)") .arg(val[0][0],0, 'f', decimals) .arg(val[0][1],0, 'f', decimals) .arg(val[0][2],0, 'f', decimals) .arg(val[0][3],0, 'f', decimals) .arg(val[1][0],0, 'f', decimals) .arg(val[1][1],0, 'f', decimals) .arg(val[1][2],0, 'f', decimals) .arg(val[1][3],0, 'f', decimals) .arg(val[2][0],0, 'f', decimals) .arg(val[2][1],0, 'f', decimals) .arg(val[2][2],0, 'f', decimals) .arg(val[2][3],0, 'f', decimals) .arg(val[3][0],0, 'f', decimals) .arg(val[3][1],0, 'f', decimals) .arg(val[3][2],0, 'f', decimals) .arg(val[3][3],0, 'f', decimals); setPropertyValue(data); } QWidget* PropertyMatrixItem::createEditor(QWidget* parent, const QObject* /*receiver*/, const char* /*method*/) const { QLineEdit *le = new QLineEdit(parent); le->setFrame(false); le->setReadOnly(true); return le; } void PropertyMatrixItem::setEditorData(QWidget *editor, const QVariant& data) const { QLocale loc; QLineEdit* le = qobject_cast(editor); const Base::Matrix4D& value = data.value(); QString text = QString::fromLatin1("[%1 %2 %3 %4 %5 %6 %7 %8 %9 %10 %11 %12 %13 %14 %15 %16]") .arg(loc.toString(value[0][0], 'f', 2), //(unsigned short usNdx) loc.toString(value[0][1], 'f', 2), loc.toString(value[0][2], 'f', 2), loc.toString(value[0][3], 'f', 2), loc.toString(value[1][0], 'f', 2), loc.toString(value[1][1], 'f', 2), loc.toString(value[1][2], 'f', 2), loc.toString(value[1][3], 'f', 2), loc.toString(value[2][0], 'f', 2)) .arg(loc.toString(value[2][1], 'f', 2), loc.toString(value[2][2], 'f', 2), loc.toString(value[2][3], 'f', 2), loc.toString(value[3][0], 'f', 2), loc.toString(value[3][1], 'f', 2), loc.toString(value[3][2], 'f', 2), loc.toString(value[3][3], 'f', 2)); le->setText(text); } QVariant PropertyMatrixItem::editorData(QWidget *editor) const { QLineEdit *le = qobject_cast(editor); return QVariant(le->text()); } double PropertyMatrixItem::getA11() const { return data(1,Qt::EditRole).value()[0][0]; } void PropertyMatrixItem::setA11(double A11) { setData(QVariant::fromValue(Base::Matrix4D(A11,getA12(),getA13(),getA14(),getA21(),getA22(),getA23(),getA24(),getA31(),getA32(),getA33(),getA34(),getA41(),getA42(),getA43(),getA44() ))); } double PropertyMatrixItem::getA12() const { return data(1,Qt::EditRole).value()[0][1]; } void PropertyMatrixItem::setA12(double A12) { setData(QVariant::fromValue(Base::Matrix4D(getA11(),A12,getA13(),getA14(),getA21(),getA22(),getA23(),getA24(),getA31(),getA32(),getA33(),getA34(),getA41(),getA42(),getA43(),getA44() ))); } double PropertyMatrixItem::getA13() const { return data(1,Qt::EditRole).value()[0][2]; } void PropertyMatrixItem::setA13(double A13) { setData(QVariant::fromValue(Base::Matrix4D(getA11(),getA12(),A13,getA14(),getA21(),getA22(),getA23(),getA24(),getA31(),getA32(),getA33(),getA34(),getA41(),getA42(),getA43(),getA44() ))); } double PropertyMatrixItem::getA14() const { return data(1,Qt::EditRole).value()[0][3]; } void PropertyMatrixItem::setA14(double A14) { setData(QVariant::fromValue(Base::Matrix4D(getA11(),getA12(),getA13(),A14,getA21(),getA22(),getA23(),getA24(),getA31(),getA32(),getA33(),getA34(),getA41(),getA42(),getA43(),getA44() ))); } double PropertyMatrixItem::getA21() const { return data(1,Qt::EditRole).value()[1][0]; } void PropertyMatrixItem::setA21(double A21) { setData(QVariant::fromValue(Base::Matrix4D(getA11(),getA12(),getA13(),getA14(),A21,getA22(),getA23(),getA24(),getA31(),getA32(),getA33(),getA34(),getA41(),getA42(),getA43(),getA44() ))); } double PropertyMatrixItem::getA22() const { return data(1,Qt::EditRole).value()[1][1]; } void PropertyMatrixItem::setA22(double A22) { setData(QVariant::fromValue(Base::Matrix4D(getA11(),getA12(),getA13(),getA14(),getA21(),A22,getA23(),getA24(),getA31(),getA32(),getA33(),getA34(),getA41(),getA42(),getA43(),getA44() ))); } double PropertyMatrixItem::getA23() const { return data(1,Qt::EditRole).value()[1][2]; } void PropertyMatrixItem::setA23(double A23) { setData(QVariant::fromValue(Base::Matrix4D(getA11(),getA12(),getA13(),getA14(),getA21(),getA22(),A23,getA24(),getA31(),getA32(),getA33(),getA34(),getA41(),getA42(),getA43(),getA44() ))); } double PropertyMatrixItem::getA24() const { return data(1,Qt::EditRole).value()[1][3]; } void PropertyMatrixItem::setA24(double A24) { setData(QVariant::fromValue(Base::Matrix4D(getA11(),getA12(),getA13(),getA14(),getA21(),getA22(),getA23(),A24,getA31(),getA32(),getA33(),getA34(),getA41(),getA42(),getA43(),getA44() ))); } double PropertyMatrixItem::getA31() const { return data(1,Qt::EditRole).value()[2][0]; } void PropertyMatrixItem::setA31(double A31) { setData(QVariant::fromValue(Base::Matrix4D(getA11(),getA12(),getA13(),getA14(),getA21(),getA22(),getA23(),getA24(),A31,getA32(),getA33(),getA34(),getA41(),getA42(),getA43(),getA44() ))); } double PropertyMatrixItem::getA32() const { return data(1,Qt::EditRole).value()[2][1]; } void PropertyMatrixItem::setA32(double A32) { setData(QVariant::fromValue(Base::Matrix4D(getA11(),getA12(),getA13(),getA14(),getA21(),getA22(),getA23(),getA24(),getA31(),A32,getA33(),getA34(),getA41(),getA42(),getA43(),getA44() ))); } double PropertyMatrixItem::getA33() const { return data(1,Qt::EditRole).value()[2][2]; } void PropertyMatrixItem::setA33(double A33) { setData(QVariant::fromValue(Base::Matrix4D(getA11(),getA12(),getA13(),getA14(),getA21(),getA22(),getA23(),getA24(),getA31(),getA32(),A33,getA34(),getA41(),getA42(),getA43(),getA44() ))); } double PropertyMatrixItem::getA34() const { return data(1,Qt::EditRole).value()[2][3]; } void PropertyMatrixItem::setA34(double A34) { setData(QVariant::fromValue(Base::Matrix4D(getA11(),getA12(),getA13(),getA14(),getA21(),getA22(),getA23(),getA24(),getA31(),getA32(),getA33(),A34,getA41(),getA42(),getA43(),getA44() ))); } double PropertyMatrixItem::getA41() const { return data(1,Qt::EditRole).value()[3][0]; } void PropertyMatrixItem::setA41(double A41) { setData(QVariant::fromValue(Base::Matrix4D(getA11(),getA12(),getA13(),getA14(),getA21(),getA22(),getA23(),getA24(),getA31(),getA32(),getA33(),getA34(),A41,getA42(),getA43(),getA44() ))); } double PropertyMatrixItem::getA42() const { return data(1,Qt::EditRole).value()[3][1]; } void PropertyMatrixItem::setA42(double A42) { setData(QVariant::fromValue(Base::Matrix4D(getA11(),getA12(),getA13(),getA14(),getA21(),getA22(),getA23(),getA24(),getA31(),getA32(),getA33(),getA34(),getA41(),A42,getA43(),getA44() ))); } double PropertyMatrixItem::getA43() const { return data(1,Qt::EditRole).value()[3][2]; } void PropertyMatrixItem::setA43(double A43) { setData(QVariant::fromValue(Base::Matrix4D(getA11(),getA12(),getA13(),getA14(),getA21(),getA22(),getA23(),getA24(),getA31(),getA32(),getA33(),getA34(),getA41(),getA42(),A43,getA44() ))); } double PropertyMatrixItem::getA44() const { return data(1,Qt::EditRole).value()[3][3]; } void PropertyMatrixItem::setA44(double A44) { setData(QVariant::fromValue(Base::Matrix4D(getA11(),getA12(),getA13(),getA14(),getA21(),getA22(),getA23(),getA24(),getA31(),getA32(),getA33(),getA34(),getA41(),getA42(),getA43(),A44 ))); } // --------------------------------------------------------------- RotationHelper::RotationHelper() : init_axis(false) , changed_value(false) , rot_angle(0) , rot_axis(0,0,1) { } void RotationHelper::setChanged(bool value) { changed_value = value; } bool RotationHelper::hasChangedAndReset() { if (!changed_value) return false; changed_value = false; return true; } bool RotationHelper::isAxisInitialized() const { return init_axis; } void RotationHelper::setValue(const Base::Vector3d& axis, double angle) { rot_axis = axis; rot_angle = angle; init_axis = true; } void RotationHelper::getValue(Base::Vector3d& axis, double& angle) const { axis = rot_axis; angle = rot_angle; } double RotationHelper::getAngle(const Base::Rotation& val) const { double angle; Base::Vector3d dir; val.getRawValue(dir, angle); if (dir * this->rot_axis < 0.0) angle = -angle; return angle; } Base::Rotation RotationHelper::setAngle(double angle) { Base::Rotation rot; rot.setValue(this->rot_axis, Base::toRadians(angle)); changed_value = true; rot_angle = angle; return rot; } Base::Vector3d RotationHelper::getAxis() const { // We must store the rotation axis in a member because // if we read the value from the property we would always // get a normalized vector which makes it quite unhandy // to work with return this->rot_axis; } Base::Rotation RotationHelper::setAxis(const Base::Rotation& value, const Base::Vector3d& axis) { this->rot_axis = axis; Base::Rotation rot = value; Base::Vector3d dummy; double angle; rot.getValue(dummy, angle); if (dummy * axis < 0.0) angle = -angle; rot.setValue(axis, angle); changed_value = true; return rot; } void RotationHelper::assignProperty(const Base::Rotation& value, double eps) { double angle; Base::Vector3d dir; value.getRawValue(dir, angle); Base::Vector3d cross = this->rot_axis.Cross(dir); double len2 = cross.Sqr(); if (angle != 0) { // vectors are not parallel if (len2 > eps) this->rot_axis = dir; // vectors point into opposite directions else if (this->rot_axis.Dot(dir) < 0) this->rot_axis = -this->rot_axis; } this->rot_angle = Base::toDegrees(angle); } // --------------------------------------------------------------- PROPERTYITEM_SOURCE(Gui::PropertyEditor::PropertyRotationItem) PropertyRotationItem::PropertyRotationItem() { m_a = static_cast(PropertyUnitItem::create()); m_a->setParent(this); m_a->setPropertyName(QLatin1String(QT_TRANSLATE_NOOP("App::Property", "Angle"))); this->appendChild(m_a); m_d = static_cast(PropertyVectorItem::create()); m_d->setParent(this); m_d->setPropertyName(QLatin1String(QT_TRANSLATE_NOOP("App::Property", "Axis"))); m_d->setReadOnly(true); this->appendChild(m_d); } PropertyRotationItem::~PropertyRotationItem() { } Base::Quantity PropertyRotationItem::getAngle() const { QVariant value = data(1, Qt::EditRole); if (!value.canConvert()) return Base::Quantity(0.0); const Base::Rotation& val = value.value(); double angle = h.getAngle(val); return Base::Quantity(Base::toDegrees(angle), Base::Unit::Angle); } void PropertyRotationItem::setAngle(Base::Quantity angle) { QVariant value = data(1, Qt::EditRole); if (!value.canConvert()) return; Base::Rotation rot = h.setAngle(angle.getValue()); setValue(QVariant::fromValue(rot)); } Base::Vector3d PropertyRotationItem::getAxis() const { return h.getAxis(); } void PropertyRotationItem::setAxis(const Base::Vector3d& axis) { QVariant value = data(1, Qt::EditRole); if (!value.canConvert()) return; Base::Rotation rot = value.value(); rot = h.setAxis(rot, axis); setValue(QVariant::fromValue(rot)); } void PropertyRotationItem::assignProperty(const App::Property* prop) { // Choose an adaptive epsilon to avoid changing the axis when they are considered to // be equal. See https://forum.freecadweb.org/viewtopic.php?f=10&t=24662&start=10 double eps = std::pow(10.0, -2*(decimals()+1)); if (prop->getTypeId().isDerivedFrom(App::PropertyRotation::getClassTypeId())) { const Base::Rotation& value = static_cast(prop)->getValue(); h.assignProperty(value, eps); } } QVariant PropertyRotationItem::value(const App::Property* prop) const { assert(prop && prop->getTypeId().isDerivedFrom(App::PropertyRotation::getClassTypeId())); const Base::Rotation& value = static_cast(prop)->getValue(); double angle; Base::Vector3d dir; value.getRawValue(dir, angle); if (!h.isAxisInitialized()) { if (m_a->hasExpression()) { QString str = m_a->expressionAsString(); angle = str.toDouble(); } else { angle = Base::toDegrees(angle); } PropertyItem* x = m_d->child(0); PropertyItem* y = m_d->child(1); PropertyItem* z = m_d->child(2); if (x->hasExpression()) { QString str = x->expressionAsString(); dir.x = str.toDouble(); } if (y->hasExpression()) { QString str = y->expressionAsString(); dir.y = str.toDouble(); } if (z->hasExpression()) { QString str = z->expressionAsString(); dir.z = str.toDouble(); } h.setValue(dir, angle); } return QVariant::fromValue(value); } QVariant PropertyRotationItem::toolTip(const App::Property* prop) const { assert(prop && prop->getTypeId().isDerivedFrom(App::PropertyRotation::getClassTypeId())); const Base::Rotation& p = static_cast(prop)->getValue(); double angle; Base::Vector3d dir; p.getRawValue(dir, angle); angle = Base::toDegrees(angle); QLocale loc; QString data = QString::fromUtf8("Axis: (%1 %2 %3)\n" "Angle: %4") .arg(loc.toString(dir.x,'f',decimals()), loc.toString(dir.y,'f',decimals()), loc.toString(dir.z,'f',decimals()), Base::Quantity(angle, Base::Unit::Angle).getUserString()); return QVariant(data); } QVariant PropertyRotationItem::toString(const QVariant& prop) const { const Base::Rotation& p = prop.value(); double angle; Base::Vector3d dir; p.getRawValue(dir, angle); angle = Base::toDegrees(angle); QLocale loc; QString data = QString::fromUtf8("[(%1 %2 %3); %4]") .arg(loc.toString(dir.x,'f',2), loc.toString(dir.y,'f',2), loc.toString(dir.z,'f',2), Base::Quantity(angle, Base::Unit::Angle).getUserString()); return QVariant(data); } void PropertyRotationItem::setValue(const QVariant& value) { if (!value.canConvert()) return; // Accept this only if the user changed the axis, angle or position but // not if >this< item loses focus if (!h.hasChangedAndReset()) return; Base::Vector3d axis; double angle; h.getValue(axis, angle); Base::QuantityFormat format(Base::QuantityFormat::Fixed, decimals()); QString data = QString::fromLatin1("App.Rotation(App.Vector(%1,%2,%3),%4)") .arg(Base::UnitsApi::toNumber(axis.x, format)) .arg(Base::UnitsApi::toNumber(axis.y, format)) .arg(Base::UnitsApi::toNumber(axis.z, format)) .arg(Base::UnitsApi::toNumber(angle, format)); setPropertyValue(data); } QWidget* PropertyRotationItem::createEditor(QWidget* parent, const QObject* receiver, const char* method) const { Q_UNUSED(parent) Q_UNUSED(receiver) Q_UNUSED(method) return nullptr; } void PropertyRotationItem::setEditorData(QWidget *editor, const QVariant& data) const { Q_UNUSED(editor) Q_UNUSED(data) } QVariant PropertyRotationItem::editorData(QWidget *editor) const { Q_UNUSED(editor) return QVariant(); } void PropertyRotationItem::propertyBound() { if (isBound()) { m_a->bind(App::ObjectIdentifier(getPath())<bind(App::ObjectIdentifier(getPath())<(dlg); if (dlg && !task) { // there is already another task dialog which must be closed first Gui::Control().showDialog(dlg); return; } if (!task) { task = new Gui::Dialog::TaskPlacement(); } if (!_task) { _task = task; connect(task, SIGNAL(placementChanged(const QVariant &, bool, bool)), this, SLOT(updateValue(const QVariant&, bool, bool))); } task->setPlacement(value().value()); task->setPropertyName(propertyname); task->bindObject(); Gui::Control().showDialog(task); } void PlacementEditor::showValue(const QVariant& d) { const Base::Placement& p = d.value(); double angle; Base::Vector3d dir, pos; p.getRotation().getRawValue(dir, angle); angle = Base::toDegrees(angle); pos = p.getPosition(); QLocale loc; QString data = QString::fromUtf8("[(%1 %2 %3);%4 \xc2\xb0;(%5 %6 %7)]") .arg(loc.toString(dir.x,'f',2), loc.toString(dir.y,'f',2), loc.toString(dir.z,'f',2), loc.toString(angle,'f',2), loc.toString(pos.x,'f',2), loc.toString(pos.y,'f',2), loc.toString(pos.z,'f',2)); getLabel()->setText(data); } void PlacementEditor::updateValue(const QVariant& v, bool incr, bool data) { if (data) { if (incr) { QVariant u = value(); const Base::Placement& plm = u.value(); const Base::Placement& rel = v.value(); Base::Placement newp = rel * plm; setValue(QVariant::fromValue(newp)); } else { setValue(v); } } } PROPERTYITEM_SOURCE(Gui::PropertyEditor::PropertyPlacementItem) PropertyPlacementItem::PropertyPlacementItem() { m_a = static_cast(PropertyUnitItem::create()); m_a->setParent(this); m_a->setPropertyName(QLatin1String(QT_TRANSLATE_NOOP("App::Property", "Angle"))); this->appendChild(m_a); m_d = static_cast(PropertyVectorItem::create()); m_d->setParent(this); m_d->setPropertyName(QLatin1String(QT_TRANSLATE_NOOP("App::Property", "Axis"))); m_d->setReadOnly(true); this->appendChild(m_d); m_p = static_cast(PropertyVectorDistanceItem::create()); m_p->setParent(this); m_p->setPropertyName(QLatin1String(QT_TRANSLATE_NOOP("App::Property", "Position"))); m_p->setReadOnly(true); this->appendChild(m_p); } PropertyPlacementItem::~PropertyPlacementItem() { } Base::Quantity PropertyPlacementItem::getAngle() const { QVariant value = data(1, Qt::EditRole); if (!value.canConvert()) return Base::Quantity(0.0); const Base::Placement& val = value.value(); double angle = h.getAngle(val.getRotation()); return Base::Quantity(Base::toDegrees(angle), Base::Unit::Angle); } void PropertyPlacementItem::setAngle(Base::Quantity angle) { QVariant value = data(1, Qt::EditRole); if (!value.canConvert()) return; Base::Placement val = value.value(); Base::Rotation rot = h.setAngle(angle.getValue()); val.setRotation(rot); setValue(QVariant::fromValue(val)); } Base::Vector3d PropertyPlacementItem::getAxis() const { return h.getAxis(); } void PropertyPlacementItem::setAxis(const Base::Vector3d& axis) { QVariant value = data(1, Qt::EditRole); if (!value.canConvert()) return; Base::Placement val = value.value(); Base::Rotation rot = val.getRotation(); rot = h.setAxis(rot, axis); val.setRotation(rot); setValue(QVariant::fromValue(val)); } Base::Vector3d PropertyPlacementItem::getPosition() const { QVariant value = data(1, Qt::EditRole); if (!value.canConvert()) return Base::Vector3d(0,0,0); const Base::Placement& val = value.value(); return val.getPosition(); } void PropertyPlacementItem::setPosition(const Base::Vector3d& pos) { QVariant value = data(1, Qt::EditRole); if (!value.canConvert()) return; Base::Placement val = value.value(); val.setPosition(pos); h.setChanged(true); setValue(QVariant::fromValue(val)); } void PropertyPlacementItem::assignProperty(const App::Property* prop) { // Choose an adaptive epsilon to avoid changing the axis when they are considered to // be equal. See https://forum.freecadweb.org/viewtopic.php?f=10&t=24662&start=10 double eps = std::pow(10.0, -2*(decimals()+1)); if (prop->getTypeId().isDerivedFrom(App::PropertyPlacement::getClassTypeId())) { const Base::Placement& value = static_cast(prop)->getValue(); h.assignProperty(value.getRotation(), eps); } } QVariant PropertyPlacementItem::value(const App::Property* prop) const { assert(prop && prop->getTypeId().isDerivedFrom(App::PropertyPlacement::getClassTypeId())); const Base::Placement& value = static_cast(prop)->getValue(); double angle; Base::Vector3d dir; value.getRotation().getRawValue(dir, angle); if (!h.isAxisInitialized()) { if (m_a->hasExpression()) { QString str = m_a->expressionAsString(); angle = str.toDouble(); } else { angle = Base::toDegrees(angle); } PropertyItem* x = m_d->child(0); PropertyItem* y = m_d->child(1); PropertyItem* z = m_d->child(2); if (x->hasExpression()) { QString str = x->expressionAsString(); dir.x = str.toDouble(); } if (y->hasExpression()) { QString str = y->expressionAsString(); dir.y = str.toDouble(); } if (z->hasExpression()) { QString str = z->expressionAsString(); dir.z = str.toDouble(); } h.setValue(dir, angle); } return QVariant::fromValue(value); } QVariant PropertyPlacementItem::toolTip(const App::Property* prop) const { assert(prop && prop->getTypeId().isDerivedFrom(App::PropertyPlacement::getClassTypeId())); const Base::Placement& p = static_cast(prop)->getValue(); double angle; Base::Vector3d dir, pos; p.getRotation().getRawValue(dir, angle); angle = Base::toDegrees(angle); pos = p.getPosition(); QLocale loc; QString data = QString::fromUtf8("Axis: (%1 %2 %3)\n" "Angle: %4\n" "Position: (%5 %6 %7)") .arg(loc.toString(dir.x,'f',decimals()), loc.toString(dir.y,'f',decimals()), loc.toString(dir.z,'f',decimals()), Base::Quantity(angle, Base::Unit::Angle).getUserString(), Base::Quantity(pos.x, Base::Unit::Length).getUserString(), Base::Quantity(pos.y, Base::Unit::Length).getUserString(), Base::Quantity(pos.z, Base::Unit::Length).getUserString()); return QVariant(data); } QVariant PropertyPlacementItem::toString(const QVariant& prop) const { const Base::Placement& p = prop.value(); double angle; Base::Vector3d dir, pos; p.getRotation().getRawValue(dir, angle); angle = Base::toDegrees(angle); pos = p.getPosition(); QLocale loc; QString data = QString::fromUtf8("[(%1 %2 %3); %4; (%5 %6 %7)]") .arg(loc.toString(dir.x,'f',2), loc.toString(dir.y,'f',2), loc.toString(dir.z,'f',2), Base::Quantity(angle, Base::Unit::Angle).getUserString(), Base::Quantity(pos.x, Base::Unit::Length).getUserString(), Base::Quantity(pos.y, Base::Unit::Length).getUserString(), Base::Quantity(pos.z, Base::Unit::Length).getUserString()); return QVariant(data); } void PropertyPlacementItem::setValue(const QVariant& value) { if (hasExpression() || !value.canConvert()) return; // Accept this only if the user changed the axis, angle or position but // not if >this< item loses focus if (!h.hasChangedAndReset()) return; const Base::Placement& val = value.value(); Base::Vector3d pos = val.getPosition(); Base::Vector3d axis; double angle; h.getValue(axis, angle); Base::QuantityFormat format(Base::QuantityFormat::Fixed, decimals()); QString data = QString::fromLatin1("App.Placement(" "App.Vector(%1,%2,%3)," "App.Rotation(App.Vector(%4,%5,%6),%7))") .arg(Base::UnitsApi::toNumber(pos.x, format)) .arg(Base::UnitsApi::toNumber(pos.y, format)) .arg(Base::UnitsApi::toNumber(pos.z, format)) .arg(Base::UnitsApi::toNumber(axis.x, format)) .arg(Base::UnitsApi::toNumber(axis.y, format)) .arg(Base::UnitsApi::toNumber(axis.z, format)) .arg(Base::UnitsApi::toNumber(angle, format)); setPropertyValue(data); } QWidget* PropertyPlacementItem::createEditor(QWidget* parent, const QObject* receiver, const char* method) const { PlacementEditor *pe = new PlacementEditor(this->propertyName(), parent); QObject::connect(pe, SIGNAL(valueChanged(const QVariant &)), receiver, method); pe->setDisabled(isReadOnly()); return pe; } void PropertyPlacementItem::setEditorData(QWidget *editor, const QVariant& data) const { PlacementEditor *pe = qobject_cast(editor); pe->setValue(data); } QVariant PropertyPlacementItem::editorData(QWidget *editor) const { PlacementEditor *pe = qobject_cast(editor); return pe->value(); } void PropertyPlacementItem::propertyBound() { if (isBound()) { m_a->bind(App::ObjectIdentifier(getPath())<bind(App::ObjectIdentifier(getPath())<bind(App::ObjectIdentifier(getPath())<(PropertyStringListItem::create()); m_enum->setParent(this); m_enum->setPropertyName(QLatin1String(QT_TRANSLATE_NOOP("App::Property", "Enum"))); this->appendChild(m_enum); } } void PropertyEnumItem::propertyBound() { if (m_enum && isBound()) m_enum->bind(App::ObjectIdentifier(getPath())<getTypeId().isDerivedFrom(App::PropertyEnumeration::getClassTypeId())) { const App::PropertyEnumeration* prop_enum = static_cast(prop); for(int i=0; igetEnum().maxValue(); ++i) res.push_back(QString::fromUtf8(prop_enum->getEnums()[i])); } return res; } QVariant PropertyEnumItem::value(const App::Property* prop) const { assert(prop && prop->getTypeId().isDerivedFrom(App::PropertyEnumeration::getClassTypeId())); const App::PropertyEnumeration* prop_enum = static_cast(prop); if(!prop_enum->isValid()) return QVariant(QString()); return QVariant(QString::fromUtf8(prop_enum->getValueAsString())); } void PropertyEnumItem::setValue(const QVariant& value) { if (hasExpression()) return; QString data; if (value.type() == QVariant::StringList) { QStringList values = value.toStringList(); QTextStream str(&data); str << "["; for (QStringList::Iterator it = values.begin(); it != values.end(); ++it) { QString text(*it); text.replace(QString::fromUtf8("'"),QString::fromUtf8("\\'")); std::string pystr = Base::Tools::escapedUnicodeFromUtf8(text.toUtf8()); pystr = Base::Interpreter().strToPython(pystr.c_str()); str << "u\"" << pystr.c_str() << "\", "; } str << "]"; } else if (value.canConvert(QVariant::String)) { QByteArray val = value.toString().toUtf8(); std::string str = Base::Tools::escapedUnicodeFromUtf8(val); data = QString::fromLatin1("u\"%1\"").arg(QString::fromStdString(str)); } else return; setPropertyValue(data); } namespace { class EnumItems; struct EnumItem { QString text; QString fullText; std::shared_ptr children; EnumItem(const QString &t = QString(), const QString &f = QString()) :text(t), fullText(f) {} void populate(QMenu *menu); }; class EnumItems : public std::vector { }; void EnumItem::populate(QMenu *menu) { if (!children || children->empty()) { auto action = menu->addAction(text); action->setData(fullText); return; } auto subMenu = menu->addMenu(text); for (auto &item : *children) item.populate(subMenu); } } // anonymous namespace QWidget* PropertyEnumItem::createEditor(QWidget* parent, const QObject* receiver, const char* method) const { const std::vector& items = getPropertyData(); QStringList commonModes, modes; for (std::vector::const_iterator it = items.begin(); it != items.end(); ++it) { if ((*it)->getTypeId() == App::PropertyEnumeration::getClassTypeId()) { App::PropertyEnumeration* prop = static_cast(*it); if (prop->getEnums() == nullptr) { commonModes.clear(); return nullptr; } const std::vector& value = prop->getEnumVector(); if (it == items.begin()) { for (std::vector::const_iterator jt = value.begin(); jt != value.end(); ++jt) commonModes << QString::fromUtf8(jt->c_str()); } else { for (std::vector::const_iterator jt = value.begin(); jt != value.end(); ++jt) { if (commonModes.contains(QString::fromUtf8(jt->c_str()))) modes << QString::fromUtf8(jt->c_str()); } commonModes = modes; modes.clear(); } } } if (commonModes.isEmpty()) return nullptr; int index = -1; std::shared_ptr enumItems; for (auto &mode : commonModes) { ++index; auto fields = mode.split(QStringLiteral("|")); if (!enumItems && fields.size() <= 1) continue; if (!enumItems) { enumItems = std::make_shared(); for (int i=0; iemplace_back(commonModes[i], mode); } auto children = enumItems; int j = -1; for (auto &field : fields) { ++j; field = field.trimmed(); auto it = children->end(); if (field.isEmpty()) { if (!children->empty()) --it; else continue; } else { it = std::find_if(children->begin(), children->end(), [&field](const EnumItem &item) { return item.text == field; }); if (it == children->end()) it = children->emplace(children->end(), field, mode); } if (j + 1 == (int)fields.size()) break; if (!it->children) it->children = std::make_shared(); children = it->children; } } if (!enumItems) { QComboBox *cb = new QComboBox(parent); cb->setFrame(false); cb->setDisabled(isReadOnly()); cb->addItems(commonModes); QObject::connect(cb, SIGNAL(activated(int)), receiver, method); return cb; } auto button = new PropertyEnumButton(parent); button->setDisabled(isReadOnly()); QMenu *menu = new QMenu(button); for (auto &item : *enumItems) item.populate(menu); button->setMenu(menu); QObject::connect(menu, &QMenu::aboutToShow, this, [=]() { menu->setMinimumWidth(button->width()); }); QObject::connect(menu, &QMenu::triggered, this, [=](QAction *action) { button->setText(action->data().toString()); button->picked(); }); QObject::connect(button, SIGNAL(picked()), receiver, method); return button; } void PropertyEnumItem::setEditorData(QWidget *editor, const QVariant& data) const { if (auto cb = qobject_cast(editor)) { cb->setEditable(false); cb->setCurrentIndex(cb->findText(data.toString())); } else if (auto btn = qobject_cast(editor)) btn->setText(data.toString()); } QVariant PropertyEnumItem::editorData(QWidget *editor) const { if (auto cb = qobject_cast(editor)) return QVariant(cb->currentText()); else if (auto btn = qobject_cast(editor)) return btn->text(); return QVariant(); } // --------------------------------------------------------------- PROPERTYITEM_SOURCE(Gui::PropertyEditor::PropertyStringListItem) PropertyStringListItem::PropertyStringListItem() { } QWidget* PropertyStringListItem::createEditor(QWidget* parent, const QObject* receiver, const char* method) const { Gui::LabelEditor* le = new Gui::LabelEditor(parent); le->setAutoFillBackground(true); le->setDisabled(isReadOnly()); QObject::connect(le, SIGNAL(textChanged(const QString&)), receiver, method); return le; } void PropertyStringListItem::setEditorData(QWidget *editor, const QVariant& data) const { Gui::LabelEditor *le = qobject_cast(editor); QStringList list = data.toStringList(); le->setText(list.join(QChar::fromLatin1('\n'))); } QVariant PropertyStringListItem::editorData(QWidget *editor) const { Gui::LabelEditor *le = qobject_cast(editor); QString complete = le->text(); QStringList list = complete.split(QChar::fromLatin1('\n')); return QVariant(list); } QVariant PropertyStringListItem::toString(const QVariant& prop) const { QStringList list = prop.toStringList(); if (list.size() > 10) { list = list.mid(0, 10); list.append(QLatin1String("...")); } QString text = QString::fromUtf8("[%1]").arg(list.join(QLatin1String(","))); return QVariant(text); } QVariant PropertyStringListItem::value(const App::Property* prop) const { assert(prop && prop->getTypeId().isDerivedFrom(App::PropertyStringList::getClassTypeId())); QStringList list; const std::vector& value = (static_cast(prop))->getValues(); for ( std::vector::const_iterator jt = value.begin(); jt != value.end(); ++jt ) { list << QString::fromUtf8((*jt).c_str()); } return QVariant(list); } void PropertyStringListItem::setValue(const QVariant& value) { if (hasExpression() || !value.canConvert(QVariant::StringList)) return; QStringList values = value.toStringList(); QString data; QTextStream str(&data); str.setCodec("UTF-8"); str << "["; for (QStringList::Iterator it = values.begin(); it != values.end(); ++it) { QString text(*it); std::string pystr = Base::Interpreter().strToPython(text.toUtf8().constData()); str << "\"" << QString::fromUtf8(pystr.c_str()) << "\", "; } str << "]"; setPropertyValue(data); } // --------------------------------------------------------------- PROPERTYITEM_SOURCE(Gui::PropertyEditor::PropertyFloatListItem) PropertyFloatListItem::PropertyFloatListItem() { } QWidget* PropertyFloatListItem::createEditor(QWidget* parent, const QObject* receiver, const char* method) const { Gui::LabelEditor* le = new Gui::LabelEditor(parent); le->setAutoFillBackground(true); le->setInputType(Gui::LabelEditor::Float); le->setDisabled(isReadOnly()); QObject::connect(le, SIGNAL(textChanged(const QString&)), receiver, method); return le; } void PropertyFloatListItem::setEditorData(QWidget *editor, const QVariant& data) const { Gui::LabelEditor *le = qobject_cast(editor); QStringList list = data.toStringList(); le->setText(list.join(QChar::fromLatin1('\n'))); } QVariant PropertyFloatListItem::editorData(QWidget *editor) const { Gui::LabelEditor *le = qobject_cast(editor); QString complete = le->text(); QStringList list = complete.split(QChar::fromLatin1('\n')); return QVariant(list); } QVariant PropertyFloatListItem::toString(const QVariant& prop) const { QStringList list = prop.toStringList(); if (list.size() > 10) { list = list.mid(0, 10); list.append(QLatin1String("...")); } QString text = QString::fromUtf8("[%1]").arg(list.join(QLatin1String(","))); return QVariant(text); } QVariant PropertyFloatListItem::value(const App::Property* prop) const { assert(prop && prop->getTypeId().isDerivedFrom(App::PropertyFloatList::getClassTypeId())); QStringList list; const std::vector& value = static_cast(prop)->getValues(); for (std::vector::const_iterator jt = value.begin(); jt != value.end(); ++jt) { list << QString::number(*jt, 'f', decimals()); } return QVariant(list); } void PropertyFloatListItem::setValue(const QVariant& value) { if (hasExpression() || !value.canConvert(QVariant::StringList)) return; QStringList values = value.toStringList(); QString data; QTextStream str(&data); str << "["; for (QStringList::Iterator it = values.begin(); it != values.end(); ++it) { str << *it << ","; } str << "]"; if (data == QString::fromUtf8("[,]")) data = QString::fromUtf8("[]"); setPropertyValue(data); } // --------------------------------------------------------------- PROPERTYITEM_SOURCE(Gui::PropertyEditor::PropertyIntegerListItem) PropertyIntegerListItem::PropertyIntegerListItem() { } QWidget* PropertyIntegerListItem::createEditor(QWidget* parent, const QObject* receiver, const char* method) const { Gui::LabelEditor* le = new Gui::LabelEditor(parent); le->setAutoFillBackground(true); le->setInputType(Gui::LabelEditor::Integer); le->setDisabled(isReadOnly()); QObject::connect(le, SIGNAL(textChanged(const QString&)), receiver, method); return le; } void PropertyIntegerListItem::setEditorData(QWidget *editor, const QVariant& data) const { Gui::LabelEditor *le = qobject_cast(editor); QStringList list = data.toStringList(); le->setText(list.join(QChar::fromLatin1('\n'))); } QVariant PropertyIntegerListItem::editorData(QWidget *editor) const { Gui::LabelEditor *le = qobject_cast(editor); QString complete = le->text(); QStringList list = complete.split(QChar::fromLatin1('\n')); return QVariant(list); } QVariant PropertyIntegerListItem::toString(const QVariant& prop) const { QStringList list = prop.toStringList(); if (list.size() > 10) { list = list.mid(0, 10); list.append(QLatin1String("...")); } QString text = QString::fromUtf8("[%1]").arg(list.join(QLatin1String(","))); return QVariant(text); } QVariant PropertyIntegerListItem::value(const App::Property* prop) const { assert(prop && prop->getTypeId().isDerivedFrom(App::PropertyIntegerList::getClassTypeId())); QStringList list; const std::vector& value = static_cast(prop)->getValues(); for (std::vector::const_iterator jt = value.begin(); jt != value.end(); ++jt) { list << QString::number(*jt); } return QVariant(list); } void PropertyIntegerListItem::setValue(const QVariant& value) { if (hasExpression() || !value.canConvert(QVariant::StringList)) return; QStringList values = value.toStringList(); QString data; QTextStream str(&data); str << "["; for (QStringList::Iterator it = values.begin(); it != values.end(); ++it) { str << *it << ","; } str << "]"; if (data == QString::fromUtf8("[,]")) data = QString::fromUtf8("[]"); setPropertyValue(data); } // -------------------------------------------------------------------- PROPERTYITEM_SOURCE(Gui::PropertyEditor::PropertyColorItem) PropertyColorItem::PropertyColorItem() { } QVariant PropertyColorItem::decoration(const QVariant& value) const { QColor color = value.value(); int size = QApplication::style()->pixelMetric(QStyle::PM_ListViewIconSize); QPixmap p(size, size); p.fill(color); return QVariant(p); } QVariant PropertyColorItem::toString(const QVariant& prop) const { QColor value = prop.value(); QString color = QString::fromLatin1("[%1, %2, %3]") .arg(value.red()).arg(value.green()).arg(value.blue()); return QVariant(color); } QVariant PropertyColorItem::value(const App::Property* prop) const { assert(prop && prop->getTypeId().isDerivedFrom(App::PropertyColor::getClassTypeId())); App::Color value = ((App::PropertyColor*)prop)->getValue(); return QVariant(value.asValue()); } void PropertyColorItem::setValue(const QVariant& value) { if (hasExpression() || !value.canConvert()) return; QColor col = value.value(); App::Color val; val.setValue(col); QString data = QString::fromLatin1("(%1,%2,%3)") .arg(val.r,0,'f',decimals()) .arg(val.g,0,'f',decimals()) .arg(val.b,0,'f',decimals()); setPropertyValue(data); } QWidget* PropertyColorItem::createEditor(QWidget* parent, const QObject* receiver, const char* method) const { Gui::ColorButton* cb = new Gui::ColorButton( parent ); cb->setDisabled(isReadOnly()); QObject::connect(cb, SIGNAL(changed()), receiver, method); return cb; } void PropertyColorItem::setEditorData(QWidget *editor, const QVariant& data) const { Gui::ColorButton *cb = qobject_cast(editor); QColor color = data.value(); cb->setColor(color); } QVariant PropertyColorItem::editorData(QWidget *editor) const { Gui::ColorButton *cb = qobject_cast(editor); QVariant var; var.setValue(cb->color()); return var; } // -------------------------------------------------------------------- namespace Gui { namespace PropertyEditor { class Material { public: QColor diffuseColor; QColor ambientColor; QColor specularColor; QColor emissiveColor; float shininess; float transparency; }; } } Q_DECLARE_METATYPE(Gui::PropertyEditor::Material) PROPERTYITEM_SOURCE(Gui::PropertyEditor::PropertyMaterialItem) PropertyMaterialItem::PropertyMaterialItem() { diffuse = static_cast(PropertyColorItem::create()); diffuse->setParent(this); diffuse->setPropertyName(QLatin1String("DiffuseColor")); this->appendChild(diffuse); ambient = static_cast(PropertyColorItem::create()); ambient->setParent(this); ambient->setPropertyName(QLatin1String("AmbientColor")); this->appendChild(ambient); specular = static_cast(PropertyColorItem::create()); specular->setParent(this); specular->setPropertyName(QLatin1String("SpecularColor")); this->appendChild(specular); emissive = static_cast(PropertyColorItem::create()); emissive->setParent(this); emissive->setPropertyName(QLatin1String("EmissiveColor")); this->appendChild(emissive); shininess = static_cast(PropertyFloatItem::create()); shininess->setParent(this); shininess->setPropertyName(QLatin1String("Shininess")); this->appendChild(shininess); transparency = static_cast(PropertyFloatItem::create()); transparency->setParent(this); transparency->setPropertyName(QLatin1String("Transparency")); this->appendChild(transparency); } PropertyMaterialItem::~PropertyMaterialItem() { } void PropertyMaterialItem::propertyBound() { } QColor PropertyMaterialItem::getDiffuseColor() const { QVariant value = data(1, Qt::EditRole); if (!value.canConvert()) return QColor(); Material val = value.value(); return val.diffuseColor; } void PropertyMaterialItem::setDiffuseColor(const QColor& color) { QVariant value = data(1, Qt::EditRole); if (!value.canConvert()) return; Material mat = value.value(); mat.diffuseColor = color; setValue(QVariant::fromValue(mat)); } QColor PropertyMaterialItem::getAmbientColor() const { QVariant value = data(1, Qt::EditRole); if (!value.canConvert()) return QColor(); Material val = value.value(); return val.ambientColor; } void PropertyMaterialItem::setAmbientColor(const QColor& color) { QVariant value = data(1, Qt::EditRole); if (!value.canConvert()) return; Material mat = value.value(); mat.ambientColor = color; setValue(QVariant::fromValue(mat)); } QColor PropertyMaterialItem::getSpecularColor() const { QVariant value = data(1, Qt::EditRole); if (!value.canConvert()) return QColor(); Material val = value.value(); return val.specularColor; } void PropertyMaterialItem::setSpecularColor(const QColor& color) { QVariant value = data(1, Qt::EditRole); if (!value.canConvert()) return; Material mat = value.value(); mat.specularColor = color; setValue(QVariant::fromValue(mat)); } QColor PropertyMaterialItem::getEmissiveColor() const { QVariant value = data(1, Qt::EditRole); if (!value.canConvert()) return QColor(); Material val = value.value(); return val.emissiveColor; } void PropertyMaterialItem::setEmissiveColor(const QColor& color) { QVariant value = data(1, Qt::EditRole); if (!value.canConvert()) return; Material mat = value.value(); mat.emissiveColor = color; setValue(QVariant::fromValue(mat)); } float PropertyMaterialItem::getShininess() const { QVariant value = data(1, Qt::EditRole); if (!value.canConvert()) return 0; Material val = value.value(); return val.shininess; } void PropertyMaterialItem::setShininess(float s) { QVariant value = data(1, Qt::EditRole); if (!value.canConvert()) return; Material mat = value.value(); mat.shininess = s; setValue(QVariant::fromValue(mat)); } float PropertyMaterialItem::getTransparency() const { QVariant value = data(1, Qt::EditRole); if (!value.canConvert()) return 0; Material val = value.value(); return val.transparency; } void PropertyMaterialItem::setTransparency(float t) { QVariant value = data(1, Qt::EditRole); if (!value.canConvert()) return; Material mat = value.value(); mat.transparency = t; setValue(QVariant::fromValue(mat)); } QVariant PropertyMaterialItem::decoration(const QVariant& value) const { // use the diffuse color Material val = value.value(); QColor color = val.diffuseColor; int size = QApplication::style()->pixelMetric(QStyle::PM_ListViewIconSize); QPixmap p(size, size); p.fill(color); return QVariant(p); } QVariant PropertyMaterialItem::toString(const QVariant& prop) const { // use the diffuse color Material val = prop.value(); QColor value = val.diffuseColor; QString color = QString::fromLatin1("[%1, %2, %3]") .arg(value.red()).arg(value.green()).arg(value.blue()); return QVariant(color); } QVariant PropertyMaterialItem::toolTip(const App::Property* prop) const { assert(prop && prop->getTypeId().isDerivedFrom(App::PropertyMaterial::getClassTypeId())); const App::Material& value = static_cast(prop)->getValue(); QColor dc = value.diffuseColor.asValue(); QColor ac = value.ambientColor.asValue(); QColor sc = value.specularColor.asValue(); QColor ec = value.emissiveColor.asValue(); QString data = QString::fromUtf8( "Diffuse color: [%1, %2, %3]\n" "Ambient color: [%4, %5, %6]\n" "Specular color: [%7, %8, %9]\n" "Emissive color: [%10, %11, %12]\n" "Shininess: %13\n" "Transparency: %14" ) .arg(dc.red()).arg(dc.green()).arg(dc.blue()) .arg(ac.red()).arg(ac.green()).arg(ac.blue()) .arg(sc.red()).arg(sc.green()).arg(sc.blue()) .arg(ec.red()).arg(ec.green()).arg(ec.blue()) .arg(value.shininess) .arg(value.transparency) ; return QVariant(data); } QVariant PropertyMaterialItem::value(const App::Property* prop) const { assert(prop && prop->getTypeId().isDerivedFrom(App::PropertyMaterial::getClassTypeId())); const App::Material& value = static_cast(prop)->getValue(); Material mat; mat.diffuseColor = value.diffuseColor.asValue(); mat.ambientColor = value.ambientColor.asValue(); mat.specularColor = value.specularColor.asValue(); mat.emissiveColor = value.emissiveColor.asValue(); mat.shininess = value.shininess; mat.transparency = value.transparency; return QVariant::fromValue(mat); } void PropertyMaterialItem::setValue(const QVariant& value) { if (hasExpression() || !value.canConvert()) return; Material mat = value.value(); App::Color dc; dc.setValue(mat.diffuseColor); App::Color ac; ac.setValue(mat.ambientColor); App::Color sc; sc.setValue(mat.specularColor); App::Color ec; ec.setValue(mat.emissiveColor); float s = mat.shininess; float t = mat.transparency; QString data = QString::fromLatin1( "App.Material(" "DiffuseColor=(%1,%2,%3)," "AmbientColor=(%4,%5,%6)," "SpecularColor=(%7,%8,%9)," "EmissiveColor=(%10,%11,%12)," "Shininess=(%13)," "Transparency=(%14)," ")" ) .arg(dc.r, 0, 'f', decimals()) .arg(dc.g, 0, 'f', decimals()) .arg(dc.b, 0, 'f', decimals()) .arg(ac.r, 0, 'f', decimals()) .arg(ac.g, 0, 'f', decimals()) .arg(ac.b, 0, 'f', decimals()) .arg(sc.r, 0, 'f', decimals()) .arg(sc.g, 0, 'f', decimals()) .arg(sc.b, 0, 'f', decimals()) .arg(ec.r, 0, 'f', decimals()) .arg(ec.g, 0, 'f', decimals()) .arg(ec.b, 0, 'f', decimals()) .arg(s, 0, 'f', decimals()) .arg(t, 0, 'f', decimals()) ; setPropertyValue(data); } QWidget* PropertyMaterialItem::createEditor(QWidget* parent, const QObject* receiver, const char* method) const { Gui::ColorButton* cb = new Gui::ColorButton(parent); cb->setDisabled(isReadOnly()); QObject::connect(cb, SIGNAL(changed()), receiver, method); return cb; } void PropertyMaterialItem::setEditorData(QWidget *editor, const QVariant& data) const { if (!data.canConvert()) return; Material val = data.value(); Gui::ColorButton *cb = qobject_cast(editor); cb->setColor(val.diffuseColor); } QVariant PropertyMaterialItem::editorData(QWidget *editor) const { Gui::ColorButton *cb = qobject_cast(editor); QVariant value = data(1, Qt::EditRole); if (!value.canConvert()) return QVariant(); Material val = value.value(); val.diffuseColor = cb->color(); return QVariant::fromValue(val); } // -------------------------------------------------------------------- PROPERTYITEM_SOURCE(Gui::PropertyEditor::PropertyMaterialListItem) PropertyMaterialListItem::PropertyMaterialListItem() { // This editor gets a list of materials but it only edits the first item. diffuse = static_cast(PropertyColorItem::create()); diffuse->setParent(this); diffuse->setPropertyName(QLatin1String("DiffuseColor")); this->appendChild(diffuse); ambient = static_cast(PropertyColorItem::create()); ambient->setParent(this); ambient->setPropertyName(QLatin1String("AmbientColor")); this->appendChild(ambient); specular = static_cast(PropertyColorItem::create()); specular->setParent(this); specular->setPropertyName(QLatin1String("SpecularColor")); this->appendChild(specular); emissive = static_cast(PropertyColorItem::create()); emissive->setParent(this); emissive->setPropertyName(QLatin1String("EmissiveColor")); this->appendChild(emissive); shininess = static_cast(PropertyFloatItem::create()); shininess->setParent(this); shininess->setPropertyName(QLatin1String("Shininess")); this->appendChild(shininess); transparency = static_cast(PropertyFloatItem::create()); transparency->setParent(this); transparency->setPropertyName(QLatin1String("Transparency")); this->appendChild(transparency); } PropertyMaterialListItem::~PropertyMaterialListItem() { } void PropertyMaterialListItem::propertyBound() { } QColor PropertyMaterialListItem::getDiffuseColor() const { QVariant value = data(1, Qt::EditRole); if (!value.canConvert()) return QColor(); QVariantList list = value.toList(); if (list.isEmpty()) return QColor(); if (!list[0].canConvert()) return QColor(); Material mat = list[0].value(); return mat.diffuseColor; } void PropertyMaterialListItem::setDiffuseColor(const QColor& color) { QVariant value = data(1, Qt::EditRole); if (!value.canConvert()) return; QVariantList list = value.toList(); if (list.isEmpty()) return; if (!list[0].canConvert()) return; Material mat = list[0].value(); mat.diffuseColor = color; list[0] = QVariant::fromValue(mat); setValue(list); } QColor PropertyMaterialListItem::getAmbientColor() const { QVariant value = data(1, Qt::EditRole); if (!value.canConvert()) return QColor(); QVariantList list = value.toList(); if (list.isEmpty()) return QColor(); if (!list[0].canConvert()) return QColor(); Material mat = list[0].value(); return mat.ambientColor; } void PropertyMaterialListItem::setAmbientColor(const QColor& color) { QVariant value = data(1, Qt::EditRole); if (!value.canConvert()) return; QVariantList list = value.toList(); if (list.isEmpty()) return; if (!list[0].canConvert()) return; Material mat = list[0].value(); mat.ambientColor = color; list[0] = QVariant::fromValue(mat); setValue(list); } QColor PropertyMaterialListItem::getSpecularColor() const { QVariant value = data(1, Qt::EditRole); if (!value.canConvert()) return QColor(); QVariantList list = value.toList(); if (list.isEmpty()) return QColor(); if (!list[0].canConvert()) return QColor(); Material mat = list[0].value(); return mat.specularColor; } void PropertyMaterialListItem::setSpecularColor(const QColor& color) { QVariant value = data(1, Qt::EditRole); if (!value.canConvert()) return; QVariantList list = value.toList(); if (list.isEmpty()) return; if (!list[0].canConvert()) return; Material mat = list[0].value(); mat.specularColor = color; list[0] = QVariant::fromValue(mat); setValue(list); } QColor PropertyMaterialListItem::getEmissiveColor() const { QVariant value = data(1, Qt::EditRole); if (!value.canConvert()) return QColor(); QVariantList list = value.toList(); if (list.isEmpty()) return QColor(); if (!list[0].canConvert()) return QColor(); Material mat = list[0].value(); return mat.emissiveColor; } void PropertyMaterialListItem::setEmissiveColor(const QColor& color) { QVariant value = data(1, Qt::EditRole); if (!value.canConvert()) return; QVariantList list = value.toList(); if (list.isEmpty()) return; if (!list[0].canConvert()) return; Material mat = list[0].value(); mat.emissiveColor = color; list[0] = QVariant::fromValue(mat); setValue(list); } float PropertyMaterialListItem::getShininess() const { QVariant value = data(1, Qt::EditRole); if (!value.canConvert()) return 0; QVariantList list = value.toList(); if (list.isEmpty()) return 0; if (!list[0].canConvert()) return 0; Material mat = list[0].value(); return mat.shininess; } void PropertyMaterialListItem::setShininess(float s) { QVariant value = data(1, Qt::EditRole); if (!value.canConvert()) return; QVariantList list = value.toList(); if (list.isEmpty()) return; if (!list[0].canConvert()) return; Material mat = list[0].value(); mat.shininess = s; list[0] = QVariant::fromValue(mat); setValue(list); } float PropertyMaterialListItem::getTransparency() const { QVariant value = data(1, Qt::EditRole); if (!value.canConvert()) return 0; QVariantList list = value.toList(); if (list.isEmpty()) return 0; if (!list[0].canConvert()) return 0; Material mat = list[0].value(); return mat.transparency; } void PropertyMaterialListItem::setTransparency(float t) { QVariant value = data(1, Qt::EditRole); if (!value.canConvert()) return; QVariantList list = value.toList(); if (list.isEmpty()) return; if (!list[0].canConvert()) return; Material mat = list[0].value(); mat.transparency = t; list[0] = QVariant::fromValue(mat); setValue(list); } QVariant PropertyMaterialListItem::decoration(const QVariant& value) const { if (!value.canConvert()) return QVariant(); QVariantList list = value.toList(); if (list.isEmpty()) return QVariant(); if (!list[0].canConvert()) return QVariant(); // use the diffuse color Material mat = list[0].value(); QColor color = mat.diffuseColor; int size = QApplication::style()->pixelMetric(QStyle::PM_ListViewIconSize); QPixmap p(size, size); p.fill(color); return QVariant(p); } QVariant PropertyMaterialListItem::toString(const QVariant& prop) const { if (!prop.canConvert()) return QVariant(); QVariantList list = prop.toList(); if (list.isEmpty()) return QVariant(); if (!list[0].canConvert()) return QVariant(); // use the diffuse color Material mat = list[0].value(); QColor value = mat.diffuseColor; QString color = QString::fromLatin1("[%1, %2, %3]") .arg(value.red()).arg(value.green()).arg(value.blue()); return QVariant(color); } QVariant PropertyMaterialListItem::toolTip(const App::Property* prop) const { assert(prop && prop->getTypeId().isDerivedFrom(App::PropertyMaterialList::getClassTypeId())); const std::vector& values = static_cast(prop)->getValues(); if (values.empty()) return QVariant(); App::Material value = values.front(); QColor dc = value.diffuseColor.asValue(); QColor ac = value.ambientColor.asValue(); QColor sc = value.specularColor.asValue(); QColor ec = value.emissiveColor.asValue(); QString data = QString::fromUtf8( "Diffuse color: [%1, %2, %3]\n" "Ambient color: [%4, %5, %6]\n" "Specular color: [%7, %8, %9]\n" "Emissive color: [%10, %11, %12]\n" "Shininess: %13\n" "Transparency: %14" ) .arg(dc.red()).arg(dc.green()).arg(dc.blue()) .arg(ac.red()).arg(ac.green()).arg(ac.blue()) .arg(sc.red()).arg(sc.green()).arg(sc.blue()) .arg(ec.red()).arg(ec.green()).arg(ec.blue()) .arg(value.shininess) .arg(value.transparency) ; return QVariant(data); } QVariant PropertyMaterialListItem::value(const App::Property* prop) const { assert(prop && prop->getTypeId().isDerivedFrom(App::PropertyMaterialList::getClassTypeId())); const std::vector& value = static_cast(prop)->getValues(); QVariantList variantList; for (std::vector::const_iterator it = value.begin(); it != value.end(); ++it) { Material mat; mat.diffuseColor = it->diffuseColor.asValue(); mat.ambientColor = it->ambientColor.asValue(); mat.specularColor = it->specularColor.asValue(); mat.emissiveColor = it->emissiveColor.asValue(); mat.shininess = it->shininess; mat.transparency = it->transparency; variantList << QVariant::fromValue(mat); } return variantList; } void PropertyMaterialListItem::setValue(const QVariant& value) { if (hasExpression() || !value.canConvert()) return; QVariantList list = value.toList(); if (list.isEmpty()) return; QString data; QTextStream str(&data); str << "("; for (QVariantList::iterator it = list.begin(); it != list.end(); ++it) { Material mat = it->value(); App::Color dc; dc.setValue(mat.diffuseColor); App::Color ac; ac.setValue(mat.ambientColor); App::Color sc; sc.setValue(mat.specularColor); App::Color ec; ec.setValue(mat.emissiveColor); float s = mat.shininess; float t = mat.transparency; QString item = QString::fromLatin1( "App.Material(" "DiffuseColor=(%1,%2,%3)," "AmbientColor=(%4,%5,%6)," "SpecularColor=(%7,%8,%9)," "EmissiveColor=(%10,%11,%12)," "Shininess=(%13)," "Transparency=(%14)," ")" ) .arg(dc.r, 0, 'f', decimals()) .arg(dc.g, 0, 'f', decimals()) .arg(dc.b, 0, 'f', decimals()) .arg(ac.r, 0, 'f', decimals()) .arg(ac.g, 0, 'f', decimals()) .arg(ac.b, 0, 'f', decimals()) .arg(sc.r, 0, 'f', decimals()) .arg(sc.g, 0, 'f', decimals()) .arg(sc.b, 0, 'f', decimals()) .arg(ec.r, 0, 'f', decimals()) .arg(ec.g, 0, 'f', decimals()) .arg(ec.b, 0, 'f', decimals()) .arg(s, 0, 'f', decimals()) .arg(t, 0, 'f', decimals()) ; str << item << ", "; } str << ")"; setPropertyValue(data); } QWidget* PropertyMaterialListItem::createEditor(QWidget* parent, const QObject* receiver, const char* method) const { Gui::ColorButton* cb = new Gui::ColorButton(parent); cb->setDisabled(isReadOnly()); QObject::connect(cb, SIGNAL(changed()), receiver, method); return cb; } void PropertyMaterialListItem::setEditorData(QWidget *editor, const QVariant& data) const { if (!data.canConvert()) return; QVariantList list = data.toList(); if (list.isEmpty()) return; if (!list[0].canConvert()) return; // use the diffuse color Material mat = list[0].value(); QColor color = mat.diffuseColor; Gui::ColorButton *cb = qobject_cast(editor); cb->setColor(color); } QVariant PropertyMaterialListItem::editorData(QWidget *editor) const { Gui::ColorButton *cb = qobject_cast(editor); QVariant value = data(1, Qt::EditRole); if (!value.canConvert()) return QVariant(); QVariantList list = value.toList(); if (list.isEmpty()) return QVariant(); if (!list[0].canConvert()) return QVariant(); // use the diffuse color Material mat = list[0].value(); mat.diffuseColor = cb->color(); list[0] = QVariant::fromValue(mat); return list; } // -------------------------------------------------------------------- PROPERTYITEM_SOURCE(Gui::PropertyEditor::PropertyFileItem) PropertyFileItem::PropertyFileItem() { } QVariant PropertyFileItem::value(const App::Property* prop) const { assert(prop && prop->getTypeId().isDerivedFrom(App::PropertyFile::getClassTypeId())); std::string value = static_cast(prop)->getValue(); return QVariant(QString::fromUtf8(value.c_str())); } void PropertyFileItem::setValue(const QVariant& value) { if (hasExpression() || !value.canConvert(QVariant::String)) return; QString val = value.toString(); QString data = QString::fromLatin1("\"%1\"").arg(val); setPropertyValue(data); } QVariant PropertyFileItem::toolTip(const App::Property* prop) const { return value(prop); } QWidget* PropertyFileItem::createEditor(QWidget* parent, const QObject* receiver, const char* method) const { Gui::FileChooser *fc = new Gui::FileChooser(parent); fc->setAutoFillBackground(true); fc->setDisabled(isReadOnly()); QObject::connect(fc, SIGNAL(fileNameSelected(const QString&)), receiver, method); return fc; } void PropertyFileItem::setEditorData(QWidget *editor, const QVariant& data) const { const App::Property* prop = getFirstProperty(); if (prop) { const App::PropertyFile* propFile = static_cast(prop); std::string filter = propFile->getFilter(); Gui::FileChooser *fc = qobject_cast(editor); if (!filter.empty()) { fc->setFilter(Base::Tools::fromStdString(filter)); } fc->setFileName(data.toString()); } } QVariant PropertyFileItem::editorData(QWidget *editor) const { Gui::FileChooser *fc = qobject_cast(editor); return QVariant(fc->fileName()); } // -------------------------------------------------------------------- PROPERTYITEM_SOURCE(Gui::PropertyEditor::PropertyPathItem) PropertyPathItem::PropertyPathItem() { } QVariant PropertyPathItem::value(const App::Property* prop) const { assert(prop && prop->getTypeId().isDerivedFrom(App::PropertyPath::getClassTypeId())); std::string value = static_cast(prop)->getValue().string(); return QVariant(QString::fromUtf8(value.c_str())); } void PropertyPathItem::setValue(const QVariant& value) { if (hasExpression() || !value.canConvert(QVariant::String)) return; QString val = value.toString(); QString data = QString::fromLatin1("\"%1\"").arg(val); setPropertyValue(data); } QVariant PropertyPathItem::toolTip(const App::Property* prop) const { return value(prop); } QWidget* PropertyPathItem::createEditor(QWidget* parent, const QObject* receiver, const char* method) const { Gui::FileChooser *fc = new Gui::FileChooser(parent); fc->setMode(FileChooser::Directory); fc->setAutoFillBackground(true); fc->setDisabled(isReadOnly()); QObject::connect(fc, SIGNAL(fileNameSelected(const QString&)), receiver, method); return fc; } void PropertyPathItem::setEditorData(QWidget *editor, const QVariant& data) const { Gui::FileChooser *fc = qobject_cast(editor); fc->setFileName(data.toString()); } QVariant PropertyPathItem::editorData(QWidget *editor) const { Gui::FileChooser *fc = qobject_cast(editor); return QVariant(fc->fileName()); } // -------------------------------------------------------------------- PROPERTYITEM_SOURCE(Gui::PropertyEditor::PropertyTransientFileItem) PropertyTransientFileItem::PropertyTransientFileItem() { } QVariant PropertyTransientFileItem::value(const App::Property* prop) const { assert(prop && prop->getTypeId().isDerivedFrom(App::PropertyFileIncluded::getClassTypeId())); std::string value = static_cast(prop)->getValue(); return QVariant(QString::fromUtf8(value.c_str())); } void PropertyTransientFileItem::setValue(const QVariant& value) { if (hasExpression() || !value.canConvert(QVariant::String)) return; QString val = value.toString(); QString data = QString::fromLatin1("\"%1\"").arg(val); setPropertyValue(data); } QVariant PropertyTransientFileItem::toolTip(const App::Property* prop) const { return value(prop); } QWidget* PropertyTransientFileItem::createEditor(QWidget* parent, const QObject* receiver, const char* method) const { Gui::FileChooser *fc = new Gui::FileChooser(parent); fc->setAutoFillBackground(true); fc->setDisabled(isReadOnly()); QObject::connect(fc, SIGNAL(fileNameSelected(const QString&)), receiver, method); return fc; } void PropertyTransientFileItem::setEditorData(QWidget *editor, const QVariant& data) const { Gui::FileChooser *fc = qobject_cast(editor); fc->setFileName(data.toString()); } QVariant PropertyTransientFileItem::editorData(QWidget *editor) const { Gui::FileChooser *fc = qobject_cast(editor); return QVariant(fc->fileName()); } // --------------------------------------------------------------- LinkSelection::LinkSelection(const App::SubObjectT &link) : link(link) { } LinkSelection::~LinkSelection() { } void LinkSelection::select() { auto sobj = link.getSubObject(); if(!sobj) { QMessageBox::critical(getMainWindow(), tr("Error"), tr("Object not found")); return; } Gui::Selection().selStackPush(); Gui::Selection().clearSelection(); Gui::Selection().addSelection(link.getDocumentName().c_str(), link.getObjectName().c_str(), link.getSubName().c_str()); this->deleteLater(); } // --------------------------------------------------------------- LinkLabel::LinkLabel (QWidget * parent, const App::Property *prop) : QWidget(parent), objProp(prop), dlg(nullptr) { QHBoxLayout *layout = new QHBoxLayout(this); layout->setMargin(0); layout->setSpacing(1); label = new QLabel(this); label->setAutoFillBackground(true); label->setTextFormat(Qt::RichText); // Below is necessary for the hytperlink to be clickable without losing focus label->setTextInteractionFlags(Qt::TextBrowserInteraction); layout->addWidget(label); editButton = new QPushButton(QLatin1String("..."), this); #if defined (Q_OS_MAC) editButton->setAttribute(Qt::WA_LayoutUsesWidgetRect); // layout size from QMacStyle was not correct #endif editButton->setToolTip(tr("Change the linked object")); layout->addWidget(editButton); this->setFocusPolicy(Qt::StrongFocus); this->setFocusProxy(label); // setLayout(layout); connect(label, SIGNAL(linkActivated(const QString&)), this, SLOT(onLinkActivated(const QString&))); connect(editButton, SIGNAL(clicked()), this, SLOT(onEditClicked())); } LinkLabel::~LinkLabel() { } void LinkLabel::updatePropertyLink() { QString text; auto owner = objProp.getObject(); auto prop = Base::freecad_dynamic_cast(objProp.getProperty()); link = QVariant(); if(owner && prop) { auto links = DlgPropertyLink::getLinksFromProperty(prop); if(links.size() == 1) { auto &sobj = links.front(); link = QVariant::fromValue(sobj); QString linkcolor = QApplication::palette().color(QPalette::Link).name(); text = QString::fromLatin1( "" "

" "%5" "

" ) .arg(QLatin1String(sobj.getDocumentName().c_str()), QLatin1String(sobj.getObjectName().c_str()), QString::fromUtf8(sobj.getSubName().c_str()), linkcolor, DlgPropertyLink::formatObject( owner->getDocument(), sobj.getObject(), sobj.getSubName().c_str())); } else if (links.size()) { text = DlgPropertyLink::formatLinks(owner->getDocument(), links); } } label->setText(text); } QVariant LinkLabel::propertyLink() const { return link; } void LinkLabel::onLinkActivated (const QString& s) { Q_UNUSED(s); LinkSelection* select = new LinkSelection(qvariant_cast(link)); QTimer::singleShot(50, select, SLOT(select())); } void LinkLabel::onEditClicked () { if(!dlg) { dlg = new DlgPropertyLink(this); dlg->init(objProp,true); connect(dlg, SIGNAL(finished(int)), this, SLOT(onLinkChanged())); } else dlg->init(objProp,false); dlg->show(); } void LinkLabel::onLinkChanged() { if(dlg) { auto links = dlg->currentLinks(); if(links != dlg->originalLinks()) { link = QVariant::fromValue(links); /*emit*/ linkChanged(link); updatePropertyLink(); } } } void LinkLabel::resizeEvent(QResizeEvent* e) { editButton->setFixedWidth(e->size().height()); } // -------------------------------------------------------------------- PROPERTYITEM_SOURCE(Gui::PropertyEditor::PropertyLinkItem) PropertyLinkItem::PropertyLinkItem() { } QVariant PropertyLinkItem::toString(const QVariant& prop) const { QString res; if(propertyItems.size()) { App::DocumentObjectT owner(propertyItems[0]); res = DlgPropertyLink::formatLinks(owner.getDocument(), qvariant_cast >(prop)); } return res; } QVariant PropertyLinkItem::data(int column, int role) const { if(propertyItems.size() && column == 1 && (role == Qt::ForegroundRole || role == Qt::ToolTipRole)) { auto propLink = Base::freecad_dynamic_cast(propertyItems[0]); if(propLink) { if(role==Qt::ForegroundRole && propLink->checkRestore()>1) return QVariant::fromValue(QColor(0xff,0,0)); else if(role == Qt::ToolTipRole) { auto xlink = Base::freecad_dynamic_cast(propertyItems[0]); if(xlink) { const char *filePath = xlink->getFilePath(); if(filePath && filePath[0]) return QVariant::fromValue(QString::fromUtf8(filePath)); } } } } return PropertyItem::data(column,role); } QVariant PropertyLinkItem::value(const App::Property* prop) const { auto propLink = Base::freecad_dynamic_cast(prop); if(!propLink) return QVariant(); auto links = DlgPropertyLink::getLinksFromProperty(propLink); if(links.empty()) return QVariant(); return QVariant::fromValue(links); } void PropertyLinkItem::setValue(const QVariant& value) { auto links = qvariant_cast >(value); setPropertyValue(DlgPropertyLink::linksToPython(links)); } QWidget* PropertyLinkItem::createEditor(QWidget* parent, const QObject* receiver, const char* method) const { if(propertyItems.empty()) return nullptr; LinkLabel *ll = new LinkLabel(parent, propertyItems.front()); ll->setAutoFillBackground(true); ll->setDisabled(isReadOnly()); QObject::connect(ll, SIGNAL(linkChanged(const QVariant&)), receiver, method); return ll; } void PropertyLinkItem::setEditorData(QWidget *editor, const QVariant& data) const { (void)data; LinkLabel *ll = static_cast(editor); return ll->updatePropertyLink(); } QVariant PropertyLinkItem::editorData(QWidget *editor) const { LinkLabel *ll = static_cast(editor); return ll->propertyLink(); } // -------------------------------------------------------------------- PROPERTYITEM_SOURCE(Gui::PropertyEditor::PropertyLinkListItem) PropertyLinkListItem::PropertyLinkListItem() { } PropertyItemEditorFactory::PropertyItemEditorFactory() { } PropertyItemEditorFactory::~PropertyItemEditorFactory() { } QWidget * PropertyItemEditorFactory::createEditor (int /*type*/, QWidget * /*parent*/) const { // do not allow to create any editor widgets because we do that in subclasses of PropertyItem return nullptr; } QByteArray PropertyItemEditorFactory::valuePropertyName (int /*type*/) const { // do not allow to set properties because we do that in subclasses of PropertyItem return ""; } #include "moc_PropertyItem.cpp"