/*
Stanley Designer
Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
#include "scene_tree.h"
#include "ui_scene_tree.h"
#include "glcamera.h"
#include "qglview.h"
#include
#include
#include
#include
enum Column {
cName,
cVis,
cMaterial
};
enum ItemRole {
irObject = Qt::UserRole,
irType = Qt::UserRole + 1,
};
enum ObjectType {
otNode = 1,
otMesh = 2,
otLight = 4,
otCamera = 8,
};
SceneTree::SceneTree(QWidget * parent): QWidget(parent) {
ui = new Ui::SceneTree();
ui->setupUi(this);
ui->treeObjects->header()->setSectionResizeMode(cVis, QHeaderView::ResizeToContents);
ui->treeObjects->header()->setSectionsMovable(false);
ui->treeObjects->header()->swapSections(cName, cVis);
icon_empty = QIcon(":/icons/type-empty.png");
icon_geo = QIcon(":/icons/type-geo.png");
icon_camera = QIcon(":/icons/type-camera.png");
icon_light = QIcon(":/icons/type-light.png");
icon_vis[0] = QIcon(":/icons/layer-visible-off.png");
icon_vis[1] = QIcon(":/icons/layer-visible-on.png");
QAction * a = 0;
a = new QAction(QIcon(":/icons/type-camera.png"), "Focus");
connect(a, SIGNAL(triggered()), this, SLOT(focusObjects()));
ui->treeObjects->addAction(a);
a = new QAction(QIcon(":/icons/edit-delete.png"), "Remove");
connect(a, SIGNAL(triggered()), this, SLOT(removeObjects()));
ui->treeObjects->addAction(a);
view = 0;
block_tree = false;
connect(ui->treeObjects->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), this, SLOT(treeObjects_selectionCnahged()));
}
SceneTree::~SceneTree() {
delete ui;
}
void SceneTree::assignQGLView(QGLView * v) {
view = v;
connect(view, SIGNAL(selectionChanged()), this, SLOT(selectionChanged()));
connect(view, SIGNAL(materialsChanged()), this, SLOT(materialsChanged()));
connect(view->scene(), SIGNAL(treeChanged()), this, SLOT(objectsTreeChanged()));
connect(view->scene(), SIGNAL(__objectDeleted(ObjectBase*)), this, SLOT(__objectDeleted(ObjectBase*)));
objectsTreeChanged();
}
void SceneTree::changeEvent(QEvent * e) {
QWidget::changeEvent(e);
if (e->type() == QEvent::LanguageChange) {
ui->retranslateUi(this);
return;
}
}
void SceneTree::rememberExpanded(QTreeWidgetItem * ti) {
for (int i = 0; i < ti->childCount(); ++i) {
QTreeWidgetItem * ci = ti->child(i);
if (ci->isExpanded())
expanded_ << itemObject(ci);
rememberExpanded(ci);
}
}
void SceneTree::restoreExpanded(QTreeWidgetItem * ti) {
for (int i = 0; i < ti->childCount(); ++i) {
QTreeWidgetItem * ci = ti->child(i);
ci->setExpanded(expanded_.contains(itemObject(ci)));
restoreExpanded(ci);
}
}
void SceneTree::makeObjetTree(ObjectBase * o, QTreeWidgetItem * ti) {
for (int i = 0; i < o->childCount(); ++i) {
ObjectBase * co = o->child(i);
QTreeWidgetItem * ci = new QTreeWidgetItem(ti);
ci->setText(cName, co->name());
ci->setCheckState(cVis, co->isVisible() ? Qt::Checked : Qt::Unchecked);
ci->setIcon(cVis, icon_vis[co->isVisible(true)]);
if (co->material())
ci->setText(cMaterial, co->material()->name);
ci->setFlags(ci->flags() | Qt::ItemIsEditable);
ObjectType t = otNode;
switch (co->type()) {
case ObjectBase::glMesh:
if (co->mesh()) {
t = otMesh;
ci->setIcon(cName, icon_geo);
geo_items << ci;
} else {
ci->setIcon(cName, icon_empty);
}
break;
case ObjectBase::glLight:
t = otLight;
ci->setIcon(cName, icon_light);
break;
case ObjectBase::glCamera:
t = otCamera;
ci->setIcon(cName, icon_camera);
break;
default: break;
}
ci->setData(cName, irObject, quintptr(co));
ci->setData(cName, irType, int(t));
ci->setSelected(co->isSelected());
makeObjetTree(co, ci);
}
}
ObjectBase * SceneTree::itemObject(QTreeWidgetItem * item) const {
if (!item) return 0;
return (ObjectBase*)(item->data(cName, irObject).toULongLong());
}
int SceneTree::itemType(QTreeWidgetItem * item) const {
if (!item) return otNode;
return item->data(cName, irType).toInt();
}
void SceneTree::selectionChanged() {
if (block_tree) return;
block_tree = true;
QList il = ui->treeObjects->findItems("", Qt::MatchContains | Qt::MatchRecursive);
const ObjectBase * fo = 0;
if (view->selectedObjects().size() == 1)
fo = view->selectedObject();
foreach (QTreeWidgetItem * i, il) {
ObjectBase * o = itemObject(i);
i->setSelected(o->isSelected());
if (fo && (fo == o)) {
ui->treeObjects->setCurrentItem(i);
}
}
block_tree = false;
checkButtons();
}
void SceneTree::materialsChanged() {
foreach (QTreeWidgetItem * i, geo_items) {
ObjectBase * o = itemObject(i);
if (!o) continue;
if (o->material())
i->setText(cMaterial, o->material()->name);
}
}
bool SceneTree::filterTree(QTreeWidgetItem * ti, const QString & filter, int types) {
bool ret = false;
for (int i = 0; i < ti->childCount(); ++i) {
QTreeWidgetItem * ci = ti->child(i);
QString cit = ci->text(cName);
int t = itemType(ci);
if (ci->childCount() > 0) {
if (!filterTree(ci, filter, types)) {
ci->setHidden(true);
continue;
}
ci->setHidden(false);
ret = true;
} else {
bool f = false;
if (filter.isEmpty()) {
f = true;
} else {
f = f || cit.contains(filter);
}
if ((types & t) != t)
f = false;
ci->setHidden(!f);
if (f) ret = true;
}
}
return ret;
}
void SceneTree::checkButtons() {
bool has_1 = false, has_m = false;
if (view) {
has_1 = !view->selectedObjects().isEmpty();
has_m = view->selectedObjects().size() > 1;
}
ui->buttonFocus ->setEnabled(has_1);
ui->buttonRemove->setEnabled(has_1);
ui->buttonClone ->setEnabled(has_1);
ui->buttonSelectParent->setEnabled(has_1);
ui->buttonGroup->setEnabled(has_m);
}
void SceneTree::treeObjects_selectionCnahged() {
if (block_tree || !view) return;
block_tree = true;
view->scene()->clearSelection();
QList sol;
QList til = ui->treeObjects->selectedItems();
foreach (QTreeWidgetItem * i, til)
sol << itemObject(i);
view->scene()->selectObjects(sol);
block_tree = false;
checkButtons();
}
void SceneTree::filter() {
int types = 0;
if (ui->buttonFilterNode ->isChecked()) types |= otNode ;
if (ui->buttonFilterMesh ->isChecked()) types |= otMesh ;
if (ui->buttonFilterLight ->isChecked()) types |= otLight ;
if (ui->buttonFilterCamera->isChecked()) types |= otCamera;
if (types == 0) types = 0xFF;
filterTree(ui->treeObjects->invisibleRootItem(), ui->lineFilter->text(), types);
ui->treeObjects->invisibleRootItem()->setHidden(false);
}
void SceneTree::__objectDeleted(ObjectBase * o) {
for (int i = 0; i < geo_items.size(); ++i)
if (itemObject(geo_items[i]) == o) {
geo_items.removeAt(i);
--i;
}
}
void SceneTree::on_treeObjects_itemChanged(QTreeWidgetItem * item, int column) {
if (block_tree) return;
if (column == cName) {
ObjectBase * o = itemObject(item);
if (o) o->setName(item->text(cName));
}
if (column == cVis) {
bool vis = item->checkState(cVis) == Qt::Checked;
QList til = ui->treeObjects->selectedItems();
if (!til.contains(item)) {
til.clear();
til << item;
}
foreach (QTreeWidgetItem * ti, til) {
//itemObject(ti)->setVisible(vis);
itemObject(ti)->setVisible(vis);
}
}
}
void SceneTree::on_treeObjects_itemMoved(QTreeWidgetItem * item, QTreeWidgetItem * new_parent) {
ObjectBase * co = itemObject(item);
if (!co->hasParent()) return;
//co->parent()->removeChild(co);
if (new_parent == ui->treeObjects->invisibleRootItem()) {
view->scene()->rootObject()->addChild(co);
} else {
ObjectBase * po = itemObject(new_parent);
if (po)
po->addChild(co);
}
}
void SceneTree::on_buttonAddNode_clicked() {
if (!view) return;
ObjectBase * no = new ObjectBase();
view->scene()->addObject(no);
view->scene()->selectObject(no);
}
void SceneTree::on_buttonAddLight_clicked() {
if (!view) return;
ObjectBase * no = new Light();
view->scene()->addObject(no);
view->scene()->selectObject(no);
}
void SceneTree::on_buttonClone_clicked() {
if (!view) return;
QList sil = ui->treeObjects->selectedItems();
QList col;
foreach (QTreeWidgetItem * i, sil) {
ObjectBase * o = itemObject(i);
if (!o) continue;
ObjectBase * no = o->clone();
o->parent()->addChild(no);
col << no;
}
view->scene()->selectObjects(col);
}
void SceneTree::on_buttonSelectParent_clicked() {
if (!view) return;
QList sol = view->scene()->selectedObjects(true);
QSet nsl;
foreach (ObjectBase * o, sol) {
ObjectBase * po = o->parent();
if (po != view->scene()->rootObject())
o = po;
nsl << o;
}
view->scene()->selectObjects(nsl.toList());
}
void SceneTree::on_buttonGroup_clicked() {
if (!view) return;
QList sol = view->scene()->selectedObjects(true);
ObjectBase * cp = sol[0]->parent();
ObjectBase * nr = new ObjectBase();
cp->addChild(nr);
foreach (ObjectBase * o, sol)
nr->addChild(o);
view->scene()->selectObject(nr);
}
void SceneTree::removeObjects() {
if (!view) return;
QList sil = ui->treeObjects->selectedItems();
foreach (QTreeWidgetItem * i, sil) {
ObjectBase * o = itemObject(i);
if (o) delete o;
}
qDeleteAll(sil);
}
void SceneTree::focusObjects() {
if (!view) return;
if (!view->camera()) return;
Box3D bb;
QList ol = view->selectedObjects();
foreach (ObjectBase * o, ol) {
o->calculateBoundingBox();
bb |= o->boundingBox();
}
view->focusOn(bb);
}
void SceneTree::objectsTreeChanged() {
int vpos = ui->treeObjects->verticalScrollBar()->value();
expanded_.clear();
geo_items.clear();
rememberExpanded(ui->treeObjects->invisibleRootItem());
block_tree = true;
ui->treeObjects->clear();
block_tree = false;
if (!view) return;
block_tree = true;
makeObjetTree(view->scene()->rootObject(), ui->treeObjects->invisibleRootItem());
restoreExpanded(ui->treeObjects->invisibleRootItem());
block_tree = false;
filter();
QApplication::processEvents();
ui->treeObjects->verticalScrollBar()->setValue(vpos);
}