/*************************************************************************** * Copyright (c) 2023 David Carter * * * * 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 * * . * * * **************************************************************************/ #include "PreCompiled.h" #ifndef _PreComp_ #include #include #include #include #endif #include #include #include #include #include #include "ModelSelect.h" #include "ui_ModelSelect.h" using namespace MatGui; /* TRANSLATOR MatGui::ModelSelect */ ModelSelect::ModelSelect(QWidget* parent, Materials::ModelFilter filter) : QDialog(parent) , _filter(filter) , ui(new Ui_ModelSelect) { ui->setupUi(this); getFavorites(); getRecents(); createModelTree(); createModelProperties(); ui->buttonURL->setIcon(QIcon(QString::fromStdString(":/icons/internet-web-browser.svg"))); ui->buttonDOI->setIcon(QIcon(QString::fromStdString(":/icons/internet-web-browser.svg"))); connect(ui->standardButtons, &QDialogButtonBox::accepted, this, &ModelSelect::accept); connect(ui->standardButtons, &QDialogButtonBox::rejected, this, &ModelSelect::reject); QItemSelectionModel* selectionModel = ui->treeModels->selectionModel(); connect(selectionModel, &QItemSelectionModel::selectionChanged, this, &ModelSelect::onSelectModel); connect(ui->buttonURL, &QPushButton::clicked, this, &ModelSelect::onURL); connect(ui->buttonDOI, &QPushButton::clicked, this, &ModelSelect::onDOI); connect(ui->buttonFavorite, &QPushButton::clicked, this, &ModelSelect::onFavourite); connect(ui->treeModels, &QTreeView::doubleClicked, this, &ModelSelect::onDoubleClick); ui->standardButtons->button(QDialogButtonBox::Ok)->setEnabled(false); ui->buttonFavorite->setEnabled(false); } void ModelSelect::getFavorites() { _favorites.clear(); auto param = App::GetApplication().GetParameterGroupByPath( "User parameter:BaseApp/Preferences/Mod/Material/Models/Favorites"); int count = param->GetInt("Favorites", 0); for (int i = 0; i < count; i++) { QString key = QString::fromLatin1("FAV%1").arg(i); QString uuid = QString::fromStdString(param->GetASCII(key.toStdString().c_str(), "")); _favorites.push_back(uuid); } } void ModelSelect::saveFavorites() { auto param = App::GetApplication().GetParameterGroupByPath( "User parameter:BaseApp/Preferences/Mod/Material/Models/Favorites"); // Clear out the existing favorites int count = param->GetInt("Favorites", 0); for (int i = 0; i < count; i++) { QString key = QString::fromLatin1("FAV%1").arg(i); param->RemoveASCII(key.toStdString().c_str()); } // Add the current values param->SetInt("Favorites", _favorites.size()); int j = 0; for (auto& favorite : _favorites) { QString key = QString::fromLatin1("FAV%1").arg(j); param->SetASCII(key.toStdString().c_str(), favorite.toStdString()); j++; } } void ModelSelect::addFavorite(const QString& uuid) { if (!isFavorite(uuid)) { _favorites.push_back(uuid); saveFavorites(); refreshModelTree(); } } void ModelSelect::removeFavorite(const QString& uuid) { if (isFavorite(uuid)) { _favorites.remove(uuid); saveFavorites(); refreshModelTree(); } } bool ModelSelect::isFavorite(const QString& uuid) const { for (auto& it : _favorites) { if (it == uuid) { return true; } } return false; } void ModelSelect::getRecents() { _recents.clear(); auto param = App::GetApplication().GetParameterGroupByPath( "User parameter:BaseApp/Preferences/Mod/Material/Models/Recent"); _recentMax = param->GetInt("RecentMax", 5); int count = param->GetInt("Recent", 0); for (int i = 0; i < count; i++) { QString key = QString::fromLatin1("MRU%1").arg(i); QString uuid = QString::fromStdString(param->GetASCII(key.toStdString().c_str(), "")); _recents.push_back(uuid); } } void ModelSelect::saveRecents() { auto param = App::GetApplication().GetParameterGroupByPath( "User parameter:BaseApp/Preferences/Mod/Material/Models/Recent"); // Clear out the existing favorites int count = param->GetInt("Recent", 0); for (int i = 0; i < count; i++) { QString key = QString::fromLatin1("MRU%1").arg(i); param->RemoveASCII(key.toStdString().c_str()); } // Add the current values int size = _recents.size(); if (size > _recentMax) { size = _recentMax; } param->SetInt("Recent", size); int j = 0; for (auto& recent : _recents) { QString key = QString::fromLatin1("MRU%1").arg(j); param->SetASCII(key.toStdString().c_str(), recent.toStdString()); j++; if (j >= size) { break; } } } void ModelSelect::addRecent(const QString& uuid) { // Ensure no duplicates if (isRecent(uuid)) { _recents.remove(uuid); } _recents.push_front(uuid); while (_recents.size() > static_cast(_recentMax)) { _recents.pop_back(); } saveRecents(); } bool ModelSelect::isRecent(const QString& uuid) const { for (auto& it : _recents) { if (it == uuid) { return true; } } return false; } /* * Destroys the object and frees any allocated resources */ ModelSelect::~ModelSelect() { // no need to delete child widgets, Qt does it all for us } void ModelSelect::addExpanded(QTreeView* tree, QStandardItem* parent, QStandardItem* child) { parent->appendRow(child); tree->setExpanded(child->index(), true); } void ModelSelect::addExpanded(QTreeView* tree, QStandardItemModel* parent, QStandardItem* child) { parent->appendRow(child); tree->setExpanded(child->index(), true); } void ModelSelect::addModels( QStandardItem& parent, const std::shared_ptr>> modelTree, const QIcon& icon) { auto tree = ui->treeModels; for (auto& mod : *modelTree) { std::shared_ptr nodePtr = mod.second; if (nodePtr->getType() == Materials::ModelTreeNode::DataNode) { auto model = nodePtr->getData(); QString uuid = model->getUUID(); auto card = new QStandardItem(icon, model->getName()); card->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled); card->setData(QVariant(uuid), Qt::UserRole); addExpanded(tree, &parent, card); } else { auto node = new QStandardItem(mod.first); addExpanded(tree, &parent, node); node->setFlags(Qt::ItemIsEnabled | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled); auto treeMap = nodePtr->getFolder(); addModels(*node, treeMap, icon); } } } void ModelSelect::addRecents(QStandardItem* parent) { auto tree = ui->treeModels; for (auto& uuid : _recents) { try { auto model = getModelManager().getModel(uuid); if (getModelManager().passFilter(_filter, model->getType())) { QIcon icon = QIcon(model->getLibrary()->getIconPath()); auto card = new QStandardItem(icon, model->getName()); card->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled); card->setData(QVariant(uuid), Qt::UserRole); addExpanded(tree, parent, card); } } catch (const Materials::ModelNotFound&) { } } } void ModelSelect::addFavorites(QStandardItem* parent) { auto tree = ui->treeModels; for (auto& uuid : _favorites) { try { auto model = getModelManager().getModel(uuid); if (getModelManager().passFilter(_filter, model->getType())) { QIcon icon = QIcon(model->getLibrary()->getIconPath()); auto card = new QStandardItem(icon, model->getName()); card->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled); card->setData(QVariant(uuid), Qt::UserRole); addExpanded(tree, parent, card); } } catch (const Materials::ModelNotFound&) { } } } void ModelSelect::createModelTree() { auto tree = ui->treeModels; auto model = new QStandardItemModel(); tree->setModel(model); tree->setHeaderHidden(true); fillTree(); } void ModelSelect::refreshModelTree() { auto tree = ui->treeModels; auto model = static_cast(tree->model()); model->clear(); fillTree(); } void ModelSelect::fillTree() { auto tree = ui->treeModels; auto model = static_cast(tree->model()); model->clear(); auto lib = new QStandardItem(tr("Favorites")); lib->setFlags(Qt::ItemIsEnabled | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled); addExpanded(tree, model, lib); addFavorites(lib); lib = new QStandardItem(tr("Recent")); lib->setFlags(Qt::ItemIsEnabled | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled); addExpanded(tree, model, lib); addRecents(lib); auto libraries = getModelManager().getModelLibraries(); for (auto& library : *libraries) { lib = new QStandardItem(library->getName()); lib->setFlags(Qt::ItemIsEnabled | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled); addExpanded(tree, model, lib); auto modelTree = getModelManager().getModelTree(library, _filter); addModels(*lib, modelTree, QIcon(library->getIconPath())); } } void ModelSelect::setHeaders(QStandardItemModel* model) { QStringList headers; headers.append(tr("Inherited")); headers.append(tr("Property")); headers.append(tr("Units")); headers.append(tr("Description")); headers.append(tr("URL")); model->setHorizontalHeaderLabels(headers); } void ModelSelect::setColumnWidths(QTableView* table) { table->setColumnWidth(0, 75); table->setColumnWidth(1, 200); table->setColumnWidth(2, 200); table->setColumnWidth(3, 200); table->setColumnWidth(4, 200); } void ModelSelect::createModelProperties() { auto table = ui->tableProperties; auto model = new QStandardItemModel(); table->setModel(model); table->setEditTriggers(QAbstractItemView::NoEditTriggers); setHeaders(model); setColumnWidths(table); // table->setHeaderHidden(false); // table->setUniformRowHeights(true); // table->setItemDelegate(new MaterialDelegate(this)); } void ModelSelect::updateModelProperties(std::shared_ptr model) { QTableView* table = ui->tableProperties; QStandardItemModel* tableModel = static_cast(table->model()); tableModel->clear(); setHeaders(tableModel); setColumnWidths(table); for (auto itp = model->begin(); itp != model->end(); itp++) { QList items; QString key = itp->first; const Materials::ModelProperty modelProperty = static_cast(itp->second); auto inherited = new QStandardItem(QString::fromStdString(modelProperty.isInherited() ? "*" : "")); // inherited->setToolTip(QString::fromStdString(modelProperty.getDescription())); items.append(inherited); auto propertyItem = new QStandardItem(key); items.append(propertyItem); auto unitsItem = new QStandardItem(modelProperty.getUnits()); items.append(unitsItem); auto descriptionItem = new QStandardItem(modelProperty.getDescription()); items.append(descriptionItem); auto urlItem = new QStandardItem(modelProperty.getURL()); items.append(urlItem); // addExpanded(tree, modelRoot, propertyItem); tableModel->appendRow(items); } } void ModelSelect::updateMaterialModel(const QString& uuid) { auto model = getModelManager().getModel(uuid); // Update the general information ui->editName->setText(model->getName()); ui->editURL->setText(model->getURL()); ui->editDOI->setText(model->getDOI()); ui->editDescription->setText(model->getDescription()); if (model->getType() == Materials::Model::ModelType_Physical) { ui->tabWidget->setTabText(1, tr("Properties")); } else { ui->tabWidget->setTabText(1, tr("Appearance")); } updateModelProperties(model); } void ModelSelect::clearMaterialModel() { // Update the general information ui->editName->setText(QString::fromStdString("")); ui->editURL->setText(QString::fromStdString("")); ui->editDOI->setText(QString::fromStdString("")); ui->editDescription->setText(QString::fromStdString("")); ui->tabWidget->setTabText(1, tr("Properties")); QTableView* table = ui->tableProperties; QStandardItemModel* tableModel = static_cast(table->model()); tableModel->clear(); setHeaders(tableModel); setColumnWidths(table); } void ModelSelect::onSelectModel(const QItemSelection& selected, const QItemSelection& deselected) { Q_UNUSED(deselected); Base::Console().Log("ModelSelect::onSelectModel()\n"); QStandardItemModel* model = static_cast(ui->treeModels->model()); QModelIndexList indexes = selected.indexes(); for (auto it = indexes.begin(); it != indexes.end(); it++) { QStandardItem* item = model->itemFromIndex(*it); Base::Console().Log("%s\n", item->text().toStdString().c_str()); if (item) { try { _selected = item->data(Qt::UserRole).toString(); Base::Console().Log("\t%s\n", _selected.toStdString().c_str()); updateMaterialModel(_selected); ui->standardButtons->button(QDialogButtonBox::Ok)->setEnabled(true); ui->buttonFavorite->setEnabled(true); } catch (const std::exception& e) { _selected = QString::fromStdString(""); Base::Console().Log("Error %s\n", e.what()); clearMaterialModel(); ui->standardButtons->button(QDialogButtonBox::Ok)->setEnabled(false); ui->buttonFavorite->setEnabled(false); } } } } void ModelSelect::onDoubleClick(const QModelIndex& index) { Q_UNUSED(index) Base::Console().Log("ModelSelect::onDoubleClick()\n"); accept(); } void ModelSelect::onURL(bool checked) { Q_UNUSED(checked) Base::Console().Log("URL\n"); QString url = ui->editURL->text(); if (url.length() > 0) { QDesktopServices::openUrl(QUrl(url, QUrl::TolerantMode)); } } void ModelSelect::onDOI(bool checked) { Q_UNUSED(checked) Base::Console().Log("DOI\n"); QString url = QString::fromStdString("https://doi.org/") + ui->editDOI->text(); if (url.length() > 0) { QDesktopServices::openUrl(QUrl(url, QUrl::TolerantMode)); } } void ModelSelect::onFavourite(bool checked) { Q_UNUSED(checked) Base::Console().Log("Favorite\n"); if (isFavorite(_selected)) { removeFavorite(_selected); } else { addFavorite(_selected); } } void ModelSelect::accept() { addRecent(_selected); QDialog::accept(); } void ModelSelect::reject() { QDialog::reject(); } #include "moc_ModelSelect.cpp"