]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - radiant/map.cpp
added buffering to minimise GtkTextBuffer insert calls
[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
68 #include "gtkutil/filechooser.h"
69 #include "timer.h"
70 #include "select.h"
71 #include "plugin.h"
72 #include "filetypes.h"
73 #include "gtkdlgs.h"
74 #include "entityinspector.h"
75 #include "points.h"
76 #include "qe3.h"
77 #include "camwindow.h"
78 #include "xywindow.h"
79 #include "mainframe.h"
80 #include "preferences.h"
81 #include "referencecache.h"
82 #include "mru.h"
83 #include "commands.h"
84 #include "autosave.h"
85
86 class NameObserver
87 {
88   UniqueNames& m_names;
89   CopiedString m_name;
90
91   void construct()
92   {
93     if(!empty())
94     {
95       //globalOutputStream() << "construct " << makeQuoted(c_str()) << "\n";
96       m_names.insert(name_read(c_str()));
97     }
98   }
99   void destroy()
100   {
101     if(!empty())
102     {
103       //globalOutputStream() << "destroy " << makeQuoted(c_str()) << "\n";
104       m_names.erase(name_read(c_str()));
105     }
106   }
107
108   NameObserver& operator=(const NameObserver& other);
109 public:
110   NameObserver(UniqueNames& names) : m_names(names)
111   {
112     construct();
113   }
114   NameObserver(const NameObserver& other) : m_names(other.m_names), m_name(other.m_name)
115   {
116     construct();
117   }
118   ~NameObserver()
119   {
120     destroy();
121   }
122   bool empty() const
123   {
124     return string_empty(c_str()); 
125   }
126   const char* c_str() const
127   {
128     return m_name.c_str();
129   }
130   void nameChanged(const char* name)
131   {
132     destroy();
133     m_name = name;
134     construct();
135   }
136   typedef MemberCaller1<NameObserver, const char*, &NameObserver::nameChanged> NameChangedCaller;
137 };
138
139 class BasicNamespace : public Namespace
140 {
141   typedef std::map<NameCallback, NameObserver> Names;
142   Names m_names;
143   UniqueNames m_uniqueNames;
144 public:
145   ~BasicNamespace()
146   {
147     ASSERT_MESSAGE(m_names.empty(), "namespace: names still registered at shutdown");
148   }
149   void attach(const NameCallback& setName, const NameCallbackCallback& attachObserver)
150   {
151     std::pair<Names::iterator, bool> result = m_names.insert(Names::value_type(setName, m_uniqueNames));
152     ASSERT_MESSAGE(result.second, "cannot attach name");
153     attachObserver(NameObserver::NameChangedCaller((*result.first).second));
154     //globalOutputStream() << "attach: " << reinterpret_cast<const unsigned int&>(setName) << "\n";
155   }
156   void detach(const NameCallback& setName, const NameCallbackCallback& detachObserver)
157   {
158     Names::iterator i = m_names.find(setName);
159     ASSERT_MESSAGE(i != m_names.end(), "cannot detach name");
160     //globalOutputStream() << "detach: " << reinterpret_cast<const unsigned int&>(setName) << "\n";
161     detachObserver(NameObserver::NameChangedCaller((*i).second));
162     m_names.erase(i);
163   }
164
165   void makeUnique(const char* name, const NameCallback& setName) const
166   {
167     char buffer[1024];
168     name_write(buffer, m_uniqueNames.make_unique(name_read(name)));
169     setName(buffer);
170   }
171
172   void mergeNames(const BasicNamespace& other) const
173   {
174     typedef std::list<NameCallback> SetNameCallbacks;
175     typedef std::map<CopiedString, SetNameCallbacks> NameGroups;
176     NameGroups groups;
177
178     UniqueNames uniqueNames(other.m_uniqueNames);
179
180     for(Names::const_iterator i = m_names.begin(); i != m_names.end(); ++i)
181     {
182       groups[(*i).second.c_str()].push_back((*i).first);
183     }
184
185     for(NameGroups::iterator i = groups.begin(); i != groups.end(); ++i)
186     {
187       name_t uniqueName(uniqueNames.make_unique(name_read((*i).first.c_str())));
188       uniqueNames.insert(uniqueName);
189
190       char buffer[1024];
191       name_write(buffer, uniqueName);
192
193       //globalOutputStream() << "renaming " << makeQuoted((*i).first.c_str()) << " to " << makeQuoted(buffer) << "\n";
194
195       SetNameCallbacks& setNameCallbacks = (*i).second;
196
197       for(SetNameCallbacks::const_iterator j = setNameCallbacks.begin(); j != setNameCallbacks.end(); ++j)
198       {
199         (*j)(buffer);
200       }
201     }
202   }
203 };
204
205 BasicNamespace g_defaultNamespace;
206 BasicNamespace g_cloneNamespace;
207
208 class NamespaceAPI
209 {
210   Namespace* m_namespace;
211 public:
212   typedef Namespace Type;
213   STRING_CONSTANT(Name, "*");
214
215   NamespaceAPI()
216   {
217     m_namespace = &g_defaultNamespace;
218   }
219   Namespace* getTable()
220   {
221     return m_namespace;
222   }
223 };
224
225 typedef SingletonModule<NamespaceAPI> NamespaceModule;
226 typedef Static<NamespaceModule> StaticNamespaceModule;
227 StaticRegisterModule staticRegisterDefaultNamespace(StaticNamespaceModule::instance());
228
229
230 std::list<Namespaced*> g_cloned;
231
232 inline Namespaced* Node_getNamespaced(scene::Node& node)
233 {
234   return NodeTypeCast<Namespaced>::cast(node);
235 }
236
237 void Node_gatherNamespaced(scene::Node& node)
238 {
239   Namespaced* namespaced = Node_getNamespaced(node);
240   if(namespaced != 0)
241   {
242     g_cloned.push_back(namespaced);
243   }
244 }
245
246 class GatherNamespaced : public scene::Traversable::Walker
247 {
248 public:
249   bool pre(scene::Node& node) const
250   {
251     Node_gatherNamespaced(node);
252     return true;
253   }
254 };
255
256 void Map_gatherNamespaced(scene::Node& root)
257 {
258   Node_traverseSubgraph(root, GatherNamespaced());
259 }
260
261 void Map_mergeClonedNames()
262 {
263   for(std::list<Namespaced*>::const_iterator i = g_cloned.begin(); i != g_cloned.end(); ++i)
264   {
265     (*i)->setNamespace(g_cloneNamespace);
266   }
267   g_cloneNamespace.mergeNames(g_defaultNamespace);
268   for(std::list<Namespaced*>::const_iterator i = g_cloned.begin(); i != g_cloned.end(); ++i)
269   {
270     (*i)->setNamespace(g_defaultNamespace);
271   }
272
273   g_cloned.clear();
274 }
275
276 class WorldNode
277 {
278   scene::Node* m_node;
279 public:
280   WorldNode()
281     : m_node(0)
282   {
283   }
284   void set(scene::Node* node)
285   {
286     if(m_node != 0)
287       m_node->DecRef();
288     m_node = node;
289     if(m_node != 0)
290       m_node->IncRef();
291   }
292   scene::Node* get() const
293   {
294     return m_node;
295   }
296 };
297
298 class Map;
299 void Map_SetValid(Map& map, bool valid);
300 void Map_UpdateTitle(const Map& map);
301 void Map_SetWorldspawn(Map& map, scene::Node* node);
302
303
304 class Map : public ModuleObserver
305 {
306 public:
307   CopiedString m_name;
308   Resource* m_resource;
309   bool m_valid;
310
311   bool m_modified;
312   void (*m_modified_changed)(const Map&);
313
314   typedef std::vector<Callback> MapValidCallbacks;
315   MapValidCallbacks 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 Callback& callback)
367 {
368   map.m_mapValidCallbacks.push_back(callback);
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   std::for_each(map.m_mapValidCallbacks.begin(), map.m_mapValidCallbacks.end(), CallbackInvoke());
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   //
1051   // move the view to a start position
1052   //
1053   Map_StartPosition();
1054
1055   g_currentMap = &g_map;
1056 }
1057
1058 class Excluder
1059 {
1060 public:
1061   virtual bool excluded(scene::Node& node) const = 0;
1062 };
1063
1064 class ExcludeWalker : public scene::Traversable::Walker
1065 {
1066   const scene::Traversable::Walker& m_walker;
1067   const Excluder* m_exclude;
1068   mutable bool m_skip;
1069 public:
1070   ExcludeWalker(const scene::Traversable::Walker& walker, const Excluder& exclude)
1071     : m_walker(walker), m_exclude(&exclude), m_skip(false)
1072   {
1073   }
1074   bool pre(scene::Node& node) const
1075   {
1076     if(m_exclude->excluded(node) || node.isRoot())
1077     {
1078       m_skip = true;
1079       return false;
1080     }
1081     else
1082     {
1083       m_walker.pre(node);
1084     }
1085     return true;
1086   }
1087   void post(scene::Node& node) const
1088   {
1089     if(m_skip)
1090     {
1091       m_skip = false;
1092     }
1093     else
1094     {
1095       m_walker.post(node);
1096     }
1097   }
1098 };
1099
1100 class AnyInstanceSelected : public scene::Instantiable::Visitor
1101 {
1102   bool& m_selected;
1103 public:
1104   AnyInstanceSelected(bool& selected) : m_selected(selected)
1105   {
1106     m_selected = false;
1107   }
1108   void visit(scene::Instance& instance) const
1109   {
1110     Selectable* selectable = Instance_getSelectable(instance);
1111     if(selectable != 0
1112       && selectable->isSelected())
1113     {
1114       m_selected = true;
1115     }
1116   }
1117 };
1118
1119 bool Node_instanceSelected(scene::Node& node)
1120 {
1121   scene::Instantiable* instantiable = Node_getInstantiable(node);
1122   ASSERT_NOTNULL(instantiable);
1123   bool selected;
1124   instantiable->forEachInstance(AnyInstanceSelected(selected));
1125   return selected;
1126 }
1127
1128 class SelectedDescendantWalker : public scene::Traversable::Walker
1129 {
1130   bool& m_selected;
1131 public:
1132   SelectedDescendantWalker(bool& selected) : m_selected(selected)
1133   {
1134     m_selected = false;
1135   }
1136
1137   bool pre(scene::Node& node) const
1138   {
1139     if(node.isRoot())
1140     {
1141       return false;
1142     }
1143
1144     if(Node_instanceSelected(node))
1145     {
1146       m_selected = true;
1147     }
1148
1149     return true;
1150   }
1151 };
1152
1153 bool Node_selectedDescendant(scene::Node& node)
1154 {
1155   bool selected;
1156   Node_traverseSubgraph(node, SelectedDescendantWalker(selected));
1157   return selected;
1158 }
1159
1160 class SelectionExcluder : public Excluder
1161 {
1162 public:
1163   bool excluded(scene::Node& node) const
1164   {
1165     return !Node_selectedDescendant(node);
1166   }
1167 };
1168
1169 class IncludeSelectedWalker : public scene::Traversable::Walker
1170 {
1171   const scene::Traversable::Walker& m_walker;
1172   mutable std::size_t m_selected;
1173   mutable bool m_skip;
1174
1175   bool selectedParent() const
1176   {
1177     return m_selected != 0;
1178   }
1179 public:
1180   IncludeSelectedWalker(const scene::Traversable::Walker& walker)
1181     : m_walker(walker), m_selected(0), m_skip(false)
1182   {
1183   }
1184   bool pre(scene::Node& node) const
1185   {
1186     // include node if:
1187     // node is not a 'root' AND ( node is selected OR any child of node is selected OR any parent of node is selected )
1188     if(!node.isRoot() && (Node_selectedDescendant(node) || selectedParent()))
1189     {
1190       if(Node_instanceSelected(node))
1191       {
1192         ++m_selected;
1193       }
1194       m_walker.pre(node);
1195       return true;
1196     }
1197     else
1198     {
1199       m_skip = true;
1200       return false;
1201     }
1202   }
1203   void post(scene::Node& node) const
1204   {
1205     if(m_skip)
1206     {
1207       m_skip = false;
1208     }
1209     else
1210     {
1211       if(Node_instanceSelected(node))
1212       {
1213         --m_selected;
1214       }
1215       m_walker.post(node);
1216     }
1217   }
1218 };
1219
1220 void Map_Traverse_Selected(scene::Node& root, const scene::Traversable::Walker& walker)
1221 {
1222   scene::Traversable* traversable = Node_getTraversable(root);
1223   if(traversable != 0)
1224   {
1225 #if 0
1226     traversable->traverse(ExcludeWalker(walker, SelectionExcluder()));
1227 #else
1228     traversable->traverse(IncludeSelectedWalker(walker));
1229 #endif
1230   }
1231 }
1232
1233 void Map_ExportSelected(TextOutputStream& out, const MapFormat& format)
1234 {
1235   format.writeGraph(GlobalSceneGraph().root(), Map_Traverse_Selected, out);
1236 }
1237
1238 void Map_Traverse(scene::Node& root, const scene::Traversable::Walker& walker)
1239 {
1240   scene::Traversable* traversable = Node_getTraversable(root);
1241   if(traversable != 0)
1242   {
1243     traversable->traverse(walker);
1244   }
1245 }
1246
1247 class RegionExcluder : public Excluder
1248 {
1249 public:
1250   bool excluded(scene::Node& node) const
1251   {
1252     return node.excluded();
1253   }
1254 };
1255
1256 void Map_Traverse_Region(scene::Node& root, const scene::Traversable::Walker& walker)
1257 {
1258   scene::Traversable* traversable = Node_getTraversable(root);
1259   if(traversable != 0)
1260   {
1261     traversable->traverse(ExcludeWalker(walker, RegionExcluder()));
1262   }
1263 }
1264
1265 bool Map_SaveRegion(const char *filename)
1266 {
1267   AddRegionBrushes();
1268
1269   bool success = MapResource_saveFile(MapFormat_forFile(filename), GlobalSceneGraph().root(), Map_Traverse_Region, filename); 
1270
1271   RemoveRegionBrushes();
1272
1273   return success;
1274 }
1275
1276
1277 void Map_RenameAbsolute(const char* absolute)
1278 {
1279   Resource* resource = GlobalReferenceCache().capture(absolute);
1280   NodeSmartReference clone(NewMapRoot(path_make_relative(absolute, GlobalFileSystem().findRoot(absolute))));
1281   resource->setNode(clone.get_pointer());
1282
1283   {
1284     //ScopeTimer timer("clone subgraph");
1285     Node_getTraversable(GlobalSceneGraph().root())->traverse(CloneAll(clone));
1286   }
1287
1288   g_map.m_resource->detach(g_map);
1289   GlobalReferenceCache().release(g_map.m_name.c_str());
1290
1291   g_map.m_resource = resource;
1292
1293   g_map.m_name = absolute;
1294   Map_UpdateTitle(g_map);
1295
1296   g_map.m_resource->attach(g_map);
1297 }
1298
1299 void Map_Rename(const char* filename)
1300 {
1301   if(!string_equal(g_map.m_name.c_str(), filename))
1302   {
1303     ScopeDisableScreenUpdates disableScreenUpdates("Processing...", "Saving Map");
1304
1305     Map_RenameAbsolute(filename);
1306     
1307     SceneChangeNotify();
1308   }
1309   else
1310   {
1311     SaveReferences();
1312   }
1313 }
1314
1315 bool Map_Save()
1316 {
1317         Pointfile_Clear();
1318
1319   ScopeTimer timer("map save");
1320   SaveReferences();
1321   return true; // assume success..
1322 }
1323
1324 /*
1325 ===========
1326 Map_New
1327
1328 ===========
1329 */
1330 void Map_New()
1331 {
1332         //globalOutputStream() << "Map_New\n";
1333
1334         g_map.m_name = "unnamed.map";
1335   Map_UpdateTitle(g_map);
1336
1337   {
1338     g_map.m_resource = GlobalReferenceCache().capture(g_map.m_name.c_str());
1339 //    ASSERT_MESSAGE(g_map.m_resource->getNode() == 0, "bleh");
1340     g_map.m_resource->attach(g_map);
1341
1342     SceneChangeNotify();
1343   }
1344
1345   FocusViews(g_vector3_identity, 0);
1346
1347   g_currentMap = &g_map;
1348 }
1349
1350 extern void ConstructRegionBrushes(scene::Node* brushes[6], const Vector3& region_mins, const Vector3& region_maxs);
1351
1352 void ConstructRegionStartpoint(scene::Node* startpoint, const Vector3& region_mins, const Vector3& region_maxs)
1353 {
1354   /*! 
1355   \todo we need to make sure that the player start IS inside the region and bail out if it's not
1356   the compiler will refuse to compile a map with a player_start somewhere in empty space..
1357   for now, let's just print an error
1358   */
1359   
1360   Vector3 vOrig(Camera_getOrigin(*g_pParentWnd->GetCamWnd()));
1361
1362   for (int i=0 ; i<3 ; i++)
1363   {
1364     if (vOrig[i] > region_maxs[i] || vOrig[i] < region_mins[i])
1365     {
1366       globalErrorStream() << "Camera is NOT in the region, it's likely that the region won't compile correctly\n";
1367       break;
1368     }
1369   }
1370   
1371   // write the info_playerstart
1372   char sTmp[1024];
1373   sprintf(sTmp, "%d %d %d", (int)vOrig[0], (int)vOrig[1], (int)vOrig[2]);
1374   Node_getEntity(*startpoint)->setKeyValue("origin", sTmp);
1375   sprintf(sTmp, "%d", (int)Camera_getAngles(*g_pParentWnd->GetCamWnd())[CAMERA_YAW]);
1376   Node_getEntity(*startpoint)->setKeyValue("angle", sTmp);
1377 }
1378
1379 /*
1380 ===========================================================
1381
1382   REGION
1383
1384 ===========================================================
1385 */
1386 bool    region_active;
1387 Vector3 region_mins(g_MinWorldCoord, g_MinWorldCoord, g_MinWorldCoord);
1388 Vector3 region_maxs(g_MaxWorldCoord, g_MaxWorldCoord, g_MaxWorldCoord);
1389
1390 scene::Node* region_sides[6];
1391 scene::Node* region_startpoint = 0;
1392
1393 /*
1394 ===========
1395 AddRegionBrushes
1396 a regioned map will have temp walls put up at the region boundary
1397 \todo TODO TTimo old implementation of region brushes
1398   we still add them straight in the worldspawn and take them out after the map is saved
1399   with the new implementation we should be able to append them in a temporary manner to the data we pass to the map module
1400 ===========
1401 */
1402 void AddRegionBrushes (void)
1403 {
1404         int             i;
1405
1406   for(i=0; i<6; i++)
1407   {
1408     region_sides[i] = &GlobalBrushCreator().createBrush();
1409     Node_getTraversable(Map_FindOrInsertWorldspawn(g_map))->insert(*region_sides[i]);
1410   }
1411
1412   region_startpoint = &GlobalEntityCreator().createEntity(GlobalEntityClassManager().findOrInsert("info_player_start", false));
1413
1414   ConstructRegionBrushes(region_sides, region_mins, region_maxs);
1415   ConstructRegionStartpoint(region_startpoint, region_mins, region_maxs);
1416
1417   Node_getTraversable(GlobalSceneGraph().root())->insert(*region_startpoint);
1418 }
1419
1420 void RemoveRegionBrushes (void)
1421 {
1422   for(std::size_t i=0; i<6; i++)
1423   {
1424     Node_getTraversable(*Map_GetWorldspawn(g_map))->erase(*region_sides[i]);
1425   }
1426   Node_getTraversable(GlobalSceneGraph().root())->erase(*region_startpoint);
1427 }
1428
1429 inline void exclude_node(scene::Node& node, bool exclude)
1430 {
1431   exclude
1432     ? node.enable(scene::Node::eExcluded)
1433     : node.disable(scene::Node::eExcluded);
1434 }
1435
1436 class ExcludeAllWalker : public scene::Graph::Walker
1437 {
1438   bool m_exclude;
1439 public:
1440   ExcludeAllWalker(bool exclude)
1441     : m_exclude(exclude)
1442   {
1443   }
1444   bool pre(const scene::Path& path, scene::Instance& instance) const
1445   {
1446     exclude_node(path.top(), m_exclude);
1447
1448     return true;
1449   }
1450 };
1451
1452 void Scene_Exclude_All(bool exclude)
1453 {
1454   GlobalSceneGraph().traverse(ExcludeAllWalker(exclude));
1455 }
1456
1457 bool Instance_isSelected(const scene::Instance& instance)
1458 {
1459   const Selectable* selectable = Instance_getSelectable(instance);
1460   return selectable != 0 && selectable->isSelected();
1461 }
1462
1463 class ExcludeSelectedWalker : public scene::Graph::Walker
1464 {
1465   bool m_exclude;
1466 public:
1467   ExcludeSelectedWalker(bool exclude)
1468     : m_exclude(exclude)
1469   {
1470   }
1471   bool pre(const scene::Path& path, scene::Instance& instance) const
1472   {
1473     exclude_node(path.top(), (instance.isSelected() || instance.childSelected() || instance.parentSelected()) == m_exclude);
1474     return true;
1475   }
1476 };
1477
1478 void Scene_Exclude_Selected(bool exclude)
1479 {
1480   GlobalSceneGraph().traverse(ExcludeSelectedWalker(exclude));
1481 }
1482
1483 class ExcludeRegionedWalker : public scene::Graph::Walker
1484 {
1485   bool m_exclude;
1486 public:
1487   ExcludeRegionedWalker(bool exclude)
1488     : m_exclude(exclude)
1489   {
1490   }
1491   bool pre(const scene::Path& path, scene::Instance& instance) const
1492   {
1493     exclude_node(
1494       path.top(),
1495       !(
1496         (
1497           aabb_intersects_aabb(
1498             instance.worldAABB(),
1499             aabb_for_minmax(region_mins, region_maxs)
1500           ) != 0
1501         ) ^ m_exclude
1502       )
1503     );
1504
1505     return true;
1506   }
1507 };
1508
1509 void Scene_Exclude_Region(bool exclude)
1510 {
1511   GlobalSceneGraph().traverse(ExcludeRegionedWalker(exclude));
1512 }
1513
1514 /*
1515 ===========
1516 Map_RegionOff
1517
1518 Other filtering options may still be on
1519 ===========
1520 */
1521 void Map_RegionOff()
1522 {
1523         region_active = false;
1524
1525         region_maxs[0] = g_MaxWorldCoord - 64;
1526         region_mins[0] = g_MinWorldCoord + 64;
1527         region_maxs[1] = g_MaxWorldCoord - 64;
1528         region_mins[1] = g_MinWorldCoord + 64;
1529         region_maxs[2] = g_MaxWorldCoord - 64;
1530         region_mins[2] = g_MinWorldCoord + 64;
1531         
1532         Scene_Exclude_All(false);
1533 }
1534
1535 void Map_ApplyRegion (void)
1536 {
1537         region_active = true;
1538
1539         Scene_Exclude_Region(false);
1540 }
1541
1542
1543 /*
1544 ========================
1545 Map_RegionSelectedBrushes
1546 ========================
1547 */
1548 void Map_RegionSelectedBrushes (void)
1549 {
1550         Map_RegionOff();
1551
1552   if(GlobalSelectionSystem().countSelected() != 0
1553     && GlobalSelectionSystem().Mode() == SelectionSystem::ePrimitive)
1554   {
1555           region_active = true;
1556           Select_GetBounds (region_mins, region_maxs);
1557
1558           Scene_Exclude_Selected(false);
1559     
1560     GlobalSelectionSystem().setSelectedAll(false);
1561   }
1562 }
1563
1564
1565 /*
1566 ===========
1567 Map_RegionXY
1568 ===========
1569 */
1570 void Map_RegionXY(float x_min, float y_min, float x_max, float y_max)
1571 {
1572         Map_RegionOff();
1573
1574         region_mins[0] = x_min;
1575   region_maxs[0] = x_max;
1576   region_mins[1] = y_min;
1577   region_maxs[1] = y_max;
1578   region_mins[2] = g_MinWorldCoord + 64;
1579         region_maxs[2] = g_MaxWorldCoord - 64;
1580
1581         Map_ApplyRegion();
1582 }
1583
1584 void Map_RegionBounds(const AABB& bounds)
1585 {
1586   Map_RegionOff();
1587
1588   region_mins = vector3_subtracted(bounds.origin, bounds.extents);
1589   region_maxs = vector3_added(bounds.origin, bounds.extents);
1590
1591   deleteSelection();
1592
1593   Map_ApplyRegion();
1594 }
1595
1596 /*
1597 ===========
1598 Map_RegionBrush
1599 ===========
1600 */
1601 void Map_RegionBrush (void)
1602 {
1603   if(GlobalSelectionSystem().countSelected() != 0)
1604   {
1605     scene::Instance& instance = GlobalSelectionSystem().ultimateSelected();
1606     Map_RegionBounds(instance.worldAABB());
1607   }
1608 }
1609
1610 //
1611 //================
1612 //Map_ImportFile
1613 //================
1614 //
1615 bool Map_ImportFile(const char* filename)
1616 {
1617   ScopeDisableScreenUpdates disableScreenUpdates("Processing...", "Loading Map");
1618
1619   bool success = false;
1620   {
1621     Resource* resource = GlobalReferenceCache().capture(filename);
1622     resource->refresh(); // avoid loading old version if map has changed on disk since last import
1623     if(resource->load())
1624     {
1625       NodeSmartReference clone(NewMapRoot(""));
1626
1627       {
1628         //ScopeTimer timer("clone subgraph");
1629         Node_getTraversable(*resource->getNode())->traverse(CloneAll(clone));
1630       }
1631
1632       Map_gatherNamespaced(clone);
1633       Map_mergeClonedNames();
1634       MergeMap(clone);
1635       success = true;
1636     }
1637     GlobalReferenceCache().release(filename);
1638   }
1639
1640         SceneChangeNotify();
1641
1642   return success;
1643 }
1644
1645 /*
1646 ===========
1647 Map_SaveFile
1648 ===========
1649 */
1650 bool Map_SaveFile(const char* filename)
1651 {
1652   ScopeDisableScreenUpdates disableScreenUpdates("Processing...", "Saving Map");
1653   return MapResource_saveFile(MapFormat_forFile(filename), GlobalSceneGraph().root(), Map_Traverse, filename); 
1654 }
1655
1656 //
1657 //===========
1658 //Map_SaveSelected
1659 //===========
1660 //
1661 // Saves selected world brushes and whole entities with partial/full selections
1662 //
1663 bool Map_SaveSelected(const char* filename)
1664 {
1665   return MapResource_saveFile(MapFormat_forFile(filename), GlobalSceneGraph().root(), Map_Traverse_Selected, filename); 
1666 }
1667
1668
1669 class ParentSelectedBrushesToEntityWalker : public scene::Graph::Walker
1670 {
1671   scene::Node& m_parent;
1672 public:
1673   ParentSelectedBrushesToEntityWalker(scene::Node& parent) : m_parent(parent)
1674   {
1675   }
1676   bool pre(const scene::Path& path, scene::Instance& instance) const
1677   {
1678     if(path.top().get_pointer() != &m_parent
1679       && Node_isPrimitive(path.top()))
1680     {
1681       Selectable* selectable = Instance_getSelectable(instance);
1682       if(selectable != 0
1683         && selectable->isSelected()
1684         && path.size() > 1)
1685       {
1686         return false;
1687       }
1688     }
1689     return true;
1690   }
1691   void post(const scene::Path& path, scene::Instance& instance) const
1692   {
1693     if(path.top().get_pointer() != &m_parent
1694       && Node_isPrimitive(path.top()))
1695     {
1696       Selectable* selectable = Instance_getSelectable(instance);
1697       if(selectable != 0
1698         && selectable->isSelected()
1699         && path.size() > 1)
1700       {
1701         scene::Node& parent = path.parent();
1702         if(&parent != &m_parent)
1703         {
1704           NodeSmartReference node(path.top().get());
1705           Node_getTraversable(parent)->erase(node);
1706           Node_getTraversable(m_parent)->insert(node);
1707         }
1708       }
1709     }
1710   }
1711 };
1712
1713 void Scene_parentSelectedBrushesToEntity(scene::Graph& graph, scene::Node& parent)
1714 {
1715   graph.traverse(ParentSelectedBrushesToEntityWalker(parent));
1716 }
1717
1718 class CountSelectedBrushes : public scene::Graph::Walker
1719 {
1720   std::size_t& m_count;
1721   mutable std::size_t m_depth;
1722 public:
1723   CountSelectedBrushes(std::size_t& count) : m_count(count), m_depth(0)
1724   {
1725     m_count = 0;
1726   }
1727   bool pre(const scene::Path& path, scene::Instance& instance) const
1728   {
1729     if(++m_depth != 1 && path.top().get().isRoot())
1730     {
1731       return false;
1732     }
1733     Selectable* selectable = Instance_getSelectable(instance);
1734     if(selectable != 0
1735       && selectable->isSelected()
1736       && Node_isPrimitive(path.top()))
1737     {
1738       ++m_count;
1739     }
1740     return true;
1741   }
1742   void post(const scene::Path& path, scene::Instance& instance) const
1743   {
1744     --m_depth;
1745   }
1746 };
1747
1748 std::size_t Scene_countSelectedBrushes(scene::Graph& graph)
1749 {
1750   std::size_t count;
1751   graph.traverse(CountSelectedBrushes(count));
1752   return count;
1753 }
1754
1755 enum ENodeType
1756 {
1757   eNodeUnknown,
1758   eNodeMap,
1759   eNodeEntity,
1760   eNodePrimitive,
1761 };
1762
1763 const char* nodetype_get_name(ENodeType type)
1764 {
1765   if(type == eNodeMap)
1766     return "map";
1767   if(type == eNodeEntity)
1768     return "entity";
1769   if(type == eNodePrimitive)
1770     return "primitive";
1771   return "unknown";
1772 }
1773
1774 ENodeType node_get_nodetype(scene::Node& node)
1775 {
1776   if(Node_isEntity(node))
1777   {
1778     return eNodeEntity;
1779   }
1780   if(Node_isPrimitive(node))
1781   {
1782     return eNodePrimitive;
1783   }
1784   return eNodeUnknown;
1785 }
1786
1787 bool contains_entity(scene::Node& node)
1788 {
1789   return Node_getTraversable(node) != 0 && !Node_isBrush(node) && !Node_isPatch(node) && !Node_isEntity(node);
1790 }
1791
1792 bool contains_primitive(scene::Node& node)
1793 {
1794   return Node_isEntity(node) && Node_getTraversable(node) != 0 && Node_getEntity(node)->isContainer();
1795 }
1796
1797 ENodeType node_get_contains(scene::Node& node)
1798 {
1799   if(contains_entity(node))
1800   {
1801     return eNodeEntity;
1802   }
1803   if(contains_primitive(node))
1804   {
1805     return eNodePrimitive;
1806   }
1807   return eNodeUnknown;
1808 }
1809
1810 void Path_parent(const scene::Path& parent, const scene::Path& child)
1811 {
1812   ENodeType contains = node_get_contains(parent.top());
1813   ENodeType type = node_get_nodetype(child.top());
1814
1815   if(contains != eNodeUnknown && contains == type)
1816   {
1817     NodeSmartReference node(child.top().get());
1818     Path_deleteTop(child);
1819     Node_getTraversable(parent.top())->insert(node);
1820     SceneChangeNotify();
1821   }
1822   else
1823   {
1824     globalErrorStream() << "failed - " << nodetype_get_name(type) << " cannot be parented to " << nodetype_get_name(contains) << " container.\n";
1825   }
1826 }
1827
1828 void Scene_parentSelected()
1829 {
1830   UndoableCommand undo("parentSelected");
1831
1832   if(GlobalSelectionSystem().countSelected() > 1)
1833   {
1834     class ParentSelectedBrushesToEntityWalker : public SelectionSystem::Visitor
1835     {
1836       const scene::Path& m_parent;
1837     public:
1838       ParentSelectedBrushesToEntityWalker(const scene::Path& parent) : m_parent(parent)
1839       {
1840       }
1841       void visit(scene::Instance& instance) const
1842       {
1843         if(&m_parent != &instance.path())
1844         {
1845           Path_parent(m_parent, instance.path());
1846         }
1847       }
1848     };
1849     
1850     ParentSelectedBrushesToEntityWalker visitor(GlobalSelectionSystem().ultimateSelected().path());
1851     GlobalSelectionSystem().foreachSelected(visitor);
1852   }
1853   else
1854   {
1855     globalOutputStream() << "failed - did not find two selected nodes.\n";
1856   }
1857 }
1858
1859
1860
1861 void NewMap()
1862 {
1863   if (ConfirmModified("New Map"))
1864   {
1865     Map_RegionOff();
1866           Map_Free();
1867     Map_New();
1868   }
1869 }
1870
1871 void maps_directory(StringOutputStream& buffer)
1872 {
1873   ASSERT_MESSAGE(!string_empty(g_qeglobals.m_userGamePath.c_str()), "maps_directory: user-game-path is empty");
1874   buffer << g_qeglobals.m_userGamePath.c_str() << "maps/";
1875   Q_mkdir(buffer.c_str());
1876 }
1877
1878 const char* map_open(const char* title)
1879 {
1880         StringOutputStream buf(256);
1881   maps_directory(buf);
1882   return file_dialog(GTK_WIDGET(MainFrame_getWindow()), TRUE, title, buf.c_str(), MapFormat::Name());
1883 }
1884
1885 const char* map_save(const char* title)
1886 {
1887         StringOutputStream buf(256);
1888   maps_directory(buf);
1889   return file_dialog(GTK_WIDGET(MainFrame_getWindow()), FALSE, title, buf.c_str(), 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
2234 class MapEntityClasses : public ModuleObserver
2235 {
2236   std::size_t m_unrealised;
2237 public:
2238   MapEntityClasses() : m_unrealised(1)
2239   {
2240   }
2241   void realise()
2242   {
2243     if(--m_unrealised == 0)
2244     {
2245       if(g_map.m_resource != 0)
2246       {
2247         ScopeDisableScreenUpdates disableScreenUpdates("Processing...", "Loading Map");
2248               g_map.m_resource->realise();
2249       }
2250     }
2251   }
2252   void unrealise()
2253   {
2254     if(++m_unrealised == 1)
2255     {
2256       if(g_map.m_resource != 0)
2257       {
2258         g_map.m_resource->flush();
2259               g_map.m_resource->unrealise();
2260       }
2261     }
2262   }
2263 };
2264
2265 MapEntityClasses g_MapEntityClasses;
2266
2267
2268 void Map_constructPreferences(PreferencesPage& page)
2269 {
2270   page.appendCheckBox("", "Load last map on open", g_bLoadLastMap);
2271 }
2272
2273 #include "preferencesystem.h"
2274
2275 CopiedString g_strLastMap;
2276 bool g_bLoadLastMap = false;
2277
2278 void Map_Construct()
2279 {
2280   GlobalCommands_insert("RegionOff", FreeCaller<RegionOff>());
2281   GlobalCommands_insert("RegionSetXY", FreeCaller<RegionXY>());
2282   GlobalCommands_insert("RegionSetBrush", FreeCaller<RegionBrush>());
2283   GlobalCommands_insert("RegionSetSelection", FreeCaller<RegionSelected>(), Accelerator('R', (GdkModifierType)(GDK_SHIFT_MASK|GDK_CONTROL_MASK)));
2284
2285   GlobalPreferenceSystem().registerPreference("LastMap", CopiedStringImportStringCaller(g_strLastMap), CopiedStringExportStringCaller(g_strLastMap));
2286   GlobalPreferenceSystem().registerPreference("LoadLastMap", BoolImportStringCaller(g_bLoadLastMap), BoolExportStringCaller(g_bLoadLastMap));
2287   GlobalPreferenceSystem().registerPreference("MapInfoDlg", WindowPositionImportStringCaller(g_posMapInfoWnd), WindowPositionExportStringCaller(g_posMapInfoWnd));
2288   
2289   PreferencesDialog_addSettingsPreferences(FreeCaller1<PreferencesPage&, Map_constructPreferences>());
2290
2291   GlobalEntityClassManager().attach(g_MapEntityClasses);
2292 }
2293
2294 void Map_Destroy()
2295 {
2296   GlobalEntityClassManager().detach(g_MapEntityClasses);
2297 }