]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - radiant/map.cpp
Q3map2:
[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 bool Node_instanceSelected( scene::Node& node );
738
739 class CloneAllSelected : public scene::Traversable::Walker
740 {
741 mutable scene::Path m_path;
742 public:
743 CloneAllSelected( scene::Node& root )
744         : m_path( makeReference( root ) ){
745 }
746 bool pre( scene::Node& node ) const {
747         if ( node.isRoot() ) {
748                 return false;
749         }
750
751         if( Node_instanceSelected( node ) ){
752                 m_path.push( makeReference( node_clone( node ) ) );
753                 m_path.top().get().IncRef();
754         }
755
756         return true;
757 }
758 void post( scene::Node& node ) const {
759         if ( node.isRoot() ) {
760                 return;
761         }
762
763         if( Node_instanceSelected( node ) ){
764                 Node_getTraversable( m_path.parent() )->insert( m_path.top() );
765
766                 m_path.top().get().DecRef();
767                 m_path.pop();
768         }
769 }
770 };
771
772 scene::Node& Node_Clone_Selected( scene::Node& node ){
773         scene::Node& clone = node_clone( node );
774         scene::Traversable* traversable = Node_getTraversable( node );
775         if ( traversable != 0 ) {
776                 traversable->traverse( CloneAllSelected( clone ) );
777         }
778         return clone;
779 }
780
781
782 typedef std::map<CopiedString, std::size_t> EntityBreakdown;
783
784 class EntityBreakdownWalker : public scene::Graph::Walker
785 {
786 EntityBreakdown& m_entitymap;
787 public:
788 EntityBreakdownWalker( EntityBreakdown& entitymap )
789         : m_entitymap( entitymap ){
790 }
791 bool pre( const scene::Path& path, scene::Instance& instance ) const {
792         Entity* entity = Node_getEntity( path.top() );
793         if ( entity != 0 ) {
794                 const EntityClass& eclass = entity->getEntityClass();
795                 if ( m_entitymap.find( eclass.name() ) == m_entitymap.end() ) {
796                         m_entitymap[eclass.name()] = 1;
797                 }
798                 else{ ++m_entitymap[eclass.name()]; }
799         }
800         return true;
801 }
802 };
803
804 void Scene_EntityBreakdown( EntityBreakdown& entitymap ){
805         GlobalSceneGraph().traverse( EntityBreakdownWalker( entitymap ) );
806 }
807
808 class CountStuffWalker : public scene::Graph::Walker
809 {
810 int& m_patches;
811 int& m_ents_ingame;
812 int& m_groupents;
813 int& m_groupents_ingame;
814 public:
815 CountStuffWalker( int& patches, int& ents_ingame, int& groupents, int& groupents_ingame )
816         : m_patches( patches ), m_ents_ingame( ents_ingame ), m_groupents( groupents ), m_groupents_ingame( groupents_ingame ){
817 }
818 bool pre( const scene::Path& path, scene::Instance& instance ) const {
819         Patch* patch = Node_getPatch( path.top() );
820         if( patch != 0 ){
821                 ++m_patches;
822         }
823         Entity* entity = Node_getEntity( path.top() );
824         if ( entity != 0 ){
825                 if( entity->isContainer() ){
826                         ++m_groupents;
827                         if( !string_equal_nocase( "func_group", entity->getKeyValue( "classname" ) ) &&
828                                 !string_equal_nocase( "_decal", entity->getKeyValue( "classname" ) ) ){
829                                 ++m_groupents_ingame;
830                                 ++m_ents_ingame;
831                         }
832                         return true;
833                 }
834                 if( !string_equal_nocase_n( "light", entity->getKeyValue( "classname" ), 5 ) &&
835                         !string_equal_nocase( "misc_model", entity->getKeyValue( "classname" ) ) ){
836                         ++m_ents_ingame;
837                 }
838         }
839         return true;
840 }
841 };
842
843 void Scene_CountStuff( int& patches, int& ents_ingame, int& groupents, int& groupents_ingame ){
844         GlobalSceneGraph().traverse( CountStuffWalker( patches, ents_ingame, groupents, groupents_ingame ) );
845 }
846
847 WindowPosition g_posMapInfoWnd( c_default_window_pos );
848
849 void DoMapInfo(){
850         ModalDialog dialog;
851
852         GtkWidget* w_brushes;
853         GtkWidget* w_patches;
854         GtkWidget* w_ents;
855         GtkWidget* w_ents_ingame;
856         GtkWidget* w_groupents;
857         GtkWidget* w_groupents_ingame;
858
859         GtkListStore* EntityBreakdownWalker;
860
861         GtkWindow* window = create_dialog_window( MainFrame_getWindow(), "Map Info", G_CALLBACK( dialog_delete_callback ), &dialog );
862
863         window_set_position( window, g_posMapInfoWnd );
864
865         {
866                 GtkVBox* vbox = create_dialog_vbox( 4, 4 );
867                 gtk_container_add( GTK_CONTAINER( window ), GTK_WIDGET( vbox ) );
868
869                 {
870                         GtkHBox* hbox = create_dialog_hbox( 4 );
871                         gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( hbox ), FALSE, FALSE, 0 );
872
873                         {
874                                 GtkTable* table = create_dialog_table( 3, 4, 4, 4 );
875                                 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( table ), TRUE, TRUE, 0 );
876                                 {
877                                         GtkWidget* label = gtk_label_new( "Total Brushes:" );
878                                         gtk_widget_show( label );
879                                         gtk_table_attach( GTK_TABLE( table ), label, 0, 1, 0, 1,
880                                                                           (GtkAttachOptions) ( GTK_FILL ),
881                                                                           (GtkAttachOptions) ( 0 ), 0, 0 );
882                                         gtk_misc_set_alignment( GTK_MISC( label ), 0.0, 0.5 );
883                                 }
884                                 {
885                                         GtkWidget* label = gtk_label_new( "" );
886                                         gtk_widget_show( label );
887                                         gtk_table_attach( GTK_TABLE( table ), label, 1, 2, 0, 1,
888                                                                           (GtkAttachOptions) ( GTK_FILL | GTK_EXPAND ),
889                                                                           (GtkAttachOptions) ( 0 ), 3, 0 );
890                                         gtk_misc_set_alignment( GTK_MISC( label ), 0.0, 0.5 );
891                                         w_brushes = label;
892                                 }
893                                 {
894                                         GtkWidget* label = gtk_label_new( "Total Patches:" );
895                                         gtk_widget_show( label );
896                                         gtk_table_attach( GTK_TABLE( table ), label, 2, 3, 0, 1,
897                                                                           (GtkAttachOptions) ( GTK_FILL ),
898                                                                           (GtkAttachOptions) ( 0 ), 0, 0 );
899                                         gtk_misc_set_alignment( GTK_MISC( label ), 0.0, 0.5 );
900                                 }
901                                 {
902                                         GtkWidget* label = gtk_label_new( "" );
903                                         gtk_widget_show( label );
904                                         gtk_table_attach( GTK_TABLE( table ), label, 3, 4, 0, 1,
905                                                                           (GtkAttachOptions) ( GTK_FILL | GTK_EXPAND ),
906                                                                           (GtkAttachOptions) ( 0 ), 3, 0 );
907                                         gtk_misc_set_alignment( GTK_MISC( label ), 0.0, 0.5 );
908                                         w_patches = label;
909                                 }
910                                 {
911                                         GtkWidget* label = gtk_label_new( "Total Entities:" );
912                                         gtk_widget_show( label );
913                                         gtk_table_attach( GTK_TABLE( table ), label, 0, 1, 1, 2,
914                                                                           (GtkAttachOptions) ( GTK_FILL ),
915                                                                           (GtkAttachOptions) ( 0 ), 0, 0 );
916                                         gtk_misc_set_alignment( GTK_MISC( label ), 0.0, 0.5 );
917                                 }
918                                 {
919                                         GtkWidget* label = gtk_label_new( "" );
920                                         gtk_widget_show( label );
921                                         gtk_table_attach( GTK_TABLE( table ), label, 1, 2, 1, 2,
922                                                                           (GtkAttachOptions) ( GTK_FILL | GTK_EXPAND ),
923                                                                           (GtkAttachOptions) ( 0 ), 3, 0 );
924                                         gtk_misc_set_alignment( GTK_MISC( label ), 0.0, 0.5 );
925                                         w_ents = label;
926                                 }
927                                 {
928                                         GtkWidget* label = gtk_label_new( "Ingame Entities:" );
929                                         gtk_widget_show( label );
930                                         gtk_table_attach( GTK_TABLE( table ), label, 2, 3, 1, 2,
931                                                                           (GtkAttachOptions) ( GTK_FILL ),
932                                                                           (GtkAttachOptions) ( 0 ), 0, 0 );
933                                         gtk_misc_set_alignment( GTK_MISC( label ), 0.0, 0.5 );
934                                 }
935                                 {
936                                         GtkWidget* label = gtk_label_new( "" );
937                                         gtk_widget_show( label );
938                                         gtk_table_attach( GTK_TABLE( table ), label, 3, 4, 1, 2,
939                                                                           (GtkAttachOptions) ( GTK_FILL | GTK_EXPAND ),
940                                                                           (GtkAttachOptions) ( 0 ), 3, 0 );
941                                         gtk_misc_set_alignment( GTK_MISC( label ), 0.0, 0.5 );
942                                         w_ents_ingame = label;
943                                 }
944                                 {
945                                         GtkWidget* label = gtk_label_new( "Group Entities:" );
946                                         gtk_widget_show( label );
947                                         gtk_table_attach( GTK_TABLE( table ), label, 0, 1, 2, 3,
948                                                                           (GtkAttachOptions) ( GTK_FILL ),
949                                                                           (GtkAttachOptions) ( 0 ), 0, 0 );
950                                         gtk_misc_set_alignment( GTK_MISC( label ), 0.0, 0.5 );
951                                 }
952                                 {
953                                         GtkWidget* label = gtk_label_new( "" );
954                                         gtk_widget_show( label );
955                                         gtk_table_attach( GTK_TABLE( table ), label, 1, 2, 2, 3,
956                                                                           (GtkAttachOptions) ( GTK_FILL | GTK_EXPAND ),
957                                                                           (GtkAttachOptions) ( 0 ), 3, 0 );
958                                         gtk_misc_set_alignment( GTK_MISC( label ), 0.0, 0.5 );
959                                         w_groupents = label;
960                                 }
961                                 {
962                                         GtkWidget* label = gtk_label_new( "Ingame Group Entities:" );
963                                         gtk_widget_show( label );
964                                         gtk_table_attach( GTK_TABLE( table ), label, 2, 3, 2, 3,
965                                                                           (GtkAttachOptions) ( GTK_FILL ),
966                                                                           (GtkAttachOptions) ( 0 ), 0, 0 );
967                                         gtk_misc_set_alignment( GTK_MISC( label ), 0.0, 0.5 );
968                                 }
969                                 {
970                                         GtkWidget* label = gtk_label_new( "" );
971                                         gtk_widget_show( label );
972                                         gtk_table_attach( GTK_TABLE( table ), label, 3, 4, 2, 3,
973                                                                           (GtkAttachOptions) ( GTK_FILL | GTK_EXPAND ),
974                                                                           (GtkAttachOptions) ( 0 ), 3, 0 );
975                                         gtk_misc_set_alignment( GTK_MISC( label ), 0.0, 0.5 );
976                                         w_groupents_ingame = label;
977                                 }
978
979                         }
980                         {
981                                 GtkVBox* vbox2 = create_dialog_vbox( 4 );
982                                 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox2 ), FALSE, FALSE, 0 );
983
984                                 {
985                                         GtkButton* button = create_dialog_button( "Close", G_CALLBACK( dialog_button_ok ), &dialog );
986                                         gtk_box_pack_start( GTK_BOX( vbox2 ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
987                                 }
988                         }
989                 }
990                 {
991                         GtkWidget* label = gtk_label_new( "*** Entity breakdown ***" );
992                         gtk_widget_show( label );
993                         gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( label ), FALSE, TRUE, 0 );
994                         gtk_misc_set_alignment( GTK_MISC( label ), 0.5, 0.5 );
995                 }
996                 {
997                         GtkScrolledWindow* scr = create_scrolled_window( GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC, 4 );
998                         gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( scr ), TRUE, TRUE, 0 );
999
1000                         {
1001                                 GtkListStore* store = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_UINT );
1002
1003                                 GtkWidget* view = gtk_tree_view_new_with_model( GTK_TREE_MODEL( store ) );
1004                                 gtk_tree_view_set_headers_clickable( GTK_TREE_VIEW( view ), TRUE );
1005
1006                                 {
1007                                         GtkCellRenderer* renderer = gtk_cell_renderer_text_new();
1008                                         GtkTreeViewColumn* column = gtk_tree_view_column_new_with_attributes( "Entity", renderer, "text", 0, 0 );
1009                                         gtk_tree_view_append_column( GTK_TREE_VIEW( view ), column );
1010                                         gtk_tree_view_column_set_sort_column_id( column, 0 );
1011                                 }
1012
1013                                 {
1014                                         GtkCellRenderer* renderer = gtk_cell_renderer_text_new();
1015                                         GtkTreeViewColumn* column = gtk_tree_view_column_new_with_attributes( "Count", renderer, "text", 1, 0 );
1016                                         gtk_tree_view_append_column( GTK_TREE_VIEW( view ), column );
1017                                         gtk_tree_view_column_set_sort_column_id( column, 1 );
1018                                 }
1019
1020                                 gtk_widget_show( view );
1021
1022                                 gtk_container_add( GTK_CONTAINER( scr ), view );
1023
1024                                 EntityBreakdownWalker = store;
1025                         }
1026                 }
1027         }
1028
1029         // Initialize fields
1030
1031         {
1032                 EntityBreakdown entitymap;
1033                 Scene_EntityBreakdown( entitymap );
1034
1035                 for ( EntityBreakdown::iterator i = entitymap.begin(); i != entitymap.end(); ++i )
1036                 {
1037                         GtkTreeIter iter;
1038                         gtk_list_store_append( GTK_LIST_STORE( EntityBreakdownWalker ), &iter );
1039                         gtk_list_store_set( GTK_LIST_STORE( EntityBreakdownWalker ), &iter, 0, ( *i ).first.c_str(), 1, Unsigned( ( *i ).second ), -1 );
1040                 }
1041         }
1042
1043         g_object_unref( G_OBJECT( EntityBreakdownWalker ) );
1044
1045         int n_patches = 0;
1046         int n_ents_ingame = 0;
1047         int n_groupents = 0;
1048         int n_groupents_ingame = 0;
1049         Scene_CountStuff( n_patches, n_ents_ingame, n_groupents, n_groupents_ingame );
1050         //globalOutputStream() << n_patches << n_ents_ingame << n_groupents << n_groupents_ingame << "\n";
1051
1052         char *markup;
1053
1054         markup = g_markup_printf_escaped( "<span style=\"italic\"><b>%u</b></span>  ", Unsigned( g_brushCount.get() ) );
1055         gtk_label_set_markup( GTK_LABEL( w_brushes ), markup );
1056         g_free( markup );
1057
1058         markup = g_markup_printf_escaped( "<span style=\"italic\"><b>%i</b></span>  ", n_patches );
1059         gtk_label_set_markup( GTK_LABEL( w_patches ), markup );
1060         g_free( markup );
1061
1062         markup = g_markup_printf_escaped( "<span style=\"italic\"><b>%u</b></span>  ", Unsigned( g_entityCount.get() ) );
1063         gtk_label_set_markup( GTK_LABEL( w_ents ), markup );
1064         g_free( markup );
1065
1066         markup = g_markup_printf_escaped( "<span style=\"italic\"><b>%i</b></span>  ", n_ents_ingame );
1067         gtk_label_set_markup( GTK_LABEL( w_ents_ingame ), markup );
1068         g_free( markup );
1069
1070         markup = g_markup_printf_escaped( "<span style=\"italic\"><b>%i</b></span>  ", n_groupents );
1071         gtk_label_set_markup( GTK_LABEL( w_groupents ), markup );
1072         g_free( markup );
1073
1074         markup = g_markup_printf_escaped( "<span style=\"italic\"><b>%i</b></span>  ", n_groupents_ingame );
1075         gtk_label_set_markup( GTK_LABEL( w_groupents_ingame ), markup );
1076         g_free( markup );
1077
1078         modal_dialog_show( window, dialog );
1079
1080         // save before exit
1081         window_get_position( window, g_posMapInfoWnd );
1082
1083         gtk_widget_destroy( GTK_WIDGET( window ) );
1084 }
1085
1086
1087
1088 class ScopeTimer
1089 {
1090 Timer m_timer;
1091 const char* m_message;
1092 public:
1093 ScopeTimer( const char* message )
1094         : m_message( message ){
1095         m_timer.start();
1096 }
1097 ~ScopeTimer(){
1098         double elapsed_time = m_timer.elapsed_msec() / 1000.f;
1099         globalOutputStream() << m_message << " timer: " << FloatFormat( elapsed_time, 5, 2 ) << " second(s) elapsed\n";
1100 }
1101 };
1102
1103 /*
1104    ================
1105    Map_LoadFile
1106    ================
1107  */
1108
1109 void Map_LoadFile( const char *filename ){
1110         globalOutputStream() << "Loading map from " << filename << "\n";
1111         ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Loading Map" );
1112
1113         {
1114                 ScopeTimer timer( "map load" );
1115
1116                 const MapFormat* format = NULL;
1117                 const char* moduleName = findModuleName( &GlobalFiletypes(), MapFormat::Name(), path_get_extension( filename ) );
1118                 if ( string_not_empty( moduleName ) ) {
1119                         format = ReferenceAPI_getMapModules().findModule( moduleName );
1120                 }
1121
1122                 for ( int i = 0; i < Brush_toggleFormatCount(); ++i )
1123                 {
1124                         if ( i ) {
1125                                 Map_Free();
1126                         }
1127                         Brush_toggleFormat( i );
1128                         g_map.m_name = filename;
1129                         Map_UpdateTitle( g_map );
1130                         g_map.m_resource = GlobalReferenceCache().capture( g_map.m_name.c_str() );
1131                         if ( format ) {
1132                                 format->wrongFormat = false;
1133                         }
1134                         g_map.m_resource->attach( g_map );
1135                         if ( format ) {
1136                                 if ( !format->wrongFormat ) {
1137                                         break;
1138                                 }
1139                         }
1140                 }
1141
1142                 Node_getTraversable( GlobalSceneGraph().root() )->traverse( entity_updateworldspawn() );
1143         }
1144
1145         globalOutputStream() << "--- LoadMapFile ---\n";
1146         globalOutputStream() << g_map.m_name.c_str() << "\n";
1147
1148         globalOutputStream() << Unsigned( g_brushCount.get() ) << " primitive\n";
1149         globalOutputStream() << Unsigned( g_entityCount.get() ) << " entities\n";
1150
1151         //GlobalEntityCreator().printStatistics();
1152
1153         //
1154         // move the view to a start position
1155         //
1156         Map_StartPosition();
1157
1158         g_currentMap = &g_map;
1159 }
1160
1161 class Excluder
1162 {
1163 public:
1164 virtual bool excluded( scene::Node& node ) const = 0;
1165 };
1166
1167 class ExcludeWalker : public scene::Traversable::Walker
1168 {
1169 const scene::Traversable::Walker& m_walker;
1170 const Excluder* m_exclude;
1171 mutable bool m_skip;
1172 public:
1173 ExcludeWalker( const scene::Traversable::Walker& walker, const Excluder& exclude )
1174         : m_walker( walker ), m_exclude( &exclude ), m_skip( false ){
1175 }
1176 bool pre( scene::Node& node ) const {
1177         if ( m_exclude->excluded( node ) || node.isRoot() ) {
1178                 m_skip = true;
1179                 return false;
1180         }
1181         else
1182         {
1183                 m_walker.pre( node );
1184         }
1185         return true;
1186 }
1187 void post( scene::Node& node ) const {
1188         if ( m_skip ) {
1189                 m_skip = false;
1190         }
1191         else
1192         {
1193                 m_walker.post( node );
1194         }
1195 }
1196 };
1197
1198 class AnyInstanceSelected : public scene::Instantiable::Visitor
1199 {
1200 bool& m_selected;
1201 public:
1202 AnyInstanceSelected( bool& selected ) : m_selected( selected ){
1203         m_selected = false;
1204 }
1205 void visit( scene::Instance& instance ) const {
1206         Selectable* selectable = Instance_getSelectable( instance );
1207         if ( selectable != 0
1208                  && selectable->isSelected() ) {
1209                 m_selected = true;
1210         }
1211 }
1212 };
1213
1214 bool Node_instanceSelected( scene::Node& node ){
1215         scene::Instantiable* instantiable = Node_getInstantiable( node );
1216         ASSERT_NOTNULL( instantiable );
1217         bool selected;
1218         instantiable->forEachInstance( AnyInstanceSelected( selected ) );
1219         return selected;
1220 }
1221
1222 class SelectedDescendantWalker : public scene::Traversable::Walker
1223 {
1224 bool& m_selected;
1225 public:
1226 SelectedDescendantWalker( bool& selected ) : m_selected( selected ){
1227         m_selected = false;
1228 }
1229
1230 bool pre( scene::Node& node ) const {
1231         if ( node.isRoot() ) {
1232                 return false;
1233         }
1234
1235         if ( Node_instanceSelected( node ) ) {
1236                 m_selected = true;
1237         }
1238
1239         return true;
1240 }
1241 };
1242
1243 bool Node_selectedDescendant( scene::Node& node ){
1244         bool selected;
1245         Node_traverseSubgraph( node, SelectedDescendantWalker( selected ) );
1246         return selected;
1247 }
1248
1249 class SelectionExcluder : public Excluder
1250 {
1251 public:
1252 bool excluded( scene::Node& node ) const {
1253         return !Node_selectedDescendant( node );
1254 }
1255 };
1256
1257 class IncludeSelectedWalker : public scene::Traversable::Walker
1258 {
1259 const scene::Traversable::Walker& m_walker;
1260 mutable std::size_t m_selected;
1261 mutable bool m_skip;
1262
1263 bool selectedParent() const {
1264         return m_selected != 0;
1265 }
1266 public:
1267 IncludeSelectedWalker( const scene::Traversable::Walker& walker )
1268         : m_walker( walker ), m_selected( 0 ), m_skip( false ){
1269 }
1270 bool pre( scene::Node& node ) const {
1271         // include node if:
1272         // node is not a 'root' AND ( node is selected OR any child of node is selected OR any parent of node is selected )
1273         if ( !node.isRoot() && ( Node_selectedDescendant( node ) || selectedParent() ) ) {
1274                 if ( Node_instanceSelected( node ) ) {
1275                         ++m_selected;
1276                 }
1277                 m_walker.pre( node );
1278                 return true;
1279         }
1280         else
1281         {
1282                 m_skip = true;
1283                 return false;
1284         }
1285 }
1286 void post( scene::Node& node ) const {
1287         if ( m_skip ) {
1288                 m_skip = false;
1289         }
1290         else
1291         {
1292                 if ( Node_instanceSelected( node ) ) {
1293                         --m_selected;
1294                 }
1295                 m_walker.post( node );
1296         }
1297 }
1298 };
1299
1300 void Map_Traverse_Selected( scene::Node& root, const scene::Traversable::Walker& walker ){
1301         scene::Traversable* traversable = Node_getTraversable( root );
1302         if ( traversable != 0 ) {
1303 #if 0
1304                 traversable->traverse( ExcludeWalker( walker, SelectionExcluder() ) );
1305 #else
1306                 traversable->traverse( IncludeSelectedWalker( walker ) );
1307 #endif
1308         }
1309 }
1310
1311 void Map_ExportSelected( TextOutputStream& out, const MapFormat& format ){
1312         format.writeGraph( GlobalSceneGraph().root(), Map_Traverse_Selected, out );
1313 }
1314
1315 void Map_Traverse( scene::Node& root, const scene::Traversable::Walker& walker ){
1316         scene::Traversable* traversable = Node_getTraversable( root );
1317         if ( traversable != 0 ) {
1318                 traversable->traverse( walker );
1319         }
1320 }
1321
1322 class RegionExcluder : public Excluder
1323 {
1324 public:
1325 bool excluded( scene::Node& node ) const {
1326         return node.excluded();
1327 }
1328 };
1329
1330 void Map_Traverse_Region( scene::Node& root, const scene::Traversable::Walker& walker ){
1331         scene::Traversable* traversable = Node_getTraversable( root );
1332         if ( traversable != 0 ) {
1333                 traversable->traverse( ExcludeWalker( walker, RegionExcluder() ) );
1334         }
1335 }
1336
1337 bool Map_SaveRegion( const char *filename ){
1338         AddRegionBrushes();
1339
1340         bool success = MapResource_saveFile( MapFormat_forFile( filename ), GlobalSceneGraph().root(), Map_Traverse_Region, filename );
1341
1342         RemoveRegionBrushes();
1343
1344         return success;
1345 }
1346
1347
1348 void Map_RenameAbsolute( const char* absolute ){
1349         Resource* resource = GlobalReferenceCache().capture( absolute );
1350         NodeSmartReference clone( NewMapRoot( path_make_relative( absolute, GlobalFileSystem().findRoot( absolute ) ) ) );
1351         resource->setNode( clone.get_pointer() );
1352
1353         {
1354                 //ScopeTimer timer("clone subgraph");
1355                 Node_getTraversable( GlobalSceneGraph().root() )->traverse( CloneAll( clone ) );
1356         }
1357
1358         g_map.m_resource->detach( g_map );
1359         GlobalReferenceCache().release( g_map.m_name.c_str() );
1360
1361         g_map.m_resource = resource;
1362
1363         g_map.m_name = absolute;
1364         Map_UpdateTitle( g_map );
1365
1366         g_map.m_resource->attach( g_map );
1367 }
1368
1369 void Map_Rename( const char* filename ){
1370         if ( !string_equal( g_map.m_name.c_str(), filename ) ) {
1371                 ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Saving Map" );
1372
1373                 Map_RenameAbsolute( filename );
1374
1375                 SceneChangeNotify();
1376         }
1377         else
1378         {
1379                 SaveReferences();
1380         }
1381 }
1382
1383 bool Map_Save(){
1384         Pointfile_Clear();
1385
1386         ScopeTimer timer( "map save" );
1387         SaveReferences();
1388         return true; // assume success..
1389 }
1390
1391 /*
1392    ===========
1393    Map_New
1394
1395    ===========
1396  */
1397 void Map_New(){
1398         //globalOutputStream() << "Map_New\n";
1399
1400         g_map.m_name = "unnamed.map";
1401         Map_UpdateTitle( g_map );
1402
1403         {
1404                 g_map.m_resource = GlobalReferenceCache().capture( g_map.m_name.c_str() );
1405 //    ASSERT_MESSAGE(g_map.m_resource->getNode() == 0, "bleh");
1406                 g_map.m_resource->attach( g_map );
1407
1408                 SceneChangeNotify();
1409         }
1410
1411         FocusViews( g_vector3_identity, 0 );
1412
1413         g_currentMap = &g_map;
1414 }
1415
1416 extern void ConstructRegionBrushes( scene::Node * brushes[6], const Vector3 &region_mins, const Vector3 &region_maxs );
1417
1418 void ConstructRegionStartpoint( scene::Node* startpoint, const Vector3& region_mins, const Vector3& region_maxs ){
1419         /*!
1420            \todo we need to make sure that the player start IS inside the region and bail out if it's not
1421            the compiler will refuse to compile a map with a player_start somewhere in empty space..
1422            for now, let's just print an error
1423          */
1424
1425         Vector3 vOrig( Camera_getOrigin( *g_pParentWnd->GetCamWnd() ) );
1426
1427         for ( int i = 0 ; i < 3 ; i++ )
1428         {
1429                 if ( vOrig[i] > region_maxs[i] || vOrig[i] < region_mins[i] ) {
1430                         globalErrorStream() << "Camera is NOT in the region, it's likely that the region won't compile correctly\n";
1431                         break;
1432                 }
1433         }
1434
1435         // write the info_playerstart
1436         char sTmp[1024];
1437         sprintf( sTmp, "%d %d %d", (int)vOrig[0], (int)vOrig[1], (int)vOrig[2] );
1438         Node_getEntity( *startpoint )->setKeyValue( "origin", sTmp );
1439         sprintf( sTmp, "%d", (int)Camera_getAngles( *g_pParentWnd->GetCamWnd() )[CAMERA_YAW] );
1440         Node_getEntity( *startpoint )->setKeyValue( "angle", sTmp );
1441 }
1442
1443 /*
1444    ===========================================================
1445
1446    REGION
1447
1448    ===========================================================
1449  */
1450 bool region_active = false;
1451
1452 BoolExportCaller g_region_caller( region_active );
1453 ToggleItem g_region_item( g_region_caller );
1454
1455 Vector3 region_mins( g_MinWorldCoord, g_MinWorldCoord, g_MinWorldCoord );
1456 Vector3 region_maxs( g_MaxWorldCoord, g_MaxWorldCoord, g_MaxWorldCoord );
1457
1458 scene::Node* region_sides[6];
1459 scene::Node* region_startpoint = 0;
1460
1461 /*
1462    ===========
1463    AddRegionBrushes
1464    a regioned map will have temp walls put up at the region boundary
1465    \todo TODO TTimo old implementation of region brushes
1466    we still add them straight in the worldspawn and take them out after the map is saved
1467    with the new implementation we should be able to append them in a temporary manner to the data we pass to the map module
1468    ===========
1469  */
1470 void AddRegionBrushes( void ){
1471         int i;
1472
1473         for ( i = 0; i < 6; i++ )
1474         {
1475                 region_sides[i] = &GlobalBrushCreator().createBrush();
1476                 Node_getTraversable( Map_FindOrInsertWorldspawn( g_map ) )->insert( NodeSmartReference( *region_sides[i] ) );
1477         }
1478
1479         region_startpoint = &GlobalEntityCreator().createEntity( GlobalEntityClassManager().findOrInsert( "info_player_start", false ) );
1480
1481         ConstructRegionBrushes( region_sides, region_mins, region_maxs );
1482         ConstructRegionStartpoint( region_startpoint, region_mins, region_maxs );
1483
1484         Node_getTraversable( GlobalSceneGraph().root() )->insert( NodeSmartReference( *region_startpoint ) );
1485 }
1486
1487 void RemoveRegionBrushes( void ){
1488         for ( std::size_t i = 0; i < 6; i++ )
1489         {
1490                 Node_getTraversable( *Map_GetWorldspawn( g_map ) )->erase( *region_sides[i] );
1491         }
1492         Node_getTraversable( GlobalSceneGraph().root() )->erase( *region_startpoint );
1493 }
1494
1495 inline void exclude_node( scene::Node& node, bool exclude ){
1496         exclude
1497         ? node.enable( scene::Node::eExcluded )
1498         : node.disable( scene::Node::eExcluded );
1499 }
1500
1501 class ExcludeAllWalker : public scene::Graph::Walker
1502 {
1503 bool m_exclude;
1504 public:
1505 ExcludeAllWalker( bool exclude )
1506         : m_exclude( exclude ){
1507 }
1508 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1509         exclude_node( path.top(), m_exclude );
1510
1511         return true;
1512 }
1513 };
1514
1515 void Scene_Exclude_All( bool exclude ){
1516         GlobalSceneGraph().traverse( ExcludeAllWalker( exclude ) );
1517 }
1518
1519 bool Instance_isSelected( const scene::Instance& instance ){
1520         const Selectable* selectable = Instance_getSelectable( instance );
1521         return selectable != 0 && selectable->isSelected();
1522 }
1523
1524 class ExcludeSelectedWalker : public scene::Graph::Walker
1525 {
1526 bool m_exclude;
1527 public:
1528 ExcludeSelectedWalker( bool exclude )
1529         : m_exclude( exclude ){
1530 }
1531 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1532         exclude_node( path.top(), ( instance.isSelected() || instance.childSelected() || instance.parentSelected() ) == m_exclude );
1533         return true;
1534 }
1535 };
1536
1537 void Scene_Exclude_Selected( bool exclude ){
1538         GlobalSceneGraph().traverse( ExcludeSelectedWalker( exclude ) );
1539 }
1540
1541 class ExcludeRegionedWalker : public scene::Graph::Walker
1542 {
1543 bool m_exclude;
1544 public:
1545 ExcludeRegionedWalker( bool exclude )
1546         : m_exclude( exclude ){
1547 }
1548 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1549         exclude_node(
1550                 path.top(),
1551                 !(
1552                         (
1553                                 aabb_intersects_aabb(
1554                                         instance.worldAABB(),
1555                                         aabb_for_minmax( region_mins, region_maxs )
1556                                         ) != 0
1557                         ) ^ m_exclude
1558                         )
1559                 );
1560
1561         return true;
1562 }
1563 };
1564
1565 void Scene_Exclude_Region( bool exclude ){
1566         GlobalSceneGraph().traverse( ExcludeRegionedWalker( exclude ) );
1567 }
1568
1569 /*
1570    ===========
1571    Map_RegionOff
1572
1573    Other filtering options may still be on
1574    ===========
1575  */
1576 void Map_RegionOff(){
1577         region_active = false;
1578         g_region_item.update();
1579
1580         region_maxs[0] = g_MaxWorldCoord - 64;
1581         region_mins[0] = g_MinWorldCoord + 64;
1582         region_maxs[1] = g_MaxWorldCoord - 64;
1583         region_mins[1] = g_MinWorldCoord + 64;
1584         region_maxs[2] = g_MaxWorldCoord - 64;
1585         region_mins[2] = g_MinWorldCoord + 64;
1586
1587         Scene_Exclude_All( false );
1588 }
1589
1590 void Map_ApplyRegion( void ){
1591         region_active = true;
1592         g_region_item.update();
1593
1594         Scene_Exclude_Region( false );
1595 }
1596
1597
1598 /*
1599    ========================
1600    Map_RegionSelectedBrushes
1601    ========================
1602  */
1603 void Map_RegionSelectedBrushes( void ){
1604         Map_RegionOff();
1605
1606         if ( GlobalSelectionSystem().countSelected() != 0
1607                  && GlobalSelectionSystem().Mode() == SelectionSystem::ePrimitive ) {
1608                 region_active = true;
1609                 g_region_item.update();
1610                 Select_GetBounds( region_mins, region_maxs );
1611
1612                 Scene_Exclude_Selected( false );
1613
1614                 GlobalSelectionSystem().setSelectedAll( false );
1615         }
1616 }
1617
1618
1619 /*
1620    ===========
1621    Map_RegionXY
1622    ===========
1623  */
1624 void Map_RegionXY( float x_min, float y_min, float x_max, float y_max ){
1625         Map_RegionOff();
1626
1627         region_mins[0] = x_min;
1628         region_maxs[0] = x_max;
1629         region_mins[1] = y_min;
1630         region_maxs[1] = y_max;
1631         region_mins[2] = g_MinWorldCoord + 64;
1632         region_maxs[2] = g_MaxWorldCoord - 64;
1633
1634         Map_ApplyRegion();
1635 }
1636
1637 void Map_RegionBounds( const AABB& bounds ){
1638         Map_RegionOff();
1639
1640         region_mins = vector3_subtracted( bounds.origin, bounds.extents );
1641         region_maxs = vector3_added( bounds.origin, bounds.extents );
1642
1643         deleteSelection();
1644
1645         Map_ApplyRegion();
1646 }
1647
1648 /*
1649    ===========
1650    Map_RegionBrush
1651    ===========
1652  */
1653 void Map_RegionBrush( void ){
1654         if ( GlobalSelectionSystem().countSelected() != 0 ) {
1655                 scene::Instance& instance = GlobalSelectionSystem().ultimateSelected();
1656                 Map_RegionBounds( instance.worldAABB() );
1657         }
1658 }
1659
1660 //
1661 //================
1662 //Map_ImportFile
1663 //================
1664 //
1665 bool Map_ImportFile( const char* filename ){
1666         ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Loading Map" );
1667
1668         bool success = false;
1669
1670         if ( extension_equal( path_get_extension( filename ), "bsp" ) ) {
1671                 goto tryDecompile;
1672         }
1673
1674         {
1675                 const MapFormat* format = NULL;
1676                 const char* moduleName = findModuleName( &GlobalFiletypes(), MapFormat::Name(), path_get_extension( filename ) );
1677                 if ( string_not_empty( moduleName ) ) {
1678                         format = ReferenceAPI_getMapModules().findModule( moduleName );
1679                 }
1680
1681                 if ( format ) {
1682                         format->wrongFormat = false;
1683                 }
1684                 Resource* resource = GlobalReferenceCache().capture( filename );
1685                 resource->refresh(); // avoid loading old version if map has changed on disk since last import
1686                 if ( !resource->load() ) {
1687                         GlobalReferenceCache().release( filename );
1688                         goto tryDecompile;
1689                 }
1690                 if ( format ) {
1691                         if ( format->wrongFormat ) {
1692                                 GlobalReferenceCache().release( filename );
1693                                 goto tryDecompile;
1694                         }
1695                 }
1696                 NodeSmartReference clone( NewMapRoot( "" ) );
1697                 Node_getTraversable( *resource->getNode() )->traverse( CloneAll( clone ) );
1698                 Map_gatherNamespaced( clone );
1699                 Map_mergeClonedNames();
1700                 MergeMap( clone );
1701                 success = true;
1702                 GlobalReferenceCache().release( filename );
1703         }
1704
1705         SceneChangeNotify();
1706
1707         return success;
1708
1709 tryDecompile:
1710
1711         const char *type = GlobalRadiant().getGameDescriptionKeyValue( "q3map2_type" );
1712         int n = string_length( path_get_extension( filename ) );
1713         if ( n && ( extension_equal( path_get_extension( filename ), "bsp" ) || extension_equal( path_get_extension( filename ), "map" ) ) ) {
1714                 StringBuffer output;
1715                 output.push_string( AppPath_get() );
1716                 output.push_string( "q3map2." );
1717                 output.push_string( RADIANT_EXECUTABLE );
1718                 output.push_string( " -v -game " );
1719                 output.push_string( ( type && *type ) ? type : "quake3" );
1720                 output.push_string( " -fs_basepath \"" );
1721                 output.push_string( EnginePath_get() );
1722                 output.push_string( "\" -fs_homepath \"" );
1723                 output.push_string( g_qeglobals.m_userEnginePath.c_str() );
1724                 output.push_string( "\" -fs_game " );
1725                 output.push_string( gamename_get() );
1726                 output.push_string( " -convert -format " );
1727                 output.push_string( Brush::m_type == eBrushTypeQuake3BP ? "map_bp" : "map" );
1728                 if ( extension_equal( path_get_extension( filename ), "map" ) ) {
1729                         output.push_string( " -readmap " );
1730                 }
1731                 output.push_string( " \"" );
1732                 output.push_string( filename );
1733                 output.push_string( "\"" );
1734
1735                 // run
1736                 Q_Exec( NULL, output.c_str(), NULL, false, true );
1737
1738                 // rebuild filename as "filenamewithoutext_converted.map"
1739                 output.clear();
1740                 output.push_range( filename, filename + string_length( filename ) - ( n + 1 ) );
1741                 output.push_string( "_converted.map" );
1742                 filename = output.c_str();
1743
1744                 // open
1745                 Resource* resource = GlobalReferenceCache().capture( filename );
1746                 resource->refresh(); // avoid loading old version if map has changed on disk since last import
1747                 if ( !resource->load() ) {
1748                         GlobalReferenceCache().release( filename );
1749                         goto tryDecompile;
1750                 }
1751                 NodeSmartReference clone( NewMapRoot( "" ) );
1752                 Node_getTraversable( *resource->getNode() )->traverse( CloneAll( clone ) );
1753                 Map_gatherNamespaced( clone );
1754                 Map_mergeClonedNames();
1755                 MergeMap( clone );
1756                 success = true;
1757                 GlobalReferenceCache().release( filename );
1758         }
1759
1760         SceneChangeNotify();
1761         return success;
1762 }
1763
1764 /*
1765    ===========
1766    Map_SaveFile
1767    ===========
1768  */
1769 bool Map_SaveFile( const char* filename ){
1770         ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Saving Map" );
1771         return MapResource_saveFile( MapFormat_forFile( filename ), GlobalSceneGraph().root(), Map_Traverse, filename );
1772 }
1773
1774 //
1775 //===========
1776 //Map_SaveSelected
1777 //===========
1778 //
1779 // Saves selected world brushes and whole entities with partial/full selections
1780 //
1781 bool Map_SaveSelected( const char* filename ){
1782         return MapResource_saveFile( MapFormat_forFile( filename ), GlobalSceneGraph().root(), Map_Traverse_Selected, filename );
1783 }
1784
1785 class ParentSelectedBrushesToEntityWalker : public scene::Graph::Walker
1786 {
1787         scene::Node& m_parent;
1788         mutable bool m_emptyOldParent;
1789
1790 public:
1791 ParentSelectedBrushesToEntityWalker( scene::Node& parent ) : m_parent( parent ), m_emptyOldParent( false ){
1792 }
1793 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1794         if ( path.top().get_pointer() != &m_parent && ( Node_isPrimitive( path.top() ) || m_emptyOldParent ) ) {
1795                 Selectable* selectable = Instance_getSelectable( instance );
1796                 if ( selectable && selectable->isSelected() && path.size() > 1 ) {
1797                         return false;
1798                 }
1799         }
1800         return true;
1801 }
1802 void post( const scene::Path& path, scene::Instance& instance ) const {
1803         if ( path.top().get_pointer() == &m_parent )
1804                 return;
1805
1806         if ( Node_isPrimitive( path.top() ) ){
1807                 m_emptyOldParent = false;
1808                 Selectable* selectable = Instance_getSelectable( instance );
1809
1810                 if ( selectable && selectable->isSelected() && path.size() > 1 ){
1811                         scene::Node& parent = path.parent();
1812                         if ( &parent != &m_parent ){
1813                                 NodeSmartReference node( path.top().get() );
1814                                 scene::Traversable* traversable_parent = Node_getTraversable( parent );
1815                                 traversable_parent->erase( node );
1816                                 Node_getTraversable( m_parent )->insert( node );
1817                                 if ( traversable_parent->empty() )
1818                                         m_emptyOldParent = true;
1819                         }
1820                 }
1821         }
1822         else if ( m_emptyOldParent ){
1823                 m_emptyOldParent = false;
1824                 // delete empty entities
1825                 Entity* entity = Node_getEntity( path.top() );
1826                 if ( entity != 0 && path.top().get_pointer() != Map_FindWorldspawn( g_map )     && Node_getTraversable( path.top() )->empty() ) {
1827                         Path_deleteTop( path );
1828                 }
1829         }
1830 }
1831 };
1832
1833 void Scene_parentSelectedBrushesToEntity( scene::Graph& graph, scene::Node& parent ){
1834         graph.traverse( ParentSelectedBrushesToEntityWalker( parent ) );
1835 }
1836
1837 class CountSelectedBrushes : public scene::Graph::Walker
1838 {
1839 std::size_t& m_count;
1840 mutable std::size_t m_depth;
1841 public:
1842 CountSelectedBrushes( std::size_t& count ) : m_count( count ), m_depth( 0 ){
1843         m_count = 0;
1844 }
1845 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1846         if ( ++m_depth != 1 && path.top().get().isRoot() ) {
1847                 return false;
1848         }
1849         Selectable* selectable = Instance_getSelectable( instance );
1850         if ( selectable != 0
1851                  && selectable->isSelected()
1852                  && Node_isPrimitive( path.top() ) ) {
1853                 ++m_count;
1854         }
1855         return true;
1856 }
1857 void post( const scene::Path& path, scene::Instance& instance ) const {
1858         --m_depth;
1859 }
1860 };
1861
1862 std::size_t Scene_countSelectedBrushes( scene::Graph& graph ){
1863         std::size_t count;
1864         graph.traverse( CountSelectedBrushes( count ) );
1865         return count;
1866 }
1867
1868 enum ENodeType
1869 {
1870         eNodeUnknown,
1871         eNodeMap,
1872         eNodeEntity,
1873         eNodePrimitive,
1874 };
1875
1876 const char* nodetype_get_name( ENodeType type ){
1877         if ( type == eNodeMap ) {
1878                 return "map";
1879         }
1880         if ( type == eNodeEntity ) {
1881                 return "entity";
1882         }
1883         if ( type == eNodePrimitive ) {
1884                 return "primitive";
1885         }
1886         return "unknown";
1887 }
1888
1889 ENodeType node_get_nodetype( scene::Node& node ){
1890         if ( Node_isEntity( node ) ) {
1891                 return eNodeEntity;
1892         }
1893         if ( Node_isPrimitive( node ) ) {
1894                 return eNodePrimitive;
1895         }
1896         return eNodeUnknown;
1897 }
1898
1899 bool contains_entity( scene::Node& node ){
1900         return Node_getTraversable( node ) != 0 && !Node_isBrush( node ) && !Node_isPatch( node ) && !Node_isEntity( node );
1901 }
1902
1903 bool contains_primitive( scene::Node& node ){
1904         return Node_isEntity( node ) && Node_getTraversable( node ) != 0 && Node_getEntity( node )->isContainer();
1905 }
1906
1907 ENodeType node_get_contains( scene::Node& node ){
1908         if ( contains_entity( node ) ) {
1909                 return eNodeEntity;
1910         }
1911         if ( contains_primitive( node ) ) {
1912                 return eNodePrimitive;
1913         }
1914         return eNodeUnknown;
1915 }
1916
1917 void Path_parent( const scene::Path& parent, const scene::Path& child ){
1918         ENodeType contains = node_get_contains( parent.top() );
1919         ENodeType type = node_get_nodetype( child.top() );
1920
1921         if ( contains != eNodeUnknown && contains == type ) {
1922                 NodeSmartReference node( child.top().get() );
1923                 Path_deleteTop( child );
1924                 Node_getTraversable( parent.top() )->insert( node );
1925                 SceneChangeNotify();
1926         }
1927         else
1928         {
1929                 globalErrorStream() << "failed - " << nodetype_get_name( type ) << " cannot be parented to " << nodetype_get_name( contains ) << " container.\n";
1930         }
1931 }
1932
1933 void Scene_parentSelected(){
1934         UndoableCommand undo( "parentSelected" );
1935
1936         if ( GlobalSelectionSystem().countSelected() > 1 ) {
1937                 class ParentSelectedBrushesToEntityWalker : public SelectionSystem::Visitor
1938                 {
1939                 const scene::Path& m_parent;
1940 public:
1941                 ParentSelectedBrushesToEntityWalker( const scene::Path& parent ) : m_parent( parent ){
1942                 }
1943                 void visit( scene::Instance& instance ) const {
1944                         if ( &m_parent != &instance.path() ) {
1945                                 Path_parent( m_parent, instance.path() );
1946                         }
1947                 }
1948                 };
1949
1950                 ParentSelectedBrushesToEntityWalker visitor( GlobalSelectionSystem().ultimateSelected().path() );
1951                 GlobalSelectionSystem().foreachSelected( visitor );
1952         }
1953         else
1954         {
1955                 globalOutputStream() << "failed - did not find two selected nodes.\n";
1956         }
1957 }
1958
1959
1960
1961 void NewMap(){
1962         if ( ConfirmModified( "New Map" ) ) {
1963                 Map_RegionOff();
1964                 Map_Free();
1965                 Map_New();
1966         }
1967 }
1968
1969 CopiedString g_mapsPath;
1970
1971 const char* getMapsPath(){
1972         return g_mapsPath.c_str();
1973 }
1974
1975 const char* map_open( const char* title ){
1976         return file_dialog( GTK_WIDGET( MainFrame_getWindow() ), TRUE, title, getMapsPath(), MapFormat::Name(), true, false, false );
1977 }
1978
1979 const char* map_import( const char* title ){
1980         return file_dialog( GTK_WIDGET( MainFrame_getWindow() ), TRUE, title, getMapsPath(), MapFormat::Name(), false, true, false );
1981 }
1982
1983 const char* map_save( const char* title ){
1984         return file_dialog( GTK_WIDGET( MainFrame_getWindow() ), FALSE, title, getMapsPath(), MapFormat::Name(), false, false, true );
1985 }
1986
1987 void OpenMap(){
1988         if ( !ConfirmModified( "Open Map" ) ) {
1989                 return;
1990         }
1991
1992         const char* filename = map_open( "Open Map" );
1993
1994         if ( filename != 0 ) {
1995                 MRU_AddFile( filename );
1996                 Map_RegionOff();
1997                 Map_Free();
1998                 Map_LoadFile( filename );
1999         }
2000 }
2001
2002 void ImportMap(){
2003         const char* filename = map_import( "Import Map" );
2004
2005         if ( filename != 0 ) {
2006                 UndoableCommand undo( "mapImport" );
2007                 Map_ImportFile( filename );
2008         }
2009 }
2010
2011 bool Map_SaveAs(){
2012         const char* filename = map_save( "Save Map" );
2013
2014         if ( filename != 0 ) {
2015                 MRU_AddFile( filename );
2016                 Map_Rename( filename );
2017                 return Map_Save();
2018         }
2019         return false;
2020 }
2021
2022 void SaveMapAs(){
2023         Map_SaveAs();
2024 }
2025
2026 void SaveMap(){
2027         if ( Map_Unnamed( g_map ) ) {
2028                 SaveMapAs();
2029         }
2030         else if ( Map_Modified( g_map ) ) {
2031                 Map_Save();
2032                 MRU_AddFile( g_map.m_name.c_str() );    //add on saving, but not opening via cmd line: spoils the list
2033         }
2034 }
2035
2036 void ExportMap(){
2037         const char* filename = map_save( "Export Selection" );
2038
2039         if ( filename != 0 ) {
2040                 Map_SaveSelected( filename );
2041         }
2042 }
2043
2044 void SaveRegion(){
2045         const char* filename = map_save( "Export Region" );
2046
2047         if ( filename != 0 ) {
2048                 Map_SaveRegion( filename );
2049         }
2050 }
2051
2052
2053 void RegionOff(){
2054         Map_RegionOff();
2055         SceneChangeNotify();
2056 }
2057
2058 void RegionXY(){
2059         Map_RegionXY(
2060                 g_pParentWnd->GetXYWnd()->GetOrigin()[0] - 0.5f * g_pParentWnd->GetXYWnd()->Width() / g_pParentWnd->GetXYWnd()->Scale(),
2061                 g_pParentWnd->GetXYWnd()->GetOrigin()[1] - 0.5f * g_pParentWnd->GetXYWnd()->Height() / g_pParentWnd->GetXYWnd()->Scale(),
2062                 g_pParentWnd->GetXYWnd()->GetOrigin()[0] + 0.5f * g_pParentWnd->GetXYWnd()->Width() / g_pParentWnd->GetXYWnd()->Scale(),
2063                 g_pParentWnd->GetXYWnd()->GetOrigin()[1] + 0.5f * g_pParentWnd->GetXYWnd()->Height() / g_pParentWnd->GetXYWnd()->Scale()
2064                 );
2065         SceneChangeNotify();
2066 }
2067
2068 void RegionBrush(){
2069         Map_RegionBrush();
2070         SceneChangeNotify();
2071 }
2072
2073 void RegionSelected(){
2074         Map_RegionSelectedBrushes();
2075         SceneChangeNotify();
2076 }
2077
2078
2079
2080
2081
2082 class BrushFindByIndexWalker : public scene::Traversable::Walker
2083 {
2084 mutable std::size_t m_index;
2085 scene::Path& m_path;
2086 public:
2087 BrushFindByIndexWalker( std::size_t index, scene::Path& path )
2088         : m_index( index ), m_path( path ){
2089 }
2090 bool pre( scene::Node& node ) const {
2091         if ( Node_isPrimitive( node ) && m_index-- == 0 ) {
2092                 m_path.push( makeReference( node ) );
2093         }
2094         return false;
2095 }
2096 };
2097
2098 class EntityFindByIndexWalker : public scene::Traversable::Walker
2099 {
2100 mutable std::size_t m_index;
2101 scene::Path& m_path;
2102 public:
2103 EntityFindByIndexWalker( std::size_t index, scene::Path& path )
2104         : m_index( index ), m_path( path ){
2105 }
2106 bool pre( scene::Node& node ) const {
2107         if ( Node_isEntity( node ) && m_index-- == 0 ) {
2108                 m_path.push( makeReference( node ) );
2109         }
2110         return false;
2111 }
2112 };
2113
2114 void Scene_FindEntityBrush( std::size_t entity, std::size_t brush, scene::Path& path ){
2115         path.push( makeReference( GlobalSceneGraph().root() ) );
2116         {
2117                 Node_getTraversable( path.top() )->traverse( EntityFindByIndexWalker( entity, path ) );
2118         }
2119         if ( path.size() == 2 ) {
2120                 scene::Traversable* traversable = Node_getTraversable( path.top() );
2121                 if ( traversable != 0 ) {
2122                         traversable->traverse( BrushFindByIndexWalker( brush, path ) );
2123                 }
2124         }
2125 }
2126
2127 inline bool Node_hasChildren( scene::Node& node ){
2128         scene::Traversable* traversable = Node_getTraversable( node );
2129         return traversable != 0 && !traversable->empty();
2130 }
2131
2132 void SelectBrush( int entitynum, int brushnum ){
2133         scene::Path path;
2134         Scene_FindEntityBrush( entitynum, brushnum, path );
2135         if ( path.size() == 3 || ( path.size() == 2 && !Node_hasChildren( path.top() ) ) ) {
2136                 scene::Instance* instance = GlobalSceneGraph().find( path );
2137                 ASSERT_MESSAGE( instance != 0, "SelectBrush: path not found in scenegraph" );
2138                 Selectable* selectable = Instance_getSelectable( *instance );
2139                 ASSERT_MESSAGE( selectable != 0, "SelectBrush: path not selectable" );
2140                 selectable->setSelected( true );
2141                 g_pParentWnd->GetXYWnd()->PositionView( instance->worldAABB().origin );
2142         }
2143 }
2144
2145
2146 class BrushFindIndexWalker : public scene::Traversable::Walker
2147 {
2148 mutable const scene::Node* m_node;
2149 std::size_t& m_count;
2150 public:
2151 BrushFindIndexWalker( const scene::Node& node, std::size_t& count )
2152         : m_node( &node ), m_count( count ){
2153 }
2154 bool pre( scene::Node& node ) const {
2155         if ( Node_isPrimitive( node ) ) {
2156                 if ( m_node == &node ) {
2157                         m_node = 0;
2158                 }
2159                 if ( m_node ) {
2160                         ++m_count;
2161                 }
2162         }
2163         return true;
2164 }
2165 };
2166
2167 class EntityFindIndexWalker : public scene::Traversable::Walker
2168 {
2169 mutable const scene::Node* m_node;
2170 std::size_t& m_count;
2171 public:
2172 EntityFindIndexWalker( const scene::Node& node, std::size_t& count )
2173         : m_node( &node ), m_count( count ){
2174 }
2175 bool pre( scene::Node& node ) const {
2176         if ( Node_isEntity( node ) ) {
2177                 if ( m_node == &node ) {
2178                         m_node = 0;
2179                 }
2180                 if ( m_node ) {
2181                         ++m_count;
2182                 }
2183         }
2184         return true;
2185 }
2186 };
2187
2188 static void GetSelectionIndex( int *ent, int *brush ){
2189         std::size_t count_brush = 0;
2190         std::size_t count_entity = 0;
2191         if ( GlobalSelectionSystem().countSelected() != 0 ) {
2192                 const scene::Path& path = GlobalSelectionSystem().ultimateSelected().path();
2193
2194                 {
2195                         scene::Traversable* traversable = Node_getTraversable( path.parent() );
2196                         if ( traversable != 0 && path.size() == 3 ) {
2197                                 traversable->traverse( BrushFindIndexWalker( path.top(), count_brush ) );
2198                         }
2199                 }
2200
2201                 {
2202                         scene::Traversable* traversable = Node_getTraversable( GlobalSceneGraph().root() );
2203                         if ( traversable != 0 ) {
2204                                 if( path.size() == 3 ){
2205                                         traversable->traverse( EntityFindIndexWalker( path.parent(), count_entity ) );
2206                                 }
2207                                 else if ( path.size() == 2 ){
2208                                         traversable->traverse( EntityFindIndexWalker( path.top(), count_entity ) );
2209                                 }
2210                         }
2211                 }
2212         }
2213         *brush = int(count_brush);
2214         *ent = int(count_entity);
2215 }
2216
2217 void DoFind(){
2218         ModalDialog dialog;
2219         GtkEntry* entity;
2220         GtkEntry* brush;
2221
2222         GtkWindow* window = create_dialog_window( MainFrame_getWindow(), "Find Brush", G_CALLBACK( dialog_delete_callback ), &dialog );
2223
2224         GtkAccelGroup* accel = gtk_accel_group_new();
2225         gtk_window_add_accel_group( window, accel );
2226
2227         {
2228                 GtkVBox* vbox = create_dialog_vbox( 4, 4 );
2229                 gtk_container_add( GTK_CONTAINER( window ), GTK_WIDGET( vbox ) );
2230                 {
2231                         GtkTable* table = create_dialog_table( 2, 2, 4, 4 );
2232                         gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( table ), TRUE, TRUE, 0 );
2233                         {
2234                                 GtkWidget* label = gtk_label_new( "Entity number" );
2235                                 gtk_widget_show( label );
2236                                 gtk_table_attach( GTK_TABLE( table ), label, 0, 1, 0, 1,
2237                                                                   (GtkAttachOptions) ( 0 ),
2238                                                                   (GtkAttachOptions) ( 0 ), 0, 0 );
2239                         }
2240                         {
2241                                 GtkWidget* label = gtk_label_new( "Brush number" );
2242                                 gtk_widget_show( label );
2243                                 gtk_table_attach( GTK_TABLE( table ), label, 0, 1, 1, 2,
2244                                                                   (GtkAttachOptions) ( 0 ),
2245                                                                   (GtkAttachOptions) ( 0 ), 0, 0 );
2246                         }
2247                         {
2248                                 GtkEntry* entry = GTK_ENTRY( gtk_entry_new() );
2249                                 gtk_widget_show( GTK_WIDGET( entry ) );
2250                                 gtk_table_attach( table, GTK_WIDGET( entry ), 1, 2, 0, 1,
2251                                                                   (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
2252                                                                   (GtkAttachOptions) ( 0 ), 0, 0 );
2253                                 gtk_widget_grab_focus( GTK_WIDGET( entry ) );
2254                                 entity = entry;
2255                         }
2256                         {
2257                                 GtkEntry* entry = GTK_ENTRY( gtk_entry_new() );
2258                                 gtk_widget_show( GTK_WIDGET( entry ) );
2259                                 gtk_table_attach( table, GTK_WIDGET( entry ), 1, 2, 1, 2,
2260                                                                   (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
2261                                                                   (GtkAttachOptions) ( 0 ), 0, 0 );
2262
2263                                 brush = entry;
2264                         }
2265                 }
2266                 {
2267                         GtkHBox* hbox = create_dialog_hbox( 4 );
2268                         gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( hbox ), TRUE, TRUE, 0 );
2269                         {
2270                                 GtkButton* button = create_dialog_button( "Find", G_CALLBACK( dialog_button_ok ), &dialog );
2271                                 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
2272                                 widget_make_default( GTK_WIDGET( button ) );
2273                                 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel, GDK_Return, (GdkModifierType)0, (GtkAccelFlags)0 );
2274                         }
2275                         {
2276                                 GtkButton* button = create_dialog_button( "Close", G_CALLBACK( dialog_button_cancel ), &dialog );
2277                                 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
2278                                 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel, GDK_Escape, (GdkModifierType)0, (GtkAccelFlags)0 );
2279                         }
2280                 }
2281         }
2282
2283         // Initialize dialog
2284         char buf[16];
2285         int ent, br;
2286
2287         GetSelectionIndex( &ent, &br );
2288         sprintf( buf, "%i", ent );
2289         gtk_entry_set_text( entity, buf );
2290         sprintf( buf, "%i", br );
2291         gtk_entry_set_text( brush, buf );
2292
2293         if ( modal_dialog_show( window, dialog ) == eIDOK ) {
2294                 const char *entstr = gtk_entry_get_text( entity );
2295                 const char *brushstr = gtk_entry_get_text( brush );
2296                 SelectBrush( atoi( entstr ), atoi( brushstr ) );
2297         }
2298
2299         gtk_widget_destroy( GTK_WIDGET( window ) );
2300 }
2301
2302 void Map_constructPreferences( PreferencesPage& page ){
2303         page.appendCheckBox( "", "Load last map at startup", g_bLoadLastMap );
2304 }
2305
2306
2307 class MapEntityClasses : public ModuleObserver
2308 {
2309 std::size_t m_unrealised;
2310 public:
2311 MapEntityClasses() : m_unrealised( 1 ){
2312 }
2313 void realise(){
2314         if ( --m_unrealised == 0 ) {
2315                 if ( g_map.m_resource != 0 ) {
2316                         ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Loading Map" );
2317                         g_map.m_resource->realise();
2318                 }
2319         }
2320 }
2321 void unrealise(){
2322         if ( ++m_unrealised == 1 ) {
2323                 if ( g_map.m_resource != 0 ) {
2324                         g_map.m_resource->flush();
2325                         g_map.m_resource->unrealise();
2326                 }
2327         }
2328 }
2329 };
2330
2331 MapEntityClasses g_MapEntityClasses;
2332
2333
2334 class MapModuleObserver : public ModuleObserver
2335 {
2336 std::size_t m_unrealised;
2337 public:
2338 MapModuleObserver() : m_unrealised( 1 ){
2339 }
2340 void realise(){
2341         if ( --m_unrealised == 0 ) {
2342                 ASSERT_MESSAGE( !string_empty( g_qeglobals.m_userGamePath.c_str() ), "maps_directory: user-game-path is empty" );
2343                 StringOutputStream buffer( 256 );
2344                 buffer << g_qeglobals.m_userGamePath.c_str() << "maps/";
2345                 Q_mkdir( buffer.c_str() );
2346                 g_mapsPath = buffer.c_str();
2347         }
2348 }
2349 void unrealise(){
2350         if ( ++m_unrealised == 1 ) {
2351                 g_mapsPath = "";
2352         }
2353 }
2354 };
2355
2356 MapModuleObserver g_MapModuleObserver;
2357
2358 #include "preferencesystem.h"
2359
2360 CopiedString g_strLastMap;
2361 bool g_bLoadLastMap = false;
2362
2363 void Map_Construct(){
2364         GlobalCommands_insert( "RegionOff", FreeCaller<RegionOff>() );
2365         GlobalCommands_insert( "RegionSetXY", FreeCaller<RegionXY>() );
2366         GlobalCommands_insert( "RegionSetBrush", FreeCaller<RegionBrush>() );
2367         //GlobalCommands_insert( "RegionSetSelection", FreeCaller<RegionSelected>(), Accelerator( 'R', (GdkModifierType)( GDK_SHIFT_MASK | GDK_CONTROL_MASK ) ) );
2368         GlobalToggles_insert( "RegionSetSelection", FreeCaller<RegionSelected>(), ToggleItem::AddCallbackCaller( g_region_item ), Accelerator( 'R', (GdkModifierType)( GDK_SHIFT_MASK | GDK_CONTROL_MASK ) ) );
2369
2370         GlobalPreferenceSystem().registerPreference( "LastMap", CopiedStringImportStringCaller( g_strLastMap ), CopiedStringExportStringCaller( g_strLastMap ) );
2371         GlobalPreferenceSystem().registerPreference( "LoadLastMap", BoolImportStringCaller( g_bLoadLastMap ), BoolExportStringCaller( g_bLoadLastMap ) );
2372         GlobalPreferenceSystem().registerPreference( "MapInfoDlg", WindowPositionImportStringCaller( g_posMapInfoWnd ), WindowPositionExportStringCaller( g_posMapInfoWnd ) );
2373
2374         PreferencesDialog_addSettingsPreferences( FreeCaller1<PreferencesPage&, Map_constructPreferences>() );
2375
2376         GlobalEntityClassManager().attach( g_MapEntityClasses );
2377         Radiant_attachHomePathsObserver( g_MapModuleObserver );
2378 }
2379
2380 void Map_Destroy(){
2381         Radiant_detachHomePathsObserver( g_MapModuleObserver );
2382         GlobalEntityClassManager().detach( g_MapEntityClasses );
2383 }