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