NOW I do it right: #woxblox#
[divverent/netradiant.git] / libs / maplib.h
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:
41   NameableString(const char* name)
42     : m_name(name)
43   {
44   }
45
46   const char* name() const
47   {
48     return m_name.c_str();
49   }
50   void attach(const NameCallback& callback)
51   {
52   }
53   void detach(const NameCallback& callback)
54   {
55   }
56 };
57
58
59 class UndoFileChangeTracker : public UndoTracker, public MapFile
60 {
61   std::size_t m_size;
62   std::size_t m_saved;
63   typedef void (UndoFileChangeTracker::*Pending)();
64   Pending m_pending;
65   Callback m_changed;
66
67 public:
68   UndoFileChangeTracker() : m_size(0), m_saved(MAPFILE_MAX_CHANGES), m_pending(0)
69   {
70   }
71   void print()
72   {
73     globalOutputStream() << "saved: " << Unsigned(m_saved) << " size: " << Unsigned(m_size) << "\n";
74   }
75
76   void push()
77   {
78     ++m_size;
79     m_changed();
80     //print();
81   }
82   void pop()
83   {
84     --m_size;
85     m_changed();
86     //print();
87   }
88   void pushOperation()
89   {
90     if(m_size < m_saved)
91     {
92       // redo queue has been flushed.. it is now impossible to get back to the saved state via undo/redo
93       m_saved = MAPFILE_MAX_CHANGES;
94     }
95     push();
96   }
97   void clear()
98   {
99     m_size = 0;
100     m_changed();
101     //print();
102   }
103   void begin()
104   {
105     m_pending = Pending(&UndoFileChangeTracker::pushOperation);
106   }
107   void undo()
108   {
109     m_pending = Pending(&UndoFileChangeTracker::pop);
110   }
111   void redo()
112   {
113     m_pending = Pending(&UndoFileChangeTracker::push);
114   }
115
116   void changed()
117   {
118     if(m_pending != 0)
119     {
120       ((*this).*m_pending)();
121       m_pending = 0;
122     }
123   }
124
125   void save()
126   {
127     m_saved = m_size;
128     m_changed();
129   }
130   bool saved() const
131   {
132     return m_saved == m_size;
133   }
134
135   void setChangedCallback(const Callback& changed)
136   {
137     m_changed = changed;
138     m_changed();
139   }
140
141   std::size_t changes() const
142   {
143     return m_size;
144   }
145 };
146
147
148 class MapRoot : public scene::Node::Symbiot, public scene::Instantiable, public scene::Traversable::Observer
149 {
150   class TypeCasts
151   {
152     NodeTypeCastTable m_casts;
153   public:
154     TypeCasts()
155     {
156       NodeStaticCast<MapRoot, scene::Instantiable>::install(m_casts);
157       NodeContainedCast<MapRoot, scene::Traversable>::install(m_casts);
158       NodeContainedCast<MapRoot, TransformNode>::install(m_casts);
159       NodeContainedCast<MapRoot, Nameable>::install(m_casts);
160       NodeContainedCast<MapRoot, MapFile>::install(m_casts);
161     }
162     NodeTypeCastTable& get()
163     {
164       return m_casts;
165     }
166   };
167
168   scene::Node m_node;
169   IdentityTransform m_transform;
170   TraversableNodeSet m_traverse;
171   InstanceSet m_instances;
172   typedef SelectableInstance Instance;
173   NameableString m_name;
174   UndoFileChangeTracker m_changeTracker;
175 public:
176   typedef LazyStatic<TypeCasts> StaticTypeCasts;
177
178   scene::Traversable& get(NullType<scene::Traversable>)
179   {
180     return m_traverse;
181   }
182   TransformNode& get(NullType<TransformNode>)
183   {
184     return m_transform;
185   }
186   Nameable& get(NullType<Nameable>)
187   {
188     return m_name;
189   }
190   MapFile& get(NullType<MapFile>)
191   {
192     return m_changeTracker;
193   }
194
195   MapRoot(const char* name) : m_node(this, this, StaticTypeCasts::instance().get()), m_name(name)
196   {
197     m_node.m_isRoot = true;
198
199     m_traverse.attach(this);
200
201     GlobalUndoSystem().trackerAttach(m_changeTracker);
202   }
203   ~MapRoot()
204   {
205   }
206   void release()
207   {
208     GlobalUndoSystem().trackerDetach(m_changeTracker);
209
210     m_traverse.detach(this);
211     delete this;
212   }
213   scene::Node& node()
214   {
215     return m_node;
216   }
217
218   InstanceCounter m_instanceCounter;
219   void instanceAttach(const scene::Path& path)
220   {
221     if(++m_instanceCounter.m_count == 1)
222     {
223       m_traverse.instanceAttach(path_find_mapfile(path.begin(), path.end()));
224     }
225   }
226   void instanceDetach(const scene::Path& path)
227   {
228     if(--m_instanceCounter.m_count == 0)
229     {
230       m_traverse.instanceDetach(path_find_mapfile(path.begin(), path.end()));
231     }
232   }
233
234   void insert(scene::Node& child)
235   {
236     m_instances.insert(child);
237   }
238   void erase(scene::Node& child)
239   {
240     m_instances.erase(child);
241   }
242
243   scene::Node& clone() const
244   {
245     return (new MapRoot(*this))->node();
246   }
247
248   scene::Instance* create(const scene::Path& path, scene::Instance* parent)
249   {
250     return new Instance(path, parent);
251   }
252   void forEachInstance(const scene::Instantiable::Visitor& visitor)
253   {
254     m_instances.forEachInstance(visitor);
255   }
256   void insert(scene::Instantiable::Observer* observer, const scene::Path& path, scene::Instance* instance)
257   {
258     m_instances.insert(observer, path, instance);
259     instanceAttach(path);
260   }
261   scene::Instance* erase(scene::Instantiable::Observer* observer, const scene::Path& path)
262   {
263     instanceDetach(path);
264     return m_instances.erase(observer, path);
265   }
266 };
267
268 inline void MapRoot_construct()
269 {
270 }
271
272 inline void MapRoot_destroy()
273 {
274 }
275
276 inline NodeSmartReference NewMapRoot(const char* name)
277 {
278   return NodeSmartReference((new MapRoot(name))->node());
279 }
280
281
282 #endif