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