Files
IfcOpenShell/src/bonsaiviewer/modules/models/FederationItemModel.cpp
T
Dion Moult 8dfe00cdf8 Improve viewer variable names
Rename short local variables and parameters in the viewer loading, sidecar, and BonsaiViewer command paths to make their responsibilities clearer.\n\nGenerated with the assistance of an AI coding tool.
2026-07-03 19:20:50 +10:00

275 lines
12 KiB
C++

// This file was generated with the assistance of an AI coding tool.
/********************************************************************************
* *
* This file is part of IfcOpenShell. *
* *
* IfcOpenShell is free software: you can redistribute it and/or modify *
* it under the terms of the Lesser GNU General Public License as published by *
* the Free Software Foundation, either version 3.0 of the License, or *
* (at your option) any later version. *
* *
* IfcOpenShell 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 *
* Lesser GNU General Public License for more details. *
* *
* You should have received a copy of the Lesser GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
#include "FederationItemModel.h"
#include "../../ViewerSettings.h"
#include "../../components/SvgIcon.h"
#include "../../../ifcviewer/Federation.h"
#include <QBrush>
#include <QColor>
namespace bonsaiviewer::modules::models {
namespace {
QStandardItem* siblingVisibilityItem(QStandardItem* name_item) {
QStandardItem* parent = name_item->parent();
if (!parent) parent = name_item->model()->invisibleRootItem();
return parent->child(name_item->row(), 1);
}
template <typename F>
void walkSubtree(QStandardItem* root, F visit) {
visit(root);
for (int i = 0; i < root->rowCount(); ++i) {
walkSubtree(root->child(i, 0), visit);
}
}
} // namespace
FederationItemModel::FederationItemModel(Federation* federation, QObject* parent)
: QStandardItemModel(parent)
, federation_(federation)
{
setColumnCount(2);
rebuildAll();
connect(federation_, &Federation::groupAdded, this, &FederationItemModel::onGroupAdded);
connect(federation_, &Federation::groupRemoved, this, &FederationItemModel::onGroupRemoved);
connect(federation_, &Federation::groupChanged, this, &FederationItemModel::onGroupChanged);
connect(federation_, &Federation::groupVisibilityChanged, this, &FederationItemModel::onGroupVisibilityChanged);
connect(federation_, &Federation::modelAdded, this, &FederationItemModel::onModelAdded);
connect(federation_, &Federation::modelRemoved, this, &FederationItemModel::onModelRemoved);
connect(federation_, &Federation::modelVisibilityChanged, this, &FederationItemModel::onModelVisibilityChanged);
connect(federation_, &Federation::modelGroupChanged, this, &FederationItemModel::onModelGroupChanged);
connect(federation_, &Federation::modelChanged, this, &FederationItemModel::onModelChanged);
}
void FederationItemModel::rebuildAll() {
clear();
setColumnCount(2);
id_to_name_item_.clear();
for (const auto& root_group : federation_->rootGroups()) {
appendGroupSubtreeTo(invisibleRootItem(), root_group->id);
}
for (const auto& model : federation_->models()) {
if (!model.group_id.isEmpty()) continue;
appendModelTo(invisibleRootItem(), model.id);
}
}
QStandardItem* FederationItemModel::makeGroupNameItem(const QString& group_id, const QString& display_name) const {
auto* item = new QStandardItem(components::icons::makeSvgIcon(":/icons/folder.svg"), display_name);
item->setData(group_id, IdRole);
item->setData(int(ItemKind::Group), KindRole);
item->setEditable(false);
return item;
}
QStandardItem* FederationItemModel::makeModelNameItem(const QString& fed_id, const QString& display_name) const {
auto* item = new QStandardItem(components::icons::makeSvgIcon(":/icons/cube.svg"), display_name);
item->setData(fed_id, IdRole);
item->setData(int(ItemKind::Model), KindRole);
item->setEditable(false);
return item;
}
QStandardItem* FederationItemModel::makeVisibilityItem(ItemKind kind, bool visible) const {
QString icon_path;
if (kind == ItemKind::Group) {
icon_path = visible ? ":/icons/eye.svg" : ":/icons/eye-closed.svg";
} else {
icon_path = visible ? ":/icons/eye-solid.svg" : ":/icons/eye-closed.svg";
}
auto* item = new QStandardItem(components::icons::makeSvgIcon(icon_path), QString());
item->setEditable(false);
return item;
}
void FederationItemModel::styleRowVisibility(QStandardItem* name_item, bool visible) const {
QStandardItem* vis_item = siblingVisibilityItem(name_item);
if (visible) {
name_item->setData(QVariant(), Qt::ForegroundRole);
if (vis_item) vis_item->setData(QVariant(), Qt::ForegroundRole);
} else {
const QBrush disabled(QColor(bonsaiviewer::ViewerSettings::instance().color("disabled_text")));
name_item->setForeground(disabled);
if (vis_item) vis_item->setForeground(disabled);
}
}
QStandardItem* FederationItemModel::findItem(const QString& id) const {
return id_to_name_item_.value(id, nullptr);
}
QStandardItem* FederationItemModel::parentItemForGroup(const QString& parent_group_id) const {
if (parent_group_id.isEmpty()) return invisibleRootItem();
QStandardItem* found = findItem(parent_group_id);
return found ? found : invisibleRootItem();
}
void FederationItemModel::appendModelTo(QStandardItem* parent_item, const QString& fed_id) {
const Federation::Model* model = federation_->findById(fed_id);
if (!model) return;
auto* name_item = makeModelNameItem(fed_id, model->display_name);
auto* vis_item = makeVisibilityItem(ItemKind::Model, federation_->isModelEffectivelyVisible(fed_id));
parent_item->appendRow({name_item, vis_item});
id_to_name_item_.insert(fed_id, name_item);
styleRowVisibility(name_item, federation_->isModelEffectivelyVisible(fed_id));
}
void FederationItemModel::appendGroupSubtreeTo(QStandardItem* parent_item, const QString& group_id) {
const Federation::Group* group = federation_->findGroupById(group_id);
if (!group) return;
auto* name_item = makeGroupNameItem(group_id, group->display_name);
auto* vis_item = makeVisibilityItem(ItemKind::Group, group->visible);
parent_item->appendRow({name_item, vis_item});
id_to_name_item_.insert(group_id, name_item);
styleRowVisibility(name_item, group->visible);
for (const auto& child : group->children) {
appendGroupSubtreeTo(name_item, child->id);
}
for (const auto& model : federation_->models()) {
if (model.group_id != group_id) continue;
appendModelTo(name_item, model.id);
}
}
void FederationItemModel::refreshSubtreeVisibility(QStandardItem* root) {
walkSubtree(root, [this](QStandardItem* item) {
const QString id = item->data(IdRole).toString();
if (id.isEmpty()) return;
const auto kind = static_cast<ItemKind>(item->data(KindRole).toInt());
bool visible = true;
if (kind == ItemKind::Group) {
const Federation::Group* group = federation_->findGroupById(id);
visible = group && group->visible;
} else {
visible = federation_->isModelEffectivelyVisible(id);
}
QStandardItem* vis_item = siblingVisibilityItem(item);
if (vis_item) {
const QString icon_path = (kind == ItemKind::Group)
? (visible ? ":/icons/eye.svg" : ":/icons/eye-closed.svg")
: (visible ? ":/icons/eye-solid.svg" : ":/icons/eye-closed.svg");
vis_item->setIcon(components::icons::makeSvgIcon(icon_path));
}
styleRowVisibility(item, visible);
});
}
void FederationItemModel::onGroupAdded(const QString& group_id) {
const Federation::Group* group = federation_->findGroupById(group_id);
if (!group) return;
QStandardItem* parent_item = parentItemForGroup(group->parent ? group->parent->id : QString());
appendGroupSubtreeTo(parent_item, group_id);
}
void FederationItemModel::onGroupRemoved(const QString& group_id) {
QStandardItem* item = findItem(group_id);
if (!item) return;
walkSubtree(item, [this](QStandardItem* descendant) {
const QString id = descendant->data(IdRole).toString();
if (!id.isEmpty()) id_to_name_item_.remove(id);
});
QStandardItem* parent_item = item->parent();
if (!parent_item) parent_item = invisibleRootItem();
parent_item->removeRow(item->row());
}
void FederationItemModel::onGroupChanged(const QString& group_id) {
QStandardItem* item = findItem(group_id);
if (!item) return;
const Federation::Group* group = federation_->findGroupById(group_id);
if (!group) return;
QStandardItem* current_parent = item->parent();
if (!current_parent) current_parent = invisibleRootItem();
QStandardItem* target_parent = parentItemForGroup(group->parent ? group->parent->id : QString());
if (current_parent == target_parent) {
item->setText(group->display_name);
return;
}
// Reparent: take row from current parent, append at target. Pointers
// survive — id_to_name_item_ entries remain valid.
QList<QStandardItem*> taken = current_parent->takeRow(item->row());
taken.first()->setText(group->display_name);
target_parent->appendRow(taken);
refreshSubtreeVisibility(taken.first());
}
void FederationItemModel::onGroupVisibilityChanged(const QString& group_id, bool /*visible*/) {
QStandardItem* item = findItem(group_id);
if (!item) return;
refreshSubtreeVisibility(item);
}
void FederationItemModel::onModelAdded(const QString& fed_id) {
const Federation::Model* model = federation_->findById(fed_id);
if (!model) return;
QStandardItem* parent_item = parentItemForGroup(model->group_id);
appendModelTo(parent_item, fed_id);
}
void FederationItemModel::onModelRemoved(const QString& fed_id) {
QStandardItem* item = findItem(fed_id);
if (!item) return;
id_to_name_item_.remove(fed_id);
QStandardItem* parent_item = item->parent();
if (!parent_item) parent_item = invisibleRootItem();
parent_item->removeRow(item->row());
}
void FederationItemModel::onModelVisibilityChanged(const QString& fed_id, bool /*visible*/) {
QStandardItem* item = findItem(fed_id);
if (!item) return;
refreshSubtreeVisibility(item);
}
void FederationItemModel::onModelChanged(const QString& fed_id) {
QStandardItem* item = findItem(fed_id);
if (!item) return;
const Federation::Model* model = federation_->findById(fed_id);
if (!model) return;
item->setText(model->display_name);
}
void FederationItemModel::onModelGroupChanged(const QString& fed_id, const QString& new_group_id) {
QStandardItem* item = findItem(fed_id);
if (!item) return;
QStandardItem* current_parent = item->parent();
if (!current_parent) current_parent = invisibleRootItem();
QStandardItem* target_parent = parentItemForGroup(new_group_id);
if (current_parent == target_parent) return;
QList<QStandardItem*> taken = current_parent->takeRow(item->row());
target_parent->appendRow(taken);
refreshSubtreeVisibility(taken.first());
}
} // namespace bonsaiviewer::modules::models