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