1 /*
2    Copyright (C) 2001-2006, William Joseph.
3    All Rights Reserved.
4 
5    This file is part of GtkRadiant.
6 
7    GtkRadiant is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11 
12    GtkRadiant is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16 
17    You should have received a copy of the GNU General Public License
18    along with GtkRadiant; if not, write to the Free Software
19    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20  */
21 
22 #if !defined ( INCLUDED_MAPLIB_H )
23 #define INCLUDED_MAPLIB_H
24 
25 #include "nameable.h"
26 #include "mapfile.h"
27 
28 #include "traverselib.h"
29 #include "transformlib.h"
30 #include "scenelib.h"
31 #include "string/string.h"
32 #include "instancelib.h"
33 #include "selectionlib.h"
34 #include "generic/callback.h"
35 
36 
37 class NameableString : public Nameable
38 {
39 CopiedString m_name;
40 public:
NameableString(const char * name)41 NameableString( const char* name )
42 	: m_name( name ){
43 }
44 
name()45 const char* name() const {
46 	return m_name.c_str();
47 }
attach(const NameCallback & callback)48 void attach( const NameCallback& callback ){
49 }
detach(const NameCallback & callback)50 void detach( const NameCallback& callback ){
51 }
52 };
53 
54 
55 class UndoFileChangeTracker : public UndoTracker, public MapFile
56 {
57 std::size_t m_size;
58 std::size_t m_saved;
59 typedef void ( UndoFileChangeTracker::*Pending )();
60 Pending m_pending;
61 Callback m_changed;
62 
63 public:
UndoFileChangeTracker()64 UndoFileChangeTracker() : m_size( 0 ), m_saved( MAPFILE_MAX_CHANGES ), m_pending( 0 ){
65 }
print()66 void print(){
67 	globalOutputStream() << "saved: " << Unsigned( m_saved ) << " size: " << Unsigned( m_size ) << "\n";
68 }
69 
push()70 void push(){
71 	++m_size;
72 	m_changed();
73 	//print();
74 }
pop()75 void pop(){
76 	--m_size;
77 	m_changed();
78 	//print();
79 }
pushOperation()80 void pushOperation(){
81 	if ( m_size < m_saved ) {
82 		// redo queue has been flushed.. it is now impossible to get back to the saved state via undo/redo
83 		m_saved = MAPFILE_MAX_CHANGES;
84 	}
85 	push();
86 }
clear()87 void clear(){
88 	m_size = 0;
89 	m_changed();
90 	//print();
91 }
begin()92 void begin(){
93 	m_pending = Pending( &UndoFileChangeTracker::pushOperation );
94 }
undo()95 void undo(){
96 	m_pending = Pending( &UndoFileChangeTracker::pop );
97 }
redo()98 void redo(){
99 	m_pending = Pending( &UndoFileChangeTracker::push );
100 }
101 
changed()102 void changed(){
103 	if ( m_pending != 0 ) {
104 		( ( *this ).*m_pending )();
105 		m_pending = 0;
106 	}
107 }
108 
save()109 void save(){
110 	m_saved = m_size;
111 	m_changed();
112 }
saved()113 bool saved() const {
114 	return m_saved == m_size;
115 }
116 
setChangedCallback(const Callback & changed)117 void setChangedCallback( const Callback& changed ){
118 	m_changed = changed;
119 	m_changed();
120 }
121 
changes()122 std::size_t changes() const {
123 	return m_size;
124 }
125 };
126 
127 
128 class MapRoot : public scene::Node::Symbiot, public scene::Instantiable, public scene::Traversable::Observer
129 {
130 class TypeCasts
131 {
132 NodeTypeCastTable m_casts;
133 public:
TypeCasts()134 TypeCasts(){
135 	NodeStaticCast<MapRoot, scene::Instantiable>::install( m_casts );
136 	NodeContainedCast<MapRoot, scene::Traversable>::install( m_casts );
137 	NodeContainedCast<MapRoot, TransformNode>::install( m_casts );
138 	NodeContainedCast<MapRoot, Nameable>::install( m_casts );
139 	NodeContainedCast<MapRoot, MapFile>::install( m_casts );
140 }
get()141 NodeTypeCastTable& get(){
142 	return m_casts;
143 }
144 };
145 
146 scene::Node m_node;
147 IdentityTransform m_transform;
148 TraversableNodeSet m_traverse;
149 InstanceSet m_instances;
150 typedef SelectableInstance Instance;
151 NameableString m_name;
152 UndoFileChangeTracker m_changeTracker;
153 public:
154 typedef LazyStatic<TypeCasts> StaticTypeCasts;
155 
get(NullType<scene::Traversable>)156 scene::Traversable& get( NullType<scene::Traversable>){
157 	return m_traverse;
158 }
get(NullType<TransformNode>)159 TransformNode& get( NullType<TransformNode>){
160 	return m_transform;
161 }
get(NullType<Nameable>)162 Nameable& get( NullType<Nameable>){
163 	return m_name;
164 }
get(NullType<MapFile>)165 MapFile& get( NullType<MapFile>){
166 	return m_changeTracker;
167 }
168 
MapRoot(const char * name)169 MapRoot( const char* name ) : m_node( this, this, StaticTypeCasts::instance().get() ), m_name( name ){
170 	m_node.m_isRoot = true;
171 
172 	m_traverse.attach( this );
173 
174 	GlobalUndoSystem().trackerAttach( m_changeTracker );
175 }
~MapRoot()176 ~MapRoot(){
177 }
release()178 void release(){
179 	GlobalUndoSystem().trackerDetach( m_changeTracker );
180 
181 	m_traverse.detach( this );
182 	delete this;
183 }
node()184 scene::Node& node(){
185 	return m_node;
186 }
187 
188 InstanceCounter m_instanceCounter;
instanceAttach(const scene::Path & path)189 void instanceAttach( const scene::Path& path ){
190 	if ( ++m_instanceCounter.m_count == 1 ) {
191 		m_traverse.instanceAttach( path_find_mapfile( path.begin(), path.end() ) );
192 	}
193 }
instanceDetach(const scene::Path & path)194 void instanceDetach( const scene::Path& path ){
195 	if ( --m_instanceCounter.m_count == 0 ) {
196 		m_traverse.instanceDetach( path_find_mapfile( path.begin(), path.end() ) );
197 	}
198 }
199 
insert(scene::Node & child)200 void insert( scene::Node& child ){
201 	m_instances.insert( child );
202 }
erase(scene::Node & child)203 void erase( scene::Node& child ){
204 	m_instances.erase( child );
205 }
206 
clone()207 scene::Node& clone() const {
208 	return ( new MapRoot( *this ) )->node();
209 }
210 
create(const scene::Path & path,scene::Instance * parent)211 scene::Instance* create( const scene::Path& path, scene::Instance* parent ){
212 	return new Instance( path, parent );
213 }
forEachInstance(const scene::Instantiable::Visitor & visitor)214 void forEachInstance( const scene::Instantiable::Visitor& visitor ){
215 	m_instances.forEachInstance( visitor );
216 }
insert(scene::Instantiable::Observer * observer,const scene::Path & path,scene::Instance * instance)217 void insert( scene::Instantiable::Observer* observer, const scene::Path& path, scene::Instance* instance ){
218 	m_instances.insert( observer, path, instance );
219 	instanceAttach( path );
220 }
erase(scene::Instantiable::Observer * observer,const scene::Path & path)221 scene::Instance* erase( scene::Instantiable::Observer* observer, const scene::Path& path ){
222 	instanceDetach( path );
223 	return m_instances.erase( observer, path );
224 }
225 };
226 
MapRoot_construct()227 inline void MapRoot_construct(){
228 }
229 
MapRoot_destroy()230 inline void MapRoot_destroy(){
231 }
232 
NewMapRoot(const char * name)233 inline NodeSmartReference NewMapRoot( const char* name ){
234 	return NodeSmartReference( ( new MapRoot( name ) )->node() );
235 }
236 
237 
238 #endif
239