/* 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 */ #if !defined ( INCLUDED_MAPLIB_H ) #define INCLUDED_MAPLIB_H #include "nameable.h" #include "mapfile.h" #include "traverselib.h" #include "transformlib.h" #include "scenelib.h" #include "string/string.h" #include "instancelib.h" #include "selectionlib.h" #include "generic/callback.h" class NameableString : public Nameable { CopiedString m_name; public: NameableString( const char* name ) : m_name( name ){ } const char* name() const { return m_name.c_str(); } void attach( const NameCallback& callback ){ } void detach( const NameCallback& callback ){ } }; class UndoFileChangeTracker : public UndoTracker, public MapFile { std::size_t m_size; std::size_t m_saved; typedef void ( UndoFileChangeTracker::*Pending )(); Pending m_pending; Callback m_changed; public: UndoFileChangeTracker() : m_size( 0 ), m_saved( MAPFILE_MAX_CHANGES ), m_pending( 0 ){ } void print(){ globalOutputStream() << "saved: " << Unsigned( m_saved ) << " size: " << Unsigned( m_size ) << "\n"; } void push(){ ++m_size; m_changed(); //print(); } void pop(){ --m_size; m_changed(); //print(); } void pushOperation(){ if ( m_size < m_saved ) { // redo queue has been flushed.. it is now impossible to get back to the saved state via undo/redo m_saved = MAPFILE_MAX_CHANGES; } push(); } void clear(){ m_size = 0; m_changed(); //print(); } void begin(){ m_pending = Pending( &UndoFileChangeTracker::pushOperation ); } void undo(){ m_pending = Pending( &UndoFileChangeTracker::pop ); } void redo(){ m_pending = Pending( &UndoFileChangeTracker::push ); } void changed(){ if ( m_pending != 0 ) { ( ( *this ).*m_pending )(); m_pending = 0; } } void save(){ m_saved = m_size; m_changed(); } bool saved() const { return m_saved == m_size; } void setChangedCallback( const Callback& changed ){ m_changed = changed; m_changed(); } std::size_t changes() const { return m_size; } }; class MapRoot : public scene::Node::Symbiot, public scene::Instantiable, public scene::Traversable::Observer { class TypeCasts { NodeTypeCastTable m_casts; public: TypeCasts(){ NodeStaticCast::install( m_casts ); NodeContainedCast::install( m_casts ); NodeContainedCast::install( m_casts ); NodeContainedCast::install( m_casts ); NodeContainedCast::install( m_casts ); } NodeTypeCastTable& get(){ return m_casts; } }; scene::Node m_node; IdentityTransform m_transform; TraversableNodeSet m_traverse; InstanceSet m_instances; typedef SelectableInstance Instance; NameableString m_name; UndoFileChangeTracker m_changeTracker; public: typedef LazyStatic StaticTypeCasts; scene::Traversable& get( NullType){ return m_traverse; } TransformNode& get( NullType){ return m_transform; } Nameable& get( NullType){ return m_name; } MapFile& get( NullType){ return m_changeTracker; } MapRoot( const char* name ) : m_node( this, this, StaticTypeCasts::instance().get() ), m_name( name ){ m_node.m_isRoot = true; m_traverse.attach( this ); GlobalUndoSystem().trackerAttach( m_changeTracker ); } ~MapRoot(){ } void release(){ GlobalUndoSystem().trackerDetach( m_changeTracker ); m_traverse.detach( this ); delete this; } scene::Node& node(){ return m_node; } InstanceCounter m_instanceCounter; void instanceAttach( const scene::Path& path ){ if ( ++m_instanceCounter.m_count == 1 ) { m_traverse.instanceAttach( path_find_mapfile( path.begin(), path.end() ) ); } } void instanceDetach( const scene::Path& path ){ if ( --m_instanceCounter.m_count == 0 ) { m_traverse.instanceDetach( path_find_mapfile( path.begin(), path.end() ) ); } } void insert( scene::Node& child ){ m_instances.insert( child ); } void erase( scene::Node& child ){ m_instances.erase( child ); } scene::Node& clone() const { return ( new MapRoot( *this ) )->node(); } scene::Instance* create( const scene::Path& path, scene::Instance* parent ){ return new Instance( path, parent ); } void forEachInstance( const scene::Instantiable::Visitor& visitor ){ m_instances.forEachInstance( visitor ); } void insert( scene::Instantiable::Observer* observer, const scene::Path& path, scene::Instance* instance ){ m_instances.insert( observer, path, instance ); instanceAttach( path ); } scene::Instance* erase( scene::Instantiable::Observer* observer, const scene::Path& path ){ instanceDetach( path ); return m_instances.erase( observer, path ); } }; inline void MapRoot_construct(){ } inline void MapRoot_destroy(){ } inline NodeSmartReference NewMapRoot( const char* name ){ return NodeSmartReference( ( new MapRoot( name ) )->node() ); } #endif