FreeCAD / src /Mod /Material /Gui /MaterialDelegate.cpp
AbdulElahGwaith's picture
Upload folder using huggingface_hub
985c397 verified
// SPDX-License-Identifier: LGPL-2.1-or-later
/***************************************************************************
* Copyright (c) 2023 David Carter <dcarter@david.carter.ca> *
* *
* This file is part of FreeCAD. *
* *
* FreeCAD is free software: you can redistribute it and/or modify it *
* under the terms of the GNU Lesser General Public License as *
* published by the Free Software Foundation, either version 2.1 of the *
* License, or (at your option) any later version. *
* *
* FreeCAD is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with FreeCAD. If not, see *
* <https://www.gnu.org/licenses/>. *
* *
**************************************************************************/
#include <QColorDialog>
#include <QDesktopServices>
#include <QIODevice>
#include <QItemSelectionModel>
#include <QMessageBox>
#include <QPainter>
#include <QString>
#include <QStringList>
#include <QSvgRenderer>
#include <QTextStream>
#include <QVariant>
#include <limits>
#include <App/Application.h>
#include <Base/Interpreter.h>
#include <Base/Quantity.h>
#include <Gui/Application.h>
#include <Gui/Command.h>
#include <Gui/InputField.h>
#include <Gui/PrefWidgets.h>
#include <Gui/SpinBox.h>
#include <Gui/WaitCursor.h>
#include <Mod/Material/App/Exceptions.h>
#include <Mod/Material/App/ModelManager.h>
#include "Array2D.h"
#include "Array3D.h"
#include "ImageEdit.h"
#include "ListEdit.h"
#include "MaterialDelegate.h"
#include "MaterialSave.h"
#include "TextEdit.h"
using namespace MatGui;
MaterialDelegate::MaterialDelegate(QObject* parent)
: BaseDelegate(parent)
{}
bool MaterialDelegate::newRow(const QAbstractItemModel* model, const QModelIndex& index) const
{
Q_UNUSED(model)
Q_UNUSED(index)
// New rows are for lists and arrays
return false;
}
Materials::MaterialValue::ValueType MaterialDelegate::getType(const QModelIndex& index) const
{
auto treeModel = dynamic_cast<const QStandardItemModel*>(index.model());
auto item = treeModel->itemFromIndex(index);
auto group = item->parent();
if (!group) {
return {};
}
int row = index.row();
QString propertyType;
if (group->child(row, 1)) {
propertyType = group->child(row, 2)->text();
}
return Materials::MaterialValue::mapType(propertyType);
}
QString MaterialDelegate::getUnits(const QModelIndex& index) const
{
auto treeModel = dynamic_cast<const QStandardItemModel*>(index.model());
auto item = treeModel->itemFromIndex(index);
auto group = item->parent();
if (!group) {
return {};
}
int row = index.row();
QString propertyUnits;
if (group->child(row, 1)) {
propertyUnits = group->child(row, 3)->text();
}
return propertyUnits;
}
QVariant MaterialDelegate::getValue(const QModelIndex& index) const
{
auto treeModel = dynamic_cast<const QStandardItemModel*>(index.model());
auto item = treeModel->itemFromIndex(index);
auto group = item->parent();
if (!group) {
return {};
}
int row = index.row();
QVariant propertyValue;
if (group->child(row, 1)) {
auto material = group->child(row, 1)->data().value<std::shared_ptr<Materials::Material>>();
// auto propertyName = group->child(row, 0)->text();
auto propertyName = group->child(row, 0)->data().toString();
propertyValue = material->getProperty(propertyName)->getValue();
}
return propertyValue;
}
void MaterialDelegate::setValue(QAbstractItemModel* model,
const QModelIndex& index,
const QVariant& value) const
{
auto matModel = dynamic_cast<const QStandardItemModel*>(model);
auto item = matModel->itemFromIndex(index);
auto group = item->parent();
if (!group) {
return;
}
int row = index.row();
if (group->child(row, 1)) {
auto material = group->child(row, 1)->data().value<std::shared_ptr<Materials::Material>>();
auto propertyName = group->child(row, 0)->data().toString();
std::string _name = propertyName.toStdString();
auto property = material->getProperty(propertyName);
try {
property->setValue(value);
}
catch (const Base::ValueError&) {
// Units mismatch
auto quantity = value.value<Base::Quantity>();
Base::Console().log("Units mismatch '%s' = '%s', "
"setting to default property units '%s'\n",
propertyName.toStdString().c_str(),
quantity.getUserString().c_str(),
property->getUnits().toStdString().c_str());
QMessageBox msgBox;
msgBox.setWindowTitle(QStringLiteral("Property Units Mismatch"));
msgBox.setText(QStringLiteral("Units mismatch '%1' = '%2', "
"setting to default property units '%3'\n")
.arg(propertyName)
.arg(QString::fromStdString(quantity.getUserString()))
.arg(property->getUnits()));
msgBox.exec();
property->setQuantity(
Base::Quantity(quantity.getValue(), property->getUnits().toStdString()));
}
group->child(row, 1)->setText(property->getString());
}
notifyChanged(model, index);
}
void MaterialDelegate::notifyChanged(const QAbstractItemModel* model,
const QModelIndex& index) const
{
auto treeModel = dynamic_cast<const QStandardItemModel*>(model);
auto item = treeModel->itemFromIndex(index);
auto group = item->parent();
if (!group) {
return;
}
int row = index.row();
if (group->child(row, 1)) {
auto material = group->child(row, 1)->data().value<std::shared_ptr<Materials::Material>>();
// auto propertyName = group->child(row, 0)->text();
auto propertyName = group->child(row, 0)->data().toString();
auto propertyValue = material->getProperty(propertyName)->getValue();
material->setEditStateAlter();
Q_EMIT const_cast<MaterialDelegate*>(this)->propertyChange(propertyName, propertyValue);
}
}
bool MaterialDelegate::editorEvent(QEvent* event,
QAbstractItemModel* model,
const QStyleOptionViewItem& option,
const QModelIndex& index)
{
if (index.column() == 1) {
if (event->type() == QEvent::MouseButtonDblClick) {
auto treeModel = dynamic_cast<const QStandardItemModel*>(index.model());
// Check we're not the material model root. This is also used to access the entry
// columns
auto item = treeModel->itemFromIndex(index);
auto group = item->parent();
if (!group) {
return QStyledItemDelegate::editorEvent(event, model, option, index);
}
int row = index.row();
// QString propertyName = group->child(row, 0)->text();
QString propertyName = group->child(row, 0)->data().toString();
auto type = getType(index);
if (type == Materials::MaterialValue::Color) {
showColorModal(propertyName, item);
// Mark as handled
return true;
}
if (type == Materials::MaterialValue::MultiLineString) {
showMultiLineStringModal(propertyName, item);
// Mark as handled
return true;
}
if (type == Materials::MaterialValue::List || type == Materials::MaterialValue::FileList
|| type == Materials::MaterialValue::ImageList) {
showListModal(propertyName, item);
// Mark as handled
return true;
}
if (type == Materials::MaterialValue::Array2D) {
showArray2DModal(propertyName, item);
// Mark as handled
return true;
}
if (type == Materials::MaterialValue::Array3D) {
showArray3DModal(propertyName, item);
// Mark as handled
return true;
}
if (type == Materials::MaterialValue::Image || type == Materials::MaterialValue::SVG) {
showImageModal(propertyName, item);
// Mark as handled
return true;
}
}
}
return QStyledItemDelegate::editorEvent(event, model, option, index);
}
void MaterialDelegate::showColorModal(const QString& propertyName, QStandardItem* item)
{
auto currentColor = parseColor(item->text()).asValue<QColor>();
auto dlg = new QColorDialog(currentColor);
dlg->setAttribute(Qt::WA_DeleteOnClose);
if (Gui::DialogOptions::dontUseNativeColorDialog()) {
dlg->setOptions(QColorDialog::DontUseNativeDialog);
}
dlg->setOption(QColorDialog::ColorDialogOption::ShowAlphaChannel, false);
dlg->setCurrentColor(currentColor);
dlg->adjustSize();
connect(dlg, &QColorDialog::finished, this, [&](int result) {
if (result == QDialog::Accepted) {
QColor color = dlg->selectedColor();
if (color.isValid()) {
QString colorText = QString(QStringLiteral("(%1,%2,%3,%4)"))
.arg(color.red() / 255.0)
.arg(color.green() / 255.0)
.arg(color.blue() / 255.0)
.arg(color.alpha() / 255.0);
item->setText(colorText);
Q_EMIT const_cast<MaterialDelegate*>(this)->propertyChange(propertyName,
item->text());
}
}
});
dlg->exec();
}
void MaterialDelegate::showImageModal(const QString& propertyName, QStandardItem* item)
{
auto material = item->data().value<std::shared_ptr<Materials::Material>>();
auto dlg = new ImageEdit(propertyName, material);
dlg->setAttribute(Qt::WA_DeleteOnClose);
dlg->adjustSize();
// connect(dlg, &QDialog::finished, this, [&](int result) {});
dlg->exec();
}
void MaterialDelegate::showListModal(const QString& propertyName, QStandardItem* item)
{
auto material = item->data().value<std::shared_ptr<Materials::Material>>();
auto dlg = new ListEdit(propertyName, material);
dlg->setAttribute(Qt::WA_DeleteOnClose);
dlg->adjustSize();
// connect(dlg, &QDialog::finished, this, [&](int result) {});
dlg->exec();
}
void MaterialDelegate::showMultiLineStringModal(const QString& propertyName, QStandardItem* item)
{
auto material = item->data().value<std::shared_ptr<Materials::Material>>();
auto dlg = new TextEdit(propertyName, material);
dlg->setAttribute(Qt::WA_DeleteOnClose);
dlg->adjustSize();
// connect(dlg, &QDialog::finished, this, [&](int result) {});
dlg->exec();
}
void MaterialDelegate::showArray2DModal(const QString& propertyName, QStandardItem* item)
{
auto material = item->data().value<std::shared_ptr<Materials::Material>>();
auto dlg = new Array2D(propertyName, material);
dlg->setAttribute(Qt::WA_DeleteOnClose);
dlg->adjustSize();
// connect(dlg, &QDialog::finished, this, [&](int result) {});
dlg->exec();
}
void MaterialDelegate::showArray3DModal(const QString& propertyName, QStandardItem* item)
{
auto material = item->data().value<std::shared_ptr<Materials::Material>>();
auto dlg = new Array3D(propertyName, material);
dlg->setAttribute(Qt::WA_DeleteOnClose);
dlg->adjustSize();
// connect(dlg, &QDialog::finished, this, [&](int result) {});
dlg->exec();
}
void MaterialDelegate::paint(QPainter* painter,
const QStyleOptionViewItem& option,
const QModelIndex& index) const
{
if (index.column() != 1) {
QStyledItemDelegate::paint(painter, option, index);
return;
}
auto treeModel = dynamic_cast<const QStandardItemModel*>(index.model());
// Check we're not the material model root. This is also used to access the entry columns
auto item = treeModel->itemFromIndex(index);
auto group = item->parent();
if (!group) {
QStyledItemDelegate::paint(painter, option, index);
return;
}
auto type = getType(index);
if (type == Materials::MaterialValue::Quantity) {
paintQuantity(painter, option, index);
return;
}
if (type == Materials::MaterialValue::Image) {
paintImage(painter, option, index);
return;
}
if (type == Materials::MaterialValue::SVG) {
paintSVG(painter, option, index);
return;
}
if (type == Materials::MaterialValue::Color) {
paintColor(painter, option, index);
return;
}
if (type == Materials::MaterialValue::List || type == Materials::MaterialValue::FileList
|| type == Materials::MaterialValue::ImageList) {
paintList(painter, option, index);
return;
}
if (type == Materials::MaterialValue::MultiLineString) {
paintMultiLineString(painter, option, index);
return;
}
if (type == Materials::MaterialValue::Array2D || type == Materials::MaterialValue::Array3D) {
paintArray(painter, option, index);
return;
}
QStyledItemDelegate::paint(painter, option, index);
}
QWidget* MaterialDelegate::createEditor(QWidget* parent,
const QStyleOptionViewItem& styleOption,
const QModelIndex& index) const
{
Q_UNUSED(styleOption)
if (index.column() != 1) {
return nullptr;
}
auto treeModel = dynamic_cast<const QStandardItemModel*>(index.model());
// Check we're not the material model root. This is also used to access the entry columns
auto item = treeModel->itemFromIndex(index);
auto group = item->parent();
if (!group) {
return nullptr;
}
QVariant value = treeModel->data(index);
QWidget* editor = createWidget(parent, value, index);
return editor;
}
QWidget* MaterialDelegate::createWidget(QWidget* parent,
const QVariant& item,
const QModelIndex& index) const
{
QWidget* widget = nullptr;
auto type = getType(index);
if (type == Materials::MaterialValue::Integer) {
auto spinner = new Gui::IntSpinBox(parent);
spinner->setMinimum(0);
spinner->setMaximum(std::numeric_limits<int>::max());
spinner->setValue(item.toInt());
widget = spinner;
}
else if (type == Materials::MaterialValue::Float) {
auto spinner = new Gui::DoubleSpinBox(parent);
// the magnetic permeability is the parameter for which many decimals matter
// the most however, even for this, 6 digits are sufficient
spinner->setDecimals(6);
// for almost all Float parameters of materials a step of 1 would be too large
spinner->setSingleStep(0.1);
spinner->setMinimum(std::numeric_limits<double>::min());
spinner->setMaximum(std::numeric_limits<double>::max());
spinner->setValue(item.toDouble());
widget = spinner;
}
else if (type == Materials::MaterialValue::Boolean) {
auto combo = new Gui::PrefComboBox(parent);
combo->insertItem(0, QStringLiteral(""));
combo->insertItem(1, tr("False"));
combo->insertItem(2, tr("True"));
combo->setCurrentText(item.toString());
widget = combo;
}
else if (type == Materials::MaterialValue::Quantity) {
// auto input = new Gui::InputField(parent);
auto input = new Gui::QuantitySpinBox(parent);
input->setMinimum(std::numeric_limits<double>::min());
input->setMaximum(std::numeric_limits<double>::max());
input->setUnitText(getUnits(index));
// input->setPrecision(6);
input->setValue(item.value<Base::Quantity>());
widget = input;
}
else if (type == Materials::MaterialValue::File) {
auto chooser = new Gui::FileChooser(parent);
if (!item.toString().isEmpty()) {
chooser->setFileName(item.toString());
}
widget = chooser;
}
else {
// Default editor
auto lineEdit = new Gui::PrefLineEdit(parent);
lineEdit->setText(item.toString());
widget = lineEdit;
}
return widget;
}
#include "moc_MaterialDelegate.cpp"