initial
[divverent/netradiant.git] / radiant / map.cpp
1 /*
2 Copyright (C) 1999-2006 Id Software, Inc. and contributors.
3 For a list of contributors, see the accompanying CONTRIBUTORS file.
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 #include "map.h"
23
24 #include "debugging/debugging.h"
25
26 #include "imap.h"
27 #include "iselection.h"
28 #include "iundo.h"
29 #include "ibrush.h"
30 #include "ifilter.h"
31 #include "ireference.h"
32 #include "ifiletypes.h"
33 #include "ieclass.h"
34 #include "irender.h"
35 #include "ientity.h"
36 #include "editable.h"
37 #include "ifilesystem.h"
38 #include "namespace.h"
39 #include "moduleobserver.h"
40
41 #include <set>
42
43 #include <gtk/gtkmain.h>
44 #include <gtk/gtkbox.h>
45 #include <gtk/gtkentry.h>
46 #include <gtk/gtklabel.h>
47 #include <gtk/gtktable.h>
48 #include <gtk/gtktreemodel.h>
49 #include <gtk/gtktreeview.h>
50 #include <gtk/gtkliststore.h>
51 #include <gtk/gtkcellrenderertext.h>
52
53 #include "scenelib.h"
54 #include "transformlib.h"
55 #include "selectionlib.h"
56 #include "instancelib.h"
57 #include "traverselib.h"
58 #include "maplib.h"
59 #include "eclasslib.h"
60 #include "cmdlib.h"
61 #include "stream/textfilestream.h"
62 #include "os/path.h"
63 #include "uniquenames.h"
64 #include "modulesystem/singletonmodule.h"
65 #include "modulesystem/moduleregistry.h"
66 #include "stream/stringstream.h"
67 #include "signal/signal.h"
68
69 #include "gtkutil/filechooser.h"
70 #include "timer.h"
71 #include "select.h"
72 #include "plugin.h"
73 #include "filetypes.h"
74 #include "gtkdlgs.h"
75 #include "entityinspector.h"
76 #include "points.h"
77 #include "qe3.h"
78 #include "camwindow.h"
79 #include "xywindow.h"
80 #include "mainframe.h"
81 #include "preferences.h"
82 #include "referencecache.h"
83 #include "mru.h"
84 #include "commands.h"
85 #include "autosave.h"
86
87 class NameObserver
88 {
89   UniqueNames& m_names;
90   CopiedString m_name;
91
92   void construct()
93   {
94     if(!empty())
95     {
96       //globalOutputStream() << "construct " << makeQuoted(c_str()) << "\n";
97       m_names.insert(name_read(c_str()));
98     }
99   }
100   void destroy()
101   {
102     if(!empty())
103     {
104       //globalOutputStream() << "destroy " << makeQuoted(c_str()) << "\n";
105       m_names.erase(name_read(c_str()));
106     }
107   }
108
109   NameObserver& operator=(const NameObserver& other);
110 public:
111   NameObserver(UniqueNames& names) : m_names(names)
112   {
113     construct();
114   }
115   NameObserver(const NameObserver& other) : m_names(other.m_names), m_name(other.m_name)
116   {
117     construct();
118   }
119   ~NameObserver()
120   {
121     destroy();
122   }
123   bool empty() const
124   {
125     return string_empty(c_str()); 
126   }
127   const char* c_str() const
128   {
129     return m_name.c_str();
130   }
131   void nameChanged(const char* name)
132   {
133     destroy();
134     m_name = name;
135     construct();
136   }
137   typedef MemberCaller1<NameObserver, const char*, &NameObserver::nameChanged> NameChangedCaller;
138 };
139
140 class BasicNamespace : public Namespace
141 {
142   typedef std::map<NameCallback, NameObserver> Names;
143   Names m_names;
144   UniqueNames m_uniqueNames;
145 public:
146   ~BasicNamespace()
147   {
148     ASSERT_MESSAGE(m_names.empty(), "namespace: names still registered at shutdown");
149   }
150   void attach(const NameCallback& setName, const NameCallbackCallback& attachObserver)
151   {
152     std::pair<Names::iterator, bool> result = m_names.insert(Names::value_type(setName, m_uniqueNames));
153     ASSERT_MESSAGE(result.second, "cannot attach name");
154     attachObserver(NameObserver::NameChangedCaller((*result.first).second));
155     //globalOutputStream() << "attach: " << reinterpret_cast<const unsigned int&>(setName) << "\n";
156   }
157   void detach(const NameCallback& setName, const NameCallbackCallback& detachObserver)
158   {
159     Names::iterator i = m_names.find(setName);
160     ASSERT_MESSAGE(i != m_names.end(), "cannot detach name");
161     //globalOutputStream() << "detach: " << reinterpret_cast<const unsigned int&>(setName) << "\n";
162     detachObserver(NameObserver::NameChangedCaller((*i).second));
163     m_names.erase(i);
164   }
165
166   void makeUnique(const char* name, const NameCallback& setName) const
167   {
168     char buffer[1024];
169     name_write(buffer, m_uniqueNames.make_unique(name_read(name)));
170     setName(buffer);
171   }
172
173   void mergeNames(const BasicNamespace& other) const
174   {
175     typedef std::list<NameCallback> SetNameCallbacks;
176     typedef std::map<CopiedString, SetNameCallbacks> NameGroups;
177     NameGroups groups;
178
179     UniqueNames uniqueNames(other.m_uniqueNames);
180
181     for(Names::const_iterator i = m_names.begin(); i != m_names.end(); ++i)
182     {
183       groups[(*i).second.c_str()].push_back((*i).first);
184     }
185
186     for(NameGroups::iterator i = groups.begin(); i != groups.end(); ++i)
187     {
188       name_t uniqueName(uniqueNames.make_unique(name_read((*i).first.c_str())));
189       uniqueNames.insert(uniqueName);
190
191       char buffer[1024];
192       name_write(buffer, uniqueName);
193
194       //globalOutputStream() << "renaming " << makeQuoted((*i).first.c_str()) << " to " << makeQuoted(buffer) << "\n";
195
196       SetNameCallbacks& setNameCallbacks = (*i).second;
197
198       for(SetNameCallbacks::const_iterator j = setNameCallbacks.begin(); j != setNameCallbacks.end(); ++j)
199       {
200         (*j)(buffer);
201       }
202     }
203   }
204 };
205
206 BasicNamespace g_defaultNamespace;
207 BasicNamespace g_cloneNamespace;
208
209 class NamespaceAPI
210 {
211   Namespace* m_namespace;
212 public:
213   typedef Namespace Type;
214   STRING_CONSTANT(Name, "*");
215
216   NamespaceAPI()
217   {
218     m_namespace = &g_defaultNamespace;
219   }
220   Namespace* getTable()
221   {
222     return m_namespace;
223   }
224 };
225
226 typedef SingletonModule<NamespaceAPI> NamespaceModule;
227 typedef Static<NamespaceModule> StaticNamespaceModule;
228 StaticRegisterModule staticRegisterDefaultNamespace(StaticNamespaceModule::instance());
229
230
231 std::list<Namespaced*> g_cloned;
232
233 inline Namespaced* Node_getNamespaced(scene::Node& node)
234 {
235   return NodeTypeCast<Namespaced>::cast(node);
236 }
237
238 void Node_gatherNamespaced(scene::Node& node)
239 {
240   Namespaced* namespaced = Node_getNamespaced(node);
241   if(namespaced != 0)
242   {
243     g_cloned.push_back(namespaced);
244   }
245 }
246
247 class GatherNamespaced : public scene::Traversable::Walker
248 {
249 public:
250   bool pre(scene::Node& node) const
251   {
252     Node_gatherNamespaced(node);
253     return true;
254   }
255 };
256
257 void Map_gatherNamespaced(scene::Node& root)
258 {
259   Node_traverseSubgraph(root, GatherNamespaced());
260 }
261
262 void Map_mergeClonedNames()
263 {
264   for(std::list<Namespaced*>::const_iterator i = g_cloned.begin(); i != g_cloned.end(); ++i)
265   {
266     (*i)->setNamespace(g_cloneNamespace);
267   }
268   g_cloneNamespace.mergeNames(g_defaultNamespace);
269   for(std::list<Namespaced*>::const_iterator i = g_cloned.begin(); i != g_cloned.end(); ++i)
270   {
271     (*i)->setNamespace(g_defaultNamespace);
272   }
273
274   g_cloned.clear();
275 }
276
277 class WorldNode
278 {
279   scene::Node* m_node;
280 public:
281   WorldNode()
282     : m_node(0)
283   {
284   }
285   void set(scene::Node* node)
286   {
287     if(m_node != 0)
288       m_node->DecRef();
289     m_node = node;
290     if(m_node != 0)
291       m_node->IncRef();
292   }
293   scene::Node* get() const
294   {
295     return m_node;
296   }
297 };
298
299 class Map;
300 void Map_SetValid(Map& map, bool valid);
301 void Map_UpdateTitle(const Map& map);
302 void Map_SetWorldspawn(Map& map, scene::Node* node);
303
304
305 class Map : public ModuleObserver
306 {
307 public:
308   CopiedString m_name;
309   Resource* m_resource;
310   bool m_valid;
311
312   bool m_modified;
313   void (*m_modified_changed)(const Map&);
314
315   Signal0 m_mapValidCallbacks;
316
317   WorldNode m_world_node; // "classname" "worldspawn" !
318
319   Map() : m_resource(0), m_valid(false), m_modified_changed(Map_UpdateTitle)
320   {
321   }
322
323   void realise()
324   {
325     if(m_resource != 0)
326     {
327       if(Map_Unnamed(*this))
328       {
329         g_map.m_resource->setNode(NewMapRoot("").get_pointer());
330         MapFile* map = Node_getMapFile(*g_map.m_resource->getNode());
331         if(map != 0)
332         {
333           map->save();
334         }
335       }
336       else
337       {
338         m_resource->load();
339       }
340
341       GlobalSceneGraph().insert_root(*m_resource->getNode());
342
343       AutoSave_clear();
344
345       Map_SetValid(g_map, true);
346     }
347   }
348   void unrealise()
349   {
350     if(m_resource != 0)
351     {
352       Map_SetValid(g_map, false);
353       Map_SetWorldspawn(g_map, 0);
354
355
356       GlobalUndoSystem().clear();
357
358       GlobalSceneGraph().erase_root();
359     }
360   }
361 };
362
363 Map g_map;
364 Map* g_currentMap = 0;
365
366 void Map_addValidCallback(Map& map, const SignalHandler& handler)
367 {
368   map.m_mapValidCallbacks.connectLast(handler);
369 }
370
371 bool Map_Valid(const Map& map)
372 {
373   return map.m_valid;
374 }
375
376 void Map_SetValid(Map& map, bool valid)
377 {
378   map.m_valid = valid;
379   map.m_mapValidCallbacks();
380 }
381
382
383 const char* Map_Name(const Map& map)
384 {
385   return map.m_name.c_str();
386 }
387
388 bool Map_Unnamed(const Map& map)
389 {
390   return string_equal(Map_Name(map), "unnamed.map");
391 }
392
393 inline const MapFormat& MapFormat_forFile(const char* filename)
394 {
395   const char* moduleName = findModuleName(GetFileTypeRegistry(), MapFormat::Name(), path_get_extension(filename));
396   MapFormat* format = Radiant_getMapModules().findModule(moduleName);
397   ASSERT_MESSAGE(format != 0, "map format not found for file " << makeQuoted(filename));
398   return *format;
399 }
400
401 const MapFormat& Map_getFormat(const Map& map)
402 {
403   return MapFormat_forFile(Map_Name(map));
404 }
405
406
407 bool Map_Modified(const Map& map)
408 {
409   return map.m_modified;
410 }
411
412 void Map_SetModified(Map& map, bool modified)
413 {
414   if(map.m_modified ^ modified)
415   {
416     map.m_modified = modified;
417
418     map.m_modified_changed(map);
419   }
420 }
421
422 void Map_UpdateTitle(const Map& map)
423 {
424   Sys_SetTitle(map.m_name.c_str(), Map_Modified(map));
425 }
426
427
428
429 scene::Node* Map_GetWorldspawn(const Map& map)
430 {
431   return map.m_world_node.get();
432 }
433
434 void Map_SetWorldspawn(Map& map, scene::Node* node)
435 {
436   map.m_world_node.set(node);
437 }
438
439
440 // TTimo
441 // need that in a variable, will have to tweak depending on the game
442 float g_MaxWorldCoord = 64*1024;
443 float g_MinWorldCoord = -64*1024;
444
445 void AddRegionBrushes (void);
446 void RemoveRegionBrushes (void);
447
448
449 /*
450 ================
451 Map_Free
452 free all map elements, reinitialize the structures that depend on them
453 ================
454 */
455 void Map_Free()
456 {
457         Pointfile_Clear();
458
459   g_map.m_resource->detach(g_map);
460   GlobalReferenceCache().release(g_map.m_name.c_str());
461   g_map.m_resource = 0;
462
463   FlushReferences();
464
465   g_currentMap = 0;
466 }
467
468 class EntityFindByClassname : public scene::Graph::Walker
469 {
470   const char* m_name;
471   Entity*& m_entity;
472 public:
473   EntityFindByClassname(const char* name, Entity*& entity) : m_name(name), m_entity(entity)
474   {
475     m_entity = 0;
476   }
477   bool pre(const scene::Path& path, scene::Instance& instance) const
478   {
479     if(m_entity == 0)
480     {
481       Entity* entity = Node_getEntity(path.top());
482       if(entity != 0
483         && string_equal(m_name, entity->getKeyValue("classname")))
484       {
485         m_entity = entity;
486       }
487     }
488     return true;
489   }
490 };
491
492 Entity* Scene_FindEntityByClass(const char* name)
493 {
494   Entity* entity;
495   GlobalSceneGraph().traverse(EntityFindByClassname(name, entity));
496   return entity;
497 }
498
499 Entity *Scene_FindPlayerStart()
500 {
501   typedef const char* StaticString;
502   StaticString strings[] = {
503     "info_player_start",
504     "info_player_deathmatch",
505     "team_CTF_redplayer",
506     "team_CTF_blueplayer",
507     "team_CTF_redspawn",
508     "team_CTF_bluespawn",
509   };
510   typedef const StaticString* StaticStringIterator;
511   for(StaticStringIterator i = strings, end = strings+(sizeof(strings)/sizeof(StaticString)); i != end; ++i)
512   {
513     Entity* entity = Scene_FindEntityByClass(*i);
514     if(entity != 0)
515     {
516       return entity;
517     }
518   }
519   return 0;
520 }
521
522 //
523 // move the view to a start position
524 //
525
526
527 void FocusViews(const Vector3& point, float angle)
528 {
529   CamWnd& camwnd = *g_pParentWnd->GetCamWnd();
530   Camera_setOrigin(camwnd, point);
531   Vector3 angles(Camera_getAngles(camwnd));
532   angles[CAMERA_PITCH] = 0;
533   angles[CAMERA_YAW] = angle;
534   Camera_setAngles(camwnd, angles);
535
536   XYWnd* xywnd = g_pParentWnd->GetXYWnd();
537   xywnd->SetOrigin(point);
538 }
539
540 #include "stringio.h"
541
542 void Map_StartPosition()
543 {
544   Entity* entity = Scene_FindPlayerStart();
545
546   if (entity)
547   {
548     Vector3 origin;
549     string_parse_vector3(entity->getKeyValue("origin"), origin);
550     FocusViews(origin, string_read_float(entity->getKeyValue("angle")));
551   }
552   else
553   {
554     FocusViews(g_vector3_identity, 0);
555   }
556 }
557
558
559 inline bool node_is_worldspawn(scene::Node& node)
560 {
561   Entity* entity = Node_getEntity(node);
562   return entity != 0 && string_equal(entity->getKeyValue("classname"), "worldspawn");
563 }
564
565
566 // use first worldspawn
567 class entity_updateworldspawn : public scene::Traversable::Walker
568 {
569 public:
570   bool pre(scene::Node& node) const
571   {
572     if(node_is_worldspawn(node))
573     {
574       if(Map_GetWorldspawn(g_map) == 0)
575       {
576         Map_SetWorldspawn(g_map, &node);
577       }
578     }
579     return false;
580   }
581 };
582
583 scene::Node* Map_FindWorldspawn(Map& map)
584 {
585   Map_SetWorldspawn(map, 0);
586
587   Node_getTraversable(GlobalSceneGraph().root())->traverse(entity_updateworldspawn());
588
589   return Map_GetWorldspawn(map);
590 }
591
592
593 class CollectAllWalker : public scene::Traversable::Walker
594 {
595   scene::Node& m_root;
596   UnsortedNodeSet& m_nodes;
597 public:
598   CollectAllWalker(scene::Node& root, UnsortedNodeSet& nodes) : m_root(root), m_nodes(nodes)
599   {
600   }
601   bool pre(scene::Node& node) const
602   {
603     m_nodes.insert(NodeSmartReference(node));
604     Node_getTraversable(m_root)->erase(node);
605     return false;
606   }
607 };
608
609 void Node_insertChildFirst(scene::Node& parent, scene::Node& child)
610 {
611   UnsortedNodeSet nodes;
612   Node_getTraversable(parent)->traverse(CollectAllWalker(parent, nodes));
613   Node_getTraversable(parent)->insert(child);
614
615   for(UnsortedNodeSet::iterator i = nodes.begin(); i != nodes.end(); ++i)
616   {
617     Node_getTraversable(parent)->insert((*i));
618   }
619 }
620
621 scene::Node& createWorldspawn()
622 {
623   NodeSmartReference worldspawn(GlobalEntityCreator().createEntity(GlobalEntityClassManager().findOrInsert("worldspawn", true)));
624   Node_insertChildFirst(GlobalSceneGraph().root(), worldspawn);
625   return worldspawn;
626 }
627
628 void Map_UpdateWorldspawn(Map& map)
629 {
630   if(Map_FindWorldspawn(map) == 0)
631   {
632     Map_SetWorldspawn(map, &createWorldspawn());
633   }
634 }
635
636 scene::Node& Map_FindOrInsertWorldspawn(Map& map)
637 {
638   Map_UpdateWorldspawn(map);
639   return *Map_GetWorldspawn(map);
640 }
641
642
643 class MapMergeAll : public scene::Traversable::Walker
644 {
645   mutable scene::Path m_path;
646 public:
647   MapMergeAll(const scene::Path& root)
648     : m_path(root)
649   {
650   }
651   bool pre(scene::Node& node) const
652   {
653     Node_getTraversable(m_path.top())->insert(node);
654     m_path.push(makeReference(node));
655     selectPath(m_path, true);
656     return false;
657   }
658   void post(scene::Node& node) const
659   {
660     m_path.pop();
661   }
662 };
663
664 class MapMergeEntities : public scene::Traversable::Walker
665 {
666   mutable scene::Path m_path;
667 public:
668   MapMergeEntities(const scene::Path& root)
669     : m_path(root)
670   {
671   }
672   bool pre(scene::Node& node) const
673   {
674     if(node_is_worldspawn(node))
675     {
676       scene::Node* world_node = Map_FindWorldspawn(g_map);
677       if(world_node == 0)
678       {
679         Map_SetWorldspawn(g_map, &node);
680         Node_getTraversable(m_path.top().get())->insert(node);
681         m_path.push(makeReference(node));
682         Node_getTraversable(node)->traverse(SelectChildren(m_path));
683       }
684       else
685       {
686         m_path.push(makeReference(*world_node));
687         Node_getTraversable(node)->traverse(MapMergeAll(m_path));
688       }
689     }
690     else
691     {
692       Node_getTraversable(m_path.top())->insert(node);
693       m_path.push(makeReference(node));
694       if(node_is_group(node))
695       {
696         Node_getTraversable(node)->traverse(SelectChildren(m_path));
697       }
698       else
699       {
700         selectPath(m_path, true);
701       }
702     }
703     return false;
704   }
705   void post(scene::Node& node) const
706   {
707     m_path.pop();
708   }
709 };
710
711 class BasicContainer : public scene::Node::Symbiot
712 {
713   class TypeCasts
714   {
715     NodeTypeCastTable m_casts;
716   public:
717     TypeCasts()
718     {
719       NodeContainedCast<BasicContainer, scene::Traversable>::install(m_casts);
720     }
721     NodeTypeCastTable& get()
722     {
723       return m_casts;
724     }
725   };
726
727   scene::Node m_node;
728   TraversableNodeSet m_traverse;
729 public:
730
731   typedef LazyStatic<TypeCasts> StaticTypeCasts;
732
733   scene::Traversable& get(NullType<scene::Traversable>)
734   {
735     return m_traverse;
736   }
737
738   BasicContainer() : m_node(this, this, StaticTypeCasts::instance().get())
739   {
740   }
741   void release()
742   {
743     delete this;
744   }
745   scene::Node& node()
746   {
747     return m_node;
748   }
749 };
750
751 /// Merges the map graph rooted at \p node into the global scene-graph.
752 void MergeMap(scene::Node& node)
753 {
754   Node_getTraversable(node)->traverse(MapMergeEntities(scene::Path(makeReference(GlobalSceneGraph().root()))));
755 }
756 void Map_ImportSelected(TextInputStream& in, const MapFormat& format)
757 {
758   NodeSmartReference node((new BasicContainer)->node());
759   format.readGraph(node, in, GlobalEntityCreator());
760   Map_gatherNamespaced(node);
761   Map_mergeClonedNames();
762   MergeMap(node);
763 }
764
765 inline scene::Cloneable* Node_getCloneable(scene::Node& node)
766 {
767   return NodeTypeCast<scene::Cloneable>::cast(node);
768 }
769
770 inline scene::Node& node_clone(scene::Node& node)
771 {
772   scene::Cloneable* cloneable = Node_getCloneable(node);
773   if(cloneable != 0)
774   {
775     return cloneable->clone();
776   }
777   
778   return (new scene::NullNode)->node();
779 }
780
781 class CloneAll : public scene::Traversable::Walker
782 {
783   mutable scene::Path m_path;
784 public:
785   CloneAll(scene::Node& root)
786     : m_path(makeReference(root))
787   {
788   }
789   bool pre(scene::Node& node) const
790   {
791     if(node.isRoot())
792     {
793       return false;
794     }
795     
796     m_path.push(makeReference(node_clone(node)));
797     m_path.top().get().IncRef();
798
799     return true;
800   }
801   void post(scene::Node& node) const
802   {
803     if(node.isRoot())
804     {
805       return;
806     }
807
808     Node_getTraversable(m_path.parent())->insert(m_path.top());
809
810     m_path.top().get().DecRef();
811     m_path.pop();
812   }
813 };
814
815 scene::Node& Node_Clone(scene::Node& node)
816 {
817   scene::Node& clone = node_clone(node);
818   scene::Traversable* traversable = Node_getTraversable(node);
819   if(traversable != 0)
820   {
821     traversable->traverse(CloneAll(clone));
822   }
823   return clone;
824 }
825
826
827 typedef std::map<CopiedString, std::size_t> EntityBreakdown;
828
829 class EntityBreakdownWalker : public scene::Graph::Walker
830 {
831   EntityBreakdown& m_entitymap;
832 public:
833   EntityBreakdownWalker(EntityBreakdown& entitymap)
834     : m_entitymap(entitymap)
835   {
836   }
837   bool pre(const scene::Path& path, scene::Instance& instance) const
838   {
839     Entity* entity = Node_getEntity(path.top());
840     if(entity != 0)
841     {
842       const EntityClass& eclass = entity->getEntityClass();
843       if(m_entitymap.find(eclass.name()) == m_entitymap.end())
844       {
845         m_entitymap[eclass.name()] = 1;
846       }
847       else ++m_entitymap[eclass.name()];
848     }
849     return true;
850   }
851 };
852
853 void Scene_EntityBreakdown(EntityBreakdown& entitymap)
854 {
855   GlobalSceneGraph().traverse(EntityBreakdownWalker(entitymap));
856 }
857
858
859 WindowPosition g_posMapInfoWnd(c_default_window_pos);
860
861 void DoMapInfo()
862 {
863   ModalDialog dialog;
864   GtkEntry* brushes_entry;
865   GtkEntry* entities_entry;
866   GtkListStore* EntityBreakdownWalker;
867
868   GtkWindow* window = create_dialog_window(MainFrame_getWindow(), "Map Info", G_CALLBACK(dialog_delete_callback), &dialog);
869
870   window_set_position(window, g_posMapInfoWnd);
871
872   {
873     GtkVBox* vbox = create_dialog_vbox(4, 4);
874     gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(vbox));
875
876     {
877       GtkHBox* hbox = create_dialog_hbox(4);
878       gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(hbox), FALSE, TRUE, 0);
879
880       {
881         GtkTable* table = create_dialog_table(2, 2, 4, 4);
882         gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(table), TRUE, TRUE, 0);
883
884         {
885           GtkEntry* entry = GTK_ENTRY(gtk_entry_new());
886           gtk_widget_show(GTK_WIDGET(entry));
887           gtk_table_attach(table, GTK_WIDGET(entry), 1, 2, 0, 1,
888                             (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
889                             (GtkAttachOptions) (0), 0, 0);
890           gtk_entry_set_editable(entry, FALSE);
891
892           brushes_entry = entry;
893         }
894         {
895           GtkEntry* entry = GTK_ENTRY(gtk_entry_new());
896           gtk_widget_show(GTK_WIDGET(entry));
897           gtk_table_attach(table, GTK_WIDGET(entry), 1, 2, 1, 2,
898                             (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
899                             (GtkAttachOptions) (0), 0, 0);
900           gtk_entry_set_editable(entry, FALSE);
901
902           entities_entry = entry;
903         }
904         {
905           GtkWidget* label = gtk_label_new ("Total Brushes");
906           gtk_widget_show (label);
907           gtk_table_attach (GTK_TABLE (table), label, 0, 1, 0, 1,
908                             (GtkAttachOptions) (GTK_FILL),
909                             (GtkAttachOptions) (0), 0, 0);
910           gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
911         }
912         {
913           GtkWidget* label = gtk_label_new ("Total Entities");
914           gtk_widget_show (label);
915           gtk_table_attach (GTK_TABLE (table), label, 0, 1, 1, 2,
916                             (GtkAttachOptions) (GTK_FILL),
917                             (GtkAttachOptions) (0), 0, 0);
918           gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
919         }
920       }
921       {
922         GtkVBox* vbox2 = create_dialog_vbox(4);
923         gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(vbox2), FALSE, FALSE, 0);
924
925         {
926           GtkButton* button = create_dialog_button("Close", G_CALLBACK(dialog_button_ok), &dialog);
927           gtk_box_pack_start(GTK_BOX(vbox2), GTK_WIDGET(button), FALSE, FALSE, 0);
928         }
929       }
930     }
931     {
932       GtkWidget* label = gtk_label_new ("Entity breakdown");
933       gtk_widget_show (label);
934       gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(label), FALSE, TRUE, 0);
935       gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
936     }
937     {
938       GtkScrolledWindow* scr = create_scrolled_window(GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC, 4);
939       gtk_box_pack_start(GTK_BOX (vbox), GTK_WIDGET(scr), TRUE, TRUE, 0);
940
941       {
942         GtkListStore* store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_STRING);
943
944         GtkWidget* view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
945         gtk_tree_view_set_headers_clickable(GTK_TREE_VIEW(view), TRUE);
946
947         {
948           GtkCellRenderer* renderer = gtk_cell_renderer_text_new();
949           GtkTreeViewColumn* column = gtk_tree_view_column_new_with_attributes("Entity", renderer, "text", 0, 0);
950           gtk_tree_view_append_column(GTK_TREE_VIEW(view), column);
951           gtk_tree_view_column_set_sort_column_id(column, 0);
952         }
953
954         {
955           GtkCellRenderer* renderer = gtk_cell_renderer_text_new();
956           GtkTreeViewColumn* column = gtk_tree_view_column_new_with_attributes("Count", renderer, "text", 1, 0);
957           gtk_tree_view_append_column(GTK_TREE_VIEW(view), column);
958           gtk_tree_view_column_set_sort_column_id(column, 1);
959         }
960
961         gtk_widget_show(view);
962
963         gtk_container_add(GTK_CONTAINER(scr), view);
964         
965         EntityBreakdownWalker = store;
966       }
967     }
968   }
969
970   // Initialize fields
971
972   {
973     EntityBreakdown entitymap;
974     Scene_EntityBreakdown(entitymap);
975
976     for(EntityBreakdown::iterator i=entitymap.begin(); i != entitymap.end(); ++i)
977     {
978       char tmp[16];
979       sprintf (tmp, "%u", Unsigned((*i).second));
980       GtkTreeIter iter;
981       gtk_list_store_append(GTK_LIST_STORE(EntityBreakdownWalker), &iter);
982       gtk_list_store_set(GTK_LIST_STORE(EntityBreakdownWalker), &iter, 0, (*i).first.c_str(), 1, tmp, -1);
983     }
984   }
985
986   g_object_unref(G_OBJECT(EntityBreakdownWalker));
987
988   char tmp[16];
989   sprintf (tmp, "%u", Unsigned(g_brushCount.get()));
990   gtk_entry_set_text (GTK_ENTRY (brushes_entry), tmp);
991   sprintf (tmp, "%u", Unsigned(g_entityCount.get()));
992   gtk_entry_set_text (GTK_ENTRY (entities_entry), tmp);
993
994   modal_dialog_show(window, dialog);
995
996   // save before exit
997   window_get_position(window, g_posMapInfoWnd);
998
999   gtk_widget_destroy(GTK_WIDGET(window));
1000 }
1001
1002
1003
1004 class ScopeTimer
1005 {
1006   Timer m_timer;
1007   const char* m_message;
1008 public:
1009   ScopeTimer(const char* message)
1010     : m_message(message)
1011   {
1012     m_timer.start();
1013   }
1014   ~ScopeTimer()
1015   {
1016     double elapsed_time = m_timer.elapsed_msec() / 1000.f;
1017     globalOutputStream() << m_message << " timer: " << FloatFormat(elapsed_time, 5, 2) << " second(s) elapsed\n";
1018   }
1019 };
1020
1021 /*
1022 ================
1023 Map_LoadFile
1024 ================
1025 */
1026
1027 void Map_LoadFile (const char *filename)
1028 {
1029   globalOutputStream() << "Loading map from " << filename << "\n";
1030   ScopeDisableScreenUpdates disableScreenUpdates("Processing...", "Loading Map");
1031
1032   g_map.m_name = filename;
1033   Map_UpdateTitle(g_map);
1034
1035   {
1036     ScopeTimer timer("map load");
1037
1038     g_map.m_resource = GlobalReferenceCache().capture(g_map.m_name.c_str());
1039     g_map.m_resource->attach(g_map);
1040
1041     Node_getTraversable(GlobalSceneGraph().root())->traverse(entity_updateworldspawn());
1042   }
1043
1044   globalOutputStream() << "--- LoadMapFile ---\n";
1045   globalOutputStream() << g_map.m_name.c_str() << "\n";
1046   
1047   globalOutputStream() << makeLeftJustified(Unsigned(g_brushCount.get()), 5) << " primitive\n";
1048   globalOutputStream() << makeLeftJustified(Unsigned(g_entityCount.get()), 5) << " entities\n";
1049
1050   //GlobalEntityCreator().printStatistics();
1051   
1052   //
1053   // move the view to a start position
1054   //
1055   Map_StartPosition();
1056
1057   g_currentMap = &g_map;
1058 }
1059
1060 class Excluder
1061 {
1062 public:
1063   virtual bool excluded(scene::Node& node) const = 0;
1064 };
1065
1066 class ExcludeWalker : public scene::Traversable::Walker
1067 {
1068   const scene::Traversable::Walker& m_walker;
1069   const Excluder* m_exclude;
1070   mutable bool m_skip;
1071 public:
1072   ExcludeWalker(const scene::Traversable::Walker& walker, const Excluder& exclude)
1073     : m_walker(walker), m_exclude(&exclude), m_skip(false)
1074   {
1075   }
1076   bool pre(scene::Node& node) const
1077   {
1078     if(m_exclude->excluded(node) || node.isRoot())
1079     {
1080       m_skip = true;
1081       return false;
1082     }
1083     else
1084     {
1085       m_walker.pre(node);
1086     }
1087     return true;
1088   }
1089   void post(scene::Node& node) const
1090   {
1091     if(m_skip)
1092     {
1093       m_skip = false;
1094     }
1095     else
1096     {
1097       m_walker.post(node);
1098     }
1099   }
1100 };
1101
1102 class AnyInstanceSelected : public scene::Instantiable::Visitor
1103 {
1104   bool& m_selected;
1105 public:
1106   AnyInstanceSelected(bool& selected) : m_selected(selected)
1107   {
1108     m_selected = false;
1109   }
1110   void visit(scene::Instance& instance) const
1111   {
1112     Selectable* selectable = Instance_getSelectable(instance);
1113     if(selectable != 0
1114       && selectable->isSelected())
1115     {
1116       m_selected = true;
1117     }
1118   }
1119 };
1120
1121 bool Node_instanceSelected(scene::Node& node)
1122 {
1123   scene::Instantiable* instantiable = Node_getInstantiable(node);
1124   ASSERT_NOTNULL(instantiable);
1125   bool selected;
1126   instantiable->forEachInstance(AnyInstanceSelected(selected));
1127   return selected;
1128 }
1129
1130 class SelectedDescendantWalker : public scene::Traversable::Walker
1131 {
1132   bool& m_selected;
1133 public:
1134   SelectedDescendantWalker(bool& selected) : m_selected(selected)
1135   {
1136     m_selected = false;
1137   }
1138
1139   bool pre(scene::Node& node) const
1140   {
1141     if(node.isRoot())
1142     {
1143       return false;
1144     }
1145
1146     if(Node_instanceSelected(node))
1147     {
1148       m_selected = true;
1149     }
1150
1151     return true;
1152   }
1153 };
1154
1155 bool Node_selectedDescendant(scene::Node& node)
1156 {
1157   bool selected;
1158   Node_traverseSubgraph(node, SelectedDescendantWalker(selected));
1159   return selected;
1160 }
1161
1162 class SelectionExcluder : public Excluder
1163 {
1164 public:
1165   bool excluded(scene::Node& node) const
1166   {
1167     return !Node_selectedDescendant(node);
1168   }
1169 };
1170
1171 class IncludeSelectedWalker : public scene::Traversable::Walker
1172 {
1173   const scene::Traversable::Walker& m_walker;
1174   mutable std::size_t m_selected;
1175   mutable bool m_skip;
1176
1177   bool selectedParent() const
1178   {
1179     return m_selected != 0;
1180   }
1181 public:
1182   IncludeSelectedWalker(const scene::Traversable::Walker& walker)
1183     : m_walker(walker), m_selected(0), m_skip(false)
1184   {
1185   }
1186   bool pre(scene::Node& node) const
1187   {
1188     // include node if:
1189     // node is not a 'root' AND ( node is selected OR any child of node is selected OR any parent of node is selected )
1190     if(!node.isRoot() && (Node_selectedDescendant(node) || selectedParent()))
1191     {
1192       if(Node_instanceSelected(node))
1193       {
1194         ++m_selected;
1195       }
1196       m_walker.pre(node);
1197       return true;
1198     }
1199     else
1200     {
1201       m_skip = true;
1202       return false;
1203     }
1204   }
1205   void post(scene::Node& node) const
1206   {
1207     if(m_skip)
1208     {
1209       m_skip = false;
1210     }
1211     else
1212     {
1213       if(Node_instanceSelected(node))
1214       {
1215         --m_selected;
1216       }
1217       m_walker.post(node);
1218     }
1219   }
1220 };
1221
1222 void Map_Traverse_Selected(scene::Node& root, const scene::Traversable::Walker& walker)
1223 {
1224   scene::Traversable* traversable = Node_getTraversable(root);
1225   if(traversable != 0)
1226   {
1227 #if 0
1228     traversable->traverse(ExcludeWalker(walker, SelectionExcluder()));
1229 #else
1230     traversable->traverse(IncludeSelectedWalker(walker));
1231 #endif
1232   }
1233 }
1234
1235 void Map_ExportSelected(TextOutputStream& out, const MapFormat& format)
1236 {
1237   format.writeGraph(GlobalSceneGraph().root(), Map_Traverse_Selected, out);
1238 }
1239
1240 void Map_Traverse(scene::Node& root, const scene::Traversable::Walker& walker)
1241 {
1242   scene::Traversable* traversable = Node_getTraversable(root);
1243   if(traversable != 0)
1244   {
1245     traversable->traverse(walker);
1246   }
1247 }
1248
1249 class RegionExcluder : public Excluder
1250 {
1251 public:
1252   bool excluded(scene::Node& node) const
1253   {
1254     return node.excluded();
1255   }
1256 };
1257
1258 void Map_Traverse_Region(scene::Node& root, const scene::Traversable::Walker& walker)
1259 {
1260   scene::Traversable* traversable = Node_getTraversable(root);
1261   if(traversable != 0)
1262   {
1263     traversable->traverse(ExcludeWalker(walker, RegionExcluder()));
1264   }
1265 }
1266
1267 bool Map_SaveRegion(const char *filename)
1268 {
1269   AddRegionBrushes();
1270
1271   bool success = MapResource_saveFile(MapFormat_forFile(filename), GlobalSceneGraph().root(), Map_Traverse_Region, filename); 
1272
1273   RemoveRegionBrushes();
1274
1275   return success;
1276 }
1277
1278
1279 void Map_RenameAbsolute(const char* absolute)
1280 {
1281   Resource* resource = GlobalReferenceCache().capture(absolute);
1282   NodeSmartReference clone(NewMapRoot(path_make_relative(absolute, GlobalFileSystem().findRoot(absolute))));
1283   resource->setNode(clone.get_pointer());
1284
1285   {
1286     //ScopeTimer timer("clone subgraph");
1287     Node_getTraversable(GlobalSceneGraph().root())->traverse(CloneAll(clone));
1288   }
1289
1290   g_map.m_resource->detach(g_map);
1291   GlobalReferenceCache().release(g_map.m_name.c_str());
1292
1293   g_map.m_resource = resource;
1294
1295   g_map.m_name = absolute;
1296   Map_UpdateTitle(g_map);
1297
1298   g_map.m_resource->attach(g_map);
1299 }
1300
1301 void Map_Rename(const char* filename)
1302 {
1303   if(!string_equal(g_map.m_name.c_str(), filename))
1304   {
1305     ScopeDisableScreenUpdates disableScreenUpdates("Processing...", "Saving Map");
1306
1307     Map_RenameAbsolute(filename);
1308     
1309     SceneChangeNotify();
1310   }
1311   else
1312   {
1313     SaveReferences();
1314   }
1315 }
1316
1317 bool Map_Save()
1318 {
1319         Pointfile_Clear();
1320
1321   ScopeTimer timer("map save");
1322   SaveReferences();
1323   return true; // assume success..
1324 }
1325
1326 /*
1327 ===========
1328 Map_New
1329
1330 ===========
1331 */
1332 void Map_New()
1333 {
1334         //globalOutputStream() << "Map_New\n";
1335
1336         g_map.m_name = "unnamed.map";
1337   Map_UpdateTitle(g_map);
1338
1339   {
1340     g_map.m_resource = GlobalReferenceCache().capture(g_map.m_name.c_str());
1341 //    ASSERT_MESSAGE(g_map.m_resource->getNode() == 0, "bleh");
1342     g_map.m_resource->attach(g_map);
1343
1344     SceneChangeNotify();
1345   }
1346
1347   FocusViews(g_vector3_identity, 0);
1348
1349   g_currentMap = &g_map;
1350 }
1351
1352 extern void ConstructRegionBrushes(scene::Node* brushes[6], const Vector3& region_mins, const Vector3& region_maxs);
1353
1354 void ConstructRegionStartpoint(scene::Node* startpoint, const Vector3& region_mins, const Vector3& region_maxs)
1355 {
1356   /*! 
1357   \todo we need to make sure that the player start IS inside the region and bail out if it's not
1358   the compiler will refuse to compile a map with a player_start somewhere in empty space..
1359   for now, let's just print an error
1360   */
1361   
1362   Vector3 vOrig(Camera_getOrigin(*g_pParentWnd->GetCamWnd()));
1363
1364   for (int i=0 ; i<3 ; i++)
1365   {
1366     if (vOrig[i] > region_maxs[i] || vOrig[i] < region_mins[i])
1367     {
1368       globalErrorStream() << "Camera is NOT in the region, it's likely that the region won't compile correctly\n";
1369       break;
1370     }
1371   }
1372   
1373   // write the info_playerstart
1374   char sTmp[1024];
1375   sprintf(sTmp, "%d %d %d", (int)vOrig[0], (int)vOrig[1], (int)vOrig[2]);
1376   Node_getEntity(*startpoint)->setKeyValue("origin", sTmp);
1377   sprintf(sTmp, "%d", (int)Camera_getAngles(*g_pParentWnd->GetCamWnd())[CAMERA_YAW]);
1378   Node_getEntity(*startpoint)->setKeyValue("angle", sTmp);
1379 }
1380
1381 /*
1382 ===========================================================
1383
1384   REGION
1385
1386 ===========================================================
1387 */
1388 bool    region_active;
1389 Vector3 region_mins(g_MinWorldCoord, g_MinWorldCoord, g_MinWorldCoord);
1390 Vector3 region_maxs(g_MaxWorldCoord, g_MaxWorldCoord, g_MaxWorldCoord);
1391
1392 scene::Node* region_sides[6];
1393 scene::Node* region_startpoint = 0;
1394
1395 /*
1396 ===========
1397 AddRegionBrushes
1398 a regioned map will have temp walls put up at the region boundary
1399 \todo TODO TTimo old implementation of region brushes
1400   we still add them straight in the worldspawn and take them out after the map is saved
1401   with the new implementation we should be able to append them in a temporary manner to the data we pass to the map module
1402 ===========
1403 */
1404 void AddRegionBrushes (void)
1405 {
1406         int             i;
1407
1408   for(i=0; i<6; i++)
1409   {
1410     region_sides[i] = &GlobalBrushCreator().createBrush();
1411     Node_getTraversable(Map_FindOrInsertWorldspawn(g_map))->insert(NodeSmartReference(*region_sides[i]));
1412   }
1413
1414   region_startpoint = &GlobalEntityCreator().createEntity(GlobalEntityClassManager().findOrInsert("info_player_start", false));
1415
1416   ConstructRegionBrushes(region_sides, region_mins, region_maxs);
1417   ConstructRegionStartpoint(region_startpoint, region_mins, region_maxs);
1418
1419   Node_getTraversable(GlobalSceneGraph().root())->insert(NodeSmartReference(*region_startpoint));
1420 }
1421
1422 void RemoveRegionBrushes (void)
1423 {
1424   for(std::size_t i=0; i<6; i++)
1425   {
1426     Node_getTraversable(*Map_GetWorldspawn(g_map))->erase(*region_sides[i]);
1427   }
1428   Node_getTraversable(GlobalSceneGraph().root())->erase(*region_startpoint);
1429 }
1430
1431 inline void exclude_node(scene::Node& node, bool exclude)
1432 {
1433   exclude
1434     ? node.enable(scene::Node::eExcluded)
1435     : node.disable(scene::Node::eExcluded);
1436 }
1437
1438 class ExcludeAllWalker : public scene::Graph::Walker
1439 {
1440   bool m_exclude;
1441 public:
1442   ExcludeAllWalker(bool exclude)
1443     : m_exclude(exclude)
1444   {
1445   }
1446   bool pre(const scene::Path& path, scene::Instance& instance) const
1447   {
1448     exclude_node(path.top(), m_exclude);
1449
1450     return true;
1451   }
1452 };
1453
1454 void Scene_Exclude_All(bool exclude)
1455 {
1456   GlobalSceneGraph().traverse(ExcludeAllWalker(exclude));
1457 }
1458
1459 bool Instance_isSelected(const scene::Instance& instance)
1460 {
1461   const Selectable* selectable = Instance_getSelectable(instance);
1462   return selectable != 0 && selectable->isSelected();
1463 }
1464
1465 class ExcludeSelectedWalker : public scene::Graph::Walker
1466 {
1467   bool m_exclude;
1468 public:
1469   ExcludeSelectedWalker(bool exclude)
1470     : m_exclude(exclude)
1471   {
1472   }
1473   bool pre(const scene::Path& path, scene::Instance& instance) const
1474   {
1475     exclude_node(path.top(), (instance.isSelected() || instance.childSelected() || instance.parentSelected()) == m_exclude);
1476     return true;
1477   }
1478 };
1479
1480 void Scene_Exclude_Selected(bool exclude)
1481 {
1482   GlobalSceneGraph().traverse(ExcludeSelectedWalker(exclude));
1483 }
1484
1485 class ExcludeRegionedWalker : public scene::Graph::Walker
1486 {
1487   bool m_exclude;
1488 public:
1489   ExcludeRegionedWalker(bool exclude)
1490     : m_exclude(exclude)
1491   {
1492   }
1493   bool pre(const scene::Path& path, scene::Instance& instance) const
1494   {
1495     exclude_node(
1496       path.top(),
1497       !(
1498         (
1499           aabb_intersects_aabb(
1500             instance.worldAABB(),
1501             aabb_for_minmax(region_mins, region_maxs)
1502           ) != 0
1503         ) ^ m_exclude
1504       )
1505     );
1506
1507     return true;
1508   }
1509 };
1510
1511 void Scene_Exclude_Region(bool exclude)
1512 {
1513   GlobalSceneGraph().traverse(ExcludeRegionedWalker(exclude));
1514 }
1515
1516 /*
1517 ===========
1518 Map_RegionOff
1519
1520 Other filtering options may still be on
1521 ===========
1522 */
1523 void Map_RegionOff()
1524 {
1525         region_active = false;
1526
1527         region_maxs[0] = g_MaxWorldCoord - 64;
1528         region_mins[0] = g_MinWorldCoord + 64;
1529         region_maxs[1] = g_MaxWorldCoord - 64;
1530         region_mins[1] = g_MinWorldCoord + 64;
1531         region_maxs[2] = g_MaxWorldCoord - 64;
1532         region_mins[2] = g_MinWorldCoord + 64;
1533         
1534         Scene_Exclude_All(false);
1535 }
1536
1537 void Map_ApplyRegion (void)
1538 {
1539         region_active = true;
1540
1541         Scene_Exclude_Region(false);
1542 }
1543
1544
1545 /*
1546 ========================
1547 Map_RegionSelectedBrushes
1548 ========================
1549 */
1550 void Map_RegionSelectedBrushes (void)
1551 {
1552         Map_RegionOff();
1553
1554   if(GlobalSelectionSystem().countSelected() != 0
1555     && GlobalSelectionSystem().Mode() == SelectionSystem::ePrimitive)
1556   {
1557           region_active = true;
1558           Select_GetBounds (region_mins, region_maxs);
1559
1560           Scene_Exclude_Selected(false);
1561     
1562     GlobalSelectionSystem().setSelectedAll(false);
1563   }
1564 }
1565
1566
1567 /*
1568 ===========
1569 Map_RegionXY
1570 ===========
1571 */
1572 void Map_RegionXY(float x_min, float y_min, float x_max, float y_max)
1573 {
1574         Map_RegionOff();
1575
1576         region_mins[0] = x_min;
1577   region_maxs[0] = x_max;
1578   region_mins[1] = y_min;
1579   region_maxs[1] = y_max;
1580   region_mins[2] = g_MinWorldCoord + 64;
1581         region_maxs[2] = g_MaxWorldCoord - 64;
1582
1583         Map_ApplyRegion();
1584 }
1585
1586 void Map_RegionBounds(const AABB& bounds)
1587 {
1588   Map_RegionOff();
1589
1590   region_mins = vector3_subtracted(bounds.origin, bounds.extents);
1591   region_maxs = vector3_added(bounds.origin, bounds.extents);
1592
1593   deleteSelection();
1594
1595   Map_ApplyRegion();
1596 }
1597
1598 /*
1599 ===========
1600 Map_RegionBrush
1601 ===========
1602 */
1603 void Map_RegionBrush (void)
1604 {
1605   if(GlobalSelectionSystem().countSelected() != 0)
1606   {
1607     scene::Instance& instance = GlobalSelectionSystem().ultimateSelected();
1608     Map_RegionBounds(instance.worldAABB());
1609   }
1610 }
1611
1612 //
1613 //================
1614 //Map_ImportFile
1615 //================
1616 //
1617 bool Map_ImportFile(const char* filename)
1618 {
1619   ScopeDisableScreenUpdates disableScreenUpdates("Processing...", "Loading Map");
1620
1621   bool success = false;
1622   {
1623     Resource* resource = GlobalReferenceCache().capture(filename);
1624     resource->refresh(); // avoid loading old version if map has changed on disk since last import
1625     if(resource->load())
1626     {
1627       NodeSmartReference clone(NewMapRoot(""));
1628
1629       {
1630         //ScopeTimer timer("clone subgraph");
1631         Node_getTraversable(*resource->getNode())->traverse(CloneAll(clone));
1632       }
1633
1634       Map_gatherNamespaced(clone);
1635       Map_mergeClonedNames();
1636       MergeMap(clone);
1637       success = true;
1638     }
1639     GlobalReferenceCache().release(filename);
1640   }
1641
1642         SceneChangeNotify();
1643
1644   return success;
1645 }
1646
1647 /*
1648 ===========
1649 Map_SaveFile
1650 ===========
1651 */
1652 bool Map_SaveFile(const char* filename)
1653 {
1654   ScopeDisableScreenUpdates disableScreenUpdates("Processing...", "Saving Map");
1655   return MapResource_saveFile(MapFormat_forFile(filename), GlobalSceneGraph().root(), Map_Traverse, filename); 
1656 }
1657
1658 //
1659 //===========
1660 //Map_SaveSelected
1661 //===========
1662 //
1663 // Saves selected world brushes and whole entities with partial/full selections
1664 //
1665 bool Map_SaveSelected(const char* filename)
1666 {
1667   return MapResource_saveFile(MapFormat_forFile(filename), GlobalSceneGraph().root(), Map_Traverse_Selected, filename); 
1668 }
1669
1670
1671 class ParentSelectedBrushesToEntityWalker : public scene::Graph::Walker
1672 {
1673   scene::Node& m_parent;
1674 public:
1675   ParentSelectedBrushesToEntityWalker(scene::Node& parent) : m_parent(parent)
1676   {
1677   }
1678   bool pre(const scene::Path& path, scene::Instance& instance) const
1679   {
1680     if(path.top().get_pointer() != &m_parent
1681       && Node_isPrimitive(path.top()))
1682     {
1683       Selectable* selectable = Instance_getSelectable(instance);
1684       if(selectable != 0
1685         && selectable->isSelected()
1686         && path.size() > 1)
1687       {
1688         return false;
1689       }
1690     }
1691     return true;
1692   }
1693   void post(const scene::Path& path, scene::Instance& instance) const
1694   {
1695     if(path.top().get_pointer() != &m_parent
1696       && Node_isPrimitive(path.top()))
1697     {
1698       Selectable* selectable = Instance_getSelectable(instance);
1699       if(selectable != 0
1700         && selectable->isSelected()
1701         && path.size() > 1)
1702       {
1703         scene::Node& parent = path.parent();
1704         if(&parent != &m_parent)
1705         {
1706           NodeSmartReference node(path.top().get());
1707           Node_getTraversable(parent)->erase(node);
1708           Node_getTraversable(m_parent)->insert(node);
1709         }
1710       }
1711     }
1712   }
1713 };
1714
1715 void Scene_parentSelectedBrushesToEntity(scene::Graph& graph, scene::Node& parent)
1716 {
1717   graph.traverse(ParentSelectedBrushesToEntityWalker(parent));
1718 }
1719
1720 class CountSelectedBrushes : public scene::Graph::Walker
1721 {
1722   std::size_t& m_count;
1723   mutable std::size_t m_depth;
1724 public:
1725   CountSelectedBrushes(std::size_t& count) : m_count(count), m_depth(0)
1726   {
1727     m_count = 0;
1728   }
1729   bool pre(const scene::Path& path, scene::Instance& instance) const
1730   {
1731     if(++m_depth != 1 && path.top().get().isRoot())
1732     {
1733       return false;
1734     }
1735     Selectable* selectable = Instance_getSelectable(instance);
1736     if(selectable != 0
1737       && selectable->isSelected()
1738       && Node_isPrimitive(path.top()))
1739     {
1740       ++m_count;
1741     }
1742     return true;
1743   }
1744   void post(const scene::Path& path, scene::Instance& instance) const
1745   {
1746     --m_depth;
1747   }
1748 };
1749
1750 std::size_t Scene_countSelectedBrushes(scene::Graph& graph)
1751 {
1752   std::size_t count;
1753   graph.traverse(CountSelectedBrushes(count));
1754   return count;
1755 }
1756
1757 enum ENodeType
1758 {
1759   eNodeUnknown,
1760   eNodeMap,
1761   eNodeEntity,
1762   eNodePrimitive,
1763 };
1764
1765 const char* nodetype_get_name(ENodeType type)
1766 {
1767   if(type == eNodeMap)
1768     return "map";
1769   if(type == eNodeEntity)
1770     return "entity";
1771   if(type == eNodePrimitive)
1772     return "primitive";
1773   return "unknown";
1774 }
1775
1776 ENodeType node_get_nodetype(scene::Node& node)
1777 {
1778   if(Node_isEntity(node))
1779   {
1780     return eNodeEntity;
1781   }
1782   if(Node_isPrimitive(node))
1783   {
1784     return eNodePrimitive;
1785   }
1786   return eNodeUnknown;
1787 }
1788
1789 bool contains_entity(scene::Node& node)
1790 {
1791   return Node_getTraversable(node) != 0 && !Node_isBrush(node) && !Node_isPatch(node) && !Node_isEntity(node);
1792 }
1793
1794 bool contains_primitive(scene::Node& node)
1795 {
1796   return Node_isEntity(node) && Node_getTraversable(node) != 0 && Node_getEntity(node)->isContainer();
1797 }
1798
1799 ENodeType node_get_contains(scene::Node& node)
1800 {
1801   if(contains_entity(node))
1802   {
1803     return eNodeEntity;
1804   }
1805   if(contains_primitive(node))
1806   {
1807     return eNodePrimitive;
1808   }
1809   return eNodeUnknown;
1810 }
1811
1812 void Path_parent(const scene::Path& parent, const scene::Path& child)
1813 {
1814   ENodeType contains = node_get_contains(parent.top());
1815   ENodeType type = node_get_nodetype(child.top());
1816
1817   if(contains != eNodeUnknown && contains == type)
1818   {
1819     NodeSmartReference node(child.top().get());
1820     Path_deleteTop(child);
1821     Node_getTraversable(parent.top())->insert(node);
1822     SceneChangeNotify();
1823   }
1824   else
1825   {
1826     globalErrorStream() << "failed - " << nodetype_get_name(type) << " cannot be parented to " << nodetype_get_name(contains) << " container.\n";
1827   }
1828 }
1829
1830 void Scene_parentSelected()
1831 {
1832   UndoableCommand undo("parentSelected");
1833
1834   if(GlobalSelectionSystem().countSelected() > 1)
1835   {
1836     class ParentSelectedBrushesToEntityWalker : public SelectionSystem::Visitor
1837     {
1838       const scene::Path& m_parent;
1839     public:
1840       ParentSelectedBrushesToEntityWalker(const scene::Path& parent) : m_parent(parent)
1841       {
1842       }
1843       void visit(scene::Instance& instance) const
1844       {
1845         if(&m_parent != &instance.path())
1846         {
1847           Path_parent(m_parent, instance.path());
1848         }
1849       }
1850     };
1851     
1852     ParentSelectedBrushesToEntityWalker visitor(GlobalSelectionSystem().ultimateSelected().path());
1853     GlobalSelectionSystem().foreachSelected(visitor);
1854   }
1855   else
1856   {
1857     globalOutputStream() << "failed - did not find two selected nodes.\n";
1858   }
1859 }
1860
1861
1862
1863 void NewMap()
1864 {
1865   if (ConfirmModified("New Map"))
1866   {
1867     Map_RegionOff();
1868           Map_Free();
1869     Map_New();
1870   }
1871 }
1872
1873 CopiedString g_mapsPath;
1874
1875 const char* getMapsPath()
1876 {
1877   return g_mapsPath.c_str();
1878 }
1879
1880 const char* map_open(const char* title)
1881 {
1882   return file_dialog(GTK_WIDGET(MainFrame_getWindow()), TRUE, title, getMapsPath(), MapFormat::Name());
1883 }
1884
1885 const char* map_save(const char* title)
1886 {
1887   return file_dialog(GTK_WIDGET(MainFrame_getWindow()), FALSE, title, getMapsPath(), MapFormat::Name());
1888 }
1889
1890 void OpenMap()
1891 {
1892   if (!ConfirmModified("Open Map"))
1893     return;
1894
1895   const char* filename = map_open("Open Map");
1896
1897   if (filename != 0)
1898   {
1899     MRU_AddFile(filename);
1900     Map_RegionOff();
1901     Map_Free();
1902     Map_LoadFile(filename);
1903   }
1904 }
1905
1906 void ImportMap()
1907 {
1908   const char* filename = map_open("Import Map");
1909
1910   if(filename != 0)
1911   {
1912     UndoableCommand undo("mapImport");
1913     Map_ImportFile(filename);
1914   }
1915 }
1916
1917 bool Map_SaveAs()
1918 {
1919   const char* filename = map_save("Save Map");
1920   
1921   if(filename != 0)
1922   {
1923     MRU_AddFile(filename);
1924     Map_Rename(filename);
1925     return Map_Save();
1926   }
1927   return false;
1928 }
1929
1930 void SaveMapAs()
1931 {
1932   Map_SaveAs();
1933 }
1934
1935 void SaveMap()
1936 {
1937   if(Map_Unnamed(g_map))
1938   {
1939     SaveMapAs();
1940   }
1941   else if(Map_Modified(g_map))
1942   {
1943     Map_Save();
1944   }
1945 }
1946
1947 void ExportMap()
1948 {
1949   const char* filename = map_save("Export Selection");
1950
1951   if(filename != 0)
1952   {
1953     Map_SaveSelected(filename);
1954   }
1955 }
1956
1957 void SaveRegion()
1958 {
1959   const char* filename = map_save("Export Region");
1960   
1961   if(filename != 0)
1962   {
1963     Map_SaveRegion(filename);
1964   }
1965 }
1966
1967
1968 void RegionOff()
1969 {
1970   Map_RegionOff();
1971   SceneChangeNotify();
1972 }
1973
1974 void RegionXY()
1975 {
1976   Map_RegionXY(
1977     g_pParentWnd->GetXYWnd()->GetOrigin()[0] - 0.5f * g_pParentWnd->GetXYWnd()->Width() / g_pParentWnd->GetXYWnd()->Scale(),
1978     g_pParentWnd->GetXYWnd()->GetOrigin()[1] - 0.5f * g_pParentWnd->GetXYWnd()->Height() / g_pParentWnd->GetXYWnd()->Scale(),
1979     g_pParentWnd->GetXYWnd()->GetOrigin()[0] + 0.5f * g_pParentWnd->GetXYWnd()->Width() / g_pParentWnd->GetXYWnd()->Scale(),
1980     g_pParentWnd->GetXYWnd()->GetOrigin()[1] + 0.5f * g_pParentWnd->GetXYWnd()->Height() / g_pParentWnd->GetXYWnd()->Scale()
1981     );
1982   SceneChangeNotify();
1983 }
1984
1985 void RegionBrush()
1986 {
1987   Map_RegionBrush();
1988   SceneChangeNotify();
1989 }
1990
1991 void RegionSelected()
1992 {
1993   Map_RegionSelectedBrushes();
1994   SceneChangeNotify();
1995 }
1996
1997
1998
1999
2000
2001 class BrushFindByIndexWalker : public scene::Traversable::Walker
2002 {
2003   mutable std::size_t m_index;
2004   scene::Path& m_path;
2005 public:
2006   BrushFindByIndexWalker(std::size_t index, scene::Path& path)
2007     : m_index(index), m_path(path)
2008   {
2009   }
2010   bool pre(scene::Node& node) const
2011   {
2012     if(Node_isPrimitive(node) && m_index-- == 0)
2013     {
2014       m_path.push(makeReference(node));
2015     }
2016     return false;
2017   }
2018 };
2019
2020 class EntityFindByIndexWalker : public scene::Traversable::Walker
2021 {
2022   mutable std::size_t m_index;
2023   scene::Path& m_path;
2024 public:
2025   EntityFindByIndexWalker(std::size_t index, scene::Path& path)
2026     : m_index(index), m_path(path)
2027   {
2028   }
2029   bool pre(scene::Node& node) const
2030   {
2031     if(Node_isEntity(node) && m_index-- == 0)
2032     {
2033       m_path.push(makeReference(node));
2034     }
2035     return false;
2036   }
2037 };
2038
2039 void Scene_FindEntityBrush(std::size_t entity, std::size_t brush, scene::Path& path)
2040 {
2041   path.push(makeReference(GlobalSceneGraph().root()));
2042   {
2043     Node_getTraversable(path.top())->traverse(EntityFindByIndexWalker(entity, path));
2044   }
2045   if(path.size() == 2)
2046   {
2047     scene::Traversable* traversable = Node_getTraversable(path.top());
2048     if(traversable != 0)
2049     {
2050       traversable->traverse(BrushFindByIndexWalker(brush, path));
2051     }
2052   }
2053 }
2054
2055 inline bool Node_hasChildren(scene::Node& node)
2056 {
2057   scene::Traversable* traversable = Node_getTraversable(node);
2058   return traversable != 0 && !traversable->empty();
2059 }
2060
2061 void SelectBrush (int entitynum, int brushnum)
2062 {
2063   scene::Path path;
2064   Scene_FindEntityBrush(entitynum, brushnum, path);
2065   if(path.size() == 3 || (path.size() == 2 && !Node_hasChildren(path.top())))
2066   {
2067     scene::Instance* instance = GlobalSceneGraph().find(path);
2068     ASSERT_MESSAGE(instance != 0, "SelectBrush: path not found in scenegraph");
2069     Selectable* selectable = Instance_getSelectable(*instance);
2070     ASSERT_MESSAGE(selectable != 0, "SelectBrush: path not selectable");
2071     selectable->setSelected(true);
2072     g_pParentWnd->GetXYWnd()->PositionView(instance->worldAABB().origin);
2073   }
2074 }
2075
2076
2077 class BrushFindIndexWalker : public scene::Graph::Walker
2078 {
2079   mutable const scene::Node* m_node;
2080   std::size_t& m_count;
2081 public:
2082   BrushFindIndexWalker(const scene::Node& node, std::size_t& count)
2083     : m_node(&node), m_count(count)
2084   {
2085   }
2086   bool pre(const scene::Path& path, scene::Instance& instance) const
2087   {
2088     if(Node_isPrimitive(path.top()))
2089     {
2090       if(m_node == path.top().get_pointer())
2091       {
2092         m_node = 0;
2093       }
2094       if(m_node)
2095       {
2096         ++m_count;
2097       }
2098     }
2099     return true;
2100   }
2101 };
2102
2103 class EntityFindIndexWalker : public scene::Graph::Walker
2104 {
2105   mutable const scene::Node* m_node;
2106   std::size_t& m_count;
2107 public:
2108   EntityFindIndexWalker(const scene::Node& node, std::size_t& count)
2109     : m_node(&node), m_count(count)
2110   {
2111   }
2112   bool pre(const scene::Path& path, scene::Instance& instance) const
2113   {
2114     if(Node_isEntity(path.top()))
2115     {
2116       if(m_node == path.top().get_pointer())
2117       {
2118         m_node = 0;
2119       }
2120       if(m_node)
2121       {
2122         ++m_count;
2123       }
2124     }
2125     return true;
2126   }
2127 };
2128
2129 static void GetSelectionIndex (int *ent, int *brush)
2130 {
2131   std::size_t count_brush = 0;
2132   std::size_t count_entity = 0;
2133   if(GlobalSelectionSystem().countSelected() != 0)
2134   {
2135     const scene::Path& path = GlobalSelectionSystem().ultimateSelected().path();
2136
2137     GlobalSceneGraph().traverse(BrushFindIndexWalker(path.top(), count_brush));
2138     GlobalSceneGraph().traverse(EntityFindIndexWalker(path.parent(), count_entity));
2139   }
2140   *brush = int(count_brush);
2141   *ent = int(count_entity);
2142 }
2143
2144 void DoFind()
2145 {
2146   ModalDialog dialog;
2147   GtkEntry* entity;
2148   GtkEntry* brush;
2149
2150   GtkWindow* window = create_dialog_window(MainFrame_getWindow(), "Find Brush", G_CALLBACK(dialog_delete_callback), &dialog);
2151
2152   GtkAccelGroup* accel = gtk_accel_group_new();
2153   gtk_window_add_accel_group(window, accel);
2154
2155   {
2156     GtkVBox* vbox = create_dialog_vbox(4, 4);
2157     gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(vbox));
2158     {
2159       GtkTable* table = create_dialog_table(2, 2, 4, 4);
2160       gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(table), TRUE, TRUE, 0);
2161       {
2162         GtkWidget* label = gtk_label_new ("Entity number");
2163         gtk_widget_show (label);
2164         gtk_table_attach (GTK_TABLE (table), label, 0, 1, 0, 1,
2165                           (GtkAttachOptions) (0),
2166                           (GtkAttachOptions) (0), 0, 0);
2167       }
2168       {
2169         GtkWidget* label = gtk_label_new ("Brush number");
2170         gtk_widget_show (label);
2171         gtk_table_attach (GTK_TABLE (table), label, 0, 1, 1, 2,
2172                           (GtkAttachOptions) (0),
2173                           (GtkAttachOptions) (0), 0, 0);
2174       }
2175       {
2176         GtkEntry* entry = GTK_ENTRY(gtk_entry_new());
2177         gtk_widget_show(GTK_WIDGET(entry));
2178         gtk_table_attach(table, GTK_WIDGET(entry), 1, 2, 0, 1,
2179                           (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
2180                           (GtkAttachOptions) (0), 0, 0);
2181         gtk_widget_grab_focus(GTK_WIDGET(entry));
2182         entity = entry;
2183       }
2184       {
2185         GtkEntry* entry = GTK_ENTRY(gtk_entry_new());
2186         gtk_widget_show(GTK_WIDGET(entry));
2187         gtk_table_attach(table, GTK_WIDGET(entry), 1, 2, 1, 2,
2188                           (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
2189                           (GtkAttachOptions) (0), 0, 0);
2190
2191         brush = entry;
2192       }
2193     }
2194     {
2195       GtkHBox* hbox = create_dialog_hbox(4);
2196       gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(hbox), TRUE, TRUE, 0);
2197       {
2198         GtkButton* button = create_dialog_button("Find", G_CALLBACK(dialog_button_ok), &dialog);
2199         gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(button), FALSE, FALSE, 0);
2200         widget_make_default(GTK_WIDGET(button));
2201         gtk_widget_add_accelerator(GTK_WIDGET(button), "clicked", accel, GDK_Return, (GdkModifierType)0, (GtkAccelFlags)0);
2202       }
2203       {
2204         GtkButton* button = create_dialog_button("Close", G_CALLBACK(dialog_button_cancel), &dialog);
2205         gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(button), FALSE, FALSE, 0);
2206         gtk_widget_add_accelerator(GTK_WIDGET(button), "clicked", accel, GDK_Escape, (GdkModifierType)0, (GtkAccelFlags)0);
2207       }
2208     }
2209   }
2210
2211   // Initialize dialog
2212   char buf[16];
2213   int ent, br;
2214
2215   GetSelectionIndex (&ent, &br);
2216   sprintf (buf, "%i", ent);
2217   gtk_entry_set_text(entity, buf);
2218   sprintf (buf, "%i", br);
2219   gtk_entry_set_text(brush, buf);
2220
2221   if(modal_dialog_show(window, dialog) == eIDOK)
2222   {
2223     const char *entstr = gtk_entry_get_text(entity);
2224     const char *brushstr = gtk_entry_get_text(brush);
2225     SelectBrush (atoi(entstr), atoi(brushstr));
2226   }
2227
2228   gtk_widget_destroy(GTK_WIDGET(window));
2229 }
2230
2231 void Map_constructPreferences(PreferencesPage& page)
2232 {
2233   page.appendCheckBox("", "Load last map on open", g_bLoadLastMap);
2234 }
2235
2236
2237 class MapEntityClasses : public ModuleObserver
2238 {
2239   std::size_t m_unrealised;
2240 public:
2241   MapEntityClasses() : m_unrealised(1)
2242   {
2243   }
2244   void realise()
2245   {
2246     if(--m_unrealised == 0)
2247     {
2248       if(g_map.m_resource != 0)
2249       {
2250         ScopeDisableScreenUpdates disableScreenUpdates("Processing...", "Loading Map");
2251               g_map.m_resource->realise();
2252       }
2253     }
2254   }
2255   void unrealise()
2256   {
2257     if(++m_unrealised == 1)
2258     {
2259       if(g_map.m_resource != 0)
2260       {
2261         g_map.m_resource->flush();
2262               g_map.m_resource->unrealise();
2263       }
2264     }
2265   }
2266 };
2267
2268 MapEntityClasses g_MapEntityClasses;
2269
2270
2271 class MapModuleObserver : public ModuleObserver
2272 {
2273   std::size_t m_unrealised;
2274 public:
2275   MapModuleObserver() : m_unrealised(1)
2276   {
2277   }
2278   void realise()
2279   {
2280     if(--m_unrealised == 0)
2281     {
2282       ASSERT_MESSAGE(!string_empty(g_qeglobals.m_userGamePath.c_str()), "maps_directory: user-game-path is empty");
2283       StringOutputStream buffer(256);
2284       buffer << g_qeglobals.m_userGamePath.c_str() << "maps/";
2285       Q_mkdir(buffer.c_str());
2286       g_mapsPath = buffer.c_str();
2287     }
2288   }
2289   void unrealise()
2290   {
2291     if(++m_unrealised == 1)
2292     {
2293       g_mapsPath = "";
2294     }
2295   }
2296 };
2297
2298 MapModuleObserver g_MapModuleObserver;
2299
2300 #include "preferencesystem.h"
2301
2302 CopiedString g_strLastMap;
2303 bool g_bLoadLastMap = false;
2304
2305 void Map_Construct()
2306 {
2307   GlobalCommands_insert("RegionOff", FreeCaller<RegionOff>());
2308   GlobalCommands_insert("RegionSetXY", FreeCaller<RegionXY>());
2309   GlobalCommands_insert("RegionSetBrush", FreeCaller<RegionBrush>());
2310   GlobalCommands_insert("RegionSetSelection", FreeCaller<RegionSelected>(), Accelerator('R', (GdkModifierType)(GDK_SHIFT_MASK|GDK_CONTROL_MASK)));
2311
2312   GlobalPreferenceSystem().registerPreference("LastMap", CopiedStringImportStringCaller(g_strLastMap), CopiedStringExportStringCaller(g_strLastMap));
2313   GlobalPreferenceSystem().registerPreference("LoadLastMap", BoolImportStringCaller(g_bLoadLastMap), BoolExportStringCaller(g_bLoadLastMap));
2314   GlobalPreferenceSystem().registerPreference("MapInfoDlg", WindowPositionImportStringCaller(g_posMapInfoWnd), WindowPositionExportStringCaller(g_posMapInfoWnd));
2315   
2316   PreferencesDialog_addSettingsPreferences(FreeCaller1<PreferencesPage&, Map_constructPreferences>());
2317
2318   GlobalEntityClassManager().attach(g_MapEntityClasses);
2319   Radiant_attachHomePathsObserver(g_MapModuleObserver);
2320 }
2321
2322 void Map_Destroy()
2323 {
2324   Radiant_detachHomePathsObserver(g_MapModuleObserver);
2325   GlobalEntityClassManager().detach(g_MapEntityClasses);
2326 }