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