netradiant-custom/radiant/treemodel.cpp
2022-10-27 09:10:31 +03:00

337 lines
10 KiB
C++

/*
Copyright (C) 2001-2006, William Joseph.
All Rights Reserved.
This file is part of GtkRadiant.
GtkRadiant 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 2 of the License, or
(at your option) any later version.
GtkRadiant 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 GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "treemodel.h"
#include "debugging/debugging.h"
#include <map>
#include <QAbstractItemModel>
#include <QBrush>
#include <QApplication>
#include <QPalette>
#include "iscenegraph.h"
#include "nameable.h"
#include "generic/callback.h"
#include "scenelib.h"
#include "string/string.h"
#include "generic/reference.h"
inline Nameable* Node_getNameable( scene::Node& node ){
return NodeTypeCast<Nameable>::cast( node );
}
const char* node_get_name( scene::Node& node ){
Nameable* nameable = Node_getNameable( node );
return ( nameable != 0 )
? nameable->name()
: "node";
}
const char* node_get_name_safe( scene::Node& node ){
volatile intptr_t n = (intptr_t)&node; // see the comment on line 650
if ( n == 0 ) {
return "";
}
return node_get_name( node );
}
void node_attach_name_changed_callback( scene::Node& node, const NameCallback& callback ){
volatile intptr_t n = (intptr_t)&node; // see the comment on line 650
if ( n != 0 ) {
Nameable* nameable = Node_getNameable( node );
if ( nameable != 0 ) {
nameable->attach( callback );
}
}
}
void node_detach_name_changed_callback( scene::Node& node, const NameCallback& callback ){
volatile intptr_t n = (intptr_t)&node; // see the comment on line 650
if ( n != 0 ) {
Nameable* nameable = Node_getNameable( node );
if ( nameable != 0 ) {
nameable->detach( callback );
}
}
}
void graph_tree_model_set_name( const scene::Instance& instance, const char* name );
#include "iselection.h"
namespace
{
scene::Node* g_null_node = 0;
}
class NullInstance : public scene::Instance
{
public:
NullInstance() : scene::Instance( scene::Path( makeReference( *g_null_node ) ), 0, 0, Static<InstanceTypeCastTable>::instance() ){
}
};
namespace
{
NullInstance g_null_instance;
}
class GraphTreeNode
{
private:
struct Compare{
bool operator()( const GraphTreeNode *one, const GraphTreeNode *other ) const {
const int n = string_compare( one->m_name.c_str(), other->m_name.c_str() );
return n != 0? n < 0 : one->m_node < other->m_node;
}
};
typedef std::vector<GraphTreeNode*> ChildNodes;
ChildNodes m_childnodes;
public:
Reference<scene::Instance> m_instance;
GraphTreeNode* m_parent;
CopiedString m_name;
scene::Node* m_node;
typedef ChildNodes::iterator iterator;
GraphTreeNode( scene::Instance& instance, const char *name, scene::Node* node ) :
m_instance( instance ), m_parent( 0 ), m_name( name ), m_node( node ){
}
~GraphTreeNode(){
ASSERT_MESSAGE( empty(), "GraphTreeNode::~GraphTreeNode: memory leak" );
}
GraphTreeNode( GraphTreeNode&& ) noexcept = delete;
iterator begin(){
return m_childnodes.begin();
}
iterator end(){
return m_childnodes.end();
}
std::size_t size() const {
return m_childnodes.size();
}
bool empty() const {
return m_childnodes.empty();
}
// may not be called on the root node!
int getIndex() const {
return getIndex( m_parent->find( m_name.c_str(), m_node ) );
}
// iterator in parent's children list!
int getIndex( iterator it ) const {
return std::distance( m_parent->begin(), it );
}
GraphTreeNode *child( int row ) const {
if( row < 0 || size_t( row ) >= m_childnodes.size() )
return nullptr;
return m_childnodes[row];
}
iterator insert( GraphTreeNode* value ){
value->m_parent = this;
return m_childnodes.insert( std::lower_bound( begin(), end(), value, Compare() ), value );
//. ASSERT_MESSAGE( inserted, "GraphTreeNode::insert: already added" );
}
void erase( iterator i ){
m_childnodes.erase( i );
}
iterator find( const char *name, const scene::Node* node ){
return std::lower_bound( begin(), end(), name, [node]( const GraphTreeNode *other, const char *name ){
const int n = string_compare( other->m_name.c_str(), name );
return n != 0? n < 0 : other->m_node < node;
} );
}
// find index of future insertion
int lower_bound( const char *name, const scene::Node* node ){
return std::distance( begin(), find( name, node ) );
}
};
class GraphTreeModel : public QAbstractItemModel
{
public:
GraphTreeModel(){
rootItem = new GraphTreeNode( g_null_instance, "", nullptr );
}
~GraphTreeModel(){
delete rootItem;
}
QVariant data( const QModelIndex &index, int role ) const override {
if ( index.isValid() ){
GraphTreeNode *item = getNode( index );
if ( role == Qt::ItemDataRole::DisplayRole )
return item->m_name.c_str();
else if( role == c_ItemDataRole_Instance )
return QVariant::fromValue( static_cast<void*>( item->m_instance.get_pointer() ) );
else if( role == c_ItemDataRole_Node )
return QVariant::fromValue( static_cast<void*>( item->m_node ) );
else if( role == Qt::ItemDataRole::BackgroundRole ){
const QColor color = item->m_instance.get().isSelected()
? qApp->palette().color( QPalette::ColorRole::Highlight ).darker( 200 )
: item->m_instance.get().childSelected()
? qApp->palette().color( QPalette::ColorRole::Highlight ).darker( 300 )
: qApp->palette().color( QPalette::ColorRole::Base );
return QBrush( color );
}
}
return QVariant();
}
Qt::ItemFlags flags( const QModelIndex &index ) const override {
if ( !index.isValid() )
return Qt::NoItemFlags;
return QAbstractItemModel::flags( index );
}
QVariant headerData( int section, Qt::Orientation orientation, int role = Qt::DisplayRole ) const override {
return QVariant();
}
GraphTreeNode* getNode( const QModelIndex &index ) const {
return static_cast<GraphTreeNode*>( index.internalPointer() );
}
std::pair<GraphTreeNode*, QModelIndex> findParents( const scene::Path& path ) const {
GraphTreeNode* parent = rootItem;
QModelIndex index;
for ( scene::Path::const_iterator i = path.begin(); i != path.end() - 1; ++i )
{
GraphTreeNode::iterator child = parent->find( node_get_name_safe( ( *i ).get() ), ( *i ).get_pointer() );
ASSERT_MESSAGE( child != parent->end(), "ERROR" );
parent = *child;
index = this->index( ( *child )->getIndex( child ), 0, index );
ASSERT_MESSAGE( index.isValid(), "index.isValid()" );
}
return { parent, index };
}
QModelIndex index( int row, int column, const QModelIndex &parent ) const override {
if ( !hasIndex( row, column, parent ) )
return QModelIndex();
GraphTreeNode *parentItem = parent.isValid()? getNode( parent ) : rootItem;
GraphTreeNode *childItem = parentItem->child( row );
if ( childItem )
return createIndex( row, column, childItem );
return QModelIndex();
}
QModelIndex parent( const QModelIndex &index ) const override {
if ( !index.isValid() )
return QModelIndex();
GraphTreeNode *childItem = getNode( index );
GraphTreeNode *parentItem = childItem->m_parent;
if ( parentItem == rootItem )
return QModelIndex();
return createIndex( parentItem->getIndex(), 0, parentItem );
}
int rowCount( const QModelIndex &parent ) const override {
if ( parent.column() > 0 )
return 0;
GraphTreeNode *parentItem = parent.isValid()? getNode( parent ) : rootItem;
return parentItem->size();
}
int columnCount( const QModelIndex &parent = QModelIndex() ) const override {
return 1;
}
void insert( const scene::Instance& instance ) {
auto [parent, index] = findParents( instance.path() );
const int n = parent->lower_bound( node_get_name_safe( instance.path().top().get() ), instance.path().top().get_pointer() );
beginInsertRows( index, n, n );
parent->insert( new GraphTreeNode( const_cast<scene::Instance&>( instance ), node_get_name_safe( instance.path().top().get() ), instance.path().top().get_pointer() ) );
endInsertRows();
node_attach_name_changed_callback( instance.path().top(), ConstReferenceCaller1<scene::Instance, const char*, graph_tree_model_set_name>( instance ) );
}
void remove( const scene::Instance& instance ) {
node_detach_name_changed_callback( instance.path().top(), ConstReferenceCaller1<scene::Instance, const char*, graph_tree_model_set_name>( instance ) );
auto [parent, index] = findParents( instance.path() );
GraphTreeNode::iterator i = parent->find( node_get_name_safe( instance.path().top().get() ), instance.path().top().get_pointer() );
const int n = ( *i )->getIndex( i );
beginRemoveRows( index, n, n );
GraphTreeNode* node( *i );
parent->erase( i );
delete node;
endRemoveRows();
}
void rename( const scene::Instance& instance, const char* name ){
auto [parent, index] = findParents( instance.path() );
GraphTreeNode::iterator i = parent->find( node_get_name_safe( instance.path().top().get() ), instance.path().top().get_pointer() );
const int n = ( *i )->getIndex( i );
const int n2 = parent->lower_bound( name, instance.path().top().get_pointer() );
if( ( n2 - n ) == 0 || ( n2 - n ) == 1 ){ //rename
( *i )->m_name = name;
dataChanged( this->index( n, 0, index ), this->index( n, 0, index ), { Qt::ItemDataRole::DisplayRole } );
}
else{ // move
beginMoveRows( index, n, n, index, n2 );
GraphTreeNode* node( *i );
parent->erase( i );
node->m_name = name;
parent->insert( node );
endMoveRows();
}
}
private:
GraphTreeNode *rootItem;
};
GraphTreeModel* graph_tree_model_new(){
return new GraphTreeModel;
}
void graph_tree_model_delete( GraphTreeModel* model ){
delete model;
}
void graph_tree_model_insert( GraphTreeModel* model, const scene::Instance& instance ){
model->insert( instance );
}
void graph_tree_model_erase( GraphTreeModel* model, const scene::Instance& instance ){
model->remove( instance );
}
GraphTreeModel* scene_graph_get_tree_model(); // temp hack
void graph_tree_model_set_name( const scene::Instance& instance, const char* name ){
scene_graph_get_tree_model()->rename( instance, name );
}