diff --git a/src/App/PropertyGeo.h b/src/App/PropertyGeo.h index f77dcf1668..99103b7b29 100644 --- a/src/App/PropertyGeo.h +++ b/src/App/PropertyGeo.h @@ -224,6 +224,9 @@ public: virtual void Paste(const Property &from) override; virtual unsigned int getMemSize (void) const override; + const char* getEditorName(void) const override { + return "Gui::PropertyEditor::PropertyVectorListItem"; + } protected: Base::Vector3d getPyValue(PyObject *) const override; diff --git a/src/Gui/CMakeLists.txt b/src/Gui/CMakeLists.txt index 02edf8df5b..b4fa291591 100644 --- a/src/Gui/CMakeLists.txt +++ b/src/Gui/CMakeLists.txt @@ -370,6 +370,7 @@ set(Gui_MOC_HDRS TreeView.h ProjectView.h View3DInventor.h + VectorListEditor.h WidgetFactory.h Widgets.h Language/Translator.h @@ -464,6 +465,7 @@ SET(Gui_UIC_SRCS TaskElementColors.ui DlgObjectSelection.ui DlgAddProperty.ui + VectorListEditor.ui ) SET(Gui_RES_SRCS @@ -541,6 +543,7 @@ SET(Dialog_CPP_SRCS TaskElementColors.cpp DlgObjectSelection.cpp DlgAddProperty.cpp + VectorListEditor.cpp ) SET(Dialog_HPP_SRCS @@ -577,6 +580,7 @@ SET(Dialog_HPP_SRCS TaskElementColors.h DlgObjectSelection.h DlgAddProperty.h + VectorListEditor.h ) SET(Dialog_SRCS @@ -615,6 +619,7 @@ SET(Dialog_SRCS TextureMapping.ui TaskElementColors.ui DlgObjectSelection.ui + VectorListEditor.ui ) SOURCE_GROUP("Dialog" FILES ${Dialog_SRCS}) diff --git a/src/Gui/MetaTypes.h b/src/Gui/MetaTypes.h index 8825c2b9c0..75047bca9c 100644 --- a/src/Gui/MetaTypes.h +++ b/src/Gui/MetaTypes.h @@ -31,6 +31,7 @@ Q_DECLARE_METATYPE(Base::Vector3f) Q_DECLARE_METATYPE(Base::Vector3d) +Q_DECLARE_METATYPE(QList) Q_DECLARE_METATYPE(Base::Matrix4D) Q_DECLARE_METATYPE(Base::Placement) Q_DECLARE_METATYPE(Base::Quantity) diff --git a/src/Gui/SoFCDB.cpp b/src/Gui/SoFCDB.cpp index e5477e9883..340f3ceae1 100644 --- a/src/Gui/SoFCDB.cpp +++ b/src/Gui/SoFCDB.cpp @@ -140,6 +140,7 @@ void Gui::SoFCDB::init() PropertyAngleItem ::init(); PropertyBoolItem ::init(); PropertyVectorItem ::init(); + PropertyVectorListItem ::init(); PropertyVectorDistanceItem ::init(); PropertyPositionItem ::init(); PropertyDirectionItem ::init(); diff --git a/src/Gui/VectorListEditor.cpp b/src/Gui/VectorListEditor.cpp new file mode 100644 index 0000000000..7d967b1705 --- /dev/null +++ b/src/Gui/VectorListEditor.cpp @@ -0,0 +1,318 @@ +/*************************************************************************** + * Copyright (c) 2020 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_ +#endif + +#include "VectorListEditor.h" +#include "ui_VectorListEditor.h" +#include + +using namespace Gui; + +VectorTableModel::VectorTableModel(int decimals, QObject *parent) + : QAbstractTableModel(parent) + , decimals(decimals) +{ +} + +QVariant VectorTableModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (role == Qt::DisplayRole && orientation == Qt::Vertical) + return section + 1; + + if (role != Qt::DisplayRole || orientation != Qt::Horizontal) + return QVariant(); + if (section == 0) + return QVariant(QLatin1Char('x')); + if (section == 1) + return QVariant(QLatin1Char('y')); + if (section == 2) + return QVariant(QLatin1Char('z')); + else + return QVariant(); +} + +int VectorTableModel::columnCount(const QModelIndex&) const +{ + return 3; +} + +int VectorTableModel::rowCount(const QModelIndex &) const +{ + return vectors.size(); +} + +Qt::ItemFlags VectorTableModel::flags (const QModelIndex & index) const +{ + Qt::ItemFlags fl = QAbstractTableModel::flags(index); + fl = fl | Qt::ItemIsEditable; + return fl; +} + +bool VectorTableModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + int r = index.row(); + int c = index.column(); + if (role == Qt::EditRole && r < vectors.size()) { + if (value.canConvert()) { + vectors[r] = value.value(); + dataChanged(index, index.sibling(index.row(), 2)); + return true; + } + else if (c < 3) { + double d = value.toDouble(); + if (c == 0) + vectors[r].x = d; + else if (c == 1) + vectors[r].y = d; + else if (c == 2) + vectors[r].z = d; + dataChanged(index, index); + return true; + } + } + return QAbstractTableModel::setData(index, value, role); +} + +QVariant VectorTableModel::data(const QModelIndex &index, int role) const +{ + if (role == Qt::DisplayRole || role == Qt::EditRole) { + int r = index.row(); + int c = index.column(); + if (r < vectors.size() && c < 3) { + double d = 0.0; + if (c == 0) + d = vectors[r].x; + else if (c == 1) + d = vectors[r].y; + else if (c == 2) + d = vectors[r].z; + + if (role == Qt::DisplayRole) { + QString str = QString::fromLatin1("%1").arg(d, 0, 'f', decimals); + return str; + } + + return d; + } + } + + return QVariant(); +} + +QModelIndex VectorTableModel::parent(const QModelIndex &) const +{ + return QModelIndex(); +} + +void VectorTableModel::setValues(const QList& d) +{ + vectors = d; + beginResetModel(); + endResetModel(); +} + +const QList& VectorTableModel::values() const +{ + return vectors; +} + +bool VectorTableModel::insertRows(int row, int count, const QModelIndex &parent) +{ + if (vectors.size() >= row) { + beginInsertRows(parent, row, row+count-1); + Base::Vector3d v; + for (int i=0; i row) { + beginRemoveRows(parent, row, row+count-1); + for (int i=0; isetDecimals(decimals); + editor->setMinimum(INT_MIN); + editor->setMaximum(INT_MAX); + editor->setSingleStep(0.1); + + return editor; +} + +void VectorTableDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const +{ + double value = index.model()->data(index, Qt::EditRole).toDouble(); + + QDoubleSpinBox *spinBox = static_cast(editor); + spinBox->setValue(value); +} + +void VectorTableDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, + const QModelIndex &index) const +{ + QDoubleSpinBox *spinBox = static_cast(editor); + spinBox->interpretText(); + double value = spinBox->value(); + model->setData(index, value, Qt::EditRole); +} + +void VectorTableDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, + const QModelIndex &/* index */) const +{ + editor->setGeometry(option.rect); +} + +// -------------------------------------------------------------- + +/* TRANSLATOR Gui::VectorListEditor */ + +VectorListEditor::VectorListEditor(int decimals, QWidget* parent) + : QDialog(parent) + , ui(new Ui_VectorListEditor) + , model(new VectorTableModel(decimals)) +{ + ui->setupUi(this); + ui->tableWidget->setItemDelegate(new VectorTableDelegate(decimals, this)); + ui->tableWidget->setModel(model); + ui->widget->hide(); + + ui->coordX->setRange(INT_MIN, INT_MAX); + ui->coordX->setDecimals(decimals); + ui->coordY->setRange(INT_MIN, INT_MAX); + ui->coordY->setDecimals(decimals); + ui->coordZ->setRange(INT_MIN, INT_MAX); + ui->coordZ->setDecimals(decimals); + + ui->toolButtonMouse->setDisabled(true); + + connect(ui->buttonBox, SIGNAL(accepted()), this, SLOT(accept())); + connect(ui->buttonBox, SIGNAL(rejected()), this, SLOT(reject())); + + connect(ui->spinBox, SIGNAL(valueChanged(int)), this, SLOT(setCurrentRow(int))); + connect(ui->toolButtonAdd, SIGNAL(clicked(bool)), this, SLOT(addRow())); + connect(ui->toolButtonRemove, SIGNAL(clicked(bool)), this, SLOT(removeRow())); + connect(ui->toolButtonAccept, SIGNAL(clicked(bool)), this, SLOT(acceptCurrent())); +} + +VectorListEditor::~VectorListEditor() +{ +} + +void VectorListEditor::setValues(const QList& v) +{ + data = v; + model->setValues(v); + if (v.isEmpty()) { + ui->spinBox->setRange(1, 1); + ui->spinBox->setEnabled(false); + ui->toolButtonRemove->setEnabled(false); + } + else { + ui->spinBox->setRange(1, v.size()); + } +} + +const QList& VectorListEditor::getValues() const +{ + return data; +} + +void VectorListEditor::accept() +{ + data = model->values(); + QDialog::accept(); +} + +void VectorListEditor::reject() +{ + QDialog::reject(); +} + +void VectorListEditor::setCurrentRow(int row) +{ + QModelIndex index = model->index(row - 1, 0); + ui->tableWidget->setCurrentIndex(index); + ui->coordX->setValue(model->data(model->index(row - 1, 0), Qt::EditRole).toDouble()); + ui->coordY->setValue(model->data(model->index(row - 1, 1), Qt::EditRole).toDouble()); + ui->coordZ->setValue(model->data(model->index(row - 1, 2), Qt::EditRole).toDouble()); +} + +void VectorListEditor::acceptCurrent() +{ + int row = ui->spinBox->value(); + double x = ui->coordX->value(); + double y = ui->coordY->value(); + double z = ui->coordZ->value(); + QVariant value = QVariant::fromValue(Base::Vector3d(x, y, z)); + model->setData(model->index(row - 1, 0), value); +} + +void VectorListEditor::addRow() +{ + model->insertRow(ui->tableWidget->currentIndex().row() + 1); + ui->spinBox->setMaximum(model->rowCount()); + ui->spinBox->setEnabled(true); + ui->toolButtonRemove->setEnabled(true); +} + +void VectorListEditor::removeRow() +{ + model->removeRow(ui->tableWidget->currentIndex().row()); + int rowCount = model->rowCount(); + if (rowCount > 0) { + ui->spinBox->setRange(1, rowCount); + } + else { + ui->spinBox->setEnabled(false); + ui->toolButtonRemove->setEnabled(false); + } +} + +#include "moc_VectorListEditor.cpp" diff --git a/src/Gui/VectorListEditor.h b/src/Gui/VectorListEditor.h new file mode 100644 index 0000000000..3db8286492 --- /dev/null +++ b/src/Gui/VectorListEditor.h @@ -0,0 +1,109 @@ +/*************************************************************************** + * Copyright (c) 2020 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 * + * * + ***************************************************************************/ + +#ifndef GUI_VECTORLISTEDITOR_H +#define GUI_VECTORLISTEDITOR_H + +#include +#include +#include +#include +#include +#include + +namespace Gui { + +class VectorTableModel : public QAbstractTableModel +{ + Q_OBJECT + +public: + VectorTableModel(int decimals, QObject *parent = nullptr); + + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; + virtual int columnCount(const QModelIndex &parent = QModelIndex()) const; + virtual int rowCount(const QModelIndex &parent = QModelIndex()) const; + Qt::ItemFlags flags (const QModelIndex & index) const; + bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); + virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + virtual QModelIndex parent(const QModelIndex &index) const; + void setValues(const QList& d); + const QList& values() const; + virtual bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex()); + virtual bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()); + +private: + QList vectors; + int decimals; +}; + +class VectorTableDelegate : public QItemDelegate +{ + Q_OBJECT + +public: + VectorTableDelegate(int decimals, QObject *parent = 0); + + QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, + const QModelIndex &index) const; + + void setEditorData(QWidget *editor, const QModelIndex &index) const; + void setModelData(QWidget *editor, QAbstractItemModel *model, + const QModelIndex &index) const; + + void updateEditorGeometry(QWidget *editor, + const QStyleOptionViewItem &option, const QModelIndex &index) const; + +private: + int decimals; +}; + +class Ui_VectorListEditor; +class VectorListEditor : public QDialog +{ + Q_OBJECT + +public: + VectorListEditor(int decimals, QWidget* parent = nullptr); + ~VectorListEditor(); + + void setValues(const QList&); + const QList& getValues() const; + + void accept(); + void reject(); + +private Q_SLOTS: + void addRow(); + void removeRow(); + void acceptCurrent(); + void setCurrentRow(int); + +private: + std::unique_ptr ui; + VectorTableModel* model; + QList data; +}; + +} // namespace Gui + +#endif // GUI_VECTORLISTEDITOR_H diff --git a/src/Gui/VectorListEditor.ui b/src/Gui/VectorListEditor.ui new file mode 100644 index 0000000000..bd6d840b91 --- /dev/null +++ b/src/Gui/VectorListEditor.ui @@ -0,0 +1,232 @@ + + + Gui::VectorListEditor + + + + 0 + 0 + 288 + 476 + + + + Vectors + + + + QLayout::SetFixedSize + + + + + + + Id: + + + + + + + 1 + + + + + + + x: + + + + + + + + + + y: + + + + + + + + + + z: + + + + + + + + + + + + Qt::Horizontal + + + + 47 + 20 + + + + + + + + + + Qt::Vertical + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + Qt::Vertical + + + + 20 + 17 + + + + + + + + Table + + + true + + + + + + + + + + + ... + + + + :/icons/mouse_pointer.svg:/icons/mouse_pointer.svg + + + + + + + ... + + + + :/icons/list-add.svg:/icons/list-add.svg + + + + + + + ... + + + + :/icons/list-remove.svg:/icons/list-remove.svg + + + + + + + ... + + + + :/icons/edit_OK.svg:/icons/edit_OK.svg + + + + + + + + + + 0 + 300 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 80 + + + + + + + + + + spinBox + coordX + coordY + coordZ + toolButtonMouse + toolButtonAdd + toolButtonRemove + toolButtonAccept + tableWidget + pushButton + + + + + + + pushButton + toggled(bool) + widget + setVisible(bool) + + + 44 + 159 + + + 51 + 249 + + + + + diff --git a/src/Gui/propertyeditor/PropertyItem.cpp b/src/Gui/propertyeditor/PropertyItem.cpp index 5ae2414d30..737090e03e 100644 --- a/src/Gui/propertyeditor/PropertyItem.cpp +++ b/src/Gui/propertyeditor/PropertyItem.cpp @@ -60,6 +60,7 @@ #include #include #include +#include #include "PropertyItem.h" #include "PropertyView.h" @@ -1425,6 +1426,119 @@ void PropertyVectorItem::propertyBound() // --------------------------------------------------------------- +VectorListButton::VectorListButton(int decimals, QWidget * parent) + : LabelButton(parent) + , decimals(decimals) +{ +} + +VectorListButton::~VectorListButton() +{ +} + +void VectorListButton::browse() +{ + VectorListEditor dlg(decimals, Gui::getMainWindow()); + dlg.setValues(value().value>()); + if (dlg.exec() == QDialog::Accepted) { + QVariant data = QVariant::fromValue>(dlg.getValues()); + setValue(data); + } +} + +void VectorListButton::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)); + } + getLabel()->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 +{ + VectorListButton *pe = new VectorListButton(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 +{ + VectorListButton *pe = qobject_cast(editor); + pe->setValue(data); +} + +QVariant PropertyVectorListItem::editorData(QWidget *editor) const +{ + VectorListButton *pe = qobject_cast(editor); + return pe->value(); +} + +// --------------------------------------------------------------- + PROPERTYITEM_SOURCE(Gui::PropertyEditor::PropertyVectorDistanceItem) PropertyVectorDistanceItem::PropertyVectorDistanceItem() diff --git a/src/Gui/propertyeditor/PropertyItem.h b/src/Gui/propertyeditor/PropertyItem.h index 95e69de707..a879bd3c6b 100644 --- a/src/Gui/propertyeditor/PropertyItem.h +++ b/src/Gui/propertyeditor/PropertyItem.h @@ -44,6 +44,7 @@ #ifdef Q_MOC_RUN Q_DECLARE_METATYPE(Base::Vector3f) Q_DECLARE_METATYPE(Base::Vector3d) +Q_DECLARE_METATYPE(QList) Q_DECLARE_METATYPE(Base::Matrix4D) Q_DECLARE_METATYPE(Base::Placement) Q_DECLARE_METATYPE(Base::Quantity) @@ -465,6 +466,44 @@ private: PropertyFloatItem* m_z; }; +class VectorListButton : public Gui::LabelButton +{ + Q_OBJECT + +public: + VectorListButton(int decimals, QWidget * parent = 0); + ~VectorListButton(); + +private: + void browse(); + void showValue(const QVariant& d); + +private: + int decimals; +}; + +/** + * Edit properties of vector list type. + * \author Werner Mayer + */ +class GuiExport PropertyVectorListItem : public PropertyItem +{ + Q_OBJECT + PROPERTYITEM_HEADER + + virtual QWidget* createEditor(QWidget* parent, const QObject* receiver, const char* method) const; + virtual void setEditorData(QWidget *editor, const QVariant& data) const; + virtual QVariant editorData(QWidget *editor) const; + +protected: + virtual QVariant toString(const QVariant&) const; + virtual QVariant value(const App::Property*) const; + virtual void setValue(const QVariant&); + +protected: + PropertyVectorListItem(); +}; + /** * Edit properties of vector type which hold distances. * \author Stefan Troeger