/* 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 #include #include #include #include #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::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::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 ChildNodes; ChildNodes m_childnodes; public: Reference 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( item->m_instance.get_pointer() ) ); else if( role == c_ItemDataRole_Node ) return QVariant::fromValue( static_cast( 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( index.internalPointer() ); } std::pair 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( instance ), node_get_name_safe( instance.path().top().get() ), instance.path().top().get_pointer() ) ); endInsertRows(); node_attach_name_changed_callback( instance.path().top(), ConstReferenceCaller1( instance ) ); } void remove( const scene::Instance& instance ) { node_detach_name_changed_callback( instance.path().top(), ConstReferenceCaller1( 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 ); }