diff --git a/libs/container/container.h b/libs/container/container.h index 47f823e6..9e8c03f5 100644 --- a/libs/container/container.h +++ b/libs/container/container.h @@ -58,6 +58,7 @@ const Type& get() const { /// \brief An adaptor to make std::list into a Unique Sequence - which cannot contain the same value more than once. +/// It's illegal to modify inserted values directly! /// \param Value Uniquely identifies itself. Must provide a copy-constructor and an equality operator. template class UnsortedSet @@ -69,6 +70,38 @@ typedef typename Values::iterator iterator; typedef typename Values::const_iterator const_iterator; typedef typename Values::reverse_iterator reverse_iterator; typedef typename Values::const_reverse_iterator const_reverse_iterator; +private: +struct Compare{ + using is_transparent = void; + + bool operator()( const iterator& one, const iterator& other ) const { + return *one < *other; + } + bool operator()( const Value& va, const iterator& it ) const { + return va < *it; + } + bool operator()( const iterator& it, const Value& va ) const { + return *it < va; + } +}; +std::set m_set; // store sorted iterators for fast lookup +void init_set(){ // only load set, when lookup is needed + if( m_set.empty() ) + for( auto it = begin(); it != end(); ++it ) + m_set.emplace( it ); +} +public: + +UnsortedSet() = default; +UnsortedSet( const UnsortedSet& other ) : m_values( other.m_values ), m_set(){ +} +UnsortedSet( UnsortedSet&& ) noexcept = default; +UnsortedSet& operator=( const UnsortedSet& other ){ + m_values = other.m_values; + m_set.clear(); + return *this; +} +UnsortedSet& operator=( UnsortedSet&& ) noexcept = default; iterator begin(){ return m_values.begin(); @@ -103,23 +136,32 @@ std::size_t size() const { } void clear(){ m_values.clear(); + m_set.clear(); } void swap( UnsortedSet& other ){ std::swap( m_values, other.m_values ); + std::swap( m_set, other.m_set ); } + iterator insert( const Value& value ){ - ASSERT_MESSAGE( find( value ) == end(), "UnsortedSet::insert: already added" ); + init_set(); m_values.push_back( value ); + const bool inserted = m_set.emplace( --end() ).second; + ASSERT_MESSAGE( inserted, "UnsortedSet::insert: already added" ); return --end(); } void erase( const Value& value ){ - iterator i = find( value ); - ASSERT_MESSAGE( i != end(), "UnsortedSet::erase: not found" ); - m_values.erase( i ); + init_set(); + const auto it = m_set.find( value ); + ASSERT_MESSAGE( it != m_set.end(), "UnsortedSet::erase: not found" ); + m_values.erase( *it ); + m_set.erase( it ); } iterator find( const Value& value ){ - return std::find( begin(), end(), value ); + init_set(); + const auto it = m_set.find( value ); + return it == m_set.end()? m_values.end() : *it; } }; @@ -274,7 +316,7 @@ void erase( const Value& value ){ m_values.erase( i ); } iterator find( const Value& value ){ - return std::find( begin(), end(), value ); + return m_values.find( value ); } }; diff --git a/radiant/treemodel.cpp b/radiant/treemodel.cpp index 57e6812c..4b1387f9 100644 --- a/radiant/treemodel.cpp +++ b/radiant/treemodel.cpp @@ -745,6 +745,10 @@ class GraphTreeNode { typedef std::map, GraphTreeNode*> ChildNodes; ChildNodes m_childnodes; + +std::list m_list; // dummy list for child index identification +std::list::const_iterator m_parentListIterator; // iterator from parent's list +bool m_searchFromEnd = false; //silly optimization public: Reference m_instance; GraphTreeNode* m_parent; @@ -761,6 +765,11 @@ GraphTreeNode( scene::Instance& instance ) : m_instance( instance ), m_parent( 0 m_instance.get().setChildSelectedChangedCallback( Callback() ); ASSERT_MESSAGE( empty(), "GraphTreeNode::~GraphTreeNode: memory leak" ); } +GraphTreeNode() = delete; +GraphTreeNode( const GraphTreeNode& ) = delete; +GraphTreeNode( GraphTreeNode&& ) noexcept = delete; +GraphTreeNode& operator=( const GraphTreeNode& ) = delete; +GraphTreeNode& operator=( GraphTreeNode&& ) noexcept = delete; iterator begin(){ return m_childnodes.begin(); @@ -775,25 +784,30 @@ size_type size() const { bool empty() const { return m_childnodes.empty(); } +// may not be called on the root node! +int getIndex() const { + const int idx = m_parent->m_searchFromEnd? + m_parent->m_list.size() - std::distance( m_parentListIterator, m_parent->m_list.cend() ) : + std::distance( m_parent->m_list.cbegin(), m_parentListIterator ); + m_parent->m_searchFromEnd = idx * 2 > int( m_parent->size() ); + return idx; +} iterator insert( const value_type& value ){ iterator i = m_childnodes.insert( value ).first; ( *i ).second->m_parent = this; + const auto pos = std::next( i ) == end()? m_list.end() : std::next( i )->second->m_parentListIterator; + i->second->m_parentListIterator = m_list.insert( pos, 'a' ); return i; } void erase( iterator i ){ + m_list.erase( i->second->m_parentListIterator ); m_childnodes.erase( i ); } iterator find( const key_type& key ){ return m_childnodes.find( key ); } -void swap( GraphTreeNode& other ){ - std::swap( m_parent, other.m_parent ); - std::swap( m_childnodes, other.m_childnodes ); - std::swap( m_instance, other.m_instance ); -} - void rowChanged(){ graph_tree_model_row_changed( *this ); } @@ -884,15 +898,7 @@ static GtkTreePath* graph_tree_model_get_path( GtkTreeModel* tree_model, GtkTree for ( GraphTreeNode* node = ( *graph_iterator_read_tree_iter( iter ) ).second; node != graph; node = node->m_parent ) { - std::size_t index = 0; - for ( GraphTreeNode::iterator i = node->m_parent->begin(); i != node->m_parent->end(); ++i, ++index ) - { - if ( ( *i ).second == node ) { - gtk_tree_path_prepend_index( path, gint( index ) ); - break; - } - } - ASSERT_MESSAGE( index != node->m_parent->size(), "error resolving tree path" ); + gtk_tree_path_prepend_index( path, gint( node->getIndex() ) ); } return path;